changeset 9017:c2f5d79cde64 quic

QUIC: separate UDP framework for QUIC. Previously, QUIC used the existing UDP framework, which was created for UDP in Stream. However the way QUIC connections are created and looked up is different from the way UDP connections in Stream are created and looked up. Now these two implementations are decoupled.
author Roman Arutyunyan <arut@nginx.com>
date Wed, 20 Apr 2022 16:01:17 +0400
parents 55b38514729b
children 5b1011b5702b
files auto/modules src/core/ngx_connection.c src/event/ngx_event.c src/event/ngx_event_udp.c src/event/ngx_event_udp.h src/event/quic/ngx_event_quic.c src/event/quic/ngx_event_quic.h src/event/quic/ngx_event_quic_connection.h src/event/quic/ngx_event_quic_migration.c src/event/quic/ngx_event_quic_socket.c src/event/quic/ngx_event_quic_udp.c src/http/ngx_http.c src/stream/ngx_stream.c
diffstat 13 files changed, 585 insertions(+), 142 deletions(-) [+]
line wrap: on
line diff
--- a/auto/modules	Thu Feb 17 22:38:42 2022 +0300
+++ b/auto/modules	Wed Apr 20 16:01:17 2022 +0400
@@ -1344,6 +1344,7 @@
                      src/event/quic/ngx_event_quic_output.h \
                      src/event/quic/ngx_event_quic_socket.h"
     ngx_module_srcs="src/event/quic/ngx_event_quic.c \
+                     src/event/quic/ngx_event_quic_udp.c \
                      src/event/quic/ngx_event_quic_transport.c \
                      src/event/quic/ngx_event_quic_protection.c \
                      src/event/quic/ngx_event_quic_frames.c \
--- a/src/core/ngx_connection.c	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/core/ngx_connection.c	Wed Apr 20 16:01:17 2022 +0400
@@ -72,10 +72,6 @@
 
     ngx_memcpy(ls->addr_text.data, text, len);
 
-#if !(NGX_WIN32)
-    ngx_rbtree_init(&ls->rbtree, &ls->sentinel, ngx_udp_rbtree_insert_value);
-#endif
-
     ls->fd = (ngx_socket_t) -1;
     ls->type = SOCK_STREAM;
 
--- a/src/event/ngx_event.c	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/event/ngx_event.c	Wed Apr 20 16:01:17 2022 +0400
@@ -886,8 +886,16 @@
 
 #else
 
-        rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
-                                                : ngx_event_recvmsg;
+        if (c->type == SOCK_STREAM) {
+            rev->handler = ngx_event_accept;
+
+#if (NGX_QUIC)
+        } else if (ls[i].quic) {
+            rev->handler = ngx_quic_recvmsg;
+#endif
+        } else {
+            rev->handler = ngx_event_recvmsg;
+        }
 
 #if (NGX_HAVE_REUSEPORT)
 
--- a/src/event/ngx_event_udp.c	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/event/ngx_event_udp.c	Wed Apr 20 16:01:17 2022 +0400
@@ -15,27 +15,25 @@
 static void ngx_close_accepted_udp_connection(ngx_connection_t *c);
 static ssize_t ngx_udp_shared_recv(ngx_connection_t *c, u_char *buf,
     size_t size);
-static ngx_int_t ngx_create_udp_connection(ngx_connection_t *c);
+static ngx_int_t ngx_insert_udp_connection(ngx_connection_t *c);
 static ngx_connection_t *ngx_lookup_udp_connection(ngx_listening_t *ls,
-    ngx_str_t *key, struct sockaddr *local_sockaddr, socklen_t local_socklen);
+    struct sockaddr *sockaddr, socklen_t socklen,
+    struct sockaddr *local_sockaddr, socklen_t local_socklen);
 
 
 void
 ngx_event_recvmsg(ngx_event_t *ev)
 {
-    size_t             len;
     ssize_t            n;
-    ngx_str_t          key;
     ngx_buf_t          buf;
     ngx_log_t         *log;
     ngx_err_t          err;
-    socklen_t          local_socklen;
+    socklen_t          socklen, local_socklen;
     ngx_event_t       *rev, *wev;
     struct iovec       iov[1];
     struct msghdr      msg;
     ngx_sockaddr_t     sa, lsa;
-    ngx_udp_dgram_t    dgram;
-    struct sockaddr   *local_sockaddr;
+    struct sockaddr   *sockaddr, *local_sockaddr;
     ngx_listening_t   *ls;
     ngx_event_conf_t  *ecf;
     ngx_connection_t  *c, *lc;
@@ -110,21 +108,21 @@
         }
 #endif
 
