QUIC: improved path validation.

Previously, path was considered valid during arbitrary selected 10m timeout
since validation.  This is quite not what RFC 9000 says; the relevant
part is:

    An endpoint MAY skip validation of a peer address if that
    address has been seen recently.

The patch considers a path to be 'recently seen' if packets were received
during idle timeout.  If a packet is received from the path that was seen
not so recently, such path is considered new, and anti-amplification
restrictions apply.
This commit is contained in:
Vladimir Homutov 2021-12-13 17:27:29 +03:00
parent 6e7f192804
commit a31745499b
4 changed files with 15 additions and 28 deletions

View File

@ -86,6 +86,7 @@ struct ngx_quic_path_s {
socklen_t socklen; socklen_t socklen;
ngx_uint_t state; ngx_uint_t state;
ngx_msec_t expires; ngx_msec_t expires;
ngx_msec_t last_seen;
ngx_uint_t tries; ngx_uint_t tries;
off_t sent; off_t sent;
off_t received; off_t received;
@ -93,7 +94,6 @@ struct ngx_quic_path_s {
u_char challenge2[8]; u_char challenge2[8];
ngx_uint_t refcnt; ngx_uint_t refcnt;
uint64_t seqnum; uint64_t seqnum;
time_t validated_at;
ngx_str_t addr_text; ngx_str_t addr_text;
u_char text[NGX_SOCKADDR_STRLEN]; u_char text[NGX_SOCKADDR_STRLEN];
}; };

View File

@ -158,7 +158,6 @@ valid:
"quic path #%uL successfully validated", path->seqnum); "quic path #%uL successfully validated", path->seqnum);
path->state = NGX_QUIC_PATH_VALIDATED; path->state = NGX_QUIC_PATH_VALIDATED;
path->validated_at = ngx_time();
return NGX_OK; return NGX_OK;
} }
@ -217,6 +216,7 @@ ngx_quic_add_path(ngx_connection_t *c, struct sockaddr *sockaddr,
} }
path->seqnum = qc->path_seqnum++; path->seqnum = qc->path_seqnum++;
path->last_seen = ngx_current_msec;
path->socklen = socklen; path->socklen = socklen;
ngx_memcpy(path->sockaddr, sockaddr, socklen); ngx_memcpy(path->sockaddr, sockaddr, socklen);
@ -272,6 +272,7 @@ ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt)
ngx_quic_client_id_t *cid; ngx_quic_client_id_t *cid;
ngx_quic_connection_t *qc; ngx_quic_connection_t *qc;
qc = ngx_quic_get_connection(c);
qsock = ngx_quic_get_socket(c); qsock = ngx_quic_get_socket(c);
if (c->udp->dgram == NULL) { if (c->udp->dgram == NULL) {
@ -313,7 +314,6 @@ ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt)
cid = ngx_quic_used_client_id(c, path); cid = ngx_quic_used_client_id(c, path);
if (cid == NULL) { if (cid == NULL) {
qc = ngx_quic_get_connection(c);
qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR; qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR;
qc->error_reason = "no available client ids for new path"; qc->error_reason = "no available client ids for new path";
@ -328,6 +328,17 @@ ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt)
update: update:
if (path->state != NGX_QUIC_PATH_NEW) {
/* force limits/revalidation for paths that were not seen recently */
if (ngx_current_msec - path->last_seen > qc->tp.max_idle_timeout) {
path->state = NGX_QUIC_PATH_NEW;
path->sent = 0;
path->received = 0;
}
}
path->last_seen = ngx_current_msec;
len = pkt->raw->last - pkt->raw->start; len = pkt->raw->last - pkt->raw->start;
/* TODO: this may be too late in some cases; /* TODO: this may be too late in some cases;
@ -396,31 +407,10 @@ ngx_quic_handle_migration(ngx_connection_t *c, ngx_quic_header_t *pkt)
qsock->sid.seqnum, qsock->cid->seqnum, next->seqnum, qsock->sid.seqnum, qsock->cid->seqnum, next->seqnum,
ngx_quic_path_state_str(next)); ngx_quic_path_state_str(next));
switch (next->state) { if (next->state == NGX_QUIC_PATH_NEW) {
case NGX_QUIC_PATH_NEW:
if (ngx_quic_validate_path(c, qsock) != NGX_OK) { if (ngx_quic_validate_path(c, qsock) != NGX_OK) {
return NGX_ERROR; return NGX_ERROR;
} }
break;
/* migration to previously known path */
case NGX_QUIC_PATH_VALIDATING:
/* alredy validating, nothing to do */
break;
case NGX_QUIC_PATH_VALIDATED:
/* if path is old enough, revalidate */
if (ngx_time() - next->validated_at > NGX_QUIC_PATH_VALID_TIME) {
next->state = NGX_QUIC_PATH_NEW;
if (ngx_quic_validate_path(c, qsock) != NGX_OK) {
return NGX_ERROR;
}
}
break;
} }
ctx = ngx_quic_get_send_ctx(qc, pkt->level); ctx = ngx_quic_get_send_ctx(qc, pkt->level);

View File

@ -17,8 +17,6 @@
#define NGX_QUIC_PATH_VALIDATING 1 #define NGX_QUIC_PATH_VALIDATING 1
#define NGX_QUIC_PATH_VALIDATED 2 #define NGX_QUIC_PATH_VALIDATED 2
#define NGX_QUIC_PATH_VALID_TIME 600 /* seconds */
#define ngx_quic_path_state_str(p) \ #define ngx_quic_path_state_str(p) \
((p)->state == NGX_QUIC_PATH_NEW) ? "new" : \ ((p)->state == NGX_QUIC_PATH_NEW) ? "new" : \

View File

@ -82,7 +82,6 @@ ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc,
if (pkt->validated) { if (pkt->validated) {
path->state = NGX_QUIC_PATH_VALIDATED; path->state = NGX_QUIC_PATH_VALIDATED;
path->validated_at = ngx_time();
} }
/* now bind socket to client and path */ /* now bind socket to client and path */