git: 5f4f061728d8 - main - www/nginx-devel: update HTTPv3/QUIC patch to the recent commit
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 25 Jan 2022 13:59:58 UTC
The branch main has been updated by osa: URL: https://cgit.FreeBSD.org/ports/commit/?id=5f4f061728d8515176cd51d569bec152a384ecdd commit 5f4f061728d8515176cd51d569bec152a384ecdd Author: Sergey A. Osokin <osa@FreeBSD.org> AuthorDate: 2022-01-25 13:59:22 +0000 Commit: Sergey A. Osokin <osa@FreeBSD.org> CommitDate: 2022-01-25 13:59:51 +0000 www/nginx-devel: update HTTPv3/QUIC patch to the recent commit Bump PORTREVISION. --- www/nginx-devel/Makefile | 2 +- www/nginx-devel/files/extra-patch-httpv3 | 987 +++++++++++++------------------ 2 files changed, 407 insertions(+), 582 deletions(-) diff --git a/www/nginx-devel/Makefile b/www/nginx-devel/Makefile index 2ce4b8b4fce2..6d4f2874fa9a 100644 --- a/www/nginx-devel/Makefile +++ b/www/nginx-devel/Makefile @@ -2,7 +2,7 @@ PORTNAME?= nginx PORTVERSION= 1.21.5 -PORTREVISION= 10 +PORTREVISION= 11 CATEGORIES= www MASTER_SITES= https://nginx.org/download/ \ LOCAL/osa diff --git a/www/nginx-devel/files/extra-patch-httpv3 b/www/nginx-devel/files/extra-patch-httpv3 index 4c5a4cae03df..9f0ab11e7c7c 100644 --- a/www/nginx-devel/files/extra-patch-httpv3 +++ b/www/nginx-devel/files/extra-patch-httpv3 @@ -1929,7 +1929,7 @@ diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic.c -@@ -0,0 +1,1489 @@ +@@ -0,0 +1,1491 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -2063,8 +2063,8 @@ new file mode 100644 + + qc = ngx_quic_get_connection(c); + -+ scid.data = qc->socket->cid->id; -+ scid.len = qc->socket->cid->len; ++ scid.data = qc->path->cid->id; ++ scid.len = qc->path->cid->len; + + if (scid.len != ctp->initial_scid.len + || ngx_memcmp(scid.data, ctp->initial_scid.data, scid.len) != 0) @@ -2305,7 +2305,7 @@ new file mode 100644 + { + cid = ngx_queue_data(q, ngx_quic_client_id_t, queue); + -+ if (cid->seqnum == 0 || cid->refcnt == 0) { ++ if (cid->seqnum == 0 || !cid->used) { + /* + * No stateless reset token in initial connection id. + * Don't accept a token from an unused connection id. @@ -2605,10 +2605,12 @@ new file mode 100644 + u_char *p, *start; + ngx_int_t rc; + ngx_uint_t good; ++ ngx_quic_path_t *path; + ngx_quic_header_t pkt; + ngx_quic_connection_t *qc; + + good = 0; ++ path = NULL; + + size = b->last - b->pos; + @@ -2622,6 +2624,7 @@ new file mode 100644 + pkt.len = b->last - p; + pkt.log = c->log; + pkt.first = (p == start) ? 1 : 0; ++ pkt.path = path; + pkt.flags = p[0]; + pkt.raw->pos++; + @@ -2652,6 +2655,8 @@ new file mode 100644 + good = 1; + } + ++ path = pkt.path; /* preserve packet path from 1st packet */ ++ + /* NGX_OK || NGX_DECLINED */ + + /* @@ -2757,14 +2762,15 @@ new file mode 100644 + } + + if (pkt->first) { -+ if (ngx_quic_find_path(c, c->udp->dgram->sockaddr, -+ c->udp->dgram->socklen) -+ == NULL) ++ if (ngx_cmp_sockaddr(c->udp->dgram->sockaddr, ++ c->udp->dgram->socklen, ++ qc->path->sockaddr, qc->path->socklen, 1) ++ != NGX_OK) + { + /* packet comes from unknown path, possibly migration */ + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic too early migration attempt"); -+ return NGX_DECLINED; ++ return NGX_DONE; + } + } + @@ -2923,9 +2929,12 @@ new file mode 100644 + + pkt->decrypted = 1; + -+ if (pkt->first) { -+ if (ngx_quic_update_paths(c, pkt) != NGX_OK) { -+ return NGX_ERROR; ++ c->log->action = "handling decrypted packet"; ++ ++ if (pkt->path == NULL) { ++ rc = ngx_quic_set_path(c, pkt); ++ if (rc != NGX_OK) { ++ return rc; + } + } + @@ -2944,9 +2953,10 @@ new file mode 100644 + */ + ngx_quic_discard_ctx(c, ssl_encryption_initial); + -+ if (qc->socket->path->state != NGX_QUIC_PATH_VALIDATED) { -+ qc->socket->path->state = NGX_QUIC_PATH_VALIDATED; -+ qc->socket->path->limited = 0; ++ if (!qc->path->validated) { ++ qc->path->validated = 1; ++ qc->path->limited = 0; ++ ngx_quic_path_dbg(c, "in handshake", qc->path); + ngx_post_event(&qc->push, &ngx_posted_events); + } + } @@ -3085,7 +3095,6 @@ new file mode 100644 + ngx_uint_t do_close, nonprobing; + ngx_chain_t chain; + ngx_quic_frame_t frame; -+ ngx_quic_socket_t *qsock; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); @@ -3267,7 +3276,8 @@ new file mode 100644 + + case NGX_QUIC_FT_PATH_CHALLENGE: + -+ if (ngx_quic_handle_path_challenge_frame(c, &frame.u.path_challenge) ++ if (ngx_quic_handle_path_challenge_frame(c, pkt, ++ &frame.u.path_challenge) + != NGX_OK) + { + return NGX_ERROR; @@ -3326,26 +3336,18 @@ new file mode 100644 + ngx_quic_close_connection(c, NGX_OK); + } + -+ qsock = ngx_quic_get_socket(c); -+ -+ if (qsock != qc->socket) { ++ if (pkt->path != qc->path && nonprobing) { + -+ if (qsock->path != qc->socket->path && nonprobing) { -+ /* -+ * RFC 9000, 9.2. Initiating Connection Migration -+ * -+ * An endpoint can migrate a connection to a new local -+ * address by sending packets containing non-probing frames -+ * from that address. -+ */ -+ if (ngx_quic_handle_migration(c, pkt) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ } + /* -+ * else: packet arrived via non-default socket; -+ * no reason to change active path ++ * RFC 9000, 9.2. Initiating Connection Migration ++ * ++ * An endpoint can migrate a connection to a new local ++ * address by sending packets containing non-probing frames ++ * from that address. + */ ++ if (ngx_quic_handle_migration(c, pkt) != NGX_OK) { ++ return NGX_ERROR; ++ } + } + + if (ngx_quic_ack_packet(c, pkt) != NGX_OK) { @@ -3423,7 +3425,7 @@ diff --git a/src/event/quic/ngx_event_quic.h b/src/event/quic/ngx_event_quic.h new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic.h -@@ -0,0 +1,87 @@ +@@ -0,0 +1,88 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -3466,6 +3468,7 @@ new file mode 100644 + size_t stream_buffer_size; + ngx_uint_t max_concurrent_streams_bidi; + ngx_uint_t max_concurrent_streams_uni; ++ ngx_uint_t active_connection_id_limit; + ngx_int_t stream_close_code; + ngx_int_t stream_reject_code_uni; + ngx_int_t stream_reject_code_bidi; @@ -5500,7 +5503,7 @@ diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_eve new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_connection.h -@@ -0,0 +1,274 @@ +@@ -0,0 +1,272 @@ +/* + * Copyright (C) Nginx, Inc. + */ @@ -5572,7 +5575,7 @@ new file mode 100644 + size_t len; + u_char id[NGX_QUIC_CID_LEN_MAX]; + u_char sr_token[NGX_QUIC_SR_TOKEN_LEN]; -+ ngx_uint_t refcnt; ++ ngx_uint_t used; /* unsigned used:1; */ +}; + + @@ -5586,20 +5589,22 @@ new file mode 100644 +struct ngx_quic_path_s { + ngx_queue_t queue; + struct sockaddr *sockaddr; ++ ngx_sockaddr_t sa; + socklen_t socklen; -+ ngx_uint_t state; -+ ngx_uint_t limited; /* unsigned limited:1; */ ++ ngx_quic_client_id_t *cid; + ngx_msec_t expires; -+ ngx_msec_t last_seen; + ngx_uint_t tries; ++ ngx_uint_t tag; + off_t sent; + off_t received; + u_char challenge1[8]; + u_char challenge2[8]; -+ ngx_uint_t refcnt; + uint64_t seqnum; + ngx_str_t addr_text; + u_char text[NGX_SOCKADDR_STRLEN]; ++ unsigned validated:1; ++ unsigned validating:1; ++ unsigned limited:1; +}; + + @@ -5607,11 +5612,8 @@ new file mode 100644 + ngx_udp_connection_t udp; + ngx_quic_connection_t *quic; + ngx_queue_t queue; -+ + ngx_quic_server_id_t sid; -+ -+ ngx_quic_path_t *path; -+ ngx_quic_client_id_t *cid; ++ ngx_uint_t used; /* unsigned used:1; */ +}; + + @@ -5687,8 +5689,7 @@ new file mode 100644 +struct ngx_quic_connection_s { + uint32_t version; + -+ ngx_quic_socket_t *socket; -+ ngx_quic_socket_t *backup; ++ ngx_quic_path_t *path; + + ngx_queue_t sockets; + ngx_queue_t paths; @@ -5779,7 +5780,7 @@ diff --git a/src/event/quic/ngx_event_quic_connid.c b/src/event/quic/ngx_event_q new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_connid.c -@@ -0,0 +1,613 @@ +@@ -0,0 +1,502 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -5797,13 +5798,10 @@ new file mode 100644 +#if (NGX_QUIC_BPF) +static ngx_int_t ngx_quic_bpf_attach_id(ngx_connection_t *c, u_char *id); +#endif -+static ngx_int_t ngx_quic_send_retire_connection_id(ngx_connection_t *c, -+ uint64_t seqnum); -+ ++static ngx_int_t ngx_quic_retire_client_id(ngx_connection_t *c, ++ ngx_quic_client_id_t *cid); +static ngx_quic_client_id_t *ngx_quic_alloc_client_id(ngx_connection_t *c, + ngx_quic_connection_t *qc); -+static ngx_int_t ngx_quic_replace_retired_client_id(ngx_connection_t *c, -+ ngx_quic_client_id_t *retired_cid); +static ngx_int_t ngx_quic_send_server_id(ngx_connection_t *c, + ngx_quic_server_id_t *sid); + @@ -5859,9 +5857,9 @@ new file mode 100644 +ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c, + ngx_quic_new_conn_id_frame_t *f) +{ -+ uint64_t seq; + ngx_str_t id; + ngx_queue_t *q; ++ ngx_quic_frame_t *frame; + ngx_quic_client_id_t *cid, *item; + ngx_quic_connection_t *qc; + @@ -5879,10 +5877,17 @@ new file mode 100644 + * done so for that sequence number. + */ + -+ if (ngx_quic_send_retire_connection_id(c, f->seqnum) != NGX_OK) { ++ frame = ngx_quic_alloc_frame(c); ++ if (frame == NULL) { + return NGX_ERROR; + } + ++ frame->level = ssl_encryption_application; ++ frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID; ++ frame->u.retire_cid.sequence_number = f->seqnum; ++ ++ ngx_quic_queue_frame(qc, frame); ++ + goto retire; + } + @@ -5955,20 +5960,7 @@ new file mode 100644 + continue; + } + -+ /* this connection id must be retired */ -+ seq = cid->seqnum; -+ -+ if (cid->refcnt) { -+ /* we are going to retire client id which is in use */ -+ if (ngx_quic_replace_retired_client_id(c, cid) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ } else { -+ ngx_quic_unref_client_id(c, cid); -+ } -+ -+ if (ngx_quic_send_retire_connection_id(c, seq) != NGX_OK) { ++ if (ngx_quic_retire_client_id(c, cid) != NGX_OK) { + return NGX_ERROR; + } + } @@ -5995,25 +5987,47 @@ new file mode 100644 + + +static ngx_int_t -+ngx_quic_send_retire_connection_id(ngx_connection_t *c, uint64_t seqnum) ++ngx_quic_retire_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid) +{ -+ ngx_quic_frame_t *frame; ++ ngx_queue_t *q; ++ ngx_quic_path_t *path; ++ ngx_quic_client_id_t *new_cid; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + -+ frame = ngx_quic_alloc_frame(c); -+ if (frame == NULL) { -+ return NGX_ERROR; ++ if (!cid->used) { ++ return ngx_quic_free_client_id(c, cid); + } + -+ frame->level = ssl_encryption_application; -+ frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID; -+ frame->u.retire_cid.sequence_number = seqnum; ++ /* we are going to retire client id which is in use */ + -+ ngx_quic_queue_frame(qc, frame); ++ q = ngx_queue_head(&qc->paths); + -+ /* we are no longer going to use this client id */ ++ while (q != ngx_queue_sentinel(&qc->paths)) { ++ ++ path = ngx_queue_data(q, ngx_quic_path_t, queue); ++ q = ngx_queue_next(q); ++ ++ if (path->cid != cid) { ++ continue; ++ } ++ ++ if (path == qc->path) { ++ /* this is the active path: update it with new CID */ ++ new_cid = ngx_quic_next_client_id(c); ++ if (new_cid == NULL) { ++ return NGX_ERROR; ++ } ++ ++ qc->path->cid = new_cid; ++ new_cid->used = 1; ++ ++ return ngx_quic_free_client_id(c, cid); ++ } ++ ++ return ngx_quic_free_path(c, path); ++ } + + return NGX_OK; +} @@ -6100,7 +6114,7 @@ new file mode 100644 + { + cid = ngx_queue_data(q, ngx_quic_client_id_t, queue); + -+ if (cid->refcnt == 0) { ++ if (!cid->used) { + return cid; + } + } @@ -6109,42 +6123,11 @@ new file mode 100644 +} + + -+ngx_quic_client_id_t * -+ngx_quic_used_client_id(ngx_connection_t *c, ngx_quic_path_t *path) -+{ -+ ngx_queue_t *q; -+ ngx_quic_socket_t *qsock; -+ ngx_quic_connection_t *qc; -+ -+ qc = ngx_quic_get_connection(c); -+ -+ /* best guess: cid used by active path is good for us */ -+ if (qc->socket->path == path) { -+ return qc->socket->cid; -+ } -+ -+ for (q = ngx_queue_head(&qc->sockets); -+ q != ngx_queue_sentinel(&qc->sockets); -+ q = ngx_queue_next(q)) -+ { -+ qsock = ngx_queue_data(q, ngx_quic_socket_t, queue); -+ -+ if (qsock->path && qsock->path == path) { -+ return qsock->cid; -+ } -+ } -+ -+ return NULL; -+} -+ -+ +ngx_int_t +ngx_quic_handle_retire_connection_id_frame(ngx_connection_t *c, + ngx_quic_retire_cid_frame_t *f) +{ -+ ngx_quic_path_t *path; -+ ngx_quic_socket_t *qsock, **tmp; -+ ngx_quic_client_id_t *cid; ++ ngx_quic_socket_t *qsock; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); @@ -6190,76 +6173,14 @@ new file mode 100644 + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic socket #%uL is retired", qsock->sid.seqnum); + -+ /* check if client is willing to retire sid we have in use */ -+ if (qsock->sid.seqnum == qc->socket->sid.seqnum) { -+ tmp = &qc->socket; -+ -+ } else if (qc->backup && qsock->sid.seqnum == qc->backup->sid.seqnum) { -+ tmp = &qc->backup; -+ -+ } else { -+ -+ ngx_quic_close_socket(c, qsock); -+ -+ /* restore socket count up to a limit after deletion */ -+ if (ngx_quic_create_sockets(c) != NGX_OK) { -+ return NGX_ERROR; -+ } -+ -+ return NGX_OK; -+ } -+ -+ /* preserve path/cid from retired socket */ -+ path = qsock->path; -+ cid = qsock->cid; -+ -+ /* ensure that closing_socket will not drop path and cid */ -+ path->refcnt++; -+ cid->refcnt++; -+ + ngx_quic_close_socket(c, qsock); + -+ /* restore original values */ -+ path->refcnt--; -+ cid->refcnt--; -+ + /* restore socket count up to a limit after deletion */ + if (ngx_quic_create_sockets(c) != NGX_OK) { -+ goto failed; -+ } -+ -+ qsock = ngx_quic_get_unconnected_socket(c); -+ if (qsock == NULL) { -+ qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR; -+ qc->error_reason = "not enough server IDs"; -+ goto failed; ++ return NGX_ERROR; + } + -+ ngx_quic_connect(c, qsock, path, cid); -+ -+ ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic %s socket is now #%uL:%uL:%uL (%s)", -+ (*tmp) == qc->socket ? "active" : "backup", -+ qsock->sid.seqnum, qsock->cid->seqnum, -+ qsock->path->seqnum, -+ ngx_quic_path_state_str(qsock->path)); -+ -+ /* restore active/backup pointer in quic connection */ -+ *tmp = qsock; -+ + return NGX_OK; -+ -+failed: -+ -+ /* -+ * socket was closed, path and cid were preserved artifically -+ * to be reused, but it didn't happen, thus unref here -+ */ -+ -+ ngx_quic_unref_path(c, path); -+ ngx_quic_unref_client_id(c, cid); -+ -+ return NGX_ERROR; +} + + @@ -6334,70 +6255,39 @@ new file mode 100644 +} + + -+static ngx_int_t -+ngx_quic_replace_retired_client_id(ngx_connection_t *c, -+ ngx_quic_client_id_t *retired_cid) ++ngx_int_t ++ngx_quic_free_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid) +{ -+ ngx_queue_t *q; -+ ngx_quic_socket_t *qsock; -+ ngx_quic_client_id_t *cid; ++ ngx_quic_frame_t *frame; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + -+ for (q = ngx_queue_head(&qc->sockets); -+ q != ngx_queue_sentinel(&qc->sockets); -+ q = ngx_queue_next(q)) -+ { -+ qsock = ngx_queue_data(q, ngx_quic_socket_t, queue); -+ -+ if (qsock->cid == retired_cid) { -+ -+ cid = ngx_quic_next_client_id(c); -+ if (cid == NULL) { -+ return NGX_ERROR; -+ } -+ -+ qsock->cid = cid; -+ cid->refcnt++; -+ -+ ngx_quic_unref_client_id(c, retired_cid); -+ -+ if (retired_cid->refcnt == 0) { -+ return NGX_OK; -+ } -+ } ++ frame = ngx_quic_alloc_frame(c); ++ if (frame == NULL) { ++ return NGX_ERROR; + } + -+ return NGX_OK; -+} -+ -+ -+void -+ngx_quic_unref_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid) -+{ -+ ngx_quic_connection_t *qc; -+ -+ if (cid->refcnt) { -+ cid->refcnt--; -+ } /* else: unused client id */ ++ frame->level = ssl_encryption_application; ++ frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID; ++ frame->u.retire_cid.sequence_number = cid->seqnum; + -+ if (cid->refcnt) { -+ return; -+ } ++ ngx_quic_queue_frame(qc, frame); + -+ qc = ngx_quic_get_connection(c); ++ /* we are no longer going to use this client id */ + + ngx_queue_remove(&cid->queue); + ngx_queue_insert_head(&qc->free_client_ids, &cid->queue); + + qc->nclient_ids--; ++ ++ return NGX_OK; +} diff --git a/src/event/quic/ngx_event_quic_connid.h b/src/event/quic/ngx_event_quic_connid.h new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_connid.h -@@ -0,0 +1,30 @@ +@@ -0,0 +1,29 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -6423,16 +6313,15 @@ new file mode 100644 +ngx_quic_client_id_t *ngx_quic_create_client_id(ngx_connection_t *c, + ngx_str_t *id, uint64_t seqnum, u_char *token); +ngx_quic_client_id_t *ngx_quic_next_client_id(ngx_connection_t *c); -+ngx_quic_client_id_t *ngx_quic_used_client_id(ngx_connection_t *c, -+ ngx_quic_path_t *path); -+void ngx_quic_unref_client_id(ngx_connection_t *c, ngx_quic_client_id_t *cid); ++ngx_int_t ngx_quic_free_client_id(ngx_connection_t *c, ++ ngx_quic_client_id_t *cid); + +#endif /* _NGX_EVENT_QUIC_CONNID_H_INCLUDED_ */ diff --git a/src/event/quic/ngx_event_quic_frames.c b/src/event/quic/ngx_event_quic_frames.c new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_frames.c -@@ -0,0 +1,811 @@ +@@ -0,0 +1,813 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -6971,14 +6860,16 @@ new file mode 100644 + continue; + } + -+ for (p = b->pos + offset; p != b->last && in; /* void */ ) { ++ p = b->pos + offset; ++ ++ while (in) { + + if (!ngx_buf_in_memory(in->buf) || in->buf->pos == in->buf->last) { + in = in->next; + continue; + } + -+ if (limit == 0) { ++ if (p == b->last || limit == 0) { + break; + } + @@ -7295,7 +7186,7 @@ diff --git a/src/event/quic/ngx_event_quic_migration.c b/src/event/quic/ngx_even new file mode 100644 --- /dev/null +++ b/src/event/quic/ngx_event_quic_migration.c -@@ -0,0 +1,689 @@ +@@ -0,0 +1,672 @@ + +/* + * Copyright (C) Nginx, Inc. @@ -7314,17 +7205,14 @@ new file mode 100644 + ngx_quic_path_t *path); +static ngx_int_t ngx_quic_send_path_challenge(ngx_connection_t *c, + ngx_quic_path_t *path); -+static ngx_int_t ngx_quic_path_restore(ngx_connection_t *c); -+static ngx_quic_path_t *ngx_quic_alloc_path(ngx_connection_t *c); ++static ngx_quic_path_t *ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag); + + +ngx_int_t +ngx_quic_handle_path_challenge_frame(ngx_connection_t *c, -+ ngx_quic_path_challenge_frame_t *f) ++ ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f) +{ -+ ngx_quic_path_t *path; + ngx_quic_frame_t frame, *fp; -+ ngx_quic_socket_t *qsock; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); @@ -7341,18 +7229,16 @@ new file mode 100644 + * A PATH_RESPONSE frame MUST be sent on the network path where the + * PATH_CHALLENGE frame was received. + */ -+ qsock = ngx_quic_get_socket(c); -+ path = qsock->path; + + /* + * An endpoint MUST expand datagrams that contain a PATH_RESPONSE frame + * to at least the smallest allowed maximum datagram size of 1200 bytes. + */ -+ if (ngx_quic_frame_sendto(c, &frame, 1200, path) != NGX_OK) { ++ if (ngx_quic_frame_sendto(c, &frame, 1200, pkt->path) != NGX_OK) { + return NGX_ERROR; + } + -+ if (qsock == qc->socket) { ++ if (pkt->path == qc->path) { + /* + * RFC 9000, 9.3.3. Off-Path Packet Forwarding + * @@ -7399,7 +7285,7 @@ new file mode 100644 + { + path = ngx_queue_data(q, ngx_quic_path_t, queue); + -+ if (path->state != NGX_QUIC_PATH_VALIDATING) { ++ if (!path->validating) { + continue; + } + @@ -7410,7 +7296,7 @@ new file mode 100644 + } + } + -+ ngx_log_error(NGX_LOG_INFO, c->log, 0, ++ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "quic stale PATH_RESPONSE ignored"); + + return NGX_OK; @@ -7428,8 +7314,9 @@ new file mode 100644 + + rst = 1; + -+ if (qc->backup) { -+ prev = qc->backup->path; ++ prev = ngx_quic_get_path(c, NGX_QUIC_PATH_BACKUP); ++ ++ if (prev != NULL) { + + if (ngx_cmp_sockaddr(prev->sockaddr, prev->socklen, + path->sockaddr, path->socklen, 0) @@ -7462,20 +7349,24 @@ new file mode 100644 + } + + ngx_log_error(NGX_LOG_INFO, c->log, 0, -+ "quic path #%uL successfully validated", path->seqnum); ++ "quic path #%uL addr:%V successfully validated", ++ path->seqnum, &path->addr_text); ++ ++ ngx_quic_path_dbg(c, "is validated", path); + -+ path->state = NGX_QUIC_PATH_VALIDATED; ++ path->validated = 1; ++ path->validating = 0; + path->limited = 0; + + return NGX_OK; +} + + -+static ngx_quic_path_t * -+ngx_quic_alloc_path(ngx_connection_t *c) ++ngx_quic_path_t * ++ngx_quic_new_path(ngx_connection_t *c, ++ struct sockaddr *sockaddr, socklen_t socklen, ngx_quic_client_id_t *cid) +{ + ngx_queue_t *q; -+ struct sockaddr *sa; + ngx_quic_path_t *path; + ngx_quic_connection_t *qc; + @@ -7488,9 +7379,7 @@ new file mode 100644 + + ngx_queue_remove(&path->queue); + -+ sa = path->sockaddr; + ngx_memzero(path, sizeof(ngx_quic_path_t)); -+ path->sockaddr = sa; + + } else { + @@ -7498,37 +7387,18 @@ new file mode 100644 + if (path == NULL) { + return NULL; + } -+ -+ path->sockaddr = ngx_palloc(c->pool, NGX_SOCKADDRLEN); -+ if (path->sockaddr == NULL) { -+ return NULL; -+ } + } + -+ return path; -+} -+ -+ -+ngx_quic_path_t * -+ngx_quic_add_path(ngx_connection_t *c, struct sockaddr *sockaddr, -+ socklen_t socklen) -+{ -+ ngx_quic_path_t *path; -+ ngx_quic_connection_t *qc; -+ -+ qc = ngx_quic_get_connection(c); ++ ngx_queue_insert_tail(&qc->paths, &path->queue); + -+ path = ngx_quic_alloc_path(c); -+ if (path == NULL) { -+ return NULL; -+ } ++ path->cid = cid; ++ cid->used = 1; + -+ path->state = NGX_QUIC_PATH_NEW; + path->limited = 1; + + path->seqnum = qc->path_seqnum++; -+ path->last_seen = ngx_current_msec; + ++ path->sockaddr = &path->sa.sockaddr; + path->socklen = socklen; + ngx_memcpy(path->sockaddr, sockaddr, socklen); + @@ -7536,19 +7406,15 @@ new file mode 100644 + path->addr_text.len = ngx_sock_ntop(sockaddr, socklen, path->text, + NGX_SOCKADDR_STRLEN, 1); + -+ ngx_queue_insert_tail(&qc->paths, &path->queue); -+ + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, -+ "quic path #%uL created src:%V", ++ "quic path #%uL created addr:%V", + path->seqnum, &path->addr_text); -+ + return path; +} + + -+ngx_quic_path_t * -+ngx_quic_find_path(ngx_connection_t *c, struct sockaddr *sockaddr, -+ socklen_t socklen) ++static ngx_quic_path_t * ++ngx_quic_get_path(ngx_connection_t *c, ngx_uint_t tag) +{ + ngx_queue_t *q; + ngx_quic_path_t *path; @@ -7562,10 +7428,7 @@ new file mode 100644 + { + path = ngx_queue_data(q, ngx_quic_path_t, queue); + -+ if (ngx_cmp_sockaddr(sockaddr, socklen, -+ path->sockaddr, path->socklen, 1) -+ == NGX_OK) -+ { ++ if (path->tag == tag) { + return path; + } + } @@ -7575,83 +7438,92 @@ new file mode 100644 + + +ngx_int_t -+ngx_quic_update_paths(ngx_connection_t *c, ngx_quic_header_t *pkt) ++ngx_quic_set_path(ngx_connection_t *c, ngx_quic_header_t *pkt) +{ + off_t len; -+ ngx_quic_path_t *path; ++ ngx_queue_t *q; ++ ngx_quic_path_t *path, *probe; + ngx_quic_socket_t *qsock; ++ ngx_quic_send_ctx_t *ctx; + ngx_quic_client_id_t *cid; + ngx_quic_connection_t *qc; + + qc = ngx_quic_get_connection(c); + qsock = ngx_quic_get_socket(c); + ++ len = pkt->raw->last - pkt->raw->start; ++ + if (c->udp->dgram == NULL) { -+ /* 1st ever packet in connection, path already exists */ -+ path = qsock->path; ++ /* first ever packet in connection, path already exists */ ++ path = qc->path; + goto update; + } + -+ path = ngx_quic_find_path(c, c->udp->dgram->sockaddr, -+ c->udp->dgram->socklen); -+ -+ if (path == NULL) { -+ path = ngx_quic_add_path(c, c->udp->dgram->sockaddr, -+ c->udp->dgram->socklen); -+ if (path == NULL) { -+ return NGX_ERROR; -+ } -+ -+ if (qsock->path) { -+ /* NAT rebinding case: packet to same CID, but from new address */ ++ probe = NULL; + -+ ngx_quic_unref_path(c, qsock->path); -+ -+ qsock->path = path; -+ path->refcnt++; ++ for (q = ngx_queue_head(&qc->paths); ++ q != ngx_queue_sentinel(&qc->paths); ++ q = ngx_queue_next(q)) ++ { ++ path = ngx_queue_data(q, ngx_quic_path_t, queue); + ++ if (ngx_cmp_sockaddr(c->udp->dgram->sockaddr, c->udp->dgram->socklen, ++ path->sockaddr, path->socklen, 1) ++ == NGX_OK) ++ { + goto update; + } + -+ } else if (qsock->path) { -+ goto update; ++ if (path->tag == NGX_QUIC_PATH_PROBE) { ++ probe = path; ++ } + } + -+ /* prefer unused client IDs if available */ -+ cid = ngx_quic_next_client_id(c); -+ if (cid == NULL) { ++ /* packet from new path, drop current probe, if any */ + -+ /* try to reuse connection ID used on the same path */ -+ cid = ngx_quic_used_client_id(c, path); -+ if (cid == NULL) { ++ ctx = ngx_quic_get_send_ctx(qc, pkt->level); + -+ qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR; -+ qc->error_reason = "no available client ids for new path"; ++ /* ++ * only accept highest-numbered packets to prevent connection id ++ * exhaustion by excessive probing packets from unknown paths ++ */ ++ if (pkt->pn != ctx->largest_pn) { ++ return NGX_DONE; ++ } + -+ ngx_log_error(NGX_LOG_ERR, c->log, 0, -+ "no available client ids for new path"); ++ if (probe && ngx_quic_free_path(c, probe) != NGX_OK) { ++ return NGX_ERROR; ++ } + -+ return NGX_ERROR; -+ } ++ /* new path requires new client id */ ++ cid = ngx_quic_next_client_id(c); ++ if (cid == NULL) { ++ ngx_log_error(NGX_LOG_ERR, c->log, 0, ++ "quic no available client ids for new path"); ++ /* stop processing of this datagram */ ++ return NGX_DONE; + } + -+ ngx_quic_connect(c, qsock, path, cid); ++ path = ngx_quic_new_path(c, c->udp->dgram->sockaddr, ++ c->udp->dgram->socklen, cid); ++ if (path == NULL) { ++ return NGX_ERROR; ++ } + -+update: ++ path->tag = NGX_QUIC_PATH_PROBE; + -+ 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) { *** 878 LINES SKIPPED ***