# HG changeset patch # User Vladimir Homutov # Date 1586179166 -10800 # Node ID 1cdd535323090d1a62d83ec5b044ea9f6b50095f # Parent 9b9d592c0da371465eacb4782c38e5c87e690253 ACK ranges processing. + since number of ranges in unknown, provide a function to parse them once again in handler to avoid memory allocation + ack handler now processes all ranges, not only the first + ECN counters are parsed and saved into frame if present diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -134,6 +134,8 @@ static ngx_int_t ngx_quic_payload_handle static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *f); +static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c, + ngx_quic_namespace_t *ns, uint64_t min, uint64_t max); static ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, ngx_quic_crypto_frame_t *frame); static ngx_int_t ngx_quic_handle_stream_frame(ngx_connection_t *c, @@ -1242,9 +1244,10 @@ static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *ack) { - ngx_uint_t found, min; - ngx_queue_t *q, range; - ngx_quic_frame_t *f; + ssize_t n; + u_char *pos, *end; + uint64_t gap, range; + ngx_uint_t i, min, max; ngx_quic_namespace_t *ns; ns = &c->quic->ns[ngx_quic_ns(pkt->level)]; @@ -1253,6 +1256,12 @@ ngx_quic_handle_ack_frame(ngx_connection "ngx_quic_handle_ack_frame in namespace %d", ngx_quic_ns(pkt->level)); + /* + * TODO: If any computed packet number is negative, an endpoint MUST + * generate a connection error of type FRAME_ENCODING_ERROR. + * (19.3.1) + */ + if (ack->first_range > ack->largest) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "invalid first range in ack frame"); @@ -1260,6 +1269,62 @@ ngx_quic_handle_ack_frame(ngx_connection } min = ack->largest - ack->first_range; + max = ack->largest; + + if (ngx_quic_handle_ack_frame_range(c, ns, min, max) != NGX_OK) { + return NGX_ERROR; + } + + /* 13.2.3. Receiver Tracking of ACK Frames */ + if (ns->largest < max) { + ns->largest = max; + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "updated largest received: %ui", max); + } + + pos = ack->ranges_start; + end = ack->ranges_end; + + for (i = 0; i < ack->range_count; i++) { + + n = ngx_quic_parse_ack_range(pkt, pos, end, &gap, &range); + if (n == NGX_ERROR) { + return NGX_ERROR; + } + pos += n; + + if (gap >= min) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "invalid range %ui in ack frame", i); + return NGX_ERROR; + } + + max = min - 1 - gap; + + if (range > max + 1) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "invalid range %ui in ack frame", i); + return NGX_ERROR; + } + + min = max - range + 1; + + if (ngx_quic_handle_ack_frame_range(c, ns, min, max) != NGX_OK) { + return NGX_ERROR; + } + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_namespace_t *ns, + uint64_t min, uint64_t max) +{ + ngx_uint_t found; + ngx_queue_t *q, range; + ngx_quic_frame_t *f; found = 0; @@ -1271,7 +1336,7 @@ ngx_quic_handle_ack_frame(ngx_connection f = ngx_queue_data(q, ngx_quic_frame_t, queue); - if (f->pnum >= min && f->pnum <= ack->largest) { + if (f->pnum >= min && f->pnum <= max) { q = ngx_queue_next(q); ngx_queue_remove(&f->queue); ngx_quic_free_frame(c, f); @@ -1284,9 +1349,9 @@ ngx_quic_handle_ack_frame(ngx_connection if (!found) { - if (ack->largest <= ns->pnum) { + if (max <= ns->pnum) { /* duplicate ACK or ACK for non-ack-eliciting frame */ - goto done; + return NGX_OK; } ngx_log_error(NGX_LOG_INFO, c->log, 0, @@ -1295,15 +1360,6 @@ ngx_quic_handle_ack_frame(ngx_connection return NGX_ERROR; } -done: - - /* 13.2.3. Receiver Tracking of ACK Frames */ - if (ns->largest < ack->largest) { - ns->largest = ack->largest; - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, - "updated largest received: %ui", ns->largest); - } - return NGX_OK; } diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c --- a/src/event/ngx_event_quic_transport.c +++ b/src/event/ngx_event_quic_transport.c @@ -561,9 +561,10 @@ ssize_t ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end, ngx_quic_frame_t *f) { - u_char *p; - uint8_t flags; - uint64_t varint; + u_char *p; + uint8_t flags; + uint64_t varint; + ngx_uint_t i; flags = pkt->flags; p = start; @@ -641,21 +642,19 @@ ngx_quic_parse_frame(ngx_quic_header_t * return NGX_ERROR; } - if (f->u.ack.range_count) { - p = ngx_quic_parse_int(p, end, &f->u.ack.ranges[0]); + f->u.ack.ranges_start = p; + + /* process all ranges to get bounds, values are ignored */ + for (i = 0; i < f->u.ack.range_count; i++) { + p = ngx_quic_parse_int_multi(p, end, &varint, &varint, NULL); if (p == NULL) { ngx_log_error(NGX_LOG_INFO, pkt->log, 0, - "failed to parse ack frame first range"); + "failed to parse ack frame range %ui", i); return NGX_ERROR; } } - if (f->type == NGX_QUIC_FT_ACK_ECN) { - ngx_log_error(NGX_LOG_INFO, pkt->log, 0, - "TODO: parse ECN ack frames"); - /* TODO: add parsing of such frames */ - return NGX_ERROR; - } + f->u.ack.ranges_end = p; ngx_log_debug4(NGX_LOG_DEBUG_EVENT, pkt->log, 0, "ACK: { largest=%ui delay=%ui count=%ui first=%ui}", @@ -664,6 +663,21 @@ ngx_quic_parse_frame(ngx_quic_header_t * f->u.ack.range_count, f->u.ack.first_range); + if (f->type == NGX_QUIC_FT_ACK_ECN) { + + p = ngx_quic_parse_int_multi(p, end, &f->u.ack.ect0, + &f->u.ack.ect1, &f->u.ack.ce, NULL); + if (p == NULL) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, + "failed to parse ack frame ECT counts", i); + return NGX_ERROR; + } + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pkt->log, 0, + "ACK ECN counters: %ui %ui %ui", + f->u.ack.ect0, f->u.ack.ect1, f->u.ack.ce); + } + break; case NGX_QUIC_FT_PING: @@ -1112,6 +1126,35 @@ not_allowed: ssize_t +ngx_quic_parse_ack_range(ngx_quic_header_t *pkt, u_char *start, u_char *end, + uint64_t *gap, uint64_t *range) +{ + u_char *p; + + p = start; + + p = ngx_quic_parse_int(p, end, gap); + if (p == NULL) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, + "failed to parse ack frame gap"); + return NGX_ERROR; + } + + p = ngx_quic_parse_int(p, end, range); + if (p == NULL) { + ngx_log_error(NGX_LOG_INFO, pkt->log, 0, + "failed to parse ack frame range"); + return NGX_ERROR; + } + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pkt->log, 0, + "ACK range: gap %ui range %ui", *gap, *range); + + return p - start; +} + + +ssize_t ngx_quic_create_frame(u_char *p, ngx_quic_frame_t *f) { /* diff --git a/src/event/ngx_event_quic_transport.h b/src/event/ngx_event_quic_transport.h --- a/src/event/ngx_event_quic_transport.h +++ b/src/event/ngx_event_quic_transport.h @@ -102,8 +102,11 @@ typedef struct { uint64_t delay; uint64_t range_count; uint64_t first_range; - uint64_t ranges[20]; - /* TODO: ecn counts */ + uint64_t ect0; + uint64_t ect1; + uint64_t ce; + u_char *ranges_start; + u_char *ranges_end; } ngx_quic_ack_frame_t; @@ -284,6 +287,9 @@ ssize_t ngx_quic_parse_frame(ngx_quic_he ngx_quic_frame_t *frame); ssize_t ngx_quic_create_frame(u_char *p, ngx_quic_frame_t *f); +ssize_t ngx_quic_parse_ack_range(ngx_quic_header_t *pkt, u_char *start, + u_char *end, uint64_t *gap, uint64_t *range); + ngx_int_t ngx_quic_parse_transport_params(u_char *p, u_char *end, ngx_quic_tp_t *tp, ngx_log_t *log); ssize_t ngx_quic_create_transport_params(u_char *p, u_char *end,