Fix amplification mitigation in 0-RTT case.

From the spec:

   Prior to validating the client address, servers MUST NOT send more
   than three times as many bytes as the number of bytes they have
   received.  This limits the magnitude of any amplification attack that
   can be mounted using spoofed source addresses.  In determining this
   limit, servers only count the size of successfully processed packets.
This commit is contained in:
Dmitri Tikhonov 2020-05-06 10:03:36 -04:00
parent 652129e69b
commit 10f94146d0
5 changed files with 85 additions and 8 deletions

View file

@ -57,6 +57,11 @@ struct lsquic_conn_public {
#if LSQUIC_EXTRA_CHECKS #if LSQUIC_EXTRA_CHECKS
unsigned long stream_frame_bytes; unsigned long stream_frame_bytes;
#endif #endif
/* "unsigned" is wide enough: these values are only used for amplification
* limit before initial path is validated.
*/
unsigned bytes_in; /* successfully processed */
unsigned bytes_out;
}; };
#endif #endif

View file

@ -137,6 +137,12 @@ enum ifull_conn_flags
}; };
enum more_flags
{
MF_VALIDATE_PATH = 1 << 0,
};
#define N_PATHS 2 #define N_PATHS 2
enum send enum send
@ -314,6 +320,7 @@ struct ietf_full_conn
unsigned ifc_max_streams_in[N_SDS]; unsigned ifc_max_streams_in[N_SDS];
uint64_t ifc_max_stream_data_uni; uint64_t ifc_max_stream_data_uni;
enum ifull_conn_flags ifc_flags; enum ifull_conn_flags ifc_flags;
enum more_flags ifc_mflags;
enum send_flags ifc_send_flags; enum send_flags ifc_send_flags;
enum send_flags ifc_delayed_send; enum send_flags ifc_delayed_send;
struct { struct {
@ -1306,6 +1313,12 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
conn->ifc_paths[0].cop_path = imc->imc_path; conn->ifc_paths[0].cop_path = imc->imc_path;
conn->ifc_paths[0].cop_flags = COP_VALIDATED; conn->ifc_paths[0].cop_flags = COP_VALIDATED;
conn->ifc_used_paths = 1 << 0; conn->ifc_used_paths = 1 << 0;
if (imc->imc_flags & IMC_ADDR_VALIDATED)
lsquic_send_ctl_path_validated(&conn->ifc_send_ctl);
else
conn->ifc_mflags |= MF_VALIDATE_PATH;
conn->ifc_pub.bytes_out = imc->imc_bytes_out;
conn->ifc_pub.bytes_in = imc->imc_bytes_in;
if (imc->imc_flags & IMC_PATH_CHANGED) if (imc->imc_flags & IMC_PATH_CHANGED)
{ {
LSQ_DEBUG("path changed during mini conn: schedule PATH_CHALLENGE"); LSQ_DEBUG("path changed during mini conn: schedule PATH_CHALLENGE");
@ -6369,6 +6382,14 @@ process_regular_packet (struct ietf_full_conn *conn,
else else
conn->ifc_spin_bit = !lsquic_packet_in_spin_bit(packet_in); conn->ifc_spin_bit = !lsquic_packet_in_spin_bit(packet_in);
} }
conn->ifc_pub.bytes_in += packet_in->pi_data_sz;
if ((conn->ifc_mflags & MF_VALIDATE_PATH) &&
(packet_in->pi_header_type == HETY_NOT_SET
|| packet_in->pi_header_type == HETY_HANDSHAKE))
{
conn->ifc_mflags &= ~MF_VALIDATE_PATH;
lsquic_send_ctl_path_validated(&conn->ifc_send_ctl);
}
return 0; return 0;
case REC_ST_DUP: case REC_ST_DUP:
LSQ_INFO("packet %"PRIu64" is a duplicate", packet_in->pi_packno); LSQ_INFO("packet %"PRIu64" is a duplicate", packet_in->pi_packno);
@ -6567,6 +6588,8 @@ ietf_full_conn_ci_packet_sent (struct lsquic_conn *lconn,
lsquic_alarmset_set(&conn->ifc_alset, AL_BLOCKED_KA, lsquic_alarmset_set(&conn->ifc_alset, AL_BLOCKED_KA,
packet_out->po_sent + (1 + (7 & lsquic_crand_get_nybble( packet_out->po_sent + (1 + (7 & lsquic_crand_get_nybble(
conn->ifc_enpub->enp_crand))) * 1000000); conn->ifc_enpub->enp_crand))) * 1000000);
conn->ifc_pub.bytes_out += lsquic_packet_out_sent_sz(&conn->ifc_conn,
packet_out);
} }

View file

@ -214,7 +214,7 @@ typedef struct lsquic_packet_out
lconn->cn_pf->pf_packout_max_header_size(lconn, po_flags, dcid_len)) lconn->cn_pf->pf_packout_max_header_size(lconn, po_flags, dcid_len))
#define lsquic_packet_out_total_sz(lconn, p) (\ #define lsquic_packet_out_total_sz(lconn, p) (\
lconn->cn_pf->pf_packout_size(lconn, p)) (lconn)->cn_pf->pf_packout_size(lconn, p))
#if __GNUC__ #if __GNUC__
#if LSQUIC_EXTRA_CHECKS #if LSQUIC_EXTRA_CHECKS

