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
This commit is contained in:
Vladimir Homutov 2020-04-06 16:19:26 +03:00
parent c0c3a400ef
commit 88b9aed247
3 changed files with 134 additions and 29 deletions

View File

@ -134,6 +134,8 @@ static ngx_int_t ngx_quic_payload_handler(ngx_connection_t *c,
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_t *c, ngx_quic_header_t *pkt,
"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_t *c, ngx_quic_header_t *pkt,
}
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_t *c, ngx_quic_header_t *pkt,
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_t *c, ngx_quic_header_t *pkt,
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_t *c, ngx_quic_header_t *pkt,
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;
}

View File

@ -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 *pkt, u_char *start, u_char *end,
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 *pkt, u_char *start, u_char *end,
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:
@ -1111,6 +1125,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)
{

View File

@ -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_header_t *pkt, u_char *start, u_char *end,
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,