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 ***