-        dgram.sockaddr = msg.msg_name;
-        dgram.socklen = msg.msg_namelen;
+        sockaddr = msg.msg_name;
+        socklen = msg.msg_namelen;
 
-        if (dgram.socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
-            dgram.socklen = sizeof(ngx_sockaddr_t);
+        if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
+            socklen = sizeof(ngx_sockaddr_t);
         }
 
-        if (dgram.socklen == 0) {
+        if (socklen == 0) {
 
             /*
              * on Linux recvmsg() returns zero msg_namelen
              * when receiving packets from unbound AF_UNIX sockets
              */
 
-            dgram.socklen = sizeof(struct sockaddr);
+            socklen = sizeof(struct sockaddr);
             ngx_memzero(&sa, sizeof(struct sockaddr));
             sa.sockaddr.sa_family = ls->sockaddr->sa_family;
         }
@@ -152,35 +150,8 @@
 
 #endif
 
-        key.data = (u_char *) dgram.sockaddr;
-        key.len = dgram.socklen;
-
-#if (NGX_HAVE_UNIX_DOMAIN)
-
-        if (dgram.sockaddr->sa_family == AF_UNIX) {
-            struct sockaddr_un *saun = (struct sockaddr_un *) dgram.sockaddr;
-
-            if (dgram.socklen <= (socklen_t) offsetof(struct sockaddr_un,
-                                                      sun_path)
-                || saun->sun_path[0] == '\0')
-            {
-                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
-                               "unbound unix socket");
-                key.len = 0;
-            }
-        }
-
-#endif
-
-#if (NGX_QUIC)
-        if (ls->quic) {
-            if (ngx_quic_get_packet_dcid(ev->log, buffer, n, &key) != NGX_OK) {
-                goto next;
-            }
-        }
-#endif
-
-        c = ngx_lookup_udp_connection(ls, &key, local_sockaddr, local_socklen);
+        c = ngx_lookup_udp_connection(ls, sockaddr, socklen, local_sockaddr,
+                                      local_socklen);
 
         if (c) {
 
@@ -202,14 +173,10 @@
 
             buf.pos = buffer;
             buf.last = buffer + n;
-            buf.start = buf.pos;
-            buf.end = buffer + sizeof(buffer);
 
             rev = c->read;
 
-            dgram.buffer = &buf;
-
-            c->udp->dgram = &dgram;
+            c->udp->buffer = &buf;
 
             rev->ready = 1;
             rev->active = 0;
@@ -217,7 +184,7 @@
             rev->handler(rev);
 
             if (c->udp) {
-                c->udp->dgram = NULL;
+                c->udp->buffer = NULL;
             }
 
             rev->ready = 0;
@@ -240,7 +207,7 @@
 
         c->shared = 1;
         c->type = SOCK_DGRAM;
-        c->socklen = dgram.socklen;
+        c->socklen = socklen;
 
 #if (NGX_STAT_STUB)
         (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
@@ -252,21 +219,13 @@
             return;
         }
 
-        len = dgram.socklen;
-
-#if (NGX_QUIC)
-        if (ls->quic) {
-            len = NGX_SOCKADDRLEN;
-        }
-#endif
-
-        c->sockaddr = ngx_palloc(c->pool, len);
+        c->sockaddr = ngx_palloc(c->pool, socklen);
         if (c->sockaddr == NULL) {
             ngx_close_accepted_udp_connection(c);
             return;
         }
 
-        ngx_memcpy(c->sockaddr, dgram.sockaddr, dgram.socklen);
+        ngx_memcpy(c->sockaddr, sockaddr, socklen);
 
         log = ngx_palloc(c->pool, sizeof(ngx_log_t));
         if (log == NULL) {
@@ -369,7 +328,7 @@
         }
 #endif
 
-        if (ngx_create_udp_connection(c) != NGX_OK) {
+        if (ngx_insert_udp_connection(c) != NGX_OK) {
             ngx_close_accepted_udp_connection(c);
             return;
         }
@@ -412,17 +371,17 @@
     ssize_t     n;
     ngx_buf_t  *b;
 
-    if (c->udp == NULL || c->udp->dgram == NULL) {
+    if (c->udp == NULL || c->udp->buffer == NULL) {
         return NGX_AGAIN;
     }
 
-    b = c->udp->dgram->buffer;
+    b = c->udp->buffer;
 
     n = ngx_min(b->last - b->pos, (ssize_t) size);
 
     ngx_memcpy(buf, b->pos, n);
 
-    c->udp->dgram = NULL;
+    c->udp->buffer = NULL;
 
     c->read->ready = 0;
     c->read->active = 1;
@@ -458,8 +417,8 @@
             udpt = (ngx_udp_connection_t *) temp;
             ct = udpt->connection;
 
-            rc = ngx_memn2cmp(udp->key.data, udpt->key.data,
-                              udp->key.len, udpt->key.len);
+            rc = ngx_cmp_sockaddr(c->sockaddr, c->socklen,
+                                  ct->sockaddr, ct->socklen, 1);
 
             if (rc == 0 && c->listening->wildcard) {
                 rc = ngx_cmp_sockaddr(c->local_sockaddr, c->local_socklen,
@@ -485,18 +444,12 @@
 
 
 static ngx_int_t
-ngx_create_udp_connection(ngx_connection_t *c)
+ngx_insert_udp_connection(ngx_connection_t *c)
 {
-    ngx_str_t              key;
+    uint32_t               hash;
     ngx_pool_cleanup_t    *cln;
     ngx_udp_connection_t  *udp;
 
-#if (NGX_QUIC)
-    if (c->listening->quic) {
-        return NGX_OK;
-    }
-#endif
-
     if (c->udp) {
         return NGX_OK;
     }
@@ -506,6 +459,19 @@
         return NGX_ERROR;
     }
 
+    udp->connection = c;
+
+    ngx_crc32_init(hash);
+    ngx_crc32_update(&hash, (u_char *) c->sockaddr, c->socklen);
+
+    if (c->listening->wildcard) {
+        ngx_crc32_update(&hash, (u_char *) c->local_sockaddr, c->local_socklen);
+    }
+
+    ngx_crc32_final(hash);
+
+    udp->node.key = hash;
+
     cln = ngx_pool_cleanup_add(c->pool, 0);
     if (cln == NULL) {
         return NGX_ERROR;
@@ -514,10 +480,7 @@
     cln->data = c;
     cln->handler = ngx_delete_udp_connection;
 
-    key.data = (u_char *) c->sockaddr;
-    key.len = c->socklen;
-
-    ngx_insert_udp_connection(c, udp, &key);
+    ngx_rbtree_insert(&c->listening->rbtree, &udp->node);
 
     c->udp = udp;
 
@@ -526,30 +489,6 @@
 
 
 void
-ngx_insert_udp_connection(ngx_connection_t *c, ngx_udp_connection_t *udp,
-    ngx_str_t *key)
-{
-    uint32_t  hash;
-
-    ngx_crc32_init(hash);
-
-    ngx_crc32_update(&hash, key->data, key->len);
-
-    if (c->listening->wildcard) {
-        ngx_crc32_update(&hash, (u_char *) c->local_sockaddr, c->local_socklen);
-    }
-
-    ngx_crc32_final(hash);
-
-    udp->connection = c;
-    udp->key = *key;
-    udp->node.key = hash;
-
-    ngx_rbtree_insert(&c->listening->rbtree, &udp->node);
-}
-
-
-void
 ngx_delete_udp_connection(void *data)
 {
     ngx_connection_t  *c = data;
@@ -565,8 +504,8 @@
 
 
 static ngx_connection_t *
-ngx_lookup_udp_connection(ngx_listening_t *ls, ngx_str_t *key,
-    struct sockaddr *local_sockaddr, socklen_t local_socklen)
+ngx_lookup_udp_connection(ngx_listening_t *ls, struct sockaddr *sockaddr,
+    socklen_t socklen, struct sockaddr *local_sockaddr, socklen_t local_socklen)
 {
     uint32_t               hash;
     ngx_int_t              rc;
@@ -574,15 +513,27 @@
     ngx_rbtree_node_t     *node, *sentinel;
     ngx_udp_connection_t  *udp;
 
-    if (key->len == 0) {
-        return NULL;
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+    if (sockaddr->sa_family == AF_UNIX) {
+        struct sockaddr_un *saun = (struct sockaddr_un *) sockaddr;
+
+        if (socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)
+            || saun->sun_path[0] == '\0')
+        {
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
+                           "unbound unix socket");
+            return NULL;
+        }
     }
 
+#endif
+
     node = ls->rbtree.root;
     sentinel = ls->rbtree.sentinel;
 
     ngx_crc32_init(hash);
-    ngx_crc32_update(&hash, key->data, key->len);
+    ngx_crc32_update(&hash, (u_char *) sockaddr, socklen);
 
     if (ls->wildcard) {
         ngx_crc32_update(&hash, (u_char *) local_sockaddr, local_socklen);
@@ -608,7 +559,8 @@
 
         c = udp->connection;
 
-        rc = ngx_memn2cmp(key->data, udp->key.data, key->len, udp->key.len);
+        rc = ngx_cmp_sockaddr(sockaddr, socklen,
+                              c->sockaddr, c->socklen, 1);
 
         if (rc == 0 && ls->wildcard) {
             rc = ngx_cmp_sockaddr(local_sockaddr, local_socklen,
@@ -616,13 +568,6 @@
         }
 
         if (rc == 0) {
-
-#if (NGX_QUIC)
-            if (ls->quic && c->udp != udp) {
-                c->udp = udp;
-            }
-#endif
-
             return c;
         }
 
--- a/src/event/ngx_event_udp.h	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/event/ngx_event_udp.h	Wed Apr 20 16:01:17 2022 +0400
@@ -23,18 +23,10 @@
 #endif
 
 
-typedef struct {
-    ngx_buf_t                 *buffer;
-    struct sockaddr           *sockaddr;
-    socklen_t                  socklen;
-} ngx_udp_dgram_t;
-
-
 struct ngx_udp_connection_s {
-    ngx_rbtree_node_t          node;
-    ngx_connection_t          *connection;
-    ngx_str_t                  key;
-    ngx_udp_dgram_t           *dgram;
+    ngx_rbtree_node_t   node;
+    ngx_connection_t   *connection;
+    ngx_buf_t          *buffer;
 };
 
 
@@ -65,9 +57,6 @@
 ssize_t ngx_sendmsg(ngx_connection_t *c, struct msghdr *msg, int flags);
 void ngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
-void ngx_insert_udp_connection(ngx_connection_t *c, ngx_udp_connection_t *udp,
-    ngx_str_t *key);
-
 #endif
 
 void ngx_delete_udp_connection(void *data);
--- a/src/event/quic/ngx_event_quic.c	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/event/quic/ngx_event_quic.c	Wed Apr 20 16:01:17 2022 +0400
@@ -430,7 +430,7 @@
         return;
     }
 
-    b = c->udp->dgram->buffer;
+    b = c->udp->buffer;
 
     rc = ngx_quic_handle_datagram(c, b, NULL);
 
@@ -758,6 +758,7 @@
     ngx_quic_header_t *pkt)
 {
     ngx_int_t               rc;
+    ngx_quic_socket_t      *qsock;
     ngx_quic_connection_t  *qc;
 
     c->log->action = "parsing quic packet";
@@ -809,8 +810,9 @@
             }
 
             if (pkt->first) {
-                if (ngx_cmp_sockaddr(c->udp->dgram->sockaddr,
-                                     c->udp->dgram->socklen,
+                qsock = ngx_quic_get_socket(c);
+
+                if (ngx_cmp_sockaddr(&qsock->sockaddr.sockaddr, qsock->socklen,
                                      qc->path->sockaddr, qc->path->socklen, 1)
                     != NGX_OK)
                 {
--- a/src/event/quic/ngx_event_quic.h	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/event/quic/ngx_event_quic.h	Wed Apr 20 16:01:17 2022 +0400
@@ -102,6 +102,9 @@
 };
 
 
+void ngx_quic_recvmsg(ngx_event_t *ev);
+void ngx_quic_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
 void ngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf);
 ngx_connection_t *ngx_quic_open_stream(ngx_connection_t *c, ngx_uint_t bidi);
 void ngx_quic_finalize_connection(ngx_connection_t *c, ngx_uint_t err,
--- a/src/event/quic/ngx_event_quic_connection.h	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/event/quic/ngx_event_quic_connection.h	Wed Apr 20 16:01:17 2022 +0400
@@ -107,6 +107,8 @@
     ngx_quic_connection_t            *quic;
     ngx_queue_t                       queue;
     ngx_quic_server_id_t              sid;
+    ngx_sockaddr_t                    sockaddr;
+    socklen_t                         socklen;
     ngx_uint_t                        used; /* unsigned  used:1; */
 };
 
--- a/src/event/quic/ngx_event_quic_migration.c	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/event/quic/ngx_event_quic_migration.c	Wed Apr 20 16:01:17 2022 +0400
@@ -264,7 +264,7 @@
 
     len = pkt->raw->last - pkt->raw->start;
 
-    if (c->udp->dgram == NULL) {
+    if (c->udp->buffer == NULL) {
         /* first ever packet in connection, path already exists  */
         path = qc->path;
         goto update;
@@ -278,7 +278,7 @@
     {
         path = ngx_queue_data(q, ngx_quic_path_t, queue);
 
-        if (ngx_cmp_sockaddr(c->udp->dgram->sockaddr, c->udp->dgram->socklen,
+        if (ngx_cmp_sockaddr(&qsock->sockaddr.sockaddr, qsock->socklen,
                              path->sockaddr, path->socklen, 1)
             == NGX_OK)
         {
@@ -315,8 +315,7 @@
         return NGX_DONE;
     }
 
-    path = ngx_quic_new_path(c, c->udp->dgram->sockaddr,
-                             c->udp->dgram->socklen, cid);
+    path = ngx_quic_new_path(c, &qsock->sockaddr.sockaddr, qsock->socklen, cid);
     if (path == NULL) {
         return NGX_ERROR;
     }
--- a/src/event/quic/ngx_event_quic_socket.c	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/event/quic/ngx_event_quic_socket.c	Wed Apr 20 16:01:17 2022 +0400
@@ -177,7 +177,10 @@
     id.data = sid->id;
     id.len = sid->len;
 
-    ngx_insert_udp_connection(c, &qsock->udp, &id);
+    qsock->udp.connection = c;
+    qsock->udp.node.key = ngx_crc32_long(id.data, id.len);
+
+    ngx_rbtree_insert(&c->listening->rbtree, &qsock->udp.node);
 
     ngx_queue_insert_tail(&qc->sockets, &qsock->queue);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/event/quic/ngx_event_quic_udp.c	Wed Apr 20 16:01:17 2022 +0400
@@ -0,0 +1,473 @@
+
+/*
+ * Copyright (C) Roman Arutyunyan
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_quic_connection.h>
+
+
+static void ngx_quic_close_accepted_connection(ngx_connection_t *c);
+static ngx_connection_t *ngx_quic_lookup_connection(ngx_listening_t *ls,
+    ngx_str_t *key, struct sockaddr *local_sockaddr, socklen_t local_socklen);
+
+
+void
+ngx_quic_recvmsg(ngx_event_t *ev)
+{
+    ssize_t             n;
+    ngx_str_t           key;
+    ngx_buf_t           buf;
+    ngx_log_t          *log;
+    ngx_err_t           err;
+    socklen_t           socklen, local_socklen;
+    ngx_event_t        *rev, *wev;
+    struct iovec        iov[1];
+    struct msghdr       msg;
+    ngx_sockaddr_t      sa, lsa;
+    struct sockaddr    *sockaddr, *local_sockaddr;
+    ngx_listening_t    *ls;
+    ngx_event_conf_t   *ecf;
+    ngx_connection_t   *c, *lc;
+    ngx_quic_socket_t  *qsock;
+    static u_char       buffer[65535];
+
+#if (NGX_HAVE_ADDRINFO_CMSG)
+    u_char             msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))];
+#endif
+
+    if (ev->timedout) {
+        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {
+            return;
+        }
+
+        ev->timedout = 0;
+    }
+
+    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);
+
+    if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
+        ev->available = ecf->multi_accept;
+    }
+
+    lc = ev->data;
+    ls = lc->listening;
+    ev->ready = 0;
+
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                   "quic recvmsg on %V, ready: %d",
+                   &ls->addr_text, ev->available);
+
+    do {
+        ngx_memzero(&msg, sizeof(struct msghdr));
+
+        iov[0].iov_base = (void *) buffer;
+        iov[0].iov_len = sizeof(buffer);
+
+        msg.msg_name = &sa;
+        msg.msg_namelen = sizeof(ngx_sockaddr_t);
+        msg.msg_iov = iov;
+        msg.msg_iovlen = 1;
+
+#if (NGX_HAVE_ADDRINFO_CMSG)
+        if (ls->wildcard) {
+            msg.msg_control = &msg_control;
+            msg.msg_controllen = sizeof(msg_control);
+
+            ngx_memzero(&msg_control, sizeof(msg_control));
+       }
+#endif
+
+        n = recvmsg(lc->fd, &msg, 0);
+
+        if (n == -1) {
+            err = ngx_socket_errno;
+
+            if (err == NGX_EAGAIN) {
+                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,
+                               "quic recvmsg() not ready");
+                return;
+            }
+
+            ngx_log_error(NGX_LOG_ALERT, ev->log, err, "quic recvmsg() failed");
+
+            return;
+        }
+
+#if (NGX_HAVE_ADDRINFO_CMSG)
+        if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
+            ngx_log_error(NGX_LOG_ALERT, ev->log, 0,
+                          "quic recvmsg() truncated data");
+            continue;
+        }
+#endif
+
+        sockaddr = msg.msg_name;
+        socklen = msg.msg_namelen;
+
+        if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {
+            socklen = sizeof(ngx_sockaddr_t);
+        }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+        if (sockaddr->sa_family == AF_UNIX) {
+            struct sockaddr_un *saun = (struct sockaddr_un *) sockaddr;
+
+            if (socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)
+                || saun->sun_path[0] == '\0')
+            {
+                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
+                               "unbound unix socket");
+                goto next;
+            }
+        }
+
+#endif
+
+        local_sockaddr = ls->sockaddr;
+        local_socklen = ls->socklen;
+
+#if (NGX_HAVE_ADDRINFO_CMSG)
+
+        if (ls->wildcard) {
+            struct cmsghdr  *cmsg;
+
+            ngx_memcpy(&lsa, local_sockaddr, local_socklen);
+            local_sockaddr = &lsa.sockaddr;
+
+            for (cmsg = CMSG_FIRSTHDR(&msg);
+                 cmsg != NULL;
+                 cmsg = CMSG_NXTHDR(&msg, cmsg))
+            {
+                if (ngx_get_srcaddr_cmsg(cmsg, local_sockaddr) == NGX_OK) {
+                    break;
+                }
+            }
+        }
+
+#endif
+
+        if (ngx_quic_get_packet_dcid(ev->log, buffer, n, &key) != NGX_OK) {
+            goto next;
+        }
+
+        c = ngx_quic_lookup_connection(ls, &key, local_sockaddr, local_socklen);
+
+        if (c) {
+
+#if (NGX_DEBUG)
+            if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {
+                ngx_log_handler_pt  handler;
+
+                handler = c->log->handler;
+                c->log->handler = NULL;
+
+                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                               "quic recvmsg: fd:%d n:%z", c->fd, n);
+
+                c->log->handler = handler;
+            }
+#endif
+
+            ngx_memzero(&buf, sizeof(ngx_buf_t));
+
+            buf.pos = buffer;
+            buf.last = buffer + n;
+            buf.start = buf.pos;
+            buf.end = buffer + sizeof(buffer);
+
+            qsock = ngx_quic_get_socket(c);
+
+            ngx_memcpy(&qsock->sockaddr.sockaddr, sockaddr, socklen);
+            qsock->socklen = socklen;
+
+            c->udp->buffer = &buf;
+
+            rev = c->read;
+            rev->ready = 1;
+            rev->active = 0;
+
+            rev->handler(rev);
+
+            if (c->udp) {
+                c->udp->buffer = NULL;
+            }
+
+            rev->ready = 0;
+            rev->active = 1;
+
+            goto next;
+        }
+
+#if (NGX_STAT_STUB)
+        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);
+#endif
+
+        ngx_accept_disabled = ngx_cycle->connection_n / 8
+                              - ngx_cycle->free_connection_n;
+
+        c = ngx_get_connection(lc->fd, ev->log);
+        if (c == NULL) {
+            return;
+        }
+
+        c->shared = 1;
+        c->type = SOCK_DGRAM;
+        c->socklen = socklen;
+
+#if (NGX_STAT_STUB)
+        (void) ngx_atomic_fetch_add(ngx_stat_active, 1);
+#endif
+
+        c->pool = ngx_create_pool(ls->pool_size, ev->log);
+        if (c->pool == NULL) {
+            ngx_quic_close_accepted_connection(c);
+            return;
+        }
+
+        c->sockaddr = ngx_palloc(c->pool, NGX_SOCKADDRLEN);
+        if (c->sockaddr == NULL) {
+            ngx_quic_close_accepted_connection(c);
+            return;
+        }
+
+        ngx_memcpy(c->sockaddr, sockaddr, socklen);
+
+        log = ngx_palloc(c->pool, sizeof(ngx_log_t));
+        if (log == NULL) {
+            ngx_quic_close_accepted_connection(c);
+            return;
+        }
+
+        *log = ls->log;
+
+        c->log = log;
+        c->pool->log = log;
+        c->listening = ls;
+
+        if (local_sockaddr == &lsa.sockaddr) {
+            local_sockaddr = ngx_palloc(c->pool, local_socklen);
+            if (local_sockaddr == NULL) {
+                ngx_quic_close_accepted_connection(c);
+                return;
+            }
+
+            ngx_memcpy(local_sockaddr, &lsa, local_socklen);
+        }
+
+        c->local_sockaddr = local_sockaddr;
+        c->local_socklen = local_socklen;
+
+        c->buffer = ngx_create_temp_buf(c->pool, n);
+        if (c->buffer == NULL) {
+            ngx_quic_close_accepted_connection(c);
+            return;
+        }
+
+        c->buffer->last = ngx_cpymem(c->buffer->last, buffer, n);
+
+        rev = c->read;
+        wev = c->write;
+
+        rev->active = 1;
+        wev->ready = 1;
+
+        rev->log = log;
+        wev->log = log;
+
+        /*
+         * TODO: MT: - ngx_atomic_fetch_add()
+         *             or protection by critical section or light mutex
+         *
+         * TODO: MP: - allocated in a shared memory
+         *           - ngx_atomic_fetch_add()
+         *             or protection by critical section or light mutex
+         */
+
+        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
+
+        c->start_time = ngx_current_msec;
+
+#if (NGX_STAT_STUB)
+        (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);
+#endif
+
+        if (ls->addr_ntop) {
+            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
+            if (c->addr_text.data == NULL) {
+                ngx_quic_close_accepted_connection(c);
+                return;
+            }
+
+            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,
+                                             c->addr_text.data,
+                                             ls->addr_text_max_len, 0);
+            if (c->addr_text.len == 0) {
+                ngx_quic_close_accepted_connection(c);
+                return;
+            }
+        }
+
+#if (NGX_DEBUG)
+        {
+        ngx_str_t  addr;
+        u_char     text[NGX_SOCKADDR_STRLEN];
+
+        ngx_debug_accepted_connection(ecf, c);
+
+        if (log->log_level & NGX_LOG_DEBUG_EVENT) {
+            addr.data = text;
+            addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,
+                                     NGX_SOCKADDR_STRLEN, 1);
+
+            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,
+                           "*%uA quic recvmsg: %V fd:%d n:%z",
+                           c->number, &addr, c->fd, n);
+        }
+
+        }
+#endif
+
+        log->data = NULL;
+        log->handler = NULL;
+
+        ls->handler(c);
+
+    next:
+
+        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {
+            ev->available -= n;
+        }
+
+    } while (ev->available);
+}
+
+
+static void
+ngx_quic_close_accepted_connection(ngx_connection_t *c)
+{
+    ngx_free_connection(c);
+
+    c->fd = (ngx_socket_t) -1;
+
+    if (c->pool) {
+        ngx_destroy_pool(c->pool);
+    }
+
+#if (NGX_STAT_STUB)
+    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);
+#endif
+}
+
+
+void
+ngx_quic_rbtree_insert_value(ngx_rbtree_node_t *temp,
+    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+    ngx_int_t            rc;
+    ngx_connection_t    *c, *ct;
+    ngx_rbtree_node_t  **p;
+    ngx_quic_socket_t   *qsock, *qsockt;
+
+    for ( ;; ) {
+
+        if (node->key < temp->key) {
+
+            p = &temp->left;
+
+        } else if (node->key > temp->key) {
+
+            p = &temp->right;
+
+        } else { /* node->key == temp->key */
+
+            qsock = (ngx_quic_socket_t *) node;
+            c = qsock->udp.connection;
+
+            qsockt = (ngx_quic_socket_t *) temp;
+            ct = qsockt->udp.connection;
+
+            rc = ngx_memn2cmp(qsock->sid.id, qsockt->sid.id,
+                              qsock->sid.len, qsockt->sid.len);
+
+            if (rc == 0 && c->listening->wildcard) {
+                rc = ngx_cmp_sockaddr(c->local_sockaddr, c->local_socklen,
+                                      ct->local_sockaddr, ct->local_socklen, 1);
+            }
+
+            p = (rc < 0) ? &temp->left : &temp->right;
+        }
+
+        if (*p == sentinel) {
+            break;
+        }
+
+        temp = *p;
+    }
+
+    *p = node;
+    node->parent = temp;
+    node->left = sentinel;
+    node->right = sentinel;
+    ngx_rbt_red(node);
+}
+
+
+static ngx_connection_t *
+ngx_quic_lookup_connection(ngx_listening_t *ls, ngx_str_t *key,
+    struct sockaddr *local_sockaddr, socklen_t local_socklen)
+{
+    uint32_t            hash;
+    ngx_int_t           rc;
+    ngx_connection_t   *c;
+    ngx_rbtree_node_t  *node, *sentinel;
+    ngx_quic_socket_t  *qsock;
+
+    if (key->len == 0) {
+        return NULL;
+    }
+
+    node = ls->rbtree.root;
+    sentinel = ls->rbtree.sentinel;
+    hash = ngx_crc32_long(key->data, key->len);
+
+    while (node != sentinel) {
+
+        if (hash < node->key) {
+            node = node->left;
+            continue;
+        }
+
+        if (hash > node->key) {
+            node = node->right;
+            continue;
+        }
+
+        /* hash == node->key */
+
+        qsock = (ngx_quic_socket_t *) node;
+
+        rc = ngx_memn2cmp(key->data, qsock->sid.id, key->len, qsock->sid.len);
+
+        c = qsock->udp.connection;
+
+        if (rc == 0 && ls->wildcard) {
+            rc = ngx_cmp_sockaddr(local_sockaddr, local_socklen,
+                                  c->local_sockaddr, c->local_socklen, 1);
+        }
+
+        if (rc == 0) {
+            c->udp = &qsock->udp;
+            return c;
+        }
+
+        node = (rc < 0) ? node->left : node->right;
+    }
+
+    return NULL;
+}
--- a/src/http/ngx_http.c	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/http/ngx_http.c	Wed Apr 20 16:01:17 2022 +0400
@@ -1822,7 +1822,14 @@
     ls->wildcard = addr->opt.wildcard;
 
 #if (NGX_HTTP_V3)
+
     ls->quic = addr->opt.http3;
+
+    if (ls->quic) {
+        ngx_rbtree_init(&ls->rbtree, &ls->sentinel,
+                        ngx_quic_rbtree_insert_value);
+    }
+
 #endif
 
     return ls;
--- a/src/stream/ngx_stream.c	Thu Feb 17 22:38:42 2022 +0300
+++ b/src/stream/ngx_stream.c	Wed Apr 20 16:01:17 2022 +0400
@@ -519,8 +519,23 @@
 #endif
 
 #if (NGX_STREAM_QUIC)
+
             ls->quic = addr[i].opt.quic;
+
+            if (ls->quic) {
+                ngx_rbtree_init(&ls->rbtree, &ls->sentinel,
+                                ngx_quic_rbtree_insert_value);
+            }
+
 #endif
+
+#if !(NGX_WIN32)
+            if (!ls->quic) {
+                ngx_rbtree_init(&ls->rbtree, &ls->sentinel,
+                                ngx_udp_rbtree_insert_value);
+            }
+#endif
+
             stport = ngx_palloc(cf->pool, sizeof(ngx_stream_port_t));
             if (stport == NULL) {
                 return NGX_CONF_ERROR;