Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2215698/?format=api
{ "id": 2215698, "url": "http://patchwork.ozlabs.org/api/patches/2215698/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-cifs-client/patch/101b27c8b95dd86586da6da7801d43e97ac8e644.1774410440.git.lucien.xin@gmail.com/", "project": { "id": 12, "url": "http://patchwork.ozlabs.org/api/projects/12/?format=api", "name": "Linux CIFS Client", "link_name": "linux-cifs-client", "list_id": "linux-cifs.vger.kernel.org", "list_email": "linux-cifs@vger.kernel.org", "web_url": "", "scm_url": "", "webscm_url": "", "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<101b27c8b95dd86586da6da7801d43e97ac8e644.1774410440.git.lucien.xin@gmail.com>", "list_archive_url": null, "date": "2026-03-25T03:47:20", "name": "[net-next,v11,15/15] quic: add packet parser base", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "5108b2edc48f9a5d1ee2663f95466762257009c4", "submitter": { "id": 61073, "url": "http://patchwork.ozlabs.org/api/people/61073/?format=api", "name": "Xin Long", "email": "lucien.xin@gmail.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-cifs-client/patch/101b27c8b95dd86586da6da7801d43e97ac8e644.1774410440.git.lucien.xin@gmail.com/mbox/", "series": [ { "id": 497380, "url": "http://patchwork.ozlabs.org/api/series/497380/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-cifs-client/list/?series=497380", "date": "2026-03-25T03:47:06", "name": "net: introduce QUIC infrastructure and core subcomponents", "version": 11, "mbox": "http://patchwork.ozlabs.org/series/497380/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2215698/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2215698/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-cifs+bounces-10517-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-cifs@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=WFhP1bBP;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=2600:3c04:e001:36c::12fc:5321; helo=tor.lore.kernel.org;\n envelope-from=linux-cifs+bounces-10517-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=\"WFhP1bBP\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.160.179", "smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com", "smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=gmail.com" ], "Received": [ "from tor.lore.kernel.org (tor.lore.kernel.org\n [IPv6:2600:3c04:e001:36c::12fc:5321])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fgY1s3C5zz1y1G\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 14:54:25 +1100 (AEDT)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby tor.lore.kernel.org (Postfix) with ESMTP id 2055430F472F\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 25 Mar 2026 03:50:09 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 93C2A1428F4;\n\tWed, 25 Mar 2026 03:50:05 +0000 (UTC)", "from mail-qt1-f179.google.com (mail-qt1-f179.google.com\n [209.85.160.179])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id F14F12EAB61\n\tfor <linux-cifs@vger.kernel.org>; Wed, 25 Mar 2026 03:50:02 +0000 (UTC)", "by mail-qt1-f179.google.com with SMTP id\n d75a77b69052e-5090c7e9081so16962031cf.0\n for <linux-cifs@vger.kernel.org>;\n Tue, 24 Mar 2026 20:50:02 -0700 (PDT)", "from wsfd-netdev58.anl.eng.rdu2.dc.redhat.com ([66.187.232.140])\n by smtp.gmail.com with ESMTPSA id\n d75a77b69052e-50b36cb2e29sm150093001cf.1.2026.03.24.20.49.59\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 24 Mar 2026 20:50:00 -0700 (PDT)" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1774410605; cv=none;\n b=WllpgKuI2NKwVx7GZXaq4o+XdmK313m1ROZ2PXUyovswujU0/ZSAVPgAeV4HQxDwoMZdud6YNPZqrkePlyZCiY2b7trR2UqIJmO9/1dqr6FMl2MZILa66hJv4S/ows09ox87Yl2A4fwBAxvR1obklP+gnNL11mWaGea8sodXEEo=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1774410605; c=relaxed/simple;\n\tbh=ENnzQQizjvKejUJbwP6Q7VeMcSU8yTspa4yccfRscGw=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=KOJSttXR5qj2igCohKfC1G4mrew+8ehJGD1TkOEAf+CcS+8kizzqiS+FU61gQE8+H2WjH+6Ga77WTZjMBKYrzd7ALEbVEFkEiLVwiv8biSyy0xMlkuWOV15XjtodxNqPVNaZZouymBkl5v08Fx8KYX+JatvA5oBz1MANTY4Rty8=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com;\n spf=pass smtp.mailfrom=gmail.com;\n dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=WFhP1bBP; arc=none smtp.client-ip=209.85.160.179", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1774410602; x=1775015402;\n darn=vger.kernel.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n :message-id:reply-to;\n bh=fDoOV6F9CK5ZCtWwveB2rD4e4y0+B0FSgdGs3/39j84=;\n b=WFhP1bBPlK4gG1hLkZRpm9rZeaxt1MuyGtZHk1nz65NsCrxr3rGLzUzDHvHmagNwzl\n EESUJjQNdsul35gXoUU0APPPGOx9JREgJg/k5HxAS+7Q4cWo2066I9AtMiVLNBFyILV1\n nS47Ki9u9gZcMrN3mXc+HeqBg/K3wsPpKGOHOYuamO5UFBAtT0NKDjfJngjL7mQhB4vF\n +Klwwf36EoVFN/eg91sXWofXqk8sLcmhG2EHuCVDmVN+YIR0/Ailw9mAhNvkLJsh3+Iz\n 5gALVk7naQh7So/2/+zdBttOeCH/RCk9v25LKbOuGIS3O82P2SWkTpqf0vjns82OjDkz\n Fm6g==", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1774410602; x=1775015402;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n :to:cc:subject:date:message-id:reply-to;\n bh=fDoOV6F9CK5ZCtWwveB2rD4e4y0+B0FSgdGs3/39j84=;\n b=ktcAfdB0Gxdq1/zuJkrjsh5dNXQA0yh8yNopEJ45oIlTfAYR5pijjfjngbYcoTvckE\n 6UT3jErUrqOZ/NgWpkzk7ySJ7Igl3l/vrttcD6BgK1ryjEIgTazCbZNBpoVcsUl7h8oS\n TzqRAWO6pKow7FcWS8q5jUNXssUpxwRR65TvHCW57uPwVawy0SAVIvmAa2g2jyXUvzzc\n 8/EYjavZ8YSkoD9oc2sXumo0RE+UqAMRAtI/sbio5e71WC422pzSr56xCv9LL2vojhIu\n v0kb/mxB8ktcn1nKtSPHMxJiFIvmVnz91WzOOivr4JClvaAnN3JTetQBZtZksMzo4AbJ\n cPhg==", "X-Forwarded-Encrypted": "i=1;\n AJvYcCXwfN/0O8xUMrOTVyMR/qARGOoDdlOsMyTNX38jCCu/OpO2Ck2+Bb1q8x54Qt8Bdt/+q+ywgF6ffQdr@vger.kernel.org", "X-Gm-Message-State": "AOJu0YxUVakdCjDqgUDVkzb5CUr+3rMCy6gwiFmS2REAC5v/iC7JnAum\n\t7xuBnF/p9vnTPyA9ziw3H9ppWOaDbjAWyXam7Tgn4XoMc10hLG/zL+3L", "X-Gm-Gg": "ATEYQzwovELzfMkZq2pZSlvtXmLPeyU/EuQtZbFBoYyZmWlapWgM49hRlh3NJHCnBdf\n\toqLRVsGDd0GXkpfxhoo71I2pJioX+90ehaBlj6H1k+tsQbO+raWLP86tEziPt+LlzBq8boH2uUx\n\tc5MYopyNY9bcgmSmCy6v9cIjturT/YYF56qINhSSappyDZGDjtIiPPZmqVRNfd4TxHCjYuOf41K\n\tqgGA2pjUkDWT6EmfBIOXACdJmdlxMMwxReOZ+C8LCKxgZDl9gqx/Vaua0F/2ft0qGrO+TyaX8hq\n\tN57VIxTEsRvNl9RuzSryQDiHwip9AoZymF7H+W6qfXndWaAj7cuR+nnxhD1JXI0sEjOhDObSN9k\n\tJO6oBlDSHrVLo0tTzlUqPz42tNANuvxNdVzIFWo24o4rtesMLjr4aEJfYf1QVECcgYdhHedass/\n\txsgfQokhspaTSFr3LHItg7a99b4c4AAP8IuuHaMEGqBiRup7o4wbjbNeSd2OYb3VnjAdzFTWXE4\n\tiohrde25NUY3h81lmVkVgxhM4+ykVOLKO276sFyBJ0F8fMP75HBmGyIrqG9/jp4h0wdDjojm4ek", "X-Received": "by 2002:a05:622a:251a:b0:50b:51f7:c660 with SMTP id\n d75a77b69052e-50b80eaa066mr31612771cf.61.1774410601727;\n Tue, 24 Mar 2026 20:50:01 -0700 (PDT)", "From": "Xin Long <lucien.xin@gmail.com>", "To": "network dev <netdev@vger.kernel.org>,\n\tquic@lists.linux.dev", "Cc": "davem@davemloft.net,\n\tkuba@kernel.org,\n\tEric Dumazet <edumazet@google.com>,\n\tPaolo Abeni <pabeni@redhat.com>,\n\tSimon Horman <horms@kernel.org>,\n\tStefan Metzmacher <metze@samba.org>,\n\tMoritz Buhl <mbuhl@openbsd.org>,\n\tTyler Fanelli <tfanelli@redhat.com>,\n\tPengtao He <hepengtao@xiaomi.com>,\n\tThomas Dreibholz <dreibh@simula.no>,\n\tlinux-cifs@vger.kernel.org,\n\tSteve French <smfrench@gmail.com>,\n\tNamjae Jeon <linkinjeon@kernel.org>,\n\tPaulo Alcantara <pc@manguebit.com>,\n\tTom Talpey <tom@talpey.com>,\n\tkernel-tls-handshake@lists.linux.dev,\n\tChuck Lever <chuck.lever@oracle.com>,\n\tJeff Layton <jlayton@kernel.org>,\n\tSteve Dickson <steved@redhat.com>,\n\tHannes Reinecke <hare@suse.de>,\n\tAlexander Aring <aahringo@redhat.com>,\n\tDavid Howells <dhowells@redhat.com>,\n\tMatthieu Baerts <matttbe@kernel.org>,\n\tJohn Ericson <mail@johnericson.me>,\n\tCong Wang <xiyou.wangcong@gmail.com>,\n\t\"D . Wythe\" <alibuda@linux.alibaba.com>,\n\tJason Baron <jbaron@akamai.com>,\n\tilliliti <illiliti@protonmail.com>,\n\tSabrina Dubroca <sd@queasysnail.net>,\n\tMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>,\n\tDaniel Stenberg <daniel@haxx.se>,\n\tAndy Gospodarek <andrew.gospodarek@broadcom.com>,\n\t\"Marc E . Fiuczynski\" <marc@fiuczynski.com>", "Subject": "[PATCH net-next v11 15/15] quic: add packet parser base", "Date": "Tue, 24 Mar 2026 23:47:20 -0400", "Message-ID": "\n <101b27c8b95dd86586da6da7801d43e97ac8e644.1774410440.git.lucien.xin@gmail.com>", "X-Mailer": "git-send-email 2.47.1", "In-Reply-To": "<cover.1774410440.git.lucien.xin@gmail.com>", "References": "<cover.1774410440.git.lucien.xin@gmail.com>", "Precedence": "bulk", "X-Mailing-List": "linux-cifs@vger.kernel.org", "List-Id": "<linux-cifs.vger.kernel.org>", "List-Subscribe": "<mailto:linux-cifs+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-cifs+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit" }, "content": "This patch uses 'quic_packet' to handle packing of QUIC packets on the\nreceive (RX) path.\n\nIt introduces mechanisms to parse the ALPN from client Initial packets\nto determine the correct listener socket. Received packets are then\nrouted and processed accordingly. Similar to the TX path, handling for\napplication and handshake packets is not yet implemented.\n\n- quic_packet_parse_alpn()`: Parse the ALPN from a client Initial packet,\n then locate the appropriate listener using the ALPN.\n\n- quic_packet_rcv(): Locate the appropriate socket to handle the packet\n via quic_packet_process().\n\n- quic_packet_process()`: Process the received packet.\n\nIn addition to packet flow, this patch adds support for ICMP-based MTU\nupdates by locating the relevant socket and updating the stored PMTU\naccordingly.\n\n- quic_packet_rcv_err_pmtu(): Find the socket and update the PMTU via\n quic_packet_mss_update().\n\nSigned-off-by: Xin Long <lucien.xin@gmail.com>\n---\nv5:\n - In quic_packet_rcv_err(), remove the unnecessary quic_is_listen()\n check and move quic_get_mtu_info() out of sock lock (suggested\n by Paolo).\n - Replace cancel_work_sync() to disable_work_sync() (suggested by\n Paolo).\nv6:\n - Fix the loop using skb_dequeue() in quic_packet_backlog_work(), and\n kfree_skb() when sk is not found (reported by AI Reviews).\n - Remove skb_pull() from quic_packet_rcv(), since it is now handled\n in quic_path_rcv().\n - Note for AI reviews: add if (dst) check in quic_packet_rcv_err_pmtu(),\n although quic_packet_route() >= 0 already guarantees it is not NULL.\n - Note for AI reviews: it is safe to do *plen -= QUIC_HLEN in\n quic_packet_get_version_and_connid(), since quic_packet_get_sock()\n already checks if (skb->len < QUIC_HLEN).\n - Note for AI reviews: cb->length - cb->number_len - QUIC_TAG_LEN\n cannot underflow, because quic_crypto_header_decrypt() already checks\n if (cb->length < QUIC_PN_MAX_LEN + QUIC_SAMPLE_LEN).\n - Note for AI reviews: the cast length in quic_packet_parse_alpn()\n is safe, as there is a prior check if (length > (u16)len); len is\n skb->len, which cannot exceed U16_MAX for UDP packet with QUIC.\n - Note for AI reviews: it's correct to do if (flags &\n QUIC_F_MTU_REDUCED_DEFERRED) in quic_release_cb(), since\n QUIC_MTU_REDUCED_DEFERRED is the bit used with test_and_set_bit().\n - Note for AI reviews: move skb_cb->backlog = 1 before adding skb to\n backlog, although it's safe to write skb_cb after adding to backlog\n with sk_lock.slock, as skb dequeue from backlog requires sk_lock.slock.\nv7:\n - Pass udp sk to quic_packet_rcv(), quic_packet_rcv_err() and\n quic_sock_lookup().\n - Move the call to skb_linearize() and skb_set_owner_sk_safe() to\n .quic_path_rcv()/quic_packet_rcv().\nv8:\n - Replace the global ALPN demultiplexing sysctl with the static key in\n quic_packet_parse_alpn() (noted by Stefan).\n - Refetch skb->data after decrypt in ALPN parsing, as skb_cow_data()\n may reallocate the skb data buffer (reported by Syzkaller).\n - The indirect quic_path_rcv has been removed and call quic_packet_rcv()\n directly via extern.\n - Do not restore skb data when QUIC Initial decryption fails, as the\n caller will free the skb for this failure anyway.\n - With patch 14 removed, define a temporary QUIC_FRAME_CRYPTO ID when\n parsing the ALPN.\nv9:\n - Remove local_bh_disable() in quic_packet_get_listen_sock() as it's now\n using rcu_read_lock instead of spin_sock in quic_listen_sock_lookup()\n (noted by Paolo).\nv10:\n - Return QUIC_PACKET_INVALID (instead of -1) for invalid packet types in\n quic_packet_version_get_type().\n - Update the comment to clarify in quic_packet_rcv_err() that ICMP errors\n embed the original QUIC packet, reversing src/dst addrs when parsed.\n - Use qn->backlog_list.lock in quic_packet_backlog_schedule() to\n prevent a TOCTOU race between the head->qlen check and its update in\n __skb_queue_tail().\n - Add check 'len < TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN' before parsing\n ClientHello in quic_packet_get_alpn().\n - Add more limits in quic_packet_get_alpn() to improve robustness against\n malformed TLS ClientHello messages.\n - Move skb_queue_purge() to after disable_work_sync() in quic_net_exit()\n for clarity and to satisfy AI review.\n - quic_sock.config.plpmtud_probe_interval has been moved to\n quic_path_group.plpmtud_interval, so update its usage in\n quic_packet_rcv_err_pmtu() accordingly.\n - Remove quic_packet_select_version() and quic_packet_version_change();\n they will be reintroduced later when needed in the next patch series.\nv11:\n - Note for AI review: refcount increments in quic_listen_sock_lookup()\n and quic_sock_lookup() are left unchanged due to code complexity.\n - Set maximum line length to 80 characters.\n - Do not mark backlog packets as sleepable (cb->backlog = 1) in\n sk_add_backlog path; Replace spin_(un)lock() with spin_(un)lock_bh()\n in quic_packet_backlog_schedule().\n - Return -ENOBUFS instead of -1 in quic_packet_backlog_schedule().\n - Change err parameter type from u8 to bool (icmp) in quic_packet_rcv().\n - Propagate errors from quic_packet_get_sock() and sk_add_backlog() in\n quic_packet_rcv().\n - Propagate errors from quic_packet_get_dcid() and\n quic_packet_parse_alpn() in quic_packet_get_sock() via ERR_PTR().\n - Propagate errors from quic_packet_parse_alpn() in\n quic_packet_get_listen_sock() via ERR_PTR().\n - Propagate errors from quic_packet_get_version_and_connid() and\n quic_packet_get_token() in quic_packet_parse_alpn().\n - Do not hold skb when calling quic_packet_backlog_schedule() in\n quic_packet_parse_alpn(); do not free skb when returning\n -EINPROGRESS from quic_packet_get_sock() in quic_packet_rcv().\n - Move the quic_packet_rcv() declaration from packet.h to path.h,\n as it's only called in path.c (noted by AI review).\n - Merge quic_packet_get_dcid() and quic_packet_get_version_and_connid()\n into quic_packet_get_long_header() and extract quic_packet_get_connid()\n (noted by AI review).\n---\n net/quic/packet.c | 605 ++++++++++++++++++++++++++++++++++++++++++++\n net/quic/packet.h | 8 +\n net/quic/path.c | 4 +-\n net/quic/path.h | 2 +\n net/quic/protocol.c | 5 +\n net/quic/protocol.h | 4 +\n net/quic/socket.c | 149 +++++++++++\n net/quic/socket.h | 7 +\n 8 files changed, 782 insertions(+), 2 deletions(-)", "diff": "diff --git a/net/quic/packet.c b/net/quic/packet.c\nindex 0805bc77c2a2..88fbe839789d 100644\n--- a/net/quic/packet.c\n+++ b/net/quic/packet.c\n@@ -14,6 +14,611 @@\n \n #define QUIC_HLEN\t\t1\n \n+#define QUIC_LONG_HLEN(dcid, scid) \\\n+\t(QUIC_HLEN + QUIC_VERSION_LEN + 1 + (dcid)->len + 1 + (scid)->len)\n+\n+#define QUIC_VERSION_NUM\t2\n+\n+/* Supported QUIC versions and their compatible versions. Used for Compatible\n+ * Version Negotiation in rfc9368#section-2.3.\n+ */\n+static u32 quic_versions[QUIC_VERSION_NUM][4] = {\n+\t/* Version,\tCompatible Versions */\n+\t{ QUIC_VERSION_V1, QUIC_VERSION_V2, QUIC_VERSION_V1, 0 },\n+\t{ QUIC_VERSION_V2, QUIC_VERSION_V2, QUIC_VERSION_V1, 0 },\n+};\n+\n+/* Get the compatible version list for a given QUIC version. */\n+u32 *quic_packet_compatible_versions(u32 version)\n+{\n+\tu8 i;\n+\n+\tfor (i = 0; i < QUIC_VERSION_NUM; i++)\n+\t\tif (version == quic_versions[i][0])\n+\t\t\treturn quic_versions[i];\n+\treturn NULL;\n+}\n+\n+/* Convert version-specific type to internal standard packet type. */\n+static u8 quic_packet_version_get_type(u32 version, u8 type)\n+{\n+\tif (version == QUIC_VERSION_V1)\n+\t\treturn type;\n+\n+\tswitch (type) {\n+\tcase QUIC_PACKET_INITIAL_V2:\n+\t\treturn QUIC_PACKET_INITIAL;\n+\tcase QUIC_PACKET_0RTT_V2:\n+\t\treturn QUIC_PACKET_0RTT;\n+\tcase QUIC_PACKET_HANDSHAKE_V2:\n+\t\treturn QUIC_PACKET_HANDSHAKE;\n+\tcase QUIC_PACKET_RETRY_V2:\n+\t\treturn QUIC_PACKET_RETRY;\n+\tdefault:\n+\t\treturn QUIC_PACKET_INVALID;\n+\t}\n+}\n+\n+/* Extracts a QUIC Connection ID from a buffer in the long header packet. */\n+static int quic_packet_get_connid(struct quic_conn_id *connid, u8 **pp,\n+\t\t\t\t u32 *plen)\n+{\n+\tu64 len;\n+\n+\tif (!quic_get_int(pp, plen, &len, 1) ||\n+\t len > *plen || len > QUIC_CONN_ID_MAX_LEN)\n+\t\treturn -EINVAL;\n+\n+\tquic_conn_id_update(connid, *pp, len);\n+\t*plen -= len;\n+\t*pp += len;\n+\treturn 0;\n+}\n+\n+/* Parse QUIC version and connection IDs (DCID and SCID) from a Long header\n+ * packet buffer.\n+ */\n+static int quic_packet_get_long_header(struct quic_conn_id *dcid,\n+\t\t\t\t struct quic_conn_id *scid, u32 *version,\n+\t\t\t\t u8 **pp, u32 *plen)\n+{\n+\tint err;\n+\tu64 v;\n+\n+\t*pp += QUIC_HLEN;\n+\t*plen -= QUIC_HLEN;\n+\n+\tif (!quic_get_int(pp, plen, &v, QUIC_VERSION_LEN))\n+\t\treturn -EINVAL;\n+\tif (version)\n+\t\t*version = v;\n+\n+\terr = quic_packet_get_connid(dcid, pp, plen);\n+\tif (err)\n+\t\treturn err;\n+\tif (!scid)\n+\t\treturn 0;\n+\treturn quic_packet_get_connid(scid, pp, plen);\n+}\n+\n+/* Extracts a QUIC token from a buffer in the Client Initial packet. */\n+static int quic_packet_get_token(struct quic_data *token, u8 **pp, u32 *plen)\n+{\n+\tu64 len;\n+\n+\tif (!quic_get_var(pp, plen, &len) || len > *plen)\n+\t\treturn -EINVAL;\n+\tquic_data(token, *pp, len);\n+\t*plen -= len;\n+\t*pp += len;\n+\treturn 0;\n+}\n+\n+/* Process PMTU reduction event on a QUIC socket. */\n+void quic_packet_rcv_err_pmtu(struct sock *sk)\n+{\n+\tstruct quic_path_group *paths = quic_paths(sk);\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tu32 pathmtu, info, taglen;\n+\tstruct dst_entry *dst;\n+\tbool reset_timer;\n+\n+\tif (!ip_sk_accept_pmtu(sk))\n+\t\treturn;\n+\n+\tinfo = clamp(paths->mtu_info, QUIC_PATH_MIN_PMTU, QUIC_PATH_MAX_PMTU);\n+\t/* If PLPMTUD is not enabled, update MSS using route and ICMP info. */\n+\tif (!paths->plpmtud_interval) {\n+\t\tif (quic_packet_route(sk))\n+\t\t\treturn;\n+\n+\t\tdst = __sk_dst_get(sk);\n+\t\tif (dst)\n+\t\t\tdst->ops->update_pmtu(dst, sk, NULL, info, true);\n+\t\tquic_packet_mss_update(sk, info - packet->hlen);\n+\t\treturn;\n+\t}\n+\t/* PLPMTUD is enabled: adjust to smaller PMTU, subtract headers and\n+\t * AEAD tag. Also notify the QUIC path layer for possible state\n+\t * changes and probing.\n+\t */\n+\ttaglen = quic_packet_taglen(packet);\n+\tinfo = info - packet->hlen - taglen;\n+\tpathmtu = quic_path_pl_toobig(paths, info, &reset_timer);\n+\tif (reset_timer)\n+\t\tquic_timer_reset(sk, QUIC_TIMER_PMTU, paths->plpmtud_interval);\n+\tif (pathmtu)\n+\t\tquic_packet_mss_update(sk, pathmtu + taglen);\n+}\n+\n+/* Handle ICMP Toobig packet and update QUIC socket path MTU. */\n+static int quic_packet_rcv_err(struct sock *sk, struct sk_buff *skb)\n+{\n+\tunion quic_addr daddr, saddr;\n+\tu32 info;\n+\n+\t/* ICMP embeds the original outgoing QUIC packet, so saddr/daddr are\n+\t * reversed when parsed. Only address-based socket lookup is possible\n+\t * in this case.\n+\t */\n+\tquic_get_msg_addrs(skb, &saddr, &daddr);\n+\tsk = quic_sock_lookup(skb, &daddr, &saddr, sk, NULL);\n+\tif (!sk)\n+\t\treturn -ENOENT;\n+\n+\tif (quic_get_mtu_info(skb, &info)) {\n+\t\tsock_put(sk);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Success: update socket path MTU info. */\n+\tbh_lock_sock(sk);\n+\tquic_paths(sk)->mtu_info = info;\n+\tif (sock_owned_by_user(sk)) {\n+\t\t/* Socket locked by userspace. Defer MTU processing via\n+\t\t * release_cb. Hold socket reference to prevent it being\n+\t\t * freed before deferral.\n+\t\t */\n+\t\tif (!test_and_set_bit(QUIC_MTU_REDUCED_DEFERRED,\n+\t\t\t\t &sk->sk_tsq_flags))\n+\t\t\tsock_hold(sk);\n+\t\tgoto out;\n+\t}\n+\t/* Otherwise, process the MTU reduction now. */\n+\tquic_packet_rcv_err_pmtu(sk);\n+out:\n+\tbh_unlock_sock(sk);\n+\tsock_put(sk);\n+\treturn 1;\n+}\n+\n+#define QUIC_PACKET_BACKLOG_MAX\t\t4096\n+\n+/* Queue a packet for later processing when sleeping is allowed. */\n+static int quic_packet_backlog_schedule(struct net *net, struct sk_buff *skb)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tstruct quic_net *qn = quic_net(net);\n+\tstruct sk_buff_head *head;\n+\n+\tif (cb->backlog)\n+\t\treturn 0;\n+\n+\thead = &qn->backlog_list;\n+\tspin_lock_bh(&head->lock);\n+\tif (head->qlen >= QUIC_PACKET_BACKLOG_MAX) {\n+\t\tspin_unlock_bh(&head->lock);\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_RCVDROP);\n+\t\tkfree_skb(skb);\n+\t\treturn -ENOBUFS;\n+\t}\n+\tcb->backlog = 1;\n+\t__skb_queue_tail(head, skb);\n+\tspin_unlock_bh(&head->lock);\n+\n+\tqueue_work(quic_wq, &qn->work);\n+\treturn 1;\n+}\n+\n+#define TLS_MT_CLIENT_HELLO\t1\n+#define TLS_EXT_alpn\t\t16\n+\n+/* TLS Client Hello Msg:\n+ *\n+ * uint16 ProtocolVersion;\n+ * opaque Random[32];\n+ * uint8 CipherSuite[2];\n+ *\n+ * struct {\n+ * ExtensionType extension_type;\n+ * opaque extension_data<0..2^16-1>;\n+ * } Extension;\n+ *\n+ * struct {\n+ * ProtocolVersion legacy_version = 0x0303;\n+ * Random rand;\n+ * opaque legacy_session_id<0..32>;\n+ * CipherSuite cipher_suites<2..2^16-2>;\n+ * opaque legacy_compression_methods<1..2^8-1>;\n+ * Extension extensions<8..2^16-1>;\n+ * } ClientHello;\n+ */\n+\n+#define TLS_CH_RANDOM_LEN\t32\n+#define TLS_CH_VERSION_LEN\t2\n+#define TLS_MAX_EXTENSIONS\t128\n+\n+/* Extract ALPN data from a TLS ClientHello message.\n+ *\n+ * Parses the TLS ClientHello handshake message to find the ALPN (Application\n+ * Layer Protocol Negotiation) TLS extension. It validates the TLS ClientHello\n+ * structure, including version, random, session ID, cipher suites, compression\n+ * methods, and extensions. Once the ALPN extension is found, the ALPN\n+ * protocols list is extracted and stored in @alpn.\n+ *\n+ * Return: 0 on success or no ALPN found, a negative error code on failed\n+ * parsing.\n+ */\n+static int quic_packet_get_alpn(struct quic_data *alpn, u8 *p, u32 len)\n+{\n+\tint err = -EINVAL, found = 0, exts = 0;\n+\tu64 length, type;\n+\n+\t/* Verify handshake message type (ClientHello) and its length. */\n+\tif (!quic_get_int(&p, &len, &type, 1) || type != TLS_MT_CLIENT_HELLO)\n+\t\treturn err;\n+\tif (!quic_get_int(&p, &len, &length, 3) ||\n+\t len < TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN ||\n+\t length < TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN)\n+\t\treturn err;\n+\tif (len > (u32)length) /* Cap len to handshake msg length. */\n+\t\tlen = length;\n+\t/* Skip legacy_version (2 bytes) + random (32 bytes). */\n+\tp += TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN;\n+\tlen -= TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN;\n+\t/* legacy_session_id_len must be zero (QUIC requirement). */\n+\tif (!quic_get_int(&p, &len, &length, 1) || length)\n+\t\treturn err;\n+\n+\t/* Skip cipher_suites (2 bytes length + variable data). */\n+\tif (!quic_get_int(&p, &len, &length, 2) || length > (u64)len)\n+\t\treturn err;\n+\tlen -= length;\n+\tp += length;\n+\n+\t/* Skip legacy_compression_methods (1 byte length + variable data). */\n+\tif (!quic_get_int(&p, &len, &length, 1) || length > (u64)len)\n+\t\treturn err;\n+\tlen -= length;\n+\tp += length;\n+\n+\t/* Read TLS extensions length (2 bytes). */\n+\tif (!quic_get_int(&p, &len, &length, 2))\n+\t\treturn err;\n+\tif (len > (u32)length) /* Limit len to extensions length if larger. */\n+\t\tlen = length;\n+\twhile (len > 4) { /* Scan extensions for ALPN (TLS_EXT_alpn) */\n+\t\tif (!quic_get_int(&p, &len, &type, 2))\n+\t\t\tbreak;\n+\t\tif (!quic_get_int(&p, &len, &length, 2))\n+\t\t\tbreak;\n+\t\tif (len < (u32)length) /* Incomplete TLS extensions. */\n+\t\t\treturn 0;\n+\t\tif (type == TLS_EXT_alpn) { /* Found ALPN extension. */\n+\t\t\tif (length > QUIC_ALPN_MAX_LEN)\n+\t\t\t\treturn err;\n+\t\t\tlen = length;\n+\t\t\tfound = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t\t/* Skip non-ALPN extensions. */\n+\t\tp += length;\n+\t\tlen -= length;\n+\t\tif (exts++ >= TLS_MAX_EXTENSIONS)\n+\t\t\treturn err;\n+\t}\n+\tif (!found) { /* No ALPN ext: set alpn->len = 0 and alpn->data = p. */\n+\t\tquic_data(alpn, p, 0);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Parse ALPN protocols list length (2 bytes). */\n+\tif (!quic_get_int(&p, &len, &length, 2) || length > (u64)len)\n+\t\treturn err;\n+\tquic_data(alpn, p, length); /* Store ALPN list in alpn->data. */\n+\tlen = length;\n+\twhile (len) { /* Validate ALPN protocols list format. */\n+\t\tif (!quic_get_int(&p, &len, &length, 1) || length > (u64)len) {\n+\t\t\t/* Bad ALPN: set alpn->len = 0, alpn->data = NULL. */\n+\t\t\tquic_data(alpn, NULL, 0);\n+\t\t\treturn err;\n+\t\t}\n+\t\tlen -= length;\n+\t\tp += length;\n+\t}\n+\tpr_debug(\"%s: alpn_len: %d\\n\", __func__, alpn->len);\n+\treturn 0;\n+}\n+\n+#define QUIC_FRAME_CRYPTO\t0x06\n+\n+/* Parse ALPN from a QUIC Initial packet.\n+ *\n+ * This function processes a QUIC Initial packet to extract the ALPN from the\n+ * TLS ClientHello message inside the QUIC CRYPTO frame. It verifies packet\n+ * type, version compatibility, decrypts the packet payload, and locates the\n+ * CRYPTO frame to parse the TLS ClientHello. Finally, it calls\n+ * quic_packet_get_alpn() to extract the ALPN extension data.\n+ *\n+ * Return: 0 on success or no ALPN found, a negative error code on failed\n+ * parsing.\n+ */\n+static int quic_packet_parse_alpn(struct sk_buff *skb, struct quic_data *alpn)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tstruct net *net = sock_net(skb->sk);\n+\tstruct quic_conn_id dcid, scid;\n+\tu32 len = skb->len, version;\n+\tstruct quic_crypto *crypto;\n+\tu8 *p = skb->data, type;\n+\tstruct quic_data token;\n+\tu64 offset, length;\n+\tint err;\n+\n+\tif (!static_branch_unlikely(&quic_alpn_demux_key))\n+\t\treturn 0;\n+\terr = quic_packet_get_long_header(&dcid, &scid, &version, &p, &len);\n+\tif (err)\n+\t\treturn err;\n+\tif (!quic_packet_compatible_versions(version))\n+\t\treturn 0;\n+\t/* Only parse Initial packets. */\n+\ttype = quic_packet_version_get_type(version, quic_hshdr(skb)->type);\n+\tif (type != QUIC_PACKET_INITIAL)\n+\t\treturn 0;\n+\terr = quic_packet_get_token(&token, &p, &len);\n+\tif (err)\n+\t\treturn err;\n+\tif (!quic_get_var(&p, &len, &length) || length > (u64)len)\n+\t\treturn -EINVAL;\n+\tif (quic_packet_backlog_schedule(net, skb))\n+\t\treturn -EINPROGRESS;\n+\tcb->length = (u16)length;\n+\n+\t/* Install initial keys for packet decryption to crypto. */\n+\tcrypto = &quic_net(net)->crypto;\n+\terr = quic_crypto_initial_keys_install(crypto, &dcid, version, 1);\n+\tif (err)\n+\t\treturn err;\n+\tcb->number_offset = (u16)(p - skb->data);\n+\terr = quic_crypto_decrypt(crypto, skb);\n+\tif (err) {\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_DECDROP);\n+\t\treturn err;\n+\t}\n+\n+\tQUIC_INC_STATS(net, QUIC_MIB_PKT_DECFASTPATHS);\n+\tcb->resume = 1; /* Mark this packet as already decrypted. */\n+\n+\t/* Find the QUIC CRYPTO frame. */\n+\tp = skb->data + cb->number_offset + cb->number_len;\n+\tlen = cb->length - cb->number_len - QUIC_TAG_LEN;\n+\tfor (; len && !(*p); p++, len--) /* Skip the padding frame. */\n+\t\t;\n+\tif (!len-- || *p++ != QUIC_FRAME_CRYPTO)\n+\t\treturn 0;\n+\tif (!quic_get_var(&p, &len, &offset) || offset)\n+\t\treturn 0;\n+\tif (!quic_get_var(&p, &len, &length) || length > (u64)len)\n+\t\treturn 0;\n+\n+\t/* Parse the TLS CLIENT_HELLO message. */\n+\treturn quic_packet_get_alpn(alpn, p, length);\n+}\n+\n+/* Lookup listening socket for Client Initial packet (in process context). */\n+static struct sock *quic_packet_get_listen_sock(struct sk_buff *skb)\n+{\n+\tunion quic_addr daddr, saddr;\n+\tstruct quic_data alpns = {};\n+\tstruct sock *sk;\n+\tint err;\n+\n+\tquic_get_msg_addrs(skb, &daddr, &saddr);\n+\n+\terr = quic_packet_parse_alpn(skb, &alpns);\n+\tif (err)\n+\t\treturn ERR_PTR(err);\n+\n+\tsk = quic_listen_sock_lookup(skb, &daddr, &saddr, &alpns);\n+\tif (!sk)\n+\t\treturn ERR_PTR(-ENOENT);\n+\treturn sk;\n+}\n+\n+/* Determine the QUIC socket associated with an incoming packet. */\n+static struct sock *quic_packet_get_sock(struct sk_buff *skb)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tstruct net *net = sock_net(skb->sk);\n+\tstruct quic_conn_id dcid, *conn_id;\n+\tunion quic_addr daddr, saddr;\n+\tstruct quic_data alpns = {};\n+\tstruct sock *sk = NULL;\n+\tu32 len = skb->len;\n+\tu8 *p = skb->data;\n+\tint err;\n+\n+\tif (skb->len < QUIC_HLEN)\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\tif (quic_hdr(skb)->form == QUIC_PACKET_FORM_SHORT) {\n+\t\t/* Short header path. */\n+\t\tif (skb->len < QUIC_HLEN + QUIC_CONN_ID_DEF_LEN)\n+\t\t\treturn ERR_PTR(-EINVAL);\n+\t\t/* Fast path: look up QUIC connection by fixed-length DCID\n+\t\t * (Currently, only QUIC_CONN_ID_DEF_LEN-length SCIDs are used).\n+\t\t */\n+\t\tconn_id = quic_conn_id_lookup(net, skb->data + QUIC_HLEN,\n+\t\t\t\t\t QUIC_CONN_ID_DEF_LEN);\n+\t\tif (conn_id) {\n+\t\t\tcb->seqno = quic_conn_id_number(conn_id);\n+\t\t\t/* Return associated socket. */\n+\t\t\treturn quic_conn_id_sk(conn_id);\n+\t\t}\n+\n+\t\t/* Fallback: listener socket lookup\n+\t\t * (May be used to send a stateless reset from a listen socket).\n+\t\t */\n+\t\tquic_get_msg_addrs(skb, &daddr, &saddr);\n+\t\tsk = quic_listen_sock_lookup(skb, &daddr, &saddr, &alpns);\n+\t\tif (sk)\n+\t\t\treturn sk;\n+\t\t/* Final fallback: address-based connection lookup\n+\t\t * (May be used to receive a stateless reset).\n+\t\t */\n+\t\tsk = quic_sock_lookup(skb, &daddr, &saddr, skb->sk, NULL);\n+\t\tif (!sk)\n+\t\t\treturn ERR_PTR(-ENOENT);\n+\t\treturn sk;\n+\t}\n+\n+\t/* Long header path. */\n+\terr = quic_packet_get_long_header(&dcid, NULL, NULL, &p, &len);\n+\tif (err)\n+\t\treturn ERR_PTR(err);\n+\t/* Fast path: look up QUIC connection by parsed DCID. */\n+\tconn_id = quic_conn_id_lookup(net, dcid.data, dcid.len);\n+\tif (conn_id) {\n+\t\tcb->seqno = quic_conn_id_number(conn_id);\n+\t\treturn quic_conn_id_sk(conn_id); /* Return associated socket. */\n+\t}\n+\n+\t/* Fallback: address + DCID lookup\n+\t * (May be used for 0-RTT or a follow-up Client Initial packet).\n+\t */\n+\tquic_get_msg_addrs(skb, &daddr, &saddr);\n+\tsk = quic_sock_lookup(skb, &daddr, &saddr, skb->sk, &dcid);\n+\tif (sk)\n+\t\treturn sk;\n+\t/* Final fallback: listener socket lookup\n+\t * (Used for receiving the first Client Initial packet).\n+\t */\n+\terr = quic_packet_parse_alpn(skb, &alpns);\n+\tif (err)\n+\t\treturn ERR_PTR(err);\n+\tsk = quic_listen_sock_lookup(skb, &daddr, &saddr, &alpns);\n+\tif (!sk)\n+\t\treturn ERR_PTR(-ENOENT);\n+\treturn sk;\n+}\n+\n+/* Entry point for processing received QUIC packets. */\n+int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp)\n+{\n+\tstruct net *net = sock_net(sk);\n+\tint err;\n+\n+\tif (unlikely(icmp))\n+\t\treturn quic_packet_rcv_err(sk, skb);\n+\n+\t/* Save the UDP socket to skb->sk for later QUIC socket lookup. */\n+\tif (skb_linearize(skb) || !skb_set_owner_sk_safe(skb, sk)) {\n+\t\terr = -EINVAL;\n+\t\tgoto err;\n+\t}\n+\n+\t/* Look up socket from socket or connection IDs hash tables. */\n+\tsk = quic_packet_get_sock(skb);\n+\tif (IS_ERR(sk)) {\n+\t\terr = PTR_ERR(sk);\n+\t\tif (err == -EINPROGRESS)\n+\t\t\treturn 0;\n+\t\tgoto err;\n+\t}\n+\n+\tbh_lock_sock(sk);\n+\tif (sock_owned_by_user(sk)) {\n+\t\t/* Socket is busy (owned by user context): queue to backlog. */\n+\t\terr = sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf));\n+\t\tif (err) {\n+\t\t\tbh_unlock_sock(sk);\n+\t\t\tsock_put(sk);\n+\t\t\tgoto err;\n+\t\t}\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_RCVBACKLOGS);\n+\t} else {\n+\t\t/* Socket not busy: process immediately. */\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_RCVFASTPATHS);\n+\t\tsk->sk_backlog_rcv(sk, skb); /* quic_packet_process(). */\n+\t}\n+\tbh_unlock_sock(sk);\n+\tsock_put(sk);\n+\treturn 0;\n+err:\n+\tpr_debug(\"%s: failed, len: %d, err: %d\\n\", __func__, skb->len, err);\n+\tQUIC_INC_STATS(net, QUIC_MIB_PKT_RCVDROP);\n+\tkfree_skb(skb);\n+\treturn err;\n+}\n+\n+static int quic_packet_listen_process(struct sock *sk, struct sk_buff *skb)\n+{\n+\tkfree_skb(skb);\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_packet_handshake_process(struct sock *sk, struct sk_buff *skb)\n+{\n+\tkfree_skb(skb);\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_packet_app_process(struct sock *sk, struct sk_buff *skb)\n+{\n+\tkfree_skb(skb);\n+\treturn -EOPNOTSUPP;\n+}\n+\n+int quic_packet_process(struct sock *sk, struct sk_buff *skb)\n+{\n+\tif (quic_is_closed(sk)) {\n+\t\tkfree_skb(skb);\n+\t\treturn 0;\n+\t}\n+\n+\tif (quic_is_listen(sk))\n+\t\treturn quic_packet_listen_process(sk, skb);\n+\n+\tif (quic_hdr(skb)->form == QUIC_PACKET_FORM_LONG)\n+\t\treturn quic_packet_handshake_process(sk, skb);\n+\n+\treturn quic_packet_app_process(sk, skb);\n+}\n+\n+/* Work function to process packets in the backlog queue. */\n+void quic_packet_backlog_work(struct work_struct *work)\n+{\n+\tstruct quic_net *qn = container_of(work, struct quic_net, work);\n+\tstruct sk_buff_head *head = &qn->backlog_list;\n+\tstruct sk_buff *skb;\n+\tstruct sock *sk;\n+\n+\twhile ((skb = skb_dequeue(head)) != NULL) {\n+\t\tsk = quic_packet_get_listen_sock(skb);\n+\t\tif (IS_ERR(sk)) {\n+\t\t\tQUIC_INC_STATS(sock_net(skb->sk), QUIC_MIB_PKT_RCVDROP);\n+\t\t\tkfree_skb(skb);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tlock_sock(sk);\n+\t\tquic_packet_process(sk, skb);\n+\t\trelease_sock(sk);\n+\t\tsock_put(sk);\n+\t}\n+}\n+\n /* Make these fixed for easy coding. */\n #define QUIC_PACKET_NUMBER_LEN\tQUIC_PN_MAX_LEN\n #define QUIC_PACKET_LENGTH_LEN\t4\ndiff --git a/net/quic/packet.h b/net/quic/packet.h\nindex 834c4f72271b..9e2f429d4d93 100644\n--- a/net/quic/packet.h\n+++ b/net/quic/packet.h\n@@ -57,6 +57,8 @@ struct quic_packet {\n \n #define QUIC_VERSION_LEN\t\t4\n \n+#define QUIC_ALPN_MAX_LEN\t\t128\n+\n #define QUIC_PACKET_MSS_NORMAL\t\t0\n #define QUIC_PACKET_MSS_DGRAM\t\t1\n \n@@ -106,6 +108,7 @@ static inline void quic_packet_reset(struct quic_packet *packet)\n \tpacket->ack_immediate = 0;\n }\n \n+int quic_packet_process(struct sock *sk, struct sk_buff *skb);\n int quic_packet_config(struct sock *sk, u8 level, u8 path);\n \n int quic_packet_xmit(struct sock *sk, struct sk_buff *skb);\n@@ -115,3 +118,8 @@ int quic_packet_route(struct sock *sk);\n void quic_packet_mss_update(struct sock *sk, u32 mss);\n void quic_packet_flush(struct sock *sk);\n void quic_packet_init(struct sock *sk);\n+\n+u32 *quic_packet_compatible_versions(u32 version);\n+\n+void quic_packet_backlog_work(struct work_struct *work);\n+void quic_packet_rcv_err_pmtu(struct sock *sk);\ndiff --git a/net/quic/path.c b/net/quic/path.c\nindex 7f72fdd9c45f..eb8cb48fe56e 100644\n--- a/net/quic/path.c\n+++ b/net/quic/path.c\n@@ -25,14 +25,14 @@ static int quic_udp_rcv(struct sock *sk, struct sk_buff *skb)\n \n \tskb_pull(skb, sizeof(struct udphdr));\n \tskb_dst_force(skb);\n-\tkfree_skb(skb);\n+\tquic_packet_rcv(sk, skb, false);\n \t/* .encap_rcv must return 0 if skb was either consumed or dropped. */\n \treturn 0;\n }\n \n static int quic_udp_err(struct sock *sk, struct sk_buff *skb)\n {\n-\treturn 0;\n+\treturn quic_packet_rcv(sk, skb, true);\n }\n \n static void quic_udp_sock_put_work(struct work_struct *work)\ndiff --git a/net/quic/path.h b/net/quic/path.h\nindex ca18eb38e907..9f772d989676 100644\n--- a/net/quic/path.h\n+++ b/net/quic/path.h\n@@ -163,6 +163,8 @@ quic_path_orig_dcid(struct quic_path_group *paths)\n \treturn paths->retry ? &paths->retry_dcid : &paths->orig_dcid;\n }\n \n+int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp);\n+\n bool quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa,\n \t\t\t union quic_addr *da, struct sock *sk);\n int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path);\ndiff --git a/net/quic/protocol.c b/net/quic/protocol.c\nindex 7f055c88bbde..0012d362330a 100644\n--- a/net/quic/protocol.c\n+++ b/net/quic/protocol.c\n@@ -270,6 +270,9 @@ static int __net_init quic_net_init(struct net *net)\n \t\treturn err;\n \t}\n \n+\tINIT_WORK(&qn->work, quic_packet_backlog_work);\n+\tskb_queue_head_init(&qn->backlog_list);\n+\n #if IS_ENABLED(CONFIG_PROC_FS)\n \terr = quic_net_proc_init(net);\n \tif (err) {\n@@ -288,6 +291,8 @@ static void __net_exit quic_net_exit(struct net *net)\n #if IS_ENABLED(CONFIG_PROC_FS)\n \tquic_net_proc_exit(net);\n #endif\n+\tdisable_work_sync(&qn->work);\n+\tskb_queue_purge(&qn->backlog_list);\n \tquic_crypto_free(&qn->crypto);\n \tfree_percpu(qn->stat);\n \tqn->stat = NULL;\ndiff --git a/net/quic/protocol.h b/net/quic/protocol.h\nindex b8584e72ff14..25001aaaad4a 100644\n--- a/net/quic/protocol.h\n+++ b/net/quic/protocol.h\n@@ -51,6 +51,10 @@ struct quic_net {\n #endif\n \t/* Context for decrypting Initial packets for ALPN */\n \tstruct quic_crypto crypto;\n+\n+\t/* Queue of packets deferred for processing in process context */\n+\tstruct sk_buff_head backlog_list;\n+\tstruct work_struct work; /* Work to drain/process backlog_list */\n };\n \n struct quic_net *quic_net(struct net *net);\ndiff --git a/net/quic/socket.c b/net/quic/socket.c\nindex b9fbc33c0f79..bb52f83e9e54 100644\n--- a/net/quic/socket.c\n+++ b/net/quic/socket.c\n@@ -24,6 +24,149 @@ static void quic_enter_memory_pressure(struct sock *sk)\n \tWRITE_ONCE(quic_memory_pressure, 1);\n }\n \n+/* Lookup a connected QUIC socket based on address and dest connection ID.\n+ *\n+ * This function searches the established (non-listening) QUIC socket table for\n+ * a socket that matches the source and dest addresses and, optionally, the\n+ * dest connection ID (DCID). The value returned by quic_path_orig_dcid() might\n+ * be the original dest connection ID from the ClientHello or the Source\n+ * Connection ID from a Retry packet before.\n+ *\n+ * The DCID is provided from a handshake packet when searching by source\n+ * connection ID fails, such as when the peer has not yet received server's\n+ * response and updated the DCID.\n+ *\n+ * Return: A pointer to the matching connected socket, or NULL if no match is\n+ * found.\n+ */\n+struct sock *quic_sock_lookup(struct sk_buff *skb, union quic_addr *sa,\n+\t\t\t union quic_addr *da, struct sock *usk,\n+\t\t\t struct quic_conn_id *dcid)\n+{\n+\tstruct net *net = sock_net(usk);\n+\tstruct quic_path_group *paths;\n+\tstruct hlist_nulls_node *node;\n+\tstruct quic_shash_head *head;\n+\tstruct sock *sk = NULL, *tmp;\n+\tstruct quic_conn_id *odcid;\n+\tunsigned int hash;\n+\n+\thash = quic_sock_hash(net, sa, da);\n+\thead = quic_sock_head(hash);\n+\n+\trcu_read_lock();\n+begin:\n+\tsk_nulls_for_each_rcu(tmp, node, &head->head) {\n+\t\tif (net != sock_net(tmp))\n+\t\t\tcontinue;\n+\t\tpaths = quic_paths(tmp);\n+\t\todcid = quic_path_orig_dcid(paths);\n+\t\tif (quic_cmp_sk_addr(tmp, quic_path_saddr(paths, 0), sa) &&\n+\t\t quic_cmp_sk_addr(tmp, quic_path_daddr(paths, 0), da) &&\n+\t\t quic_path_usock(paths, 0) == usk &&\n+\t\t (!dcid || !quic_conn_id_cmp(odcid, dcid))) {\n+\t\t\tsk = tmp;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\t/* If the nulls value we got at the end of the iteration is different\n+\t * from the expected one, we must restart the lookup as the list was\n+\t * modified concurrently.\n+\t */\n+\tif (!sk && get_nulls_value(node) != hash)\n+\t\tgoto begin;\n+\n+\tif (sk && unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))\n+\t\tsk = NULL;\n+\trcu_read_unlock();\n+\treturn sk;\n+}\n+\n+/* Find the listening QUIC socket for an incoming packet.\n+ *\n+ * This function searches the QUIC socket table for a listening socket that\n+ * matches the dest address and port, and the ALPN(s) if presented in the\n+ * ClientHello. If multiple listening sockets are bound to the same address,\n+ * port, and ALPN(s) (e.g., via SO_REUSEPORT), this function selects a socket\n+ * from the reuseport group.\n+ *\n+ * Return: A pointer to the matching listening socket, or NULL if no match is\n+ * found.\n+ */\n+struct sock *quic_listen_sock_lookup(struct sk_buff *skb, union quic_addr *sa,\n+\t\t\t\t union quic_addr *da,\n+\t\t\t\t struct quic_data *alpns)\n+{\n+\tstruct net *net = sock_net(skb->sk);\n+\tstruct hlist_nulls_node *node;\n+\tstruct sock *sk = NULL, *tmp;\n+\tstruct quic_shash_head *head;\n+\tstruct quic_data alpn;\n+\tunion quic_addr *a;\n+\tu32 hash, len;\n+\tu64 length;\n+\tu8 *p;\n+\n+\thash = quic_listen_sock_hash(net, ntohs(sa->v4.sin_port));\n+\thead = quic_listen_sock_head(hash);\n+\n+\trcu_read_lock();\n+begin:\n+\tif (!alpns->len) { /* No ALPNs or parse failed */\n+\t\tsk_nulls_for_each_rcu(tmp, node, &head->head) {\n+\t\t\t/* If alpns->data != NULL, TLS parsing succeeded but no\n+\t\t\t * ALPN was found. In this case, only match sockets\n+\t\t\t * that have no ALPN set.\n+\t\t\t */\n+\t\t\ta = quic_path_saddr(quic_paths(tmp), 0);\n+\t\t\tif (net == sock_net(tmp) &&\n+\t\t\t quic_cmp_sk_addr(tmp, a, sa) &&\n+\t\t\t quic_path_usock(quic_paths(tmp), 0) == skb->sk &&\n+\t\t\t (!alpns->data || !quic_alpn(tmp)->len)) {\n+\t\t\t\tsk = tmp;\n+\t\t\t\tif (!quic_is_any_addr(a))\n+\t\t\t\t\tbreak; /* Prefer specific addr match. */\n+\t\t\t}\n+\t\t}\n+\t\tgoto out;\n+\t}\n+\n+\t/* ALPN present: loop through each ALPN entry. */\n+\tfor (p = alpns->data, len = alpns->len; len;\n+\t len -= length, p += length) {\n+\t\tquic_get_int(&p, &len, &length, 1);\n+\t\tquic_data(&alpn, p, length);\n+\t\tsk_nulls_for_each_rcu(tmp, node, &head->head) {\n+\t\t\ta = quic_path_saddr(quic_paths(tmp), 0);\n+\t\t\tif (net == sock_net(tmp) &&\n+\t\t\t quic_cmp_sk_addr(tmp, a, sa) &&\n+\t\t\t quic_path_usock(quic_paths(tmp), 0) == skb->sk &&\n+\t\t\t quic_data_has(quic_alpn(tmp), &alpn)) {\n+\t\t\t\tsk = tmp;\n+\t\t\t\tif (!quic_is_any_addr(a))\n+\t\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t\tif (sk)\n+\t\t\tbreak;\n+\t}\n+out:\n+\t/* If the nulls value we got at the end of the iteration is different\n+\t * from the expected one, we must restart the lookup as the list was\n+\t * modified concurrently.\n+\t */\n+\tif (!sk && get_nulls_value(node) != hash)\n+\t\tgoto begin;\n+\n+\tif (sk && sk->sk_reuseport)\n+\t\tsk = reuseport_select_sock(sk, quic_addr_hash(net, da), skb, 1);\n+\n+\tif (sk && unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))\n+\t\tsk = NULL;\n+\trcu_read_unlock();\n+\treturn sk;\n+}\n+\n static void quic_write_space(struct sock *sk)\n {\n \t__poll_t mask = EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;\n@@ -213,6 +356,10 @@ static void quic_release_cb(struct sock *sk)\n \t\tnflags = flags & ~QUIC_DEFERRED_ALL;\n \t} while (!try_cmpxchg(&sk->sk_tsq_flags, &flags, nflags));\n \n+\tif (flags & QUIC_F_MTU_REDUCED_DEFERRED) {\n+\t\tquic_packet_rcv_err_pmtu(sk);\n+\t\t__sock_put(sk);\n+\t}\n \tif (flags & QUIC_F_LOSS_DEFERRED) {\n \t\tquic_timer_loss_handler(sk);\n \t\t__sock_put(sk);\n@@ -262,6 +409,7 @@ struct proto quic_prot = {\n \t.accept\t\t= quic_accept,\n \t.hash\t\t= quic_hash,\n \t.unhash\t\t= quic_unhash,\n+\t.backlog_rcv\t= quic_packet_process,\n \t.release_cb\t= quic_release_cb,\n \t.no_autobind\t= true,\n \t.obj_size\t= sizeof(struct quic_sock),\n@@ -292,6 +440,7 @@ struct proto quicv6_prot = {\n \t.accept\t\t= quic_accept,\n \t.hash\t\t= quic_hash,\n \t.unhash\t\t= quic_unhash,\n+\t.backlog_rcv\t= quic_packet_process,\n \t.release_cb\t= quic_release_cb,\n \t.no_autobind\t= true,\n \t.obj_size\t= sizeof(struct quic6_sock),\ndiff --git a/net/quic/socket.h b/net/quic/socket.h\nindex 1efc76ec2033..3c1bea767be9 100644\n--- a/net/quic/socket.h\n+++ b/net/quic/socket.h\n@@ -200,3 +200,10 @@ static inline void quic_set_state(struct sock *sk, int state)\n \tinet_sk_set_state(sk, state);\n \tsk->sk_state_change(sk);\n }\n+\n+struct sock *quic_listen_sock_lookup(struct sk_buff *skb, union quic_addr *sa,\n+\t\t\t\t union quic_addr *da,\n+\t\t\t\t struct quic_data *alpns);\n+struct sock *quic_sock_lookup(struct sk_buff *skb, union quic_addr *sa,\n+\t\t\t union quic_addr *da, struct sock *usk,\n+\t\t\t struct quic_conn_id *dcid);\n", "prefixes": [ "net-next", "v11", "15/15" ] }