View file

@ -119,6 +119,12 @@ send_ctl_all_bytes_out (const struct lsquic_send_ctl *ctl);
static void static void
send_ctl_reschedule_poison (struct lsquic_send_ctl *ctl); send_ctl_reschedule_poison (struct lsquic_send_ctl *ctl);
static int
send_ctl_can_send_pre_hsk (struct lsquic_send_ctl *ctl);
static int
send_ctl_can_send (struct lsquic_send_ctl *ctl);
#ifdef NDEBUG #ifdef NDEBUG
static static
#elif __GNUC__ #elif __GNUC__
@ -363,6 +369,11 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
ctl->sc_flags |= SC_SANITY_CHECK; ctl->sc_flags |= SC_SANITY_CHECK;
#endif #endif
ctl->sc_gap = UINT64_MAX - 1 /* Can't have +1 == 0 */; ctl->sc_gap = UINT64_MAX - 1 /* Can't have +1 == 0 */;
if ((ctl->sc_conn_pub->lconn->cn_flags & (LSCONN_IETF|LSCONN_SERVER))
== (LSCONN_IETF|LSCONN_SERVER))
ctl->sc_can_send = send_ctl_can_send_pre_hsk;
else
ctl->sc_can_send = send_ctl_can_send;
} }
@ -1367,13 +1378,8 @@ lsquic_send_ctl_pacer_blocked (struct lsquic_send_ctl *ctl)
} }
#ifndef NDEBUG static int
#if __GNUC__ send_ctl_can_send (struct lsquic_send_ctl *ctl)
__attribute__((weak))
#endif
#endif
int
lsquic_send_ctl_can_send (lsquic_send_ctl_t *ctl)
{ {
const unsigned n_out = send_ctl_all_bytes_out(ctl); const unsigned n_out = send_ctl_all_bytes_out(ctl);
LSQ_DEBUG("%s: n_out: %u (unacked_all: %u); cwnd: %"PRIu64, __func__, LSQ_DEBUG("%s: n_out: %u (unacked_all: %u); cwnd: %"PRIu64, __func__,
@ -1400,6 +1406,37 @@ lsquic_send_ctl_can_send (lsquic_send_ctl_t *ctl)
} }
static int
send_ctl_can_send_pre_hsk (struct lsquic_send_ctl *ctl)
{
unsigned bytes_in, bytes_out;
bytes_in = ctl->sc_conn_pub->bytes_in;
bytes_out = ctl->sc_conn_pub->bytes_out + ctl->sc_bytes_scheduled;
if (bytes_out >= bytes_in * 2 + bytes_in / 2 /* This should work out
to around 3 on average */)
{
LSQ_DEBUG("%s: amplification block: %u bytes in, %u bytes out",
__func__, bytes_in, bytes_out);
return 0;
}
else
return send_ctl_can_send(ctl);
}
#ifndef NDEBUG
#if __GNUC__
__attribute__((weak))
#endif
#endif
int
lsquic_send_ctl_can_send (struct lsquic_send_ctl *ctl)
{
return ctl->sc_can_send(ctl);
}
/* Like lsquic_send_ctl_can_send(), but no mods */ /* Like lsquic_send_ctl_can_send(), but no mods */
static int static int
send_ctl_could_send (const struct lsquic_send_ctl *ctl) send_ctl_could_send (const struct lsquic_send_ctl *ctl)
@ -2982,3 +3019,11 @@ lsquic_send_ctl_begin_optack_detection (struct lsquic_send_ctl *ctl)
rand = lsquic_crand_get_byte(ctl->sc_enpub->enp_crand); rand = lsquic_crand_get_byte(ctl->sc_enpub->enp_crand);
ctl->sc_gap = ctl->sc_cur_packno + 1 + rand; ctl->sc_gap = ctl->sc_cur_packno + 1 + rand;
} }
void
lsquic_send_ctl_path_validated (struct lsquic_send_ctl *ctl)
{
LSQ_DEBUG("path validated: switch to regular can_send");
ctl->sc_can_send = send_ctl_can_send;
}

View file

@ -64,6 +64,7 @@ typedef struct lsquic_send_ctl {
lsquic_time_t sc_largest_acked_sent_time; lsquic_time_t sc_largest_acked_sent_time;
lsquic_time_t sc_last_sent_time; lsquic_time_t sc_last_sent_time;
lsquic_time_t sc_last_rto_time; lsquic_time_t sc_last_rto_time;
int (*sc_can_send)(struct lsquic_send_ctl *);
unsigned sc_bytes_unacked_retx; unsigned sc_bytes_unacked_retx;
unsigned sc_bytes_scheduled; unsigned sc_bytes_scheduled;
union { union {
@ -383,4 +384,7 @@ lsquic_send_ctl_begin_optack_detection (struct lsquic_send_ctl *);
#define lsquic_send_ctl_n_unacked(ctl_) ((ctl_)->sc_n_in_flight_retx) #define lsquic_send_ctl_n_unacked(ctl_) ((ctl_)->sc_n_in_flight_retx)
void
lsquic_send_ctl_path_validated (struct lsquic_send_ctl *);
#endif #endif