From 10f94146d0ece8d7d5170d184effd597832acc4e Mon Sep 17 00:00:00 2001 From: Dmitri Tikhonov Date: Wed, 6 May 2020 10:03:36 -0400 Subject: [PATCH] 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. --- src/liblsquic/lsquic_conn_public.h | 5 +++ src/liblsquic/lsquic_full_conn_ietf.c | 23 +++++++++++ src/liblsquic/lsquic_packet_out.h | 2 +- src/liblsquic/lsquic_send_ctl.c | 59 +++++++++++++++++++++++---- src/liblsquic/lsquic_send_ctl.h | 4 ++ 5 files changed, 85 insertions(+), 8 deletions(-) diff --git a/src/liblsquic/lsquic_conn_public.h b/src/liblsquic/lsquic_conn_public.h index 47dfeb0..f78d0eb 100644 --- a/src/liblsquic/lsquic_conn_public.h +++ b/src/liblsquic/lsquic_conn_public.h @@ -57,6 +57,11 @@ struct lsquic_conn_public { #if LSQUIC_EXTRA_CHECKS unsigned long stream_frame_bytes; #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 diff --git a/src/liblsquic/lsquic_full_conn_ietf.c b/src/liblsquic/lsquic_full_conn_ietf.c index 61a4c3a..1a886c6 100644 --- a/src/liblsquic/lsquic_full_conn_ietf.c +++ b/src/liblsquic/lsquic_full_conn_ietf.c @@ -137,6 +137,12 @@ enum ifull_conn_flags }; +enum more_flags +{ + MF_VALIDATE_PATH = 1 << 0, +}; + + #define N_PATHS 2 enum send @@ -314,6 +320,7 @@ struct ietf_full_conn unsigned ifc_max_streams_in[N_SDS]; uint64_t ifc_max_stream_data_uni; enum ifull_conn_flags ifc_flags; + enum more_flags ifc_mflags; enum send_flags ifc_send_flags; enum send_flags ifc_delayed_send; 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_flags = COP_VALIDATED; 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) { LSQ_DEBUG("path changed during mini conn: schedule PATH_CHALLENGE"); @@ -6369,6 +6382,14 @@ process_regular_packet (struct ietf_full_conn *conn, else 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; case REC_ST_DUP: 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, packet_out->po_sent + (1 + (7 & lsquic_crand_get_nybble( conn->ifc_enpub->enp_crand))) * 1000000); + conn->ifc_pub.bytes_out += lsquic_packet_out_sent_sz(&conn->ifc_conn, + packet_out); } diff --git a/src/liblsquic/lsquic_packet_out.h b/src/liblsquic/lsquic_packet_out.h index 74467d7..867e3fe 100644 --- a/src/liblsquic/lsquic_packet_out.h +++ b/src/liblsquic/lsquic_packet_out.h @@ -214,7 +214,7 @@ typedef struct lsquic_packet_out lconn->cn_pf->pf_packout_max_header_size(lconn, po_flags, dcid_len)) #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 LSQUIC_EXTRA_CHECKS diff --git a/src/liblsquic/lsquic_send_ctl.c b/src/liblsquic/lsquic_send_ctl.c index 7ac100e..4994af1 100644 --- a/src/liblsquic/lsquic_send_ctl.c +++ b/src/liblsquic/lsquic_send_ctl.c @@ -119,6 +119,12 @@ send_ctl_all_bytes_out (const struct lsquic_send_ctl *ctl); static void 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 static #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; #endif 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 -#if __GNUC__ -__attribute__((weak)) -#endif -#endif -int -lsquic_send_ctl_can_send (lsquic_send_ctl_t *ctl) +static int +send_ctl_can_send (struct lsquic_send_ctl *ctl) { const unsigned n_out = send_ctl_all_bytes_out(ctl); 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 */ static int 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); 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; +} diff --git a/src/liblsquic/lsquic_send_ctl.h b/src/liblsquic/lsquic_send_ctl.h index c4d7685..908f381 100644 --- a/src/liblsquic/lsquic_send_ctl.h +++ b/src/liblsquic/lsquic_send_ctl.h @@ -64,6 +64,7 @@ typedef struct lsquic_send_ctl { lsquic_time_t sc_largest_acked_sent_time; lsquic_time_t sc_last_sent_time; lsquic_time_t sc_last_rto_time; + int (*sc_can_send)(struct lsquic_send_ctl *); unsigned sc_bytes_unacked_retx; unsigned sc_bytes_scheduled; 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) +void +lsquic_send_ctl_path_validated (struct lsquic_send_ctl *); + #endif