changeset 8326:1cdd53532309 quic

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
author Vladimir Homutov <vl@nginx.com>
date Mon, 06 Apr 2020 16:19:26 +0300
parents 9b9d592c0da3
children 0ae50d90658a
files src/event/ngx_event_quic.c src/event/ngx_event_quic_transport.c src/event/ngx_event_quic_transport.h
diffstat 3 files changed, 134 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- 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;
 }
 
--- 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)
 {
     /*
--- 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,