| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/14 17:48 | flow | patch-triage |
8mArgs:null Results: {
"EnableConfigs": [
"IP_QUIC",
"INET",
"IPV6"
],
"FocusSymbols": [
"quic_init_sock",
"quic_destroy_sock",
"quic_sock_destruct",
"quic_do_setsockopt",
"quic_do_getsockopt",
"quic_common_setsockopt",
"quic_common_getsockopt",
"quic_setsockopt",
"quic_getsockopt",
"quic_release_cb",
"quic_stream_init",
"quic_stream_free",
"quic_pnspace_init",
"quic_pnspace_free",
"quic_conn_id_set_init",
"quic_conn_id_set_free",
"quic_packet_init",
"quic_packet_free",
"quic_timer_init",
"quic_timer_free",
"quic_cong_init",
"quic_path_init",
"quic_path_unbind",
"quic_crypto_free",
"quic_snmp_seq_show"
],
"KMSANReasoning": "The patch series introduces initial protocol support and foundational structures for QUIC (RFC 9000), including socket operations, packet framing, connection ID handling, congestion control, and cryptographic wrappers.\n\nKMSAN applicability was evaluated against the new protocol logic:\n1. Kernel-to-user info-leaks: QUIC-specific socket options (quic_do_getsockopt), message reception (quic_recvmsg), accept, connect, and bind are currently stubs returning -EOPNOTSUPP. Address retrieval (quic_get_sk_addr) delegates to inet_getname / inet6_getname, which properly populate and clear padding (e.g. sin_zero) in sockaddr structures.\n2. Uninitialized memory access: Heap allocations across the subsystem either use kzalloc (e.g. stream structures, pn_map bitmaps, connection ID structures) or explicitly initialize every struct field upon allocation (quic_request_sock, quic_udp_sock). Stack buffers in cryptographic and framing routines are explicitly zeroed or bounded by parsed lengths before consumption.\n3. Control flow: Variable parsing helpers (quic_get_int, quic_get_var, quic_packet_get_long_header) check buffer bounds before reading and update outputs only on success.\n\nAny potential bugs in this patch series (such as bounds errors in packet parsing, use-after-free in reference-counted UDP tunnel sockets or RCU-managed connection IDs, or lock hierarchy issues) are covered by standard KASAN and LOCKDEP. There are no uninitialized memory risks requiring a dedicated KMSAN session.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces the foundational in-kernel implementation of the QUIC transport protocol (IPPROTO_QUIC == 261, SOL_QUIC == 288), registering AF_INET and AF_INET6 SOCK_STREAM and SOCK_DGRAM protocol switches. Although higher-level features like packet exchange and full stream I/O are partially stubbed out with -EOPNOTSUPP at this stage, the core socket creation, initialization, teardown, timer setup, memory allocation accounting, socket options, procfs stats, and sysctl interfaces are fully reachable and executable from unprivileged userspace via socket() and standard socket syscalls. Fuzzing is warranted to test resource allocation, lifecycle management, and socket state handling.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/14 17:48 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 85c120c4d16fd7ce68c939879ba590511ca36840\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Sep 14 17:48:10 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst\nindex 208f46967ee59..6dd9d6970cec4 100644\n--- a/Documentation/networking/ip-sysctl.rst\n+++ b/Documentation/networking/ip-sysctl.rst\n@@ -3809,6 +3809,45 @@ l3mdev_accept - BOOLEAN\n \tDefault: 1 (enabled)\n \n \n+``/proc/sys/net/quic/*`` Variables\n+===================================\n+\n+quic_mem - vector of 3 LONGs: min, pressure, max\n+\tNumber of pages allowed for queueing by all QUIC sockets.\n+\n+\tmin: below this number of pages QUIC is not bothered about its\n+\tmemory appetite.\n+\n+\tpressure: when amount of memory allocated by QUIC exceeds this number\n+\tof pages, QUIC moderates its memory consumption and enters memory\n+\tpressure mode, which is exited when memory consumption falls\n+\tunder \"min\".\n+\n+\tmax: number of pages allowed for queueing by all QUIC sockets.\n+\n+\tDefaults are calculated at boot time from amount of available\n+\tmemory.\n+\n+quic_rmem - vector of 3 INTEGERs: min, default, max\n+\tOnly the first value (\"min\") is used, \"default\" and \"max\" are\n+\tignored.\n+\n+\tmin: Minimal size of receive buffer used by QUIC sockets.\n+\tIt is guaranteed to each QUIC socket, even under moderate memory\n+\tpressure.\n+\n+\tDefault: 4K\n+\n+quic_wmem - vector of 3 INTEGERs: min, default, max\n+\tOnly the first value (\"min\") is used, \"default\" and \"max\" are\n+\tignored.\n+\n+\tmin: Amount of memory reserved for send buffers for QUIC sockets.\n+\tEach QUIC socket has rights to use it due to fact of its birth.\n+\n+\tDefault: 4K\n+\n+\n ``/proc/sys/net/core/*``\n ========================\n \ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 0e04d92d1b098..ba5b5249e8fed 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -22666,6 +22666,15 @@ L:\tlinux-wireless@vger.kernel.org\n S:\tMaintained\n F:\tdrivers/net/wireless/quantenna/\n \n+QUIC PROTOCOL\n+M:\tXin Long \u003clucien.xin@gmail.com\u003e\n+L:\tquic@lists.linux.dev\n+S:\tMaintained\n+W:\thttps://github.com/lxin/quic\n+F:\tinclude/linux/quic.h\n+F:\tinclude/uapi/linux/quic.h\n+F:\tnet/quic/\n+\n RADEON and AMDGPU DRM DRIVERS\n M:\tAlex Deucher \u003calexander.deucher@amd.com\u003e\n M:\tChristian König \u003cchristian.koenig@amd.com\u003e\ndiff --git a/include/linux/quic.h b/include/linux/quic.h\nnew file mode 100644\nindex 0000000000000..51c099e9547f5\n--- /dev/null\n+++ b/include/linux/quic.h\n@@ -0,0 +1,38 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#ifndef _LINUX_QUIC_H\n+#define _LINUX_QUIC_H\n+\n+#include \u003clinux/sockptr.h\u003e\n+#include \u003cuapi/linux/quic.h\u003e\n+\n+struct sock;\n+\n+#if IS_REACHABLE(CONFIG_IP_QUIC)\n+int quic_do_setsockopt(struct sock *sk, int optname, sockptr_t optval,\n+\t\t unsigned int optlen);\n+int quic_do_getsockopt(struct sock *sk, int optname, sockptr_t optval,\n+\t\t sockptr_t optlen);\n+#else\n+static inline int quic_do_setsockopt(struct sock *sk, int optname,\n+\t\t\t\t sockptr_t optval, unsigned int optlen)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static inline int quic_do_getsockopt(struct sock *sk, int optname,\n+\t\t\t\t sockptr_t optval, sockptr_t optlen)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+#endif\n+\n+#endif\ndiff --git a/include/linux/socket.h b/include/linux/socket.h\nindex 5a5eb12501032..aeea3baa96732 100644\n--- a/include/linux/socket.h\n+++ b/include/linux/socket.h\n@@ -400,6 +400,7 @@ struct ucred {\n #define SOL_MCTP\t285\n #define SOL_SMC\t\t286\n #define SOL_VSOCK\t287\n+#define SOL_QUIC\t288\n \n /* IPX options */\n #define IPX_TYPE\t1\ndiff --git a/include/trace/events/sock.h b/include/trace/events/sock.h\nindex b5310439536e0..ed43fe6d2705b 100644\n--- a/include/trace/events/sock.h\n+++ b/include/trace/events/sock.h\n@@ -20,7 +20,8 @@\n #define inet_protocol_names\t\t\\\n \t\tEM(IPPROTO_TCP)\t\t\t\\\n \t\tEM(IPPROTO_SCTP)\t\t\\\n-\t\tEMe(IPPROTO_MPTCP)\n+\t\tEM(IPPROTO_MPTCP)\t\t\\\n+\t\tEMe(IPPROTO_QUIC)\n \n #define tcp_state_names\t\t\t\\\n \t\tEM(TCP_ESTABLISHED)\t\t\\\ndiff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h\nindex ced0fc3c3aa53..e4072152f2e62 100644\n--- a/include/uapi/linux/in.h\n+++ b/include/uapi/linux/in.h\n@@ -85,6 +85,8 @@ enum {\n #define IPPROTO_RAW\t\tIPPROTO_RAW\n IPPROTO_SMC = 256,\t\t/* Shared Memory Communications\t\t*/\n #define IPPROTO_SMC\t\tIPPROTO_SMC\n+ IPPROTO_QUIC = 261,\t\t/* A UDP-Based Multiplexed Secure Transport */\n+#define IPPROTO_QUIC\t\tIPPROTO_QUIC\n IPPROTO_MPTCP = 262,\t\t/* Multipath TCP connection\t\t*/\n #define IPPROTO_MPTCP\t\tIPPROTO_MPTCP\n IPPROTO_MAX\ndiff --git a/include/uapi/linux/quic.h b/include/uapi/linux/quic.h\nnew file mode 100644\nindex 0000000000000..ae37d6a7bc36f\n--- /dev/null\n+++ b/include/uapi/linux/quic.h\n@@ -0,0 +1,241 @@\n+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#ifndef _UAPI_LINUX_QUIC_H\n+#define _UAPI_LINUX_QUIC_H\n+\n+#include \u003clinux/types.h\u003e\n+#ifdef __KERNEL__\n+#include \u003clinux/socket.h\u003e\n+#else\n+#include \u003csys/socket.h\u003e /* for MSG_* flags */\n+#endif\n+\n+/* NOTE: Structure descriptions are specified in:\n+ * https://datatracker.ietf.org/doc/html/draft-lxin-quic-socket-apis\n+ */\n+\n+/* Send or Receive Options APIs */\n+enum quic_cmsg_type {\n+\tQUIC_STREAM_INFO,\n+\tQUIC_HANDSHAKE_INFO,\n+};\n+\n+#define QUIC_STREAM_TYPE_SERVER_MASK\t0x01\n+#define QUIC_STREAM_TYPE_UNI_MASK\t0x02\n+#define QUIC_STREAM_TYPE_MASK\t\t0x03\n+\n+enum quic_msg_flags {\n+\t/* flags for stream_flags */\n+\tMSG_QUIC_STREAM_NEW\t\t= MSG_SYN,\n+\tMSG_QUIC_STREAM_FIN\t\t= MSG_FIN,\n+\tMSG_QUIC_STREAM_UNI\t\t= MSG_CONFIRM,\n+\tMSG_QUIC_STREAM_DONTWAIT\t= MSG_EOR,\n+\tMSG_QUIC_STREAM_SNDBLOCK\t= MSG_ERRQUEUE,\n+\n+\t/* extended flags for msg_flags */\n+\tMSG_QUIC_DATAGRAM\t\t= MSG_RST,\n+\tMSG_QUIC_NOTIFICATION\t\t= MSG_MORE,\n+};\n+\n+enum quic_crypto_level {\n+\tQUIC_CRYPTO_APP,\n+\tQUIC_CRYPTO_INITIAL,\n+\tQUIC_CRYPTO_HANDSHAKE,\n+\tQUIC_CRYPTO_EARLY,\n+\tQUIC_CRYPTO_MAX,\n+};\n+\n+struct quic_handshake_info {\n+\t__u8\tcrypto_level;\n+};\n+\n+struct quic_stream_info {\n+\t__s64\tstream_id;\n+\t__u32\tstream_flags;\n+};\n+\n+/* Socket Options APIs */\n+#define QUIC_SOCKOPT_EVENT\t\t\t\t0\n+#define QUIC_SOCKOPT_STREAM_OPEN\t\t\t1\n+#define QUIC_SOCKOPT_STREAM_RESET\t\t\t2\n+#define QUIC_SOCKOPT_STREAM_STOP_SENDING\t\t3\n+#define QUIC_SOCKOPT_CONNECTION_ID\t\t\t4\n+#define QUIC_SOCKOPT_CONNECTION_CLOSE\t\t\t5\n+#define QUIC_SOCKOPT_CONNECTION_MIGRATION\t\t6\n+#define QUIC_SOCKOPT_KEY_UPDATE\t\t\t\t7\n+#define QUIC_SOCKOPT_TRANSPORT_PARAM\t\t\t8\n+#define QUIC_SOCKOPT_CONFIG\t\t\t\t9\n+#define QUIC_SOCKOPT_TOKEN\t\t\t\t10\n+#define QUIC_SOCKOPT_ALPN\t\t\t\t11\n+#define QUIC_SOCKOPT_SESSION_TICKET\t\t\t12\n+#define QUIC_SOCKOPT_CRYPTO_SECRET\t\t\t13\n+#define QUIC_SOCKOPT_TRANSPORT_PARAM_EXT\t\t14\n+\n+#define QUIC_VERSION_V1\t\t\t0x1\n+#define QUIC_VERSION_V2\t\t\t0x6b3343cf\n+\n+struct quic_transport_param {\n+\t__u8\tremote;\n+\t__u8\tdisable_active_migration;\n+\t__u8\tgrease_quic_bit;\n+\t__u8\tstateless_reset;\n+\t__u8\tdisable_1rtt_encryption;\n+\t__u8\tdisable_compatible_version;\n+\t__u8\tactive_connection_id_limit;\n+\t__u8\tack_delay_exponent;\n+\t__u16\tmax_datagram_frame_size;\n+\t__u16\tmax_udp_payload_size;\n+\t__u32\tmax_idle_timeout;\n+\t__u32\tmax_ack_delay;\n+\t__u16\tmax_streams_bidi;\n+\t__u16\tmax_streams_uni;\n+\t__u64\tmax_data;\n+\t__u64\tmax_stream_data_bidi_local;\n+\t__u64\tmax_stream_data_bidi_remote;\n+\t__u64\tmax_stream_data_uni;\n+};\n+\n+struct quic_config {\n+\t__u32\tversion;\n+\t__u32\tplpmtud_probe_interval;\n+\t__u32\tinitial_smoothed_rtt;\n+\t__u32\tpayload_cipher_type;\n+\t__u8\tcongestion_control_algo;\n+\t__u8\tvalidate_peer_address;\n+\t__u8\tstream_data_nodelay;\n+\t__u8\treceive_session_ticket;\n+\t__u8\tcertificate_request;\n+\t__u8\treserved[3];\n+\t__u32\tkeepalive_probe_interval;\n+};\n+\n+struct quic_crypto_secret {\n+\t__u8\tsend; /* send or recv */\n+\t__u8\tlevel; /* crypto level */\n+\t__u16\treserved;\n+\t__u32\ttype; /* TLS_CIPHER_* */\n+#define QUIC_CRYPTO_SECRET_BUFFER_SIZE 48\n+\t__u8\tsecret[QUIC_CRYPTO_SECRET_BUFFER_SIZE];\n+};\n+\n+enum quic_cong_algo {\n+\tQUIC_CONG_ALG_RENO,\n+\tQUIC_CONG_ALG_MAX,\n+};\n+\n+struct quic_errinfo {\n+\t__s64\tstream_id;\n+\t__u32\terrcode;\n+};\n+\n+struct quic_connection_id_info {\n+\t__u8\tdest;\n+\t__u8\treserved[3];\n+\t__u32\tactive;\n+\t__u32\tprior_to;\n+};\n+\n+struct quic_event_option {\n+\t__u8\ttype;\n+\t__u8\ton;\n+};\n+\n+/* Event APIs */\n+enum quic_event_type {\n+\tQUIC_EVENT_NONE,\n+\tQUIC_EVENT_STREAM_UPDATE,\n+\tQUIC_EVENT_STREAM_MAX_DATA,\n+\tQUIC_EVENT_STREAM_MAX_STREAM,\n+\tQUIC_EVENT_CONNECTION_ID,\n+\tQUIC_EVENT_CONNECTION_CLOSE,\n+\tQUIC_EVENT_CONNECTION_MIGRATION,\n+\tQUIC_EVENT_KEY_UPDATE,\n+\tQUIC_EVENT_NEW_TOKEN,\n+\tQUIC_EVENT_NEW_SESSION_TICKET,\n+\tQUIC_EVENT_MAX,\n+};\n+\n+enum {\n+\tQUIC_STREAM_SEND_STATE_READY,\n+\tQUIC_STREAM_SEND_STATE_SEND,\n+\tQUIC_STREAM_SEND_STATE_SENT,\n+\tQUIC_STREAM_SEND_STATE_RECVD,\n+\tQUIC_STREAM_SEND_STATE_RESET_SENT,\n+\tQUIC_STREAM_SEND_STATE_RESET_RECVD,\n+\n+\tQUIC_STREAM_RECV_STATE_RECV,\n+\tQUIC_STREAM_RECV_STATE_SIZE_KNOWN,\n+\tQUIC_STREAM_RECV_STATE_RECVD,\n+\tQUIC_STREAM_RECV_STATE_READ,\n+\tQUIC_STREAM_RECV_STATE_RESET_RECVD,\n+\tQUIC_STREAM_RECV_STATE_RESET_READ,\n+};\n+\n+struct quic_stream_update {\n+\t__s64\tid;\n+\t__u8\tstate;\n+\t__u8\treserved[3];\n+\t__u32\terrcode;\n+\t__u64\tfinalsz;\n+};\n+\n+struct quic_stream_max_data {\n+\t__s64\tid;\n+\t__u64\tmax_data;\n+};\n+\n+struct quic_connection_close {\n+\t__u32\terrcode;\n+\t__u8\tframe;\n+\t__u8\treserved[3];\n+#define QUIC_CLOSE_PHRASE_BUFFER_SIZE 64\n+\t__u8\tphrase[QUIC_CLOSE_PHRASE_BUFFER_SIZE];\n+};\n+\n+union quic_event {\n+\tstruct quic_stream_update\tupdate;\n+\tstruct quic_stream_max_data\tmax_data;\n+\tstruct quic_connection_close\tclose;\n+\tstruct quic_connection_id_info\tinfo;\n+\t__u64\tmax_stream;\n+\t__u8\tlocal_migration;\n+\t__u8\tkey_update_phase;\n+};\n+\n+enum {\n+\tQUIC_TRANSPORT_ERROR_NONE\t\t\t= 0x00,\n+\tQUIC_TRANSPORT_ERROR_INTERNAL\t\t\t= 0x01,\n+\tQUIC_TRANSPORT_ERROR_CONNECTION_REFUSED\t\t= 0x02,\n+\tQUIC_TRANSPORT_ERROR_FLOW_CONTROL\t\t= 0x03,\n+\tQUIC_TRANSPORT_ERROR_STREAM_LIMIT\t\t= 0x04,\n+\tQUIC_TRANSPORT_ERROR_STREAM_STATE\t\t= 0x05,\n+\tQUIC_TRANSPORT_ERROR_FINAL_SIZE\t\t\t= 0x06,\n+\tQUIC_TRANSPORT_ERROR_FRAME_ENCODING\t\t= 0x07,\n+\tQUIC_TRANSPORT_ERROR_TRANSPORT_PARAM\t\t= 0x08,\n+\tQUIC_TRANSPORT_ERROR_CONNECTION_ID_LIMIT\t= 0x09,\n+\tQUIC_TRANSPORT_ERROR_PROTOCOL_VIOLATION\t\t= 0x0a,\n+\tQUIC_TRANSPORT_ERROR_INVALID_TOKEN\t\t= 0x0b,\n+\tQUIC_TRANSPORT_ERROR_APPLICATION\t\t= 0x0c,\n+\tQUIC_TRANSPORT_ERROR_CRYPTO_BUF_EXCEEDED\t= 0x0d,\n+\tQUIC_TRANSPORT_ERROR_KEY_UPDATE\t\t\t= 0x0e,\n+\tQUIC_TRANSPORT_ERROR_AEAD_LIMIT_REACHED\t\t= 0x0f,\n+\tQUIC_TRANSPORT_ERROR_NO_VIABLE_PATH\t\t= 0x10,\n+\tQUIC_TRANSPORT_ERROR_VERSION_NEGOTIATION\t= 0x11,\n+\n+\t/* The cryptographic handshake failed. A range of 256 values is reserved\n+\t * for carrying error codes specific to the cryptographic handshake that\n+\t * is used. Codes for errors occurring when TLS is used for the\n+\t * cryptographic handshake are described in Section 4.8 of [QUIC-TLS].\n+\t */\n+\tQUIC_TRANSPORT_ERROR_CRYPTO\t\t\t= 0x0100,\n+};\n+\n+#endif /* _UAPI_LINUX_QUIC_H */\ndiff --git a/net/Kconfig b/net/Kconfig\nindex e384773935515..ab0d82e108ea9 100644\n--- a/net/Kconfig\n+++ b/net/Kconfig\n@@ -251,6 +251,7 @@ source \"net/bridge/netfilter/Kconfig\"\n \n endif # if NETFILTER\n \n+source \"net/quic/Kconfig\"\n source \"net/sctp/Kconfig\"\n source \"net/rds/Kconfig\"\n source \"net/tipc/Kconfig\"\ndiff --git a/net/Makefile b/net/Makefile\nindex 5b2dd7f07a85e..e2b25ede017d7 100644\n--- a/net/Makefile\n+++ b/net/Makefile\n@@ -39,6 +39,7 @@ obj-$(CONFIG_PHONET)\t\t+= phonet/\n ifneq ($(CONFIG_VLAN_8021Q),)\n obj-y\t\t\t\t+= 8021q/\n endif\n+obj-$(CONFIG_IP_QUIC)\t\t+= quic/\n obj-$(CONFIG_IP_SCTP)\t\t+= sctp/\n obj-$(CONFIG_RDS)\t\t+= rds/\n obj-$(CONFIG_WIRELESS)\t\t+= wireless/\ndiff --git a/net/quic/Kconfig b/net/quic/Kconfig\nnew file mode 100644\nindex 0000000000000..602e6d89eafdd\n--- /dev/null\n+++ b/net/quic/Kconfig\n@@ -0,0 +1,35 @@\n+# SPDX-License-Identifier: GPL-2.0-or-later\n+#\n+# QUIC configuration\n+#\n+\n+menuconfig IP_QUIC\n+\ttristate \"QUIC: A UDP-Based Multiplexed Secure Transport (Experimental)\"\n+\tdepends on INET\n+\tdepends on IPV6\n+\tselect CRYPTO\n+\tselect CRYPTO_HMAC\n+\tselect CRYPTO_AES\n+\tselect CRYPTO_GCM\n+\tselect CRYPTO_CCM\n+\tselect CRYPTO_CHACHA20POLY1305\n+\tselect NET_UDP_TUNNEL\n+\tdefault n\n+\thelp\n+\t QUIC: A UDP-Based Multiplexed and Secure Transport\n+\n+\t From rfc9000 \u003chttps://www.rfc-editor.org/rfc/rfc9000.html\u003e.\n+\n+\t QUIC provides applications with flow-controlled streams for structured\n+\t communication, low-latency connection establishment, and network path\n+\t migration. QUIC includes security measures that ensure\n+\t confidentiality, integrity, and availability in a range of deployment\n+\t circumstances. Accompanying documents describe the integration of\n+\t TLS for key negotiation, loss detection, and an exemplary congestion\n+\t control algorithm.\n+\n+\t To compile this protocol support as a module, choose M here: the\n+\t module will be called quic. Debug messages are handled by the\n+\t kernel's dynamic debugging framework.\n+\n+\t If in doubt, say N.\ndiff --git a/net/quic/Makefile b/net/quic/Makefile\nnew file mode 100644\nindex 0000000000000..0f903f4a7ff1f\n--- /dev/null\n+++ b/net/quic/Makefile\n@@ -0,0 +1,9 @@\n+# SPDX-License-Identifier: GPL-2.0-or-later\n+#\n+# Makefile for QUIC support code.\n+#\n+\n+obj-$(CONFIG_IP_QUIC) += quic.o\n+\n+quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o \\\n+\t cong.o pnspace.o crypto.o timer.o packet.o\ndiff --git a/net/quic/common.c b/net/quic/common.c\nnew file mode 100644\nindex 0000000000000..51d71b6733c7d\n--- /dev/null\n+++ b/net/quic/common.c\n@@ -0,0 +1,565 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003clinux/unaligned.h\u003e\n+#include \u003cnet/netns/hash.h\u003e\n+#include \u003clinux/vmalloc.h\u003e\n+#include \u003clinux/jhash.h\u003e\n+\n+#include \"common.h\"\n+\n+#define QUIC_VARINT_2BYTE_PREFIX\t0x40\n+#define QUIC_VARINT_4BYTE_PREFIX\t0x80\n+#define QUIC_VARINT_8BYTE_PREFIX\t0xc0\n+\n+#define QUIC_VARINT_LENGTH(p)\t\tBIT((*(p)) \u003e\u003e 6)\n+\n+struct quic_hashinfo {\n+\tstruct quic_shash_table shash; /* Source connection ID hashtable */\n+\tstruct quic_shash_table lhash; /* Listening sock hashtable */\n+\tstruct quic_shash_table chash; /* Connection sock hashtable */\n+\tstruct quic_uhash_table uhash; /* UDP sock hashtable */\n+};\n+\n+static struct quic_hashinfo quic_hashinfo;\n+\n+u32 quic_sock_hash_size(void)\n+{\n+\treturn quic_hashinfo.chash.size;\n+}\n+\n+u32 quic_sock_hash(struct net *net, union quic_addr *s, union quic_addr *d)\n+{\n+\tu32 ports = ((__force u32)s-\u003ev4.sin_port) \u003c\u003c 16 |\n+\t\t (__force u32)d-\u003ev4.sin_port;\n+\tu32 saddr = (s-\u003esa.sa_family == AF_INET6) ?\n+\t\t jhash(\u0026s-\u003ev6.sin6_addr, 16, 0) :\n+\t\t (__force u32)s-\u003ev4.sin_addr.s_addr;\n+\tu32 daddr = (d-\u003esa.sa_family == AF_INET6) ?\n+\t\t jhash(\u0026d-\u003ev6.sin6_addr, 16, 0) :\n+\t\t (__force u32)d-\u003ev4.sin_addr.s_addr;\n+\tu32 hash = jhash_3words(saddr, daddr, ports, net_hash_mix(net));\n+\n+\treturn hash \u0026 (quic_sock_hash_size() - 1);\n+}\n+\n+struct quic_shash_head *quic_sock_head(u32 hash)\n+{\n+\treturn \u0026quic_hashinfo.chash.hash[hash];\n+}\n+\n+u32 quic_listen_sock_hash_size(void)\n+{\n+\treturn quic_hashinfo.lhash.size;\n+}\n+\n+u32 quic_listen_sock_hash(struct net *net, u16 port)\n+{\n+\tu32 hash = jhash_1word((__force u32)port, net_hash_mix(net));\n+\n+\treturn hash \u0026 (quic_listen_sock_hash_size() - 1);\n+}\n+\n+struct quic_shash_head *quic_listen_sock_head(u32 hash)\n+{\n+\treturn \u0026quic_hashinfo.lhash.hash[hash];\n+}\n+\n+struct quic_shash_head *quic_source_conn_id_head(struct net *net, u8 *scid,\n+\t\t\t\t\t\t u32 len)\n+{\n+\tu32 hash = jhash_1word(jhash(scid, len, 0), net_hash_mix(net));\n+\tstruct quic_shash_table *ht = \u0026quic_hashinfo.shash;\n+\n+\treturn \u0026ht-\u003ehash[hash \u0026 (ht-\u003esize - 1)];\n+}\n+\n+struct quic_uhash_head *quic_udp_sock_head(struct net *net, u16 port)\n+{\n+\tu32 hash = jhash_1word((__force u32)port, net_hash_mix(net));\n+\tstruct quic_uhash_table *ht = \u0026quic_hashinfo.uhash;\n+\n+\treturn \u0026ht-\u003ehash[hash \u0026 (ht-\u003esize - 1)];\n+}\n+\n+u32 quic_addr_hash(struct net *net, union quic_addr *a)\n+{\n+\tu32 addr = (a-\u003esa.sa_family == AF_INET6) ?\n+\t\t jhash(\u0026a-\u003ev6.sin6_addr, 16, 0) :\n+\t\t (__force u32)a-\u003ev4.sin_addr.s_addr;\n+\n+\treturn jhash_2words(addr, (__force u32)a-\u003ev4.sin_port,\n+\t\t\t net_hash_mix(net));\n+}\n+\n+void quic_hash_tables_destroy(void)\n+{\n+\tvfree(quic_hashinfo.shash.hash);\n+\tvfree(quic_hashinfo.lhash.hash);\n+\tvfree(quic_hashinfo.chash.hash);\n+\tvfree(quic_hashinfo.uhash.hash);\n+}\n+\n+static int quic_shash_table_init(struct quic_shash_table *ht, u32 size)\n+{\n+\tint i;\n+\n+\tht-\u003ehash = vmalloc_array(size, sizeof(struct quic_shash_head));\n+\tif (!ht-\u003ehash)\n+\t\treturn -ENOMEM;\n+\n+\tht-\u003esize = size;\n+\tfor (i = 0; i \u003c ht-\u003esize; i++) {\n+\t\tspin_lock_init(\u0026ht-\u003ehash[i].lock);\n+\t\tINIT_HLIST_NULLS_HEAD(\u0026ht-\u003ehash[i].head, i);\n+\t}\n+\treturn 0;\n+}\n+\n+static int quic_uhash_table_init(struct quic_uhash_table *ht, u32 size)\n+{\n+\tint i;\n+\n+\tht-\u003ehash = vmalloc_array(size, sizeof(struct quic_uhash_head));\n+\tif (!ht-\u003ehash)\n+\t\treturn -ENOMEM;\n+\n+\tht-\u003esize = size;\n+\tfor (i = 0; i \u003c ht-\u003esize; i++) {\n+\t\tmutex_init(\u0026ht-\u003ehash[i].lock);\n+\t\tINIT_HLIST_HEAD(\u0026ht-\u003ehash[i].head);\n+\t}\n+\treturn 0;\n+}\n+\n+int quic_hash_tables_init(void)\n+{\n+\tunsigned long nr_pages = totalram_pages();\n+\tunsigned long limit;\n+\tu32 size;\n+\tint err;\n+\n+\t/* Scale hash table size based on system memory, similar to SCTP. */\n+\tif (nr_pages \u003e= (128 * 1024))\n+\t\tlimit = nr_pages \u003e\u003e (22 - PAGE_SHIFT);\n+\telse\n+\t\tlimit = nr_pages \u003e\u003e (24 - PAGE_SHIFT);\n+\n+\tlimit = roundup_pow_of_two(limit ?: 1);\n+\n+\t/* Source connection ID table (fast lookup, larger size) */\n+\tsize = min_t(unsigned long, limit, 64 * 1024UL);\n+\terr = quic_shash_table_init(\u0026quic_hashinfo.shash, size);\n+\tif (err)\n+\t\tgoto err;\n+\tsize = min_t(unsigned long, limit, 16 * 1024UL);\n+\terr = quic_shash_table_init(\u0026quic_hashinfo.lhash, size);\n+\tif (err)\n+\t\tgoto err;\n+\terr = quic_shash_table_init(\u0026quic_hashinfo.chash, size);\n+\tif (err)\n+\t\tgoto err;\n+\terr = quic_uhash_table_init(\u0026quic_hashinfo.uhash, size);\n+\tif (err)\n+\t\tgoto err;\n+\treturn 0;\n+err:\n+\tquic_hash_tables_destroy();\n+\treturn err;\n+}\n+\n+/* Returns the number of bytes required to encode a QUIC variable-length\n+ * integer.\n+ */\n+u8 quic_var_len(u64 n)\n+{\n+\tif (n \u003c= QUIC_VARINT_1BYTE_MAX)\n+\t\treturn 1;\n+\tif (n \u003c= QUIC_VARINT_2BYTE_MAX)\n+\t\treturn 2;\n+\tif (n \u003c= QUIC_VARINT_4BYTE_MAX)\n+\t\treturn 4;\n+\treturn 8;\n+}\n+\n+/* Decodes a QUIC variable-length integer from a buffer. */\n+u8 quic_get_var(u8 **pp, u32 *plen, u64 *val)\n+{\n+\tu8 *p = *pp, len;\n+\tu64 v = 0;\n+\n+\tif (!*plen)\n+\t\treturn 0;\n+\n+\tlen = QUIC_VARINT_LENGTH(p);\n+\tif (*plen \u003c len)\n+\t\treturn 0;\n+\n+\tswitch (len) {\n+\tcase 1:\n+\t\tv = *p;\n+\t\tbreak;\n+\tcase 2:\n+\t\tv = get_unaligned_be16(p) \u0026 QUIC_VARINT_2BYTE_MAX;\n+\t\tbreak;\n+\tcase 4:\n+\t\tv = get_unaligned_be32(p) \u0026 QUIC_VARINT_4BYTE_MAX;\n+\t\tbreak;\n+\tcase 8:\n+\t\tv = get_unaligned_be64(p) \u0026 QUIC_VARINT_8BYTE_MAX;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\n+\t*plen -= len;\n+\t*pp = p + len;\n+\t*val = v;\n+\treturn len;\n+}\n+\n+/* Reads a fixed-length integer from the buffer. */\n+u32 quic_get_int(u8 **pp, u32 *plen, u64 *val, u32 len)\n+{\n+\tu8 *p = *pp;\n+\tu64 v = 0;\n+\n+\tif (*plen \u003c len)\n+\t\treturn 0;\n+\n+\tswitch (len) {\n+\tcase 1:\n+\t\tv = *p;\n+\t\tbreak;\n+\tcase 2:\n+\t\tv = get_unaligned_be16(p);\n+\t\tbreak;\n+\tcase 3:\n+\t\tv = get_unaligned_be24(p);\n+\t\tbreak;\n+\tcase 4:\n+\t\tv = get_unaligned_be32(p);\n+\t\tbreak;\n+\tcase 8:\n+\t\tv = get_unaligned_be64(p);\n+\t\tbreak;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+\t*plen -= len;\n+\t*pp = p + len;\n+\t*val = v;\n+\treturn len;\n+}\n+\n+u32 quic_get_data(u8 **pp, u32 *plen, u8 *data, u32 len)\n+{\n+\tif (*plen \u003c len)\n+\t\treturn 0;\n+\n+\tmemcpy(data, *pp, len);\n+\t*pp += len;\n+\t*plen -= len;\n+\n+\treturn len;\n+}\n+\n+/* Encodes a value into the QUIC variable-length integer format. */\n+u8 *quic_put_var(u8 *p, u64 num)\n+{\n+\tif (num \u003c= QUIC_VARINT_1BYTE_MAX) {\n+\t\t*p++ = (u8)num;\n+\t\treturn p;\n+\t}\n+\tif (num \u003c= QUIC_VARINT_2BYTE_MAX) {\n+\t\tput_unaligned_be16((u16)num, p);\n+\t\t*p |= QUIC_VARINT_2BYTE_PREFIX;\n+\t\treturn p + 2;\n+\t}\n+\tif (num \u003c= QUIC_VARINT_4BYTE_MAX) {\n+\t\tput_unaligned_be32((u32)num, p);\n+\t\t*p |= QUIC_VARINT_4BYTE_PREFIX;\n+\t\treturn p + 4;\n+\t}\n+\tput_unaligned_be64(num, p);\n+\t*p |= QUIC_VARINT_8BYTE_PREFIX;\n+\treturn p + 8;\n+}\n+\n+/* Writes a fixed-length integer to the buffer in network byte order. */\n+u8 *quic_put_int(u8 *p, u64 num, u8 len)\n+{\n+\tswitch (len) {\n+\tcase 1:\n+\t\t*p++ = (u8)num;\n+\t\treturn p;\n+\tcase 2:\n+\t\tput_unaligned_be16((u16)num, p);\n+\t\treturn p + 2;\n+\tcase 4:\n+\t\tput_unaligned_be32((u32)num, p);\n+\t\treturn p + 4;\n+\tcase 8:\n+\t\tput_unaligned_be64(num, p);\n+\t\treturn p + 8;\n+\tdefault:\n+\t\treturn NULL;\n+\t}\n+}\n+\n+/* Encodes a value as a variable-length integer with explicit length. */\n+u8 *quic_put_varint(u8 *p, u64 num, u8 len)\n+{\n+\tswitch (len) {\n+\tcase 1:\n+\t\t*p++ = (u8)num;\n+\t\treturn p;\n+\tcase 2:\n+\t\tput_unaligned_be16((u16)num, p);\n+\t\t*p |= QUIC_VARINT_2BYTE_PREFIX;\n+\t\treturn p + 2;\n+\tcase 4:\n+\t\tput_unaligned_be32((u32)num, p);\n+\t\t*p |= QUIC_VARINT_4BYTE_PREFIX;\n+\t\treturn p + 4;\n+\tdefault:\n+\t\treturn NULL;\n+\t}\n+}\n+\n+u8 *quic_put_data(u8 *p, u8 *data, u32 len)\n+{\n+\tif (!len)\n+\t\treturn p;\n+\n+\tmemcpy(p, data, len);\n+\treturn p + len;\n+}\n+\n+/* Writes a transport parameter as two varints: ID and value length, followed\n+ * by value.\n+ */\n+u8 *quic_put_param(u8 *p, u16 id, u64 value)\n+{\n+\tp = quic_put_var(p, id);\n+\tp = quic_put_var(p, quic_var_len(value));\n+\treturn quic_put_var(p, value);\n+}\n+\n+/* Reads a QUIC transport parameter value. */\n+u8 quic_get_param(u64 *pdest, u8 **pp, u32 *plen)\n+{\n+\tu64 valuelen;\n+\n+\tif (!quic_get_var(pp, plen, \u0026valuelen))\n+\t\treturn 0;\n+\n+\tif (*plen \u003c valuelen)\n+\t\treturn 0;\n+\n+\tif (quic_get_var(pp, plen, pdest) != valuelen)\n+\t\treturn 0;\n+\n+\treturn (u8)valuelen;\n+}\n+\n+/* rfc9000#section-a.3: DecodePacketNumber()\n+ *\n+ * Reconstructs the full packet number from a truncated one.\n+ */\n+s64 quic_get_num(s64 max_pkt_num, s64 pkt_num, u32 n)\n+{\n+\ts64 expected = max_pkt_num + 1;\n+\ts64 win = BIT_ULL(n * 8);\n+\ts64 hwin = win / 2;\n+\ts64 mask = win - 1;\n+\ts64 cand;\n+\n+\tcand = (expected \u0026 ~mask) | pkt_num;\n+\tif (cand \u003c= expected - hwin \u0026\u0026 cand \u003c BIT_ULL(QUIC_PN_BITS) - win)\n+\t\treturn cand + win;\n+\tif (cand \u003e expected + hwin \u0026\u0026 cand \u003e= win)\n+\t\treturn cand - win;\n+\treturn cand;\n+}\n+\n+int quic_data_dup(struct quic_data *to, u8 *data, u32 len, gfp_t gfp)\n+{\n+\tif (!len) {\n+\t\tquic_data_free(to);\n+\t\treturn 0;\n+\t}\n+\n+\tdata = kmemdup(data, len, gfp);\n+\tif (!data)\n+\t\treturn -ENOMEM;\n+\n+\tkfree(to-\u003edata);\n+\tto-\u003edata = data;\n+\tto-\u003elen = len;\n+\treturn 0;\n+}\n+\n+int quic_data_append(struct quic_data *to, u8 *data, u32 len, gfp_t gfp)\n+{\n+\tu8 *p;\n+\n+\tif (!len)\n+\t\treturn 0;\n+\n+\tif (to-\u003elen \u003e U32_MAX - len)\n+\t\treturn -EOVERFLOW;\n+\n+\tp = kmalloc(to-\u003elen + len, gfp);\n+\tif (!p)\n+\t\treturn -ENOMEM;\n+\tp = quic_put_data(p, to-\u003edata, to-\u003elen);\n+\tp = quic_put_data(p, data, len);\n+\n+\tkfree(to-\u003edata);\n+\tto-\u003elen = to-\u003elen + len;\n+\tto-\u003edata = p - to-\u003elen;\n+\treturn 0;\n+}\n+\n+/* Check whether 'd2' is equal to any element inside the list 'd1'.\n+ *\n+ * 'd1' is assumed to be a sequence of length-prefixed elements. Each element\n+ * is compared to 'd2' using 'quic_data_cmp()'.\n+ *\n+ * Returns true if a match is found, false otherwise.\n+ */\n+bool quic_data_has(struct quic_data *d1, struct quic_data *d2)\n+{\n+\tstruct quic_data d;\n+\tu64 length;\n+\tu32 len;\n+\tu8 *p;\n+\n+\tfor (p = d1-\u003edata, len = d1-\u003elen; len; len -= length, p += length) {\n+\t\tif (!quic_get_int(\u0026p, \u0026len, \u0026length, 1) || len \u003c length)\n+\t\t\treturn false;\n+\t\tquic_data(\u0026d, p, length);\n+\t\tif (!quic_data_cmp(\u0026d, d2))\n+\t\t\treturn true;\n+\t}\n+\treturn false;\n+}\n+\n+/* Check if any element of 'd1' is present in the list 'd2'.\n+ *\n+ * Iterates through each element in 'd1', and uses 'quic_data_has()' to check\n+ * for its presence in 'd2'.\n+ *\n+ * Returns true if any match is found, false otherwise.\n+ */\n+bool quic_data_match(struct quic_data *d1, struct quic_data *d2)\n+{\n+\tstruct quic_data d;\n+\tu64 length;\n+\tu32 len;\n+\tu8 *p;\n+\n+\tfor (p = d1-\u003edata, len = d1-\u003elen; len; len -= length, p += length) {\n+\t\tif (!quic_get_int(\u0026p, \u0026len, \u0026length, 1) || len \u003c length)\n+\t\t\treturn false;\n+\t\tquic_data(\u0026d, p, length);\n+\t\tif (quic_data_has(d2, \u0026d))\n+\t\t\treturn true;\n+\t}\n+\treturn false;\n+}\n+\n+/* Serialize a list of 'quic_data' elements into a comma-separated string.\n+ *\n+ * Each element in 'from' is length-prefixed. This function copies their raw\n+ * content into the output buffer 'to', inserting commas in between. The\n+ * resulting string length is written to '*plen'.\n+ */\n+int quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from)\n+{\n+\tu32 remlen = *plen;\n+\tstruct quic_data d;\n+\tu8 *data = to, *p;\n+\tu64 length;\n+\tu32 len;\n+\n+\tp = from-\u003edata;\n+\tlen = from-\u003elen;\n+\twhile (len) {\n+\t\tif (!quic_get_int(\u0026p, \u0026len, \u0026length, 1) || len \u003c length)\n+\t\t\treturn -EINVAL;\n+\n+\t\tquic_data(\u0026d, p, length);\n+\t\tif (d.len \u003e remlen)\n+\t\t\treturn -EOVERFLOW;\n+\n+\t\tdata = quic_put_data(data, d.data, d.len);\n+\t\tremlen -= d.len;\n+\t\tp += d.len;\n+\t\tlen -= d.len;\n+\t\tif (len) {\n+\t\t\tif (!remlen)\n+\t\t\t\treturn -EOVERFLOW;\n+\t\t\tdata = quic_put_int(data, ',', 1);\n+\t\t\tremlen--;\n+\t\t}\n+\t}\n+\t*plen = data - to;\n+\treturn 0;\n+}\n+\n+/* Parse a comma-separated string into a 'quic_data' list format.\n+ *\n+ * Each comma-separated token is turned into a length-prefixed element. The\n+ * first byte of each element stores the length. Elements are stored in\n+ * 'to-\u003edata', and 'to-\u003elen' is updated.\n+ */\n+int quic_data_from_string(struct quic_data *to, u8 *from, u32 len)\n+{\n+\tu32 remlen = to-\u003elen;\n+\tstruct quic_data d;\n+\tu8 *p = to-\u003edata;\n+\n+\tto-\u003elen = 0;\n+\twhile (len) {\n+\t\twhile (len \u0026\u0026 *from == ' ') {\n+\t\t\tfrom++;\n+\t\t\tlen--;\n+\t\t}\n+\t\tif (!len)\n+\t\t\tbreak;\n+\t\tif (!remlen)\n+\t\t\treturn -EOVERFLOW;\n+\t\td.data = p++;\n+\t\td.len = 0;\n+\t\tremlen--;\n+\t\twhile (len) {\n+\t\t\tif (*from == ',') {\n+\t\t\t\tfrom++;\n+\t\t\t\tlen--;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tif (!remlen)\n+\t\t\t\treturn -EOVERFLOW;\n+\t\t\t*p++ = *from++;\n+\t\t\tlen--;\n+\t\t\td.len++;\n+\t\t\tremlen--;\n+\t\t}\n+\t\tif (d.len \u003e U8_MAX)\n+\t\t\treturn -EINVAL;\n+\t\t*d.data = (u8)(d.len);\n+\t\tto-\u003elen += d.len + 1;\n+\t}\n+\treturn 0;\n+}\ndiff --git a/net/quic/common.h b/net/quic/common.h\nnew file mode 100644\nindex 0000000000000..6cb2b1b89cfb1\n--- /dev/null\n+++ b/net/quic/common.h\n@@ -0,0 +1,220 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003cnet/net_namespace.h\u003e\n+\n+#define QUIC_MAX_ACK_DELAY\t(16384 * 1000)\n+#define QUIC_DEF_ACK_DELAY\t25000\n+\n+#define QUIC_STREAM_BIT_FIN\t0x01\n+#define QUIC_STREAM_BIT_LEN\t0x02\n+#define QUIC_STREAM_BIT_OFF\t0x04\n+#define QUIC_STREAM_BIT_MASK\t0x08\n+\n+#define QUIC_CONN_ID_MAX_LEN\t20\n+#define QUIC_CONN_ID_DEF_LEN\t8\n+\n+#define QUIC_PN_MAX_LEN\t\t4 /* For encoded packet number */\n+#define QUIC_PN_BITS\t\t62\n+#define QUIC_PN_MAX\t\t(BIT_ULL(QUIC_PN_BITS) - 1)\n+\n+#define QUIC_VARINT_1BYTE_MAX\t\t0x3fULL\n+#define QUIC_VARINT_2BYTE_MAX\t\t0x3fffULL\n+#define QUIC_VARINT_4BYTE_MAX\t\t0x3fffffffULL\n+#define QUIC_VARINT_8BYTE_MAX\t\t0x3fffffffffffffffULL\n+\n+struct quic_conn_id {\n+\tu8 data[QUIC_CONN_ID_MAX_LEN];\n+\tu8 len;\n+};\n+\n+static inline void quic_conn_id_update(struct quic_conn_id *conn_id, u8 *data,\n+\t\t\t\t u32 len)\n+{\n+\t/* The caller must ensure len does not exceed QUIC_CONN_ID_MAX_LEN. */\n+\tif (WARN_ON_ONCE(len \u003e QUIC_CONN_ID_MAX_LEN))\n+\t\treturn;\n+\tmemcpy(conn_id-\u003edata, data, len);\n+\tconn_id-\u003elen = (u8)len;\n+}\n+\n+struct quic_skb_cb {\n+\t/* Callback and temporary context when encryption/decryption completes\n+\t * in async mode\n+\t */\n+\tvoid (*crypto_done)(struct sk_buff *skb, int err);\n+\tvoid *crypto_ctx;\n+\tunion {\n+\t\tstruct sk_buff *last; /* Last packet in bundle on TX */\n+\t\tu64 time; /* Arrival timestamp in UDP tunnel on RX */\n+\t};\n+\ts64 number; /* Parsed packet number, or the largest previously seen */\n+\tu32 seqno; /* Dest connection ID number on RX */\n+\tu16 length; /* Payload length + packet number length */\n+\n+\tu16 number_offset; /* Offset of packet number field */\n+\tu8 number_len; /* Length of the packet number field */\n+\tu8 level; /* Encryption level: Initial, Handshake, App, or Early */\n+\n+\tu16 errcode; /* Error code on packet processing failure */\n+\tu8 errframe; /* Frame type causing packet processing failure */\n+\n+\tu8 key_update:1; /* Key update triggered by this packet */\n+\tu8 key_phase:1; /* Key phase used (0 or 1) */\n+\tu8 backlog:1; /* Enqueued into backlog list */\n+\tu8 resume:1; /* Crypto already processed (encrypted or decrypted) */\n+\tu8 path:1; /* Packet arrived from a new or migrating path */\n+\tu8 sync:1; /* Force synchronous crypto (process context only) */\n+\tu8 ecn:2; /* ECN marking used on TX */\n+};\n+\n+#define QUIC_SKB_CB(skb)\t((struct quic_skb_cb *)\u0026((skb)-\u003ecb[0]))\n+\n+struct quichdr {\n+#if defined(__LITTLE_ENDIAN_BITFIELD)\n+\t__u8 pnl:2,\n+\t key:1,\n+\t reserved:2,\n+\t spin:1,\n+\t fixed:1,\n+\t form:1;\n+#elif defined(__BIG_ENDIAN_BITFIELD)\n+\t__u8 form:1,\n+\t fixed:1,\n+\t spin:1,\n+\t reserved:2,\n+\t key:1,\n+\t pnl:2;\n+#endif\n+};\n+\n+static inline struct quichdr *quic_hdr(struct sk_buff *skb)\n+{\n+\treturn (struct quichdr *)skb-\u003edata;\n+}\n+\n+struct quichshdr {\n+#if defined(__LITTLE_ENDIAN_BITFIELD)\n+\t__u8 pnl:2,\n+\t reserved:2,\n+\t type:2,\n+\t fixed:1,\n+\t form:1;\n+#elif defined(__BIG_ENDIAN_BITFIELD)\n+\t__u8 form:1,\n+\t fixed:1,\n+\t type:2,\n+\t reserved:2,\n+\t pnl:2;\n+#endif\n+};\n+\n+static inline struct quichshdr *quic_hshdr(struct sk_buff *skb)\n+{\n+\treturn (struct quichshdr *)skb-\u003edata;\n+}\n+\n+union quic_addr {\n+\tstruct sockaddr_in6 v6;\n+\tstruct sockaddr_in v4;\n+\tstruct sockaddr sa;\n+};\n+\n+static inline union quic_addr *quic_addr(const void *addr)\n+{\n+\treturn (union quic_addr *)addr;\n+}\n+\n+struct quic_shash_head {\n+\tstruct hlist_nulls_head\thead;\n+\tspinlock_t\t\tlock; /* Protects 'head' in atomic context */\n+};\n+\n+struct quic_shash_table {\n+\tstruct quic_shash_head *hash;\n+\tu32 size;\n+};\n+\n+struct quic_uhash_head {\n+\tstruct hlist_head\thead;\n+\tstruct mutex\t\tlock; /* Protects 'head' in process context */\n+};\n+\n+struct quic_uhash_table {\n+\tstruct quic_uhash_head *hash;\n+\tu32 size;\n+};\n+\n+struct quic_data {\n+\tu8 *data;\n+\tu32 len;\n+};\n+\n+static inline struct quic_data *quic_data(struct quic_data *d, u8 *data,\n+\t\t\t\t\t u32 len)\n+{\n+\td-\u003edata = data;\n+\td-\u003elen = len;\n+\treturn d;\n+}\n+\n+static inline int quic_data_cmp(struct quic_data *d1, struct quic_data *d2)\n+{\n+\treturn d1-\u003elen != d2-\u003elen || memcmp(d1-\u003edata, d2-\u003edata, d1-\u003elen);\n+}\n+\n+static inline void quic_data_free(struct quic_data *d)\n+{\n+\tkfree(d-\u003edata);\n+\td-\u003edata = NULL;\n+\td-\u003elen = 0;\n+}\n+\n+static inline u64 quic_ktime_get_us(void)\n+{\n+\treturn ktime_to_us(ktime_get());\n+}\n+\n+u32 quic_sock_hash(struct net *net, union quic_addr *s, union quic_addr *d);\n+struct quic_shash_head *quic_sock_head(u32 hash);\n+u32 quic_sock_hash_size(void);\n+\n+u32 quic_listen_sock_hash(struct net *net, u16 port);\n+struct quic_shash_head *quic_listen_sock_head(u32 hash);\n+u32 quic_listen_sock_hash_size(void);\n+\n+struct quic_shash_head *quic_source_conn_id_head(struct net *net, u8 *scid,\n+\t\t\t\t\t\t u32 len);\n+struct quic_uhash_head *quic_udp_sock_head(struct net *net, u16 port);\n+u32 quic_addr_hash(struct net *net, union quic_addr *a);\n+\n+void quic_hash_tables_destroy(void);\n+int quic_hash_tables_init(void);\n+\n+u32 quic_get_data(u8 **pp, u32 *plen, u8 *data, u32 len);\n+u32 quic_get_int(u8 **pp, u32 *plen, u64 *val, u32 len);\n+s64 quic_get_num(s64 max_pkt_num, s64 pkt_num, u32 n);\n+u8 quic_get_param(u64 *pdest, u8 **pp, u32 *plen);\n+u8 quic_get_var(u8 **pp, u32 *plen, u64 *val);\n+u8 quic_var_len(u64 n);\n+\n+u8 *quic_put_param(u8 *p, u16 id, u64 value);\n+u8 *quic_put_data(u8 *p, u8 *data, u32 len);\n+u8 *quic_put_varint(u8 *p, u64 num, u8 len);\n+u8 *quic_put_int(u8 *p, u64 num, u8 len);\n+u8 *quic_put_var(u8 *p, u64 num);\n+\n+int quic_data_from_string(struct quic_data *to, u8 *from, u32 len);\n+int quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from);\n+\n+int quic_data_append(struct quic_data *to, u8 *data, u32 len, gfp_t gfp);\n+int quic_data_dup(struct quic_data *to, u8 *data, u32 len, gfp_t gfp);\n+bool quic_data_match(struct quic_data *d1, struct quic_data *d2);\n+bool quic_data_has(struct quic_data *d1, struct quic_data *d2);\ndiff --git a/net/quic/cong.c b/net/quic/cong.c\nnew file mode 100644\nindex 0000000000000..78f28bbdfb374\n--- /dev/null\n+++ b/net/quic/cong.c\n@@ -0,0 +1,340 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003clinux/quic.h\u003e\n+\n+#include \"common.h\"\n+#include \"cong.h\"\n+\n+/* NEW RENO APIs */\n+static void quic_reno_handle_packet_lost(struct quic_cong *cong)\n+{\n+\tswitch (cong-\u003estate) {\n+\tcase QUIC_CONG_SLOW_START:\n+\t\tpr_debug(\"%s: slow_start -\u003e recovery, cwnd: %u, ssth: %u\\n\",\n+\t\t\t __func__, cong-\u003ewindow, cong-\u003essthresh);\n+\t\tbreak;\n+\tcase QUIC_CONG_RECOVERY_PERIOD:\n+\t\treturn;\n+\tcase QUIC_CONG_CONGESTION_AVOIDANCE:\n+\t\tpr_debug(\"%s: cong_avoid -\u003e recovery, cwnd: %u, ssth: %u\\n\",\n+\t\t\t __func__, cong-\u003ewindow, cong-\u003essthresh);\n+\t\tbreak;\n+\tdefault:\n+\t\tpr_debug(\"%s: wrong congestion state: %d\\n\", __func__,\n+\t\t\t cong-\u003estate);\n+\t\treturn;\n+\t}\n+\n+\tcong-\u003erecovery_time = cong-\u003etime;\n+\tcong-\u003estate = QUIC_CONG_RECOVERY_PERIOD;\n+\tcong-\u003essthresh = max(cong-\u003ewindow \u003e\u003e 1U, cong-\u003emin_window);\n+\tcong-\u003ewindow = cong-\u003essthresh;\n+}\n+\n+static void quic_reno_on_packet_lost(struct quic_cong *cong, u64 time,\n+\t\t\t\t u32 bytes, s64 number)\n+{\n+\tquic_reno_handle_packet_lost(cong);\n+}\n+\n+static void quic_reno_on_packet_acked(struct quic_cong *cong, u64 time,\n+\t\t\t\t u32 bytes, s64 number)\n+{\n+\tu64 new_window;\n+\n+\tswitch (cong-\u003estate) {\n+\tcase QUIC_CONG_SLOW_START:\n+\t\tnew_window = (u64)cong-\u003ewindow + bytes;\n+\t\tcong-\u003ewindow = min_t(u64, new_window, cong-\u003emax_window);\n+\t\tif (cong-\u003ewindow \u003c cong-\u003essthresh)\n+\t\t\tbreak;\n+\t\tcong-\u003estate = QUIC_CONG_CONGESTION_AVOIDANCE;\n+\t\tpr_debug(\"%s: slow_start -\u003e cong_avoid, cwnd: %u, ssth: %u\\n\",\n+\t\t\t __func__, cong-\u003ewindow, cong-\u003essthresh);\n+\t\tbreak;\n+\tcase QUIC_CONG_RECOVERY_PERIOD:\n+\t\tif (cong-\u003erecovery_time \u003e= time)\n+\t\t\tbreak;\n+\t\tcong-\u003estate = QUIC_CONG_CONGESTION_AVOIDANCE;\n+\t\tpr_debug(\"%s: recovery -\u003e cong_avoid, cwnd: %u, ssth: %u\\n\",\n+\t\t\t __func__, cong-\u003ewindow, cong-\u003essthresh);\n+\t\tfallthrough;\n+\tcase QUIC_CONG_CONGESTION_AVOIDANCE:\n+\t\t/* cong-\u003ewindow is never zero; it is initialized by\n+\t\t * quic_packet_route() during connect/accept.\n+\t\t */\n+\t\tnew_window = div64_ul((u64)cong-\u003emss * bytes, cong-\u003ewindow) +\n+\t\t\t cong-\u003ewindow;\n+\t\tcong-\u003ewindow = min_t(u64, new_window, cong-\u003emax_window);\n+\t\tbreak;\n+\tdefault:\n+\t\tpr_debug(\"%s: wrong congestion state: %d\\n\", __func__,\n+\t\t\t cong-\u003estate);\n+\t\treturn;\n+\t}\n+}\n+\n+static void quic_reno_on_process_ecn(struct quic_cong *cong)\n+{\n+\tquic_reno_handle_packet_lost(cong);\n+}\n+\n+static void quic_reno_on_init(struct quic_cong *cong)\n+{\n+}\n+\n+static const struct quic_cong_ops quic_congs[] = {\n+\t{ /* QUIC_CONG_ALG_RENO */\n+\t\t.on_packet_acked = quic_reno_on_packet_acked,\n+\t\t.on_packet_lost = quic_reno_on_packet_lost,\n+\t\t.on_process_ecn = quic_reno_on_process_ecn,\n+\t\t.on_init = quic_reno_on_init,\n+\t},\n+};\n+\n+static bool quic_cong_check_persistent_congestion(struct quic_cong *cong,\n+\t\t\t\t\t\t u64 time)\n+{\n+\tu32 ssthresh;\n+\n+\ttime -= cong-\u003epc_start_time;\n+\n+\t/* rfc9002#section-7.6.1:\n+\t * (smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay) *\n+\t * kPersistentCongestionThreshold\n+\t */\n+\tssthresh = cong-\u003esmoothed_rtt +\n+\t\t max(4 * cong-\u003erttvar, QUIC_KGRANULARITY);\n+\tssthresh = (ssthresh + cong-\u003emax_ack_delay) *\n+\t\t QUIC_KPERSISTENT_CONGESTION_THRESHOLD;\n+\n+\treturn time \u003e ssthresh;\n+}\n+\n+/* COMMON APIs */\n+void quic_cong_on_packet_lost(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t s64 number)\n+{\n+\tif (cong-\u003epc_start_time \u0026\u0026 time \u003e cong-\u003epc_start_time \u0026\u0026\n+\t quic_cong_check_persistent_congestion(cong, time)) {\n+\t\tcong-\u003epc_start_time = 0;\n+\t\tcong-\u003emin_rtt_valid = 0;\n+\t\tcong-\u003ewindow = cong-\u003emin_window;\n+\t\tcong-\u003estate = QUIC_CONG_SLOW_START;\n+\t\treturn;\n+\t}\n+\n+\tif (!cong-\u003epc_start_time \u0026\u0026 cong-\u003eis_rtt_set)\n+\t\tcong-\u003epc_start_time = time;\n+\n+\tcong-\u003eops-\u003eon_packet_lost(cong, time, bytes, number);\n+}\n+\n+void quic_cong_on_packet_acked(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t s64 number)\n+{\n+\t/* When a packet is acked, if time - cong-\u003epc_start_time \u003c= duration\n+\t * threshold, it means the acked packet was sent within the persistent\n+\t * congestion window.\n+\t *\n+\t * This breaks the condition in rfc9002#section-7.6.2:\n+\t *\n+\t * - across all packet number spaces, none of the packets sent between\n+\t * the send times of these two packets are acknowledged;\n+\t *\n+\t * so pc_start_time is reset to 0.\n+\t */\n+\tif (cong-\u003epc_start_time \u0026\u0026 time \u003e cong-\u003epc_start_time \u0026\u0026\n+\t !quic_cong_check_persistent_congestion(cong, time))\n+\t\tcong-\u003epc_start_time = 0;\n+\n+\tcong-\u003eops-\u003eon_packet_acked(cong, time, bytes, number);\n+}\n+\n+void quic_cong_on_process_ecn(struct quic_cong *cong)\n+{\n+\tcong-\u003eops-\u003eon_process_ecn(cong);\n+}\n+\n+/* Update Probe Timeout (PTO) and loss detection delay based on RTT stats. */\n+static void quic_cong_pto_update(struct quic_cong *cong)\n+{\n+\tu32 loss_delay;\n+\n+\t/* rfc9002#section-6.2.1:\n+\t * PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay\n+\t *\n+\t * Calculate the base PTO here, excluding max_ack_delay. max_ack_delay\n+\t * is added when calculating the PTO for the App packet number space.\n+\t */\n+\tcong-\u003epto = cong-\u003esmoothed_rtt +\n+\t\t max(4 * cong-\u003erttvar, QUIC_KGRANULARITY);\n+\n+\t/* rfc9002#section-6.1.2:\n+\t * max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity)\n+\t */\n+\tloss_delay = QUIC_KTIME_THRESHOLD(max(cong-\u003esmoothed_rtt,\n+\t\t\t\t\t cong-\u003elatest_rtt));\n+\tcong-\u003eloss_delay = max(loss_delay, QUIC_KGRANULARITY);\n+\n+\tpr_debug(\"%s: update pto: %u\\n\", __func__, cong-\u003epto);\n+}\n+\n+/* Update pacing timestamp after sending 'bytes' bytes.\n+ *\n+ * This function tracks when the next packet is allowed to be sent based on\n+ * pacing rate.\n+ */\n+static void quic_cong_update_pacing_time(struct quic_cong *cong, u32 bytes)\n+{\n+\tu64 prior_time, credit, len_ns, rate = cong-\u003epacing_rate;\n+\n+\tif (!rate)\n+\t\treturn;\n+\n+\tprior_time = cong-\u003epacing_time;\n+\tcong-\u003epacing_time = max(cong-\u003epacing_time, ktime_get_ns());\n+\tcredit = cong-\u003epacing_time - prior_time;\n+\n+\t/* take into account OS jitter */\n+\tlen_ns = div64_u64((u64)bytes * NSEC_PER_SEC, rate);\n+\tlen_ns -= min_t(u64, len_ns / 2, credit);\n+\tcong-\u003epacing_time += len_ns;\n+}\n+\n+/* Compute and update the pacing rate based on congestion window and smoothed\n+ * RTT.\n+ */\n+static void quic_cong_pace_update(struct quic_cong *cong, u64 max_rate)\n+{\n+\tu64 rate;\n+\n+\tif (unlikely(!cong-\u003esmoothed_rtt))\n+\t\treturn;\n+\n+\t/* rate = N * congestion_window / smoothed_rtt */\n+\trate = div64_ul((u64)cong-\u003ewindow * USEC_PER_SEC * 2,\n+\t\t\tcong-\u003esmoothed_rtt);\n+\n+\tcong-\u003epacing_rate = min_t(u64, rate, max_rate);\n+\tpr_debug(\"%s: update pacing rate: %llu, max rate: %llu, srtt: %u\\n\",\n+\t\t __func__, cong-\u003epacing_rate, max_rate, cong-\u003esmoothed_rtt);\n+}\n+\n+void quic_cong_on_packet_sent(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t s64 number)\n+{\n+\tif (!bytes)\n+\t\treturn;\n+\tif (cong-\u003eops-\u003eon_packet_sent)\n+\t\tcong-\u003eops-\u003eon_packet_sent(cong, time, bytes, number);\n+\tquic_cong_update_pacing_time(cong, bytes);\n+}\n+\n+void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u64 max_rate)\n+{\n+\tif (!bytes)\n+\t\treturn;\n+\tif (cong-\u003eops-\u003eon_ack_recv)\n+\t\tcong-\u003eops-\u003eon_ack_recv(cong, bytes, max_rate);\n+\tquic_cong_pace_update(cong, max_rate);\n+}\n+\n+/* rfc9002#section-5: Estimating the Round-Trip Time */\n+void quic_cong_rtt_update(struct quic_cong *cong, u64 time, u32 ack_delay)\n+{\n+\tu32 adjusted_rtt, rttvar_sample;\n+\n+\t/* Ignore RTT sample if ACK delay is suspiciously large. */\n+\tif (ack_delay \u003e cong-\u003emax_ack_delay * 2 ||\n+\t cong-\u003etime - time \u003e QUIC_RTT_MAX)\n+\t\treturn;\n+\n+\t/* rfc9002#section-5.1:\n+\t * latest_rtt = ack_time - send_time_of_largest_acked\n+\t */\n+\tcong-\u003elatest_rtt = cong-\u003etime - time;\n+\n+\t/* rfc9002#section-5.2: Estimating min_rtt */\n+\tif (!cong-\u003emin_rtt_valid) {\n+\t\tcong-\u003emin_rtt = cong-\u003elatest_rtt;\n+\t\tcong-\u003emin_rtt_valid = 1;\n+\t}\n+\tif (cong-\u003emin_rtt \u003e cong-\u003elatest_rtt)\n+\t\tcong-\u003emin_rtt = cong-\u003elatest_rtt;\n+\n+\tif (!cong-\u003eis_rtt_set) {\n+\t\t/* rfc9002#section-5.3:\n+\t\t * smoothed_rtt = latest_rtt\n+\t\t * rttvar = latest_rtt / 2\n+\t\t */\n+\t\tcong-\u003esmoothed_rtt = cong-\u003elatest_rtt;\n+\t\tcong-\u003erttvar = cong-\u003esmoothed_rtt / 2;\n+\t\tquic_cong_pto_update(cong);\n+\t\tcong-\u003eis_rtt_set = 1;\n+\t\treturn;\n+\t}\n+\n+\t/* rfc9002#section-5.3:\n+\t * adjusted_rtt = latest_rtt\n+\t * if (latest_rtt \u003e= min_rtt + ack_delay):\n+\t * adjusted_rtt = latest_rtt - ack_delay\n+\t * smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt\n+\t * rttvar_sample = abs(smoothed_rtt - adjusted_rtt)\n+\t * rttvar = 3/4 * rttvar + 1/4 * rttvar_sample\n+\t */\n+\tadjusted_rtt = cong-\u003elatest_rtt;\n+\tif (cong-\u003elatest_rtt \u003e= cong-\u003emin_rtt + ack_delay)\n+\t\tadjusted_rtt = cong-\u003elatest_rtt - ack_delay;\n+\n+\tcong-\u003esmoothed_rtt = (cong-\u003esmoothed_rtt * 7 + adjusted_rtt) / 8;\n+\trttvar_sample = abs_diff(cong-\u003esmoothed_rtt, adjusted_rtt);\n+\tcong-\u003erttvar = (cong-\u003erttvar * 3 + rttvar_sample) / 4;\n+\tquic_cong_pto_update(cong);\n+\n+\tif (cong-\u003eops-\u003eon_rtt_update)\n+\t\tcong-\u003eops-\u003eon_rtt_update(cong);\n+}\n+\n+void quic_cong_set_algo(struct quic_cong *cong, u8 algo)\n+{\n+\t/* The caller must ensure algo \u003c QUIC_CONG_ALG_MAX. */\n+\tif (WARN_ON_ONCE(algo \u003e= QUIC_CONG_ALG_MAX))\n+\t\treturn;\n+\tcong-\u003ealgo = algo;\n+\tcong-\u003estate = QUIC_CONG_SLOW_START;\n+\tcong-\u003essthresh = U32_MAX;\n+\tcong-\u003eops = \u0026quic_congs[algo];\n+\tcong-\u003eops-\u003eon_init(cong);\n+}\n+\n+void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt)\n+{\n+\t/* rfc9002#section-5.3:\n+\t * smoothed_rtt = kInitialRtt\n+\t * rttvar = kInitialRtt / 2\n+\t */\n+\tcong-\u003einitial_srtt = srtt;\n+\tcong-\u003elatest_rtt = srtt;\n+\tcong-\u003esmoothed_rtt = cong-\u003elatest_rtt;\n+\tcong-\u003erttvar = cong-\u003esmoothed_rtt / 2;\n+\tquic_cong_pto_update(cong);\n+}\n+\n+void quic_cong_init(struct quic_cong *cong)\n+{\n+\tcong-\u003emax_ack_delay = QUIC_DEF_ACK_DELAY;\n+\tcong-\u003emax_window = S32_MAX / 4;\n+\tquic_cong_set_algo(cong, QUIC_CONG_ALG_RENO);\n+\tquic_cong_set_srtt(cong, QUIC_RTT_INIT);\n+}\ndiff --git a/net/quic/cong.h b/net/quic/cong.h\nnew file mode 100644\nindex 0000000000000..0d3697eb916a0\n--- /dev/null\n+++ b/net/quic/cong.h\n@@ -0,0 +1,132 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#define QUIC_KPERSISTENT_CONGESTION_THRESHOLD\t3\n+#define QUIC_KPACKET_THRESHOLD\t\t\t3\n+#define QUIC_KTIME_THRESHOLD(rtt)\t\t((rtt) * 9 / 8)\n+#define QUIC_KGRANULARITY\t\t\t1000U\n+\n+#define QUIC_RTT_INIT\t\t333000U\n+#define QUIC_RTT_MAX\t\t6000000U\n+#define QUIC_RTT_MIN\t\tQUIC_KGRANULARITY\n+\n+/* rfc9002#section-7.3: Congestion Control States\n+ *\n+ * New path or +------------+\n+ * persistent congestion | Slow |\n+ * (O)----------------------\u003e| Start |\n+ * +------------+\n+ * |\n+ * Loss or |\n+ * ECN-CE increase |\n+ * v\n+ * +------------+ Loss or +------------+\n+ * | Congestion | ECN-CE increase | Recovery |\n+ * | Avoidance |------------------\u003e| Period |\n+ * +------------+ +------------+\n+ * ^ |\n+ * | |\n+ * +----------------------------+\n+ * Acknowledgment of packet\n+ * sent during recovery\n+ */\n+enum quic_cong_state {\n+\tQUIC_CONG_SLOW_START,\n+\tQUIC_CONG_RECOVERY_PERIOD,\n+\tQUIC_CONG_CONGESTION_AVOIDANCE,\n+};\n+\n+struct quic_cong {\n+\t/* RTT tracking */\n+\tu32 max_ack_delay;\t/* max_ack_delay from rfc9000#section-18.2 */\n+\tu32 smoothed_rtt;\t/* Smoothed RTT */\n+\tu32 latest_rtt;\t\t/* Latest RTT sample */\n+\tu32 min_rtt;\t\t/* Lowest observed RTT */\n+\tu32 rttvar;\t\t/* RTT variation */\n+\tu32 pto;\t\t/* Probe timeout */\n+\n+\t/* Timing \u0026 pacing */\n+\tu64 pc_start_time; /* Persistent congestion tracking timestamp */\n+\tu64 recovery_time;\t/* Recovery period start timestamp */\n+\tu64 pacing_rate;\t/* Packet sending speed Bytes/sec */\n+\tu64 pacing_time;\t/* Next scheduled send timestamp (ns) */\n+\tu64 time;\t\t/* Cached current timestamp */\n+\n+\t/* Congestion window */\n+\tu32 max_window;\t\t/* Max growth cap */\n+\tu32 min_window;\t\t/* Min window limit */\n+\tu32 loss_delay;\t\t/* Time before marking loss */\n+\tu32 ssthresh;\t\t/* Slow start threshold */\n+\tu32 window;\t\t/* Bytes in flight allowed */\n+\tu32 mss;\t\t/* QUIC MSS (excl. UDP) */\n+\n+\t/* Algorithm-specific */\n+\tconst struct quic_cong_ops *ops;\n+\tu64 priv[8];\t\t/* Algo private data */\n+\n+\tu32 initial_srtt;\t/* Initial smoothed RTT */\n+\tu8 algo;\t\t/* Congestion control algorithm */\n+\n+\t/* Flags \u0026 state */\n+\tu8 min_rtt_valid;\t/* min_rtt initialized */\n+\tu8 is_rtt_set;\t\t/* RTT samples exist */\n+\tu8 state;\t\t/* State machine in rfc9002#section-7.3 */\n+};\n+\n+/* Hooks for congestion control algorithms */\n+struct quic_cong_ops {\n+\tvoid (*on_packet_acked)(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t\ts64 number);\n+\tvoid (*on_packet_lost)(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t s64 number);\n+\tvoid (*on_process_ecn)(struct quic_cong *cong);\n+\tvoid (*on_init)(struct quic_cong *cong);\n+\n+\t/* Optional callbacks */\n+\tvoid (*on_packet_sent)(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t s64 number);\n+\tvoid (*on_ack_recv)(struct quic_cong *cong, u32 bytes, u64 max_rate);\n+\tvoid (*on_rtt_update)(struct quic_cong *cong);\n+};\n+\n+static inline void quic_cong_set_mss(struct quic_cong *cong, u32 mss)\n+{\n+\tif (cong-\u003emss == mss)\n+\t\treturn;\n+\n+\t/* rfc9002#section-7.2: Initial and Minimum Congestion Window */\n+\tcong-\u003emss = mss;\n+\tcong-\u003emin_window = mss * 2;\n+\n+\tif (!cong-\u003ewindow)\n+\t\tcong-\u003ewindow = min(mss * 10, 14720U);\n+\tif (cong-\u003ewindow \u003c cong-\u003emin_window)\n+\t\tcong-\u003ewindow = cong-\u003emin_window;\n+}\n+\n+static inline void *quic_cong_priv(struct quic_cong *cong)\n+{\n+\treturn (void *)cong-\u003epriv;\n+}\n+\n+void quic_cong_on_packet_acked(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t s64 number);\n+void quic_cong_on_packet_lost(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t s64 number);\n+void quic_cong_on_process_ecn(struct quic_cong *cong);\n+\n+void quic_cong_on_packet_sent(struct quic_cong *cong, u64 time, u32 bytes,\n+\t\t\t s64 number);\n+void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u64 max_rate);\n+void quic_cong_rtt_update(struct quic_cong *cong, u64 time, u32 ack_delay);\n+\n+void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt);\n+void quic_cong_set_algo(struct quic_cong *cong, u8 algo);\n+void quic_cong_init(struct quic_cong *cong);\ndiff --git a/net/quic/connid.c b/net/quic/connid.c\nnew file mode 100644\nindex 0000000000000..e726a1d02c970\n--- /dev/null\n+++ b/net/quic/connid.c\n@@ -0,0 +1,283 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003ccrypto/utils.h\u003e\n+#include \u003clinux/quic.h\u003e\n+#include \u003cnet/sock.h\u003e\n+\n+#include \"common.h\"\n+#include \"connid.h\"\n+\n+/* Lookup a source connection ID (scid) in the global source connection ID hash\n+ * table.\n+ */\n+struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len)\n+{\n+\tstruct quic_shash_head *head = quic_source_conn_id_head(net, scid, len);\n+\tstruct quic_source_conn_id *s_conn_id;\n+\tstruct quic_conn_id *conn_id = NULL;\n+\tstruct hlist_nulls_node *node;\n+\n+\tWARN_ON_ONCE(!rcu_read_lock_held());\n+\n+\thlist_nulls_for_each_entry_rcu(s_conn_id, node, \u0026head-\u003ehead, node) {\n+\t\tif (net != sock_net(s_conn_id-\u003esk))\n+\t\t\tcontinue;\n+\t\tif (s_conn_id-\u003ecommon.id.len != len ||\n+\t\t memcmp(scid, \u0026s_conn_id-\u003ecommon.id.data, len))\n+\t\t\tcontinue;\n+\t\tif (likely(refcount_inc_not_zero(\u0026s_conn_id-\u003esk-\u003esk_refcnt)))\n+\t\t\tconn_id = \u0026s_conn_id-\u003ecommon.id;\n+\t\tbreak;\n+\t}\n+\t/* No need to check get_nulls_value(node) != hash for !conn_id, as\n+\t * hashtable size is fixed and a conn_id can not rehashed.\n+\t */\n+\treturn conn_id;\n+}\n+\n+/* Check if a given stateless reset token exists in any connection ID in the\n+ * connection ID set.\n+ */\n+bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token)\n+{\n+\tstruct quic_common_conn_id *common;\n+\tstruct quic_dest_conn_id *dcid;\n+\n+\tdcid = (struct quic_dest_conn_id *)id_set-\u003eactive;\n+\tif (memchr_inv(dcid-\u003etoken, 0, QUIC_CONN_ID_TOKEN_LEN) \u0026\u0026\n+\t !crypto_memneq(dcid-\u003etoken, token, QUIC_CONN_ID_TOKEN_LEN))\n+\t\treturn true; /* Fast path. */\n+\n+\tlist_for_each_entry(common, \u0026id_set-\u003ehead, list) {\n+\t\tdcid = (struct quic_dest_conn_id *)common;\n+\t\tif (common == id_set-\u003eactive)\n+\t\t\tcontinue;\n+\t\tif (memchr_inv(dcid-\u003etoken, 0, QUIC_CONN_ID_TOKEN_LEN) \u0026\u0026\n+\t\t !crypto_memneq(dcid-\u003etoken, token, QUIC_CONN_ID_TOKEN_LEN))\n+\t\t\treturn true;\n+\t}\n+\treturn false;\n+}\n+\n+static void quic_source_conn_id_free_rcu(struct rcu_head *head)\n+{\n+\tstruct quic_source_conn_id *s_conn_id;\n+\n+\ts_conn_id = container_of(head, struct quic_source_conn_id, rcu);\n+\tkfree(s_conn_id);\n+}\n+\n+static void quic_source_conn_id_free(struct quic_source_conn_id *s_conn_id)\n+{\n+\tu8 *data = s_conn_id-\u003ecommon.id.data;\n+\tu32 len = s_conn_id-\u003ecommon.id.len;\n+\tstruct quic_shash_head *head;\n+\n+\thead = quic_source_conn_id_head(sock_net(s_conn_id-\u003esk), data, len);\n+\tspin_lock_bh(\u0026head-\u003elock);\n+\thlist_nulls_del_init_rcu(\u0026s_conn_id-\u003enode);\n+\tspin_unlock_bh(\u0026head-\u003elock);\n+\n+\t/* Freeing is deferred via RCU to avoid use-after-free during\n+\t * concurrent lookups.\n+\t */\n+\tcall_rcu(\u0026s_conn_id-\u003ercu, quic_source_conn_id_free_rcu);\n+}\n+\n+static void quic_conn_id_del(struct quic_common_conn_id *common)\n+{\n+\tlist_del(\u0026common-\u003elist);\n+\tif (!common-\u003ehashed) {\n+\t\tkfree(common);\n+\t\treturn;\n+\t}\n+\tquic_source_conn_id_free((struct quic_source_conn_id *)common);\n+}\n+\n+/* Add a connection ID with sequence number and associated private data to the\n+ * connection ID set.\n+ */\n+int quic_conn_id_add(struct quic_conn_id_set *id_set,\n+\t\t struct quic_conn_id *conn_id, u32 number, void *data,\n+\t\t gfp_t gfp)\n+{\n+\tbool dest = id_set-\u003eentry_size == sizeof(struct quic_dest_conn_id);\n+\tstruct quic_source_conn_id *s_conn_id, *pos;\n+\tstruct quic_dest_conn_id *d_conn_id;\n+\tstruct quic_common_conn_id *common;\n+\tstruct hlist_nulls_node *node;\n+\tstruct quic_shash_head *head;\n+\tstruct list_head *list;\n+\tstruct net *net;\n+\n+\t/* Locate insertion point to keep list ordered by number. */\n+\tlist = \u0026id_set-\u003ehead;\n+\tlist_for_each_entry(common, list, list) {\n+\t\tif (number == common-\u003enumber) {\n+\t\t\tif (quic_conn_id_cmp(\u0026common-\u003eid, conn_id))\n+\t\t\t\treturn -EINVAL;\n+\t\t\tif (dest \u0026\u0026 data) {\n+\t\t\t\td_conn_id = (struct quic_dest_conn_id *)common;\n+\t\t\t\tif (crypto_memneq(d_conn_id-\u003etoken, data,\n+\t\t\t\t\t\t QUIC_CONN_ID_TOKEN_LEN))\n+\t\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t\treturn 0; /* Ignore if it already exists on the list. */\n+\t\t}\n+\t\tif (number \u003c common-\u003enumber) {\n+\t\t\tlist = \u0026common-\u003elist;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (conn_id-\u003elen \u003e QUIC_CONN_ID_MAX_LEN)\n+\t\treturn -EINVAL;\n+\tcommon = kzalloc(id_set-\u003eentry_size, gfp);\n+\tif (!common)\n+\t\treturn -ENOMEM;\n+\tcommon-\u003eid = *conn_id;\n+\tcommon-\u003enumber = number;\n+\tif (dest) {\n+\t\t/* For destination connection IDs, copy the stateless reset\n+\t\t * token if available.\n+\t\t */\n+\t\tif (data) {\n+\t\t\td_conn_id = (struct quic_dest_conn_id *)common;\n+\t\t\tmemcpy(d_conn_id-\u003etoken, data, QUIC_CONN_ID_TOKEN_LEN);\n+\t\t}\n+\t} else {\n+\t\t/* For source connection IDs, mark as hashed and insert into\n+\t\t * the global source connection ID hashtable.\n+\t\t */\n+\t\tcommon-\u003ehashed = 1;\n+\t\ts_conn_id = (struct quic_source_conn_id *)common;\n+\t\ts_conn_id-\u003esk = data;\n+\t\tnet = sock_net(s_conn_id-\u003esk);\n+\n+\t\thead = quic_source_conn_id_head(net, common-\u003eid.data,\n+\t\t\t\t\t\tcommon-\u003eid.len);\n+\t\tspin_lock_bh(\u0026head-\u003elock);\n+\n+\t\t/* Check for collision before inserting */\n+\t\thlist_nulls_for_each_entry(pos, node, \u0026head-\u003ehead, node) {\n+\t\t\tif (net != sock_net(pos-\u003esk))\n+\t\t\t\tcontinue;\n+\t\t\tif (quic_conn_id_cmp(\u0026pos-\u003ecommon.id, \u0026common-\u003eid))\n+\t\t\t\tcontinue;\n+\t\t\tspin_unlock_bh(\u0026head-\u003elock);\n+\t\t\tkfree(common);\n+\t\t\tWARN_ON_ONCE(1);\n+\t\t\treturn -EEXIST;\n+\t\t}\n+\n+\t\thlist_nulls_add_head_rcu(\u0026s_conn_id-\u003enode, \u0026head-\u003ehead);\n+\t\tspin_unlock_bh(\u0026head-\u003elock);\n+\t}\n+\tlist_add_tail(\u0026common-\u003elist, list);\n+\n+\tif (number == quic_conn_id_last_number(id_set) + 1) {\n+\t\tif (!id_set-\u003eactive)\n+\t\t\tid_set-\u003eactive = common;\n+\t\tid_set-\u003ecount++;\n+\n+\t\t/* Increment count for consecutive following IDs. */\n+\t\tlist_for_each_entry_continue(common, \u0026id_set-\u003ehead, list) {\n+\t\t\tif (common-\u003enumber != ++number)\n+\t\t\t\tbreak;\n+\t\t\tid_set-\u003ecount++;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n+/* Remove consecutive connection IDs from the set with sequence numbers less\n+ * than or equal to a number.\n+ */\n+void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number)\n+{\n+\tstruct quic_common_conn_id *common, *tmp;\n+\tstruct list_head *list;\n+\n+\t/* The number must be less than the sequence number of the last\n+\t * consecutive connection ID in the set.\n+\t */\n+\tif (WARN_ON_ONCE(number \u003e= quic_conn_id_last_number(id_set)))\n+\t\treturn;\n+\tlist = \u0026id_set-\u003ehead;\n+\tlist_for_each_entry_safe(common, tmp, list, list) {\n+\t\tif (common-\u003enumber \u003e number)\n+\t\t\tbreak;\n+\t\tif (id_set-\u003eactive == common)\n+\t\t\tid_set-\u003eactive = tmp;\n+\t\tif (id_set-\u003ealt == common)\n+\t\t\tid_set-\u003ealt = tmp;\n+\t\tquic_conn_id_del(common);\n+\t\tid_set-\u003ecount--;\n+\t}\n+}\n+\n+struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set,\n+\t\t\t\t u32 number)\n+{\n+\tstruct quic_common_conn_id *common;\n+\n+\tlist_for_each_entry(common, \u0026id_set-\u003ehead, list) {\n+\t\tif (common-\u003enumber \u003e number)\n+\t\t\tbreak;\n+\t\tif (common-\u003enumber == number)\n+\t\t\treturn \u0026common-\u003eid;\n+\t}\n+\treturn NULL;\n+}\n+\n+void quic_conn_id_update_active(struct quic_conn_id_set *id_set, u32 number)\n+{\n+\tstruct quic_conn_id *conn_id;\n+\n+\tif (number == id_set-\u003eactive-\u003enumber)\n+\t\treturn;\n+\tconn_id = quic_conn_id_find(id_set, number);\n+\tif (!conn_id)\n+\t\treturn;\n+\tquic_conn_id_set_active(id_set, conn_id);\n+}\n+\n+void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source)\n+{\n+\tid_set-\u003eentry_size = source ? sizeof(struct quic_source_conn_id) :\n+\t\t\t\t sizeof(struct quic_dest_conn_id);\n+\tINIT_LIST_HEAD(\u0026id_set-\u003ehead);\n+}\n+\n+void quic_conn_id_set_free(struct quic_conn_id_set *id_set)\n+{\n+\tstruct quic_common_conn_id *common, *tmp;\n+\n+\tlist_for_each_entry_safe(common, tmp, \u0026id_set-\u003ehead, list)\n+\t\tquic_conn_id_del(common);\n+\tid_set-\u003ecount = 0;\n+\tid_set-\u003ealt = NULL;\n+\tid_set-\u003eactive = NULL;\n+}\n+\n+void quic_conn_id_get_param(struct quic_conn_id_set *id_set,\n+\t\t\t struct quic_transport_param *p)\n+{\n+\tp-\u003eactive_connection_id_limit = id_set-\u003emax_count;\n+}\n+\n+void quic_conn_id_set_param(struct quic_conn_id_set *id_set,\n+\t\t\t struct quic_transport_param *p)\n+{\n+\tid_set-\u003emax_count = p-\u003eactive_connection_id_limit;\n+}\ndiff --git a/net/quic/connid.h b/net/quic/connid.h\nnew file mode 100644\nindex 0000000000000..abad396a6ad6a\n--- /dev/null\n+++ b/net/quic/connid.h\n@@ -0,0 +1,183 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#define QUIC_CONN_ID_LIMIT\t8\n+#define QUIC_CONN_ID_DEF\t7\n+#define QUIC_CONN_ID_LEAST\t2\n+\n+#define QUIC_CONN_ID_TOKEN_LEN\t16\n+\n+/* Common fields shared by both source and destination Connection IDs */\n+struct quic_common_conn_id {\n+\tstruct quic_conn_id id; /* Connection ID value and its length */\n+\tstruct list_head list; /* List node for connection ID management */\n+\tu32 number; /* Sequence number assigned to this Connection ID */\n+\tu8 hashed; /* Non-zero if stored in source_conn_id hash table */\n+};\n+\n+struct quic_source_conn_id {\n+\tstruct quic_common_conn_id common;\n+\tstruct hlist_nulls_node node; /* Hash table node for fast lookup */\n+\tstruct rcu_head rcu; /* RCU header for deferred destruction */\n+\tstruct sock *sk; /* Socket associated with this Connection ID */\n+};\n+\n+struct quic_dest_conn_id {\n+\tstruct quic_common_conn_id common;\n+\t/* Stateless reset token in rfc9000#section-10.3 */\n+\tu8 token[QUIC_CONN_ID_TOKEN_LEN];\n+};\n+\n+struct quic_conn_id_set {\n+\t/* Connection ID in use on the current path */\n+\tstruct quic_common_conn_id *active;\n+\t/* Connection ID to use for a new path (e.g., after migration) */\n+\tstruct quic_common_conn_id *alt;\n+\tstruct list_head head; /* List head of available connection IDs */\n+\tu8 entry_size; /* Size of each connection ID entry in the list */\n+\tu8 max_count; /* active_connection_id_limit in rfc9000#section-18.2 */\n+\tu8 count; /* Current number of connection IDs in the list */\n+};\n+\n+static inline u32 quic_conn_id_first_number(struct quic_conn_id_set *id_set)\n+{\n+\tstruct quic_common_conn_id *common;\n+\n+\t/* The id_set is guaranteed to be non-empty when called (sk is not in\n+\t * CLOSE state).\n+\t */\n+\tcommon = list_first_entry(\u0026id_set-\u003ehead, struct quic_common_conn_id,\n+\t\t\t\t list);\n+\treturn common-\u003enumber;\n+}\n+\n+static inline u32 quic_conn_id_last_number(struct quic_conn_id_set *id_set)\n+{\n+\treturn quic_conn_id_first_number(id_set) + id_set-\u003ecount - 1;\n+}\n+\n+static inline void quic_conn_id_generate(struct quic_conn_id *conn_id)\n+{\n+\tget_random_bytes(conn_id-\u003edata, QUIC_CONN_ID_DEF_LEN);\n+\tconn_id-\u003elen = QUIC_CONN_ID_DEF_LEN;\n+}\n+\n+/* Select an alternate destination Connection ID for a new path (e.g., after\n+ * migration).\n+ */\n+static inline bool quic_conn_id_select_alt(struct quic_conn_id_set *id_set,\n+\t\t\t\t\t bool active)\n+{\n+\tif (id_set-\u003ealt)\n+\t\treturn true;\n+\t/* NAT rebinding: peer keeps using the current source conn_id.\n+\t * In this case, continue using the same dest conn_id for the new path.\n+\t */\n+\tif (active) {\n+\t\tid_set-\u003ealt = id_set-\u003eactive;\n+\t\treturn true;\n+\t}\n+\t/* Treat the prev conn_ids as used.\n+\t * Try selecting the next conn_id in the list, unless at the end.\n+\t */\n+\tif (id_set-\u003eactive-\u003enumber != quic_conn_id_last_number(id_set)) {\n+\t\tid_set-\u003ealt = list_next_entry(id_set-\u003eactive, list);\n+\t\treturn true;\n+\t}\n+\t/* If there's only one conn_id in the list, reuse the active one. */\n+\tif (id_set-\u003eactive-\u003enumber == quic_conn_id_first_number(id_set)) {\n+\t\tid_set-\u003ealt = id_set-\u003eactive;\n+\t\treturn true;\n+\t}\n+\t/* No alternate conn_id could be selected. Caller should send a\n+\t * QUIC_FRAME_RETIRE_CONNECTION_ID frame to request new connection IDs\n+\t * from the peer.\n+\t */\n+\treturn false;\n+}\n+\n+static inline void quic_conn_id_set_alt(struct quic_conn_id_set *id_set,\n+\t\t\t\t\tstruct quic_conn_id *alt)\n+{\n+\tid_set-\u003ealt = (struct quic_common_conn_id *)alt;\n+}\n+\n+/* Swap the active and alternate destination Connection IDs after path\n+ * migration completes, since the path has already been switched accordingly.\n+ */\n+static inline void quic_conn_id_swap_active(struct quic_conn_id_set *id_set)\n+{\n+\tvoid *active = id_set-\u003eactive;\n+\n+\tid_set-\u003eactive = id_set-\u003ealt;\n+\tid_set-\u003ealt = active;\n+}\n+\n+/* Choose which destination Connection ID to use for a new path migration if\n+ * alt is true.\n+ */\n+static inline struct quic_conn_id *\n+quic_conn_id_choose(struct quic_conn_id_set *id_set, u8 alt)\n+{\n+\treturn (alt \u0026\u0026 id_set-\u003ealt) ? \u0026id_set-\u003ealt-\u003eid : \u0026id_set-\u003eactive-\u003eid;\n+}\n+\n+static inline struct quic_conn_id *\n+quic_conn_id_active(struct quic_conn_id_set *id_set)\n+{\n+\treturn \u0026id_set-\u003eactive-\u003eid;\n+}\n+\n+static inline void quic_conn_id_set_active(struct quic_conn_id_set *id_set,\n+\t\t\t\t\t struct quic_conn_id *active)\n+{\n+\tid_set-\u003eactive = (struct quic_common_conn_id *)active;\n+}\n+\n+static inline u32 quic_conn_id_number(struct quic_conn_id *conn_id)\n+{\n+\treturn ((struct quic_common_conn_id *)conn_id)-\u003enumber;\n+}\n+\n+static inline struct sock *quic_conn_id_sk(struct quic_conn_id *conn_id)\n+{\n+\treturn ((struct quic_source_conn_id *)conn_id)-\u003esk;\n+}\n+\n+static inline void quic_conn_id_set_token(struct quic_conn_id *conn_id,\n+\t\t\t\t\t u8 *token)\n+{\n+\tmemcpy(((struct quic_dest_conn_id *)conn_id)-\u003etoken, token,\n+\t QUIC_CONN_ID_TOKEN_LEN);\n+}\n+\n+static inline int quic_conn_id_cmp(struct quic_conn_id *a,\n+\t\t\t\t struct quic_conn_id *b)\n+{\n+\treturn a-\u003elen != b-\u003elen || memcmp(a-\u003edata, b-\u003edata, a-\u003elen);\n+}\n+\n+int quic_conn_id_add(struct quic_conn_id_set *id_set,\n+\t\t struct quic_conn_id *conn_id, u32 number, void *data,\n+\t\t gfp_t gfp);\n+bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token);\n+void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number);\n+\n+struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set,\n+\t\t\t\t u32 number);\n+struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len);\n+void quic_conn_id_update_active(struct quic_conn_id_set *id_set, u32 number);\n+\n+void quic_conn_id_get_param(struct quic_conn_id_set *id_set,\n+\t\t\t struct quic_transport_param *p);\n+void quic_conn_id_set_param(struct quic_conn_id_set *id_set,\n+\t\t\t struct quic_transport_param *p);\n+void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source);\n+void quic_conn_id_set_free(struct quic_conn_id_set *id_set);\ndiff --git a/net/quic/crypto.c b/net/quic/crypto.c\nnew file mode 100644\nindex 0000000000000..d62c6c4bea7de\n--- /dev/null\n+++ b/net/quic/crypto.c\n@@ -0,0 +1,1251 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003ccrypto/skcipher.h\u003e\n+#include \u003clinux/skbuff.h\u003e\n+#include \u003ccrypto/utils.h\u003e\n+#include \u003ccrypto/aead.h\u003e\n+#include \u003ccrypto/hash.h\u003e\n+#include \u003clinux/quic.h\u003e\n+#include \u003cnet/tls.h\u003e\n+\n+#include \"common.h\"\n+#include \"crypto.h\"\n+\n+/* HKDF-Extract. */\n+static int quic_crypto_hkdf_extract(struct crypto_shash *tfm,\n+\t\t\t\t struct quic_data *srt,\n+\t\t\t\t struct quic_data *hash,\n+\t\t\t\t struct quic_data *key)\n+{\n+\tint err;\n+\n+\terr = crypto_shash_setkey(tfm, srt-\u003edata, srt-\u003elen);\n+\tif (err)\n+\t\treturn err;\n+\n+\treturn crypto_shash_tfm_digest(tfm, hash-\u003edata, hash-\u003elen, key-\u003edata);\n+}\n+\n+#define QUIC_MAX_INFO_LEN\t256\n+\n+/* HKDF-Expand-Label. */\n+static int quic_crypto_hkdf_expand(struct crypto_shash *tfm,\n+\t\t\t\t struct quic_data *srt,\n+\t\t\t\t struct quic_data *label,\n+\t\t\t\t struct quic_data *key)\n+{\n+\tu8 info[QUIC_MAX_INFO_LEN], *p = info, tmp[QUIC_SECRET_LEN];\n+\tunsigned int i, infolen, hashlen = srt-\u003elen;\n+\tSHASH_DESC_ON_STACK(desc, tfm);\n+\tu8 LABEL[] = \"tls13 \", cnt = 1;\n+\tconst u8 *prev = NULL;\n+\tint err;\n+\n+\t/* rfc8446#section-7.1:\n+\t *\n+\t * HKDF-Expand-Label(Secret, Label, Context, Length) =\n+\t * HKDF-Expand(Secret, HkdfLabel, Length)\n+\t *\n+\t * Where HkdfLabel is specified as:\n+\t *\n+\t * struct {\n+\t * uint16 length = Length;\n+\t * opaque label\u003c7..255\u003e = \"tls13 \" + Label;\n+\t * opaque context\u003c0..255\u003e = Context;\n+\t * } HkdfLabel;\n+\t */\n+\tput_unaligned_be16(key-\u003elen, p);\n+\tp += 2;\n+\t*p++ = (u8)(sizeof(LABEL) - 1 + label-\u003elen);\n+\tp = quic_put_data(p, LABEL, sizeof(LABEL) - 1);\n+\tp = quic_put_data(p, label-\u003edata, label-\u003elen);\n+\t*p++ = 0;\n+\tinfolen = (unsigned int)(p - info);\n+\n+\terr = crypto_shash_setkey(tfm, srt-\u003edata, srt-\u003elen);\n+\tif (err)\n+\t\treturn err;\n+\tdesc-\u003etfm = tfm;\n+\n+\tfor (i = 0; i \u003c key-\u003elen; i += hashlen) {\n+\t\terr = crypto_shash_init(desc);\n+\t\tif (err)\n+\t\t\tgoto out;\n+\n+\t\tif (prev) {\n+\t\t\terr = crypto_shash_update(desc, prev, hashlen);\n+\t\t\tif (err)\n+\t\t\t\tgoto out;\n+\t\t}\n+\n+\t\terr = crypto_shash_update(desc, info, infolen);\n+\t\tif (err)\n+\t\t\tgoto out;\n+\n+\t\tBUILD_BUG_ON(sizeof(cnt) != 1);\n+\t\tif (key-\u003elen - i \u003c hashlen) {\n+\t\t\terr = crypto_shash_finup(desc, \u0026cnt, 1, tmp);\n+\t\t\tif (err)\n+\t\t\t\tgoto out;\n+\t\t\tmemcpy(\u0026key-\u003edata[i], tmp, key-\u003elen - i);\n+\t\t\tmemzero_explicit(tmp, sizeof(tmp));\n+\t\t} else {\n+\t\t\terr = crypto_shash_finup(desc, \u0026cnt, 1, \u0026key-\u003edata[i]);\n+\t\t\tif (err)\n+\t\t\t\tgoto out;\n+\t\t}\n+\t\tcnt++;\n+\t\tprev = \u0026key-\u003edata[i];\n+\t}\n+out:\n+\tshash_desc_zero(desc);\n+\tmemzero_explicit(tmp, sizeof(tmp));\n+\treturn err;\n+}\n+\n+#define KEY_LABEL_V1\t\t\"quic key\"\n+#define IV_LABEL_V1\t\t\"quic iv\"\n+#define HP_KEY_LABEL_V1\t\t\"quic hp\"\n+\n+#define KU_LABEL_V1\t\t\"quic ku\"\n+\n+/* rfc9369#section-3.3.2:\n+ *\n+ * The labels used in rfc9001 to derive packet protection keys, header\n+ * protection keys, Retry Integrity Tag keys, and key updates change from \"quic\n+ * key\" to \"quicv2 key\", from \"quic iv\" to \"quicv2 iv\", from \"quic hp\" to\n+ * \"quicv2 hp\", and from \"quic ku\" to \"quicv2 ku\".\n+ */\n+#define KEY_LABEL_V2\t\t\"quicv2 key\"\n+#define IV_LABEL_V2\t\t\"quicv2 iv\"\n+#define HP_KEY_LABEL_V2\t\t\"quicv2 hp\"\n+\n+#define KU_LABEL_V2\t\t\"quicv2 ku\"\n+\n+/* Packet Protection Keys. */\n+static int quic_crypto_keys_derive(struct crypto_shash *tfm,\n+\t\t\t\t struct quic_data *s, struct quic_data *k,\n+\t\t\t\t struct quic_data *i, struct quic_data *hp_k,\n+\t\t\t\t u32 version)\n+{\n+\tstruct quic_data hp_k_l = {HP_KEY_LABEL_V1, strlen(HP_KEY_LABEL_V1)};\n+\tstruct quic_data k_l = {KEY_LABEL_V1, strlen(KEY_LABEL_V1)};\n+\tstruct quic_data i_l = {IV_LABEL_V1, strlen(IV_LABEL_V1)};\n+\tint err;\n+\n+\t/* rfc9001#section-5.1:\n+\t *\n+\t * The current encryption level secret and the label \"quic key\" are\n+\t * input to the KDF to produce the AEAD key; the label \"quic iv\" is\n+\t * used to derive the Initialization Vector (IV). The header protection\n+\t * key uses the \"quic hp\" label. Using these labels provides key\n+\t * separation between QUIC and TLS.\n+\t */\n+\tif (version == QUIC_VERSION_V2) {\n+\t\tquic_data(\u0026hp_k_l, HP_KEY_LABEL_V2, strlen(HP_KEY_LABEL_V2));\n+\t\tquic_data(\u0026k_l, KEY_LABEL_V2, strlen(KEY_LABEL_V2));\n+\t\tquic_data(\u0026i_l, IV_LABEL_V2, strlen(IV_LABEL_V2));\n+\t}\n+\n+\terr = quic_crypto_hkdf_expand(tfm, s, \u0026k_l, k);\n+\tif (err)\n+\t\treturn err;\n+\terr = quic_crypto_hkdf_expand(tfm, s, \u0026i_l, i);\n+\tif (err)\n+\t\treturn err;\n+\t/* Don't change hp key for key update. */\n+\tif (!hp_k)\n+\t\treturn 0;\n+\n+\treturn quic_crypto_hkdf_expand(tfm, s, \u0026hp_k_l, hp_k);\n+}\n+\n+/* Derive and install reception (RX) or transmission (TX) packet protection\n+ * keys for the current key phase. This installs AEAD protection key, IV, and\n+ * optionally header protection key.\n+ */\n+static int quic_crypto_keys_derive_and_install(struct quic_crypto *crypto,\n+\t\t\t\t\t bool rx, u8 phase)\n+{\n+\tstruct quic_data srt = {}, k, iv, hp_k = {}, *hp = NULL;\n+\tu8 key[QUIC_KEY_LEN], hp_key[QUIC_KEY_LEN] = {};\n+\tu32 keylen, ivlen = QUIC_IV_LEN;\n+\tstruct crypto_skcipher *hp_tfm;\n+\tstruct crypto_aead *tfm;\n+\tint err;\n+\n+\tkeylen = crypto-\u003ecipher-\u003ekeylen;\n+\tquic_data(\u0026k, key, keylen);\n+\n+\tif (rx) {\n+\t\tquic_data(\u0026srt, crypto-\u003erx_secret[phase],\n+\t\t\t crypto-\u003ecipher-\u003esecretlen);\n+\t\tquic_data(\u0026iv, crypto-\u003erx_iv[phase], ivlen);\n+\t\ttfm = crypto-\u003erx_tfm[phase];\n+\t\thp_tfm = crypto-\u003erx_hp_tfm;\n+\t} else {\n+\t\tquic_data(\u0026srt, crypto-\u003etx_secret[phase],\n+\t\t\t crypto-\u003ecipher-\u003esecretlen);\n+\t\tquic_data(\u0026iv, crypto-\u003etx_iv[phase], ivlen);\n+\t\ttfm = crypto-\u003etx_tfm[phase];\n+\t\thp_tfm = crypto-\u003etx_hp_tfm;\n+\t}\n+\n+\t/* Only derive header protection key when not in key update. */\n+\tif (crypto-\u003ekey_phase == phase)\n+\t\thp = quic_data(\u0026hp_k, hp_key, keylen);\n+\terr = quic_crypto_keys_derive(crypto-\u003esecret_tfm, \u0026srt, \u0026k, \u0026iv, hp,\n+\t\t\t\t crypto-\u003eversion);\n+\tif (err)\n+\t\tgoto out;\n+\terr = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);\n+\tif (err)\n+\t\tgoto out;\n+\terr = crypto_aead_setkey(tfm, key, keylen);\n+\tif (err)\n+\t\tgoto out;\n+\tif (hp) {\n+\t\terr = crypto_skcipher_setkey(hp_tfm, hp_key, keylen);\n+\t\tif (err)\n+\t\t\tgoto out;\n+\t}\n+out:\n+\tmemzero_explicit(key, sizeof(key));\n+\tmemzero_explicit(hp_key, sizeof(hp_key));\n+\treturn err;\n+}\n+\n+#define QUIC_CIPHER_MIN TLS_CIPHER_AES_GCM_128\n+#define QUIC_CIPHER_MAX TLS_CIPHER_CHACHA20_POLY1305\n+\n+#define TLS_CIPHER_AES_GCM_128_SECRET_SIZE\t\t32\n+#define TLS_CIPHER_AES_GCM_256_SECRET_SIZE\t\t48\n+#define TLS_CIPHER_AES_CCM_128_SECRET_SIZE\t\t32\n+#define TLS_CIPHER_CHACHA20_POLY1305_SECRET_SIZE\t32\n+\n+#define CIPHER_DESC(type, aead_n, skc_n, sha_n)[type - QUIC_CIPHER_MIN] = { \\\n+\t.secretlen = type ## _SECRET_SIZE, \\\n+\t.keylen = type ## _KEY_SIZE, \\\n+\t.aead = aead_n, \\\n+\t.skc = skc_n, \\\n+\t.shash = sha_n, \\\n+}\n+\n+static const struct quic_cipher\n+ciphers[QUIC_CIPHER_MAX + 1 - QUIC_CIPHER_MIN] = {\n+\tCIPHER_DESC(TLS_CIPHER_AES_GCM_128,\n+\t\t \"gcm(aes)\", \"ecb(aes)\", \"hmac(sha256)\"),\n+\tCIPHER_DESC(TLS_CIPHER_AES_GCM_256,\n+\t\t \"gcm(aes)\", \"ecb(aes)\", \"hmac(sha384)\"),\n+\tCIPHER_DESC(TLS_CIPHER_AES_CCM_128,\n+\t\t \"ccm(aes)\", \"ecb(aes)\", \"hmac(sha256)\"),\n+\tCIPHER_DESC(TLS_CIPHER_CHACHA20_POLY1305,\n+\t\t \"rfc7539(chacha20,poly1305)\", \"chacha20\", \"hmac(sha256)\"),\n+};\n+\n+static bool quic_crypto_is_cipher_ccm(struct quic_crypto *crypto)\n+{\n+\treturn crypto-\u003ecipher_type == TLS_CIPHER_AES_CCM_128;\n+}\n+\n+static bool quic_crypto_is_cipher_chacha(struct quic_crypto *crypto)\n+{\n+\treturn crypto-\u003ecipher_type == TLS_CIPHER_CHACHA20_POLY1305;\n+}\n+\n+static void *quic_crypto_skcipher_mem_alloc(struct crypto_skcipher *tfm,\n+\t\t\t\t\t u32 mask_size, u8 **iv,\n+\t\t\t\t\t struct skcipher_request **req,\n+\t\t\t\t\t gfp_t gfp)\n+{\n+\tunsigned int iv_size, req_size;\n+\tunsigned int len;\n+\tu8 *mem;\n+\n+\tiv_size = crypto_skcipher_ivsize(tfm);\n+\treq_size = sizeof(**req) + crypto_skcipher_reqsize(tfm);\n+\n+\tlen = mask_size;\n+\tlen += iv_size;\n+\tlen += crypto_skcipher_alignmask(tfm) \u0026\n+\t ~(crypto_tfm_ctx_alignment() - 1);\n+\tlen = ALIGN(len, crypto_tfm_ctx_alignment());\n+\tlen += req_size;\n+\n+\tmem = kzalloc(len, gfp);\n+\tif (!mem)\n+\t\treturn NULL;\n+\n+\t*iv = (u8 *)PTR_ALIGN(mem + mask_size,\n+\t\t\t crypto_skcipher_alignmask(tfm) + 1);\n+\t*req = (struct skcipher_request *)PTR_ALIGN(*iv + iv_size,\n+\t\t\t\t\t\t crypto_tfm_ctx_alignment());\n+\n+\treturn (void *)mem;\n+}\n+\n+/* Extracts and reconstructs the packet number from an incoming QUIC packet. */\n+static int quic_crypto_get_number(struct sk_buff *skb)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\ts64 number_max = cb-\u003enumber;\n+\tu32 len = cb-\u003elength;\n+\tu8 *p;\n+\n+\t/* rfc9000#section-17.1:\n+\t *\n+\t * Once header protection is removed, the packet number is decoded by\n+\t * finding the packet number value that is closest to the next expected\n+\t * packet. The next expected packet is the highest received packet\n+\t * number plus one.\n+\t */\n+\tp = (u8 *)quic_hdr(skb) + cb-\u003enumber_offset;\n+\tif (!quic_get_int(\u0026p, \u0026len, \u0026cb-\u003enumber, cb-\u003enumber_len))\n+\t\treturn -EINVAL;\n+\tcb-\u003enumber = quic_get_num(number_max, cb-\u003enumber, cb-\u003enumber_len);\n+\treturn 0;\n+}\n+\n+#define QUIC_SAMPLE_LEN\t\t16\n+\n+#define QUIC_HEADER_FORM_BIT\t0x80\n+#define QUIC_LONG_HEADER_MASK\t0x0f\n+#define QUIC_SHORT_HEADER_MASK\t0x1f\n+\n+/* Header Protection. */\n+static int quic_crypto_header_protect(struct quic_crypto *crypto,\n+\t\t\t\t struct sk_buff *skb, bool enc, gfp_t gfp)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tu8 *mask, *iv, *p, h_mask, chacha;\n+\tstruct skcipher_request *req;\n+\tstruct crypto_skcipher *tfm;\n+\tstruct sk_buff *trailer;\n+\tstruct scatterlist sg;\n+\tint err, i;\n+\n+\tchacha = quic_crypto_is_cipher_chacha(crypto);\n+\tif (!enc) {\n+\t\ttfm = crypto-\u003erx_hp_tfm;\n+\t\tif (cb-\u003elength \u003c QUIC_PN_MAX_LEN + QUIC_SAMPLE_LEN)\n+\t\t\treturn -EINVAL;\n+\n+\t\terr = skb_cow_data(skb, 0, \u0026trailer);\n+\t\tif (err \u003c 0)\n+\t\t\treturn err;\n+\t} else {\n+\t\ttfm = crypto-\u003etx_hp_tfm;\n+\t}\n+\n+\tmask = quic_crypto_skcipher_mem_alloc(tfm, QUIC_SAMPLE_LEN, \u0026iv, \u0026req,\n+\t\t\t\t\t gfp);\n+\tif (!mask)\n+\t\treturn -ENOMEM;\n+\n+\t/* rfc9001#section-5.4.2: Header Protection Sample:\n+\t *\n+\t * # pn_offset is the start of the Packet Number field.\n+\t * sample_offset = pn_offset + 4\n+\t *\n+\t * sample = packet[sample_offset..sample_offset+sample_length]\n+\t *\n+\t * rfc9001#section-5.4.3: AES-Based Header Protection:\n+\t *\n+\t * header_protection(hp_key, sample):\n+\t * mask = AES-ECB(hp_key, sample)\n+\t *\n+\t * rfc9001#section-5.4.4: ChaCha20-Based Header Protection:\n+\t *\n+\t * header_protection(hp_key, sample):\n+\t * counter = sample[0..3]\n+\t * nonce = sample[4..15]\n+\t * mask = ChaCha20(hp_key, counter, nonce, {0,0,0,0,0})\n+\t */\n+\tp = skb-\u003edata + cb-\u003enumber_offset + QUIC_PN_MAX_LEN;\n+\tmemcpy((chacha ? iv : mask), p, QUIC_SAMPLE_LEN);\n+\tsg_init_one(\u0026sg, mask, QUIC_SAMPLE_LEN);\n+\tskcipher_request_set_tfm(req, tfm);\n+\tskcipher_request_set_crypt(req, \u0026sg, \u0026sg, QUIC_SAMPLE_LEN, iv);\n+\terr = crypto_skcipher_encrypt(req);\n+\tif (err)\n+\t\tgoto out;\n+\n+\t/* rfc9001#section-5.4.1:\n+\t *\n+\t * mask = header_protection(hp_key, sample)\n+\t *\n+\t * pn_length = (packet[0] \u0026 0x03) + 1\n+\t * if (packet[0] \u0026 0x80) == 0x80:\n+\t * # Long header: 4 bits masked\n+\t * packet[0] ^= mask[0] \u0026 0x0f\n+\t * else:\n+\t * # Short header: 5 bits masked\n+\t * packet[0] ^= mask[0] \u0026 0x1f\n+\t *\n+\t * # pn_offset is the start of the Packet Number field.\n+\t * packet[pn_offset:pn_offset+pn_length] ^= mask[1:1+pn_length]\n+\t */\n+\tp = skb-\u003edata;\n+\th_mask = ((*p \u0026 QUIC_HEADER_FORM_BIT) == QUIC_HEADER_FORM_BIT) ?\n+\t\t QUIC_LONG_HEADER_MASK : QUIC_SHORT_HEADER_MASK;\n+\t*p = (u8)(*p ^ (mask[0] \u0026 h_mask));\n+\tif (!enc) {\n+\t\tif (!quic_hdr(skb)-\u003eform)\n+\t\t\tcb-\u003ekey_phase = quic_hdr(skb)-\u003ekey;\n+\t\tcb-\u003enumber_len = quic_hdr(skb)-\u003epnl + 1;\n+\t}\n+\tp += cb-\u003enumber_offset;\n+\tfor (i = 1; i \u003c= cb-\u003enumber_len; i++)\n+\t\t*p++ ^= mask[i];\n+\n+\tif (!enc)\n+\t\terr = quic_crypto_get_number(skb);\n+out:\n+\tkfree_sensitive(mask);\n+\treturn err;\n+}\n+\n+static void *quic_crypto_aead_mem_alloc(struct crypto_aead *tfm, u32 ctx_size,\n+\t\t\t\t\tu8 **iv, struct aead_request **req,\n+\t\t\t\t\tstruct scatterlist **sg, u32 nsg,\n+\t\t\t\t\tgfp_t gfp)\n+{\n+\tunsigned int iv_size, req_size;\n+\tunsigned int len;\n+\tu8 *mem;\n+\n+\tiv_size = crypto_aead_ivsize(tfm);\n+\treq_size = sizeof(**req) + crypto_aead_reqsize(tfm);\n+\n+\tlen = ctx_size;\n+\tlen += iv_size;\n+\tlen += crypto_aead_alignmask(tfm) \u0026 ~(crypto_tfm_ctx_alignment() - 1);\n+\tlen = ALIGN(len, crypto_tfm_ctx_alignment());\n+\tlen += req_size;\n+\tlen = ALIGN(len, __alignof__(struct scatterlist));\n+\tlen += nsg * sizeof(**sg);\n+\n+\tmem = kzalloc(len, gfp);\n+\tif (!mem)\n+\t\treturn NULL;\n+\n+\t*iv = (u8 *)PTR_ALIGN(mem + ctx_size, crypto_aead_alignmask(tfm) + 1);\n+\t*req = (struct aead_request *)PTR_ALIGN(*iv + iv_size,\n+\t\t\t\t\t\tcrypto_tfm_ctx_alignment());\n+\t*sg = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size,\n+\t\t\t\t\t __alignof__(struct scatterlist));\n+\n+\treturn (void *)mem;\n+}\n+\n+static void quic_crypto_done(void *data, int err)\n+{\n+\tstruct sk_buff *skb = data;\n+\tstruct quic_crypto *crypto;\n+\tstruct quic_skb_cb *cb;\n+\n+\tif (err == -EINPROGRESS)\n+\t\treturn;\n+\n+\tcb = QUIC_SKB_CB(skb);\n+\tcrypto = *(struct quic_crypto **)cb-\u003ecrypto_ctx;\n+\tatomic_dec(\u0026crypto-\u003easync_pending[cb-\u003ekey_phase]);\n+\n+\tkfree_sensitive(cb-\u003ecrypto_ctx);\n+\tcb-\u003ecrypto_done(skb, err);\n+}\n+\n+/* AEAD Usage. */\n+static int quic_crypto_payload_protect(struct quic_crypto *crypto,\n+\t\t\t\t struct sk_buff *skb, bool enc, gfp_t gfp)\n+{\n+\tu8 *base_iv, *iv, i, nonce[QUIC_IV_LEN], ccm, phase;\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tu32 len, hlen, sglen, nsg;\n+\tstruct aead_request *req;\n+\tstruct crypto_aead *tfm;\n+\tstruct sk_buff *trailer;\n+\tstruct scatterlist *sg;\n+\tvoid *ctx;\n+\t__be64 n;\n+\tint err;\n+\n+\tccm = quic_crypto_is_cipher_ccm(crypto);\n+\tphase = cb-\u003ekey_phase;\n+\thlen = cb-\u003enumber_offset + cb-\u003enumber_len;\n+\tif (enc) {\n+\t\ttfm = crypto-\u003etx_tfm[phase];\n+\t\tbase_iv = crypto-\u003etx_iv[phase];\n+\t\tlen = skb-\u003elen;\n+\t\terr = skb_cow_data(skb, QUIC_TAG_LEN, \u0026trailer);\n+\t\tif (err \u003c 0)\n+\t\t\treturn err;\n+\t\tpskb_put(skb, trailer, QUIC_TAG_LEN);\n+\t\tif (!quic_hdr(skb)-\u003eform)\n+\t\t\tquic_hdr(skb)-\u003ekey = phase;\n+\t\tsglen = skb-\u003elen;\n+\t\tnsg = (u32)err;\n+\t} else {\n+\t\ttfm = crypto-\u003erx_tfm[phase];\n+\t\tbase_iv = crypto-\u003erx_iv[phase];\n+\t\tlen = cb-\u003elength + cb-\u003enumber_offset;\n+\t\tif (len - hlen \u003c QUIC_TAG_LEN)\n+\t\t\treturn -EINVAL;\n+\t\tsglen = len;\n+\t\tnsg = 1;\n+\t}\n+\n+\tctx = quic_crypto_aead_mem_alloc(tfm, sizeof(void *), \u0026iv, \u0026req, \u0026sg,\n+\t\t\t\t\t nsg, gfp);\n+\tif (!ctx)\n+\t\treturn -ENOMEM;\n+\n+\tsg_init_table(sg, nsg);\n+\terr = skb_to_sgvec(skb, sg, 0, sglen);\n+\tif (err \u003c 0)\n+\t\tgoto out;\n+\n+\t/* rfc9001#section-5.3:\n+\t *\n+\t * The associated data, A, for the AEAD is the contents of the QUIC\n+\t * header, starting from the first byte of either the short or long\n+\t * header, up to and including the unprotected packet number.\n+\t *\n+\t * The nonce, N, is formed by combining the packet protection IV with\n+\t * the packet number. The 62 bits of the reconstructed QUIC packet\n+\t * number in network byte order are left-padded with zeros to the size\n+\t * of the IV. The exclusive OR of the padded packet number and the IV\n+\t * forms the AEAD nonce.\n+\t */\n+\tmemcpy(nonce, base_iv, QUIC_IV_LEN);\n+\tn = cpu_to_be64(cb-\u003enumber);\n+\tfor (i = 0; i \u003c sizeof(n); i++)\n+\t\tnonce[QUIC_IV_LEN - sizeof(n) + i] ^= ((u8 *)\u0026n)[i];\n+\n+\t/* For CCM based ciphers, first byte of IV is a constant. */\n+\tiv[0] = TLS_AES_CCM_IV_B0_BYTE;\n+\tmemcpy(\u0026iv[ccm], nonce, QUIC_IV_LEN);\n+\taead_request_set_tfm(req, tfm);\n+\taead_request_set_ad(req, hlen);\n+\taead_request_set_crypt(req, sg, sg, len - hlen, iv);\n+\tif (cb-\u003esync) {\n+\t\tDECLARE_CRYPTO_WAIT(wait);\n+\n+\t\taead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,\n+\t\t\t\t\t crypto_req_done, \u0026wait);\n+\t\terr = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);\n+\t\tif (err == -EINPROGRESS || err == -EBUSY)\n+\t\t\terr = crypto_wait_req(err, \u0026wait);\n+\t\tgoto out;\n+\t}\n+\n+\taead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,\n+\t\t\t\t quic_crypto_done, skb);\n+\t*(struct quic_crypto **)ctx = crypto;\n+\tatomic_inc(\u0026crypto-\u003easync_pending[phase]);\n+\tcb-\u003ecrypto_ctx = ctx; /* Async free context for quic_crypto_done() */\n+\terr = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);\n+\tif (err == -EINPROGRESS || err == -EBUSY) {\n+\t\tmemzero_explicit(nonce, sizeof(nonce));\n+\t\treturn -EINPROGRESS;\n+\t}\n+\tatomic_dec(\u0026crypto-\u003easync_pending[phase]);\n+\tcb-\u003ecrypto_ctx = NULL;\n+\n+out:\n+\tkfree_sensitive(ctx);\n+\tmemzero_explicit(nonce, sizeof(nonce));\n+\treturn err;\n+}\n+\n+/* Encrypts a QUIC packet before transmission. This function performs AEAD\n+ * encryption of the packet payload and applies header protection. It handles\n+ * key phase tracking and key update timing.\n+ *\n+ * Return: 0 on success, or a negative error code.\n+ */\n+int quic_crypto_encrypt(struct quic_crypto *crypto, struct sk_buff *skb,\n+\t\t\tgfp_t gfp)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tint err;\n+\n+\t/* Packet payload is already encrypted (e.g., resumed from async),\n+\t * proceed to header protection only.\n+\t */\n+\tif (cb-\u003eresume)\n+\t\tgoto out;\n+\n+\tcb-\u003ekey_phase = crypto-\u003ekey_phase;\n+\t/* If a key update is pending and this is the first packet using the\n+\t * new key, save the current time. Later used to clear old keys after\n+\t * some time has passed (see quic_crypto_decrypt()).\n+\t */\n+\tif (crypto-\u003ekey_pending \u0026\u0026 !crypto-\u003ekey_update_send_time)\n+\t\tcrypto-\u003ekey_update_send_time = quic_ktime_get_us();\n+\n+\terr = quic_crypto_payload_protect(crypto, skb, true, gfp);\n+\tif (err)\n+\t\treturn err;\n+out:\n+\treturn quic_crypto_header_protect(crypto, skb, true, gfp);\n+}\n+\n+/* Decrypts a QUIC packet after reception. This function removes header\n+ * protection, decrypts the payload, and processes any key updates if the key\n+ * phase bit changes.\n+ *\n+ * Return: 0 on success, or a negative error code.\n+ */\n+int quic_crypto_decrypt(struct quic_crypto *crypto, struct sk_buff *skb,\n+\t\t\tgfp_t gfp)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tint err = 0;\n+\tu64 time;\n+\tu8 phase;\n+\n+\t/* Payload was decrypted asynchronously. Proceed with parsing packet\n+\t * number and key phase.\n+\t */\n+\tif (cb-\u003eresume) {\n+\t\terr = quic_crypto_get_number(skb);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\tgoto out;\n+\t}\n+\tif (!cb-\u003enumber_len) { /* Packet header not yet decrypted. */\n+\t\terr = quic_crypto_header_protect(crypto, skb, false, gfp);\n+\t\tif (err) {\n+\t\t\tpr_debug(\"%s: hd decrypt err %d\\n\", __func__, err);\n+\t\t\treturn err;\n+\t\t}\n+\t}\n+\n+\t/* rfc9001#section-6:\n+\t *\n+\t * The Key Phase bit allows a recipient to detect a change in keying\n+\t * material without needing to receive the first packet that triggered\n+\t * the change. An endpoint that notices a changed Key Phase bit updates\n+\t * keys and decrypts the packet that contains the changed value.\n+\t */\n+\tphase = cb-\u003ekey_phase;\n+\tif (phase != crypto-\u003ekey_phase \u0026\u0026 !crypto-\u003ekey_pending) {\n+\t\tif (!crypto-\u003esend_ready) /* Not ready for key update. */\n+\t\t\treturn -EINVAL;\n+\t\tif (!cb-\u003ebacklog) /* Key update requires process context. */\n+\t\t\treturn -EKEYREVOKED;\n+\t\t/* Cannot do key update while async crypto is in progress. */\n+\t\tif (unlikely(atomic_read(\u0026crypto-\u003easync_pending[phase])))\n+\t\t\treturn -EBUSY;\n+\t\terr = quic_crypto_key_update(crypto); /* Perform key update. */\n+\t\tif (err) {\n+\t\t\tcb-\u003eerrcode = QUIC_TRANSPORT_ERROR_KEY_UPDATE;\n+\t\t\treturn err;\n+\t\t}\n+\t\tcb-\u003esync = 1;\n+\t\tcb-\u003ekey_update = 1; /* Mark packet as triggering key update. */\n+\t}\n+\n+\terr = quic_crypto_payload_protect(crypto, skb, false, gfp);\n+\tif (err) {\n+\t\tif (err == -EINPROGRESS)\n+\t\t\treturn err;\n+\t\t/* When using the old keys can not decrypt the packets, the\n+\t\t * peer might start another key_update. Thus, clear the last\n+\t\t * key_pending so that next packets will trigger the new\n+\t\t * key-update.\n+\t\t */\n+\t\tif (crypto-\u003ekey_pending \u0026\u0026 phase != crypto-\u003ekey_phase) {\n+\t\t\tcrypto-\u003ekey_pending = 0;\n+\t\t\tcrypto-\u003ekey_update_time = 0;\n+\t\t\tcrypto-\u003ekey_update_send_time = 0;\n+\t\t}\n+\t\treturn err;\n+\t}\n+\n+out:\n+\t/* rfc9001#section-6.2:\n+\t *\n+\t * If a packet is successfully processed using the next key and IV,\n+\t * then the peer has initiated a key update.\n+\t */\n+\tif (cb-\u003ekey_update) {\n+\t\tcrypto-\u003ekey_pending = 1;\n+\t\tcrypto-\u003ekey_derived = 0;\n+\t\tcrypto-\u003ekey_phase = !crypto-\u003ekey_phase;\n+\t}\n+\t/* rfc9001#section-6.1:\n+\t *\n+\t * An endpoint MUST retain old keys until it has successfully\n+\t * unprotected a packet sent using the new keys. An endpoint SHOULD\n+\t * retain old keys for some time after unprotecting a packet sent using\n+\t * the new keys.\n+\t */\n+\tif (crypto-\u003ekey_pending \u0026\u0026 cb-\u003ekey_phase == crypto-\u003ekey_phase) {\n+\t\ttime = crypto-\u003ekey_update_send_time;\n+\t\tif (time \u0026\u0026\n+\t\t quic_ktime_get_us() - time \u003e= crypto-\u003ekey_update_time) {\n+\t\t\tcrypto-\u003ekey_pending = 0;\n+\t\t\tcrypto-\u003ekey_update_time = 0;\n+\t\t\tcrypto-\u003ekey_update_send_time = 0;\n+\t\t}\n+\t}\n+\treturn err;\n+}\n+\n+int quic_crypto_set_cipher(struct quic_crypto *crypto, u32 type)\n+{\n+\tconst struct quic_cipher *cipher;\n+\tvoid *tfm;\n+\tint err;\n+\n+\tif (type \u003c QUIC_CIPHER_MIN || type \u003e QUIC_CIPHER_MAX)\n+\t\treturn -EINVAL;\n+\n+\tcipher = \u0026ciphers[type - QUIC_CIPHER_MIN];\n+\ttfm = crypto_alloc_shash(cipher-\u003eshash, 0, 0);\n+\tif (IS_ERR(tfm))\n+\t\treturn PTR_ERR(tfm);\n+\tcrypto-\u003esecret_tfm = tfm;\n+\n+\t/* Allocate AEAD and HP transform for each RX key phase. */\n+\ttfm = crypto_alloc_aead(cipher-\u003eaead, 0, 0);\n+\tif (IS_ERR(tfm)) {\n+\t\terr = PTR_ERR(tfm);\n+\t\tgoto err;\n+\t}\n+\tcrypto-\u003erx_tfm[0] = tfm;\n+\ttfm = crypto_alloc_aead(cipher-\u003eaead, 0, 0);\n+\tif (IS_ERR(tfm)) {\n+\t\terr = PTR_ERR(tfm);\n+\t\tgoto err;\n+\t}\n+\tcrypto-\u003erx_tfm[1] = tfm;\n+\ttfm = crypto_alloc_sync_skcipher(cipher-\u003eskc, 0, 0);\n+\tif (IS_ERR(tfm)) {\n+\t\terr = PTR_ERR(tfm);\n+\t\tgoto err;\n+\t}\n+\tcrypto-\u003erx_hp_tfm = tfm;\n+\n+\t/* Allocate AEAD and HP transform for each TX key phase. */\n+\ttfm = crypto_alloc_aead(cipher-\u003eaead, 0, 0);\n+\tif (IS_ERR(tfm)) {\n+\t\terr = PTR_ERR(tfm);\n+\t\tgoto err;\n+\t}\n+\tcrypto-\u003etx_tfm[0] = tfm;\n+\ttfm = crypto_alloc_aead(cipher-\u003eaead, 0, 0);\n+\tif (IS_ERR(tfm)) {\n+\t\terr = PTR_ERR(tfm);\n+\t\tgoto err;\n+\t}\n+\tcrypto-\u003etx_tfm[1] = tfm;\n+\ttfm = crypto_alloc_sync_skcipher(cipher-\u003eskc, 0, 0);\n+\tif (IS_ERR(tfm)) {\n+\t\terr = PTR_ERR(tfm);\n+\t\tgoto err;\n+\t}\n+\tcrypto-\u003etx_hp_tfm = tfm;\n+\n+\tcrypto-\u003ecipher = cipher;\n+\tcrypto-\u003ecipher_type = type;\n+\treturn 0;\n+err:\n+\tquic_crypto_free(crypto);\n+\treturn err;\n+}\n+\n+int quic_crypto_set_secret(struct quic_crypto *crypto,\n+\t\t\t struct quic_crypto_secret *srt, u32 version)\n+{\n+\tconst struct quic_cipher *cipher;\n+\tu8 phase = crypto-\u003ekey_phase;\n+\tint err;\n+\n+\t/* If no cipher has been initialized yet, set it up. */\n+\tif (!crypto-\u003ecipher) {\n+\t\terr = quic_crypto_set_cipher(crypto, srt-\u003etype);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\tcipher = crypto-\u003ecipher;\n+\n+\t/* Handle RX path setup. */\n+\tif (!srt-\u003esend) {\n+\t\tcrypto-\u003eversion = version;\n+\t\tmemcpy(crypto-\u003erx_secret[phase], srt-\u003esecret,\n+\t\t cipher-\u003esecretlen);\n+\t\terr = quic_crypto_keys_derive_and_install(crypto, true, phase);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t\tcrypto-\u003erecv_ready = 1;\n+\t\treturn 0;\n+\t}\n+\n+\t/* Handle TX path setup. */\n+\tcrypto-\u003eversion = version;\n+\tmemcpy(crypto-\u003etx_secret[phase], srt-\u003esecret, cipher-\u003esecretlen);\n+\terr = quic_crypto_keys_derive_and_install(crypto, false, phase);\n+\tif (err)\n+\t\treturn err;\n+\tcrypto-\u003esend_ready = 1;\n+\treturn 0;\n+}\n+\n+/* Save token secret in Initial TX secret (phase 1) for token generation. */\n+int quic_crypto_set_token_secret(struct quic_crypto *crypto)\n+{\n+\t/* Reuse TX AEAD (phase 1) in Initial crypto. */\n+\tu8 key[TLS_CIPHER_AES_GCM_128_KEY_SIZE], *srt = crypto-\u003etx_secret[1];\n+\tstruct crypto_aead *tfm = crypto-\u003etx_tfm[1];\n+\tstruct quic_data s = {}, k, i;\n+\tint err;\n+\n+\tif (!memchr_inv(srt, 0, TLS_CIPHER_AES_GCM_128_SECRET_SIZE))\n+\t\tget_random_bytes(srt, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);\n+\n+\tquic_data(\u0026s, srt, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);\n+\tquic_data(\u0026k, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);\n+\tquic_data(\u0026i, crypto-\u003etx_iv[1], QUIC_IV_LEN);\n+\terr = quic_crypto_keys_derive(crypto-\u003esecret_tfm, \u0026s, \u0026k, \u0026i, NULL,\n+\t\t\t\t QUIC_VERSION_V1);\n+\tif (err)\n+\t\tgoto out;\n+\terr = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);\n+\tif (err)\n+\t\tgoto out;\n+\terr = crypto_aead_setkey(tfm, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);\n+out:\n+\tmemzero_explicit(key, sizeof(key));\n+\treturn err;\n+}\n+\n+/* Initiating a Key Update. */\n+int quic_crypto_key_update(struct quic_crypto *crypto)\n+{\n+\tstruct quic_data l = {KU_LABEL_V1, strlen(KU_LABEL_V1)};\n+\tu8 phase = crypto-\u003ekey_phase;\n+\tstruct quic_data k, srt;\n+\tu32 secret_len;\n+\tint err;\n+\n+\tif (crypto-\u003ekey_pending || !crypto-\u003erecv_ready)\n+\t\treturn -EINVAL;\n+\tif (crypto-\u003ekey_derived)\n+\t\treturn 0;\n+\n+\t/* rfc9001#section-6.1:\n+\t *\n+\t * Endpoints maintain separate read and write secrets for packet\n+\t * protection. An endpoint initiates a key update by updating its\n+\t * packet protection write secret and using that to protect new\n+\t * packets. The endpoint creates a new write secret from the existing\n+\t * write secret. This uses the KDF function provided by TLS with a\n+\t * label of \"quic ku\". The corresponding key and IV are created from\n+\t * that secret. The header protection key is not updated.\n+\t *\n+\t * For example, to update write keys with TLS 1.3, HKDF-Expand-Label is\n+\t * used as:\n+\t * secret_\u003cn+1\u003e = HKDF-Expand-Label(secret_\u003cn\u003e, \"quic ku\",\n+\t * \"\", Hash.length)\n+\t */\n+\tsecret_len = crypto-\u003ecipher-\u003esecretlen;\n+\tif (crypto-\u003eversion == QUIC_VERSION_V2)\n+\t\tquic_data(\u0026l, KU_LABEL_V2, strlen(KU_LABEL_V2));\n+\n+\tquic_data(\u0026srt, crypto-\u003etx_secret[phase], secret_len);\n+\tquic_data(\u0026k, crypto-\u003etx_secret[!phase], secret_len);\n+\terr = quic_crypto_hkdf_expand(crypto-\u003esecret_tfm, \u0026srt, \u0026l, \u0026k);\n+\tif (err)\n+\t\treturn err;\n+\terr = quic_crypto_keys_derive_and_install(crypto, false, !phase);\n+\tif (err)\n+\t\treturn err;\n+\n+\tquic_data(\u0026srt, crypto-\u003erx_secret[phase], secret_len);\n+\tquic_data(\u0026k, crypto-\u003erx_secret[!phase], secret_len);\n+\terr = quic_crypto_hkdf_expand(crypto-\u003esecret_tfm, \u0026srt, \u0026l, \u0026k);\n+\tif (err)\n+\t\treturn err;\n+\terr = quic_crypto_keys_derive_and_install(crypto, true, !phase);\n+\tif (err)\n+\t\treturn err;\n+\n+\tcrypto-\u003ekey_derived = 1;\n+\treturn 0;\n+}\n+\n+void quic_crypto_free(struct quic_crypto *crypto)\n+{\n+\tif (crypto-\u003erx_tfm[0])\n+\t\tcrypto_free_aead(crypto-\u003erx_tfm[0]);\n+\tif (crypto-\u003erx_tfm[1])\n+\t\tcrypto_free_aead(crypto-\u003erx_tfm[1]);\n+\tif (crypto-\u003etx_tfm[0])\n+\t\tcrypto_free_aead(crypto-\u003etx_tfm[0]);\n+\tif (crypto-\u003etx_tfm[1])\n+\t\tcrypto_free_aead(crypto-\u003etx_tfm[1]);\n+\tif (crypto-\u003esecret_tfm)\n+\t\tcrypto_free_shash(crypto-\u003esecret_tfm);\n+\tif (crypto-\u003erx_hp_tfm)\n+\t\tcrypto_free_skcipher(crypto-\u003erx_hp_tfm);\n+\tif (crypto-\u003etx_hp_tfm)\n+\t\tcrypto_free_skcipher(crypto-\u003etx_hp_tfm);\n+\n+\tmemzero_explicit(crypto, offsetof(struct quic_crypto, send_offset));\n+}\n+\n+#define QUIC_INITIAL_SALT_V1 \\\n+\t\"\\x38\\x76\\x2c\\xf7\\xf5\\x59\\x34\\xb3\\x4d\\x17\" \\\n+\t\"\\x9a\\xe6\\xa4\\xc8\\x0c\\xad\\xcc\\xbb\\x7f\\x0a\"\n+\n+#define QUIC_INITIAL_SALT_V2 \\\n+\t\"\\x0d\\xed\\xe3\\xde\\xf7\\x00\\xa6\\xdb\\x81\\x93\" \\\n+\t\"\\x81\\xbe\\x6e\\x26\\x9d\\xcb\\xf9\\xbd\\x2e\\xd9\"\n+\n+#define QUIC_INITIAL_SALT_LEN\t20\n+\n+/* Initial Secrets. */\n+int quic_crypto_initial_keys_install(struct quic_crypto *crypto,\n+\t\t\t\t struct quic_conn_id *conn_id,\n+\t\t\t\t u32 version, bool is_serv)\n+{\n+\tu8 secret[TLS_CIPHER_AES_GCM_128_SECRET_SIZE];\n+\tstruct quic_data salt, s, k, l, dcid;\n+\tstruct quic_crypto_secret srt = {};\n+\tchar *tl, *rl, *sal;\n+\tint err;\n+\n+\t/* rfc9001#section-5.2:\n+\t *\n+\t * The secret used by clients to construct Initial packets uses the PRK\n+\t * and the label \"client in\" as input to the HKDF-Expand-Label function\n+\t * from TLS [TLS13] to produce a 32-byte secret. Packets constructed by\n+\t * the server use the same process with the label \"server in\". The hash\n+\t * function for HKDF when deriving initial secrets and keys is SHA-256\n+\t * [SHA].\n+\t *\n+\t * This process in pseudocode is:\n+\t *\n+\t * initial_salt = 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a\n+\t * initial_secret = HKDF-Extract(initial_salt,\n+\t * client_dst_connection_id)\n+\t *\n+\t * client_initial_secret = HKDF-Expand-Label(initial_secret,\n+\t * \"client in\", \"\",\n+\t * Hash.length)\n+\t * server_initial_secret = HKDF-Expand-Label(initial_secret,\n+\t * \"server in\", \"\",\n+\t * Hash.length)\n+\t */\n+\tif (is_serv) {\n+\t\trl = \"client in\";\n+\t\ttl = \"server in\";\n+\t} else {\n+\t\ttl = \"client in\";\n+\t\trl = \"server in\";\n+\t}\n+\tsal = QUIC_INITIAL_SALT_V1;\n+\tif (version == QUIC_VERSION_V2)\n+\t\tsal = QUIC_INITIAL_SALT_V2;\n+\tquic_data(\u0026salt, sal, QUIC_INITIAL_SALT_LEN);\n+\tquic_data(\u0026dcid, conn_id-\u003edata, conn_id-\u003elen);\n+\tquic_data(\u0026s, secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);\n+\terr = quic_crypto_hkdf_extract(crypto-\u003esecret_tfm, \u0026salt, \u0026dcid, \u0026s);\n+\tif (err)\n+\t\tgoto out;\n+\n+\tquic_data(\u0026l, tl, strlen(tl));\n+\tquic_data(\u0026k, srt.secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);\n+\tsrt.type = TLS_CIPHER_AES_GCM_128;\n+\tsrt.send = 1;\n+\terr = quic_crypto_hkdf_expand(crypto-\u003esecret_tfm, \u0026s, \u0026l, \u0026k);\n+\tif (err)\n+\t\tgoto out;\n+\terr = quic_crypto_set_secret(crypto, \u0026srt, version);\n+\tif (err)\n+\t\tgoto out;\n+\n+\tquic_data(\u0026l, rl, strlen(rl));\n+\tquic_data(\u0026k, srt.secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);\n+\tsrt.type = TLS_CIPHER_AES_GCM_128;\n+\tsrt.send = 0;\n+\terr = quic_crypto_hkdf_expand(crypto-\u003esecret_tfm, \u0026s, \u0026l, \u0026k);\n+\tif (err)\n+\t\tgoto out;\n+\terr = quic_crypto_set_secret(crypto, \u0026srt, version);\n+out:\n+\tmemzero_explicit(secret, sizeof(secret));\n+\tmemzero_explicit(\u0026srt, sizeof(srt));\n+\treturn err;\n+}\n+\n+#define QUIC_RETRY_KEY_V1 \\\n+\t\"\\xbe\\x0c\\x69\\x0b\\x9f\\x66\\x57\\x5a\\x1d\\x76\\x6b\\x54\\xe3\\x68\\xc8\\x4e\"\n+#define QUIC_RETRY_KEY_V2 \\\n+\t\"\\x8f\\xb4\\xb0\\x1b\\x56\\xac\\x48\\xe2\\x60\\xfb\\xcb\\xce\\xad\\x7c\\xcc\\x92\"\n+\n+#define QUIC_RETRY_NONCE_V1 \"\\x46\\x15\\x99\\xd3\\x5d\\x63\\x2b\\xf2\\x23\\x98\\x25\\xbb\"\n+#define QUIC_RETRY_NONCE_V2 \"\\xd8\\x69\\x69\\xbc\\x2d\\x7c\\x6d\\x99\\x90\\xef\\xb0\\x4a\"\n+\n+/* Retry Packet Integrity. */\n+int quic_crypto_get_retry_tag(struct quic_crypto *crypto, struct sk_buff *skb,\n+\t\t\t struct quic_conn_id *odcid, u32 version, u8 *tag)\n+{\n+\t/* Reuse RX AEAD (phase 1) in Initial crypto. */\n+\tstruct crypto_aead *tfm = crypto-\u003erx_tfm[1];\n+\tu8 *pseudo_retry, *p, *iv, *key;\n+\tDECLARE_CRYPTO_WAIT(wait);\n+\tstruct aead_request *req;\n+\tstruct scatterlist *sg;\n+\tu32 plen;\n+\tint err;\n+\n+\t/* The caller must ensure skb-\u003elen \u003e QUIC_TAG_LEN. */\n+\tif (skb-\u003elen \u003c= QUIC_TAG_LEN)\n+\t\treturn -EINVAL;\n+\n+\t/* rfc9001#section-5.8:\n+\t *\n+\t * The Retry Integrity Tag is a 128-bit field that is computed as the\n+\t * output of AEAD_AES_128_GCM used with the following inputs:\n+\t *\n+\t * - The secret key, K, is 128 bits equal to\n+\t * 0xbe0c690b9f66575a1d766b54e368c84e.\n+\t * - The nonce, N, is 96 bits equal to 0x461599d35d632bf2239825bb.\n+\t * - The plaintext, P, is empty.\n+\t * - The associated data, A, is the contents of the Retry\n+\t * Pseudo-Packet,\n+\t *\n+\t * The Retry Pseudo-Packet is not sent over the wire. It is computed by\n+\t * taking the transmitted Retry packet, removing the Retry Integrity\n+\t * Tag, and prepending the two following fields: ODCID Length +\n+\t * Original Destination Connection ID (ODCID).\n+\t */\n+\terr = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);\n+\tif (err)\n+\t\treturn err;\n+\tkey = QUIC_RETRY_KEY_V1;\n+\tif (version == QUIC_VERSION_V2)\n+\t\tkey = QUIC_RETRY_KEY_V2;\n+\terr = crypto_aead_setkey(tfm, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);\n+\tif (err)\n+\t\treturn err;\n+\n+\tplen = 1 + odcid-\u003elen + skb-\u003elen - QUIC_TAG_LEN;\n+\tpseudo_retry = quic_crypto_aead_mem_alloc(tfm, plen + QUIC_TAG_LEN, \u0026iv,\n+\t\t\t\t\t\t \u0026req, \u0026sg, 1, GFP_KERNEL);\n+\tif (!pseudo_retry)\n+\t\treturn -ENOMEM;\n+\n+\tp = pseudo_retry;\n+\tp = quic_put_int(p, odcid-\u003elen, 1);\n+\tp = quic_put_data(p, odcid-\u003edata, odcid-\u003elen);\n+\tp = quic_put_data(p, skb-\u003edata, skb-\u003elen - QUIC_TAG_LEN);\n+\tsg_init_one(sg, pseudo_retry, plen + QUIC_TAG_LEN);\n+\n+\tmemcpy(iv, QUIC_RETRY_NONCE_V1, QUIC_IV_LEN);\n+\tif (version == QUIC_VERSION_V2)\n+\t\tmemcpy(iv, QUIC_RETRY_NONCE_V2, QUIC_IV_LEN);\n+\taead_request_set_tfm(req, tfm);\n+\taead_request_set_ad(req, plen);\n+\taead_request_set_crypt(req, sg, sg, 0, iv);\n+\taead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,\n+\t\t\t\t crypto_req_done, \u0026wait);\n+\terr = crypto_aead_encrypt(req);\n+\tif (err == -EINPROGRESS || err == -EBUSY)\n+\t\terr = crypto_wait_req(err, \u0026wait);\n+\tif (!err)\n+\t\tmemcpy(tag, p, QUIC_TAG_LEN);\n+\n+\tkfree_sensitive(pseudo_retry);\n+\treturn err;\n+}\n+\n+/* Derives a key and IV using HKDF, configures the AEAD transform and performs\n+ * AEAD encryption/decryption for the provided token.\n+ */\n+static int quic_crypto_token_protect(struct quic_crypto *crypto, u8 *token,\n+\t\t\t\t u32 len, u32 adlen, bool enc)\n+{\n+\t/* Reuse TX AEAD (phase 1) in Initial crypto. */\n+\tstruct crypto_aead *tfm = crypto-\u003etx_tfm[1];\n+\tu32 extra = enc ? QUIC_TAG_LEN : 0, tslen;\n+\tDECLARE_CRYPTO_WAIT(wait);\n+\tstruct aead_request *req;\n+\tstruct scatterlist *sg;\n+\tvoid *ctx = NULL;\n+\tu8 *nonce, *p, i;\n+\t__be64 n;\n+\tint err;\n+\tu64 ts;\n+\n+\tctx = quic_crypto_aead_mem_alloc(tfm, 0, \u0026nonce, \u0026req, \u0026sg, 1,\n+\t\t\t\t\t GFP_KERNEL);\n+\tif (!ctx) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\tmemcpy(nonce, crypto-\u003etx_iv[1], QUIC_IV_LEN);\n+\n+\ttslen = sizeof(ts);\n+\tp = token + adlen - tslen;\n+\tquic_get_int(\u0026p, \u0026tslen, \u0026ts, tslen);\n+\n+\tn = cpu_to_be64(ts);\n+\tfor (i = 0; i \u003c sizeof(n); i++)\n+\t\tnonce[QUIC_IV_LEN - sizeof(n) + i] ^= ((u8 *)\u0026n)[i];\n+\n+\tsg_init_one(sg, token, len);\n+\taead_request_set_tfm(req, tfm);\n+\taead_request_set_ad(req, adlen);\n+\taead_request_set_crypt(req, sg, sg, len - adlen - extra, nonce);\n+\taead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,\n+\t\t\t\t crypto_req_done, \u0026wait);\n+\terr = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);\n+\tif (err == -EINPROGRESS || err == -EBUSY)\n+\t\terr = crypto_wait_req(err, \u0026wait);\n+\n+out:\n+\tkfree_sensitive(ctx);\n+\treturn err;\n+}\n+\n+/* Generate a token for Retry or address validation.\n+ *\n+ * Builds a token with the format: [flag][client address][timestamp][original\n+ * DCID][auth tag]\n+ *\n+ * Encrypts the token (excluding the first flag byte) using AES-GCM with a key\n+ * and IV derived via HKDF. The original DCID is stored to be recovered later\n+ * from a Client Initial packet. Ensures the token is bound to the client\n+ * address and time, preventing reuse or tampering.\n+ *\n+ * Returns 0 on success or a negative error code on failure.\n+ */\n+int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,\n+\t\t\t u32 addrlen, struct quic_conn_id *conn_id,\n+\t\t\t u8 *token, u32 *tlen)\n+{\n+\tu8 *token_buf, *p, flag = *token;\n+\tu64 ts = quic_ktime_get_us();\n+\tu32 len, tslen = sizeof(ts);\n+\tint err;\n+\n+\tlen = sizeof(flag) + addrlen + tslen + conn_id-\u003elen + QUIC_TAG_LEN;\n+\ttoken_buf = kmalloc(len, GFP_KERNEL);\n+\tif (!token_buf)\n+\t\treturn -ENOMEM;\n+\n+\tp = token_buf;\n+\tp = quic_put_int(p, flag, sizeof(flag));\n+\tp = quic_put_data(p, addr, addrlen);\n+\tp = quic_put_int(p, ts, tslen);\n+\tquic_put_data(p, conn_id-\u003edata, conn_id-\u003elen);\n+\n+\terr = quic_crypto_token_protect(crypto, token_buf, len,\n+\t\t\t\t\tsizeof(flag) + addrlen + tslen, true);\n+\tif (err)\n+\t\tgoto out;\n+\n+\tmemcpy(token, token_buf, len);\n+\t*tlen = len;\n+out:\n+\tkfree_sensitive(token_buf);\n+\treturn err;\n+}\n+\n+/* Validate a Retry or address validation token.\n+ *\n+ * Decrypts the token using derived key and IV. Checks that the decrypted\n+ * address matches the provided address, validates the embedded timestamp\n+ * against current time with a version-specific timeout. If applicable, it\n+ * extracts and returns the original destination connection ID (ODCID) for\n+ * Retry packets.\n+ *\n+ * Returns 0 if the token is valid, -EINVAL if invalid, or another negative\n+ * error code.\n+ */\n+int quic_crypto_verify_token(struct quic_crypto *crypto, void *addr,\n+\t\t\t u32 addrlen, struct quic_conn_id *conn_id,\n+\t\t\t u8 *token, u32 len)\n+{\n+\tu64 t, ts = quic_ktime_get_us(), timeout = QUIC_TOKEN_TIMEOUT_RETRY;\n+\tu8 *token_buf, *p, flag;\n+\tu32 tslen = sizeof(ts);\n+\tint err;\n+\n+\tif (len \u003c sizeof(flag) + addrlen + tslen + QUIC_TAG_LEN)\n+\t\treturn -EINVAL;\n+\ttoken_buf = kmemdup(token, len, GFP_KERNEL);\n+\tif (!token_buf)\n+\t\treturn -ENOMEM;\n+\n+\terr = quic_crypto_token_protect(crypto, token_buf, len,\n+\t\t\t\t\tsizeof(flag) + addrlen + tslen, false);\n+\tif (err)\n+\t\tgoto out;\n+\n+\terr = -EINVAL;\n+\tp = token_buf;\n+\tflag = *p++;\n+\tlen -= sizeof(flag);\n+\tif (crypto_memneq(p, addr, addrlen))\n+\t\tgoto out;\n+\n+\tp += addrlen;\n+\tlen -= addrlen;\n+\tif (flag == QUIC_TOKEN_FLAG_REGULAR)\n+\t\ttimeout = QUIC_TOKEN_TIMEOUT_REGULAR;\n+\tif (!quic_get_int(\u0026p, \u0026len, \u0026t, tslen) || t \u003e ts || ts - t \u003e timeout)\n+\t\tgoto out;\n+\n+\tlen -= QUIC_TAG_LEN;\n+\tif (len \u003e QUIC_CONN_ID_MAX_LEN)\n+\t\tgoto out;\n+\n+\tif (flag == QUIC_TOKEN_FLAG_RETRY)\n+\t\tquic_conn_id_update(conn_id, p, len);\n+\terr = 0;\n+out:\n+\tkfree_sensitive(token_buf);\n+\treturn err;\n+}\n+\n+/* Derive a secret using HKDF-Extract and HKDF-Expand with the given label.\n+ * Used to generate a stateless reset token or session resumption master key.\n+ */\n+int quic_crypto_derive_secret(struct quic_crypto *crypto, void *data, u32 len,\n+\t\t\t char *label, u8 *srt, u32 srt_len)\n+{\n+\tstruct crypto_shash *tfm = crypto-\u003esecret_tfm;\n+\tu8 secret[TLS_CIPHER_AES_GCM_128_SECRET_SIZE];\n+\tstruct quic_data salt, s, l, k;\n+\tint err;\n+\n+\tquic_data(\u0026salt, data, len);\n+\tquic_data(\u0026k, crypto-\u003etx_secret[1], TLS_CIPHER_AES_GCM_128_SECRET_SIZE);\n+\tquic_data(\u0026s, secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);\n+\terr = quic_crypto_hkdf_extract(tfm, \u0026salt, \u0026k, \u0026s);\n+\tif (err)\n+\t\tgoto out;\n+\n+\tquic_data(\u0026l, label, strlen(label));\n+\tquic_data(\u0026k, srt, srt_len);\n+\terr = quic_crypto_hkdf_expand(tfm, \u0026s, \u0026l, \u0026k);\n+out:\n+\tmemzero_explicit(secret, sizeof(secret));\n+\treturn err;\n+}\ndiff --git a/net/quic/crypto.h b/net/quic/crypto.h\nnew file mode 100644\nindex 0000000000000..77281a824f721\n--- /dev/null\n+++ b/net/quic/crypto.h\n@@ -0,0 +1,88 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#define QUIC_TAG_LEN\t16\n+#define QUIC_IV_LEN\t12\n+#define QUIC_KEY_LEN\t32\n+#define QUIC_SECRET_LEN\t48\n+\n+#define QUIC_TOKEN_FLAG_REGULAR\t\t0\n+#define QUIC_TOKEN_FLAG_RETRY\t\t1\n+#define QUIC_TOKEN_TIMEOUT_RETRY\t3000000\n+#define QUIC_TOKEN_TIMEOUT_REGULAR\t600000000\n+\n+struct quic_cipher {\n+\tu32 secretlen; /* Length of the traffic secret */\n+\tu32 keylen; /* Length of the AEAD key */\n+\n+\tchar *shash; /* Name of hash algorithm used for key derivation */\n+\tchar *aead; /* Name of AEAD algorithm used for payload en/decryption */\n+\tchar *skc; /* Name of cipher algorithm used for header protection */\n+};\n+\n+struct quic_crypto {\n+\tstruct crypto_skcipher *tx_hp_tfm; /* TX header protection tfm */\n+\tstruct crypto_skcipher *rx_hp_tfm; /* RX header protection tfm */\n+\tstruct crypto_shash *secret_tfm; /* Key derivation (HKDF) tfm */\n+\tstruct crypto_aead *tx_tfm[2]; /* AEAD tfm for TX (key phase 0 and 1) */\n+\tstruct crypto_aead *rx_tfm[2]; /* AEAD tfm for RX (key phase 0 and 1) */\n+\n+\tconst struct quic_cipher *cipher; /* Cipher info (selected cipher) */\n+\tu32 cipher_type; /* Cipher suite (e.g., AES_GCM_128, etc.) */\n+\n+\tu8 tx_secret[2][QUIC_SECRET_LEN]; /* TX secret (key phase 0 and 1) */\n+\tu8 rx_secret[2][QUIC_SECRET_LEN]; /* RX secret (key phase 0 and 1) */\n+\tu8 tx_iv[2][QUIC_IV_LEN]; /* IVs for TX (key phase 0 and 1) */\n+\tu8 rx_iv[2][QUIC_IV_LEN]; /* IVs for RX (key phase 0 and 1) */\n+\tatomic_t async_pending[2]; /* Async pending count (key phase 0 and 1) */\n+\n+\t/* Timestamp 1st packet sent after key update */\n+\tu64 key_update_send_time;\n+\tu64 key_update_time; /* Timestamp old keys retained after key update */\n+\tu32 version; /* QUIC version in use */\n+\n+\tu8 ticket_ready:1; /* True if a session ticket is ready to read */\n+\tu8 key_pending:1; /* A key update is in progress */\n+\tu8 key_derived:1; /* Key derived for the key update */\n+\tu8 send_ready:1; /* TX encryption context is initialized */\n+\tu8 recv_ready:1; /* RX decryption context is initialized */\n+\tu8 key_phase:1; /* Current key phase being used (0 or 1) */\n+\n+\tu64 send_offset; /* Number of handshake bytes sent by user */\n+\tu64 recv_offset; /* Number of handshake bytes read by user */\n+};\n+\n+int quic_crypto_set_secret(struct quic_crypto *crypto,\n+\t\t\t struct quic_crypto_secret *srt, u32 version);\n+int quic_crypto_set_cipher(struct quic_crypto *crypto, u32 type);\n+int quic_crypto_key_update(struct quic_crypto *crypto);\n+\n+int quic_crypto_encrypt(struct quic_crypto *crypto, struct sk_buff *skb,\n+\t\t\tgfp_t gfp);\n+int quic_crypto_decrypt(struct quic_crypto *crypto, struct sk_buff *skb,\n+\t\t\tgfp_t gfp);\n+\n+int quic_crypto_derive_secret(struct quic_crypto *crypto, void *data, u32 len,\n+\t\t\t char *label, u8 *srt, u32 srt_len);\n+int quic_crypto_initial_keys_install(struct quic_crypto *crypto,\n+\t\t\t\t struct quic_conn_id *conn_id,\n+\t\t\t\t u32 version, bool is_serv);\n+int quic_crypto_set_token_secret(struct quic_crypto *crypto);\n+\n+int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,\n+\t\t\t u32 addrlen, struct quic_conn_id *conn_id,\n+\t\t\t u8 *token, u32 *tlen);\n+int quic_crypto_get_retry_tag(struct quic_crypto *crypto, struct sk_buff *skb,\n+\t\t\t struct quic_conn_id *odcid, u32 version, u8 *tag);\n+int quic_crypto_verify_token(struct quic_crypto *crypto, void *addr,\n+\t\t\t u32 addrlen, struct quic_conn_id *conn_id,\n+\t\t\t u8 *token, u32 len);\n+\n+void quic_crypto_free(struct quic_crypto *crypto);\ndiff --git a/net/quic/family.c b/net/quic/family.c\nnew file mode 100644\nindex 0000000000000..f61a8203b2e17\n--- /dev/null\n+++ b/net/quic/family.c\n@@ -0,0 +1,446 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003cnet/inet_common.h\u003e\n+#include \u003cnet/udp_tunnel.h\u003e\n+#include \u003clinux/icmp.h\u003e\n+\n+#include \"common.h\"\n+#include \"family.h\"\n+\n+static bool quic_v4_is_any_addr(union quic_addr *addr)\n+{\n+\treturn addr-\u003ev4.sin_addr.s_addr == htonl(INADDR_ANY);\n+}\n+\n+static bool quic_v6_is_any_addr(union quic_addr *addr)\n+{\n+\treturn ipv6_addr_any(\u0026addr-\u003ev6.sin6_addr);\n+}\n+\n+static void quic_v4_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,\n+\t\t\t\t union quic_addr *a)\n+{\n+\tconf-\u003efamily = AF_INET;\n+\tconf-\u003elocal_ip.s_addr = a-\u003ev4.sin_addr.s_addr;\n+\tconf-\u003elocal_udp_port = a-\u003ev4.sin_port;\n+\tconf-\u003ebind_ifindex = sk-\u003esk_bound_dev_if;\n+}\n+\n+static void quic_v6_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,\n+\t\t\t\t union quic_addr *a)\n+{\n+\tconf-\u003efamily = AF_INET6;\n+\tconf-\u003elocal_ip6 = a-\u003ev6.sin6_addr;\n+\tconf-\u003elocal_udp_port = a-\u003ev6.sin6_port;\n+\tconf-\u003euse_udp6_rx_checksums = true;\n+\tconf-\u003euse_udp6_tx_checksums = true;\n+\tconf-\u003eipv6_v6only = ipv6_only_sock(sk);\n+\tconf-\u003ebind_ifindex = quic_get_dev_if(sk, a);\n+}\n+\n+static int quic_v4_flow_route(struct sock *sk, union quic_addr *da,\n+\t\t\t union quic_addr *sa, struct flowi *fl)\n+{\n+\tstruct flowi4 *fl4;\n+\tstruct rtable *rt;\n+\n+\tif (__sk_dst_check(sk, 0))\n+\t\treturn 1;\n+\n+\tmemset(fl, 0x00, sizeof(*fl));\n+\tfl4 = \u0026fl-\u003eu.ip4;\n+\tfl4-\u003esaddr = sa-\u003ev4.sin_addr.s_addr;\n+\tfl4-\u003efl4_sport = sa-\u003ev4.sin_port;\n+\tfl4-\u003edaddr = da-\u003ev4.sin_addr.s_addr;\n+\tfl4-\u003efl4_dport = da-\u003ev4.sin_port;\n+\tfl4-\u003eflowi4_proto = IPPROTO_UDP;\n+\tfl4-\u003eflowi4_oif = quic_get_dev_if(sk, da);\n+\n+\tfl4-\u003eflowi4_scope = ip_sock_rt_scope(sk);\n+\tfl4-\u003eflowi4_dscp = inet_sk_dscp(inet_sk(sk));\n+\n+\tfl4-\u003eflowi4_uid = sk_uid(sk);\n+\tfl4-\u003eflowi4_mark = sk-\u003esk_mark;\n+\n+\trt = ip_route_output_flow(sock_net(sk), fl4, sk);\n+\tif (IS_ERR(rt))\n+\t\treturn PTR_ERR(rt);\n+\n+\tif (quic_v4_is_any_addr(sa)) {\n+\t\tsa-\u003ev4.sin_family = AF_INET;\n+\t\tsa-\u003ev4.sin_addr.s_addr = fl4-\u003esaddr;\n+\t}\n+\tsk_setup_caps(sk, \u0026rt-\u003edst);\n+\treturn 0;\n+}\n+\n+static int quic_v6_flow_route(struct sock *sk, union quic_addr *da,\n+\t\t\t union quic_addr *sa, struct flowi *fl)\n+{\n+\tstruct ipv6_pinfo *np = inet6_sk(sk);\n+\tstruct in6_addr *final_p, final;\n+\tstruct ip6_flowlabel *flowlabel;\n+\tstruct dst_entry *dst;\n+\tstruct flowi6 *fl6;\n+\n+\tif (__sk_dst_check(sk, np-\u003edst_cookie))\n+\t\treturn 1;\n+\n+\tmemset(fl, 0x00, sizeof(*fl));\n+\tfl6 = \u0026fl-\u003eu.ip6;\n+\tfl6-\u003esaddr = sa-\u003ev6.sin6_addr;\n+\tfl6-\u003efl6_sport = sa-\u003ev6.sin6_port;\n+\tfl6-\u003edaddr = da-\u003ev6.sin6_addr;\n+\tfl6-\u003efl6_dport = da-\u003ev6.sin6_port;\n+\tfl6-\u003eflowi6_proto = IPPROTO_UDP;\n+\tfl6-\u003eflowi6_oif = quic_get_dev_if(sk, da);\n+\n+\tif (inet6_test_bit(SNDFLOW, sk)) {\n+\t\tfl6-\u003eflowlabel = (da-\u003ev6.sin6_flowinfo \u0026 IPV6_FLOWINFO_MASK);\n+\t\tif (fl6-\u003eflowlabel \u0026 IPV6_FLOWLABEL_MASK) {\n+\t\t\tflowlabel = fl6_sock_lookup(sk, fl6-\u003eflowlabel);\n+\t\t\tif (IS_ERR(flowlabel))\n+\t\t\t\treturn -EINVAL;\n+\t\t\tfl6_sock_release(flowlabel);\n+\t\t}\n+\t}\n+\tfl6-\u003eflowlabel = ip6_make_flowinfo(np-\u003etclass, fl6-\u003eflowlabel);\n+\n+\tfl6-\u003eflowi6_uid = sk_uid(sk);\n+\tfl6-\u003eflowi6_mark = sk-\u003esk_mark;\n+\n+\trcu_read_lock();\n+\tfinal_p = fl6_update_dst(fl6, rcu_dereference(np-\u003eopt), \u0026final);\n+\trcu_read_unlock();\n+\n+\tdst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p);\n+\tif (IS_ERR(dst))\n+\t\treturn PTR_ERR(dst);\n+\n+\tif (quic_v6_is_any_addr(sa)) {\n+\t\tsa-\u003ev6.sin6_family = AF_INET6;\n+\t\tsa-\u003ev6.sin6_addr = fl6-\u003esaddr;\n+\t\tif ((ipv6_addr_type(\u0026fl6-\u003esaddr) \u0026 IPV6_ADDR_LINKLOCAL))\n+\t\t\tsa-\u003ev6.sin6_scope_id = fl6-\u003eflowi6_oif;\n+\t}\n+\tip6_dst_store(sk, dst, false, false);\n+\treturn 0;\n+}\n+\n+static void quic_v4_lower_xmit(struct sock *sk, struct sk_buff *skb,\n+\t\t\t struct flowi *fl)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tstruct flowi4 *fl4 = \u0026fl-\u003eu.ip4;\n+\tu8 tos = inet_sk(sk)-\u003etos, ttl;\n+\tstruct dst_entry *dst;\n+\t__be16 df = 0;\n+\n+\tpr_debug(\"%s: skb: %p, len: %d, num: %lld, %pI4:%d -\u003e %pI4:%d\\n\",\n+\t\t __func__, skb, skb-\u003elen, cb-\u003enumber, \u0026fl4-\u003esaddr,\n+\t\t ntohs(fl4-\u003efl4_sport), \u0026fl4-\u003edaddr, ntohs(fl4-\u003efl4_dport));\n+\n+\tdst = sk_dst_get(sk);\n+\tif (!dst) {\n+\t\tkfree_skb(skb);\n+\t\treturn;\n+\t}\n+\tif (ip_dont_fragment(sk, dst) \u0026\u0026 !skb-\u003eignore_df)\n+\t\tdf = htons(IP_DF);\n+\n+\tif (cb-\u003eecn)\n+\t\ttos = (tos \u0026 ~INET_ECN_MASK) | cb-\u003eecn;\n+\tttl = (u8)ip4_dst_hoplimit(dst);\n+\tudp_tunnel_xmit_skb((struct rtable *)dst, sk, skb, fl4-\u003esaddr,\n+\t\t\t fl4-\u003edaddr, tos, ttl, df, fl4-\u003efl4_sport,\n+\t\t\t fl4-\u003efl4_dport, false, false, 0);\n+}\n+\n+static void quic_v6_lower_xmit(struct sock *sk, struct sk_buff *skb,\n+\t\t\t struct flowi *fl)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tu8 tc = inet6_sk(sk)-\u003etclass, ttl;\n+\tstruct flowi6 *fl6 = \u0026fl-\u003eu.ip6;\n+\tstruct dst_entry *dst;\n+\t__be32 label;\n+\n+\tpr_debug(\"%s: skb: %p, len: %d, num: %lld, %pI6c:%d -\u003e %pI6c:%d\\n\",\n+\t\t __func__, skb, skb-\u003elen, cb-\u003enumber, \u0026fl6-\u003esaddr,\n+\t\t ntohs(fl6-\u003efl6_sport), \u0026fl6-\u003edaddr, ntohs(fl6-\u003efl6_dport));\n+\n+\tdst = sk_dst_get(sk);\n+\tif (!dst) {\n+\t\tkfree_skb(skb);\n+\t\treturn;\n+\t}\n+\n+\tif (cb-\u003eecn)\n+\t\ttc = (tc \u0026 ~INET_ECN_MASK) | cb-\u003eecn;\n+\tttl = (u8)ip6_dst_hoplimit(dst);\n+\tlabel = ip6_make_flowlabel(sock_net(sk), skb, fl6-\u003eflowlabel, true,\n+\t\t\t\t fl6);\n+\tudp_tunnel6_xmit_skb(dst, sk, skb, NULL, \u0026fl6-\u003esaddr, \u0026fl6-\u003edaddr, tc,\n+\t\t\t ttl, label, fl6-\u003efl6_sport, fl6-\u003efl6_dport, false,\n+\t\t\t 0);\n+}\n+\n+static void quic_v4_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,\n+\t\t\t\t union quic_addr *sa)\n+{\n+\tstruct udphdr *uh = udp_hdr(skb);\n+\n+\tsa-\u003ev4.sin_family = AF_INET;\n+\tsa-\u003ev4.sin_port = uh-\u003esource;\n+\tsa-\u003ev4.sin_addr.s_addr = ip_hdr(skb)-\u003esaddr;\n+\n+\tda-\u003ev4.sin_family = AF_INET;\n+\tda-\u003ev4.sin_port = uh-\u003edest;\n+\tda-\u003ev4.sin_addr.s_addr = ip_hdr(skb)-\u003edaddr;\n+}\n+\n+static void quic_v6_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,\n+\t\t\t\t union quic_addr *sa)\n+{\n+\tstruct udphdr *uh = udp_hdr(skb);\n+\n+\tsa-\u003ev6.sin6_family = AF_INET6;\n+\tsa-\u003ev6.sin6_port = uh-\u003esource;\n+\tsa-\u003ev6.sin6_addr = ipv6_hdr(skb)-\u003esaddr;\n+\tsa-\u003ev6.sin6_scope_id = skb-\u003eskb_iif;\n+\n+\tda-\u003ev6.sin6_family = AF_INET6;\n+\tda-\u003ev6.sin6_port = uh-\u003edest;\n+\tda-\u003ev6.sin6_addr = ipv6_hdr(skb)-\u003edaddr;\n+\tda-\u003ev6.sin6_scope_id = skb-\u003eskb_iif;\n+}\n+\n+static int quic_v4_get_mtu_info(struct sk_buff *skb, u32 *info)\n+{\n+\tstruct icmphdr *hdr;\n+\n+\thdr = (struct icmphdr *)(skb_network_header(skb) -\n+\t\t\t\t sizeof(struct icmphdr));\n+\tif (hdr-\u003etype == ICMP_DEST_UNREACH \u0026\u0026 hdr-\u003ecode == ICMP_FRAG_NEEDED) {\n+\t\t*info = ntohs(hdr-\u003eun.frag.mtu);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Defer other types' processing to UDP error handler. */\n+\treturn -EINVAL;\n+}\n+\n+static int quic_v6_get_mtu_info(struct sk_buff *skb, u32 *info)\n+{\n+\tstruct icmp6hdr *hdr;\n+\n+\thdr = (struct icmp6hdr *)(skb_network_header(skb) -\n+\t\t\t\t sizeof(struct icmp6hdr));\n+\tif (hdr-\u003eicmp6_type == ICMPV6_PKT_TOOBIG) {\n+\t\t*info = ntohl(hdr-\u003eicmp6_mtu);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Defer other types' processing to UDP error handler. */\n+\treturn -EINVAL;\n+}\n+\n+static bool quic_v4_cmp_sk_addr(struct sock *sk, union quic_addr *a,\n+\t\t\t\tunion quic_addr *addr)\n+{\n+\tif (a-\u003ev4.sin_port != addr-\u003ev4.sin_port)\n+\t\treturn false;\n+\tif (a-\u003ev4.sin_family != addr-\u003ev4.sin_family)\n+\t\treturn false;\n+\t/* Match only if socket is also ANY-bound. */\n+\tif (addr-\u003ev4.sin_addr.s_addr == htonl(INADDR_ANY))\n+\t\treturn a-\u003ev4.sin_addr.s_addr == htonl(INADDR_ANY);\n+\tif (a-\u003ev4.sin_addr.s_addr == htonl(INADDR_ANY))\n+\t\treturn true;\n+\treturn a-\u003ev4.sin_addr.s_addr == addr-\u003ev4.sin_addr.s_addr;\n+}\n+\n+static bool quic_v6_cmp_sk_addr(struct sock *sk, union quic_addr *a,\n+\t\t\t\tunion quic_addr *addr)\n+{\n+\tif (a-\u003esa.sa_family == AF_INET \u0026\u0026 addr-\u003esa.sa_family == AF_INET)\n+\t\treturn quic_v4_cmp_sk_addr(sk, a, addr);\n+\n+\tif (a-\u003ev4.sin_port != addr-\u003ev4.sin_port)\n+\t\treturn false;\n+\n+\tif (a-\u003esa.sa_family != addr-\u003esa.sa_family) {\n+\t\tif (ipv6_only_sock(sk) || a-\u003esa.sa_family == AF_INET)\n+\t\t\treturn false;\n+\t\treturn quic_is_any_addr(a);\n+\t}\n+\n+\t/* Match only if socket is also ANY-bound. */\n+\tif (ipv6_addr_any(\u0026addr-\u003ev6.sin6_addr))\n+\t\treturn ipv6_addr_any(\u0026a-\u003ev6.sin6_addr);\n+\tif (ipv6_addr_any(\u0026a-\u003ev6.sin6_addr))\n+\t\treturn true;\n+\tif (!ipv6_addr_equal(\u0026a-\u003ev6.sin6_addr, \u0026addr-\u003ev6.sin6_addr))\n+\t\treturn false;\n+\tif ((ipv6_addr_type(\u0026a-\u003ev6.sin6_addr) \u0026 IPV6_ADDR_LINKLOCAL) \u0026\u0026\n+\t a-\u003ev6.sin6_scope_id \u0026\u0026 addr-\u003ev6.sin6_scope_id \u0026\u0026\n+\t a-\u003ev6.sin6_scope_id != addr-\u003ev6.sin6_scope_id)\n+\t\treturn false;\n+\treturn true;\n+}\n+\n+static int quic_v4_get_sk_addr(struct socket *sock, struct sockaddr *uaddr,\n+\t\t\t int peer)\n+{\n+\treturn inet_getname(sock, uaddr, peer);\n+}\n+\n+static int quic_v6_get_sk_addr(struct socket *sock, struct sockaddr *uaddr,\n+\t\t\t int peer)\n+{\n+\tunion quic_addr *a = quic_addr(uaddr);\n+\tint ret;\n+\n+\tret = inet6_getname(sock, uaddr, peer);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tif (a-\u003esa.sa_family == AF_INET6 \u0026\u0026\n+\t ipv6_addr_v4mapped(\u0026a-\u003ev6.sin6_addr)) {\n+\t\ta-\u003ev4.sin_family = AF_INET;\n+\t\ta-\u003ev4.sin_port = a-\u003ev6.sin6_port;\n+\t\ta-\u003ev4.sin_addr.s_addr = a-\u003ev6.sin6_addr.s6_addr32[3];\n+\t}\n+\n+\tif (a-\u003esa.sa_family == AF_INET) {\n+\t\tmemset(a-\u003ev4.sin_zero, 0, sizeof(a-\u003ev4.sin_zero));\n+\t\treturn sizeof(struct sockaddr_in);\n+\t}\n+\treturn sizeof(struct sockaddr_in6);\n+}\n+\n+#define quic_af_ipv4(a)\t\t((a)-\u003esa.sa_family == AF_INET)\n+\n+u32 quic_encap_len(union quic_addr *a)\n+{\n+\treturn (quic_af_ipv4(a) ? sizeof(struct iphdr) :\n+\t\t\t\t sizeof(struct ipv6hdr)) +\n+\t sizeof(struct udphdr);\n+}\n+\n+bool quic_is_any_addr(union quic_addr *a)\n+{\n+\treturn quic_af_ipv4(a) ? quic_v4_is_any_addr(a) :\n+\t\t\t\t quic_v6_is_any_addr(a);\n+}\n+\n+void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,\n+\t\t\tunion quic_addr *a)\n+{\n+\tquic_af_ipv4(a) ? quic_v4_udp_conf_init(sk, conf, a) :\n+\t\t\t quic_v6_udp_conf_init(sk, conf, a);\n+}\n+\n+int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa,\n+\t\t struct flowi *fl)\n+{\n+\tif (sa-\u003esa.sa_family \u0026\u0026 da-\u003esa.sa_family != sa-\u003esa.sa_family)\n+\t\treturn -EINVAL;\n+\n+\treturn quic_af_ipv4(da) ? quic_v4_flow_route(sk, da, sa, fl) :\n+\t\t\t\t quic_v6_flow_route(sk, da, sa, fl);\n+}\n+\n+void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da,\n+\t\t struct flowi *fl)\n+{\n+\tlocal_bh_disable();\n+\tquic_af_ipv4(da) ? quic_v4_lower_xmit(sk, skb, fl) :\n+\t\t\t quic_v6_lower_xmit(sk, skb, fl);\n+\tlocal_bh_enable();\n+}\n+\n+#define quic_skb_ipv4(skb)\t(ip_hdr(skb)-\u003eversion == 4)\n+\n+void quic_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,\n+\t\t\tunion quic_addr *sa)\n+{\n+\tmemset(sa, 0, sizeof(*sa));\n+\tmemset(da, 0, sizeof(*da));\n+\tquic_skb_ipv4(skb) ? quic_v4_get_msg_addrs(skb, da, sa) :\n+\t\t\t quic_v6_get_msg_addrs(skb, da, sa);\n+}\n+\n+int quic_get_mtu_info(struct sk_buff *skb, u32 *info)\n+{\n+\treturn quic_skb_ipv4(skb) ? quic_v4_get_mtu_info(skb, info) :\n+\t\t\t\t quic_v6_get_mtu_info(skb, info);\n+}\n+\n+#define quic_pf_ipv4(sk)\t((sk)-\u003esk_family == PF_INET)\n+\n+bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a,\n+\t\t union quic_addr *addr)\n+{\n+\treturn quic_pf_ipv4(sk) ? quic_v4_cmp_sk_addr(sk, a, addr) :\n+\t\t\t\t quic_v6_cmp_sk_addr(sk, a, addr);\n+}\n+\n+int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, int peer)\n+{\n+\treturn quic_pf_ipv4(sock-\u003esk) ? quic_v4_get_sk_addr(sock, a, peer) :\n+\t\t\t\t\tquic_v6_get_sk_addr(sock, a, peer);\n+}\n+\n+int quic_get_dev_if(struct sock *sk, union quic_addr *a)\n+{\n+\tif (!quic_af_ipv4(a) \u0026\u0026\n+\t ipv6_addr_type(\u0026a-\u003ev6.sin6_addr) \u0026 IPV6_ADDR_LINKLOCAL \u0026\u0026\n+\t a-\u003ev6.sin6_scope_id)\n+\t\treturn a-\u003ev6.sin6_scope_id;\n+\n+\treturn sk-\u003esk_bound_dev_if;\n+}\n+\n+void quic_set_skb_iif(struct sk_buff *skb)\n+{\n+\t/* Save the inet/inet6 iif before skb dst/cb are cleared. */\n+\tskb-\u003eskb_iif = quic_skb_ipv4(skb) ? inet_iif(skb) : inet6_iif(skb);\n+}\n+\n+int quic_common_setsockopt(struct sock *sk, int level, int optname,\n+\t\t\t sockptr_t optval, unsigned int optlen)\n+{\n+\treturn quic_pf_ipv4(sk) ?\n+\t ip_setsockopt(sk, level, optname, optval, optlen) :\n+\t ipv6_setsockopt(sk, level, optname, optval, optlen);\n+}\n+\n+int quic_common_getsockopt(struct sock *sk, int level, int optname,\n+\t\t\t char __user *optval, int __user *optlen)\n+{\n+\treturn quic_pf_ipv4(sk) ?\n+\t ip_getsockopt(sk, level, optname, optval, optlen) :\n+\t ipv6_getsockopt(sk, level, optname, optval, optlen);\n+}\n+\n+bool quic_sk_accept_pmtu(struct sock *sk, struct sk_buff *skb)\n+{\n+\treturn quic_skb_ipv4(skb) ? ip_sk_accept_pmtu(sk) :\n+\t\t\t\t ip6_sk_accept_pmtu(sk);\n+}\n+\n+void quic_sk_destruct(struct sock *sk)\n+{\n+\tquic_pf_ipv4(sk) ? inet_sock_destruct(sk) : inet6_sock_destruct(sk);\n+}\ndiff --git a/net/quic/family.h b/net/quic/family.h\nnew file mode 100644\nindex 0000000000000..02342c4b7c99e\n--- /dev/null\n+++ b/net/quic/family.h\n@@ -0,0 +1,44 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#define QUIC_PORT_LEN\t\t2\n+#define QUIC_ADDR4_LEN\t\t4\n+#define QUIC_ADDR6_LEN\t\t16\n+\n+#define QUIC_PREF_ADDR_LEN \\\n+\t(QUIC_ADDR4_LEN + QUIC_PORT_LEN + QUIC_ADDR6_LEN + QUIC_PORT_LEN)\n+\n+bool quic_is_any_addr(union quic_addr *a);\n+u32 quic_encap_len(union quic_addr *a);\n+\n+void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da,\n+\t\t struct flowi *fl);\n+int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa,\n+\t\t struct flowi *fl);\n+void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,\n+\t\t\tunion quic_addr *a);\n+\n+void quic_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,\n+\t\t\tunion quic_addr *sa);\n+int quic_get_mtu_info(struct sk_buff *skb, u32 *info);\n+\n+bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a,\n+\t\t union quic_addr *addr);\n+int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, int peer);\n+\n+int quic_get_dev_if(struct sock *sk, union quic_addr *a);\n+void quic_set_skb_iif(struct sk_buff *skb);\n+\n+int quic_common_setsockopt(struct sock *sk, int level, int optname,\n+\t\t\t sockptr_t optval, unsigned int optlen);\n+int quic_common_getsockopt(struct sock *sk, int level, int optname,\n+\t\t\t char __user *optval, int __user *optlen);\n+bool quic_sk_accept_pmtu(struct sock *sk, struct sk_buff *skb);\n+void quic_sk_destruct(struct sock *sk);\ndiff --git a/net/quic/packet.c b/net/quic/packet.c\nnew file mode 100644\nindex 0000000000000..db87c32d7469d\n--- /dev/null\n+++ b/net/quic/packet.c\n@@ -0,0 +1,1055 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \"socket.h\"\n+\n+#define QUIC_HLEN\t\t1\n+\n+#define QUIC_LONG_HLEN(dcid, scid) \\\n+\t(QUIC_HLEN + QUIC_VERSION_LEN + 1 + (dcid)-\u003elen + 1 + (scid)-\u003elen)\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 \u003c 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, \u0026len, 1) ||\n+\t len \u003e *plen || len \u003e 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, \u0026v, 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, \u0026len) || len \u003e *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 (quic_is_closed(sk))\n+\t\treturn;\n+\n+\tinfo = clamp(paths-\u003emtu_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-\u003eplpmtud_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-\u003eops-\u003eupdate_pmtu(dst, sk, NULL, info, true);\n+\t\tquic_packet_mss_update(sk, info - packet-\u003ehlen);\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+\tpacket-\u003elevel = QUIC_CRYPTO_APP;\n+\ttaglen = quic_packet_taglen(packet);\n+\tinfo = info - packet-\u003ehlen - taglen;\n+\tpathmtu = quic_path_pl_toobig(paths, info, \u0026reset_timer);\n+\tif (reset_timer)\n+\t\tquic_timer_reset(sk, QUIC_TIMER_PMTU, paths-\u003eplpmtud_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, \u0026saddr, \u0026daddr);\n+\tsk = quic_sock_lookup(skb, \u0026daddr, \u0026saddr, sk, NULL);\n+\tif (!sk)\n+\t\treturn -ENOENT;\n+\n+\tif (quic_get_mtu_info(skb, \u0026info) || !quic_sk_accept_pmtu(sk, skb)) {\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)-\u003emtu_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 \u0026sk-\u003esk_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+/* Queue a packet for later processing when sleeping is allowed. */\n+static int quic_packet_deferred_schedule(struct sk_buff *skb)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tstruct sock *sk = skb-\u003esk;\n+\tint len = skb-\u003etruesize;\n+\n+\tif (cb-\u003ebacklog)\n+\t\treturn 0;\n+\n+\tif (sk_rmem_alloc_get(sk) + len \u003e sk-\u003esk_rcvbuf ||\n+\t !__sk_rmem_schedule(sk, len, false)) {\n+\t\tQUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);\n+\t\tkfree_skb(skb);\n+\t\treturn -ENOBUFS;\n+\t}\n+\tcb-\u003ebacklog = 1;\n+\tskb_set_owner_r(skb, sk);\n+\t__skb_queue_tail(\u0026quic_packet(sk)-\u003edeferred_list, skb);\n+\n+\tsock_hold(sk);\n+\tif (!queue_work(quic_wq, quic_work(sk)))\n+\t\tsock_put(sk);\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\u003c0..2^16-1\u003e;\n+ * } Extension;\n+ *\n+ * struct {\n+ * ProtocolVersion legacy_version = 0x0303;\n+ * Random rand;\n+ * opaque legacy_session_id\u003c0..32\u003e;\n+ * CipherSuite cipher_suites\u003c2..2^16-2\u003e;\n+ * opaque legacy_compression_methods\u003c1..2^8-1\u003e;\n+ * Extension extensions\u003c8..2^16-1\u003e;\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+#define QUIC_FRAME_CRYPTO\t0x06\n+\n+/* Decrypt Initial packet and extract ALPN from TLS ClientHello for ALPN-based\n+ * socket demultiplexing. Marks packet as decrypted (cb-\u003eresume = 1) to avoid\n+ * redundant decryption later.\n+ */\n+static int quic_packet_get_alpn(struct sk_buff *skb, struct quic_data *alpn)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tint err, found = 0, exts = 0;\n+\tstruct quic_crypto *crypto;\n+\tstruct quic_packet *packet;\n+\tstruct sock *sk = skb-\u003esk;\n+\tu64 length, offset, type;\n+\tstruct net *net;\n+\tu32 len;\n+\tu8 *p;\n+\n+\t/* Install initial keys for decryption. */\n+\tcrypto = quic_crypto(sk, QUIC_CRYPTO_INITIAL);\n+\tpacket = quic_packet(sk);\n+\terr = quic_crypto_initial_keys_install(crypto, \u0026packet-\u003edcid,\n+\t\t\t\t\t packet-\u003eversion, true);\n+\tif (err)\n+\t\treturn err;\n+\tcb-\u003esync = 1;\n+\tnet = sock_net(sk);\n+\terr = quic_crypto_decrypt(crypto, skb, GFP_KERNEL);\n+\tif (err) {\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_DECDROP);\n+\t\treturn err;\n+\t}\n+\tQUIC_INC_STATS(net, QUIC_MIB_PKT_DECFASTPATHS);\n+\tcb-\u003eresume = 1; /* Mark this packet as already decrypted. */\n+\n+\t/* Find the QUIC CRYPTO frame. */\n+\tp = skb-\u003edata + cb-\u003enumber_offset + cb-\u003enumber_len;\n+\tlen = cb-\u003elength - cb-\u003enumber_len - QUIC_TAG_LEN;\n+\tfor (; len \u0026\u0026 !(*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(\u0026p, \u0026len, \u0026offset) || offset)\n+\t\treturn 0;\n+\tif (!quic_get_var(\u0026p, \u0026len, \u0026length) || length \u003e (u64)len)\n+\t\treturn 0;\n+\tif (len \u003e (u32)length) /* Cap len to crypto frame length. */\n+\t\tlen = length;\n+\n+\terr = -EINVAL;\n+\t/* Verify handshake message type (ClientHello) and its length. */\n+\tif (!quic_get_int(\u0026p, \u0026len, \u0026type, 1) || type != TLS_MT_CLIENT_HELLO)\n+\t\treturn err;\n+\tif (!quic_get_int(\u0026p, \u0026len, \u0026length, 3) ||\n+\t len \u003c TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN ||\n+\t length \u003c TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN)\n+\t\treturn err;\n+\tif (len \u003e (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(\u0026p, \u0026len, \u0026length, 1) || length)\n+\t\treturn err;\n+\n+\t/* Skip cipher_suites (2 bytes length + variable data). */\n+\tif (!quic_get_int(\u0026p, \u0026len, \u0026length, 2) || length \u003e (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(\u0026p, \u0026len, \u0026length, 1) || length \u003e (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(\u0026p, \u0026len, \u0026length, 2))\n+\t\treturn err;\n+\tif (len \u003e (u32)length) /* Limit len to extensions length if larger. */\n+\t\tlen = length;\n+\twhile (len \u003e= 4) { /* Scan extensions for ALPN (TLS_EXT_alpn). */\n+\t\tif (exts++ \u003e= TLS_MAX_EXTENSIONS)\n+\t\t\treturn err;\n+\t\tif (!quic_get_int(\u0026p, \u0026len, \u0026type, 2))\n+\t\t\tbreak;\n+\t\tif (!quic_get_int(\u0026p, \u0026len, \u0026length, 2))\n+\t\t\tbreak;\n+\t\tif (len \u003c (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 \u003e 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}\n+\tif (!found) { /* No ALPN ext: set alpn-\u003elen = 0 and alpn-\u003edata = 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(\u0026p, \u0026len, \u0026length, 2) || length \u003e (u64)len)\n+\t\treturn err;\n+\tquic_data(alpn, p, length); /* Store ALPN list in alpn-\u003edata. */\n+\tlen = length;\n+\twhile (len) { /* Validate ALPN protocols list format. */\n+\t\tif (!quic_get_int(\u0026p, \u0026len, \u0026length, 1) || length \u003e (u64)len) {\n+\t\t\t/* Bad ALPN: set alpn-\u003elen = 0, alpn-\u003edata = 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-\u003elen);\n+\treturn 0;\n+}\n+\n+/* Determine the QUIC socket associated with an incoming packet. */\n+static struct sock *quic_packet_get_sock(struct sk_buff *skb, struct sock *usk)\n+{\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tstruct quic_conn_id dcid = {}, *conn_id;\n+\tstruct net *net = sock_net(usk);\n+\tunion quic_addr daddr, saddr;\n+\tstruct quic_data alpns = {};\n+\tstruct sock *sk = NULL;\n+\tu32 len = skb-\u003elen;\n+\tu8 *p = skb-\u003edata;\n+\tint err;\n+\n+\tif (skb-\u003elen \u003c QUIC_HLEN)\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\tif (quic_hdr(skb)-\u003eform == QUIC_PACKET_FORM_SHORT) {\n+\t\t/* Short header path. */\n+\t\tif (skb-\u003elen \u003c 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-\u003edata + QUIC_HLEN,\n+\t\t\t\t\t QUIC_CONN_ID_DEF_LEN);\n+\t\tif (conn_id) {\n+\t\t\tcb-\u003eseqno = 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, \u0026daddr, \u0026saddr);\n+\t\tsk = quic_listen_sock_lookup(skb, \u0026daddr, \u0026saddr, usk, \u0026alpns);\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, \u0026daddr, \u0026saddr, usk, 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(\u0026dcid, NULL, NULL, \u0026p, \u0026len);\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-\u003eseqno = 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, \u0026daddr, \u0026saddr);\n+\tsk = quic_sock_lookup(skb, \u0026daddr, \u0026saddr, usk, \u0026dcid);\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+\tsk = quic_listen_sock_lookup(skb, \u0026daddr, \u0026saddr, usk, \u0026alpns);\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+\tif (skb_linearize(skb)) {\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, sk);\n+\tif (IS_ERR(sk)) {\n+\t\terr = PTR_ERR(sk);\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-\u003esk_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-\u003esk_backlog_rcv(sk, skb); /* quic_backlog_rcv(). */\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-\u003elen, err);\n+\tQUIC_INC_STATS(net, QUIC_MIB_PKT_RCVDROP);\n+\tkfree_skb(skb);\n+\treturn err;\n+}\n+\n+static int quic_packet_retry_create_and_xmit(struct sock *sk)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_packet_version_create_and_xmit(struct sock *sk, gfp_t gfp)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_packet_stateless_reset_create_and_xmit(struct sock *sk, u32 len,\n+\t\t\t\t\t\t gfp_t gfp)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_packet_refuse_close_create_and_xmit(struct sock *sk,\n+\t\t\t\t\t\t u32 errcode)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+/* Process an incoming packet on a listening QUIC socket.\n+ *\n+ * Depending on the packet type and state, this may involve creating a request\n+ * socket for a new connection, responding with a Stateless Reset for\n+ * unexpected Handshake or 1-RTT packets, issuing a Retry packet for address\n+ * validation when needed, or sending a Version Negotiation packet if the\n+ * client's QUIC version is unsupported.\n+ */\n+static int quic_packet_listen_process(struct sock *sk, struct sk_buff *skb,\n+\t\t\t\t gfp_t gfp)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tu32 version, errcode, toff, len = skb-\u003elen;\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tu8 *p = skb-\u003edata, type, retry = 0;\n+\tstruct net *net = sock_net(sk);\n+\tstruct quic_conn_id odcid = {};\n+\tstruct quic_request_sock *req;\n+\tstruct quic_data alpns = {};\n+\tstruct quic_crypto *crypto;\n+\tstruct quic_data token;\n+\tu64 length;\n+\tint err;\n+\n+\tif (quic_hshdr(skb)-\u003eform == QUIC_PACKET_FORM_SHORT) {\n+\t\t/* rfc9000#section-10.3:\n+\t\t *\n+\t\t * An endpoint MAY send a Stateless Reset in response to\n+\t\t * receiving a packet that it cannot associate with an active\n+\t\t * connection.\n+\t\t */\n+\t\tif (len \u003c QUIC_HLEN + QUIC_CONN_ID_DEF_LEN) {\n+\t\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);\n+\t\t\tkfree_skb(skb);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\t/* Read Destination address (packet-\u003esaddr) and Source address\n+\t\t * (packet-\u003edaddr).\n+\t\t */\n+\t\tquic_get_msg_addrs(skb, \u0026packet-\u003esaddr, \u0026packet-\u003edaddr);\n+\t\t/* We currently only issue Connection ID with size\n+\t\t * QUIC_CONN_ID_DEF_LEN.\n+\t\t */\n+\t\tquic_conn_id_update(\u0026packet-\u003edcid,\n+\t\t\t\t (u8 *)quic_hdr(skb) + QUIC_HLEN,\n+\t\t\t\t QUIC_CONN_ID_DEF_LEN);\n+\t\t/* Send a Stateless Reset for this 1-RTT packet. */\n+\t\terr = quic_packet_stateless_reset_create_and_xmit(sk, len, gfp);\n+\t\tconsume_skb(skb);\n+\t\treturn err;\n+\t}\n+\n+\t/* Read VERSION, Destination Connection ID and Source Connection ID. */\n+\terr = quic_packet_get_long_header(\u0026packet-\u003edcid, \u0026packet-\u003escid,\n+\t\t\t\t\t \u0026version, \u0026p, \u0026len);\n+\tif (err) {\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);\n+\t\tkfree_skb(skb);\n+\t\treturn err;\n+\t}\n+\n+\t/* Read Destination address (packet-\u003esaddr) and Source address\n+\t * (packet-\u003edaddr).\n+\t */\n+\tquic_get_msg_addrs(skb, \u0026packet-\u003esaddr, \u0026packet-\u003edaddr);\n+\treq = quic_request_sock_lookup(sk);\n+\tif (req) /* If request sock already exists, enqueue packet directly. */\n+\t\tgoto out;\n+\n+\tif (quic_accept_sock_exists(sk, skb))\n+\t\treturn 0; /* Already handled by matched accept socket. */\n+\n+\t/* rfc9000#section-6.1:\n+\t *\n+\t * An endpoint MUST NOT send a Version Negotiation packet in response\n+\t * to receiving a Version Negotiation packet.\n+\t */\n+\tif (!version) {\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);\n+\t\tkfree_skb(skb);\n+\t\treturn -EINVAL;\n+\t}\n+\tif (!quic_packet_compatible_versions(version)) {\n+\t\t/* rfc9000#section-6.1:\n+\t\t *\n+\t\t * If the version selected by the client is not acceptable to\n+\t\t * the server, the server responds with a Version Negotiation\n+\t\t * packet. This includes a list of versions that the server\n+\t\t * will accept.\n+\t\t */\n+\t\terr = quic_packet_version_create_and_xmit(sk, gfp);\n+\t\tconsume_skb(skb);\n+\t\treturn err;\n+\t}\n+\n+\t/* Read Packet Type. */\n+\ttype = quic_packet_version_get_type(version, quic_hshdr(skb)-\u003etype);\n+\tif (type != QUIC_PACKET_INITIAL) { /* Send a Stateless Reset. */\n+\t\terr = quic_packet_stateless_reset_create_and_xmit(sk, skb-\u003elen,\n+\t\t\t\t\t\t\t\t gfp);\n+\t\tconsume_skb(skb);\n+\t\treturn err;\n+\t}\n+\n+\t/* This Destination Connection ID MUST be at least 8 bytes in length. */\n+\tif (packet-\u003edcid.len \u003c QUIC_CONN_ID_DEF_LEN) {\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);\n+\t\tkfree_skb(skb);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\terr = quic_packet_get_token(\u0026token, \u0026p, \u0026len); /* Read Token. */\n+\tif (err) {\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);\n+\t\tkfree_skb(skb);\n+\t\treturn err;\n+\t}\n+\tif (token.len)\n+\t\ttoff = token.data - skb-\u003edata;\n+\n+\t/* Associate skb with sk to ensure sk is valid if skb is delayed to\n+\t * process in workqueue.\n+\t */\n+\tWARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk));\n+\tpacket-\u003eversion = version;\n+\tif (!cb-\u003eresume \u0026\u0026 static_branch_unlikely(\u0026quic_alpn_demux_key)) {\n+\t\tif (quic_packet_deferred_schedule(skb))\n+\t\t\treturn -EINPROGRESS;\n+\t\tif (!quic_get_var(\u0026p, \u0026len, \u0026length) || length \u003e (u64)len) {\n+\t\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);\n+\t\t\tkfree_skb(skb);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tcb-\u003elength = (u16)length;\n+\t\tcb-\u003enumber_offset = (u16)(p - skb-\u003edata);\n+\t\terr = quic_packet_get_alpn(skb, \u0026alpns);\n+\t\tif (err) {\n+\t\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);\n+\t\t\tkfree_skb(skb);\n+\t\t\treturn err;\n+\t\t}\n+\t\tif (quic_listen_sock_switch(skb, \u0026alpns))\n+\t\t\treturn 0; /* Switched to different listen socket. */\n+\t\tif (token.len) /* Update after skb-\u003edata may change. */\n+\t\t\ttoken.data = skb-\u003edata + toff;\n+\t}\n+\n+\t/* Save original DCID for future token validation or Retry logic. */\n+\tquic_conn_id_update(\u0026odcid, packet-\u003edcid.data, packet-\u003edcid.len);\n+\t/* If configured to validate client addresses, handle token logic. */\n+\tif (packet-\u003evalidate_peer_address) {\n+\t\tif (quic_packet_deferred_schedule(skb))\n+\t\t\treturn 0;\n+\t\tif (!token.len) {\n+\t\t\t/* rfc9000#section-8.1.2:\n+\t\t\t *\n+\t\t\t * Upon receiving the client's Initial packet, the\n+\t\t\t * server can request address validation by sending a\n+\t\t\t * Retry packet containing a token.\n+\t\t\t */\n+\t\t\terr = quic_packet_retry_create_and_xmit(sk);\n+\t\t\tconsume_skb(skb);\n+\t\t\treturn err;\n+\t\t}\n+\n+\t\t/* Distinguish token source: Retry packet or NEW_TOKEN frame. */\n+\t\tretry = *(u8 *)token.data == QUIC_TOKEN_FLAG_RETRY;\n+\n+\t\t/* Verify Token. */\n+\t\tcrypto = quic_crypto(sk, QUIC_CRYPTO_INITIAL);\n+\t\terr = quic_crypto_verify_token(crypto, \u0026packet-\u003edaddr,\n+\t\t\t\t\t sizeof(packet-\u003edaddr),\n+\t\t\t\t\t \u0026odcid, token.data, token.len);\n+\t\tif (err) {\n+\t\t\tif (!retry) {\n+\t\t\t\terr = quic_packet_retry_create_and_xmit(sk);\n+\t\t\t\tconsume_skb(skb);\n+\t\t\t\treturn err;\n+\t\t\t}\n+\t\t\t/* rfc9000#section-8.1.3:\n+\t\t\t *\n+\t\t\t * If a server receives a client Initial that contains\n+\t\t\t * an invalid Retry token but is otherwise valid, it\n+\t\t\t * knows the client will not accept another Retry\n+\t\t\t * token. The server SHOULD immediately close the\n+\t\t\t * connection with an INVALID_TOKEN error.\n+\t\t\t */\n+\t\t\terrcode = QUIC_TRANSPORT_ERROR_INVALID_TOKEN;\n+\t\t\tquic_packet_refuse_close_create_and_xmit(sk, errcode);\n+\t\t\tconsume_skb(skb);\n+\t\t\treturn err;\n+\t\t}\n+\t}\n+\n+\t/* Add request sock for this new QUIC connection. */\n+\treq = quic_request_sock_create(sk, \u0026odcid, retry, gfp);\n+\tif (IS_ERR(req)) {\n+\t\tif (quic_packet_deferred_schedule(skb))\n+\t\t\treturn 0;\n+\t\t/* rfc9000#section-5.2.2:\n+\t\t *\n+\t\t * If a server refuses to accept a new connection, it SHOULD\n+\t\t * send an Initial packet containing a CONNECTION_CLOSE frame\n+\t\t * with error code CONNECTION_REFUSED.\n+\t\t */\n+\t\terrcode = QUIC_TRANSPORT_ERROR_CONNECTION_REFUSED;\n+\t\tquic_packet_refuse_close_create_and_xmit(sk, errcode);\n+\t\tconsume_skb(skb);\n+\t\treturn PTR_ERR(req);\n+\t}\n+out:\n+\t/* Add to backlog list and wake blocked accept() calls */\n+\treturn quic_request_sock_backlog_tail(sk, req, skb);\n+}\n+\n+static int quic_packet_handshake_process(struct sock *sk, struct sk_buff *skb,\n+\t\t\t\t\t gfp_t gfp)\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+\t\t\t\t gfp_t gfp)\n+{\n+\tkfree_skb(skb);\n+\treturn -EOPNOTSUPP;\n+}\n+\n+int quic_packet_process(struct sock *sk, struct sk_buff *skb, gfp_t gfp)\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, gfp);\n+\n+\tif (quic_hdr(skb)-\u003eform == QUIC_PACKET_FORM_LONG)\n+\t\treturn quic_packet_handshake_process(sk, skb, gfp);\n+\n+\treturn quic_packet_app_process(sk, skb, gfp);\n+}\n+\n+/* Work function to process packets in the backlog queue. */\n+static void quic_packet_deferred_work(struct work_struct *work)\n+{\n+\tstruct quic_sock *qs = container_of(work, struct quic_sock, work);\n+\tstruct sock *sk = \u0026qs-\u003einet.sk;\n+\tstruct sk_buff_head *head;\n+\tstruct sk_buff *skb;\n+\n+\tlock_sock(sk);\n+\thead = \u0026quic_packet(sk)-\u003edeferred_list;\n+\twhile ((skb = __skb_dequeue(head)) != NULL)\n+\t\tquic_packet_process(sk, skb, GFP_KERNEL);\n+\trelease_sock(sk);\n+\tsock_put(sk);\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\n+\n+static struct sk_buff *quic_packet_handshake_create(struct sock *sk, gfp_t gfp)\n+{\n+\treturn NULL;\n+}\n+\n+static int quic_packet_number_check(struct sock *sk, gfp_t gfp)\n+{\n+\treturn 0;\n+}\n+\n+static struct sk_buff *quic_packet_app_create(struct sock *sk, gfp_t gfp)\n+{\n+\treturn NULL;\n+}\n+\n+/* Update the MSS and inform congestion control. */\n+void quic_packet_mss_update(struct sock *sk, u32 mss)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tstruct quic_cong *cong = quic_cong(sk);\n+\n+\tpacket-\u003emss[QUIC_PACKET_MSS_NORMAL] = (u16)mss;\n+\tquic_cong_set_mss(cong, packet-\u003emss[QUIC_PACKET_MSS_NORMAL]);\n+}\n+\n+/* Perform routing for the QUIC packet on the specified path, update header\n+ * length and MSS accordingly, reset path and start PMTU timer.\n+ */\n+int quic_packet_route(struct sock *sk)\n+{\n+\tstruct quic_path_group *paths = quic_paths(sk);\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tunion quic_addr *sa, *da;\n+\tu32 pmtu;\n+\tint err;\n+\n+\tda = quic_path_daddr(paths, packet-\u003epath);\n+\tsa = quic_path_saddr(paths, packet-\u003epath);\n+\terr = quic_flow_route(sk, da, sa, \u0026paths-\u003efl);\n+\tif (err)\n+\t\treturn err \u003c 0 ? err : 0;\n+\n+\tpacket-\u003ehlen = quic_encap_len(da);\n+\tpmtu = clamp(dst_mtu(__sk_dst_get(sk)),\n+\t\t QUIC_PATH_MIN_PMTU, QUIC_PATH_MAX_PMTU);\n+\tquic_packet_mss_update(sk, pmtu - packet-\u003ehlen);\n+\n+\tquic_path_pl_reset(paths);\n+\tquic_timer_reset(sk, QUIC_TIMER_PMTU, paths-\u003eplpmtud_interval);\n+\treturn 0;\n+}\n+\n+/* Return QUIC packet header overhead for the given level and path. Includes\n+ * packet number, connection IDs, and for long headers also version, length,\n+ * and Initial token (if present). Excludes payload.\n+ */\n+u16 quic_packet_overhead(struct sock *sk, u8 level, u8 path)\n+{\n+\tstruct quic_conn_id_set *source = quic_source(sk);\n+\tstruct quic_conn_id_set *dest = quic_dest(sk);\n+\tu16 len = QUIC_HLEN;\n+\n+\tlen += QUIC_PACKET_NUMBER_LEN; /* Packet number length. */\n+\tlen += quic_conn_id_choose(dest, path)-\u003elen; /* DCID length. */\n+\tif (level == QUIC_CRYPTO_APP)\n+\t\treturn len;\n+\n+\tlen += 1; /* Length byte for DCID. */\n+\t/* Length byte + SCID length. */\n+\tlen += 1 + quic_conn_id_active(source)-\u003elen;\n+\t/* Include token for Initial packets. */\n+\tif (level == QUIC_CRYPTO_INITIAL)\n+\t\tlen += quic_var_len(quic_token(sk)-\u003elen) + quic_token(sk)-\u003elen;\n+\tlen += QUIC_VERSION_LEN; /* Version length. */\n+\tlen += QUIC_PACKET_LENGTH_LEN; /* Packet length field. */\n+\n+\treturn len;\n+}\n+\n+/* Configure the QUIC packet header and routing based on encryption level and\n+ * path.\n+ */\n+int quic_packet_config(struct sock *sk, u8 level, u8 path)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\n+\t/* If packet already has data, no need to reconfigure. */\n+\tif (!quic_packet_empty(packet))\n+\t\treturn 0;\n+\n+\tpacket-\u003epath_validating = 0;\n+\tpacket-\u003eipfragok = 0;\n+\tpacket-\u003epadding = 0;\n+\tpacket-\u003eframes = 0;\n+\n+\tpacket-\u003elevel = level;\n+\tpacket-\u003eoverhead = quic_packet_overhead(sk, level, path);\n+\tpacket-\u003elen = packet-\u003eoverhead + quic_packet_taglen(packet);\n+\n+\t/* Allow fragmentation for handshake packets if PLPMTUD is enabled, as\n+\t * MTU discovery does not rely on ICMP Packet Too Big once PLPMTUD is\n+\t * enabled.\n+\t */\n+\tpacket-\u003eipfragok = level \u0026\u0026 !!quic_paths(sk)-\u003eplpmtud_interval;\n+\n+\tif (packet-\u003epath != path) {\n+\t\t/* Path changed; update and reset routing cache */\n+\t\tpacket-\u003epath = path;\n+\t\t__sk_dst_reset(sk);\n+\t}\n+\n+\t/* Perform routing and MSS update for the configured packet. */\n+\treturn quic_packet_route(sk);\n+}\n+\n+static void quic_packet_encrypt_done(struct sk_buff *skb, int err)\n+{\n+\t/* Free it for now, future patches will implement the actual deferred\n+\t * transmission logic.\n+\t */\n+\tkfree_skb(skb);\n+}\n+\n+/* Coalescing Packets. */\n+static int quic_packet_bundle(struct sock *sk, struct sk_buff *skb)\n+{\n+\tstruct quic_skb_cb *head_cb, *cb = QUIC_SKB_CB(skb);\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tstruct sk_buff *p;\n+\n+\tif (!packet-\u003ehead) /* First packet to bundle: initialize the head. */\n+\t\tgoto init;\n+\n+\t/* If bundling would exceed MSS, flush the current bundle. */\n+\tif (packet-\u003ehead-\u003elen + skb-\u003elen \u003e\n+\t packet-\u003emss[QUIC_PACKET_MSS_NORMAL]) {\n+\t\tquic_packet_flush(sk);\n+\t\tgoto init;\n+\t}\n+\t/* Bundle it and update metadata for the aggregate skb. */\n+\tskb_orphan(skb);\n+\tp = packet-\u003ehead;\n+\thead_cb = QUIC_SKB_CB(p);\n+\tif (head_cb-\u003elast == p)\n+\t\tskb_shinfo(p)-\u003efrag_list = skb;\n+\telse\n+\t\thead_cb-\u003elast-\u003enext = skb;\n+\tp-\u003edata_len += skb-\u003elen;\n+\tp-\u003etruesize += skb-\u003etruesize;\n+\tp-\u003elen += skb-\u003elen;\n+\thead_cb-\u003elast = skb;\n+\thead_cb-\u003eecn |= cb-\u003eecn; /* Merge ECN flags. */\n+\n+out:\n+\t/* rfc9000#section-12.2: Packets with a short header (Section 17.3) do\n+\t * not contain a Length field and so cannot be followed by other\n+\t * packets in the same UDP datagram.\n+\t *\n+\t * so Return 1 to flush if it is a Short header packet.\n+\t */\n+\treturn !cb-\u003elevel;\n+init:\n+\tpacket-\u003ehead = skb;\n+\tcb-\u003elast = skb;\n+\tgoto out;\n+}\n+\n+/* Transmit a QUIC packet, possibly encrypting and bundling it. */\n+static int quic_packet_xmit(struct sock *sk, struct sk_buff *skb, gfp_t gfp)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tstruct quic_skb_cb *cb = QUIC_SKB_CB(skb);\n+\tstruct net *net = sock_net(sk);\n+\tint err;\n+\n+\t/* Associate skb with sk to ensure sk is valid during async encryption\n+\t * completion.\n+\t */\n+\tWARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk));\n+\n+\t/* Skip encryption if taglen == 0 (e.g., disable_1rtt_encryption). */\n+\tif (!packet-\u003etaglen[quic_hdr(skb)-\u003eform])\n+\t\tgoto xmit;\n+\n+\tcb-\u003ecrypto_done = quic_packet_encrypt_done;\n+\terr = quic_crypto_encrypt(quic_crypto(sk, packet-\u003elevel), skb, gfp);\n+\tif (err) {\n+\t\tif (err != -EINPROGRESS) {\n+\t\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_ENCDROP);\n+\t\t\tkfree_skb(skb);\n+\t\t\treturn err;\n+\t\t}\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_ENCBACKLOGS);\n+\t\treturn err;\n+\t}\n+\tif (!cb-\u003eresume) /* Encryption completes synchronously. */\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_PKT_ENCFASTPATHS);\n+\n+xmit:\n+\tif (quic_packet_bundle(sk, skb))\n+\t\tquic_packet_flush(sk);\n+\treturn 0;\n+}\n+\n+/* Create and transmit a new QUIC packet. */\n+int quic_packet_create_and_xmit(struct sock *sk, gfp_t gfp)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tstruct sk_buff *skb;\n+\tint err;\n+\n+\terr = quic_packet_number_check(sk, gfp);\n+\tif (err)\n+\t\tgoto err;\n+\n+\tif (packet-\u003elevel)\n+\t\tskb = quic_packet_handshake_create(sk, gfp);\n+\telse\n+\t\tskb = quic_packet_app_create(sk, gfp);\n+\tif (!skb) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err;\n+\t}\n+\n+\terr = quic_packet_xmit(sk, skb, gfp);\n+\tif (err \u0026\u0026 err != -EINPROGRESS)\n+\t\tgoto err;\n+\n+\treturn 0;\n+err:\n+\tpr_debug(\"%s: err: %d\\n\", __func__, err);\n+\treturn err;\n+}\n+\n+/* Flush any coalesced/bundled QUIC packets. */\n+void quic_packet_flush(struct sock *sk)\n+{\n+\tstruct quic_path_group *paths = quic_paths(sk);\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\n+\tif (packet-\u003ehead) {\n+\t\tquic_lower_xmit(sk, packet-\u003ehead,\n+\t\t\t\tquic_path_daddr(paths, packet-\u003epath),\n+\t\t\t\t\u0026paths-\u003efl);\n+\t\tpacket-\u003ehead = NULL;\n+\t}\n+}\n+\n+void quic_packet_init(struct sock *sk)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\n+\tINIT_LIST_HEAD(\u0026packet-\u003eframe_list);\n+\tskb_queue_head_init(\u0026packet-\u003edeferred_list);\n+\tskb_queue_head_init(\u0026packet-\u003ebacklog_list);\n+\tINIT_WORK(quic_work(sk), quic_packet_deferred_work);\n+\n+\tpacket-\u003etaglen[QUIC_PACKET_FORM_SHORT] = QUIC_TAG_LEN;\n+\tpacket-\u003etaglen[QUIC_PACKET_FORM_LONG] = QUIC_TAG_LEN;\n+\tpacket-\u003emss[QUIC_PACKET_MSS_NORMAL] = QUIC_MIN_UDP_PAYLOAD;\n+\tpacket-\u003emss[QUIC_PACKET_MSS_DGRAM] = QUIC_MIN_UDP_PAYLOAD;\n+\n+\tpacket-\u003eversion = QUIC_VERSION_V1;\n+}\n+\n+void quic_packet_free(struct sock *sk)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\n+\tflush_work(quic_work(sk));\n+\t__skb_queue_purge(\u0026packet-\u003edeferred_list);\n+\t__skb_queue_purge(\u0026packet-\u003ebacklog_list);\n+}\ndiff --git a/net/quic/packet.h b/net/quic/packet.h\nnew file mode 100644\nindex 0000000000000..7f12bb53927b8\n--- /dev/null\n+++ b/net/quic/packet.h\n@@ -0,0 +1,123 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+struct quic_packet {\n+\tstruct quic_conn_id dcid; /* Dest Conn ID from received packet */\n+\tstruct quic_conn_id scid; /* Source Conn ID from received packet */\n+\tunion quic_addr daddr; /* Dest address from received packet */\n+\tunion quic_addr saddr; /* Source address from received packet */\n+\n+\tstruct sk_buff_head deferred_list; /* Packets deferred to work queue */\n+\tstruct sk_buff_head backlog_list; /* Packets waiting for crypto keys */\n+\tstruct list_head frame_list; /* Frames to pack into packet for send */\n+\tstruct sk_buff *head; /* Head skb for packet bundling on send */\n+\tu32 version; /* QUIC version used/selected during handshake */\n+\tu16 overhead; /* QUIC header length excluding frames */\n+\tu8 taglen[2]; /* Tag length for short and long packets */\n+\tu16 padding; /* Total padding bytes to append after frames */\n+\tu16 frames; /* Number of ack-eliciting frames */\n+\tu16 mss[2]; /* MSS for datagram and non-datagram packets */\n+\tu16 hlen; /* UDP + IP header length for sending */\n+\tu16 len; /* QUIC packet length including taglen for sending */\n+\n+\tu8 validate_peer_address:1; /* Temporary; will move to quic_outqueue */\n+\tu8 path_validating:1; /* Packet contains path_validating frames */\n+\tu8 ack_eliciting:1; /* Packet contains ack-eliciting frames */\n+\tu8 ack_immediate:1; /* Send ACK immediately (skip ack_delay timer) */\n+\tu8 non_probing:1; /* Packet contains non-probing frames */\n+\tu8 has_sack:1; /* Packet contains ACK frames */\n+\tu8 ipfragok:1; /* Allow IP fragmentation */\n+\tu8 path:1; /* Path identifier used to send this packet */\n+\tu8 level; /* Encryption level used */\n+};\n+\n+#define QUIC_PACKET_INITIAL_V1\t\t0\n+#define QUIC_PACKET_0RTT_V1\t\t1\n+#define QUIC_PACKET_HANDSHAKE_V1\t2\n+#define QUIC_PACKET_RETRY_V1\t\t3\n+\n+#define QUIC_PACKET_INITIAL_V2\t\t1\n+#define QUIC_PACKET_0RTT_V2\t\t2\n+#define QUIC_PACKET_HANDSHAKE_V2\t3\n+#define QUIC_PACKET_RETRY_V2\t\t0\n+\n+#define QUIC_PACKET_INITIAL\t\tQUIC_PACKET_INITIAL_V1\n+#define QUIC_PACKET_0RTT\t\tQUIC_PACKET_0RTT_V1\n+#define QUIC_PACKET_HANDSHAKE\t\tQUIC_PACKET_HANDSHAKE_V1\n+#define QUIC_PACKET_RETRY\t\tQUIC_PACKET_RETRY_V1\n+\n+#define QUIC_PACKET_INVALID\t\t0xff\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+#define QUIC_PACKET_FORM_SHORT\t\t0\n+#define QUIC_PACKET_FORM_LONG\t\t1\n+\n+static inline u8 quic_packet_taglen(struct quic_packet *packet)\n+{\n+\treturn packet-\u003etaglen[packet-\u003elevel != QUIC_CRYPTO_APP];\n+}\n+\n+static inline void quic_packet_set_taglen(struct quic_packet *packet, u8 taglen)\n+{\n+\tpacket-\u003etaglen[QUIC_PACKET_FORM_SHORT] = taglen;\n+}\n+\n+static inline u32 quic_packet_mss(struct quic_packet *packet)\n+{\n+\treturn packet-\u003emss[QUIC_PACKET_MSS_NORMAL] - quic_packet_taglen(packet);\n+}\n+\n+static inline u32 quic_packet_max_payload(struct quic_packet *packet)\n+{\n+\treturn packet-\u003emss[QUIC_PACKET_MSS_NORMAL] - packet-\u003eoverhead -\n+\t quic_packet_taglen(packet);\n+}\n+\n+static inline u32 quic_packet_max_payload_dgram(struct quic_packet *packet)\n+{\n+\treturn packet-\u003emss[QUIC_PACKET_MSS_DGRAM] - packet-\u003eoverhead -\n+\t quic_packet_taglen(packet);\n+}\n+\n+static inline bool quic_packet_empty(struct quic_packet *packet)\n+{\n+\treturn list_empty(\u0026packet-\u003eframe_list);\n+}\n+\n+static inline void quic_packet_reset(struct quic_packet *packet)\n+{\n+\tpacket-\u003elevel = 0;\n+\tpacket-\u003ehas_sack = 0;\n+\tpacket-\u003enon_probing = 0;\n+\tpacket-\u003eack_eliciting = 0;\n+\tpacket-\u003eack_immediate = 0;\n+}\n+\n+int quic_packet_process(struct sock *sk, struct sk_buff *skb, gfp_t gfp);\n+u16 quic_packet_overhead(struct sock *sk, u8 level, u8 path);\n+int quic_packet_config(struct sock *sk, u8 level, u8 path);\n+\n+int quic_packet_create_and_xmit(struct sock *sk, gfp_t gfp);\n+int quic_packet_route(struct sock *sk);\n+\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+void quic_packet_free(struct sock *sk);\n+\n+u32 *quic_packet_compatible_versions(u32 version);\n+\n+void quic_packet_rcv_err_pmtu(struct sock *sk);\ndiff --git a/net/quic/path.c b/net/quic/path.c\nnew file mode 100644\nindex 0000000000000..a41b99dd31eff\n--- /dev/null\n+++ b/net/quic/path.c\n@@ -0,0 +1,589 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003cnet/udp_tunnel.h\u003e\n+#include \u003clinux/quic.h\u003e\n+\n+#include \"common.h\"\n+#include \"family.h\"\n+#include \"path.h\"\n+\n+static int quic_udp_rcv(struct sock *sk, struct sk_buff *skb)\n+{\n+\tquic_set_skb_iif(skb);\n+\n+\tmemset(skb-\u003ecb, 0, sizeof(skb-\u003ecb));\n+\tQUIC_SKB_CB(skb)-\u003eseqno = -1;\n+\tQUIC_SKB_CB(skb)-\u003etime = quic_ktime_get_us();\n+\n+\tskb_pull(skb, sizeof(struct udphdr));\n+\tskb_dst_force(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 quic_packet_rcv(sk, skb, true);\n+}\n+\n+static void quic_udp_sock_put_work(struct work_struct *work)\n+{\n+\tstruct quic_udp_sock *us = container_of(work, struct quic_udp_sock,\n+\t\t\t\t\t\twork);\n+\tstruct quic_uhash_head *head;\n+\tstruct sock *sk = us-\u003esk;\n+\n+\t/* Hold the sock to safely access it in quic_udp_sock_lookup() even\n+\t * after udp_tunnel_sock_release(). The release must occur before\n+\t * __hlist_del() so a new UDP tunnel socket can be created for the same\n+\t * address and port if quic_udp_sock_lookup() fails to find one.\n+\t *\n+\t * Note: udp_tunnel_sock_release() cannot be called under the mutex due\n+\t * to some lockdep warnings.\n+\t */\n+\tsock_hold(sk);\n+\tudp_tunnel_sock_release(sk);\n+\n+\thead = quic_udp_sock_head(sock_net(sk), ntohs(us-\u003eaddr.v4.sin_port));\n+\tmutex_lock(\u0026head-\u003elock);\n+\t__hlist_del(\u0026us-\u003enode);\n+\tmutex_unlock(\u0026head-\u003elock);\n+\n+\tsock_put(sk);\n+\tkfree(us);\n+}\n+\n+static struct quic_udp_sock *quic_udp_sock_create(struct sock *sk,\n+\t\t\t\t\t\t union quic_addr *a)\n+{\n+\tstruct udp_tunnel_sock_cfg tuncfg = {};\n+\tstruct udp_port_cfg udp_conf = {};\n+\tstruct net *net = sock_net(sk);\n+\tstruct quic_uhash_head *head;\n+\tstruct quic_udp_sock *us;\n+\tstruct socket *sock;\n+\tint err;\n+\n+\tus = kmalloc_obj(*us);\n+\tif (!us)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tquic_udp_conf_init(sk, \u0026udp_conf, a);\n+\terr = udp_sock_create(net, \u0026udp_conf, \u0026sock);\n+\tif (err) {\n+\t\tpr_debug(\"%s: failed to create udp sock\\n\", __func__);\n+\t\tkfree(us);\n+\t\treturn ERR_PTR(err);\n+\t}\n+\n+\ttuncfg.encap_type = 1;\n+\ttuncfg.encap_rcv = quic_udp_rcv;\n+\ttuncfg.encap_err_lookup = quic_udp_err;\n+\tsetup_udp_tunnel_sock(net, sock-\u003esk, \u0026tuncfg);\n+\n+\trefcount_set(\u0026us-\u003erefcnt, 1);\n+\tus-\u003esk = sock-\u003esk;\n+\tmemcpy(\u0026us-\u003eaddr, a, sizeof(*a));\n+\tus-\u003ebind_ifindex = udp_conf.bind_ifindex;\n+\n+\tINIT_WORK(\u0026us-\u003ework, quic_udp_sock_put_work);\n+\thead = quic_udp_sock_head(net, ntohs(a-\u003ev4.sin_port));\n+\thlist_add_head(\u0026us-\u003enode, \u0026head-\u003ehead);\n+\n+\treturn us;\n+}\n+\n+static bool quic_udp_sock_get(struct quic_udp_sock *us)\n+{\n+\treturn refcount_inc_not_zero(\u0026us-\u003erefcnt);\n+}\n+\n+static void quic_udp_sock_put(struct quic_udp_sock *us)\n+{\n+\t/* The UDP socket may be freed in atomic RX context during connection\n+\t * migration; defer the release to a workqueue.\n+\t */\n+\tif (refcount_dec_and_test(\u0026us-\u003erefcnt))\n+\t\tqueue_work(quic_wq, \u0026us-\u003ework);\n+}\n+\n+/* Lookup a quic_udp_sock in the global hash table by port or address. If 'a'\n+ * is provided, it searches for a socket whose local address matches 'a' and,\n+ * if applicable, matches the device binding. If 'a' is NULL, it searches only\n+ * by port.\n+ */\n+static struct quic_udp_sock *quic_udp_sock_lookup(struct sock *sk,\n+\t\t\t\t\t\t union quic_addr *a, u16 port)\n+{\n+\tstruct net *net = sock_net(sk);\n+\tstruct quic_uhash_head *head;\n+\tstruct quic_udp_sock *us;\n+\n+\thead = quic_udp_sock_head(net, port);\n+\thlist_for_each_entry(us, \u0026head-\u003ehead, node) {\n+\t\tif (net != sock_net(us-\u003esk))\n+\t\t\tcontinue;\n+\t\tif (a) {\n+\t\t\tif (quic_cmp_sk_addr(us-\u003esk, \u0026us-\u003eaddr, a) \u0026\u0026\n+\t\t\t us-\u003ebind_ifindex == quic_get_dev_if(sk, a))\n+\t\t\t\treturn us;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (ntohs(us-\u003eaddr.v4.sin_port) == port)\n+\t\t\treturn us;\n+\t}\n+\treturn NULL;\n+}\n+\n+static void quic_path_set_udp_sk(struct quic_path *path,\n+\t\t\t\t struct quic_udp_sock *us)\n+{\n+\tif (path-\u003eudp_sk)\n+\t\tquic_udp_sock_put(path-\u003eudp_sk);\n+\n+\tpath-\u003eudp_sk = us;\n+\tif (!us) {\n+\t\tpath-\u003eusk = NULL;\n+\t\tmemset(\u0026path-\u003euaddr, 0, sizeof(path-\u003euaddr));\n+\t\treturn;\n+\t}\n+\tpath-\u003eusk = us-\u003esk;\n+\tmemcpy(\u0026path-\u003euaddr, \u0026us-\u003eaddr, sizeof(us-\u003eaddr));\n+}\n+\n+/* Binds a QUIC path to a local port and sets up a UDP socket. */\n+int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path)\n+{\n+\tunion quic_addr *a = quic_path_saddr(paths, path);\n+\tint rover, low, high, remaining;\n+\tstruct net *net = sock_net(sk);\n+\tstruct quic_uhash_head *head;\n+\tstruct quic_udp_sock *us;\n+\tu16 port;\n+\n+\tport = ntohs(a-\u003ev4.sin_port);\n+\tif (port) {\n+\t\tif (inet_port_requires_bind_service(net, port) \u0026\u0026\n+\t\t !ns_capable(net-\u003euser_ns, CAP_NET_BIND_SERVICE))\n+\t\t\treturn -EACCES;\n+\t\thead = quic_udp_sock_head(net, port);\n+\t\tmutex_lock(\u0026head-\u003elock);\n+\t\tus = quic_udp_sock_lookup(sk, a, port);\n+\t\tif (us) {\n+\t\t\tif (!uid_eq(sk-\u003esk_uid, us-\u003esk-\u003esk_uid)) {\n+\t\t\t\tmutex_unlock(\u0026head-\u003elock);\n+\t\t\t\treturn -EADDRINUSE;\n+\t\t\t}\n+\t\t\t/* Allow reuse of an existing UDP tunnel socket.\n+\t\t\t * However, if it is in the middle of asynchronous\n+\t\t\t * teardown (via workqueue), it is temporarily unusable.\n+\t\t\t * Return -EAGAIN (not -EADDRINUSE) to signal the caller\n+\t\t\t * to retry soon.\n+\t\t\t */\n+\t\t\tif (!quic_udp_sock_get(us)) {\n+\t\t\t\tmutex_unlock(\u0026head-\u003elock);\n+\t\t\t\treturn -EAGAIN;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tus = quic_udp_sock_create(sk, a);\n+\t\t\tif (IS_ERR(us)) {\n+\t\t\t\tmutex_unlock(\u0026head-\u003elock);\n+\t\t\t\treturn PTR_ERR(us);\n+\t\t\t}\n+\t\t}\n+\t\tmutex_unlock(\u0026head-\u003elock);\n+\t\tquic_path_set_udp_sk(\u0026paths-\u003epath[path], us);\n+\t\treturn 0;\n+\t}\n+\n+\tinet_sk_get_local_port_range(sk, \u0026low, \u0026high);\n+\tremaining = (high - low) + 1;\n+\trover = get_random_u32_below(remaining) + low;\n+\tdo {\n+\t\trover++;\n+\t\tif (rover \u003c low || rover \u003e high)\n+\t\t\trover = low;\n+\t\tport = (u16)rover;\n+\t\tif (inet_is_local_reserved_port(net, port))\n+\t\t\tcontinue;\n+\n+\t\thead = quic_udp_sock_head(net, port);\n+\t\tmutex_lock(\u0026head-\u003elock);\n+\t\tif (quic_udp_sock_lookup(sk, NULL, port)) {\n+\t\t\tmutex_unlock(\u0026head-\u003elock);\n+\t\t\tcond_resched();\n+\t\t\tcontinue;\n+\t\t}\n+\t\ta-\u003ev4.sin_port = htons(port);\n+\t\tus = quic_udp_sock_create(sk, a);\n+\t\tif (IS_ERR(us)) {\n+\t\t\ta-\u003ev4.sin_port = 0;\n+\t\t\tmutex_unlock(\u0026head-\u003elock);\n+\t\t\tif (PTR_ERR(us) == -EADDRINUSE) {\n+\t\t\t\tcond_resched();\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\treturn PTR_ERR(us);\n+\t\t}\n+\t\tmutex_unlock(\u0026head-\u003elock);\n+\n+\t\tquic_path_set_udp_sk(\u0026paths-\u003epath[path], us);\n+\t\t__sk_dst_reset(sk);\n+\t\treturn 0;\n+\t} while (--remaining \u003e 0);\n+\n+\treturn -EADDRINUSE;\n+}\n+\n+/* Swaps the active and alternate QUIC paths.\n+ *\n+ * Promotes the alternate path (path[1]) to become the new active path\n+ * (path[0]). If the alternate path has a valid UDP socket, the entire path is\n+ * swapped. Otherwise, only the destination address is exchanged, assuming the\n+ * source address is the same and no rebind is needed.\n+ *\n+ * This is typically used during path migration or alternate path promotion.\n+ */\n+void quic_path_swap(struct quic_path_group *paths)\n+{\n+\tstruct quic_path path = paths-\u003epath[0];\n+\n+\tpaths-\u003ealt_probes = 0;\n+\tpaths-\u003ealt_state = QUIC_PATH_ALT_SWAPPED;\n+\n+\t/* Protect path[0] modifications with seqcount for RCU readers in\n+\t * quic_sock_lookup(). The seqcount allows readers to detect torn reads\n+\t * during the non-atomic structure assignment.\n+\t */\n+\tlocal_bh_disable();\n+\twrite_seqcount_begin(\u0026paths-\u003epath_seq);\n+\tif (paths-\u003epath[1].udp_sk) {\n+\t\tpaths-\u003epath[0] = paths-\u003epath[1];\n+\t\tpaths-\u003epath[1] = path;\n+\t\tgoto out;\n+\t}\n+\n+\tpaths-\u003epath[0].daddr = paths-\u003epath[1].daddr;\n+\tpaths-\u003epath[1].daddr = path.daddr;\n+out:\n+\twrite_seqcount_end(\u0026paths-\u003epath_seq);\n+\tlocal_bh_enable();\n+}\n+\n+/* Frees resources associated with a QUIC path.\n+ *\n+ * This is used for cleanup during error handling or when the path is no longer\n+ * needed.\n+ */\n+void quic_path_unbind(struct sock *sk, struct quic_path_group *paths, u8 path)\n+{\n+\tpaths-\u003ealt_probes = 0;\n+\tpaths-\u003ealt_state = QUIC_PATH_ALT_NONE;\n+\n+\tquic_path_set_udp_sk(\u0026paths-\u003epath[path], NULL);\n+\n+\tmemset(quic_path_daddr(paths, path), 0, sizeof(union quic_addr));\n+\tmemset(quic_path_saddr(paths, path), 0, sizeof(union quic_addr));\n+}\n+\n+/* Detects and records a potential alternate path.\n+ *\n+ * If the new source or destination address differs from the active path, and\n+ * alternate path detection is not disabled, the function updates the alternate\n+ * path slot (path[1]) with the new addresses.\n+ *\n+ * This is typically called on packet receive to detect new possible network\n+ * paths (e.g., NAT rebinding, mobility).\n+ *\n+ * Returns true if a new alternate path was detected and updated, false\n+ * otherwise.\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+{\n+\tbool remote = !quic_cmp_sk_addr(sk, quic_path_daddr(paths, 0), da);\n+\tbool local = !quic_cmp_sk_addr(sk, quic_path_saddr(paths, 0), sa);\n+\n+\tif (!local \u0026\u0026 !remote)\n+\t\treturn false;\n+\n+\tif ((local \u0026\u0026 paths-\u003edisable_saddr_alt) ||\n+\t (remote \u0026\u0026 paths-\u003edisable_daddr_alt))\n+\t\treturn false;\n+\n+\tif (!quic_path_saddr(paths, 1)-\u003ev4.sin_port)\n+\t\tquic_path_set_saddr(paths, 1, sa);\n+\n+\tif (!quic_cmp_sk_addr(sk, quic_path_saddr(paths, 1), sa))\n+\t\treturn false;\n+\n+\tif (!quic_path_daddr(paths, 1)-\u003ev4.sin_port)\n+\t\tquic_path_set_daddr(paths, 1, da);\n+\n+\treturn quic_cmp_sk_addr(sk, quic_path_daddr(paths, 1), da);\n+}\n+\n+void quic_path_get_param(struct quic_path_group *paths,\n+\t\t\t struct quic_transport_param *p)\n+{\n+\tif (p-\u003eremote) {\n+\t\tp-\u003edisable_active_migration = paths-\u003edisable_saddr_alt;\n+\t\treturn;\n+\t}\n+\tp-\u003edisable_active_migration = paths-\u003edisable_daddr_alt;\n+}\n+\n+void quic_path_set_param(struct quic_path_group *paths,\n+\t\t\t struct quic_transport_param *p)\n+{\n+\tif (p-\u003eremote) {\n+\t\tpaths-\u003edisable_saddr_alt = !!p-\u003edisable_active_migration;\n+\t\treturn;\n+\t}\n+\tpaths-\u003edisable_daddr_alt = !!p-\u003edisable_active_migration;\n+}\n+\n+/* State Machine defined in rfc8899#section-5.2 */\n+enum quic_plpmtud_state {\n+\tQUIC_PL_DISABLED,\n+\tQUIC_PL_BASE,\n+\tQUIC_PL_SEARCH,\n+\tQUIC_PL_COMPLETE,\n+\tQUIC_PL_ERROR,\n+};\n+\n+#define QUIC_BASE_PLPMTU 1200\n+#define QUIC_MAX_PLPMTU 9000\n+#define QUIC_MIN_PLPMTU 512\n+\n+#define QUIC_MAX_PROBES 3\n+\n+#define QUIC_PL_BIG_STEP 32\n+#define QUIC_PL_MIN_STEP 4\n+\n+/* Handle PLPMTUD probe failure on a QUIC path.\n+ *\n+ * Called immediately after sending a probe packet in QUIC Path MTU Discovery.\n+ * Tracks probe count and manages state transitions based on the number of\n+ * probes sent and current PLPMTUD state (BASE, SEARCH, COMPLETE, ERROR).\n+ * Detects probe failures and black holes, adjusting PMTU and probe sizes\n+ * accordingly.\n+ *\n+ * Return: New PMTU value if updated, else 0.\n+ */\n+u32 quic_path_pl_send(struct quic_path_group *paths, s64 number)\n+{\n+\tu32 pathmtu = 0;\n+\n+\tpaths-\u003epl.number = number;\n+\tif (paths-\u003epl.probe_count \u003c QUIC_MAX_PROBES)\n+\t\tgoto out;\n+\n+\tpaths-\u003epl.probe_count = 0;\n+\tif (paths-\u003epl.state == QUIC_PL_BASE) {\n+\t\tif (paths-\u003epl.probe_size == QUIC_BASE_PLPMTU) {\n+\t\t\t/* BASE_PLPMTU Confirming Failed: Base -\u003e Error. */\n+\t\t\tpaths-\u003epl.state = QUIC_PL_ERROR;\n+\n+\t\t\tpaths-\u003epl.pmtu = QUIC_BASE_PLPMTU;\n+\t\t\tpathmtu = QUIC_BASE_PLPMTU;\n+\t\t}\n+\t} else if (paths-\u003epl.state == QUIC_PL_SEARCH) {\n+\t\tif (paths-\u003epl.pmtu == paths-\u003epl.probe_size) {\n+\t\t\t/* Black Hole Detected: Search -\u003e Base. */\n+\t\t\tpaths-\u003epl.state = QUIC_PL_BASE;\n+\t\t\tpaths-\u003epl.probe_size = QUIC_BASE_PLPMTU;\n+\t\t\tpaths-\u003epl.probe_high = 0;\n+\n+\t\t\tpaths-\u003epl.pmtu = QUIC_BASE_PLPMTU;\n+\t\t\tpathmtu = QUIC_BASE_PLPMTU;\n+\t\t} else { /* Normal probe failure. */\n+\t\t\tpaths-\u003epl.probe_high = paths-\u003epl.probe_size;\n+\t\t\tpaths-\u003epl.probe_size = paths-\u003epl.pmtu;\n+\t\t}\n+\t} else if (paths-\u003epl.state == QUIC_PL_COMPLETE) {\n+\t\tif (paths-\u003epl.pmtu == paths-\u003epl.probe_size) {\n+\t\t\t/* Black Hole Detected: Search Complete -\u003e Base. */\n+\t\t\tpaths-\u003epl.state = QUIC_PL_BASE;\n+\t\t\tpaths-\u003epl.probe_size = QUIC_BASE_PLPMTU;\n+\n+\t\t\t/* probe_high already reset when entering COMPLETE. */\n+\t\t\tpaths-\u003epl.pmtu = QUIC_BASE_PLPMTU;\n+\t\t\tpathmtu = QUIC_BASE_PLPMTU;\n+\t\t}\n+\t}\n+\n+out:\n+\tpr_debug(\"%s: dst: %p, state: %d, pmtu: %d, size: %d, high: %d\\n\",\n+\t\t __func__, paths, paths-\u003epl.state, paths-\u003epl.pmtu,\n+\t\t paths-\u003epl.probe_size, paths-\u003epl.probe_high);\n+\tpaths-\u003epl.probe_count++;\n+\treturn pathmtu;\n+}\n+\n+/* Handle successful reception of a PMTU probe.\n+ *\n+ * Called when a probe packet is acknowledged. Updates probe size and\n+ * transitions state if needed (e.g., from SEARCH to COMPLETE). Expands PMTU\n+ * using binary or linear search depending on state.\n+ *\n+ * Return: New PMTU to apply if search completes, or 0 if no change.\n+ */\n+u32 quic_path_pl_recv(struct quic_path_group *paths, bool *raise_timer,\n+\t\t bool *complete)\n+{\n+\tu32 pathmtu = 0;\n+\tu16 next;\n+\n+\tpr_debug(\"%s: dst: %p, state: %d, pmtu: %d, size: %d, high: %d\\n\",\n+\t\t __func__, paths, paths-\u003epl.state, paths-\u003epl.pmtu,\n+\t\t paths-\u003epl.probe_size, paths-\u003epl.probe_high);\n+\n+\t*raise_timer = false;\n+\tpaths-\u003epl.number = 0;\n+\tpaths-\u003epl.pmtu = paths-\u003epl.probe_size;\n+\tpaths-\u003epl.probe_count = 0;\n+\tif (paths-\u003epl.state == QUIC_PL_BASE) {\n+\t\tpaths-\u003epl.state = QUIC_PL_SEARCH; /* Base -\u003e Search */\n+\t\tpaths-\u003epl.probe_size += QUIC_PL_BIG_STEP;\n+\t} else if (paths-\u003epl.state == QUIC_PL_ERROR) {\n+\t\tpaths-\u003epl.state = QUIC_PL_SEARCH; /* Error -\u003e Search */\n+\n+\t\tpaths-\u003epl.pmtu = paths-\u003epl.probe_size;\n+\t\tpathmtu = (u32)paths-\u003epl.pmtu;\n+\t\tpaths-\u003epl.probe_size += QUIC_PL_BIG_STEP;\n+\t} else if (paths-\u003epl.state == QUIC_PL_SEARCH) {\n+\t\tif (!paths-\u003epl.probe_high) {\n+\t\t\tif (paths-\u003epl.probe_size \u003c QUIC_MAX_PLPMTU) {\n+\t\t\t\tnext = paths-\u003epl.probe_size + QUIC_PL_BIG_STEP;\n+\t\t\t\tpaths-\u003epl.probe_size =\n+\t\t\t\t\tmin_t(u16, next, QUIC_MAX_PLPMTU);\n+\t\t\t\t*complete = false;\n+\t\t\t\treturn 0;\n+\t\t\t}\n+\t\t\tpaths-\u003epl.probe_high = QUIC_MAX_PLPMTU;\n+\t\t}\n+\t\tpaths-\u003epl.probe_size += QUIC_PL_MIN_STEP;\n+\t\tif (paths-\u003epl.probe_size \u003e= paths-\u003epl.probe_high) {\n+\t\t\tpaths-\u003epl.probe_high = 0;\n+\t\t\t/* Search -\u003e Search Complete */\n+\t\t\tpaths-\u003epl.state = QUIC_PL_COMPLETE;\n+\n+\t\t\tpaths-\u003epl.probe_size = paths-\u003epl.pmtu;\n+\t\t\tpathmtu = (u32)paths-\u003epl.pmtu;\n+\t\t\t*raise_timer = true;\n+\t\t}\n+\t} else if (paths-\u003epl.state == QUIC_PL_COMPLETE) {\n+\t\t/* Raise probe_size after 30 * interval in Search Complete;\n+\t\t * Search Complete -\u003e Search.\n+\t\t */\n+\t\tpaths-\u003epl.state = QUIC_PL_SEARCH;\n+\t\tnext = paths-\u003epl.probe_size + QUIC_PL_MIN_STEP;\n+\t\tpaths-\u003epl.probe_size = min_t(u16, next, QUIC_MAX_PLPMTU);\n+\t}\n+\n+\t*complete = (paths-\u003epl.state == QUIC_PL_COMPLETE);\n+\treturn pathmtu;\n+}\n+\n+/* Handle ICMP \"Packet Too Big\" messages.\n+ *\n+ * Responds to an incoming ICMP error by reducing the probe size or falling\n+ * back to a safe baseline PMTU depending on current state. Also handles cases\n+ * where the PMTU hint lies between probe and current PMTU.\n+ *\n+ * Return: New PMTU to apply if state changes, or 0 if no change.\n+ */\n+u32 quic_path_pl_toobig(struct quic_path_group *paths, u32 pmtu,\n+\t\t\tbool *reset_timer)\n+{\n+\tu32 pathmtu = 0;\n+\n+\tpr_debug(\"%s: dst: %p, state: %d, pmtu: %d, size: %d, ptb: %d\\n\",\n+\t\t __func__, paths, paths-\u003epl.state, paths-\u003epl.pmtu,\n+\t\t paths-\u003epl.probe_size, pmtu);\n+\n+\t*reset_timer = false;\n+\tif (pmtu \u003c QUIC_MIN_PLPMTU || pmtu \u003e= (u32)paths-\u003epl.probe_size)\n+\t\treturn pathmtu;\n+\n+\tif (paths-\u003epl.state == QUIC_PL_BASE) {\n+\t\tif (pmtu \u003c QUIC_BASE_PLPMTU) {\n+\t\t\tpaths-\u003epl.state = QUIC_PL_ERROR; /* Base -\u003e Error */\n+\n+\t\t\tpaths-\u003epl.pmtu = QUIC_BASE_PLPMTU;\n+\t\t\tpathmtu = QUIC_BASE_PLPMTU;\n+\t\t}\n+\t} else if (paths-\u003epl.state == QUIC_PL_SEARCH) {\n+\t\tif (pmtu \u003e= QUIC_BASE_PLPMTU \u0026\u0026 pmtu \u003c (u32)paths-\u003epl.pmtu) {\n+\t\t\tpaths-\u003epl.state = QUIC_PL_BASE; /* Search -\u003e Base */\n+\t\t\tpaths-\u003epl.probe_size = QUIC_BASE_PLPMTU;\n+\t\t\tpaths-\u003epl.probe_count = 0;\n+\n+\t\t\tpaths-\u003epl.probe_high = 0;\n+\t\t\tpaths-\u003epl.pmtu = QUIC_BASE_PLPMTU;\n+\t\t\tpathmtu = QUIC_BASE_PLPMTU;\n+\t\t} else if (pmtu \u003e (u32)paths-\u003epl.pmtu \u0026\u0026\n+\t\t\t pmtu \u003c (u32)paths-\u003epl.probe_size) {\n+\t\t\tpaths-\u003epl.probe_size = (u16)pmtu;\n+\t\t\tpaths-\u003epl.probe_count = 0;\n+\t\t}\n+\t} else if (paths-\u003epl.state == QUIC_PL_COMPLETE) {\n+\t\tif (pmtu \u003e= QUIC_BASE_PLPMTU \u0026\u0026 pmtu \u003c (u32)paths-\u003epl.pmtu) {\n+\t\t\tpaths-\u003epl.state = QUIC_PL_BASE; /* Complete -\u003e Base */\n+\t\t\tpaths-\u003epl.probe_size = QUIC_BASE_PLPMTU;\n+\t\t\tpaths-\u003epl.probe_count = 0;\n+\n+\t\t\tpaths-\u003epl.probe_high = 0;\n+\t\t\tpaths-\u003epl.pmtu = QUIC_BASE_PLPMTU;\n+\t\t\tpathmtu = QUIC_BASE_PLPMTU;\n+\t\t\t*reset_timer = true;\n+\t\t}\n+\t}\n+\treturn pathmtu;\n+}\n+\n+/* Reset PLPMTUD state for a path.\n+ *\n+ * Resets all PLPMTUD-related state to its initial configuration. Called when\n+ * a new path is initialized or when recovering from errors.\n+ */\n+void quic_path_pl_reset(struct quic_path_group *paths)\n+{\n+\tpaths-\u003epl.number = 0;\n+\tpaths-\u003epl.probe_high = 0;\n+\tpaths-\u003epl.probe_count = 0;\n+\tpaths-\u003epl.state = QUIC_PL_BASE;\n+\tpaths-\u003epl.pmtu = QUIC_BASE_PLPMTU;\n+\tpaths-\u003epl.probe_size = QUIC_BASE_PLPMTU;\n+}\n+\n+/* Check if a packet number confirms PLPMTUD probe.\n+ *\n+ * Checks whether the last probe (tracked by .number) has been acknowledged.\n+ * If the probe number lies within the ACK range, confirmation is successful.\n+ *\n+ * Return: true if probe is confirmed, false otherwise.\n+ */\n+bool quic_path_pl_confirm(struct quic_path_group *paths, s64 largest,\n+\t\t\t s64 smallest)\n+{\n+\treturn paths-\u003epl.number \u0026\u0026 paths-\u003epl.number \u003e= smallest \u0026\u0026\n+\t paths-\u003epl.number \u003c= largest;\n+}\n+\n+void quic_path_init(struct quic_path_group *paths)\n+{\n+\tseqcount_init(\u0026paths-\u003epath_seq);\n+}\ndiff --git a/net/quic/path.h b/net/quic/path.h\nnew file mode 100644\nindex 0000000000000..45c3c6bb9d973\n--- /dev/null\n+++ b/net/quic/path.h\n@@ -0,0 +1,192 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#define QUIC_PATH_MIN_PMTU\t1200U\n+#define QUIC_PATH_MAX_PMTU\t65535U\n+\n+#define QUIC_MIN_UDP_PAYLOAD\t1200\n+#define QUIC_MAX_UDP_PAYLOAD\t65527\n+\n+#define QUIC_PATH_ENTROPY_LEN\t8\n+\n+#define QUIC_PMTUD_RAISE_TIMER_FACTOR\t30\n+\n+extern struct workqueue_struct\t*quic_wq;\n+\n+/* Connection Migration State Machine:\n+ *\n+ * +--------+ recv non-probing, free old path +----------+\n+ * | NONE | \u003c-------------------------------------- | SWAPPED |\n+ * +--------+ +----------+\n+ * | ^ \\ ^\n+ * | \\ \\ |\n+ * | \\ \\ new path detected, | recv\n+ * | \\ \\ has another DCID, | Path\n+ * | \\ \\ snd Path Challenge | Response\n+ * | \\ ------------------------------- |\n+ * | ------------------------------- \\ |\n+ * | new path detected, Path \\ \\ |\n+ * | has no other DCID, Challenge \\ \\ |\n+ * | request a new DCID failed \\ \\ |\n+ * v \\ v |\n+ * +----------+ +----------+\n+ * | PENDING | ------------------------------------\u003e | PROBING |\n+ * +----------+ recv a new DCID, snd Path Challenge +----------+\n+ */\n+enum {\n+\tQUIC_PATH_ALT_NONE,\n+\tQUIC_PATH_ALT_PENDING, /* Waiting for new dest conn ID for migration */\n+\tQUIC_PATH_ALT_PROBING, /* Validating alternate path (PATH_CHALLENGE) */\n+\tQUIC_PATH_ALT_SWAPPED, /* Alternate path is now active; roles swapped */\n+};\n+\n+struct quic_udp_sock {\n+\tstruct work_struct work; /* Workqueue to destroy UDP tunnel socket */\n+\tstruct hlist_node node; /* Node in addr-based UDP socket hash table */\n+\tunion quic_addr addr; /* Source addr of underlying UDP tunnel socket */\n+\tint bind_ifindex;\n+\trefcount_t refcnt;\n+\tstruct sock *sk; /* Underlying UDP tunnel socket */\n+};\n+\n+struct quic_path {\n+\tunion quic_addr daddr; /* Destination address */\n+\tunion quic_addr saddr; /* Source address */\n+\n+\t/* Wrapped UDP socket for receiving QUIC */\n+\tstruct quic_udp_sock *udp_sk;\n+\t/* Cached UDP tunnel socket and source addr for RCU access */\n+\tunion quic_addr uaddr;\n+\tstruct sock *usk;\n+};\n+\n+struct quic_path_group {\n+\t/* Connection ID validation during handshake (rfc9000#section-7.3) */\n+\tstruct quic_conn_id retry_dcid; /* Source CID from Retry packet */\n+\tstruct quic_conn_id orig_dcid; /* Destination CID from first Initial */\n+\n+\t/* Path validation (rfc9000#section-8.2) */\n+\tu8 entropy[QUIC_PATH_ENTROPY_LEN]; /* Entropy for PATH_CHALLENGE */\n+\tstruct quic_path path[2]; /* Active path (0) and alternate path (1) */\n+\tseqcount_t path_seq; /* Protects path[0] during swap */\n+\tstruct flowi fl; /* Flow info from routing decisions */\n+\n+\t/* Anti-amplification limit (rfc9000#section-8) */\n+\tu32 ampl_sndlen; /* Bytes sent before address is validated */\n+\tu32 ampl_rcvlen; /* Bytes received to lift amplification limit */\n+\n+\t/* MTU discovery handling */\n+\tstruct { /* PLPMTUD probing (rfc8899) */\n+\t\ts64 number; /* Packet number used for current probe */\n+\t\tu16 pmtu; /* Confirmed path MTU */\n+\n+\t\tu16 probe_size; /* Current probe packet size */\n+\t\tu16 probe_high; /* Highest failed probe size */\n+\t\tu8 probe_count; /* Retry count for current probe_size */\n+\t\tu8 state; /* Probe state machine (rfc8899#section-5.2) */\n+\t} pl;\n+\tu32 mtu_info; /* PMTU value from received ICMP, pending apply */\n+\n+\tu32 plpmtud_interval; /* Time interval for the PLPMTUD probe timer */\n+\tu32 keepalive_interval; /* Time interval to maintain path liveness */\n+\n+\tu8 ecn_probes; /* ECN probe counter */\n+\tu8 validated:1; /* Path validated with PATH_RESPONSE */\n+\tu8 blocked:1; /* Blocked by anti-amplification limit */\n+\tu8 version:1; /* Version negotiation performed */\n+\tu8 retry:1; /* Retry used in initial packet */\n+\n+\t/* Connection Migration (rfc9000#section-9) */\n+\tu8 disable_saddr_alt:1;\t/* Remote disable_active_migration parameter */\n+\tu8 disable_daddr_alt:1;\t/* Local disable_active_migration parameter */\n+\tu8 pref_addr:1; /* Preferred address offered (rfc9000#section-18.2) */\n+\tu8 alt_probes; /* Number of PATH_CHALLENGE probes sent */\n+\tu8 alt_state; /* Connection migration state (see above) */\n+};\n+\n+static inline union quic_addr *quic_path_saddr(struct quic_path_group *paths,\n+\t\t\t\t\t u8 path)\n+{\n+\treturn \u0026paths-\u003epath[path].saddr;\n+}\n+\n+static inline void quic_path_set_saddr(struct quic_path_group *paths, u8 path,\n+\t\t\t\t union quic_addr *addr)\n+{\n+\tmemcpy(quic_path_saddr(paths, path), addr, sizeof(*addr));\n+}\n+\n+static inline union quic_addr *quic_path_daddr(struct quic_path_group *paths,\n+\t\t\t\t\t u8 path)\n+{\n+\treturn \u0026paths-\u003epath[path].daddr;\n+}\n+\n+static inline void quic_path_set_daddr(struct quic_path_group *paths, u8 path,\n+\t\t\t\t union quic_addr *addr)\n+{\n+\tmemcpy(quic_path_daddr(paths, path), addr, sizeof(*addr));\n+}\n+\n+static inline union quic_addr *quic_path_uaddr(struct quic_path_group *paths,\n+\t\t\t\t\t u8 path)\n+{\n+\treturn \u0026paths-\u003epath[path].uaddr;\n+}\n+\n+static inline struct sock *quic_path_usock(struct quic_path_group *paths,\n+\t\t\t\t\t u8 path)\n+{\n+\treturn paths-\u003epath[path].usk;\n+}\n+\n+static inline bool quic_path_alt_state(struct quic_path_group *paths, u8 state)\n+{\n+\treturn paths-\u003ealt_state == state;\n+}\n+\n+static inline void quic_path_set_alt_state(struct quic_path_group *paths,\n+\t\t\t\t\t u8 state)\n+{\n+\tpaths-\u003ealt_state = state;\n+}\n+\n+/* Returns the destination Connection ID (DCID) used for identifying the\n+ * connection. Per rfc9000#section-7.3, handshake packets are considered part\n+ * of the same connection if their DCID matches the one returned here.\n+ */\n+static inline struct quic_conn_id *\n+quic_path_orig_dcid(struct quic_path_group *paths)\n+{\n+\treturn paths-\u003eretry ? \u0026paths-\u003eretry_dcid : \u0026paths-\u003eorig_dcid;\n+}\n+\n+int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp);\n+void quic_path_init(struct quic_path_group *paths);\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);\n+void quic_path_unbind(struct sock *sk, struct quic_path_group *paths, u8 path);\n+void quic_path_swap(struct quic_path_group *paths);\n+\n+u32 quic_path_pl_recv(struct quic_path_group *paths, bool *raise_timer,\n+\t\t bool *complete);\n+u32 quic_path_pl_toobig(struct quic_path_group *paths, u32 pmtu,\n+\t\t\tbool *reset_timer);\n+u32 quic_path_pl_send(struct quic_path_group *paths, s64 number);\n+\n+void quic_path_get_param(struct quic_path_group *paths,\n+\t\t\t struct quic_transport_param *p);\n+void quic_path_set_param(struct quic_path_group *paths,\n+\t\t\t struct quic_transport_param *p);\n+bool quic_path_pl_confirm(struct quic_path_group *paths,\n+\t\t\t s64 largest, s64 smallest);\n+void quic_path_pl_reset(struct quic_path_group *paths);\ndiff --git a/net/quic/pnspace.c b/net/quic/pnspace.c\nnew file mode 100644\nindex 0000000000000..c7c4d0be2d980\n--- /dev/null\n+++ b/net/quic/pnspace.c\n@@ -0,0 +1,273 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003clinux/slab.h\u003e\n+\n+#include \"common.h\"\n+#include \"pnspace.h\"\n+\n+int quic_pnspace_init(struct quic_pnspace *space)\n+{\n+\tspace-\u003epn_map = kzalloc(BITS_TO_BYTES(QUIC_PN_MAP_INITIAL), GFP_KERNEL);\n+\tif (!space-\u003epn_map)\n+\t\treturn -ENOMEM;\n+\tspace-\u003epn_map_len = QUIC_PN_MAP_INITIAL;\n+\n+\tspace-\u003emax_time_limit = QUIC_PNSPACE_TIME_LIMIT;\n+\tspace-\u003enext_pn = QUIC_PNSPACE_NEXT_PN;\n+\tspace-\u003emax_pn_acked_seen = -1;\n+\tspace-\u003ebase_pn = -1;\n+\treturn 0;\n+}\n+\n+void quic_pnspace_free(struct quic_pnspace *space)\n+{\n+\tspace-\u003epn_map_len = 0;\n+\tkfree(space-\u003epn_map);\n+\tspace-\u003epn_map = NULL;\n+}\n+\n+/* Expand the bitmap tracking received packet numbers. Ensures the pn_map\n+ * bitmap can cover at least @size packet numbers. Allocates a larger bitmap,\n+ * copies existing data, and updates metadata.\n+ *\n+ * Return: 0 on success, or a negative errno value on failure.\n+ */\n+static int quic_pnspace_grow(struct quic_pnspace *space, u16 size, gfp_t gfp)\n+{\n+\tu16 len, inc, offset;\n+\tunsigned long *new;\n+\n+\tinc = ALIGN((size - space-\u003epn_map_len), BITS_PER_LONG) +\n+\t QUIC_PN_MAP_INCREMENT;\n+\tlen = (u16)min(space-\u003epn_map_len + inc, QUIC_PN_MAP_SIZE);\n+\n+\tnew = kzalloc(BITS_TO_BYTES(len), gfp);\n+\tif (!new)\n+\t\treturn -ENOMEM;\n+\n+\toffset = (u16)(space-\u003emax_pn_seen + 1 - space-\u003ebase_pn);\n+\tbitmap_copy(new, space-\u003epn_map, offset);\n+\tkfree(space-\u003epn_map);\n+\tspace-\u003epn_map = new;\n+\tspace-\u003epn_map_len = len;\n+\n+\treturn 0;\n+}\n+\n+/* Check if a packet number has been received.\n+ *\n+ * Returns: 0 if the packet number has not been received. 1 if it has already\n+ * been received. -EINVAL if the packet number is invalid (out of range 0 to\n+ * QUIC_PN_MAX) or too old to track (below min_pn_seen).\n+ */\n+int quic_pnspace_check(struct quic_pnspace *space, s64 pn)\n+{\n+\tif (pn \u003e QUIC_PN_MAX || pn \u003c 0)\n+\t\treturn -EINVAL;\n+\n+\tif (space-\u003ebase_pn == -1) /* No packet number received yet. */\n+\t\treturn 0;\n+\n+\tif (pn \u003c space-\u003emin_pn_seen)\n+\t\treturn -EINVAL;\n+\n+\tif (pn \u003c space-\u003ebase_pn)\n+\t\treturn 1;\n+\tif (pn - space-\u003ebase_pn \u003c space-\u003epn_map_len \u0026\u0026\n+\t test_bit(pn - space-\u003ebase_pn, space-\u003epn_map))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n+/* Advance base_pn past contiguous received packet numbers. Finds the next gap\n+ * (unreceived packet) beyond @pn, shifts the bitmap, and updates base_pn\n+ * accordingly.\n+ */\n+static void quic_pnspace_move(struct quic_pnspace *space, s64 pn)\n+{\n+\tu16 offset;\n+\n+\toffset = (u16)(pn + 1 - space-\u003ebase_pn);\n+\toffset = (u16)find_next_zero_bit(space-\u003epn_map, space-\u003epn_map_len,\n+\t\t\t\t\t offset);\n+\tspace-\u003ebase_pn += offset;\n+\tbitmap_shift_right(space-\u003epn_map, space-\u003epn_map, offset,\n+\t\t\t space-\u003epn_map_len);\n+}\n+\n+/* Mark a packet number as received. Updates the packet number map to record\n+ * reception of @pn. Advances base_pn if possible, and updates max/min/last\n+ * seen fields as needed.\n+ *\n+ * Returns: 0 on success or if the packet was already marked, or a negative\n+ * error returned by bitmap growth when expanding the map.\n+ */\n+int quic_pnspace_mark(struct quic_pnspace *space, s64 pn, gfp_t gfp)\n+{\n+\ts64 last_max_pn_seen, off;\n+\tu64 last_max_pn_time;\n+\tbool has_gap;\n+\tint err;\n+\n+\tif (space-\u003ebase_pn == -1) {\n+\t\t/* Initialize base_pn based on the peer's first packet number\n+\t\t * since peer's packet numbers may start at a non-zero value.\n+\t\t */\n+\t\tquic_pnspace_set_base_pn(space, pn + 1);\n+\t\treturn 0;\n+\t}\n+\n+\t/* Ignore packets with number less than current base (already\n+\t * processed).\n+\t */\n+\tif (pn \u003c space-\u003ebase_pn)\n+\t\treturn 0;\n+\n+\t/* If offset is beyond current map length, try to grow the bitmap to\n+\t * accommodate.\n+\t */\n+\toff = pn - space-\u003ebase_pn;\n+\tif (off \u003e= space-\u003epn_map_len) {\n+\t\tif (off \u003e= QUIC_PN_MAP_SIZE) {\n+\t\t\tbitmap_zero(space-\u003epn_map, space-\u003epn_map_len);\n+\t\t\tquic_pnspace_set_base_pn(space, pn + 1);\n+\t\t\treturn 0;\n+\t\t}\n+\t\terr = quic_pnspace_grow(space, off + 1, gfp);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\thas_gap = quic_pnspace_has_gap(space);\n+\tif (space-\u003emax_pn_seen \u003c pn) {\n+\t\tspace-\u003emax_pn_seen = pn;\n+\t\tspace-\u003emax_pn_time = space-\u003etime;\n+\t}\n+\n+\tif (space-\u003ebase_pn == pn) { /* PN is next expected packet. */\n+\t\tif (has_gap) /* Advance to next gap. */\n+\t\t\tquic_pnspace_move(space, pn);\n+\t\telse /* Fast path: increment base_pn if no gaps. */\n+\t\t\tspace-\u003ebase_pn++;\n+\t} else { /* Mark this packet as received in the bitmap. */\n+\t\tset_bit(off, space-\u003epn_map);\n+\t}\n+\n+\t/* Only update min and last_max_pn_seen if this packet is the current\n+\t * max_pn.\n+\t */\n+\tif (space-\u003emax_pn_seen != pn)\n+\t\treturn 0;\n+\n+\t/* Check if enough time has elapsed or enough packets have been\n+\t * received to update tracking.\n+\t */\n+\tlast_max_pn_seen = min_t(s64, space-\u003elast_max_pn_seen, space-\u003ebase_pn);\n+\tlast_max_pn_time = space-\u003elast_max_pn_time;\n+\tif (space-\u003emax_pn_time \u003c last_max_pn_time + space-\u003emax_time_limit \u0026\u0026\n+\t space-\u003emax_pn_seen \u003c= last_max_pn_seen + QUIC_PN_MAP_LIMIT)\n+\t\treturn 0;\n+\n+\t/* Advance base_pn if last_max_pn_seen is ahead of current base_pn.\n+\t * This is needed because QUIC doesn't retransmit packets;\n+\t * retransmitted frames are carried in new packets, so we move forward.\n+\t */\n+\tif (space-\u003elast_max_pn_seen + 1 \u003e space-\u003ebase_pn)\n+\t\tquic_pnspace_move(space, space-\u003elast_max_pn_seen);\n+\n+\tspace-\u003emin_pn_seen = space-\u003elast_max_pn_seen;\n+\tspace-\u003elast_max_pn_seen = space-\u003emax_pn_seen;\n+\tspace-\u003elast_max_pn_time = space-\u003emax_pn_time;\n+\treturn 0;\n+}\n+\n+/* Find the next gap in received packet numbers. Scans pn_map for a gap\n+ * starting from *@iter. A gap is a contiguous block of unreceived packets\n+ * between received ones.\n+ *\n+ * Returns: true if a gap was found, false if no more gaps exist or are\n+ * relevant.\n+ */\n+static bool quic_pnspace_next_gap_ack(const struct quic_pnspace *space,\n+\t\t\t\t s64 *iter, u16 *start, u16 *end)\n+{\n+\tu16 start_ = 0, end_ = 0, offset = (u16)(*iter - space-\u003ebase_pn);\n+\n+\tstart_ = (u16)find_next_zero_bit(space-\u003epn_map, space-\u003epn_map_len,\n+\t\t\t\t\t offset);\n+\tif (space-\u003emax_pn_seen \u003c= space-\u003ebase_pn + start_)\n+\t\treturn false;\n+\n+\tend_ = (u16)find_next_bit(space-\u003epn_map, space-\u003epn_map_len, start_);\n+\tif (space-\u003emax_pn_seen \u003c= space-\u003ebase_pn + end_ - 1)\n+\t\treturn false;\n+\n+\t*start = start_ + 1;\n+\t*end = end_;\n+\t*iter = space-\u003ebase_pn + *end;\n+\treturn true;\n+}\n+\n+/* Generate gap acknowledgment blocks (GABs). GABs describe ranges of\n+ * unacknowledged packets between received ones, and are used in ACK frames.\n+ *\n+ * This function uses a sliding window approach to ensure the most recent gaps\n+ * are preserved when the total number exceeds QUIC_PN_MAP_MAX_GABS. If there\n+ * are more gaps than the limit, the oldest gaps are merged into a single gap,\n+ * and the newest (QUIC_PN_MAP_MAX_GABS) gaps are preserved individually.\n+ *\n+ * Returns: Number of generated GABs (up to QUIC_PN_MAP_MAX_GABS).\n+ */\n+u16 quic_pnspace_num_gabs(struct quic_pnspace *space,\n+\t\t\t struct quic_gap_ack_block *gabs)\n+{\n+\tstruct quic_gap_ack_block tmp[QUIC_PN_MAP_MAX_GABS];\n+\tu16 start, end, mstart = 0, ngaps = 0, i = 0;\n+\ts64 iter;\n+\n+\tif (!quic_pnspace_has_gap(space))\n+\t\treturn 0;\n+\n+\titer = space-\u003ebase_pn;\n+\n+\t/* Scan all gaps using a sliding window to keep the newest ones. */\n+\twhile (quic_pnspace_next_gap_ack(space, \u0026iter, \u0026start, \u0026end)) {\n+\t\tif (ngaps \u003c QUIC_PN_MAP_MAX_GABS) {\n+\t\t\tif (ngaps == 0)\n+\t\t\t\tmstart = start;\n+\t\t\tgabs[ngaps].start = start;\n+\t\t\tgabs[ngaps].end = end;\n+\t\t\tngaps++;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tgabs[i].start = start;\n+\t\tgabs[i].end = end;\n+\t\ti = (i + 1) % QUIC_PN_MAP_MAX_GABS;\n+\t}\n+\tif (i == 0) {\n+\t\tif (ngaps == QUIC_PN_MAP_MAX_GABS)\n+\t\t\tgabs[0].start = mstart;\n+\t\treturn ngaps;\n+\t}\n+\n+\t/* Overflow occurred: merge all discarded gaps with the gap at\n+\t * position i (which hasn't been overwritten yet) into one.\n+\t */\n+\tgabs[i].start = mstart;\n+\tmemcpy(tmp, gabs, ngaps * sizeof(*gabs));\n+\tmemcpy(gabs, \u0026tmp[i], (ngaps - i) * sizeof(*gabs));\n+\tmemcpy(gabs + (ngaps - i), tmp, i * sizeof(*gabs));\n+\n+\treturn ngaps;\n+}\ndiff --git a/net/quic/pnspace.h b/net/quic/pnspace.h\nnew file mode 100644\nindex 0000000000000..15ce6d2ef7269\n--- /dev/null\n+++ b/net/quic/pnspace.h\n@@ -0,0 +1,201 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#define QUIC_PN_MAP_MAX_GABS\t32\n+\n+#define QUIC_PN_MAP_INITIAL\t64\n+#define QUIC_PN_MAP_INCREMENT\tQUIC_PN_MAP_INITIAL\n+#define QUIC_PN_MAP_SIZE\t4096\n+#define QUIC_PN_MAP_LIMIT\t(QUIC_PN_MAP_SIZE * 3 / 4)\n+\n+#define QUIC_PNSPACE_MAX\t(QUIC_CRYPTO_MAX - 1)\n+#define QUIC_PNSPACE_NEXT_PN\t0\n+#define QUIC_PNSPACE_TIME_LIMIT\t(333000 * 3)\n+\n+enum {\n+\tQUIC_ECN_ECT1,\n+\tQUIC_ECN_ECT0,\n+\tQUIC_ECN_CE,\n+\tQUIC_ECN_MAX\n+};\n+\n+enum {\n+\tQUIC_ECN_LOCAL, /* ECN bits from incoming IP headers */\n+\tQUIC_ECN_PEER, /* ECN bits reported by peer in ACK frames */\n+\tQUIC_ECN_ACKED, /* ECN bits from packets newly ACKed */\n+\tQUIC_ECN_DIR_MAX\n+};\n+\n+/* Represents a gap (range of missing packets) in the ACK map. The values are\n+ * offsets from base_pn, with both 'start' and 'end' being +1.\n+ */\n+struct quic_gap_ack_block {\n+\tu16 start;\n+\tu16 end;\n+};\n+\n+/* Packet Number Map (pn_map) Layout:\n+ *\n+ * min_pn_seen --\u003e++-----------------------+---------------------+---\n+ * base_pn -----^ last_max_pn_seen --^ max_pn_seen --^\n+ *\n+ * Map Advancement Logic:\n+ * - min_pn_seen = last_max_pn_seen;\n+ * - base_pn = first zero bit after last_max_pn_seen;\n+ * - last_max_pn_seen = max_pn_seen;\n+ * - last_max_pn_time = current time;\n+ *\n+ * Conditions to Advance pn_map:\n+ * - (max_pn_time - last_max_pn_time) \u003e= max_time_limit, or\n+ * - (max_pn_seen - last_max_pn_seen) \u003e QUIC_PN_MAP_LIMIT\n+ *\n+ * Gap Search Range:\n+ * - From (base_pn - 1) to max_pn_seen\n+ */\n+struct quic_pnspace {\n+\t/* ECN counters indexed by dir and ECN codepoint (ECT1, ECT0, CE) */\n+\tu64 ecn_count[QUIC_ECN_DIR_MAX][QUIC_ECN_MAX];\n+\tunsigned long *pn_map; /* Received PN bitmap for ACK generation */\n+\tu16 pn_map_len; /* Length of the PN bit map (in bits) */\n+\tu8 need_sack; /* Flag indicating a SACK frame should be sent */\n+\tu8 sack_path; /* Path used for sending the SACK frame */\n+\tu8 sack_pending; /* Delayed ACK pending */\n+\n+\ts64 last_max_pn_seen; /* Largest PN seen before pn_map advance */\n+\tu64 last_max_pn_time; /* Timestamp last_max_pn_seen was received */\n+\ts64 min_pn_seen; /* Smallest PN received */\n+\ts64 max_pn_seen; /* Largest PN received */\n+\tu64 max_pn_time; /* Timestamp max_pn_seen was received */\n+\ts64 base_pn; /* PN corresponding to the start of the pn_map */\n+\tu64 time; /* Cached now, or latest socket accept timestamp */\n+\n+\ts64 max_pn_acked_seen; /* Largest PN ACKed by peer */\n+\tu64 max_pn_acked_time; /* Timestamp max_pn_acked_seen was ACKed */\n+\tu64 last_sent_time; /* Timestamp last ack-eliciting packet sent */\n+\tu64 loss_time; /* Timestamp the packet can be declared lost */\n+\ts64 next_pn; /* Next PN to send */\n+\n+\tu32 max_time_limit; /* Time threshold to trigger pn_map advance */\n+\tu32 inflight; /* Ack-eliciting bytes in flight */\n+};\n+\n+static inline void\n+quic_pnspace_set_max_pn_acked_seen(struct quic_pnspace *space,\n+\t\t\t\t s64 max_pn_acked_seen)\n+{\n+\tif (space-\u003emax_pn_acked_seen \u003e= max_pn_acked_seen)\n+\t\treturn;\n+\tspace-\u003emax_pn_acked_seen = max_pn_acked_seen;\n+\tspace-\u003emax_pn_acked_time = quic_ktime_get_us();\n+}\n+\n+static inline void quic_pnspace_set_base_pn(struct quic_pnspace *space, s64 pn)\n+{\n+\tspace-\u003ebase_pn = pn;\n+\tspace-\u003emax_pn_seen = space-\u003ebase_pn - 1;\n+\tspace-\u003elast_max_pn_seen = space-\u003emax_pn_seen;\n+\tspace-\u003emin_pn_seen = space-\u003emax_pn_seen;\n+\n+\tspace-\u003emax_pn_time = space-\u003etime;\n+\tspace-\u003elast_max_pn_time = space-\u003emax_pn_time;\n+}\n+\n+static inline bool quic_pnspace_has_gap(const struct quic_pnspace *space)\n+{\n+\treturn space-\u003ebase_pn != space-\u003emax_pn_seen + 1;\n+}\n+\n+static inline void quic_pnspace_inc_ecn_acked(struct quic_pnspace *space,\n+\t\t\t\t\t u8 ecn)\n+{\n+\tif (!ecn)\n+\t\treturn;\n+\tspace-\u003eecn_count[QUIC_ECN_ACKED][ecn - 1]++;\n+}\n+\n+static inline void quic_pnspace_reset_ecn_acked(struct quic_pnspace *space)\n+{\n+\tspace-\u003eecn_count[QUIC_ECN_ACKED][QUIC_ECN_ECT0] = 0;\n+\tspace-\u003eecn_count[QUIC_ECN_ACKED][QUIC_ECN_ECT1] = 0;\n+\tspace-\u003eecn_count[QUIC_ECN_ACKED][QUIC_ECN_CE] = 0;\n+}\n+\n+static inline void quic_pnspace_inc_ecn_local(struct quic_pnspace *space,\n+\t\t\t\t\t u8 ecn)\n+{\n+\tif (!ecn)\n+\t\treturn;\n+\tspace-\u003eecn_count[QUIC_ECN_LOCAL][ecn - 1]++;\n+}\n+\n+/* Check if any ECN-marked packets were received. */\n+static inline bool quic_pnspace_has_ecn_local(struct quic_pnspace *space)\n+{\n+\treturn space-\u003eecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT0] ||\n+\t space-\u003eecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT1] ||\n+\t space-\u003eecn_count[QUIC_ECN_LOCAL][QUIC_ECN_CE];\n+}\n+\n+/* Validate ECN counts received in an ACK. */\n+static inline bool quic_pnspace_validate_ecn(struct quic_pnspace *space,\n+\t\t\t\t\t u64 *ecn_count)\n+{\n+\tu64 *acked = space-\u003eecn_count[QUIC_ECN_ACKED];\n+\tu64 *peer = space-\u003eecn_count[QUIC_ECN_PEER];\n+\tu64 ect0, ect1, ce;\n+\n+\tif (peer[QUIC_ECN_ECT0] \u003e ecn_count[QUIC_ECN_ECT0] ||\n+\t peer[QUIC_ECN_ECT1] \u003e ecn_count[QUIC_ECN_ECT1] ||\n+\t peer[QUIC_ECN_CE] \u003e ecn_count[QUIC_ECN_CE])\n+\t\treturn false;\n+\n+\t/* rfc9000#section-13.4.2.1:\n+\t *\n+\t * ECN validation also fails if the sum of the increase in ECT(0) and\n+\t * ECN-CE counts is less than the number of newly acknowledged packets\n+\t * that were originally sent with an ECT(0) marking (Same for ECT(1)).\n+\t */\n+\tect0 = ecn_count[QUIC_ECN_ECT0] - peer[QUIC_ECN_ECT0];\n+\tect1 = ecn_count[QUIC_ECN_ECT1] - peer[QUIC_ECN_ECT1];\n+\tce = ecn_count[QUIC_ECN_CE] - peer[QUIC_ECN_CE];\n+\n+\treturn ect0 + ce \u003e= acked[QUIC_ECN_ECT0] \u0026\u0026\n+\t ect1 + ce \u003e= acked[QUIC_ECN_ECT1];\n+}\n+\n+/* Updates the stored ECN counters based on values received in the peer's ACK\n+ * frame. Each counter is updated only if the new value is higher.\n+ *\n+ * Returns: true if CE count was increased (congestion indicated), false\n+ * otherwise.\n+ */\n+static inline bool quic_pnspace_set_ecn_peer(struct quic_pnspace *space,\n+\t\t\t\t\t u64 *ecn_count)\n+{\n+\tu64 *count = space-\u003eecn_count[QUIC_ECN_PEER];\n+\n+\tif (count[QUIC_ECN_ECT0] \u003c ecn_count[QUIC_ECN_ECT0])\n+\t\tcount[QUIC_ECN_ECT0] = ecn_count[QUIC_ECN_ECT0];\n+\tif (count[QUIC_ECN_ECT1] \u003c ecn_count[QUIC_ECN_ECT1])\n+\t\tcount[QUIC_ECN_ECT1] = ecn_count[QUIC_ECN_ECT1];\n+\tif (count[QUIC_ECN_CE] \u003c ecn_count[QUIC_ECN_CE]) {\n+\t\tcount[QUIC_ECN_CE] = ecn_count[QUIC_ECN_CE];\n+\t\treturn true;\n+\t}\n+\treturn false;\n+}\n+\n+u16 quic_pnspace_num_gabs(struct quic_pnspace *space,\n+\t\t\t struct quic_gap_ack_block *gabs);\n+int quic_pnspace_check(struct quic_pnspace *space, s64 pn);\n+int quic_pnspace_mark(struct quic_pnspace *space, s64 pn, gfp_t gfp);\n+\n+void quic_pnspace_free(struct quic_pnspace *space);\n+int quic_pnspace_init(struct quic_pnspace *space);\ndiff --git a/net/quic/protocol.c b/net/quic/protocol.c\nnew file mode 100644\nindex 0000000000000..fceb4d5723a3b\n--- /dev/null\n+++ b/net/quic/protocol.c\n@@ -0,0 +1,403 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003cnet/inet_common.h\u003e\n+#include \u003clinux/proc_fs.h\u003e\n+#include \u003cnet/protocol.h\u003e\n+#include \u003cnet/rps.h\u003e\n+#include \u003cnet/tls.h\u003e\n+\n+#include \"socket.h\"\n+\n+static unsigned int quic_net_id __read_mostly;\n+\n+struct percpu_counter quic_sockets_allocated;\n+struct workqueue_struct *quic_wq;\n+\n+DEFINE_STATIC_KEY_FALSE(quic_alpn_demux_key);\n+\n+long sysctl_quic_mem[3];\n+int sysctl_quic_rmem[3];\n+int sysctl_quic_wmem[3];\n+\n+static int quic_inet_connect(struct socket *sock, struct sockaddr_unsized *addr,\n+\t\t\t int addr_len, int flags)\n+{\n+\tstruct sock *sk = sock-\u003esk;\n+\n+\tif (addr_len \u003c (int)sizeof(addr-\u003esa_family))\n+\t\treturn -EINVAL;\n+\n+\treturn sk-\u003esk_prot-\u003econnect(sk, addr, addr_len);\n+}\n+\n+static int quic_inet_listen(struct socket *sock, int backlog)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_inet_getname(struct socket *sock, struct sockaddr *uaddr,\n+\t\t\t int peer)\n+{\n+\treturn quic_get_sk_addr(sock, uaddr, peer);\n+}\n+\n+static __poll_t quic_inet_poll(struct file *file, struct socket *sock,\n+\t\t\t poll_table *wait)\n+{\n+\treturn 0;\n+}\n+\n+struct quic_net *quic_net(struct net *net)\n+{\n+\treturn net_generic(net, quic_net_id);\n+}\n+\n+#if IS_ENABLED(CONFIG_PROC_FS)\n+static const struct snmp_mib quic_snmp_list[] = {\n+\tSNMP_MIB_ITEM(\"QuicConnCurrentEstabs\", QUIC_MIB_CONN_CURRENTESTABS),\n+\tSNMP_MIB_ITEM(\"QuicConnPassiveEstabs\", QUIC_MIB_CONN_PASSIVEESTABS),\n+\tSNMP_MIB_ITEM(\"QuicConnActiveEstabs\", QUIC_MIB_CONN_ACTIVEESTABS),\n+\tSNMP_MIB_ITEM(\"QuicPktRcvFastpaths\", QUIC_MIB_PKT_RCVFASTPATHS),\n+\tSNMP_MIB_ITEM(\"QuicPktDecFastpaths\", QUIC_MIB_PKT_DECFASTPATHS),\n+\tSNMP_MIB_ITEM(\"QuicPktEncFastpaths\", QUIC_MIB_PKT_ENCFASTPATHS),\n+\tSNMP_MIB_ITEM(\"QuicPktRcvBacklogs\", QUIC_MIB_PKT_RCVBACKLOGS),\n+\tSNMP_MIB_ITEM(\"QuicPktDecBacklogs\", QUIC_MIB_PKT_DECBACKLOGS),\n+\tSNMP_MIB_ITEM(\"QuicPktEncBacklogs\", QUIC_MIB_PKT_ENCBACKLOGS),\n+\tSNMP_MIB_ITEM(\"QuicPktInvHdrDrop\", QUIC_MIB_PKT_INVHDRDROP),\n+\tSNMP_MIB_ITEM(\"QuicPktInvNumDrop\", QUIC_MIB_PKT_INVNUMDROP),\n+\tSNMP_MIB_ITEM(\"QuicPktInvFrmDrop\", QUIC_MIB_PKT_INVFRMDROP),\n+\tSNMP_MIB_ITEM(\"QuicPktRcvDrop\", QUIC_MIB_PKT_RCVDROP),\n+\tSNMP_MIB_ITEM(\"QuicPktDecDrop\", QUIC_MIB_PKT_DECDROP),\n+\tSNMP_MIB_ITEM(\"QuicPktEncDrop\", QUIC_MIB_PKT_ENCDROP),\n+\tSNMP_MIB_ITEM(\"QuicFrmRcvBufDrop\", QUIC_MIB_FRM_RCVBUFDROP),\n+\tSNMP_MIB_ITEM(\"QuicFrmRetrans\", QUIC_MIB_FRM_RETRANS),\n+\tSNMP_MIB_ITEM(\"QuicFrmOutCloses\", QUIC_MIB_FRM_OUTCLOSES),\n+\tSNMP_MIB_ITEM(\"QuicFrmInCloses\", QUIC_MIB_FRM_INCLOSES),\n+};\n+\n+static int quic_snmp_seq_show(struct seq_file *seq, void *v)\n+{\n+\tunsigned long buff[ARRAY_SIZE(quic_snmp_list)];\n+\tconst int cnt = ARRAY_SIZE(quic_snmp_list);\n+\tstruct net *net = seq-\u003eprivate;\n+\tu32 idx;\n+\n+\tmemset(buff, 0, sizeof(buff));\n+\n+\tsnmp_get_cpu_field_batch_cnt(buff, quic_snmp_list, cnt,\n+\t\t\t\t quic_net(net)-\u003estat);\n+\tfor (idx = 0; idx \u003c cnt; idx++)\n+\t\tseq_printf(seq, \"%-32s\\t%lu\\n\", quic_snmp_list[idx].name,\n+\t\t\t buff[idx]);\n+\n+\treturn 0;\n+}\n+\n+static int quic_net_proc_init(struct net *net)\n+{\n+\tquic_net(net)-\u003eproc_net = proc_net_mkdir(net, \"quic\", net-\u003eproc_net);\n+\tif (!quic_net(net)-\u003eproc_net)\n+\t\treturn -ENOMEM;\n+\n+\tif (!proc_create_net_single(\"snmp\", 0444, quic_net(net)-\u003eproc_net,\n+\t\t\t\t quic_snmp_seq_show, NULL))\n+\t\tgoto free;\n+\treturn 0;\n+free:\n+\tremove_proc_subtree(\"quic\", net-\u003eproc_net);\n+\tquic_net(net)-\u003eproc_net = NULL;\n+\treturn -ENOMEM;\n+}\n+\n+static void quic_net_proc_exit(struct net *net)\n+{\n+\tremove_proc_subtree(\"quic\", net-\u003eproc_net);\n+\tquic_net(net)-\u003eproc_net = NULL;\n+}\n+#endif\n+\n+static const struct proto_ops quic_proto_ops = {\n+\t.family\t\t = PF_INET,\n+\t.owner\t\t = THIS_MODULE,\n+\t.release\t = inet_release,\n+\t.bind\t\t = inet_bind,\n+\t.connect\t = quic_inet_connect,\n+\t.socketpair\t = sock_no_socketpair,\n+\t.accept\t\t = inet_accept,\n+\t.getname\t = quic_inet_getname,\n+\t.poll\t\t = quic_inet_poll,\n+\t.ioctl\t\t = inet_ioctl,\n+\t.gettstamp\t = sock_gettstamp,\n+\t.listen\t\t = quic_inet_listen,\n+\t.shutdown\t = inet_shutdown,\n+\t.setsockopt\t = sock_common_setsockopt,\n+\t.getsockopt\t = sock_common_getsockopt,\n+\t.sendmsg\t = inet_sendmsg,\n+\t.recvmsg\t = inet_recvmsg,\n+\t.mmap\t\t = sock_no_mmap,\n+};\n+\n+static struct inet_protosw quic_stream_protosw = {\n+\t.type = SOCK_STREAM,\n+\t.protocol = IPPROTO_QUIC,\n+\t.prot = \u0026quic_prot,\n+\t.ops = \u0026quic_proto_ops,\n+};\n+\n+static struct inet_protosw quic_dgram_protosw = {\n+\t.type = SOCK_DGRAM,\n+\t.protocol = IPPROTO_QUIC,\n+\t.prot = \u0026quic_prot,\n+\t.ops = \u0026quic_proto_ops,\n+};\n+\n+static const struct proto_ops quicv6_proto_ops = {\n+\t.family\t\t = PF_INET6,\n+\t.owner\t\t = THIS_MODULE,\n+\t.release\t = inet6_release,\n+\t.bind\t\t = inet6_bind,\n+\t.connect\t = quic_inet_connect,\n+\t.socketpair\t = sock_no_socketpair,\n+\t.accept\t\t = inet_accept,\n+\t.getname\t = quic_inet_getname,\n+\t.poll\t\t = quic_inet_poll,\n+\t.ioctl\t\t = inet6_ioctl,\n+\t.gettstamp\t = sock_gettstamp,\n+\t.listen\t\t = quic_inet_listen,\n+\t.shutdown\t = inet_shutdown,\n+\t.setsockopt\t = sock_common_setsockopt,\n+\t.getsockopt\t = sock_common_getsockopt,\n+\t.sendmsg\t = inet_sendmsg,\n+\t.recvmsg\t = inet_recvmsg,\n+\t.mmap\t\t = sock_no_mmap,\n+};\n+\n+static struct inet_protosw quicv6_stream_protosw = {\n+\t.type = SOCK_STREAM,\n+\t.protocol = IPPROTO_QUIC,\n+\t.prot = \u0026quicv6_prot,\n+\t.ops = \u0026quicv6_proto_ops,\n+};\n+\n+static struct inet_protosw quicv6_dgram_protosw = {\n+\t.type = SOCK_DGRAM,\n+\t.protocol = IPPROTO_QUIC,\n+\t.prot = \u0026quicv6_prot,\n+\t.ops = \u0026quicv6_proto_ops,\n+};\n+\n+static int quic_protosw_init(void)\n+{\n+\tint err;\n+\n+\terr = proto_register(\u0026quic_prot, 1);\n+\tif (err)\n+\t\treturn err;\n+\n+\terr = proto_register(\u0026quicv6_prot, 1);\n+\tif (err) {\n+\t\tproto_unregister(\u0026quic_prot);\n+\t\treturn err;\n+\t}\n+\n+\tinet_register_protosw(\u0026quic_stream_protosw);\n+\tinet_register_protosw(\u0026quic_dgram_protosw);\n+\tinet6_register_protosw(\u0026quicv6_stream_protosw);\n+\tinet6_register_protosw(\u0026quicv6_dgram_protosw);\n+\n+\treturn 0;\n+}\n+\n+static void quic_protosw_exit(void)\n+{\n+\tinet_unregister_protosw(\u0026quic_dgram_protosw);\n+\tinet_unregister_protosw(\u0026quic_stream_protosw);\n+\tproto_unregister(\u0026quic_prot);\n+\n+\tinet6_unregister_protosw(\u0026quicv6_dgram_protosw);\n+\tinet6_unregister_protosw(\u0026quicv6_stream_protosw);\n+\tproto_unregister(\u0026quicv6_prot);\n+}\n+\n+static int __net_init quic_net_init(struct net *net)\n+{\n+\tstruct quic_net *qn = quic_net(net);\n+\tint err = 0;\n+\n+\tqn-\u003estat = alloc_percpu(struct quic_mib);\n+\tif (!qn-\u003estat)\n+\t\treturn -ENOMEM;\n+\n+#if IS_ENABLED(CONFIG_PROC_FS)\n+\terr = quic_net_proc_init(net);\n+\tif (err) {\n+\t\tfree_percpu(qn-\u003estat);\n+\t\tqn-\u003estat = NULL;\n+\t}\n+#endif\n+\treturn err;\n+}\n+\n+static void __net_exit quic_net_exit(struct net *net)\n+{\n+\tstruct quic_net *qn = quic_net(net);\n+\n+#if IS_ENABLED(CONFIG_PROC_FS)\n+\tquic_net_proc_exit(net);\n+#endif\n+\tfree_percpu(qn-\u003estat);\n+\tqn-\u003estat = NULL;\n+}\n+\n+static struct pernet_operations quic_net_ops = {\n+\t.init = quic_net_init,\n+\t.exit = quic_net_exit,\n+\t.id = \u0026quic_net_id,\n+\t.size = sizeof(struct quic_net),\n+};\n+\n+#if IS_ENABLED(CONFIG_SYSCTL)\n+static struct ctl_table_header *quic_sysctl_header;\n+\n+static struct ctl_table quic_table[] = {\n+\t{\n+\t\t.procname\t= \"quic_mem\",\n+\t\t.data\t\t= \u0026sysctl_quic_mem,\n+\t\t.maxlen\t\t= sizeof(sysctl_quic_mem),\n+\t\t.mode\t\t= 0644,\n+\t\t.proc_handler\t= proc_doulongvec_minmax\n+\t},\n+\t{\n+\t\t.procname\t= \"quic_rmem\",\n+\t\t.data\t\t= \u0026sysctl_quic_rmem,\n+\t\t.maxlen\t\t= sizeof(sysctl_quic_rmem),\n+\t\t.mode\t\t= 0644,\n+\t\t.proc_handler\t= proc_dointvec_minmax,\n+\t\t.extra1\t\t= SYSCTL_ONE,\n+\t},\n+\t{\n+\t\t.procname\t= \"quic_wmem\",\n+\t\t.data\t\t= \u0026sysctl_quic_wmem,\n+\t\t.maxlen\t\t= sizeof(sysctl_quic_wmem),\n+\t\t.mode\t\t= 0644,\n+\t\t.proc_handler\t= proc_dointvec_minmax,\n+\t\t.extra1\t\t= SYSCTL_ONE,\n+\t},\n+};\n+\n+static void quic_sysctl_register(void)\n+{\n+\tquic_sysctl_header = register_net_sysctl(\u0026init_net, \"net/quic\",\n+\t\t\t\t\t\t quic_table);\n+}\n+\n+static void quic_sysctl_unregister(void)\n+{\n+\tunregister_net_sysctl_table(quic_sysctl_header);\n+}\n+#endif\n+\n+static __init int quic_init(void)\n+{\n+\tint max_share, err = -ENOMEM;\n+\tunsigned long limit;\n+\n+\tBUILD_BUG_ON(sizeof(struct quic_skb_cb) \u003e\n+\t\t sizeof_field(struct sk_buff, cb));\n+\n+\t/* Set QUIC memory limits based on available system memory, similar to\n+\t * sctp_init().\n+\t */\n+\tlimit = nr_free_buffer_pages() / 8;\n+\tlimit = max(limit, 128UL);\n+\tsysctl_quic_mem[0] = (long)limit / 4 * 3;\n+\tsysctl_quic_mem[1] = (long)limit;\n+\tsysctl_quic_mem[2] = sysctl_quic_mem[0] * 2;\n+\n+\tlimit = (sysctl_quic_mem[1]) \u003c\u003c (PAGE_SHIFT - 7);\n+\tmax_share = min(4UL * 1024 * 1024, limit);\n+\n+\tsysctl_quic_rmem[0] = PAGE_SIZE;\n+\tsysctl_quic_rmem[1] = 1024 * 1024;\n+\tsysctl_quic_rmem[2] = max(sysctl_quic_rmem[1], max_share);\n+\n+\tsysctl_quic_wmem[0] = PAGE_SIZE;\n+\tsysctl_quic_wmem[1] = 16 * 1024;\n+\tsysctl_quic_wmem[2] = max(64 * 1024, max_share);\n+\n+\terr = percpu_counter_init(\u0026quic_sockets_allocated, 0, GFP_KERNEL);\n+\tif (err)\n+\t\tgoto err_percpu_counter;\n+\n+\terr = quic_hash_tables_init();\n+\tif (err)\n+\t\tgoto err_hash;\n+\n+\t/* Allocate an unbound workqueue for UDP socket destruction and backlog\n+\t * packet processing.\n+\t */\n+\tquic_wq = alloc_workqueue(\"quic_workqueue\", WQ_UNBOUND, 0);\n+\tif (!quic_wq) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err_wq;\n+\t}\n+\n+\terr = register_pernet_subsys(\u0026quic_net_ops);\n+\tif (err)\n+\t\tgoto err_def_ops;\n+\n+\terr = quic_protosw_init();\n+\tif (err)\n+\t\tgoto err_protosw;\n+\n+#if IS_ENABLED(CONFIG_SYSCTL)\n+\tquic_sysctl_register();\n+#endif\n+\tpr_info(\"quic: init\\n\");\n+\treturn 0;\n+\n+err_protosw:\n+\tunregister_pernet_subsys(\u0026quic_net_ops);\n+err_def_ops:\n+\tdestroy_workqueue(quic_wq);\n+err_wq:\n+\tquic_hash_tables_destroy();\n+err_hash:\n+\tpercpu_counter_destroy(\u0026quic_sockets_allocated);\n+err_percpu_counter:\n+\treturn err;\n+}\n+\n+static __exit void quic_exit(void)\n+{\n+#if IS_ENABLED(CONFIG_SYSCTL)\n+\tquic_sysctl_unregister();\n+#endif\n+\tquic_protosw_exit();\n+\tunregister_pernet_subsys(\u0026quic_net_ops);\n+\tflush_workqueue(quic_wq);\n+\tdestroy_workqueue(quic_wq);\n+\tquic_hash_tables_destroy();\n+\tpercpu_counter_destroy(\u0026quic_sockets_allocated);\n+\trcu_barrier();\n+\tpr_info(\"quic: exit\\n\");\n+}\n+\n+module_init(quic_init);\n+module_exit(quic_exit);\n+\n+MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); /* IPPROTO_QUIC == 261 */\n+MODULE_ALIAS_NET_PF_PROTO(PF_INET6, 261);\n+MODULE_AUTHOR(\"Xin Long \u003clucien.xin@gmail.com\u003e\");\n+MODULE_DESCRIPTION(\"Support for the QUIC protocol (RFC9000)\");\n+MODULE_LICENSE(\"GPL\");\ndiff --git a/net/quic/protocol.h b/net/quic/protocol.h\nnew file mode 100644\nindex 0000000000000..fbd0fe39eccc6\n--- /dev/null\n+++ b/net/quic/protocol.h\n@@ -0,0 +1,57 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+extern struct percpu_counter quic_sockets_allocated;\n+\n+DECLARE_STATIC_KEY_FALSE(quic_alpn_demux_key);\n+\n+extern long sysctl_quic_mem[3];\n+extern int sysctl_quic_rmem[3];\n+extern int sysctl_quic_wmem[3];\n+\n+enum {\n+\tQUIC_MIB_NUM = 0,\n+\tQUIC_MIB_CONN_CURRENTESTABS, /* Current established connections */\n+\tQUIC_MIB_CONN_PASSIVEESTABS, /* Passively established connections */\n+\tQUIC_MIB_CONN_ACTIVEESTABS, /* Actively established connections */\n+\tQUIC_MIB_PKT_RCVFASTPATHS, /* Packets received on fast path */\n+\tQUIC_MIB_PKT_DECFASTPATHS, /* Packets decrypted on fast path */\n+\tQUIC_MIB_PKT_ENCFASTPATHS, /* Packets encrypted on fast path */\n+\tQUIC_MIB_PKT_RCVBACKLOGS, /* Packets processed via backlog */\n+\tQUIC_MIB_PKT_DECBACKLOGS, /* Packets decrypted in backlog */\n+\tQUIC_MIB_PKT_ENCBACKLOGS, /* Packets encrypted in backlog */\n+\tQUIC_MIB_PKT_INVHDRDROP, /* Dropped: invalid packet header */\n+\tQUIC_MIB_PKT_INVNUMDROP, /* Dropped: invalid packet number */\n+\tQUIC_MIB_PKT_INVFRMDROP, /* Dropped: invalid frame */\n+\tQUIC_MIB_PKT_RCVDROP, /* Dropped on receive (general) */\n+\tQUIC_MIB_PKT_DECDROP, /* Dropped: decryption failure */\n+\tQUIC_MIB_PKT_ENCDROP, /* Dropped: encryption failure */\n+\tQUIC_MIB_FRM_RCVBUFDROP, /* Frames dropped: recv buf limit */\n+\tQUIC_MIB_FRM_RETRANS, /* Frames retransmitted */\n+\tQUIC_MIB_FRM_OUTCLOSES, /* CONNECTION_CLOSE frames sent */\n+\tQUIC_MIB_FRM_INCLOSES, /* CONNECTION_CLOSE frames rcvd */\n+\tQUIC_MIB_MAX\n+};\n+\n+struct quic_mib {\n+\tunsigned long mibs[QUIC_MIB_MAX]; /* Counters indexed by QUIC_MIB_* */\n+};\n+\n+struct quic_net {\n+\tDEFINE_SNMP_STAT(struct quic_mib, stat); /* Per-net QUIC MIB stats */\n+#if IS_ENABLED(CONFIG_PROC_FS)\n+\tstruct proc_dir_entry *proc_net; /* procfs entry for QUIC stats */\n+#endif\n+};\n+\n+struct quic_net *quic_net(struct net *net);\n+\n+#define QUIC_INC_STATS(net, field) SNMP_INC_STATS(quic_net(net)-\u003estat, field)\n+#define QUIC_DEC_STATS(net, field) SNMP_DEC_STATS(quic_net(net)-\u003estat, field)\ndiff --git a/net/quic/socket.c b/net/quic/socket.c\nnew file mode 100644\nindex 0000000000000..0fcb123d31bba\n--- /dev/null\n+++ b/net/quic/socket.c\n@@ -0,0 +1,654 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003cnet/inet_common.h\u003e\n+#include \u003cnet/tls.h\u003e\n+\n+#include \"socket.h\"\n+\n+static DEFINE_PER_CPU(int, quic_memory_per_cpu_fw_alloc);\n+static unsigned long quic_memory_pressure;\n+static atomic_long_t quic_memory_allocated;\n+\n+static void quic_enter_memory_pressure(struct sock *sk)\n+{\n+\tWRITE_ONCE(quic_memory_pressure, 1);\n+}\n+\n+/* Check if a matching request sock already exists. Match is based on\n+ * source/destination addresses and DCID.\n+ */\n+struct quic_request_sock *quic_request_sock_lookup(struct sock *sk)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tstruct quic_request_sock *req;\n+\n+\tlist_for_each_entry(req, quic_reqs(sk), list) {\n+\t\tif (!memcmp(\u0026req-\u003esaddr, \u0026packet-\u003esaddr, sizeof(req-\u003esaddr)) \u0026\u0026\n+\t\t !memcmp(\u0026req-\u003edaddr, \u0026packet-\u003edaddr, sizeof(req-\u003edaddr)) \u0026\u0026\n+\t\t !quic_conn_id_cmp(\u0026req-\u003edcid, \u0026packet-\u003edcid))\n+\t\t\treturn req;\n+\t}\n+\treturn NULL;\n+}\n+\n+/* Create and enqueue a QUIC request sock for a new incoming connection. */\n+struct quic_request_sock *quic_request_sock_create(struct sock *sk,\n+\t\t\t\t\t\t struct quic_conn_id *odcid,\n+\t\t\t\t\t\t u8 retry, gfp_t gfp)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tstruct quic_request_sock *req;\n+\n+\tif (sk_acceptq_is_full(sk)) /* Refuse if accept queue full. */\n+\t\treturn ERR_PTR(-ENOBUFS);\n+\n+\treq = kmalloc_obj(*req, gfp);\n+\tif (!req)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\treq-\u003eversion = packet-\u003eversion;\n+\treq-\u003edaddr = packet-\u003edaddr;\n+\treq-\u003esaddr = packet-\u003esaddr;\n+\treq-\u003escid = packet-\u003escid;\n+\treq-\u003edcid = packet-\u003edcid;\n+\treq-\u003eorig_dcid = *odcid;\n+\treq-\u003eretry = retry;\n+\n+\tskb_queue_head_init(\u0026req-\u003ebacklog_list);\n+\treq-\u003eblen = 0;\n+\n+\t/* Enqueue request into listen socket’s pending list for accept(). */\n+\tlist_add_tail(\u0026req-\u003elist, quic_reqs(sk));\n+\tsk_acceptq_added(sk);\n+\treturn req;\n+}\n+\n+int quic_request_sock_backlog_tail(struct sock *sk,\n+\t\t\t\t struct quic_request_sock *req,\n+\t\t\t\t struct sk_buff *skb)\n+{\n+\tu32 limit = sk-\u003esk_rcvbuf / sk-\u003esk_max_ack_backlog;\n+\tint len = skb-\u003etruesize;\n+\n+\tlimit = max_t(u32, limit, QUIC_MIN_UDP_PAYLOAD * 4);\n+\tif (req-\u003eblen + len \u003e limit || !__sk_rmem_schedule(sk, len, false)) {\n+\t\tQUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);\n+\t\tkfree_skb(skb);\n+\t\treturn -ENOBUFS;\n+\t}\n+\n+\tQUIC_SKB_CB(skb)-\u003ebacklog = 1;\n+\tskb_set_owner_r(skb, sk);\n+\t__skb_queue_tail(\u0026req-\u003ebacklog_list, skb);\n+\treq-\u003eblen += len;\n+\n+\tsk-\u003esk_data_ready(sk);\n+\treturn 0;\n+}\n+\n+/* Check if a matching accept socket exists. This is needed because an accept\n+ * socket might have been created after this packet was enqueued in the listen\n+ * socket's backlog.\n+ */\n+bool quic_accept_sock_exists(struct sock *sk, struct sk_buff *skb)\n+{\n+\tstruct quic_packet *packet = quic_packet(sk);\n+\tbool exist = false;\n+\n+\t/* Skip if packet is newer than the last accept socket creation time.\n+\t * No matching socket could exist in this case.\n+\t */\n+\tif (QUIC_SKB_CB(skb)-\u003etime \u003e\n+\t quic_pnspace(sk, QUIC_CRYPTO_INITIAL)-\u003etime)\n+\t\treturn exist;\n+\n+\t/* Look up accepted socket matching packet addresses and DCID. */\n+\tlocal_bh_disable();\n+\tsk = quic_sock_lookup(skb, \u0026packet-\u003esaddr, \u0026packet-\u003edaddr,\n+\t\t\t quic_path_usock(quic_paths(sk), 0),\n+\t\t\t \u0026packet-\u003edcid);\n+\tif (!sk)\n+\t\tgoto out;\n+\n+\t/* Found a matching accept socket. Process packet with this socket. */\n+\tskb_orphan(skb);\n+\tbh_lock_sock_nested(sk);\n+\tif (sock_owned_by_user(sk)) {\n+\t\t/* Socket is busy (owned by user context): queue to backlog. */\n+\t\tif (sk_add_backlog(sk, skb, READ_ONCE(sk-\u003esk_rcvbuf))) {\n+\t\t\tQUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);\n+\t\t\tkfree_skb(skb);\n+\t\t}\n+\t} else {\n+\t\t/* Socket not busy: process immediately. */\n+\t\tsk-\u003esk_backlog_rcv(sk, skb); /* quic_packet_process(). */\n+\t}\n+\tbh_unlock_sock(sk);\n+\tsock_put(sk);\n+\texist = true;\n+out:\n+\tlocal_bh_enable();\n+\treturn exist;\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+\tunion quic_addr *path_sa, *path_da;\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, seq;\n+\tbool match;\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, \u0026head-\u003ehead) {\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+\n+\t\t/* Protect path[0] reads with seqcount retry to detect torn\n+\t\t * reads during concurrent quic_path_swap(). The seqcount\n+\t\t * ensures we either see a consistent old or new path, never\n+\t\t * a mix of both.\n+\t\t */\n+\t\tdo {\n+\t\t\tseq = read_seqcount_begin(\u0026paths-\u003epath_seq);\n+\t\t\tpath_sa = quic_path_saddr(paths, 0);\n+\t\t\tpath_da = quic_path_daddr(paths, 0);\n+\t\t\tmatch = (quic_cmp_sk_addr(tmp, path_sa, sa) \u0026\u0026\n+\t\t\t\t quic_cmp_sk_addr(tmp, path_da, da) \u0026\u0026\n+\t\t\t\t quic_path_usock(paths, 0) == usk \u0026\u0026\n+\t\t\t\t (!dcid || !quic_conn_id_cmp(odcid, dcid)));\n+\t\t} while (read_seqcount_retry(\u0026paths-\u003epath_seq, seq));\n+\n+\t\tif (match) {\n+\t\t\tsk = tmp;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\t/* If the final nulls value differs from the expected one, restart the\n+\t * lookup as the node may have been rehashed (e.g., due to connection\n+\t * migration).\n+\t */\n+\tif (!sk \u0026\u0026 get_nulls_value(node) != hash)\n+\t\tgoto begin;\n+\n+\tif (sk \u0026\u0026 unlikely(!refcount_inc_not_zero(\u0026sk-\u003esk_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, struct sock *usk,\n+\t\t\t\t struct quic_data *alpns)\n+{\n+\tstruct net *net = sock_net(usk);\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-\u003ev4.sin_port));\n+\thead = quic_listen_sock_head(hash);\n+\n+\trcu_read_lock();\n+\t/* Iterate sockets, checking ALPN requirements. Address specificity\n+\t * always takes precedence over ALPN preference order.\n+\t */\n+\tsk_nulls_for_each_rcu(tmp, node, \u0026head-\u003ehead) {\n+\t\tbool alpn_match = false;\n+\n+\t\ta = quic_path_saddr(quic_paths(tmp), 0);\n+\t\tif (net != sock_net(tmp) || !quic_cmp_sk_addr(tmp, a, sa) ||\n+\t\t quic_path_usock(quic_paths(tmp), 0) != usk)\n+\t\t\tcontinue;\n+\n+\t\tif (!alpns-\u003elen) {\n+\t\t\t/* No ALPN extension or empty ALPN list.\n+\t\t\t * If alpns-\u003edata is NULL, match any socket.\n+\t\t\t * If alpns-\u003edata is set (empty ALPN), only match\n+\t\t\t * sockets with no ALPN configured.\n+\t\t\t */\n+\t\t\talpn_match = (!alpns-\u003edata || !quic_alpn(tmp)-\u003elen);\n+\t\t} else {\n+\t\t\t/* Check if any client ALPN matches this socket. */\n+\t\t\tfor (p = alpns-\u003edata, len = alpns-\u003elen; len;\n+\t\t\t len -= length, p += length) {\n+\t\t\t\tquic_get_int(\u0026p, \u0026len, \u0026length, 1);\n+\t\t\t\tquic_data(\u0026alpn, p, length);\n+\t\t\t\tif (quic_data_has(quic_alpn(tmp), \u0026alpn)) {\n+\t\t\t\t\talpn_match = true;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tif (alpn_match) {\n+\t\t\tif (!quic_is_any_addr(a)) {\n+\t\t\t\t/* Specific address - best match. */\n+\t\t\t\tsk = tmp;\n+\t\t\t\tgoto out;\n+\t\t\t}\n+\t\t\t/* ANY address - keep as candidate. */\n+\t\t\tif (!sk || a-\u003esa.sa_family == sa-\u003esa.sa_family)\n+\t\t\t\tsk = tmp;\n+\t\t}\n+\t}\n+\t/* No need to check get_nulls_value(node) != hash for !sk, as\n+\t * hashtable size is fixed and a listen sk can not rehashed.\n+\t */\n+out:\n+\tif (sk \u0026\u0026 sk-\u003esk_reuseport)\n+\t\tsk = reuseport_select_sock(sk, quic_addr_hash(net, da), skb, 1);\n+\n+\tif (sk \u0026\u0026 unlikely(!refcount_inc_not_zero(\u0026sk-\u003esk_refcnt)))\n+\t\tsk = NULL;\n+\trcu_read_unlock();\n+\treturn sk;\n+}\n+\n+/* Switch packet to a different listening socket based on ALPN matching.\n+ *\n+ * When ALPN demultiplexing is enabled, this function attempts to find a\n+ * listening socket that matches the parsed ALPN. If a different socket is\n+ * found, the packet is switched to that socket for processing.\n+ *\n+ * Return: true if switched to a different socket, false otherwise.\n+ */\n+bool quic_listen_sock_switch(struct sk_buff *skb, struct quic_data *alpns)\n+{\n+\tstruct sock *nsk, *sk = skb-\u003esk;\n+\tstruct quic_packet *packet;\n+\n+\tif (!alpns-\u003edata)\n+\t\treturn false;\n+\n+\tlocal_bh_disable();\n+\tpacket = quic_packet(sk);\n+\tnsk = quic_listen_sock_lookup(skb, \u0026packet-\u003esaddr, \u0026packet-\u003edaddr,\n+\t\t\t\t quic_path_usock(quic_paths(sk), 0),\n+\t\t\t\t alpns);\n+\tif (!nsk)\n+\t\tgoto out;\n+\tif (nsk == sk) {\n+\t\tsock_put(nsk);\n+\t\tgoto out;\n+\t}\n+\tlocal_bh_enable();\n+\trelease_sock(sk);\n+\n+\tskb_orphan(skb);\n+\n+\tlock_sock(nsk);\n+\tnsk-\u003esk_backlog_rcv(nsk, skb); /* quic_packet_process(). */\n+\trelease_sock(nsk);\n+\tsock_put(nsk);\n+\n+\tlock_sock(sk);\n+\treturn true;\n+out:\n+\tlocal_bh_enable();\n+\treturn false;\n+}\n+\n+static void quic_write_space(struct sock *sk)\n+{\n+\t__poll_t mask = EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;\n+\tstruct socket_wq *wq;\n+\n+\t/* Do not check sock_writeable(). Also wakes stream-open waiters\n+\t * blocked on stream limits, where sock_writeable() may be false.\n+\t */\n+\trcu_read_lock();\n+\twq = rcu_dereference(sk-\u003esk_wq);\n+\tif (skwq_has_sleeper(wq))\n+\t\twake_up_interruptible_sync_poll(\u0026wq-\u003ewait, mask);\n+\tsk_wake_async_rcu(sk, SOCK_WAKE_SPACE, POLL_OUT);\n+\trcu_read_unlock();\n+}\n+\n+static void quic_sock_destruct(struct sock *sk)\n+{\n+\tu8 i;\n+\n+\t/* Deferred crypto free for async encryption/decryption. */\n+\tfor (i = 0; i \u003c QUIC_CRYPTO_MAX; i++)\n+\t\tquic_crypto_free(quic_crypto(sk, i));\n+\n+\t/* Deferred ALPN free for RCU readers in quic_listen_sock_lookup(). */\n+\tquic_data_free(quic_alpn(sk));\n+\n+\tquic_sk_destruct(sk);\n+}\n+\n+static int quic_init_sock(struct sock *sk)\n+{\n+\tu8 i;\n+\n+\tsk-\u003esk_destruct = quic_sock_destruct;\n+\tsk-\u003esk_write_space = quic_write_space;\n+\tsock_set_flag(sk, SOCK_USE_WRITE_QUEUE);\n+\n+\tsk_sockets_allocated_inc(sk);\n+\tsock_prot_inuse_add(sock_net(sk), sk-\u003esk_prot, 1);\n+\tINIT_LIST_HEAD(quic_reqs(sk));\n+\n+\tquic_conn_id_set_init(quic_source(sk), true);\n+\tquic_conn_id_set_init(quic_dest(sk), false);\n+\tquic_path_init(quic_paths(sk));\n+\tquic_cong_init(quic_cong(sk));\n+\n+\tquic_timer_init(sk);\n+\tquic_packet_init(sk);\n+\n+\tif (quic_stream_init(quic_streams(sk)))\n+\t\treturn -ENOMEM;\n+\n+\tfor (i = 0; i \u003c QUIC_PNSPACE_MAX; i++) {\n+\t\tif (quic_pnspace_init(quic_pnspace(sk, i)))\n+\t\t\treturn -ENOMEM;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void quic_destroy_sock(struct sock *sk)\n+{\n+\tu8 i;\n+\n+\tquic_timer_free(sk);\n+\tquic_packet_free(sk);\n+\n+\tfor (i = 0; i \u003c QUIC_PNSPACE_MAX; i++)\n+\t\tquic_pnspace_free(quic_pnspace(sk, i));\n+\n+\tquic_path_unbind(sk, quic_paths(sk), 0);\n+\tquic_path_unbind(sk, quic_paths(sk), 1);\n+\n+\tquic_conn_id_set_free(quic_source(sk));\n+\tquic_conn_id_set_free(quic_dest(sk));\n+\n+\tquic_stream_free(quic_streams(sk));\n+\n+\tquic_data_free(quic_ticket(sk));\n+\tquic_data_free(quic_token(sk));\n+\n+\tsk_sockets_allocated_dec(sk);\n+\tsock_prot_inuse_add(sock_net(sk), sk-\u003esk_prot, -1);\n+}\n+\n+static int quic_bind(struct sock *sk, struct sockaddr_unsized *addr,\n+\t\t int addr_len)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_connect(struct sock *sk, struct sockaddr_unsized *addr,\n+\t\t\tint addr_len)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_hash(struct sock *sk)\n+{\n+\treturn 0;\n+}\n+\n+static void quic_unhash(struct sock *sk)\n+{\n+}\n+\n+static int quic_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static int quic_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,\n+\t\t\tint flags)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static struct sock *quic_accept(struct sock *sk, struct proto_accept_arg *arg)\n+{\n+\targ-\u003eerr = -EOPNOTSUPP;\n+\treturn NULL;\n+}\n+\n+static void quic_close(struct sock *sk, long timeout)\n+{\n+\tlock_sock(sk);\n+\n+\tquic_set_state(sk, QUIC_SS_CLOSED);\n+\n+\trelease_sock(sk);\n+\n+\tsk_common_release(sk);\n+}\n+\n+/**\n+ * quic_do_setsockopt - set a QUIC socket option\n+ * @sk: socket to configure\n+ * @optname: option name (QUIC-level)\n+ * @optval: user buffer containing the option value\n+ * @optlen: size of the option value\n+ *\n+ * Sets a QUIC socket option on a given socket.\n+ *\n+ * Return:\n+ * - On success, 0 is returned.\n+ * - On error, a negative error value is returned.\n+ */\n+int quic_do_setsockopt(struct sock *sk, int optname, sockptr_t optval,\n+\t\t unsigned int optlen)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+EXPORT_SYMBOL_GPL(quic_do_setsockopt);\n+\n+static int quic_setsockopt(struct sock *sk, int level, int optname,\n+\t\t\t sockptr_t optval, unsigned int optlen)\n+{\n+\tif (level != SOL_QUIC)\n+\t\treturn quic_common_setsockopt(sk, level, optname, optval,\n+\t\t\t\t\t optlen);\n+\n+\treturn quic_do_setsockopt(sk, optname, optval, optlen);\n+}\n+\n+/**\n+ * quic_do_getsockopt - get a QUIC socket option\n+ * @sk: socket to query\n+ * @optname: option name (QUIC-level)\n+ * @optval: user buffer to receive the option value\n+ * @optlen: pointer to buffer size; updated with actual size on return\n+ *\n+ * Gets a QUIC socket option from a given socket.\n+ *\n+ * Return:\n+ * - On success, 0 is returned.\n+ * - On error, a negative error value is returned.\n+ */\n+int quic_do_getsockopt(struct sock *sk, int optname, sockptr_t optval,\n+\t\t sockptr_t optlen)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+EXPORT_SYMBOL_GPL(quic_do_getsockopt);\n+\n+static int quic_getsockopt(struct sock *sk, int level, int optname,\n+\t\t\t char __user *optval, int __user *optlen)\n+{\n+\tif (level != SOL_QUIC)\n+\t\treturn quic_common_getsockopt(sk, level, optname, optval,\n+\t\t\t\t\t optlen);\n+\n+\treturn quic_do_getsockopt(sk, optname, USER_SOCKPTR(optval),\n+\t\t\t\t USER_SOCKPTR(optlen));\n+}\n+\n+static void quic_release_cb(struct sock *sk)\n+{\n+\t/* Similar to tcp_release_cb(). */\n+\tunsigned long nflags, flags = smp_load_acquire(\u0026sk-\u003esk_tsq_flags);\n+\n+\tdo {\n+\t\tif (!(flags \u0026 QUIC_DEFERRED_ALL))\n+\t\t\treturn;\n+\t\tnflags = flags \u0026 ~QUIC_DEFERRED_ALL;\n+\t} while (!try_cmpxchg(\u0026sk-\u003esk_tsq_flags, \u0026flags, nflags));\n+\n+\tif (flags \u0026 QUIC_F_MTU_REDUCED_DEFERRED) {\n+\t\tquic_packet_rcv_err_pmtu(sk);\n+\t\t__sock_put(sk);\n+\t}\n+\tif (flags \u0026 QUIC_F_LOSS_DEFERRED) {\n+\t\tquic_timer_loss_handler(sk);\n+\t\t__sock_put(sk);\n+\t}\n+\tif (flags \u0026 QUIC_F_SACK_DEFERRED) {\n+\t\tquic_timer_sack_handler(sk);\n+\t\t__sock_put(sk);\n+\t}\n+\tif (flags \u0026 QUIC_F_PATH_DEFERRED) {\n+\t\tquic_timer_path_handler(sk);\n+\t\t__sock_put(sk);\n+\t}\n+\tif (flags \u0026 QUIC_F_PMTU_DEFERRED) {\n+\t\tquic_timer_pmtu_handler(sk);\n+\t\t__sock_put(sk);\n+\t}\n+\tif (flags \u0026 QUIC_F_PACE_DEFERRED) {\n+\t\tquic_timer_pace_handler(sk);\n+\t\t__sock_put(sk);\n+\t}\n+}\n+\n+static int quic_disconnect(struct sock *sk, int flags)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static void quic_shutdown(struct sock *sk, int how)\n+{\n+\tquic_set_state(sk, QUIC_SS_CLOSED);\n+}\n+\n+static int quic_backlog_rcv(struct sock *sk, struct sk_buff *skb)\n+{\n+\treturn quic_packet_process(sk, skb, GFP_ATOMIC);\n+}\n+\n+struct proto quic_prot = {\n+\t.name\t\t= \"QUIC\",\n+\t.owner\t\t= THIS_MODULE,\n+\t.init\t\t= quic_init_sock,\n+\t.destroy\t= quic_destroy_sock,\n+\t.shutdown\t= quic_shutdown,\n+\t.setsockopt\t= quic_setsockopt,\n+\t.getsockopt\t= quic_getsockopt,\n+\t.connect\t= quic_connect,\n+\t.bind\t\t= quic_bind,\n+\t.close\t\t= quic_close,\n+\t.disconnect\t= quic_disconnect,\n+\t.sendmsg\t= quic_sendmsg,\n+\t.recvmsg\t= quic_recvmsg,\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_backlog_rcv,\n+\t.release_cb\t= quic_release_cb,\n+\t.no_autobind\t= true,\n+\t.obj_size\t= sizeof(struct quic_sock),\n+\t.sysctl_mem\t\t= sysctl_quic_mem,\n+\t.sysctl_rmem\t\t= sysctl_quic_rmem,\n+\t.sysctl_wmem\t\t= sysctl_quic_wmem,\n+\t.memory_pressure\t= \u0026quic_memory_pressure,\n+\t.enter_memory_pressure\t= quic_enter_memory_pressure,\n+\t.memory_allocated\t= \u0026quic_memory_allocated,\n+\t.per_cpu_fw_alloc\t= \u0026quic_memory_per_cpu_fw_alloc,\n+\t.sockets_allocated\t= \u0026quic_sockets_allocated,\n+};\n+\n+struct proto quicv6_prot = {\n+\t.name\t\t= \"QUICv6\",\n+\t.owner\t\t= THIS_MODULE,\n+\t.init\t\t= quic_init_sock,\n+\t.destroy\t= quic_destroy_sock,\n+\t.shutdown\t= quic_shutdown,\n+\t.setsockopt\t= quic_setsockopt,\n+\t.getsockopt\t= quic_getsockopt,\n+\t.connect\t= quic_connect,\n+\t.bind\t\t= quic_bind,\n+\t.close\t\t= quic_close,\n+\t.disconnect\t= quic_disconnect,\n+\t.sendmsg\t= quic_sendmsg,\n+\t.recvmsg\t= quic_recvmsg,\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_backlog_rcv,\n+\t.release_cb\t= quic_release_cb,\n+\t.no_autobind\t= true,\n+\t.obj_size\t= sizeof(struct quic6_sock),\n+\t.ipv6_pinfo_offset\t= offsetof(struct quic6_sock, inet6),\n+\t.sysctl_mem\t\t= sysctl_quic_mem,\n+\t.sysctl_rmem\t\t= sysctl_quic_rmem,\n+\t.sysctl_wmem\t\t= sysctl_quic_wmem,\n+\t.memory_pressure\t= \u0026quic_memory_pressure,\n+\t.enter_memory_pressure\t= quic_enter_memory_pressure,\n+\t.memory_allocated\t= \u0026quic_memory_allocated,\n+\t.per_cpu_fw_alloc\t= \u0026quic_memory_per_cpu_fw_alloc,\n+\t.sockets_allocated\t= \u0026quic_sockets_allocated,\n+};\ndiff --git a/net/quic/socket.h b/net/quic/socket.h\nnew file mode 100644\nindex 0000000000000..9fe78c0d4ae57\n--- /dev/null\n+++ b/net/quic/socket.h\n@@ -0,0 +1,242 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003cnet/udp_tunnel.h\u003e\n+#include \u003clinux/quic.h\u003e\n+\n+#include \"common.h\"\n+#include \"pnspace.h\"\n+#include \"family.h\"\n+#include \"stream.h\"\n+#include \"connid.h\"\n+#include \"crypto.h\"\n+#include \"path.h\"\n+#include \"cong.h\"\n+\n+#include \"packet.h\"\n+\n+#include \"protocol.h\"\n+#include \"timer.h\"\n+\n+extern struct proto quic_prot;\n+extern struct proto quicv6_prot;\n+\n+enum quic_state {\n+\tQUIC_SS_CLOSED\t\t= TCP_CLOSE,\n+\tQUIC_SS_LISTENING\t= TCP_LISTEN,\n+\tQUIC_SS_ESTABLISHING\t= TCP_SYN_RECV,\n+\tQUIC_SS_ESTABLISHED\t= TCP_ESTABLISHED,\n+};\n+\n+enum quic_tsq_enum {\n+\tQUIC_MTU_REDUCED_DEFERRED,\n+\tQUIC_LOSS_DEFERRED,\n+\tQUIC_SACK_DEFERRED,\n+\tQUIC_PATH_DEFERRED,\n+\tQUIC_PMTU_DEFERRED,\n+\tQUIC_PACE_DEFERRED,\n+};\n+\n+enum quic_tsq_flags {\n+\tQUIC_F_MTU_REDUCED_DEFERRED\t= BIT(QUIC_MTU_REDUCED_DEFERRED),\n+\tQUIC_F_LOSS_DEFERRED\t\t= BIT(QUIC_LOSS_DEFERRED),\n+\tQUIC_F_SACK_DEFERRED\t\t= BIT(QUIC_SACK_DEFERRED),\n+\tQUIC_F_PATH_DEFERRED\t\t= BIT(QUIC_PATH_DEFERRED),\n+\tQUIC_F_PMTU_DEFERRED\t\t= BIT(QUIC_PMTU_DEFERRED),\n+\tQUIC_F_PACE_DEFERRED\t\t= BIT(QUIC_PACE_DEFERRED),\n+};\n+\n+#define QUIC_DEFERRED_ALL (QUIC_F_MTU_REDUCED_DEFERRED |\t\\\n+\t\t\t QUIC_F_LOSS_DEFERRED |\t\t\\\n+\t\t\t QUIC_F_SACK_DEFERRED |\t\t\\\n+\t\t\t QUIC_F_PATH_DEFERRED |\t\t\\\n+\t\t\t QUIC_F_PMTU_DEFERRED |\t\t\\\n+\t\t\t QUIC_F_PACE_DEFERRED)\n+\n+struct quic_request_sock {\n+\tstruct list_head\tlist;\n+\n+\tstruct quic_conn_id\tdcid;\n+\tstruct quic_conn_id\tscid;\n+\tunion quic_addr\t\tdaddr;\n+\tunion quic_addr\t\tsaddr;\n+\n+\tstruct quic_conn_id\torig_dcid;\n+\tu32\t\t\tversion;\n+\tu8\t\t\tretry;\n+\n+\tstruct sk_buff_head\tbacklog_list;\n+\tu32\t\t\tblen;\n+};\n+\n+struct quic_sock {\n+\tstruct inet_sock\t\tinet;\n+\tstruct list_head\t\treqs;\n+\tstruct work_struct\t\twork;\n+\n+\tstruct quic_data\t\tticket;\n+\tstruct quic_data\t\ttoken;\n+\tstruct quic_data\t\talpn;\n+\n+\tstruct quic_stream_table\tstreams;\n+\tstruct quic_conn_id_set\t\tsource;\n+\tstruct quic_conn_id_set\t\tdest;\n+\tstruct quic_path_group\t\tpaths;\n+\tstruct quic_cong\t\tcong;\n+\tstruct quic_pnspace\t\tspace[QUIC_PNSPACE_MAX];\n+\tstruct quic_crypto\t\tcrypto[QUIC_CRYPTO_MAX];\n+\n+\tstruct quic_packet\t\tpacket;\n+\tstruct quic_timer\t\ttimers[QUIC_TIMER_MAX];\n+};\n+\n+struct quic6_sock {\n+\tstruct quic_sock\tquic;\n+\tstruct ipv6_pinfo\tinet6;\n+};\n+\n+static inline struct quic_sock *quic_sk(const struct sock *sk)\n+{\n+\treturn (struct quic_sock *)sk;\n+}\n+\n+static inline struct list_head *quic_reqs(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003ereqs;\n+}\n+\n+static inline struct work_struct *quic_work(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003ework;\n+}\n+\n+static inline struct quic_data *quic_token(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003etoken;\n+}\n+\n+static inline struct quic_data *quic_ticket(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003eticket;\n+}\n+\n+static inline struct quic_data *quic_alpn(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003ealpn;\n+}\n+\n+static inline struct quic_stream_table *quic_streams(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003estreams;\n+}\n+\n+static inline struct quic_conn_id_set *quic_source(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003esource;\n+}\n+\n+static inline struct quic_conn_id_set *quic_dest(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003edest;\n+}\n+\n+static inline struct quic_path_group *quic_paths(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003epaths;\n+}\n+\n+static inline bool quic_is_serv(const struct sock *sk)\n+{\n+\treturn !!sk-\u003esk_max_ack_backlog;\n+}\n+\n+static inline struct quic_cong *quic_cong(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003econg;\n+}\n+\n+static inline struct quic_pnspace *quic_pnspace(const struct sock *sk, u8 level)\n+{\n+\treturn \u0026quic_sk(sk)-\u003espace[level % QUIC_CRYPTO_EARLY];\n+}\n+\n+static inline struct quic_crypto *quic_crypto(const struct sock *sk, u8 level)\n+{\n+\treturn \u0026quic_sk(sk)-\u003ecrypto[level];\n+}\n+\n+static inline struct quic_packet *quic_packet(const struct sock *sk)\n+{\n+\treturn \u0026quic_sk(sk)-\u003epacket;\n+}\n+\n+static inline void *quic_timer(const struct sock *sk, u8 type)\n+{\n+\treturn (void *)\u0026quic_sk(sk)-\u003etimers[type];\n+}\n+\n+static inline bool quic_is_establishing(struct sock *sk)\n+{\n+\treturn sk-\u003esk_state == QUIC_SS_ESTABLISHING;\n+}\n+\n+static inline bool quic_is_established(struct sock *sk)\n+{\n+\treturn sk-\u003esk_state == QUIC_SS_ESTABLISHED;\n+}\n+\n+static inline bool quic_is_listen(struct sock *sk)\n+{\n+\treturn sk-\u003esk_state == QUIC_SS_LISTENING;\n+}\n+\n+static inline bool quic_is_closed(struct sock *sk)\n+{\n+\treturn sk-\u003esk_state == QUIC_SS_CLOSED;\n+}\n+\n+static inline void quic_set_state(struct sock *sk, int state)\n+{\n+\tstruct net *net = sock_net(sk);\n+\tint mib;\n+\n+\tif (sk-\u003esk_state == state)\n+\t\treturn;\n+\n+\tif (state == QUIC_SS_ESTABLISHED) {\n+\t\tmib = quic_is_serv(sk) ? QUIC_MIB_CONN_PASSIVEESTABS :\n+\t\t\t\t\t QUIC_MIB_CONN_ACTIVEESTABS;\n+\t\tQUIC_INC_STATS(net, mib);\n+\t\tQUIC_INC_STATS(net, QUIC_MIB_CONN_CURRENTESTABS);\n+\t} else if (quic_is_established(sk)) {\n+\t\tQUIC_DEC_STATS(net, QUIC_MIB_CONN_CURRENTESTABS);\n+\t}\n+\n+\tinet_sk_set_state(sk, state);\n+\tsk-\u003esk_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, struct sock *usk,\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+\n+bool quic_listen_sock_switch(struct sk_buff *skb, struct quic_data *alpns);\n+bool quic_accept_sock_exists(struct sock *sk, struct sk_buff *skb);\n+\n+struct quic_request_sock *quic_request_sock_create(struct sock *sk,\n+\t\t\t\t\t\t struct quic_conn_id *odcid,\n+\t\t\t\t\t\t u8 retry, gfp_t gfp);\n+int quic_request_sock_backlog_tail(struct sock *sk,\n+\t\t\t\t struct quic_request_sock *req,\n+\t\t\t\t struct sk_buff *skb);\n+struct quic_request_sock *quic_request_sock_lookup(struct sock *sk);\ndiff --git a/net/quic/stream.c b/net/quic/stream.c\nnew file mode 100644\nindex 0000000000000..6b34b117103a8\n--- /dev/null\n+++ b/net/quic/stream.c\n@@ -0,0 +1,416 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \u003clinux/quic.h\u003e\n+\n+#include \"common.h\"\n+#include \"stream.h\"\n+\n+/* Check if a stream ID is valid for sending or receiving. */\n+static bool quic_stream_id_valid(s64 stream_id, bool is_serv, bool send)\n+{\n+\tu8 type = (stream_id \u0026 QUIC_STREAM_TYPE_MASK);\n+\n+\tif (send) {\n+\t\tif (is_serv)\n+\t\t\treturn type != QUIC_STREAM_TYPE_CLIENT_UNI;\n+\t\treturn type != QUIC_STREAM_TYPE_SERVER_UNI;\n+\t}\n+\tif (is_serv)\n+\t\treturn type != QUIC_STREAM_TYPE_SERVER_UNI;\n+\treturn type != QUIC_STREAM_TYPE_CLIENT_UNI;\n+}\n+\n+/* Check if a stream ID was initiated locally. */\n+static bool quic_stream_id_local(s64 stream_id, bool is_serv)\n+{\n+\treturn is_serv ^ !(stream_id \u0026 QUIC_STREAM_TYPE_SERVER_MASK);\n+}\n+\n+/* Check if a stream ID represents a unidirectional stream. */\n+static bool quic_stream_id_uni(s64 stream_id)\n+{\n+\treturn stream_id \u0026 QUIC_STREAM_TYPE_UNI_MASK;\n+}\n+\n+#define QUIC_STREAM_HT_SIZE\t64\n+\n+static struct hlist_head *quic_stream_head(struct quic_stream_table *streams,\n+\t\t\t\t\t s64 stream_id)\n+{\n+\t/* Skip the SERVER initiator bit, which is constant per endpoint. */\n+\treturn \u0026streams-\u003ehead[(stream_id \u003e\u003e 1) \u0026 (QUIC_STREAM_HT_SIZE - 1)];\n+}\n+\n+struct quic_stream *quic_stream_find(struct quic_stream_table *streams,\n+\t\t\t\t s64 stream_id)\n+{\n+\tstruct hlist_head *head = quic_stream_head(streams, stream_id);\n+\tstruct quic_stream *stream;\n+\n+\thlist_for_each_entry(stream, head, node) {\n+\t\tif (stream-\u003eid == stream_id)\n+\t\t\tbreak;\n+\t}\n+\treturn stream;\n+}\n+\n+static void quic_stream_add(struct quic_stream_table *streams,\n+\t\t\t struct quic_stream *stream)\n+{\n+\tstruct hlist_head *head;\n+\n+\thead = quic_stream_head(streams, stream-\u003eid);\n+\thlist_add_head(\u0026stream-\u003enode, head);\n+}\n+\n+static void quic_stream_delete(struct quic_stream *stream)\n+{\n+\thlist_del_init(\u0026stream-\u003enode);\n+\tkfree(stream);\n+}\n+\n+/* Create and register new streams for sending or receiving. */\n+static struct quic_stream *quic_stream_create(struct quic_stream_table *streams,\n+\t\t\t\t\t s64 max_stream_id, bool send,\n+\t\t\t\t\t bool is_serv, gfp_t gfp)\n+{\n+\tstruct quic_stream *pos, *stream = NULL;\n+\tstruct quic_stream_limits *limits;\n+\tstruct hlist_node *tmp;\n+\tHLIST_HEAD(head);\n+\ts64 stream_id;\n+\tu32 count = 0;\n+\n+\tgfp |= __GFP_ACCOUNT;\n+\tlimits = send ? \u0026streams-\u003esend : \u0026streams-\u003erecv;\n+\tstream_id = limits-\u003enext_bidi_stream_id;\n+\tif (quic_stream_id_uni(max_stream_id))\n+\t\tstream_id = limits-\u003enext_uni_stream_id;\n+\n+\t/* rfc9000#section-2.1: A stream ID that is used out of order results in\n+\t * all streams of that type with lower-numbered stream IDs also being\n+\t * opened.\n+\t */\n+\twhile (stream_id \u003c= max_stream_id) {\n+\t\tstream = kzalloc_obj(*stream, gfp);\n+\t\tif (!stream)\n+\t\t\tgoto free;\n+\n+\t\tstream-\u003eid = stream_id;\n+\t\tif (quic_stream_id_uni(stream_id)) {\n+\t\t\tif (send) {\n+\t\t\t\tstream-\u003esend.max_bytes =\n+\t\t\t\t\tlimits-\u003emax_stream_data_uni;\n+\t\t\t} else {\n+\t\t\t\tstream-\u003erecv.max_bytes =\n+\t\t\t\t\tlimits-\u003emax_stream_data_uni;\n+\t\t\t\tstream-\u003erecv.window = stream-\u003erecv.max_bytes;\n+\t\t\t}\n+\t\t\thlist_add_head(\u0026stream-\u003enode, \u0026head);\n+\t\t\tstream_id += QUIC_STREAM_ID_STEP;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (quic_stream_id_local(stream_id, is_serv)) {\n+\t\t\tstream-\u003esend.max_bytes =\n+\t\t\t\tstreams-\u003esend.max_stream_data_bidi_remote;\n+\t\t\tstream-\u003erecv.max_bytes =\n+\t\t\t\tstreams-\u003erecv.max_stream_data_bidi_local;\n+\t\t} else {\n+\t\t\tstream-\u003esend.max_bytes =\n+\t\t\t\tstreams-\u003esend.max_stream_data_bidi_local;\n+\t\t\tstream-\u003erecv.max_bytes =\n+\t\t\t\tstreams-\u003erecv.max_stream_data_bidi_remote;\n+\t\t}\n+\t\tstream-\u003erecv.window = stream-\u003erecv.max_bytes;\n+\t\thlist_add_head(\u0026stream-\u003enode, \u0026head);\n+\t\tstream_id += QUIC_STREAM_ID_STEP;\n+\t}\n+\n+\thlist_for_each_entry_safe(pos, tmp, \u0026head, node) {\n+\t\thlist_del_init(\u0026pos-\u003enode);\n+\t\tquic_stream_add(streams, pos);\n+\t\tcount++;\n+\t}\n+\n+\t/* Streams must be opened sequentially. Update the next stream ID so the\n+\t * correct starting point is known if an out-of-order open is requested.\n+\t * Note overflow of next_uni/bidi_stream_id is impossible with s64.\n+\t */\n+\tif (quic_stream_id_uni(stream_id)) {\n+\t\tlimits-\u003enext_uni_stream_id = stream_id;\n+\t\tlimits-\u003estreams_uni += count;\n+\t\treturn stream;\n+\t}\n+\n+\tlimits-\u003enext_bidi_stream_id = stream_id;\n+\tlimits-\u003estreams_bidi += count;\n+\treturn stream;\n+\n+free:\n+\thlist_for_each_entry_safe(pos, tmp, \u0026head, node) {\n+\t\thlist_del_init(\u0026pos-\u003enode);\n+\t\tkfree(pos);\n+\t}\n+\treturn NULL;\n+}\n+\n+/* Check if a send or receive stream ID is already closed. */\n+static bool quic_stream_id_closed(struct quic_stream_table *streams,\n+\t\t\t\t s64 stream_id, bool send)\n+{\n+\tstruct quic_stream_limits *limits = send ? \u0026streams-\u003esend :\n+\t\t\t\t\t\t \u0026streams-\u003erecv;\n+\n+\tif (quic_stream_id_uni(stream_id))\n+\t\treturn stream_id \u003c limits-\u003enext_uni_stream_id;\n+\treturn stream_id \u003c limits-\u003enext_bidi_stream_id;\n+}\n+\n+/* Check if a stream ID would exceed local (recv) or peer (send) limits. */\n+bool quic_stream_id_exceeds(struct quic_stream_table *streams, s64 stream_id,\n+\t\t\t bool send)\n+{\n+\tu64 nstreams;\n+\n+\tif (!send) {\n+\t\t/* recv.max_uni/bidi_stream_id is updated in\n+\t\t * quic_stream_max_streams_update() already based on\n+\t\t * next_uni/bidi_stream_id, max_streams_uni/bidi, and\n+\t\t * streams_uni/bidi, so only recv.max_uni/bidi_stream_id needs\n+\t\t * to be checked.\n+\t\t */\n+\t\tif (quic_stream_id_uni(stream_id))\n+\t\t\treturn stream_id \u003e streams-\u003erecv.max_uni_stream_id;\n+\n+\t\treturn stream_id \u003e streams-\u003erecv.max_bidi_stream_id;\n+\t}\n+\n+\tif (quic_stream_id_uni(stream_id)) {\n+\t\tif (stream_id \u003e streams-\u003esend.max_uni_stream_id)\n+\t\t\treturn true;\n+\t\tstream_id -= streams-\u003esend.next_uni_stream_id;\n+\t\tnstreams = quic_stream_id_to_streams(stream_id);\n+\n+\t\treturn nstreams + streams-\u003esend.streams_uni \u003e\n+\t\t streams-\u003esend.max_streams_uni;\n+\t}\n+\n+\tif (stream_id \u003e streams-\u003esend.max_bidi_stream_id)\n+\t\treturn true;\n+\tstream_id -= streams-\u003esend.next_bidi_stream_id;\n+\tnstreams = quic_stream_id_to_streams(stream_id);\n+\n+\treturn nstreams + streams-\u003esend.streams_bidi \u003e\n+\t streams-\u003esend.max_streams_bidi;\n+}\n+\n+/* Get or create a send or recv stream by ID. Requires sock lock held. */\n+struct quic_stream *quic_stream_get(struct quic_stream_table *streams,\n+\t\t\t\t s64 stream_id, u32 flags, bool is_serv,\n+\t\t\t\t bool send, gfp_t gfp)\n+{\n+\tstruct quic_stream *stream;\n+\n+\tif (!quic_stream_id_valid(stream_id, is_serv, send))\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\tstream = quic_stream_find(streams, stream_id);\n+\tif (stream)\n+\t\treturn stream;\n+\n+\tif (!send \u0026\u0026 quic_stream_id_local(stream_id, is_serv)) {\n+\t\tif (quic_stream_id_closed(streams, stream_id, !send))\n+\t\t\treturn ERR_PTR(-ENOSTR);\n+\t\treturn ERR_PTR(-EINVAL);\n+\t}\n+\tif (send \u0026\u0026 !quic_stream_id_local(stream_id, is_serv))\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\tif (quic_stream_id_closed(streams, stream_id, send))\n+\t\treturn ERR_PTR(-ENOSTR);\n+\n+\tif (!(flags \u0026 MSG_QUIC_STREAM_NEW))\n+\t\treturn ERR_PTR(-EINVAL);\n+\n+\tif (quic_stream_id_exceeds(streams, stream_id, send))\n+\t\treturn ERR_PTR(-EAGAIN);\n+\n+\tstream = quic_stream_create(streams, stream_id, send, is_serv, gfp);\n+\tif (!stream)\n+\t\treturn ERR_PTR(-ENOMEM);\n+\n+\tif (send || quic_stream_id_valid(stream_id, is_serv, !send))\n+\t\tstreams-\u003esend.active_stream_id = stream_id;\n+\n+\treturn stream;\n+}\n+\n+/* Release or clean up a send or recv stream. This function updates stream\n+ * counters and state when a send stream has either successfully sent all data\n+ * or has been reset, or when a recv stream has either received all data or has\n+ * been reset. Requires sock lock held.\n+ */\n+void quic_stream_put(struct quic_stream_table *streams,\n+\t\t struct quic_stream *stream, bool is_serv, bool send)\n+{\n+\tif (quic_stream_id_uni(stream-\u003eid)) {\n+\t\t/* For uni streams, decrement uni count and delete stream. */\n+\t\tif (send) {\n+\t\t\tstreams-\u003esend.streams_uni--;\n+\t\t\tquic_stream_delete(stream);\n+\t\t\treturn;\n+\t\t}\n+\t\tstreams-\u003erecv.streams_uni--;\n+\t\tstreams-\u003erecv.uni_pending = 1;\n+\t\tquic_stream_delete(stream);\n+\t\treturn;\n+\t}\n+\n+\t/* For bidi streams, proceed only if both send and receive in a final\n+\t * state.\n+\t */\n+\tif (send) {\n+\t\tif (stream-\u003erecv.state \u003c QUIC_STREAM_RECV_STATE_RECVD)\n+\t\t\treturn;\n+\t} else {\n+\t\tif (stream-\u003esend.state != QUIC_STREAM_SEND_STATE_RECVD \u0026\u0026\n+\t\t stream-\u003esend.state != QUIC_STREAM_SEND_STATE_RESET_RECVD)\n+\t\t\treturn;\n+\t}\n+\tif (quic_stream_id_local(stream-\u003eid, is_serv)) {\n+\t\t/* Local-initiated stream: decrement send.bidi count. */\n+\t\tstreams-\u003esend.streams_bidi--;\n+\t} else {\n+\t\t/* Remote-initiated stream: decrement recv.bidi count. */\n+\t\tstreams-\u003erecv.streams_bidi--;\n+\t\tstreams-\u003erecv.bidi_pending = 1;\n+\t}\n+\tquic_stream_delete(stream);\n+}\n+\n+/* Updates the maximum allowed incoming stream IDs if any streams were recently\n+ * closed. Recalculates the max_uni and max_bidi stream ID limits based on the\n+ * number of open streams and whether any were marked for deletion.\n+ *\n+ * Returns true if either max_uni or max_bidi was updated, indicating that a\n+ * MAX_STREAMS_UNI or MAX_STREAMS_BIDI frame should be sent to the peer.\n+ */\n+bool quic_stream_max_streams_update(struct quic_stream_table *streams,\n+\t\t\t\t s64 *max_uni, s64 *max_bidi)\n+{\n+\ts64 max, rem;\n+\n+\t*max_uni = 0;\n+\t*max_bidi = 0;\n+\tif (streams-\u003erecv.uni_pending) {\n+\t\trem = streams-\u003erecv.max_streams_uni - streams-\u003erecv.streams_uni;\n+\t\tmax = streams-\u003erecv.next_uni_stream_id - QUIC_STREAM_ID_STEP +\n+\t\t (rem \u003c\u003c QUIC_STREAM_TYPE_BITS);\n+\n+\t\tstreams-\u003erecv.max_uni_stream_id = max;\n+\t\t*max_uni = quic_stream_id_to_streams(max);\n+\t\tstreams-\u003erecv.uni_pending = 0;\n+\t}\n+\tif (streams-\u003erecv.bidi_pending) {\n+\t\trem = streams-\u003erecv.max_streams_bidi -\n+\t\t streams-\u003erecv.streams_bidi;\n+\t\tmax = streams-\u003erecv.next_bidi_stream_id - QUIC_STREAM_ID_STEP +\n+\t\t\t(rem \u003c\u003c QUIC_STREAM_TYPE_BITS);\n+\n+\t\tstreams-\u003erecv.max_bidi_stream_id = max;\n+\t\t*max_bidi = quic_stream_id_to_streams(max);\n+\t\tstreams-\u003erecv.bidi_pending = 0;\n+\t}\n+\n+\treturn *max_uni || *max_bidi;\n+}\n+\n+int quic_stream_init(struct quic_stream_table *streams)\n+{\n+\tstruct hlist_head *head;\n+\tint i;\n+\n+\thead = kmalloc_objs(*head, QUIC_STREAM_HT_SIZE);\n+\tif (!head)\n+\t\treturn -ENOMEM;\n+\tfor (i = 0; i \u003c QUIC_STREAM_HT_SIZE; i++)\n+\t\tINIT_HLIST_HEAD(\u0026head[i]);\n+\tstreams-\u003ehead = head;\n+\treturn 0;\n+}\n+\n+void quic_stream_free(struct quic_stream_table *streams)\n+{\n+\tstruct quic_stream *stream;\n+\tstruct hlist_head *head;\n+\tstruct hlist_node *tmp;\n+\tint i;\n+\n+\tif (!streams-\u003ehead)\n+\t\treturn;\n+\n+\tfor (i = 0; i \u003c QUIC_STREAM_HT_SIZE; i++) {\n+\t\thead = \u0026streams-\u003ehead[i];\n+\t\thlist_for_each_entry_safe(stream, tmp, head, node)\n+\t\t\tquic_stream_delete(stream);\n+\t}\n+\tkfree(streams-\u003ehead);\n+\tstreams-\u003ehead = NULL;\n+}\n+\n+/* Populate transport parameters from stream hash table. */\n+void quic_stream_get_param(struct quic_stream_table *streams,\n+\t\t\t struct quic_transport_param *p)\n+{\n+\tstruct quic_stream_limits *limits = p-\u003eremote ? \u0026streams-\u003esend :\n+\t\t\t\t\t\t\t\u0026streams-\u003erecv;\n+\n+\tp-\u003emax_stream_data_bidi_remote = limits-\u003emax_stream_data_bidi_remote;\n+\tp-\u003emax_stream_data_bidi_local = limits-\u003emax_stream_data_bidi_local;\n+\tp-\u003emax_stream_data_uni = limits-\u003emax_stream_data_uni;\n+\tp-\u003emax_streams_bidi = limits-\u003emax_streams_bidi;\n+\tp-\u003emax_streams_uni = limits-\u003emax_streams_uni;\n+}\n+\n+/* Configure stream hashtable from transport parameters. */\n+void quic_stream_set_param(struct quic_stream_table *streams,\n+\t\t\t struct quic_transport_param *p, bool is_serv)\n+{\n+\tstruct quic_stream_limits *limits = p-\u003eremote ? \u0026streams-\u003esend :\n+\t\t\t\t\t\t\t\u0026streams-\u003erecv;\n+\tu8 bidi_type, uni_type;\n+\n+\tlimits-\u003emax_stream_data_bidi_local = p-\u003emax_stream_data_bidi_local;\n+\tlimits-\u003emax_stream_data_bidi_remote = p-\u003emax_stream_data_bidi_remote;\n+\tlimits-\u003emax_stream_data_uni = p-\u003emax_stream_data_uni;\n+\tlimits-\u003emax_streams_bidi = p-\u003emax_streams_bidi;\n+\tlimits-\u003emax_streams_uni = p-\u003emax_streams_uni;\n+\tlimits-\u003eactive_stream_id = -1;\n+\n+\tif (p-\u003eremote ^ is_serv) {\n+\t\tbidi_type = QUIC_STREAM_TYPE_CLIENT_BIDI;\n+\t\tuni_type = QUIC_STREAM_TYPE_CLIENT_UNI;\n+\t} else {\n+\t\tbidi_type = QUIC_STREAM_TYPE_SERVER_BIDI;\n+\t\tuni_type = QUIC_STREAM_TYPE_SERVER_UNI;\n+\t}\n+\n+\tlimits-\u003emax_bidi_stream_id =\n+\t\tquic_stream_streams_to_id(p-\u003emax_streams_bidi, bidi_type);\n+\tlimits-\u003enext_bidi_stream_id = bidi_type;\n+\n+\tlimits-\u003emax_uni_stream_id =\n+\t\tquic_stream_streams_to_id(p-\u003emax_streams_uni, uni_type);\n+\tlimits-\u003enext_uni_stream_id = uni_type;\n+}\ndiff --git a/net/quic/stream.h b/net/quic/stream.h\nnew file mode 100644\nindex 0000000000000..d915712f3d5fb\n--- /dev/null\n+++ b/net/quic/stream.h\n@@ -0,0 +1,133 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#define QUIC_DEF_STREAMS\t100\n+#define QUIC_MAX_STREAMS\t4096ULL\n+\n+/*\n+ * rfc9000#section-2.1:\n+ *\n+ * The least significant bit (0x01) of the stream ID identifies the initiator\n+ * of the stream. Client-initiated streams have even-numbered stream IDs\n+ * (with the bit set to 0), and server-initiated streams have odd-numbered\n+ * stream IDs (with the bit set to 1).\n+ *\n+ * The second least significant bit (0x02) of the stream ID distinguishes\n+ * between bidirectional streams (with the bit set to 0) and unidirectional\n+ * streams (with the bit set to 1).\n+ */\n+#define QUIC_STREAM_TYPE_BITS\t2\n+#define QUIC_STREAM_ID_STEP\tBIT(QUIC_STREAM_TYPE_BITS)\n+\n+#define QUIC_STREAM_TYPE_CLIENT_BIDI\t0x00\n+#define QUIC_STREAM_TYPE_SERVER_BIDI\t0x01\n+#define QUIC_STREAM_TYPE_CLIENT_UNI\t0x02\n+#define QUIC_STREAM_TYPE_SERVER_UNI\t0x03\n+\n+struct quic_stream {\n+\tstruct hlist_node node;\n+\ts64 id; /* Stream ID as defined in RFC 9000 Section 2.1 */\n+\tstruct {\n+\t\t/* Sending-side stream level flow control */\n+\t\tu64 last_max_bytes; /* Max send offset advertised by peer */\n+\t\tu64 max_bytes; /* Max offset allowed to send */\n+\t\tu64 bytes; /* Bytes already sent to peer */\n+\n+\t\tu32 errcode; /* App error code for RESET_STREAM */\n+\t\tu32 frags; /* STREAM frames sent but not yet acked */\n+\t\tu8 state; /* Send stream state, per rfc9000#section-3.1 */\n+\n+\t\tu8 data_blocked; /* True if flow control blocks sending */\n+\t} send;\n+\tstruct {\n+\t\t/* Receiving-side stream level flow control */\n+\t\tu64 max_bytes; /* Max offset peer can send */\n+\t\tu64 window; /* Remaining receive window */\n+\t\tu64 bytes; /* Bytes consumed by app */\n+\n+\t\tu64 highest; /* Highest received offset */\n+\t\tu64 offset; /* Data buffered or consumed */\n+\t\tu64 finalsz; /* Final stream size if FIN received */\n+\n+\t\tu32 frags; /* STREAM frames pending reassembly */\n+\t\tu8 state; /* Receive stream state, per rfc9000#section-3.2 */\n+\n+\t\tu8 stop_sent; /* True if STOP_SENDING has been sent */\n+\t} recv;\n+};\n+\n+struct quic_stream_limits {\n+\t/* Stream limit parameters defined in rfc9000#section-18.2:\n+\t *\n+\t * - initial_max_stream_data_bidi_remote\n+\t * - initial_max_stream_data_bidi_local\n+\t * - initial_max_stream_data_uni\n+\t * - initial_max_streams_bidi\n+\t * - initial_max_streams_uni\n+\t */\n+\tu64 max_stream_data_bidi_remote;\n+\tu64 max_stream_data_bidi_local;\n+\tu64 max_stream_data_uni;\n+\tu64 max_streams_bidi;\n+\tu64 max_streams_uni;\n+\n+\ts64 next_bidi_stream_id; /* Next bidi stream ID to open or accept */\n+\ts64 next_uni_stream_id; /* Next uni stream ID to open or accept */\n+\ts64 max_bidi_stream_id; /* Highest allowed bidi stream ID */\n+\ts64 max_uni_stream_id; /* Highest allowed uni stream ID */\n+\ts64 active_stream_id; /* Most recently opened stream ID */\n+\n+\tu8 bidi_blocked; /* STREAMS_BLOCKED_BIDI sent, awaiting ACK */\n+\tu8 uni_blocked; /* STREAMS_BLOCKED_UNI sent, awaiting ACK */\n+\tu8 bidi_pending; /* MAX_STREAMS_BIDI needs to be sent */\n+\tu8 uni_pending; /* MAX_STREAMS_UNI needs to be sent */\n+\n+\tu16 streams_bidi; /* Number of open bidi streams */\n+\tu16 streams_uni; /* Number of open uni streams */\n+};\n+\n+struct quic_stream_table {\n+\tstruct hlist_head *head; /* Hash table storing all active streams */\n+\n+\tstruct quic_stream_limits send; /* Limits advertised by peer */\n+\tstruct quic_stream_limits recv; /* Limits we advertise to peer */\n+};\n+\n+static inline u64 quic_stream_id_to_streams(s64 stream_id)\n+{\n+\treturn (u64)(stream_id \u003e\u003e QUIC_STREAM_TYPE_BITS) + 1;\n+}\n+\n+static inline s64 quic_stream_streams_to_id(u64 streams, u8 type)\n+{\n+\tif (!streams)\n+\t\treturn -1;\n+\treturn (s64)((streams - 1) \u003c\u003c QUIC_STREAM_TYPE_BITS) | type;\n+}\n+\n+struct quic_stream *quic_stream_get(struct quic_stream_table *streams,\n+\t\t\t\t s64 stream_id, u32 flags, bool is_serv,\n+\t\t\t\t bool send, gfp_t gfp);\n+void quic_stream_put(struct quic_stream_table *streams,\n+\t\t struct quic_stream *stream, bool is_serv, bool send);\n+\n+bool quic_stream_max_streams_update(struct quic_stream_table *streams,\n+\t\t\t\t s64 *max_uni, s64 *max_bidi);\n+bool quic_stream_id_exceeds(struct quic_stream_table *streams,\n+\t\t\t s64 stream_id, bool send);\n+struct quic_stream *quic_stream_find(struct quic_stream_table *streams,\n+\t\t\t\t s64 stream_id);\n+\n+void quic_stream_get_param(struct quic_stream_table *streams,\n+\t\t\t struct quic_transport_param *p);\n+void quic_stream_set_param(struct quic_stream_table *streams,\n+\t\t\t struct quic_transport_param *p, bool is_serv);\n+void quic_stream_free(struct quic_stream_table *streams);\n+int quic_stream_init(struct quic_stream_table *streams);\ndiff --git a/net/quic/timer.c b/net/quic/timer.c\nnew file mode 100644\nindex 0000000000000..0dd6d6580bbd9\n--- /dev/null\n+++ b/net/quic/timer.c\n@@ -0,0 +1,154 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Initialization/cleanup for QUIC protocol support.\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+#include \"socket.h\"\n+\n+static void quic_timer_timeout(struct quic_timer *t, int type, int defer_bit,\n+\t\t\t void (*handler)(struct sock *sk))\n+{\n+\tstruct quic_sock *qs = container_of(t, struct quic_sock, timers[type]);\n+\tstruct sock *sk = \u0026qs-\u003einet.sk;\n+\n+\tbh_lock_sock(sk);\n+\tif (sock_owned_by_user(sk)) {\n+\t\tif (!test_and_set_bit(defer_bit, \u0026sk-\u003esk_tsq_flags))\n+\t\t\tsock_hold(sk);\n+\t\tgoto out;\n+\t}\n+\n+\thandler(sk);\n+out:\n+\tbh_unlock_sock(sk);\n+\tsock_put(sk);\n+}\n+\n+void quic_timer_sack_handler(struct sock *sk)\n+{\n+}\n+\n+static void quic_timer_sack_timeout(struct timer_list *t)\n+{\n+\tquic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_SACK,\n+\t\t\t QUIC_SACK_DEFERRED, quic_timer_sack_handler);\n+}\n+\n+void quic_timer_loss_handler(struct sock *sk)\n+{\n+}\n+\n+static void quic_timer_loss_timeout(struct timer_list *t)\n+{\n+\tquic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_LOSS,\n+\t\t\t QUIC_LOSS_DEFERRED, quic_timer_loss_handler);\n+}\n+\n+void quic_timer_path_handler(struct sock *sk)\n+{\n+}\n+\n+static void quic_timer_path_timeout(struct timer_list *t)\n+{\n+\tquic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PATH,\n+\t\t\t QUIC_PATH_DEFERRED, quic_timer_path_handler);\n+}\n+\n+void quic_timer_pmtu_handler(struct sock *sk)\n+{\n+}\n+\n+static void quic_timer_pmtu_timeout(struct timer_list *t)\n+{\n+\tquic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PMTU,\n+\t\t\t QUIC_PMTU_DEFERRED, quic_timer_pmtu_handler);\n+}\n+\n+void quic_timer_pace_handler(struct sock *sk)\n+{\n+}\n+\n+static enum hrtimer_restart quic_timer_pace_timeout(struct hrtimer *hr)\n+{\n+\tquic_timer_timeout((struct quic_timer *)hr, QUIC_TIMER_PACE,\n+\t\t\t QUIC_PACE_DEFERRED, quic_timer_pace_handler);\n+\treturn HRTIMER_NORESTART;\n+}\n+\n+void quic_timer_reset(struct sock *sk, u8 type, u64 timeout)\n+{\n+\tstruct timer_list *t = quic_timer(sk, type);\n+\n+\t/* Note that type must never be QUIC_TIMER_PACE for this helper. */\n+\tif (WARN_ON_ONCE(type == QUIC_TIMER_PACE))\n+\t\treturn;\n+\tif (timeout \u0026\u0026 !mod_timer(t, jiffies + usecs_to_jiffies(timeout)))\n+\t\tsock_hold(sk);\n+}\n+\n+void quic_timer_start(struct sock *sk, u8 type, u64 timeout)\n+{\n+\tstruct timer_list *t;\n+\tstruct hrtimer *hr;\n+\n+\tif (type == QUIC_TIMER_PACE) {\n+\t\thr = quic_timer(sk, type);\n+\n+\t\tif (!hrtimer_is_queued(hr)) {\n+\t\t\thrtimer_start(hr, ns_to_ktime(timeout),\n+\t\t\t\t HRTIMER_MODE_ABS_PINNED_SOFT);\n+\t\t\tsock_hold(sk);\n+\t\t}\n+\t\treturn;\n+\t}\n+\n+\tt = quic_timer(sk, type);\n+\tif (timeout \u0026\u0026 !timer_pending(t)) {\n+\t\tif (!mod_timer(t, jiffies + usecs_to_jiffies(timeout)))\n+\t\t\tsock_hold(sk);\n+\t}\n+}\n+\n+void quic_timer_stop(struct sock *sk, u8 type)\n+{\n+\tif (type == QUIC_TIMER_PACE) {\n+\t\tif (hrtimer_try_to_cancel(quic_timer(sk, type)) == 1)\n+\t\t\tsock_put(sk);\n+\t\treturn;\n+\t}\n+\tif (timer_delete(quic_timer(sk, type)))\n+\t\tsock_put(sk);\n+}\n+\n+void quic_timer_init(struct sock *sk)\n+{\n+\ttimer_setup(quic_timer(sk, QUIC_TIMER_LOSS), quic_timer_loss_timeout,\n+\t\t 0);\n+\ttimer_setup(quic_timer(sk, QUIC_TIMER_SACK), quic_timer_sack_timeout,\n+\t\t 0);\n+\ttimer_setup(quic_timer(sk, QUIC_TIMER_PATH), quic_timer_path_timeout,\n+\t\t 0);\n+\ttimer_setup(quic_timer(sk, QUIC_TIMER_PMTU), quic_timer_pmtu_timeout,\n+\t\t 0);\n+\t/* Use hrtimer for pace timer, ensuring precise control over send\n+\t * timing.\n+\t */\n+\thrtimer_setup(quic_timer(sk, QUIC_TIMER_PACE), quic_timer_pace_timeout,\n+\t\t CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED_SOFT);\n+}\n+\n+void quic_timer_free(struct sock *sk)\n+{\n+\tquic_timer_stop(sk, QUIC_TIMER_LOSS);\n+\tquic_timer_stop(sk, QUIC_TIMER_SACK);\n+\tquic_timer_stop(sk, QUIC_TIMER_PATH);\n+\tquic_timer_stop(sk, QUIC_TIMER_PMTU);\n+\tquic_timer_stop(sk, QUIC_TIMER_PACE);\n+}\ndiff --git a/net/quic/timer.h b/net/quic/timer.h\nnew file mode 100644\nindex 0000000000000..4f63660376027\n--- /dev/null\n+++ b/net/quic/timer.h\n@@ -0,0 +1,45 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+/* QUIC kernel implementation\n+ * (C) Copyright Red Hat Corp. 2023\n+ *\n+ * This file is part of the QUIC kernel implementation\n+ *\n+ * Written or modified by:\n+ * Xin Long \u003clucien.xin@gmail.com\u003e\n+ */\n+\n+enum {\n+\tQUIC_TIMER_LOSS, /* Loss detection timer: retransmit on packet loss */\n+\tQUIC_TIMER_SACK, /* ACK delay timer, also used as idle timer alias */\n+\tQUIC_TIMER_PATH, /* Path validation timer: verifies path connectivity */\n+\tQUIC_TIMER_PMTU, /* PLPMTUD probing timer */\n+\tQUIC_TIMER_PACE, /* Pacing timer: controls packet transmission pacing */\n+\tQUIC_TIMER_MAX,\n+\tQUIC_TIMER_IDLE = QUIC_TIMER_SACK,\n+};\n+\n+struct quic_timer {\n+\tunion {\n+\t\tstruct timer_list t;\n+\t\tstruct hrtimer hr;\n+\t};\n+};\n+\n+#define QUIC_MIN_PROBE_TIMEOUT\t5000000\n+\n+#define QUIC_MIN_PATH_TIMEOUT\t1500000\n+\n+#define QUIC_MIN_IDLE_TIMEOUT\t1000000\n+#define QUIC_DEF_IDLE_TIMEOUT\t30000000\n+\n+void quic_timer_reset(struct sock *sk, u8 type, u64 timeout);\n+void quic_timer_start(struct sock *sk, u8 type, u64 timeout);\n+void quic_timer_stop(struct sock *sk, u8 type);\n+void quic_timer_init(struct sock *sk);\n+void quic_timer_free(struct sock *sk);\n+\n+void quic_timer_loss_handler(struct sock *sk);\n+void quic_timer_pace_handler(struct sock *sk);\n+void quic_timer_path_handler(struct sock *sk);\n+void quic_timer_sack_handler(struct sock *sk);\n+void quic_timer_pmtu_handler(struct sock *sk);\ndiff --git a/tools/include/uapi/linux/in.h b/tools/include/uapi/linux/in.h\nindex ced0fc3c3aa53..e4072152f2e62 100644\n--- a/tools/include/uapi/linux/in.h\n+++ b/tools/include/uapi/linux/in.h\n@@ -85,6 +85,8 @@ enum {\n #define IPPROTO_RAW\t\tIPPROTO_RAW\n IPPROTO_SMC = 256,\t\t/* Shared Memory Communications\t\t*/\n #define IPPROTO_SMC\t\tIPPROTO_SMC\n+ IPPROTO_QUIC = 261,\t\t/* A UDP-Based Multiplexed Secure Transport */\n+#define IPPROTO_QUIC\t\tIPPROTO_QUIC\n IPPROTO_MPTCP = 262,\t\t/* Multipath TCP connection\t\t*/\n #define IPPROTO_MPTCP\t\tIPPROTO_MPTCP\n IPPROTO_MAX\ndiff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h\nindex 2a8d7b14f1d11..9d6ec1b2c50ba 100644\n--- a/tools/perf/trace/beauty/include/linux/socket.h\n+++ b/tools/perf/trace/beauty/include/linux/socket.h\n@@ -400,6 +400,7 @@ struct ucred {\n #define SOL_MCTP\t285\n #define SOL_SMC\t\t286\n #define SOL_VSOCK\t287\n+#define SOL_QUIC\t288\n \n /* IPX options */\n #define IPX_TYPE\t1\ndiff --git a/usr/include/Makefile b/usr/include/Makefile\nindex ee69dd9d970e5..8b4133d38da2d 100644\n--- a/usr/include/Makefile\n+++ b/usr/include/Makefile\n@@ -31,6 +31,7 @@ no-header-test += linux/omap3isp.h\n no-header-test += linux/omapfb.h\n no-header-test += linux/patchkey.h\n no-header-test += linux/phonet.h\n+no-header-test += linux/quic.h\n no-header-test += linux/sctp.h\n no-header-test += linux/sysctl.h\n no-header-test += linux/usb/audio.h\n"
} |
| 2/1 | 2026/09/14 17:48 | agent | patch-evaluator |
4mModel:core Args: null Results: {
"EnableConfigs": [
"IP_QUIC",
"INET",
"IPV6"
],
"FocusSymbols": [
"quic_init_sock",
"quic_destroy_sock",
"quic_sock_destruct",
"quic_do_setsockopt",
"quic_do_getsockopt",
"quic_common_setsockopt",
"quic_common_getsockopt",
"quic_setsockopt",
"quic_getsockopt",
"quic_release_cb",
"quic_stream_init",
"quic_stream_free",
"quic_pnspace_init",
"quic_pnspace_free",
"quic_conn_id_set_init",
"quic_conn_id_set_free",
"quic_packet_init",
"quic_packet_free",
"quic_timer_init",
"quic_timer_free",
"quic_cong_init",
"quic_path_init",
"quic_path_unbind",
"quic_crypto_free",
"quic_snmp_seq_show"
],
"Reasoning": "The patch introduces the foundational in-kernel implementation of the QUIC transport protocol (IPPROTO_QUIC == 261, SOL_QUIC == 288), registering AF_INET and AF_INET6 SOCK_STREAM and SOCK_DGRAM protocol switches. Although higher-level features like packet exchange and full stream I/O are partially stubbed out with -EOPNOTSUPP at this stage, the core socket creation, initialization, teardown, timer setup, memory allocation accounting, socket options, procfs stats, and sysctl interfaces are fully reachable and executable from unprivileged userspace via socket() and standard socket syscalls. Fuzzing is warranted to test resource allocation, lifecycle management, and socket state handling.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 85c120c4d16fd7ce68c939879ba590511ca36840
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Sep 14 17:48:10 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index 208f46967ee59..6dd9d6970cec4 100644
--- a/Documentation/networking/ip-sysctl.rst
+++ b/Documentation/networking/ip-sysctl.rst
@@ -3809,6 +3809,45 @@ l3mdev_accept - BOOLEAN
Default: 1 (enabled)
+``/proc/sys/net/quic/*`` Variables
+===================================
+
+quic_mem - vector of 3 LONGs: min, pressure, max
+ Number of pages allowed for queueing by all QUIC sockets.
+
+ min: below this number of pages QUIC is not bothered about its
+ memory appetite.
+
+ pressure: when amount of memory allocated by QUIC exceeds this number
+ of pages, QUIC moderates its memory consumption and enters memory
+ pressure mode, which is exited when memory consumption falls
+ under "min".
+
+ max: number of pages allowed for queueing by all QUIC sockets.
+
+ Defaults are calculated at boot time from amount of available
+ memory.
+
+quic_rmem - vector of 3 INTEGERs: min, default, max
+ Only the first value ("min") is used, "default" and "max" are
+ ignored.
+
+ min: Minimal size of receive buffer used by QUIC sockets.
+ It is guaranteed to each QUIC socket, even under moderate memory
+ pressure.
+
+ Default: 4K
+
+quic_wmem - vector of 3 INTEGERs: min, default, max
+ Only the first value ("min") is used, "default" and "max" are
+ ignored.
+
+ min: Amount of memory reserved for send buffers for QUIC sockets.
+ Each QUIC socket has rights to use it due to fact of its birth.
+
+ Default: 4K
+
+
``/proc/sys/net/core/*``
========================
diff --git a/MAINTAINERS b/MAINTAINERS
index 0e04d92d1b098..ba5b5249e8fed 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22666,6 +22666,15 @@ L: linux-wireless@vger.kernel.org
S: Maintained
F: drivers/net/wireless/quantenna/
+QUIC PROTOCOL
+M: Xin Long <lucien.xin@gmail.com>
+L: quic@lists.linux.dev
+S: Maintained
+W: https://github.com/lxin/quic
+F: include/linux/quic.h
+F: include/uapi/linux/quic.h
+F: net/quic/
+
RADEON and AMDGPU DRM DRIVERS
M: Alex Deucher <alexander.deucher@amd.com>
M: Christian König <christian.koenig@amd.com>
diff --git a/include/linux/quic.h b/include/linux/quic.h
new file mode 100644
index 0000000000000..51c099e9547f5
--- /dev/null
+++ b/include/linux/quic.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#ifndef _LINUX_QUIC_H
+#define _LINUX_QUIC_H
+
+#include <linux/sockptr.h>
+#include <uapi/linux/quic.h>
+
+struct sock;
+
+#if IS_REACHABLE(CONFIG_IP_QUIC)
+int quic_do_setsockopt(struct sock *sk, int optname, sockptr_t optval,
+ unsigned int optlen);
+int quic_do_getsockopt(struct sock *sk, int optname, sockptr_t optval,
+ sockptr_t optlen);
+#else
+static inline int quic_do_setsockopt(struct sock *sk, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int quic_do_getsockopt(struct sock *sk, int optname,
+ sockptr_t optval, sockptr_t optlen)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+#endif
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 5a5eb12501032..aeea3baa96732 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -400,6 +400,7 @@ struct ucred {
#define SOL_MCTP 285
#define SOL_SMC 286
#define SOL_VSOCK 287
+#define SOL_QUIC 288
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/trace/events/sock.h b/include/trace/events/sock.h
index b5310439536e0..ed43fe6d2705b 100644
--- a/include/trace/events/sock.h
+++ b/include/trace/events/sock.h
@@ -20,7 +20,8 @@
#define inet_protocol_names \
EM(IPPROTO_TCP) \
EM(IPPROTO_SCTP) \
- EMe(IPPROTO_MPTCP)
+ EM(IPPROTO_MPTCP) \
+ EMe(IPPROTO_QUIC)
#define tcp_state_names \
EM(TCP_ESTABLISHED) \
diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h
index ced0fc3c3aa53..e4072152f2e62 100644
--- a/include/uapi/linux/in.h
+++ b/include/uapi/linux/in.h
@@ -85,6 +85,8 @@ enum {
#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_SMC = 256, /* Shared Memory Communications */
#define IPPROTO_SMC IPPROTO_SMC
+ IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed Secure Transport */
+#define IPPROTO_QUIC IPPROTO_QUIC
IPPROTO_MPTCP = 262, /* Multipath TCP connection */
#define IPPROTO_MPTCP IPPROTO_MPTCP
IPPROTO_MAX
diff --git a/include/uapi/linux/quic.h b/include/uapi/linux/quic.h
new file mode 100644
index 0000000000000..ae37d6a7bc36f
--- /dev/null
+++ b/include/uapi/linux/quic.h
@@ -0,0 +1,241 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#ifndef _UAPI_LINUX_QUIC_H
+#define _UAPI_LINUX_QUIC_H
+
+#include <linux/types.h>
+#ifdef __KERNEL__
+#include <linux/socket.h>
+#else
+#include <sys/socket.h> /* for MSG_* flags */
+#endif
+
+/* NOTE: Structure descriptions are specified in:
+ * https://datatracker.ietf.org/doc/html/draft-lxin-quic-socket-apis
+ */
+
+/* Send or Receive Options APIs */
+enum quic_cmsg_type {
+ QUIC_STREAM_INFO,
+ QUIC_HANDSHAKE_INFO,
+};
+
+#define QUIC_STREAM_TYPE_SERVER_MASK 0x01
+#define QUIC_STREAM_TYPE_UNI_MASK 0x02
+#define QUIC_STREAM_TYPE_MASK 0x03
+
+enum quic_msg_flags {
+ /* flags for stream_flags */
+ MSG_QUIC_STREAM_NEW = MSG_SYN,
+ MSG_QUIC_STREAM_FIN = MSG_FIN,
+ MSG_QUIC_STREAM_UNI = MSG_CONFIRM,
+ MSG_QUIC_STREAM_DONTWAIT = MSG_EOR,
+ MSG_QUIC_STREAM_SNDBLOCK = MSG_ERRQUEUE,
+
+ /* extended flags for msg_flags */
+ MSG_QUIC_DATAGRAM = MSG_RST,
+ MSG_QUIC_NOTIFICATION = MSG_MORE,
+};
+
+enum quic_crypto_level {
+ QUIC_CRYPTO_APP,
+ QUIC_CRYPTO_INITIAL,
+ QUIC_CRYPTO_HANDSHAKE,
+ QUIC_CRYPTO_EARLY,
+ QUIC_CRYPTO_MAX,
+};
+
+struct quic_handshake_info {
+ __u8 crypto_level;
+};
+
+struct quic_stream_info {
+ __s64 stream_id;
+ __u32 stream_flags;
+};
+
+/* Socket Options APIs */
+#define QUIC_SOCKOPT_EVENT 0
+#define QUIC_SOCKOPT_STREAM_OPEN 1
+#define QUIC_SOCKOPT_STREAM_RESET 2
+#define QUIC_SOCKOPT_STREAM_STOP_SENDING 3
+#define QUIC_SOCKOPT_CONNECTION_ID 4
+#define QUIC_SOCKOPT_CONNECTION_CLOSE 5
+#define QUIC_SOCKOPT_CONNECTION_MIGRATION 6
+#define QUIC_SOCKOPT_KEY_UPDATE 7
+#define QUIC_SOCKOPT_TRANSPORT_PARAM 8
+#define QUIC_SOCKOPT_CONFIG 9
+#define QUIC_SOCKOPT_TOKEN 10
+#define QUIC_SOCKOPT_ALPN 11
+#define QUIC_SOCKOPT_SESSION_TICKET 12
+#define QUIC_SOCKOPT_CRYPTO_SECRET 13
+#define QUIC_SOCKOPT_TRANSPORT_PARAM_EXT 14
+
+#define QUIC_VERSION_V1 0x1
+#define QUIC_VERSION_V2 0x6b3343cf
+
+struct quic_transport_param {
+ __u8 remote;
+ __u8 disable_active_migration;
+ __u8 grease_quic_bit;
+ __u8 stateless_reset;
+ __u8 disable_1rtt_encryption;
+ __u8 disable_compatible_version;
+ __u8 active_connection_id_limit;
+ __u8 ack_delay_exponent;
+ __u16 max_datagram_frame_size;
+ __u16 max_udp_payload_size;
+ __u32 max_idle_timeout;
+ __u32 max_ack_delay;
+ __u16 max_streams_bidi;
+ __u16 max_streams_uni;
+ __u64 max_data;
+ __u64 max_stream_data_bidi_local;
+ __u64 max_stream_data_bidi_remote;
+ __u64 max_stream_data_uni;
+};
+
+struct quic_config {
+ __u32 version;
+ __u32 plpmtud_probe_interval;
+ __u32 initial_smoothed_rtt;
+ __u32 payload_cipher_type;
+ __u8 congestion_control_algo;
+ __u8 validate_peer_address;
+ __u8 stream_data_nodelay;
+ __u8 receive_session_ticket;
+ __u8 certificate_request;
+ __u8 reserved[3];
+ __u32 keepalive_probe_interval;
+};
+
+struct quic_crypto_secret {
+ __u8 send; /* send or recv */
+ __u8 level; /* crypto level */
+ __u16 reserved;
+ __u32 type; /* TLS_CIPHER_* */
+#define QUIC_CRYPTO_SECRET_BUFFER_SIZE 48
+ __u8 secret[QUIC_CRYPTO_SECRET_BUFFER_SIZE];
+};
+
+enum quic_cong_algo {
+ QUIC_CONG_ALG_RENO,
+ QUIC_CONG_ALG_MAX,
+};
+
+struct quic_errinfo {
+ __s64 stream_id;
+ __u32 errcode;
+};
+
+struct quic_connection_id_info {
+ __u8 dest;
+ __u8 reserved[3];
+ __u32 active;
+ __u32 prior_to;
+};
+
+struct quic_event_option {
+ __u8 type;
+ __u8 on;
+};
+
+/* Event APIs */
+enum quic_event_type {
+ QUIC_EVENT_NONE,
+ QUIC_EVENT_STREAM_UPDATE,
+ QUIC_EVENT_STREAM_MAX_DATA,
+ QUIC_EVENT_STREAM_MAX_STREAM,
+ QUIC_EVENT_CONNECTION_ID,
+ QUIC_EVENT_CONNECTION_CLOSE,
+ QUIC_EVENT_CONNECTION_MIGRATION,
+ QUIC_EVENT_KEY_UPDATE,
+ QUIC_EVENT_NEW_TOKEN,
+ QUIC_EVENT_NEW_SESSION_TICKET,
+ QUIC_EVENT_MAX,
+};
+
+enum {
+ QUIC_STREAM_SEND_STATE_READY,
+ QUIC_STREAM_SEND_STATE_SEND,
+ QUIC_STREAM_SEND_STATE_SENT,
+ QUIC_STREAM_SEND_STATE_RECVD,
+ QUIC_STREAM_SEND_STATE_RESET_SENT,
+ QUIC_STREAM_SEND_STATE_RESET_RECVD,
+
+ QUIC_STREAM_RECV_STATE_RECV,
+ QUIC_STREAM_RECV_STATE_SIZE_KNOWN,
+ QUIC_STREAM_RECV_STATE_RECVD,
+ QUIC_STREAM_RECV_STATE_READ,
+ QUIC_STREAM_RECV_STATE_RESET_RECVD,
+ QUIC_STREAM_RECV_STATE_RESET_READ,
+};
+
+struct quic_stream_update {
+ __s64 id;
+ __u8 state;
+ __u8 reserved[3];
+ __u32 errcode;
+ __u64 finalsz;
+};
+
+struct quic_stream_max_data {
+ __s64 id;
+ __u64 max_data;
+};
+
+struct quic_connection_close {
+ __u32 errcode;
+ __u8 frame;
+ __u8 reserved[3];
+#define QUIC_CLOSE_PHRASE_BUFFER_SIZE 64
+ __u8 phrase[QUIC_CLOSE_PHRASE_BUFFER_SIZE];
+};
+
+union quic_event {
+ struct quic_stream_update update;
+ struct quic_stream_max_data max_data;
+ struct quic_connection_close close;
+ struct quic_connection_id_info info;
+ __u64 max_stream;
+ __u8 local_migration;
+ __u8 key_update_phase;
+};
+
+enum {
+ QUIC_TRANSPORT_ERROR_NONE = 0x00,
+ QUIC_TRANSPORT_ERROR_INTERNAL = 0x01,
+ QUIC_TRANSPORT_ERROR_CONNECTION_REFUSED = 0x02,
+ QUIC_TRANSPORT_ERROR_FLOW_CONTROL = 0x03,
+ QUIC_TRANSPORT_ERROR_STREAM_LIMIT = 0x04,
+ QUIC_TRANSPORT_ERROR_STREAM_STATE = 0x05,
+ QUIC_TRANSPORT_ERROR_FINAL_SIZE = 0x06,
+ QUIC_TRANSPORT_ERROR_FRAME_ENCODING = 0x07,
+ QUIC_TRANSPORT_ERROR_TRANSPORT_PARAM = 0x08,
+ QUIC_TRANSPORT_ERROR_CONNECTION_ID_LIMIT = 0x09,
+ QUIC_TRANSPORT_ERROR_PROTOCOL_VIOLATION = 0x0a,
+ QUIC_TRANSPORT_ERROR_INVALID_TOKEN = 0x0b,
+ QUIC_TRANSPORT_ERROR_APPLICATION = 0x0c,
+ QUIC_TRANSPORT_ERROR_CRYPTO_BUF_EXCEEDED = 0x0d,
+ QUIC_TRANSPORT_ERROR_KEY_UPDATE = 0x0e,
+ QUIC_TRANSPORT_ERROR_AEAD_LIMIT_REACHED = 0x0f,
+ QUIC_TRANSPORT_ERROR_NO_VIABLE_PATH = 0x10,
+ QUIC_TRANSPORT_ERROR_VERSION_NEGOTIATION = 0x11,
+
+ /* The cryptographic handshake failed. A range of 256 values is reserved
+ * for carrying error codes specific to the cryptographic handshake that
+ * is used. Codes for errors occurring when TLS is used for the
+ * cryptographic handshake are described in Section 4.8 of [QUIC-TLS].
+ */
+ QUIC_TRANSPORT_ERROR_CRYPTO = 0x0100,
+};
+
+#endif /* _UAPI_LINUX_QUIC_H */
diff --git a/net/Kconfig b/net/Kconfig
index e384773935515..ab0d82e108ea9 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -251,6 +251,7 @@ source "net/bridge/netfilter/Kconfig"
endif # if NETFILTER
+source "net/quic/Kconfig"
source "net/sctp/Kconfig"
source "net/rds/Kconfig"
source "net/tipc/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index 5b2dd7f07a85e..e2b25ede017d7 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PHONET) += phonet/
ifneq ($(CONFIG_VLAN_8021Q),)
obj-y += 8021q/
endif
+obj-$(CONFIG_IP_QUIC) += quic/
obj-$(CONFIG_IP_SCTP) += sctp/
obj-$(CONFIG_RDS) += rds/
obj-$(CONFIG_WIRELESS) += wireless/
diff --git a/net/quic/Kconfig b/net/quic/Kconfig
new file mode 100644
index 0000000000000..602e6d89eafdd
--- /dev/null
+++ b/net/quic/Kconfig
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# QUIC configuration
+#
+
+menuconfig IP_QUIC
+ tristate "QUIC: A UDP-Based Multiplexed Secure Transport (Experimental)"
+ depends on INET
+ depends on IPV6
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_AES
+ select CRYPTO_GCM
+ select CRYPTO_CCM
+ select CRYPTO_CHACHA20POLY1305
+ select NET_UDP_TUNNEL
+ default n
+ help
+ QUIC: A UDP-Based Multiplexed and Secure Transport
+
+ From rfc9000 <https://www.rfc-editor.org/rfc/rfc9000.html>.
+
+ QUIC provides applications with flow-controlled streams for structured
+ communication, low-latency connection establishment, and network path
+ migration. QUIC includes security measures that ensure
+ confidentiality, integrity, and availability in a range of deployment
+ circumstances. Accompanying documents describe the integration of
+ TLS for key negotiation, loss detection, and an exemplary congestion
+ control algorithm.
+
+ To compile this protocol support as a module, choose M here: the
+ module will be called quic. Debug messages are handled by the
+ kernel's dynamic debugging framework.
+
+ If in doubt, say N.
diff --git a/net/quic/Makefile b/net/quic/Makefile
new file mode 100644
index 0000000000000..0f903f4a7ff1f
--- /dev/null
+++ b/net/quic/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Makefile for QUIC support code.
+#
+
+obj-$(CONFIG_IP_QUIC) += quic.o
+
+quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o \
+ cong.o pnspace.o crypto.o timer.o packet.o
diff --git a/net/quic/common.c b/net/quic/common.c
new file mode 100644
index 0000000000000..51d71b6733c7d
--- /dev/null
+++ b/net/quic/common.c
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <linux/unaligned.h>
+#include <net/netns/hash.h>
+#include <linux/vmalloc.h>
+#include <linux/jhash.h>
+
+#include "common.h"
+
+#define QUIC_VARINT_2BYTE_PREFIX 0x40
+#define QUIC_VARINT_4BYTE_PREFIX 0x80
+#define QUIC_VARINT_8BYTE_PREFIX 0xc0
+
+#define QUIC_VARINT_LENGTH(p) BIT((*(p)) >> 6)
+
+struct quic_hashinfo {
+ struct quic_shash_table shash; /* Source connection ID hashtable */
+ struct quic_shash_table lhash; /* Listening sock hashtable */
+ struct quic_shash_table chash; /* Connection sock hashtable */
+ struct quic_uhash_table uhash; /* UDP sock hashtable */
+};
+
+static struct quic_hashinfo quic_hashinfo;
+
+u32 quic_sock_hash_size(void)
+{
+ return quic_hashinfo.chash.size;
+}
+
+u32 quic_sock_hash(struct net *net, union quic_addr *s, union quic_addr *d)
+{
+ u32 ports = ((__force u32)s->v4.sin_port) << 16 |
+ (__force u32)d->v4.sin_port;
+ u32 saddr = (s->sa.sa_family == AF_INET6) ?
+ jhash(&s->v6.sin6_addr, 16, 0) :
+ (__force u32)s->v4.sin_addr.s_addr;
+ u32 daddr = (d->sa.sa_family == AF_INET6) ?
+ jhash(&d->v6.sin6_addr, 16, 0) :
+ (__force u32)d->v4.sin_addr.s_addr;
+ u32 hash = jhash_3words(saddr, daddr, ports, net_hash_mix(net));
+
+ return hash & (quic_sock_hash_size() - 1);
+}
+
+struct quic_shash_head *quic_sock_head(u32 hash)
+{
+ return &quic_hashinfo.chash.hash[hash];
+}
+
+u32 quic_listen_sock_hash_size(void)
+{
+ return quic_hashinfo.lhash.size;
+}
+
+u32 quic_listen_sock_hash(struct net *net, u16 port)
+{
+ u32 hash = jhash_1word((__force u32)port, net_hash_mix(net));
+
+ return hash & (quic_listen_sock_hash_size() - 1);
+}
+
+struct quic_shash_head *quic_listen_sock_head(u32 hash)
+{
+ return &quic_hashinfo.lhash.hash[hash];
+}
+
+struct quic_shash_head *quic_source_conn_id_head(struct net *net, u8 *scid,
+ u32 len)
+{
+ u32 hash = jhash_1word(jhash(scid, len, 0), net_hash_mix(net));
+ struct quic_shash_table *ht = &quic_hashinfo.shash;
+
+ return &ht->hash[hash & (ht->size - 1)];
+}
+
+struct quic_uhash_head *quic_udp_sock_head(struct net *net, u16 port)
+{
+ u32 hash = jhash_1word((__force u32)port, net_hash_mix(net));
+ struct quic_uhash_table *ht = &quic_hashinfo.uhash;
+
+ return &ht->hash[hash & (ht->size - 1)];
+}
+
+u32 quic_addr_hash(struct net *net, union quic_addr *a)
+{
+ u32 addr = (a->sa.sa_family == AF_INET6) ?
+ jhash(&a->v6.sin6_addr, 16, 0) :
+ (__force u32)a->v4.sin_addr.s_addr;
+
+ return jhash_2words(addr, (__force u32)a->v4.sin_port,
+ net_hash_mix(net));
+}
+
+void quic_hash_tables_destroy(void)
+{
+ vfree(quic_hashinfo.shash.hash);
+ vfree(quic_hashinfo.lhash.hash);
+ vfree(quic_hashinfo.chash.hash);
+ vfree(quic_hashinfo.uhash.hash);
+}
+
+static int quic_shash_table_init(struct quic_shash_table *ht, u32 size)
+{
+ int i;
+
+ ht->hash = vmalloc_array(size, sizeof(struct quic_shash_head));
+ if (!ht->hash)
+ return -ENOMEM;
+
+ ht->size = size;
+ for (i = 0; i < ht->size; i++) {
+ spin_lock_init(&ht->hash[i].lock);
+ INIT_HLIST_NULLS_HEAD(&ht->hash[i].head, i);
+ }
+ return 0;
+}
+
+static int quic_uhash_table_init(struct quic_uhash_table *ht, u32 size)
+{
+ int i;
+
+ ht->hash = vmalloc_array(size, sizeof(struct quic_uhash_head));
+ if (!ht->hash)
+ return -ENOMEM;
+
+ ht->size = size;
+ for (i = 0; i < ht->size; i++) {
+ mutex_init(&ht->hash[i].lock);
+ INIT_HLIST_HEAD(&ht->hash[i].head);
+ }
+ return 0;
+}
+
+int quic_hash_tables_init(void)
+{
+ unsigned long nr_pages = totalram_pages();
+ unsigned long limit;
+ u32 size;
+ int err;
+
+ /* Scale hash table size based on system memory, similar to SCTP. */
+ if (nr_pages >= (128 * 1024))
+ limit = nr_pages >> (22 - PAGE_SHIFT);
+ else
+ limit = nr_pages >> (24 - PAGE_SHIFT);
+
+ limit = roundup_pow_of_two(limit ?: 1);
+
+ /* Source connection ID table (fast lookup, larger size) */
+ size = min_t(unsigned long, limit, 64 * 1024UL);
+ err = quic_shash_table_init(&quic_hashinfo.shash, size);
+ if (err)
+ goto err;
+ size = min_t(unsigned long, limit, 16 * 1024UL);
+ err = quic_shash_table_init(&quic_hashinfo.lhash, size);
+ if (err)
+ goto err;
+ err = quic_shash_table_init(&quic_hashinfo.chash, size);
+ if (err)
+ goto err;
+ err = quic_uhash_table_init(&quic_hashinfo.uhash, size);
+ if (err)
+ goto err;
+ return 0;
+err:
+ quic_hash_tables_destroy();
+ return err;
+}
+
+/* Returns the number of bytes required to encode a QUIC variable-length
+ * integer.
+ */
+u8 quic_var_len(u64 n)
+{
+ if (n <= QUIC_VARINT_1BYTE_MAX)
+ return 1;
+ if (n <= QUIC_VARINT_2BYTE_MAX)
+ return 2;
+ if (n <= QUIC_VARINT_4BYTE_MAX)
+ return 4;
+ return 8;
+}
+
+/* Decodes a QUIC variable-length integer from a buffer. */
+u8 quic_get_var(u8 **pp, u32 *plen, u64 *val)
+{
+ u8 *p = *pp, len;
+ u64 v = 0;
+
+ if (!*plen)
+ return 0;
+
+ len = QUIC_VARINT_LENGTH(p);
+ if (*plen < len)
+ return 0;
+
+ switch (len) {
+ case 1:
+ v = *p;
+ break;
+ case 2:
+ v = get_unaligned_be16(p) & QUIC_VARINT_2BYTE_MAX;
+ break;
+ case 4:
+ v = get_unaligned_be32(p) & QUIC_VARINT_4BYTE_MAX;
+ break;
+ case 8:
+ v = get_unaligned_be64(p) & QUIC_VARINT_8BYTE_MAX;
+ break;
+ default:
+ return 0;
+ }
+
+ *plen -= len;
+ *pp = p + len;
+ *val = v;
+ return len;
+}
+
+/* Reads a fixed-length integer from the buffer. */
+u32 quic_get_int(u8 **pp, u32 *plen, u64 *val, u32 len)
+{
+ u8 *p = *pp;
+ u64 v = 0;
+
+ if (*plen < len)
+ return 0;
+
+ switch (len) {
+ case 1:
+ v = *p;
+ break;
+ case 2:
+ v = get_unaligned_be16(p);
+ break;
+ case 3:
+ v = get_unaligned_be24(p);
+ break;
+ case 4:
+ v = get_unaligned_be32(p);
+ break;
+ case 8:
+ v = get_unaligned_be64(p);
+ break;
+ default:
+ return 0;
+ }
+ *plen -= len;
+ *pp = p + len;
+ *val = v;
+ return len;
+}
+
+u32 quic_get_data(u8 **pp, u32 *plen, u8 *data, u32 len)
+{
+ if (*plen < len)
+ return 0;
+
+ memcpy(data, *pp, len);
+ *pp += len;
+ *plen -= len;
+
+ return len;
+}
+
+/* Encodes a value into the QUIC variable-length integer format. */
+u8 *quic_put_var(u8 *p, u64 num)
+{
+ if (num <= QUIC_VARINT_1BYTE_MAX) {
+ *p++ = (u8)num;
+ return p;
+ }
+ if (num <= QUIC_VARINT_2BYTE_MAX) {
+ put_unaligned_be16((u16)num, p);
+ *p |= QUIC_VARINT_2BYTE_PREFIX;
+ return p + 2;
+ }
+ if (num <= QUIC_VARINT_4BYTE_MAX) {
+ put_unaligned_be32((u32)num, p);
+ *p |= QUIC_VARINT_4BYTE_PREFIX;
+ return p + 4;
+ }
+ put_unaligned_be64(num, p);
+ *p |= QUIC_VARINT_8BYTE_PREFIX;
+ return p + 8;
+}
+
+/* Writes a fixed-length integer to the buffer in network byte order. */
+u8 *quic_put_int(u8 *p, u64 num, u8 len)
+{
+ switch (len) {
+ case 1:
+ *p++ = (u8)num;
+ return p;
+ case 2:
+ put_unaligned_be16((u16)num, p);
+ return p + 2;
+ case 4:
+ put_unaligned_be32((u32)num, p);
+ return p + 4;
+ case 8:
+ put_unaligned_be64(num, p);
+ return p + 8;
+ default:
+ return NULL;
+ }
+}
+
+/* Encodes a value as a variable-length integer with explicit length. */
+u8 *quic_put_varint(u8 *p, u64 num, u8 len)
+{
+ switch (len) {
+ case 1:
+ *p++ = (u8)num;
+ return p;
+ case 2:
+ put_unaligned_be16((u16)num, p);
+ *p |= QUIC_VARINT_2BYTE_PREFIX;
+ return p + 2;
+ case 4:
+ put_unaligned_be32((u32)num, p);
+ *p |= QUIC_VARINT_4BYTE_PREFIX;
+ return p + 4;
+ default:
+ return NULL;
+ }
+}
+
+u8 *quic_put_data(u8 *p, u8 *data, u32 len)
+{
+ if (!len)
+ return p;
+
+ memcpy(p, data, len);
+ return p + len;
+}
+
+/* Writes a transport parameter as two varints: ID and value length, followed
+ * by value.
+ */
+u8 *quic_put_param(u8 *p, u16 id, u64 value)
+{
+ p = quic_put_var(p, id);
+ p = quic_put_var(p, quic_var_len(value));
+ return quic_put_var(p, value);
+}
+
+/* Reads a QUIC transport parameter value. */
+u8 quic_get_param(u64 *pdest, u8 **pp, u32 *plen)
+{
+ u64 valuelen;
+
+ if (!quic_get_var(pp, plen, &valuelen))
+ return 0;
+
+ if (*plen < valuelen)
+ return 0;
+
+ if (quic_get_var(pp, plen, pdest) != valuelen)
+ return 0;
+
+ return (u8)valuelen;
+}
+
+/* rfc9000#section-a.3: DecodePacketNumber()
+ *
+ * Reconstructs the full packet number from a truncated one.
+ */
+s64 quic_get_num(s64 max_pkt_num, s64 pkt_num, u32 n)
+{
+ s64 expected = max_pkt_num + 1;
+ s64 win = BIT_ULL(n * 8);
+ s64 hwin = win / 2;
+ s64 mask = win - 1;
+ s64 cand;
+
+ cand = (expected & ~mask) | pkt_num;
+ if (cand <= expected - hwin && cand < BIT_ULL(QUIC_PN_BITS) - win)
+ return cand + win;
+ if (cand > expected + hwin && cand >= win)
+ return cand - win;
+ return cand;
+}
+
+int quic_data_dup(struct quic_data *to, u8 *data, u32 len, gfp_t gfp)
+{
+ if (!len) {
+ quic_data_free(to);
+ return 0;
+ }
+
+ data = kmemdup(data, len, gfp);
+ if (!data)
+ return -ENOMEM;
+
+ kfree(to->data);
+ to->data = data;
+ to->len = len;
+ return 0;
+}
+
+int quic_data_append(struct quic_data *to, u8 *data, u32 len, gfp_t gfp)
+{
+ u8 *p;
+
+ if (!len)
+ return 0;
+
+ if (to->len > U32_MAX - len)
+ return -EOVERFLOW;
+
+ p = kmalloc(to->len + len, gfp);
+ if (!p)
+ return -ENOMEM;
+ p = quic_put_data(p, to->data, to->len);
+ p = quic_put_data(p, data, len);
+
+ kfree(to->data);
+ to->len = to->len + len;
+ to->data = p - to->len;
+ return 0;
+}
+
+/* Check whether 'd2' is equal to any element inside the list 'd1'.
+ *
+ * 'd1' is assumed to be a sequence of length-prefixed elements. Each element
+ * is compared to 'd2' using 'quic_data_cmp()'.
+ *
+ * Returns true if a match is found, false otherwise.
+ */
+bool quic_data_has(struct quic_data *d1, struct quic_data *d2)
+{
+ struct quic_data d;
+ u64 length;
+ u32 len;
+ u8 *p;
+
+ for (p = d1->data, len = d1->len; len; len -= length, p += length) {
+ if (!quic_get_int(&p, &len, &length, 1) || len < length)
+ return false;
+ quic_data(&d, p, length);
+ if (!quic_data_cmp(&d, d2))
+ return true;
+ }
+ return false;
+}
+
+/* Check if any element of 'd1' is present in the list 'd2'.
+ *
+ * Iterates through each element in 'd1', and uses 'quic_data_has()' to check
+ * for its presence in 'd2'.
+ *
+ * Returns true if any match is found, false otherwise.
+ */
+bool quic_data_match(struct quic_data *d1, struct quic_data *d2)
+{
+ struct quic_data d;
+ u64 length;
+ u32 len;
+ u8 *p;
+
+ for (p = d1->data, len = d1->len; len; len -= length, p += length) {
+ if (!quic_get_int(&p, &len, &length, 1) || len < length)
+ return false;
+ quic_data(&d, p, length);
+ if (quic_data_has(d2, &d))
+ return true;
+ }
+ return false;
+}
+
+/* Serialize a list of 'quic_data' elements into a comma-separated string.
+ *
+ * Each element in 'from' is length-prefixed. This function copies their raw
+ * content into the output buffer 'to', inserting commas in between. The
+ * resulting string length is written to '*plen'.
+ */
+int quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from)
+{
+ u32 remlen = *plen;
+ struct quic_data d;
+ u8 *data = to, *p;
+ u64 length;
+ u32 len;
+
+ p = from->data;
+ len = from->len;
+ while (len) {
+ if (!quic_get_int(&p, &len, &length, 1) || len < length)
+ return -EINVAL;
+
+ quic_data(&d, p, length);
+ if (d.len > remlen)
+ return -EOVERFLOW;
+
+ data = quic_put_data(data, d.data, d.len);
+ remlen -= d.len;
+ p += d.len;
+ len -= d.len;
+ if (len) {
+ if (!remlen)
+ return -EOVERFLOW;
+ data = quic_put_int(data, ',', 1);
+ remlen--;
+ }
+ }
+ *plen = data - to;
+ return 0;
+}
+
+/* Parse a comma-separated string into a 'quic_data' list format.
+ *
+ * Each comma-separated token is turned into a length-prefixed element. The
+ * first byte of each element stores the length. Elements are stored in
+ * 'to->data', and 'to->len' is updated.
+ */
+int quic_data_from_string(struct quic_data *to, u8 *from, u32 len)
+{
+ u32 remlen = to->len;
+ struct quic_data d;
+ u8 *p = to->data;
+
+ to->len = 0;
+ while (len) {
+ while (len && *from == ' ') {
+ from++;
+ len--;
+ }
+ if (!len)
+ break;
+ if (!remlen)
+ return -EOVERFLOW;
+ d.data = p++;
+ d.len = 0;
+ remlen--;
+ while (len) {
+ if (*from == ',') {
+ from++;
+ len--;
+ break;
+ }
+ if (!remlen)
+ return -EOVERFLOW;
+ *p++ = *from++;
+ len--;
+ d.len++;
+ remlen--;
+ }
+ if (d.len > U8_MAX)
+ return -EINVAL;
+ *d.data = (u8)(d.len);
+ to->len += d.len + 1;
+ }
+ return 0;
+}
diff --git a/net/quic/common.h b/net/quic/common.h
new file mode 100644
index 0000000000000..6cb2b1b89cfb1
--- /dev/null
+++ b/net/quic/common.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/net_namespace.h>
+
+#define QUIC_MAX_ACK_DELAY (16384 * 1000)
+#define QUIC_DEF_ACK_DELAY 25000
+
+#define QUIC_STREAM_BIT_FIN 0x01
+#define QUIC_STREAM_BIT_LEN 0x02
+#define QUIC_STREAM_BIT_OFF 0x04
+#define QUIC_STREAM_BIT_MASK 0x08
+
+#define QUIC_CONN_ID_MAX_LEN 20
+#define QUIC_CONN_ID_DEF_LEN 8
+
+#define QUIC_PN_MAX_LEN 4 /* For encoded packet number */
+#define QUIC_PN_BITS 62
+#define QUIC_PN_MAX (BIT_ULL(QUIC_PN_BITS) - 1)
+
+#define QUIC_VARINT_1BYTE_MAX 0x3fULL
+#define QUIC_VARINT_2BYTE_MAX 0x3fffULL
+#define QUIC_VARINT_4BYTE_MAX 0x3fffffffULL
+#define QUIC_VARINT_8BYTE_MAX 0x3fffffffffffffffULL
+
+struct quic_conn_id {
+ u8 data[QUIC_CONN_ID_MAX_LEN];
+ u8 len;
+};
+
+static inline void quic_conn_id_update(struct quic_conn_id *conn_id, u8 *data,
+ u32 len)
+{
+ /* The caller must ensure len does not exceed QUIC_CONN_ID_MAX_LEN. */
+ if (WARN_ON_ONCE(len > QUIC_CONN_ID_MAX_LEN))
+ return;
+ memcpy(conn_id->data, data, len);
+ conn_id->len = (u8)len;
+}
+
+struct quic_skb_cb {
+ /* Callback and temporary context when encryption/decryption completes
+ * in async mode
+ */
+ void (*crypto_done)(struct sk_buff *skb, int err);
+ void *crypto_ctx;
+ union {
+ struct sk_buff *last; /* Last packet in bundle on TX */
+ u64 time; /* Arrival timestamp in UDP tunnel on RX */
+ };
+ s64 number; /* Parsed packet number, or the largest previously seen */
+ u32 seqno; /* Dest connection ID number on RX */
+ u16 length; /* Payload length + packet number length */
+
+ u16 number_offset; /* Offset of packet number field */
+ u8 number_len; /* Length of the packet number field */
+ u8 level; /* Encryption level: Initial, Handshake, App, or Early */
+
+ u16 errcode; /* Error code on packet processing failure */
+ u8 errframe; /* Frame type causing packet processing failure */
+
+ u8 key_update:1; /* Key update triggered by this packet */
+ u8 key_phase:1; /* Key phase used (0 or 1) */
+ u8 backlog:1; /* Enqueued into backlog list */
+ u8 resume:1; /* Crypto already processed (encrypted or decrypted) */
+ u8 path:1; /* Packet arrived from a new or migrating path */
+ u8 sync:1; /* Force synchronous crypto (process context only) */
+ u8 ecn:2; /* ECN marking used on TX */
+};
+
+#define QUIC_SKB_CB(skb) ((struct quic_skb_cb *)&((skb)->cb[0]))
+
+struct quichdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 pnl:2,
+ key:1,
+ reserved:2,
+ spin:1,
+ fixed:1,
+ form:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 form:1,
+ fixed:1,
+ spin:1,
+ reserved:2,
+ key:1,
+ pnl:2;
+#endif
+};
+
+static inline struct quichdr *quic_hdr(struct sk_buff *skb)
+{
+ return (struct quichdr *)skb->data;
+}
+
+struct quichshdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 pnl:2,
+ reserved:2,
+ type:2,
+ fixed:1,
+ form:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 form:1,
+ fixed:1,
+ type:2,
+ reserved:2,
+ pnl:2;
+#endif
+};
+
+static inline struct quichshdr *quic_hshdr(struct sk_buff *skb)
+{
+ return (struct quichshdr *)skb->data;
+}
+
+union quic_addr {
+ struct sockaddr_in6 v6;
+ struct sockaddr_in v4;
+ struct sockaddr sa;
+};
+
+static inline union quic_addr *quic_addr(const void *addr)
+{
+ return (union quic_addr *)addr;
+}
+
+struct quic_shash_head {
+ struct hlist_nulls_head head;
+ spinlock_t lock; /* Protects 'head' in atomic context */
+};
+
+struct quic_shash_table {
+ struct quic_shash_head *hash;
+ u32 size;
+};
+
+struct quic_uhash_head {
+ struct hlist_head head;
+ struct mutex lock; /* Protects 'head' in process context */
+};
+
+struct quic_uhash_table {
+ struct quic_uhash_head *hash;
+ u32 size;
+};
+
+struct quic_data {
+ u8 *data;
+ u32 len;
+};
+
+static inline struct quic_data *quic_data(struct quic_data *d, u8 *data,
+ u32 len)
+{
+ d->data = data;
+ d->len = len;
+ return d;
+}
+
+static inline int quic_data_cmp(struct quic_data *d1, struct quic_data *d2)
+{
+ return d1->len != d2->len || memcmp(d1->data, d2->data, d1->len);
+}
+
+static inline void quic_data_free(struct quic_data *d)
+{
+ kfree(d->data);
+ d->data = NULL;
+ d->len = 0;
+}
+
+static inline u64 quic_ktime_get_us(void)
+{
+ return ktime_to_us(ktime_get());
+}
+
+u32 quic_sock_hash(struct net *net, union quic_addr *s, union quic_addr *d);
+struct quic_shash_head *quic_sock_head(u32 hash);
+u32 quic_sock_hash_size(void);
+
+u32 quic_listen_sock_hash(struct net *net, u16 port);
+struct quic_shash_head *quic_listen_sock_head(u32 hash);
+u32 quic_listen_sock_hash_size(void);
+
+struct quic_shash_head *quic_source_conn_id_head(struct net *net, u8 *scid,
+ u32 len);
+struct quic_uhash_head *quic_udp_sock_head(struct net *net, u16 port);
+u32 quic_addr_hash(struct net *net, union quic_addr *a);
+
+void quic_hash_tables_destroy(void);
+int quic_hash_tables_init(void);
+
+u32 quic_get_data(u8 **pp, u32 *plen, u8 *data, u32 len);
+u32 quic_get_int(u8 **pp, u32 *plen, u64 *val, u32 len);
+s64 quic_get_num(s64 max_pkt_num, s64 pkt_num, u32 n);
+u8 quic_get_param(u64 *pdest, u8 **pp, u32 *plen);
+u8 quic_get_var(u8 **pp, u32 *plen, u64 *val);
+u8 quic_var_len(u64 n);
+
+u8 *quic_put_param(u8 *p, u16 id, u64 value);
+u8 *quic_put_data(u8 *p, u8 *data, u32 len);
+u8 *quic_put_varint(u8 *p, u64 num, u8 len);
+u8 *quic_put_int(u8 *p, u64 num, u8 len);
+u8 *quic_put_var(u8 *p, u64 num);
+
+int quic_data_from_string(struct quic_data *to, u8 *from, u32 len);
+int quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from);
+
+int quic_data_append(struct quic_data *to, u8 *data, u32 len, gfp_t gfp);
+int quic_data_dup(struct quic_data *to, u8 *data, u32 len, gfp_t gfp);
+bool quic_data_match(struct quic_data *d1, struct quic_data *d2);
+bool quic_data_has(struct quic_data *d1, struct quic_data *d2);
diff --git a/net/quic/cong.c b/net/quic/cong.c
new file mode 100644
index 0000000000000..78f28bbdfb374
--- /dev/null
+++ b/net/quic/cong.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <linux/quic.h>
+
+#include "common.h"
+#include "cong.h"
+
+/* NEW RENO APIs */
+static void quic_reno_handle_packet_lost(struct quic_cong *cong)
+{
+ switch (cong->state) {
+ case QUIC_CONG_SLOW_START:
+ pr_debug("%s: slow_start -> recovery, cwnd: %u, ssth: %u\n",
+ __func__, cong->window, cong->ssthresh);
+ break;
+ case QUIC_CONG_RECOVERY_PERIOD:
+ return;
+ case QUIC_CONG_CONGESTION_AVOIDANCE:
+ pr_debug("%s: cong_avoid -> recovery, cwnd: %u, ssth: %u\n",
+ __func__, cong->window, cong->ssthresh);
+ break;
+ default:
+ pr_debug("%s: wrong congestion state: %d\n", __func__,
+ cong->state);
+ return;
+ }
+
+ cong->recovery_time = cong->time;
+ cong->state = QUIC_CONG_RECOVERY_PERIOD;
+ cong->ssthresh = max(cong->window >> 1U, cong->min_window);
+ cong->window = cong->ssthresh;
+}
+
+static void quic_reno_on_packet_lost(struct quic_cong *cong, u64 time,
+ u32 bytes, s64 number)
+{
+ quic_reno_handle_packet_lost(cong);
+}
+
+static void quic_reno_on_packet_acked(struct quic_cong *cong, u64 time,
+ u32 bytes, s64 number)
+{
+ u64 new_window;
+
+ switch (cong->state) {
+ case QUIC_CONG_SLOW_START:
+ new_window = (u64)cong->window + bytes;
+ cong->window = min_t(u64, new_window, cong->max_window);
+ if (cong->window < cong->ssthresh)
+ break;
+ cong->state = QUIC_CONG_CONGESTION_AVOIDANCE;
+ pr_debug("%s: slow_start -> cong_avoid, cwnd: %u, ssth: %u\n",
+ __func__, cong->window, cong->ssthresh);
+ break;
+ case QUIC_CONG_RECOVERY_PERIOD:
+ if (cong->recovery_time >= time)
+ break;
+ cong->state = QUIC_CONG_CONGESTION_AVOIDANCE;
+ pr_debug("%s: recovery -> cong_avoid, cwnd: %u, ssth: %u\n",
+ __func__, cong->window, cong->ssthresh);
+ fallthrough;
+ case QUIC_CONG_CONGESTION_AVOIDANCE:
+ /* cong->window is never zero; it is initialized by
+ * quic_packet_route() during connect/accept.
+ */
+ new_window = div64_ul((u64)cong->mss * bytes, cong->window) +
+ cong->window;
+ cong->window = min_t(u64, new_window, cong->max_window);
+ break;
+ default:
+ pr_debug("%s: wrong congestion state: %d\n", __func__,
+ cong->state);
+ return;
+ }
+}
+
+static void quic_reno_on_process_ecn(struct quic_cong *cong)
+{
+ quic_reno_handle_packet_lost(cong);
+}
+
+static void quic_reno_on_init(struct quic_cong *cong)
+{
+}
+
+static const struct quic_cong_ops quic_congs[] = {
+ { /* QUIC_CONG_ALG_RENO */
+ .on_packet_acked = quic_reno_on_packet_acked,
+ .on_packet_lost = quic_reno_on_packet_lost,
+ .on_process_ecn = quic_reno_on_process_ecn,
+ .on_init = quic_reno_on_init,
+ },
+};
+
+static bool quic_cong_check_persistent_congestion(struct quic_cong *cong,
+ u64 time)
+{
+ u32 ssthresh;
+
+ time -= cong->pc_start_time;
+
+ /* rfc9002#section-7.6.1:
+ * (smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay) *
+ * kPersistentCongestionThreshold
+ */
+ ssthresh = cong->smoothed_rtt +
+ max(4 * cong->rttvar, QUIC_KGRANULARITY);
+ ssthresh = (ssthresh + cong->max_ack_delay) *
+ QUIC_KPERSISTENT_CONGESTION_THRESHOLD;
+
+ return time > ssthresh;
+}
+
+/* COMMON APIs */
+void quic_cong_on_packet_lost(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number)
+{
+ if (cong->pc_start_time && time > cong->pc_start_time &&
+ quic_cong_check_persistent_congestion(cong, time)) {
+ cong->pc_start_time = 0;
+ cong->min_rtt_valid = 0;
+ cong->window = cong->min_window;
+ cong->state = QUIC_CONG_SLOW_START;
+ return;
+ }
+
+ if (!cong->pc_start_time && cong->is_rtt_set)
+ cong->pc_start_time = time;
+
+ cong->ops->on_packet_lost(cong, time, bytes, number);
+}
+
+void quic_cong_on_packet_acked(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number)
+{
+ /* When a packet is acked, if time - cong->pc_start_time <= duration
+ * threshold, it means the acked packet was sent within the persistent
+ * congestion window.
+ *
+ * This breaks the condition in rfc9002#section-7.6.2:
+ *
+ * - across all packet number spaces, none of the packets sent between
+ * the send times of these two packets are acknowledged;
+ *
+ * so pc_start_time is reset to 0.
+ */
+ if (cong->pc_start_time && time > cong->pc_start_time &&
+ !quic_cong_check_persistent_congestion(cong, time))
+ cong->pc_start_time = 0;
+
+ cong->ops->on_packet_acked(cong, time, bytes, number);
+}
+
+void quic_cong_on_process_ecn(struct quic_cong *cong)
+{
+ cong->ops->on_process_ecn(cong);
+}
+
+/* Update Probe Timeout (PTO) and loss detection delay based on RTT stats. */
+static void quic_cong_pto_update(struct quic_cong *cong)
+{
+ u32 loss_delay;
+
+ /* rfc9002#section-6.2.1:
+ * PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay
+ *
+ * Calculate the base PTO here, excluding max_ack_delay. max_ack_delay
+ * is added when calculating the PTO for the App packet number space.
+ */
+ cong->pto = cong->smoothed_rtt +
+ max(4 * cong->rttvar, QUIC_KGRANULARITY);
+
+ /* rfc9002#section-6.1.2:
+ * max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity)
+ */
+ loss_delay = QUIC_KTIME_THRESHOLD(max(cong->smoothed_rtt,
+ cong->latest_rtt));
+ cong->loss_delay = max(loss_delay, QUIC_KGRANULARITY);
+
+ pr_debug("%s: update pto: %u\n", __func__, cong->pto);
+}
+
+/* Update pacing timestamp after sending 'bytes' bytes.
+ *
+ * This function tracks when the next packet is allowed to be sent based on
+ * pacing rate.
+ */
+static void quic_cong_update_pacing_time(struct quic_cong *cong, u32 bytes)
+{
+ u64 prior_time, credit, len_ns, rate = cong->pacing_rate;
+
+ if (!rate)
+ return;
+
+ prior_time = cong->pacing_time;
+ cong->pacing_time = max(cong->pacing_time, ktime_get_ns());
+ credit = cong->pacing_time - prior_time;
+
+ /* take into account OS jitter */
+ len_ns = div64_u64((u64)bytes * NSEC_PER_SEC, rate);
+ len_ns -= min_t(u64, len_ns / 2, credit);
+ cong->pacing_time += len_ns;
+}
+
+/* Compute and update the pacing rate based on congestion window and smoothed
+ * RTT.
+ */
+static void quic_cong_pace_update(struct quic_cong *cong, u64 max_rate)
+{
+ u64 rate;
+
+ if (unlikely(!cong->smoothed_rtt))
+ return;
+
+ /* rate = N * congestion_window / smoothed_rtt */
+ rate = div64_ul((u64)cong->window * USEC_PER_SEC * 2,
+ cong->smoothed_rtt);
+
+ cong->pacing_rate = min_t(u64, rate, max_rate);
+ pr_debug("%s: update pacing rate: %llu, max rate: %llu, srtt: %u\n",
+ __func__, cong->pacing_rate, max_rate, cong->smoothed_rtt);
+}
+
+void quic_cong_on_packet_sent(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number)
+{
+ if (!bytes)
+ return;
+ if (cong->ops->on_packet_sent)
+ cong->ops->on_packet_sent(cong, time, bytes, number);
+ quic_cong_update_pacing_time(cong, bytes);
+}
+
+void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u64 max_rate)
+{
+ if (!bytes)
+ return;
+ if (cong->ops->on_ack_recv)
+ cong->ops->on_ack_recv(cong, bytes, max_rate);
+ quic_cong_pace_update(cong, max_rate);
+}
+
+/* rfc9002#section-5: Estimating the Round-Trip Time */
+void quic_cong_rtt_update(struct quic_cong *cong, u64 time, u32 ack_delay)
+{
+ u32 adjusted_rtt, rttvar_sample;
+
+ /* Ignore RTT sample if ACK delay is suspiciously large. */
+ if (ack_delay > cong->max_ack_delay * 2 ||
+ cong->time - time > QUIC_RTT_MAX)
+ return;
+
+ /* rfc9002#section-5.1:
+ * latest_rtt = ack_time - send_time_of_largest_acked
+ */
+ cong->latest_rtt = cong->time - time;
+
+ /* rfc9002#section-5.2: Estimating min_rtt */
+ if (!cong->min_rtt_valid) {
+ cong->min_rtt = cong->latest_rtt;
+ cong->min_rtt_valid = 1;
+ }
+ if (cong->min_rtt > cong->latest_rtt)
+ cong->min_rtt = cong->latest_rtt;
+
+ if (!cong->is_rtt_set) {
+ /* rfc9002#section-5.3:
+ * smoothed_rtt = latest_rtt
+ * rttvar = latest_rtt / 2
+ */
+ cong->smoothed_rtt = cong->latest_rtt;
+ cong->rttvar = cong->smoothed_rtt / 2;
+ quic_cong_pto_update(cong);
+ cong->is_rtt_set = 1;
+ return;
+ }
+
+ /* rfc9002#section-5.3:
+ * adjusted_rtt = latest_rtt
+ * if (latest_rtt >= min_rtt + ack_delay):
+ * adjusted_rtt = latest_rtt - ack_delay
+ * smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt
+ * rttvar_sample = abs(smoothed_rtt - adjusted_rtt)
+ * rttvar = 3/4 * rttvar + 1/4 * rttvar_sample
+ */
+ adjusted_rtt = cong->latest_rtt;
+ if (cong->latest_rtt >= cong->min_rtt + ack_delay)
+ adjusted_rtt = cong->latest_rtt - ack_delay;
+
+ cong->smoothed_rtt = (cong->smoothed_rtt * 7 + adjusted_rtt) / 8;
+ rttvar_sample = abs_diff(cong->smoothed_rtt, adjusted_rtt);
+ cong->rttvar = (cong->rttvar * 3 + rttvar_sample) / 4;
+ quic_cong_pto_update(cong);
+
+ if (cong->ops->on_rtt_update)
+ cong->ops->on_rtt_update(cong);
+}
+
+void quic_cong_set_algo(struct quic_cong *cong, u8 algo)
+{
+ /* The caller must ensure algo < QUIC_CONG_ALG_MAX. */
+ if (WARN_ON_ONCE(algo >= QUIC_CONG_ALG_MAX))
+ return;
+ cong->algo = algo;
+ cong->state = QUIC_CONG_SLOW_START;
+ cong->ssthresh = U32_MAX;
+ cong->ops = &quic_congs[algo];
+ cong->ops->on_init(cong);
+}
+
+void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt)
+{
+ /* rfc9002#section-5.3:
+ * smoothed_rtt = kInitialRtt
+ * rttvar = kInitialRtt / 2
+ */
+ cong->initial_srtt = srtt;
+ cong->latest_rtt = srtt;
+ cong->smoothed_rtt = cong->latest_rtt;
+ cong->rttvar = cong->smoothed_rtt / 2;
+ quic_cong_pto_update(cong);
+}
+
+void quic_cong_init(struct quic_cong *cong)
+{
+ cong->max_ack_delay = QUIC_DEF_ACK_DELAY;
+ cong->max_window = S32_MAX / 4;
+ quic_cong_set_algo(cong, QUIC_CONG_ALG_RENO);
+ quic_cong_set_srtt(cong, QUIC_RTT_INIT);
+}
diff --git a/net/quic/cong.h b/net/quic/cong.h
new file mode 100644
index 0000000000000..0d3697eb916a0
--- /dev/null
+++ b/net/quic/cong.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_KPERSISTENT_CONGESTION_THRESHOLD 3
+#define QUIC_KPACKET_THRESHOLD 3
+#define QUIC_KTIME_THRESHOLD(rtt) ((rtt) * 9 / 8)
+#define QUIC_KGRANULARITY 1000U
+
+#define QUIC_RTT_INIT 333000U
+#define QUIC_RTT_MAX 6000000U
+#define QUIC_RTT_MIN QUIC_KGRANULARITY
+
+/* rfc9002#section-7.3: Congestion Control States
+ *
+ * New path or +------------+
+ * persistent congestion | Slow |
+ * (O)---------------------->| Start |
+ * +------------+
+ * |
+ * Loss or |
+ * ECN-CE increase |
+ * v
+ * +------------+ Loss or +------------+
+ * | Congestion | ECN-CE increase | Recovery |
+ * | Avoidance |------------------>| Period |
+ * +------------+ +------------+
+ * ^ |
+ * | |
+ * +----------------------------+
+ * Acknowledgment of packet
+ * sent during recovery
+ */
+enum quic_cong_state {
+ QUIC_CONG_SLOW_START,
+ QUIC_CONG_RECOVERY_PERIOD,
+ QUIC_CONG_CONGESTION_AVOIDANCE,
+};
+
+struct quic_cong {
+ /* RTT tracking */
+ u32 max_ack_delay; /* max_ack_delay from rfc9000#section-18.2 */
+ u32 smoothed_rtt; /* Smoothed RTT */
+ u32 latest_rtt; /* Latest RTT sample */
+ u32 min_rtt; /* Lowest observed RTT */
+ u32 rttvar; /* RTT variation */
+ u32 pto; /* Probe timeout */
+
+ /* Timing & pacing */
+ u64 pc_start_time; /* Persistent congestion tracking timestamp */
+ u64 recovery_time; /* Recovery period start timestamp */
+ u64 pacing_rate; /* Packet sending speed Bytes/sec */
+ u64 pacing_time; /* Next scheduled send timestamp (ns) */
+ u64 time; /* Cached current timestamp */
+
+ /* Congestion window */
+ u32 max_window; /* Max growth cap */
+ u32 min_window; /* Min window limit */
+ u32 loss_delay; /* Time before marking loss */
+ u32 ssthresh; /* Slow start threshold */
+ u32 window; /* Bytes in flight allowed */
+ u32 mss; /* QUIC MSS (excl. UDP) */
+
+ /* Algorithm-specific */
+ const struct quic_cong_ops *ops;
+ u64 priv[8]; /* Algo private data */
+
+ u32 initial_srtt; /* Initial smoothed RTT */
+ u8 algo; /* Congestion control algorithm */
+
+ /* Flags & state */
+ u8 min_rtt_valid; /* min_rtt initialized */
+ u8 is_rtt_set; /* RTT samples exist */
+ u8 state; /* State machine in rfc9002#section-7.3 */
+};
+
+/* Hooks for congestion control algorithms */
+struct quic_cong_ops {
+ void (*on_packet_acked)(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+ void (*on_packet_lost)(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+ void (*on_process_ecn)(struct quic_cong *cong);
+ void (*on_init)(struct quic_cong *cong);
+
+ /* Optional callbacks */
+ void (*on_packet_sent)(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+ void (*on_ack_recv)(struct quic_cong *cong, u32 bytes, u64 max_rate);
+ void (*on_rtt_update)(struct quic_cong *cong);
+};
+
+static inline void quic_cong_set_mss(struct quic_cong *cong, u32 mss)
+{
+ if (cong->mss == mss)
+ return;
+
+ /* rfc9002#section-7.2: Initial and Minimum Congestion Window */
+ cong->mss = mss;
+ cong->min_window = mss * 2;
+
+ if (!cong->window)
+ cong->window = min(mss * 10, 14720U);
+ if (cong->window < cong->min_window)
+ cong->window = cong->min_window;
+}
+
+static inline void *quic_cong_priv(struct quic_cong *cong)
+{
+ return (void *)cong->priv;
+}
+
+void quic_cong_on_packet_acked(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+void quic_cong_on_packet_lost(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+void quic_cong_on_process_ecn(struct quic_cong *cong);
+
+void quic_cong_on_packet_sent(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u64 max_rate);
+void quic_cong_rtt_update(struct quic_cong *cong, u64 time, u32 ack_delay);
+
+void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt);
+void quic_cong_set_algo(struct quic_cong *cong, u8 algo);
+void quic_cong_init(struct quic_cong *cong);
diff --git a/net/quic/connid.c b/net/quic/connid.c
new file mode 100644
index 0000000000000..e726a1d02c970
--- /dev/null
+++ b/net/quic/connid.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <crypto/utils.h>
+#include <linux/quic.h>
+#include <net/sock.h>
+
+#include "common.h"
+#include "connid.h"
+
+/* Lookup a source connection ID (scid) in the global source connection ID hash
+ * table.
+ */
+struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len)
+{
+ struct quic_shash_head *head = quic_source_conn_id_head(net, scid, len);
+ struct quic_source_conn_id *s_conn_id;
+ struct quic_conn_id *conn_id = NULL;
+ struct hlist_nulls_node *node;
+
+ WARN_ON_ONCE(!rcu_read_lock_held());
+
+ hlist_nulls_for_each_entry_rcu(s_conn_id, node, &head->head, node) {
+ if (net != sock_net(s_conn_id->sk))
+ continue;
+ if (s_conn_id->common.id.len != len ||
+ memcmp(scid, &s_conn_id->common.id.data, len))
+ continue;
+ if (likely(refcount_inc_not_zero(&s_conn_id->sk->sk_refcnt)))
+ conn_id = &s_conn_id->common.id;
+ break;
+ }
+ /* No need to check get_nulls_value(node) != hash for !conn_id, as
+ * hashtable size is fixed and a conn_id can not rehashed.
+ */
+ return conn_id;
+}
+
+/* Check if a given stateless reset token exists in any connection ID in the
+ * connection ID set.
+ */
+bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token)
+{
+ struct quic_common_conn_id *common;
+ struct quic_dest_conn_id *dcid;
+
+ dcid = (struct quic_dest_conn_id *)id_set->active;
+ if (memchr_inv(dcid->token, 0, QUIC_CONN_ID_TOKEN_LEN) &&
+ !crypto_memneq(dcid->token, token, QUIC_CONN_ID_TOKEN_LEN))
+ return true; /* Fast path. */
+
+ list_for_each_entry(common, &id_set->head, list) {
+ dcid = (struct quic_dest_conn_id *)common;
+ if (common == id_set->active)
+ continue;
+ if (memchr_inv(dcid->token, 0, QUIC_CONN_ID_TOKEN_LEN) &&
+ !crypto_memneq(dcid->token, token, QUIC_CONN_ID_TOKEN_LEN))
+ return true;
+ }
+ return false;
+}
+
+static void quic_source_conn_id_free_rcu(struct rcu_head *head)
+{
+ struct quic_source_conn_id *s_conn_id;
+
+ s_conn_id = container_of(head, struct quic_source_conn_id, rcu);
+ kfree(s_conn_id);
+}
+
+static void quic_source_conn_id_free(struct quic_source_conn_id *s_conn_id)
+{
+ u8 *data = s_conn_id->common.id.data;
+ u32 len = s_conn_id->common.id.len;
+ struct quic_shash_head *head;
+
+ head = quic_source_conn_id_head(sock_net(s_conn_id->sk), data, len);
+ spin_lock_bh(&head->lock);
+ hlist_nulls_del_init_rcu(&s_conn_id->node);
+ spin_unlock_bh(&head->lock);
+
+ /* Freeing is deferred via RCU to avoid use-after-free during
+ * concurrent lookups.
+ */
+ call_rcu(&s_conn_id->rcu, quic_source_conn_id_free_rcu);
+}
+
+static void quic_conn_id_del(struct quic_common_conn_id *common)
+{
+ list_del(&common->list);
+ if (!common->hashed) {
+ kfree(common);
+ return;
+ }
+ quic_source_conn_id_free((struct quic_source_conn_id *)common);
+}
+
+/* Add a connection ID with sequence number and associated private data to the
+ * connection ID set.
+ */
+int quic_conn_id_add(struct quic_conn_id_set *id_set,
+ struct quic_conn_id *conn_id, u32 number, void *data,
+ gfp_t gfp)
+{
+ bool dest = id_set->entry_size == sizeof(struct quic_dest_conn_id);
+ struct quic_source_conn_id *s_conn_id, *pos;
+ struct quic_dest_conn_id *d_conn_id;
+ struct quic_common_conn_id *common;
+ struct hlist_nulls_node *node;
+ struct quic_shash_head *head;
+ struct list_head *list;
+ struct net *net;
+
+ /* Locate insertion point to keep list ordered by number. */
+ list = &id_set->head;
+ list_for_each_entry(common, list, list) {
+ if (number == common->number) {
+ if (quic_conn_id_cmp(&common->id, conn_id))
+ return -EINVAL;
+ if (dest && data) {
+ d_conn_id = (struct quic_dest_conn_id *)common;
+ if (crypto_memneq(d_conn_id->token, data,
+ QUIC_CONN_ID_TOKEN_LEN))
+ return -EINVAL;
+ }
+ return 0; /* Ignore if it already exists on the list. */
+ }
+ if (number < common->number) {
+ list = &common->list;
+ break;
+ }
+ }
+
+ if (conn_id->len > QUIC_CONN_ID_MAX_LEN)
+ return -EINVAL;
+ common = kzalloc(id_set->entry_size, gfp);
+ if (!common)
+ return -ENOMEM;
+ common->id = *conn_id;
+ common->number = number;
+ if (dest) {
+ /* For destination connection IDs, copy the stateless reset
+ * token if available.
+ */
+ if (data) {
+ d_conn_id = (struct quic_dest_conn_id *)common;
+ memcpy(d_conn_id->token, data, QUIC_CONN_ID_TOKEN_LEN);
+ }
+ } else {
+ /* For source connection IDs, mark as hashed and insert into
+ * the global source connection ID hashtable.
+ */
+ common->hashed = 1;
+ s_conn_id = (struct quic_source_conn_id *)common;
+ s_conn_id->sk = data;
+ net = sock_net(s_conn_id->sk);
+
+ head = quic_source_conn_id_head(net, common->id.data,
+ common->id.len);
+ spin_lock_bh(&head->lock);
+
+ /* Check for collision before inserting */
+ hlist_nulls_for_each_entry(pos, node, &head->head, node) {
+ if (net != sock_net(pos->sk))
+ continue;
+ if (quic_conn_id_cmp(&pos->common.id, &common->id))
+ continue;
+ spin_unlock_bh(&head->lock);
+ kfree(common);
+ WARN_ON_ONCE(1);
+ return -EEXIST;
+ }
+
+ hlist_nulls_add_head_rcu(&s_conn_id->node, &head->head);
+ spin_unlock_bh(&head->lock);
+ }
+ list_add_tail(&common->list, list);
+
+ if (number == quic_conn_id_last_number(id_set) + 1) {
+ if (!id_set->active)
+ id_set->active = common;
+ id_set->count++;
+
+ /* Increment count for consecutive following IDs. */
+ list_for_each_entry_continue(common, &id_set->head, list) {
+ if (common->number != ++number)
+ break;
+ id_set->count++;
+ }
+ }
+ return 0;
+}
+
+/* Remove consecutive connection IDs from the set with sequence numbers less
+ * than or equal to a number.
+ */
+void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number)
+{
+ struct quic_common_conn_id *common, *tmp;
+ struct list_head *list;
+
+ /* The number must be less than the sequence number of the last
+ * consecutive connection ID in the set.
+ */
+ if (WARN_ON_ONCE(number >= quic_conn_id_last_number(id_set)))
+ return;
+ list = &id_set->head;
+ list_for_each_entry_safe(common, tmp, list, list) {
+ if (common->number > number)
+ break;
+ if (id_set->active == common)
+ id_set->active = tmp;
+ if (id_set->alt == common)
+ id_set->alt = tmp;
+ quic_conn_id_del(common);
+ id_set->count--;
+ }
+}
+
+struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set,
+ u32 number)
+{
+ struct quic_common_conn_id *common;
+
+ list_for_each_entry(common, &id_set->head, list) {
+ if (common->number > number)
+ break;
+ if (common->number == number)
+ return &common->id;
+ }
+ return NULL;
+}
+
+void quic_conn_id_update_active(struct quic_conn_id_set *id_set, u32 number)
+{
+ struct quic_conn_id *conn_id;
+
+ if (number == id_set->active->number)
+ return;
+ conn_id = quic_conn_id_find(id_set, number);
+ if (!conn_id)
+ return;
+ quic_conn_id_set_active(id_set, conn_id);
+}
+
+void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source)
+{
+ id_set->entry_size = source ? sizeof(struct quic_source_conn_id) :
+ sizeof(struct quic_dest_conn_id);
+ INIT_LIST_HEAD(&id_set->head);
+}
+
+void quic_conn_id_set_free(struct quic_conn_id_set *id_set)
+{
+ struct quic_common_conn_id *common, *tmp;
+
+ list_for_each_entry_safe(common, tmp, &id_set->head, list)
+ quic_conn_id_del(common);
+ id_set->count = 0;
+ id_set->alt = NULL;
+ id_set->active = NULL;
+}
+
+void quic_conn_id_get_param(struct quic_conn_id_set *id_set,
+ struct quic_transport_param *p)
+{
+ p->active_connection_id_limit = id_set->max_count;
+}
+
+void quic_conn_id_set_param(struct quic_conn_id_set *id_set,
+ struct quic_transport_param *p)
+{
+ id_set->max_count = p->active_connection_id_limit;
+}
diff --git a/net/quic/connid.h b/net/quic/connid.h
new file mode 100644
index 0000000000000..abad396a6ad6a
--- /dev/null
+++ b/net/quic/connid.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_CONN_ID_LIMIT 8
+#define QUIC_CONN_ID_DEF 7
+#define QUIC_CONN_ID_LEAST 2
+
+#define QUIC_CONN_ID_TOKEN_LEN 16
+
+/* Common fields shared by both source and destination Connection IDs */
+struct quic_common_conn_id {
+ struct quic_conn_id id; /* Connection ID value and its length */
+ struct list_head list; /* List node for connection ID management */
+ u32 number; /* Sequence number assigned to this Connection ID */
+ u8 hashed; /* Non-zero if stored in source_conn_id hash table */
+};
+
+struct quic_source_conn_id {
+ struct quic_common_conn_id common;
+ struct hlist_nulls_node node; /* Hash table node for fast lookup */
+ struct rcu_head rcu; /* RCU header for deferred destruction */
+ struct sock *sk; /* Socket associated with this Connection ID */
+};
+
+struct quic_dest_conn_id {
+ struct quic_common_conn_id common;
+ /* Stateless reset token in rfc9000#section-10.3 */
+ u8 token[QUIC_CONN_ID_TOKEN_LEN];
+};
+
+struct quic_conn_id_set {
+ /* Connection ID in use on the current path */
+ struct quic_common_conn_id *active;
+ /* Connection ID to use for a new path (e.g., after migration) */
+ struct quic_common_conn_id *alt;
+ struct list_head head; /* List head of available connection IDs */
+ u8 entry_size; /* Size of each connection ID entry in the list */
+ u8 max_count; /* active_connection_id_limit in rfc9000#section-18.2 */
+ u8 count; /* Current number of connection IDs in the list */
+};
+
+static inline u32 quic_conn_id_first_number(struct quic_conn_id_set *id_set)
+{
+ struct quic_common_conn_id *common;
+
+ /* The id_set is guaranteed to be non-empty when called (sk is not in
+ * CLOSE state).
+ */
+ common = list_first_entry(&id_set->head, struct quic_common_conn_id,
+ list);
+ return common->number;
+}
+
+static inline u32 quic_conn_id_last_number(struct quic_conn_id_set *id_set)
+{
+ return quic_conn_id_first_number(id_set) + id_set->count - 1;
+}
+
+static inline void quic_conn_id_generate(struct quic_conn_id *conn_id)
+{
+ get_random_bytes(conn_id->data, QUIC_CONN_ID_DEF_LEN);
+ conn_id->len = QUIC_CONN_ID_DEF_LEN;
+}
+
+/* Select an alternate destination Connection ID for a new path (e.g., after
+ * migration).
+ */
+static inline bool quic_conn_id_select_alt(struct quic_conn_id_set *id_set,
+ bool active)
+{
+ if (id_set->alt)
+ return true;
+ /* NAT rebinding: peer keeps using the current source conn_id.
+ * In this case, continue using the same dest conn_id for the new path.
+ */
+ if (active) {
+ id_set->alt = id_set->active;
+ return true;
+ }
+ /* Treat the prev conn_ids as used.
+ * Try selecting the next conn_id in the list, unless at the end.
+ */
+ if (id_set->active->number != quic_conn_id_last_number(id_set)) {
+ id_set->alt = list_next_entry(id_set->active, list);
+ return true;
+ }
+ /* If there's only one conn_id in the list, reuse the active one. */
+ if (id_set->active->number == quic_conn_id_first_number(id_set)) {
+ id_set->alt = id_set->active;
+ return true;
+ }
+ /* No alternate conn_id could be selected. Caller should send a
+ * QUIC_FRAME_RETIRE_CONNECTION_ID frame to request new connection IDs
+ * from the peer.
+ */
+ return false;
+}
+
+static inline void quic_conn_id_set_alt(struct quic_conn_id_set *id_set,
+ struct quic_conn_id *alt)
+{
+ id_set->alt = (struct quic_common_conn_id *)alt;
+}
+
+/* Swap the active and alternate destination Connection IDs after path
+ * migration completes, since the path has already been switched accordingly.
+ */
+static inline void quic_conn_id_swap_active(struct quic_conn_id_set *id_set)
+{
+ void *active = id_set->active;
+
+ id_set->active = id_set->alt;
+ id_set->alt = active;
+}
+
+/* Choose which destination Connection ID to use for a new path migration if
+ * alt is true.
+ */
+static inline struct quic_conn_id *
+quic_conn_id_choose(struct quic_conn_id_set *id_set, u8 alt)
+{
+ return (alt && id_set->alt) ? &id_set->alt->id : &id_set->active->id;
+}
+
+static inline struct quic_conn_id *
+quic_conn_id_active(struct quic_conn_id_set *id_set)
+{
+ return &id_set->active->id;
+}
+
+static inline void quic_conn_id_set_active(struct quic_conn_id_set *id_set,
+ struct quic_conn_id *active)
+{
+ id_set->active = (struct quic_common_conn_id *)active;
+}
+
+static inline u32 quic_conn_id_number(struct quic_conn_id *conn_id)
+{
+ return ((struct quic_common_conn_id *)conn_id)->number;
+}
+
+static inline struct sock *quic_conn_id_sk(struct quic_conn_id *conn_id)
+{
+ return ((struct quic_source_conn_id *)conn_id)->sk;
+}
+
+static inline void quic_conn_id_set_token(struct quic_conn_id *conn_id,
+ u8 *token)
+{
+ memcpy(((struct quic_dest_conn_id *)conn_id)->token, token,
+ QUIC_CONN_ID_TOKEN_LEN);
+}
+
+static inline int quic_conn_id_cmp(struct quic_conn_id *a,
+ struct quic_conn_id *b)
+{
+ return a->len != b->len || memcmp(a->data, b->data, a->len);
+}
+
+int quic_conn_id_add(struct quic_conn_id_set *id_set,
+ struct quic_conn_id *conn_id, u32 number, void *data,
+ gfp_t gfp);
+bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token);
+void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number);
+
+struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set,
+ u32 number);
+struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len);
+void quic_conn_id_update_active(struct quic_conn_id_set *id_set, u32 number);
+
+void quic_conn_id_get_param(struct quic_conn_id_set *id_set,
+ struct quic_transport_param *p);
+void quic_conn_id_set_param(struct quic_conn_id_set *id_set,
+ struct quic_transport_param *p);
+void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source);
+void quic_conn_id_set_free(struct quic_conn_id_set *id_set);
diff --git a/net/quic/crypto.c b/net/quic/crypto.c
new file mode 100644
index 0000000000000..d62c6c4bea7de
--- /dev/null
+++ b/net/quic/crypto.c
@@ -0,0 +1,1251 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <crypto/skcipher.h>
+#include <linux/skbuff.h>
+#include <crypto/utils.h>
+#include <crypto/aead.h>
+#include <crypto/hash.h>
+#include <linux/quic.h>
+#include <net/tls.h>
+
+#include "common.h"
+#include "crypto.h"
+
+/* HKDF-Extract. */
+static int quic_crypto_hkdf_extract(struct crypto_shash *tfm,
+ struct quic_data *srt,
+ struct quic_data *hash,
+ struct quic_data *key)
+{
+ int err;
+
+ err = crypto_shash_setkey(tfm, srt->data, srt->len);
+ if (err)
+ return err;
+
+ return crypto_shash_tfm_digest(tfm, hash->data, hash->len, key->data);
+}
+
+#define QUIC_MAX_INFO_LEN 256
+
+/* HKDF-Expand-Label. */
+static int quic_crypto_hkdf_expand(struct crypto_shash *tfm,
+ struct quic_data *srt,
+ struct quic_data *label,
+ struct quic_data *key)
+{
+ u8 info[QUIC_MAX_INFO_LEN], *p = info, tmp[QUIC_SECRET_LEN];
+ unsigned int i, infolen, hashlen = srt->len;
+ SHASH_DESC_ON_STACK(desc, tfm);
+ u8 LABEL[] = "tls13 ", cnt = 1;
+ const u8 *prev = NULL;
+ int err;
+
+ /* rfc8446#section-7.1:
+ *
+ * HKDF-Expand-Label(Secret, Label, Context, Length) =
+ * HKDF-Expand(Secret, HkdfLabel, Length)
+ *
+ * Where HkdfLabel is specified as:
+ *
+ * struct {
+ * uint16 length = Length;
+ * opaque label<7..255> = "tls13 " + Label;
+ * opaque context<0..255> = Context;
+ * } HkdfLabel;
+ */
+ put_unaligned_be16(key->len, p);
+ p += 2;
+ *p++ = (u8)(sizeof(LABEL) - 1 + label->len);
+ p = quic_put_data(p, LABEL, sizeof(LABEL) - 1);
+ p = quic_put_data(p, label->data, label->len);
+ *p++ = 0;
+ infolen = (unsigned int)(p - info);
+
+ err = crypto_shash_setkey(tfm, srt->data, srt->len);
+ if (err)
+ return err;
+ desc->tfm = tfm;
+
+ for (i = 0; i < key->len; i += hashlen) {
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ if (prev) {
+ err = crypto_shash_update(desc, prev, hashlen);
+ if (err)
+ goto out;
+ }
+
+ err = crypto_shash_update(desc, info, infolen);
+ if (err)
+ goto out;
+
+ BUILD_BUG_ON(sizeof(cnt) != 1);
+ if (key->len - i < hashlen) {
+ err = crypto_shash_finup(desc, &cnt, 1, tmp);
+ if (err)
+ goto out;
+ memcpy(&key->data[i], tmp, key->len - i);
+ memzero_explicit(tmp, sizeof(tmp));
+ } else {
+ err = crypto_shash_finup(desc, &cnt, 1, &key->data[i]);
+ if (err)
+ goto out;
+ }
+ cnt++;
+ prev = &key->data[i];
+ }
+out:
+ shash_desc_zero(desc);
+ memzero_explicit(tmp, sizeof(tmp));
+ return err;
+}
+
+#define KEY_LABEL_V1 "quic key"
+#define IV_LABEL_V1 "quic iv"
+#define HP_KEY_LABEL_V1 "quic hp"
+
+#define KU_LABEL_V1 "quic ku"
+
+/* rfc9369#section-3.3.2:
+ *
+ * The labels used in rfc9001 to derive packet protection keys, header
+ * protection keys, Retry Integrity Tag keys, and key updates change from "quic
+ * key" to "quicv2 key", from "quic iv" to "quicv2 iv", from "quic hp" to
+ * "quicv2 hp", and from "quic ku" to "quicv2 ku".
+ */
+#define KEY_LABEL_V2 "quicv2 key"
+#define IV_LABEL_V2 "quicv2 iv"
+#define HP_KEY_LABEL_V2 "quicv2 hp"
+
+#define KU_LABEL_V2 "quicv2 ku"
+
+/* Packet Protection Keys. */
+static int quic_crypto_keys_derive(struct crypto_shash *tfm,
+ struct quic_data *s, struct quic_data *k,
+ struct quic_data *i, struct quic_data *hp_k,
+ u32 version)
+{
+ struct quic_data hp_k_l = {HP_KEY_LABEL_V1, strlen(HP_KEY_LABEL_V1)};
+ struct quic_data k_l = {KEY_LABEL_V1, strlen(KEY_LABEL_V1)};
+ struct quic_data i_l = {IV_LABEL_V1, strlen(IV_LABEL_V1)};
+ int err;
+
+ /* rfc9001#section-5.1:
+ *
+ * The current encryption level secret and the label "quic key" are
+ * input to the KDF to produce the AEAD key; the label "quic iv" is
+ * used to derive the Initialization Vector (IV). The header protection
+ * key uses the "quic hp" label. Using these labels provides key
+ * separation between QUIC and TLS.
+ */
+ if (version == QUIC_VERSION_V2) {
+ quic_data(&hp_k_l, HP_KEY_LABEL_V2, strlen(HP_KEY_LABEL_V2));
+ quic_data(&k_l, KEY_LABEL_V2, strlen(KEY_LABEL_V2));
+ quic_data(&i_l, IV_LABEL_V2, strlen(IV_LABEL_V2));
+ }
+
+ err = quic_crypto_hkdf_expand(tfm, s, &k_l, k);
+ if (err)
+ return err;
+ err = quic_crypto_hkdf_expand(tfm, s, &i_l, i);
+ if (err)
+ return err;
+ /* Don't change hp key for key update. */
+ if (!hp_k)
+ return 0;
+
+ return quic_crypto_hkdf_expand(tfm, s, &hp_k_l, hp_k);
+}
+
+/* Derive and install reception (RX) or transmission (TX) packet protection
+ * keys for the current key phase. This installs AEAD protection key, IV, and
+ * optionally header protection key.
+ */
+static int quic_crypto_keys_derive_and_install(struct quic_crypto *crypto,
+ bool rx, u8 phase)
+{
+ struct quic_data srt = {}, k, iv, hp_k = {}, *hp = NULL;
+ u8 key[QUIC_KEY_LEN], hp_key[QUIC_KEY_LEN] = {};
+ u32 keylen, ivlen = QUIC_IV_LEN;
+ struct crypto_skcipher *hp_tfm;
+ struct crypto_aead *tfm;
+ int err;
+
+ keylen = crypto->cipher->keylen;
+ quic_data(&k, key, keylen);
+
+ if (rx) {
+ quic_data(&srt, crypto->rx_secret[phase],
+ crypto->cipher->secretlen);
+ quic_data(&iv, crypto->rx_iv[phase], ivlen);
+ tfm = crypto->rx_tfm[phase];
+ hp_tfm = crypto->rx_hp_tfm;
+ } else {
+ quic_data(&srt, crypto->tx_secret[phase],
+ crypto->cipher->secretlen);
+ quic_data(&iv, crypto->tx_iv[phase], ivlen);
+ tfm = crypto->tx_tfm[phase];
+ hp_tfm = crypto->tx_hp_tfm;
+ }
+
+ /* Only derive header protection key when not in key update. */
+ if (crypto->key_phase == phase)
+ hp = quic_data(&hp_k, hp_key, keylen);
+ err = quic_crypto_keys_derive(crypto->secret_tfm, &srt, &k, &iv, hp,
+ crypto->version);
+ if (err)
+ goto out;
+ err = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);
+ if (err)
+ goto out;
+ err = crypto_aead_setkey(tfm, key, keylen);
+ if (err)
+ goto out;
+ if (hp) {
+ err = crypto_skcipher_setkey(hp_tfm, hp_key, keylen);
+ if (err)
+ goto out;
+ }
+out:
+ memzero_explicit(key, sizeof(key));
+ memzero_explicit(hp_key, sizeof(hp_key));
+ return err;
+}
+
+#define QUIC_CIPHER_MIN TLS_CIPHER_AES_GCM_128
+#define QUIC_CIPHER_MAX TLS_CIPHER_CHACHA20_POLY1305
+
+#define TLS_CIPHER_AES_GCM_128_SECRET_SIZE 32
+#define TLS_CIPHER_AES_GCM_256_SECRET_SIZE 48
+#define TLS_CIPHER_AES_CCM_128_SECRET_SIZE 32
+#define TLS_CIPHER_CHACHA20_POLY1305_SECRET_SIZE 32
+
+#define CIPHER_DESC(type, aead_n, skc_n, sha_n)[type - QUIC_CIPHER_MIN] = { \
+ .secretlen = type ## _SECRET_SIZE, \
+ .keylen = type ## _KEY_SIZE, \
+ .aead = aead_n, \
+ .skc = skc_n, \
+ .shash = sha_n, \
+}
+
+static const struct quic_cipher
+ciphers[QUIC_CIPHER_MAX + 1 - QUIC_CIPHER_MIN] = {
+ CIPHER_DESC(TLS_CIPHER_AES_GCM_128,
+ "gcm(aes)", "ecb(aes)", "hmac(sha256)"),
+ CIPHER_DESC(TLS_CIPHER_AES_GCM_256,
+ "gcm(aes)", "ecb(aes)", "hmac(sha384)"),
+ CIPHER_DESC(TLS_CIPHER_AES_CCM_128,
+ "ccm(aes)", "ecb(aes)", "hmac(sha256)"),
+ CIPHER_DESC(TLS_CIPHER_CHACHA20_POLY1305,
+ "rfc7539(chacha20,poly1305)", "chacha20", "hmac(sha256)"),
+};
+
+static bool quic_crypto_is_cipher_ccm(struct quic_crypto *crypto)
+{
+ return crypto->cipher_type == TLS_CIPHER_AES_CCM_128;
+}
+
+static bool quic_crypto_is_cipher_chacha(struct quic_crypto *crypto)
+{
+ return crypto->cipher_type == TLS_CIPHER_CHACHA20_POLY1305;
+}
+
+static void *quic_crypto_skcipher_mem_alloc(struct crypto_skcipher *tfm,
+ u32 mask_size, u8 **iv,
+ struct skcipher_request **req,
+ gfp_t gfp)
+{
+ unsigned int iv_size, req_size;
+ unsigned int len;
+ u8 *mem;
+
+ iv_size = crypto_skcipher_ivsize(tfm);
+ req_size = sizeof(**req) + crypto_skcipher_reqsize(tfm);
+
+ len = mask_size;
+ len += iv_size;
+ len += crypto_skcipher_alignmask(tfm) &
+ ~(crypto_tfm_ctx_alignment() - 1);
+ len = ALIGN(len, crypto_tfm_ctx_alignment());
+ len += req_size;
+
+ mem = kzalloc(len, gfp);
+ if (!mem)
+ return NULL;
+
+ *iv = (u8 *)PTR_ALIGN(mem + mask_size,
+ crypto_skcipher_alignmask(tfm) + 1);
+ *req = (struct skcipher_request *)PTR_ALIGN(*iv + iv_size,
+ crypto_tfm_ctx_alignment());
+
+ return (void *)mem;
+}
+
+/* Extracts and reconstructs the packet number from an incoming QUIC packet. */
+static int quic_crypto_get_number(struct sk_buff *skb)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ s64 number_max = cb->number;
+ u32 len = cb->length;
+ u8 *p;
+
+ /* rfc9000#section-17.1:
+ *
+ * Once header protection is removed, the packet number is decoded by
+ * finding the packet number value that is closest to the next expected
+ * packet. The next expected packet is the highest received packet
+ * number plus one.
+ */
+ p = (u8 *)quic_hdr(skb) + cb->number_offset;
+ if (!quic_get_int(&p, &len, &cb->number, cb->number_len))
+ return -EINVAL;
+ cb->number = quic_get_num(number_max, cb->number, cb->number_len);
+ return 0;
+}
+
+#define QUIC_SAMPLE_LEN 16
+
+#define QUIC_HEADER_FORM_BIT 0x80
+#define QUIC_LONG_HEADER_MASK 0x0f
+#define QUIC_SHORT_HEADER_MASK 0x1f
+
+/* Header Protection. */
+static int quic_crypto_header_protect(struct quic_crypto *crypto,
+ struct sk_buff *skb, bool enc, gfp_t gfp)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ u8 *mask, *iv, *p, h_mask, chacha;
+ struct skcipher_request *req;
+ struct crypto_skcipher *tfm;
+ struct sk_buff *trailer;
+ struct scatterlist sg;
+ int err, i;
+
+ chacha = quic_crypto_is_cipher_chacha(crypto);
+ if (!enc) {
+ tfm = crypto->rx_hp_tfm;
+ if (cb->length < QUIC_PN_MAX_LEN + QUIC_SAMPLE_LEN)
+ return -EINVAL;
+
+ err = skb_cow_data(skb, 0, &trailer);
+ if (err < 0)
+ return err;
+ } else {
+ tfm = crypto->tx_hp_tfm;
+ }
+
+ mask = quic_crypto_skcipher_mem_alloc(tfm, QUIC_SAMPLE_LEN, &iv, &req,
+ gfp);
+ if (!mask)
+ return -ENOMEM;
+
+ /* rfc9001#section-5.4.2: Header Protection Sample:
+ *
+ * # pn_offset is the start of the Packet Number field.
+ * sample_offset = pn_offset + 4
+ *
+ * sample = packet[sample_offset..sample_offset+sample_length]
+ *
+ * rfc9001#section-5.4.3: AES-Based Header Protection:
+ *
+ * header_protection(hp_key, sample):
+ * mask = AES-ECB(hp_key, sample)
+ *
+ * rfc9001#section-5.4.4: ChaCha20-Based Header Protection:
+ *
+ * header_protection(hp_key, sample):
+ * counter = sample[0..3]
+ * nonce = sample[4..15]
+ * mask = ChaCha20(hp_key, counter, nonce, {0,0,0,0,0})
+ */
+ p = skb->data + cb->number_offset + QUIC_PN_MAX_LEN;
+ memcpy((chacha ? iv : mask), p, QUIC_SAMPLE_LEN);
+ sg_init_one(&sg, mask, QUIC_SAMPLE_LEN);
+ skcipher_request_set_tfm(req, tfm);
+ skcipher_request_set_crypt(req, &sg, &sg, QUIC_SAMPLE_LEN, iv);
+ err = crypto_skcipher_encrypt(req);
+ if (err)
+ goto out;
+
+ /* rfc9001#section-5.4.1:
+ *
+ * mask = header_protection(hp_key, sample)
+ *
+ * pn_length = (packet[0] & 0x03) + 1
+ * if (packet[0] & 0x80) == 0x80:
+ * # Long header: 4 bits masked
+ * packet[0] ^= mask[0] & 0x0f
+ * else:
+ * # Short header: 5 bits masked
+ * packet[0] ^= mask[0] & 0x1f
+ *
+ * # pn_offset is the start of the Packet Number field.
+ * packet[pn_offset:pn_offset+pn_length] ^= mask[1:1+pn_length]
+ */
+ p = skb->data;
+ h_mask = ((*p & QUIC_HEADER_FORM_BIT) == QUIC_HEADER_FORM_BIT) ?
+ QUIC_LONG_HEADER_MASK : QUIC_SHORT_HEADER_MASK;
+ *p = (u8)(*p ^ (mask[0] & h_mask));
+ if (!enc) {
+ if (!quic_hdr(skb)->form)
+ cb->key_phase = quic_hdr(skb)->key;
+ cb->number_len = quic_hdr(skb)->pnl + 1;
+ }
+ p += cb->number_offset;
+ for (i = 1; i <= cb->number_len; i++)
+ *p++ ^= mask[i];
+
+ if (!enc)
+ err = quic_crypto_get_number(skb);
+out:
+ kfree_sensitive(mask);
+ return err;
+}
+
+static void *quic_crypto_aead_mem_alloc(struct crypto_aead *tfm, u32 ctx_size,
+ u8 **iv, struct aead_request **req,
+ struct scatterlist **sg, u32 nsg,
+ gfp_t gfp)
+{
+ unsigned int iv_size, req_size;
+ unsigned int len;
+ u8 *mem;
+
+ iv_size = crypto_aead_ivsize(tfm);
+ req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
+
+ len = ctx_size;
+ len += iv_size;
+ len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1);
+ len = ALIGN(len, crypto_tfm_ctx_alignment());
+ len += req_size;
+ len = ALIGN(len, __alignof__(struct scatterlist));
+ len += nsg * sizeof(**sg);
+
+ mem = kzalloc(len, gfp);
+ if (!mem)
+ return NULL;
+
+ *iv = (u8 *)PTR_ALIGN(mem + ctx_size, crypto_aead_alignmask(tfm) + 1);
+ *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size,
+ crypto_tfm_ctx_alignment());
+ *sg = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size,
+ __alignof__(struct scatterlist));
+
+ return (void *)mem;
+}
+
+static void quic_crypto_done(void *data, int err)
+{
+ struct sk_buff *skb = data;
+ struct quic_crypto *crypto;
+ struct quic_skb_cb *cb;
+
+ if (err == -EINPROGRESS)
+ return;
+
+ cb = QUIC_SKB_CB(skb);
+ crypto = *(struct quic_crypto **)cb->crypto_ctx;
+ atomic_dec(&crypto->async_pending[cb->key_phase]);
+
+ kfree_sensitive(cb->crypto_ctx);
+ cb->crypto_done(skb, err);
+}
+
+/* AEAD Usage. */
+static int quic_crypto_payload_protect(struct quic_crypto *crypto,
+ struct sk_buff *skb, bool enc, gfp_t gfp)
+{
+ u8 *base_iv, *iv, i, nonce[QUIC_IV_LEN], ccm, phase;
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ u32 len, hlen, sglen, nsg;
+ struct aead_request *req;
+ struct crypto_aead *tfm;
+ struct sk_buff *trailer;
+ struct scatterlist *sg;
+ void *ctx;
+ __be64 n;
+ int err;
+
+ ccm = quic_crypto_is_cipher_ccm(crypto);
+ phase = cb->key_phase;
+ hlen = cb->number_offset + cb->number_len;
+ if (enc) {
+ tfm = crypto->tx_tfm[phase];
+ base_iv = crypto->tx_iv[phase];
+ len = skb->len;
+ err = skb_cow_data(skb, QUIC_TAG_LEN, &trailer);
+ if (err < 0)
+ return err;
+ pskb_put(skb, trailer, QUIC_TAG_LEN);
+ if (!quic_hdr(skb)->form)
+ quic_hdr(skb)->key = phase;
+ sglen = skb->len;
+ nsg = (u32)err;
+ } else {
+ tfm = crypto->rx_tfm[phase];
+ base_iv = crypto->rx_iv[phase];
+ len = cb->length + cb->number_offset;
+ if (len - hlen < QUIC_TAG_LEN)
+ return -EINVAL;
+ sglen = len;
+ nsg = 1;
+ }
+
+ ctx = quic_crypto_aead_mem_alloc(tfm, sizeof(void *), &iv, &req, &sg,
+ nsg, gfp);
+ if (!ctx)
+ return -ENOMEM;
+
+ sg_init_table(sg, nsg);
+ err = skb_to_sgvec(skb, sg, 0, sglen);
+ if (err < 0)
+ goto out;
+
+ /* rfc9001#section-5.3:
+ *
+ * The associated data, A, for the AEAD is the contents of the QUIC
+ * header, starting from the first byte of either the short or long
+ * header, up to and including the unprotected packet number.
+ *
+ * The nonce, N, is formed by combining the packet protection IV with
+ * the packet number. The 62 bits of the reconstructed QUIC packet
+ * number in network byte order are left-padded with zeros to the size
+ * of the IV. The exclusive OR of the padded packet number and the IV
+ * forms the AEAD nonce.
+ */
+ memcpy(nonce, base_iv, QUIC_IV_LEN);
+ n = cpu_to_be64(cb->number);
+ for (i = 0; i < sizeof(n); i++)
+ nonce[QUIC_IV_LEN - sizeof(n) + i] ^= ((u8 *)&n)[i];
+
+ /* For CCM based ciphers, first byte of IV is a constant. */
+ iv[0] = TLS_AES_CCM_IV_B0_BYTE;
+ memcpy(&iv[ccm], nonce, QUIC_IV_LEN);
+ aead_request_set_tfm(req, tfm);
+ aead_request_set_ad(req, hlen);
+ aead_request_set_crypt(req, sg, sg, len - hlen, iv);
+ if (cb->sync) {
+ DECLARE_CRYPTO_WAIT(wait);
+
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+ err = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
+ if (err == -EINPROGRESS || err == -EBUSY)
+ err = crypto_wait_req(err, &wait);
+ goto out;
+ }
+
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ quic_crypto_done, skb);
+ *(struct quic_crypto **)ctx = crypto;
+ atomic_inc(&crypto->async_pending[phase]);
+ cb->crypto_ctx = ctx; /* Async free context for quic_crypto_done() */
+ err = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
+ if (err == -EINPROGRESS || err == -EBUSY) {
+ memzero_explicit(nonce, sizeof(nonce));
+ return -EINPROGRESS;
+ }
+ atomic_dec(&crypto->async_pending[phase]);
+ cb->crypto_ctx = NULL;
+
+out:
+ kfree_sensitive(ctx);
+ memzero_explicit(nonce, sizeof(nonce));
+ return err;
+}
+
+/* Encrypts a QUIC packet before transmission. This function performs AEAD
+ * encryption of the packet payload and applies header protection. It handles
+ * key phase tracking and key update timing.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int quic_crypto_encrypt(struct quic_crypto *crypto, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ int err;
+
+ /* Packet payload is already encrypted (e.g., resumed from async),
+ * proceed to header protection only.
+ */
+ if (cb->resume)
+ goto out;
+
+ cb->key_phase = crypto->key_phase;
+ /* If a key update is pending and this is the first packet using the
+ * new key, save the current time. Later used to clear old keys after
+ * some time has passed (see quic_crypto_decrypt()).
+ */
+ if (crypto->key_pending && !crypto->key_update_send_time)
+ crypto->key_update_send_time = quic_ktime_get_us();
+
+ err = quic_crypto_payload_protect(crypto, skb, true, gfp);
+ if (err)
+ return err;
+out:
+ return quic_crypto_header_protect(crypto, skb, true, gfp);
+}
+
+/* Decrypts a QUIC packet after reception. This function removes header
+ * protection, decrypts the payload, and processes any key updates if the key
+ * phase bit changes.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int quic_crypto_decrypt(struct quic_crypto *crypto, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ int err = 0;
+ u64 time;
+ u8 phase;
+
+ /* Payload was decrypted asynchronously. Proceed with parsing packet
+ * number and key phase.
+ */
+ if (cb->resume) {
+ err = quic_crypto_get_number(skb);
+ if (err)
+ return err;
+ goto out;
+ }
+ if (!cb->number_len) { /* Packet header not yet decrypted. */
+ err = quic_crypto_header_protect(crypto, skb, false, gfp);
+ if (err) {
+ pr_debug("%s: hd decrypt err %d\n", __func__, err);
+ return err;
+ }
+ }
+
+ /* rfc9001#section-6:
+ *
+ * The Key Phase bit allows a recipient to detect a change in keying
+ * material without needing to receive the first packet that triggered
+ * the change. An endpoint that notices a changed Key Phase bit updates
+ * keys and decrypts the packet that contains the changed value.
+ */
+ phase = cb->key_phase;
+ if (phase != crypto->key_phase && !crypto->key_pending) {
+ if (!crypto->send_ready) /* Not ready for key update. */
+ return -EINVAL;
+ if (!cb->backlog) /* Key update requires process context. */
+ return -EKEYREVOKED;
+ /* Cannot do key update while async crypto is in progress. */
+ if (unlikely(atomic_read(&crypto->async_pending[phase])))
+ return -EBUSY;
+ err = quic_crypto_key_update(crypto); /* Perform key update. */
+ if (err) {
+ cb->errcode = QUIC_TRANSPORT_ERROR_KEY_UPDATE;
+ return err;
+ }
+ cb->sync = 1;
+ cb->key_update = 1; /* Mark packet as triggering key update. */
+ }
+
+ err = quic_crypto_payload_protect(crypto, skb, false, gfp);
+ if (err) {
+ if (err == -EINPROGRESS)
+ return err;
+ /* When using the old keys can not decrypt the packets, the
+ * peer might start another key_update. Thus, clear the last
+ * key_pending so that next packets will trigger the new
+ * key-update.
+ */
+ if (crypto->key_pending && phase != crypto->key_phase) {
+ crypto->key_pending = 0;
+ crypto->key_update_time = 0;
+ crypto->key_update_send_time = 0;
+ }
+ return err;
+ }
+
+out:
+ /* rfc9001#section-6.2:
+ *
+ * If a packet is successfully processed using the next key and IV,
+ * then the peer has initiated a key update.
+ */
+ if (cb->key_update) {
+ crypto->key_pending = 1;
+ crypto->key_derived = 0;
+ crypto->key_phase = !crypto->key_phase;
+ }
+ /* rfc9001#section-6.1:
+ *
+ * An endpoint MUST retain old keys until it has successfully
+ * unprotected a packet sent using the new keys. An endpoint SHOULD
+ * retain old keys for some time after unprotecting a packet sent using
+ * the new keys.
+ */
+ if (crypto->key_pending && cb->key_phase == crypto->key_phase) {
+ time = crypto->key_update_send_time;
+ if (time &&
+ quic_ktime_get_us() - time >= crypto->key_update_time) {
+ crypto->key_pending = 0;
+ crypto->key_update_time = 0;
+ crypto->key_update_send_time = 0;
+ }
+ }
+ return err;
+}
+
+int quic_crypto_set_cipher(struct quic_crypto *crypto, u32 type)
+{
+ const struct quic_cipher *cipher;
+ void *tfm;
+ int err;
+
+ if (type < QUIC_CIPHER_MIN || type > QUIC_CIPHER_MAX)
+ return -EINVAL;
+
+ cipher = &ciphers[type - QUIC_CIPHER_MIN];
+ tfm = crypto_alloc_shash(cipher->shash, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+ crypto->secret_tfm = tfm;
+
+ /* Allocate AEAD and HP transform for each RX key phase. */
+ tfm = crypto_alloc_aead(cipher->aead, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->rx_tfm[0] = tfm;
+ tfm = crypto_alloc_aead(cipher->aead, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->rx_tfm[1] = tfm;
+ tfm = crypto_alloc_sync_skcipher(cipher->skc, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->rx_hp_tfm = tfm;
+
+ /* Allocate AEAD and HP transform for each TX key phase. */
+ tfm = crypto_alloc_aead(cipher->aead, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->tx_tfm[0] = tfm;
+ tfm = crypto_alloc_aead(cipher->aead, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->tx_tfm[1] = tfm;
+ tfm = crypto_alloc_sync_skcipher(cipher->skc, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->tx_hp_tfm = tfm;
+
+ crypto->cipher = cipher;
+ crypto->cipher_type = type;
+ return 0;
+err:
+ quic_crypto_free(crypto);
+ return err;
+}
+
+int quic_crypto_set_secret(struct quic_crypto *crypto,
+ struct quic_crypto_secret *srt, u32 version)
+{
+ const struct quic_cipher *cipher;
+ u8 phase = crypto->key_phase;
+ int err;
+
+ /* If no cipher has been initialized yet, set it up. */
+ if (!crypto->cipher) {
+ err = quic_crypto_set_cipher(crypto, srt->type);
+ if (err)
+ return err;
+ }
+ cipher = crypto->cipher;
+
+ /* Handle RX path setup. */
+ if (!srt->send) {
+ crypto->version = version;
+ memcpy(crypto->rx_secret[phase], srt->secret,
+ cipher->secretlen);
+ err = quic_crypto_keys_derive_and_install(crypto, true, phase);
+ if (err)
+ return err;
+ crypto->recv_ready = 1;
+ return 0;
+ }
+
+ /* Handle TX path setup. */
+ crypto->version = version;
+ memcpy(crypto->tx_secret[phase], srt->secret, cipher->secretlen);
+ err = quic_crypto_keys_derive_and_install(crypto, false, phase);
+ if (err)
+ return err;
+ crypto->send_ready = 1;
+ return 0;
+}
+
+/* Save token secret in Initial TX secret (phase 1) for token generation. */
+int quic_crypto_set_token_secret(struct quic_crypto *crypto)
+{
+ /* Reuse TX AEAD (phase 1) in Initial crypto. */
+ u8 key[TLS_CIPHER_AES_GCM_128_KEY_SIZE], *srt = crypto->tx_secret[1];
+ struct crypto_aead *tfm = crypto->tx_tfm[1];
+ struct quic_data s = {}, k, i;
+ int err;
+
+ if (!memchr_inv(srt, 0, TLS_CIPHER_AES_GCM_128_SECRET_SIZE))
+ get_random_bytes(srt, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+
+ quic_data(&s, srt, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ quic_data(&k, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+ quic_data(&i, crypto->tx_iv[1], QUIC_IV_LEN);
+ err = quic_crypto_keys_derive(crypto->secret_tfm, &s, &k, &i, NULL,
+ QUIC_VERSION_V1);
+ if (err)
+ goto out;
+ err = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);
+ if (err)
+ goto out;
+ err = crypto_aead_setkey(tfm, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+out:
+ memzero_explicit(key, sizeof(key));
+ return err;
+}
+
+/* Initiating a Key Update. */
+int quic_crypto_key_update(struct quic_crypto *crypto)
+{
+ struct quic_data l = {KU_LABEL_V1, strlen(KU_LABEL_V1)};
+ u8 phase = crypto->key_phase;
+ struct quic_data k, srt;
+ u32 secret_len;
+ int err;
+
+ if (crypto->key_pending || !crypto->recv_ready)
+ return -EINVAL;
+ if (crypto->key_derived)
+ return 0;
+
+ /* rfc9001#section-6.1:
+ *
+ * Endpoints maintain separate read and write secrets for packet
+ * protection. An endpoint initiates a key update by updating its
+ * packet protection write secret and using that to protect new
+ * packets. The endpoint creates a new write secret from the existing
+ * write secret. This uses the KDF function provided by TLS with a
+ * label of "quic ku". The corresponding key and IV are created from
+ * that secret. The header protection key is not updated.
+ *
+ * For example, to update write keys with TLS 1.3, HKDF-Expand-Label is
+ * used as:
+ * secret_<n+1> = HKDF-Expand-Label(secret_<n>, "quic ku",
+ * "", Hash.length)
+ */
+ secret_len = crypto->cipher->secretlen;
+ if (crypto->version == QUIC_VERSION_V2)
+ quic_data(&l, KU_LABEL_V2, strlen(KU_LABEL_V2));
+
+ quic_data(&srt, crypto->tx_secret[phase], secret_len);
+ quic_data(&k, crypto->tx_secret[!phase], secret_len);
+ err = quic_crypto_hkdf_expand(crypto->secret_tfm, &srt, &l, &k);
+ if (err)
+ return err;
+ err = quic_crypto_keys_derive_and_install(crypto, false, !phase);
+ if (err)
+ return err;
+
+ quic_data(&srt, crypto->rx_secret[phase], secret_len);
+ quic_data(&k, crypto->rx_secret[!phase], secret_len);
+ err = quic_crypto_hkdf_expand(crypto->secret_tfm, &srt, &l, &k);
+ if (err)
+ return err;
+ err = quic_crypto_keys_derive_and_install(crypto, true, !phase);
+ if (err)
+ return err;
+
+ crypto->key_derived = 1;
+ return 0;
+}
+
+void quic_crypto_free(struct quic_crypto *crypto)
+{
+ if (crypto->rx_tfm[0])
+ crypto_free_aead(crypto->rx_tfm[0]);
+ if (crypto->rx_tfm[1])
+ crypto_free_aead(crypto->rx_tfm[1]);
+ if (crypto->tx_tfm[0])
+ crypto_free_aead(crypto->tx_tfm[0]);
+ if (crypto->tx_tfm[1])
+ crypto_free_aead(crypto->tx_tfm[1]);
+ if (crypto->secret_tfm)
+ crypto_free_shash(crypto->secret_tfm);
+ if (crypto->rx_hp_tfm)
+ crypto_free_skcipher(crypto->rx_hp_tfm);
+ if (crypto->tx_hp_tfm)
+ crypto_free_skcipher(crypto->tx_hp_tfm);
+
+ memzero_explicit(crypto, offsetof(struct quic_crypto, send_offset));
+}
+
+#define QUIC_INITIAL_SALT_V1 \
+ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17" \
+ "\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"
+
+#define QUIC_INITIAL_SALT_V2 \
+ "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93" \
+ "\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd\x2e\xd9"
+
+#define QUIC_INITIAL_SALT_LEN 20
+
+/* Initial Secrets. */
+int quic_crypto_initial_keys_install(struct quic_crypto *crypto,
+ struct quic_conn_id *conn_id,
+ u32 version, bool is_serv)
+{
+ u8 secret[TLS_CIPHER_AES_GCM_128_SECRET_SIZE];
+ struct quic_data salt, s, k, l, dcid;
+ struct quic_crypto_secret srt = {};
+ char *tl, *rl, *sal;
+ int err;
+
+ /* rfc9001#section-5.2:
+ *
+ * The secret used by clients to construct Initial packets uses the PRK
+ * and the label "client in" as input to the HKDF-Expand-Label function
+ * from TLS [TLS13] to produce a 32-byte secret. Packets constructed by
+ * the server use the same process with the label "server in". The hash
+ * function for HKDF when deriving initial secrets and keys is SHA-256
+ * [SHA].
+ *
+ * This process in pseudocode is:
+ *
+ * initial_salt = 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a
+ * initial_secret = HKDF-Extract(initial_salt,
+ * client_dst_connection_id)
+ *
+ * client_initial_secret = HKDF-Expand-Label(initial_secret,
+ * "client in", "",
+ * Hash.length)
+ * server_initial_secret = HKDF-Expand-Label(initial_secret,
+ * "server in", "",
+ * Hash.length)
+ */
+ if (is_serv) {
+ rl = "client in";
+ tl = "server in";
+ } else {
+ tl = "client in";
+ rl = "server in";
+ }
+ sal = QUIC_INITIAL_SALT_V1;
+ if (version == QUIC_VERSION_V2)
+ sal = QUIC_INITIAL_SALT_V2;
+ quic_data(&salt, sal, QUIC_INITIAL_SALT_LEN);
+ quic_data(&dcid, conn_id->data, conn_id->len);
+ quic_data(&s, secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ err = quic_crypto_hkdf_extract(crypto->secret_tfm, &salt, &dcid, &s);
+ if (err)
+ goto out;
+
+ quic_data(&l, tl, strlen(tl));
+ quic_data(&k, srt.secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ srt.type = TLS_CIPHER_AES_GCM_128;
+ srt.send = 1;
+ err = quic_crypto_hkdf_expand(crypto->secret_tfm, &s, &l, &k);
+ if (err)
+ goto out;
+ err = quic_crypto_set_secret(crypto, &srt, version);
+ if (err)
+ goto out;
+
+ quic_data(&l, rl, strlen(rl));
+ quic_data(&k, srt.secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ srt.type = TLS_CIPHER_AES_GCM_128;
+ srt.send = 0;
+ err = quic_crypto_hkdf_expand(crypto->secret_tfm, &s, &l, &k);
+ if (err)
+ goto out;
+ err = quic_crypto_set_secret(crypto, &srt, version);
+out:
+ memzero_explicit(secret, sizeof(secret));
+ memzero_explicit(&srt, sizeof(srt));
+ return err;
+}
+
+#define QUIC_RETRY_KEY_V1 \
+ "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"
+#define QUIC_RETRY_KEY_V2 \
+ "\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92"
+
+#define QUIC_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"
+#define QUIC_RETRY_NONCE_V2 "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a"
+
+/* Retry Packet Integrity. */
+int quic_crypto_get_retry_tag(struct quic_crypto *crypto, struct sk_buff *skb,
+ struct quic_conn_id *odcid, u32 version, u8 *tag)
+{
+ /* Reuse RX AEAD (phase 1) in Initial crypto. */
+ struct crypto_aead *tfm = crypto->rx_tfm[1];
+ u8 *pseudo_retry, *p, *iv, *key;
+ DECLARE_CRYPTO_WAIT(wait);
+ struct aead_request *req;
+ struct scatterlist *sg;
+ u32 plen;
+ int err;
+
+ /* The caller must ensure skb->len > QUIC_TAG_LEN. */
+ if (skb->len <= QUIC_TAG_LEN)
+ return -EINVAL;
+
+ /* rfc9001#section-5.8:
+ *
+ * The Retry Integrity Tag is a 128-bit field that is computed as the
+ * output of AEAD_AES_128_GCM used with the following inputs:
+ *
+ * - The secret key, K, is 128 bits equal to
+ * 0xbe0c690b9f66575a1d766b54e368c84e.
+ * - The nonce, N, is 96 bits equal to 0x461599d35d632bf2239825bb.
+ * - The plaintext, P, is empty.
+ * - The associated data, A, is the contents of the Retry
+ * Pseudo-Packet,
+ *
+ * The Retry Pseudo-Packet is not sent over the wire. It is computed by
+ * taking the transmitted Retry packet, removing the Retry Integrity
+ * Tag, and prepending the two following fields: ODCID Length +
+ * Original Destination Connection ID (ODCID).
+ */
+ err = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);
+ if (err)
+ return err;
+ key = QUIC_RETRY_KEY_V1;
+ if (version == QUIC_VERSION_V2)
+ key = QUIC_RETRY_KEY_V2;
+ err = crypto_aead_setkey(tfm, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+ if (err)
+ return err;
+
+ plen = 1 + odcid->len + skb->len - QUIC_TAG_LEN;
+ pseudo_retry = quic_crypto_aead_mem_alloc(tfm, plen + QUIC_TAG_LEN, &iv,
+ &req, &sg, 1, GFP_KERNEL);
+ if (!pseudo_retry)
+ return -ENOMEM;
+
+ p = pseudo_retry;
+ p = quic_put_int(p, odcid->len, 1);
+ p = quic_put_data(p, odcid->data, odcid->len);
+ p = quic_put_data(p, skb->data, skb->len - QUIC_TAG_LEN);
+ sg_init_one(sg, pseudo_retry, plen + QUIC_TAG_LEN);
+
+ memcpy(iv, QUIC_RETRY_NONCE_V1, QUIC_IV_LEN);
+ if (version == QUIC_VERSION_V2)
+ memcpy(iv, QUIC_RETRY_NONCE_V2, QUIC_IV_LEN);
+ aead_request_set_tfm(req, tfm);
+ aead_request_set_ad(req, plen);
+ aead_request_set_crypt(req, sg, sg, 0, iv);
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+ err = crypto_aead_encrypt(req);
+ if (err == -EINPROGRESS || err == -EBUSY)
+ err = crypto_wait_req(err, &wait);
+ if (!err)
+ memcpy(tag, p, QUIC_TAG_LEN);
+
+ kfree_sensitive(pseudo_retry);
+ return err;
+}
+
+/* Derives a key and IV using HKDF, configures the AEAD transform and performs
+ * AEAD encryption/decryption for the provided token.
+ */
+static int quic_crypto_token_protect(struct quic_crypto *crypto, u8 *token,
+ u32 len, u32 adlen, bool enc)
+{
+ /* Reuse TX AEAD (phase 1) in Initial crypto. */
+ struct crypto_aead *tfm = crypto->tx_tfm[1];
+ u32 extra = enc ? QUIC_TAG_LEN : 0, tslen;
+ DECLARE_CRYPTO_WAIT(wait);
+ struct aead_request *req;
+ struct scatterlist *sg;
+ void *ctx = NULL;
+ u8 *nonce, *p, i;
+ __be64 n;
+ int err;
+ u64 ts;
+
+ ctx = quic_crypto_aead_mem_alloc(tfm, 0, &nonce, &req, &sg, 1,
+ GFP_KERNEL);
+ if (!ctx) {
+ err = -ENOMEM;
+ goto out;
+ }
+ memcpy(nonce, crypto->tx_iv[1], QUIC_IV_LEN);
+
+ tslen = sizeof(ts);
+ p = token + adlen - tslen;
+ quic_get_int(&p, &tslen, &ts, tslen);
+
+ n = cpu_to_be64(ts);
+ for (i = 0; i < sizeof(n); i++)
+ nonce[QUIC_IV_LEN - sizeof(n) + i] ^= ((u8 *)&n)[i];
+
+ sg_init_one(sg, token, len);
+ aead_request_set_tfm(req, tfm);
+ aead_request_set_ad(req, adlen);
+ aead_request_set_crypt(req, sg, sg, len - adlen - extra, nonce);
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+ err = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
+ if (err == -EINPROGRESS || err == -EBUSY)
+ err = crypto_wait_req(err, &wait);
+
+out:
+ kfree_sensitive(ctx);
+ return err;
+}
+
+/* Generate a token for Retry or address validation.
+ *
+ * Builds a token with the format: [flag][client address][timestamp][original
+ * DCID][auth tag]
+ *
+ * Encrypts the token (excluding the first flag byte) using AES-GCM with a key
+ * and IV derived via HKDF. The original DCID is stored to be recovered later
+ * from a Client Initial packet. Ensures the token is bound to the client
+ * address and time, preventing reuse or tampering.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,
+ u32 addrlen, struct quic_conn_id *conn_id,
+ u8 *token, u32 *tlen)
+{
+ u8 *token_buf, *p, flag = *token;
+ u64 ts = quic_ktime_get_us();
+ u32 len, tslen = sizeof(ts);
+ int err;
+
+ len = sizeof(flag) + addrlen + tslen + conn_id->len + QUIC_TAG_LEN;
+ token_buf = kmalloc(len, GFP_KERNEL);
+ if (!token_buf)
+ return -ENOMEM;
+
+ p = token_buf;
+ p = quic_put_int(p, flag, sizeof(flag));
+ p = quic_put_data(p, addr, addrlen);
+ p = quic_put_int(p, ts, tslen);
+ quic_put_data(p, conn_id->data, conn_id->len);
+
+ err = quic_crypto_token_protect(crypto, token_buf, len,
+ sizeof(flag) + addrlen + tslen, true);
+ if (err)
+ goto out;
+
+ memcpy(token, token_buf, len);
+ *tlen = len;
+out:
+ kfree_sensitive(token_buf);
+ return err;
+}
+
+/* Validate a Retry or address validation token.
+ *
+ * Decrypts the token using derived key and IV. Checks that the decrypted
+ * address matches the provided address, validates the embedded timestamp
+ * against current time with a version-specific timeout. If applicable, it
+ * extracts and returns the original destination connection ID (ODCID) for
+ * Retry packets.
+ *
+ * Returns 0 if the token is valid, -EINVAL if invalid, or another negative
+ * error code.
+ */
+int quic_crypto_verify_token(struct quic_crypto *crypto, void *addr,
+ u32 addrlen, struct quic_conn_id *conn_id,
+ u8 *token, u32 len)
+{
+ u64 t, ts = quic_ktime_get_us(), timeout = QUIC_TOKEN_TIMEOUT_RETRY;
+ u8 *token_buf, *p, flag;
+ u32 tslen = sizeof(ts);
+ int err;
+
+ if (len < sizeof(flag) + addrlen + tslen + QUIC_TAG_LEN)
+ return -EINVAL;
+ token_buf = kmemdup(token, len, GFP_KERNEL);
+ if (!token_buf)
+ return -ENOMEM;
+
+ err = quic_crypto_token_protect(crypto, token_buf, len,
+ sizeof(flag) + addrlen + tslen, false);
+ if (err)
+ goto out;
+
+ err = -EINVAL;
+ p = token_buf;
+ flag = *p++;
+ len -= sizeof(flag);
+ if (crypto_memneq(p, addr, addrlen))
+ goto out;
+
+ p += addrlen;
+ len -= addrlen;
+ if (flag == QUIC_TOKEN_FLAG_REGULAR)
+ timeout = QUIC_TOKEN_TIMEOUT_REGULAR;
+ if (!quic_get_int(&p, &len, &t, tslen) || t > ts || ts - t > timeout)
+ goto out;
+
+ len -= QUIC_TAG_LEN;
+ if (len > QUIC_CONN_ID_MAX_LEN)
+ goto out;
+
+ if (flag == QUIC_TOKEN_FLAG_RETRY)
+ quic_conn_id_update(conn_id, p, len);
+ err = 0;
+out:
+ kfree_sensitive(token_buf);
+ return err;
+}
+
+/* Derive a secret using HKDF-Extract and HKDF-Expand with the given label.
+ * Used to generate a stateless reset token or session resumption master key.
+ */
+int quic_crypto_derive_secret(struct quic_crypto *crypto, void *data, u32 len,
+ char *label, u8 *srt, u32 srt_len)
+{
+ struct crypto_shash *tfm = crypto->secret_tfm;
+ u8 secret[TLS_CIPHER_AES_GCM_128_SECRET_SIZE];
+ struct quic_data salt, s, l, k;
+ int err;
+
+ quic_data(&salt, data, len);
+ quic_data(&k, crypto->tx_secret[1], TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ quic_data(&s, secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ err = quic_crypto_hkdf_extract(tfm, &salt, &k, &s);
+ if (err)
+ goto out;
+
+ quic_data(&l, label, strlen(label));
+ quic_data(&k, srt, srt_len);
+ err = quic_crypto_hkdf_expand(tfm, &s, &l, &k);
+out:
+ memzero_explicit(secret, sizeof(secret));
+ return err;
+}
diff --git a/net/quic/crypto.h b/net/quic/crypto.h
new file mode 100644
index 0000000000000..77281a824f721
--- /dev/null
+++ b/net/quic/crypto.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_TAG_LEN 16
+#define QUIC_IV_LEN 12
+#define QUIC_KEY_LEN 32
+#define QUIC_SECRET_LEN 48
+
+#define QUIC_TOKEN_FLAG_REGULAR 0
+#define QUIC_TOKEN_FLAG_RETRY 1
+#define QUIC_TOKEN_TIMEOUT_RETRY 3000000
+#define QUIC_TOKEN_TIMEOUT_REGULAR 600000000
+
+struct quic_cipher {
+ u32 secretlen; /* Length of the traffic secret */
+ u32 keylen; /* Length of the AEAD key */
+
+ char *shash; /* Name of hash algorithm used for key derivation */
+ char *aead; /* Name of AEAD algorithm used for payload en/decryption */
+ char *skc; /* Name of cipher algorithm used for header protection */
+};
+
+struct quic_crypto {
+ struct crypto_skcipher *tx_hp_tfm; /* TX header protection tfm */
+ struct crypto_skcipher *rx_hp_tfm; /* RX header protection tfm */
+ struct crypto_shash *secret_tfm; /* Key derivation (HKDF) tfm */
+ struct crypto_aead *tx_tfm[2]; /* AEAD tfm for TX (key phase 0 and 1) */
+ struct crypto_aead *rx_tfm[2]; /* AEAD tfm for RX (key phase 0 and 1) */
+
+ const struct quic_cipher *cipher; /* Cipher info (selected cipher) */
+ u32 cipher_type; /* Cipher suite (e.g., AES_GCM_128, etc.) */
+
+ u8 tx_secret[2][QUIC_SECRET_LEN]; /* TX secret (key phase 0 and 1) */
+ u8 rx_secret[2][QUIC_SECRET_LEN]; /* RX secret (key phase 0 and 1) */
+ u8 tx_iv[2][QUIC_IV_LEN]; /* IVs for TX (key phase 0 and 1) */
+ u8 rx_iv[2][QUIC_IV_LEN]; /* IVs for RX (key phase 0 and 1) */
+ atomic_t async_pending[2]; /* Async pending count (key phase 0 and 1) */
+
+ /* Timestamp 1st packet sent after key update */
+ u64 key_update_send_time;
+ u64 key_update_time; /* Timestamp old keys retained after key update */
+ u32 version; /* QUIC version in use */
+
+ u8 ticket_ready:1; /* True if a session ticket is ready to read */
+ u8 key_pending:1; /* A key update is in progress */
+ u8 key_derived:1; /* Key derived for the key update */
+ u8 send_ready:1; /* TX encryption context is initialized */
+ u8 recv_ready:1; /* RX decryption context is initialized */
+ u8 key_phase:1; /* Current key phase being used (0 or 1) */
+
+ u64 send_offset; /* Number of handshake bytes sent by user */
+ u64 recv_offset; /* Number of handshake bytes read by user */
+};
+
+int quic_crypto_set_secret(struct quic_crypto *crypto,
+ struct quic_crypto_secret *srt, u32 version);
+int quic_crypto_set_cipher(struct quic_crypto *crypto, u32 type);
+int quic_crypto_key_update(struct quic_crypto *crypto);
+
+int quic_crypto_encrypt(struct quic_crypto *crypto, struct sk_buff *skb,
+ gfp_t gfp);
+int quic_crypto_decrypt(struct quic_crypto *crypto, struct sk_buff *skb,
+ gfp_t gfp);
+
+int quic_crypto_derive_secret(struct quic_crypto *crypto, void *data, u32 len,
+ char *label, u8 *srt, u32 srt_len);
+int quic_crypto_initial_keys_install(struct quic_crypto *crypto,
+ struct quic_conn_id *conn_id,
+ u32 version, bool is_serv);
+int quic_crypto_set_token_secret(struct quic_crypto *crypto);
+
+int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,
+ u32 addrlen, struct quic_conn_id *conn_id,
+ u8 *token, u32 *tlen);
+int quic_crypto_get_retry_tag(struct quic_crypto *crypto, struct sk_buff *skb,
+ struct quic_conn_id *odcid, u32 version, u8 *tag);
+int quic_crypto_verify_token(struct quic_crypto *crypto, void *addr,
+ u32 addrlen, struct quic_conn_id *conn_id,
+ u8 *token, u32 len);
+
+void quic_crypto_free(struct quic_crypto *crypto);
diff --git a/net/quic/family.c b/net/quic/family.c
new file mode 100644
index 0000000000000..f61a8203b2e17
--- /dev/null
+++ b/net/quic/family.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/inet_common.h>
+#include <net/udp_tunnel.h>
+#include <linux/icmp.h>
+
+#include "common.h"
+#include "family.h"
+
+static bool quic_v4_is_any_addr(union quic_addr *addr)
+{
+ return addr->v4.sin_addr.s_addr == htonl(INADDR_ANY);
+}
+
+static bool quic_v6_is_any_addr(union quic_addr *addr)
+{
+ return ipv6_addr_any(&addr->v6.sin6_addr);
+}
+
+static void quic_v4_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,
+ union quic_addr *a)
+{
+ conf->family = AF_INET;
+ conf->local_ip.s_addr = a->v4.sin_addr.s_addr;
+ conf->local_udp_port = a->v4.sin_port;
+ conf->bind_ifindex = sk->sk_bound_dev_if;
+}
+
+static void quic_v6_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,
+ union quic_addr *a)
+{
+ conf->family = AF_INET6;
+ conf->local_ip6 = a->v6.sin6_addr;
+ conf->local_udp_port = a->v6.sin6_port;
+ conf->use_udp6_rx_checksums = true;
+ conf->use_udp6_tx_checksums = true;
+ conf->ipv6_v6only = ipv6_only_sock(sk);
+ conf->bind_ifindex = quic_get_dev_if(sk, a);
+}
+
+static int quic_v4_flow_route(struct sock *sk, union quic_addr *da,
+ union quic_addr *sa, struct flowi *fl)
+{
+ struct flowi4 *fl4;
+ struct rtable *rt;
+
+ if (__sk_dst_check(sk, 0))
+ return 1;
+
+ memset(fl, 0x00, sizeof(*fl));
+ fl4 = &fl->u.ip4;
+ fl4->saddr = sa->v4.sin_addr.s_addr;
+ fl4->fl4_sport = sa->v4.sin_port;
+ fl4->daddr = da->v4.sin_addr.s_addr;
+ fl4->fl4_dport = da->v4.sin_port;
+ fl4->flowi4_proto = IPPROTO_UDP;
+ fl4->flowi4_oif = quic_get_dev_if(sk, da);
+
+ fl4->flowi4_scope = ip_sock_rt_scope(sk);
+ fl4->flowi4_dscp = inet_sk_dscp(inet_sk(sk));
+
+ fl4->flowi4_uid = sk_uid(sk);
+ fl4->flowi4_mark = sk->sk_mark;
+
+ rt = ip_route_output_flow(sock_net(sk), fl4, sk);
+ if (IS_ERR(rt))
+ return PTR_ERR(rt);
+
+ if (quic_v4_is_any_addr(sa)) {
+ sa->v4.sin_family = AF_INET;
+ sa->v4.sin_addr.s_addr = fl4->saddr;
+ }
+ sk_setup_caps(sk, &rt->dst);
+ return 0;
+}
+
+static int quic_v6_flow_route(struct sock *sk, union quic_addr *da,
+ union quic_addr *sa, struct flowi *fl)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct in6_addr *final_p, final;
+ struct ip6_flowlabel *flowlabel;
+ struct dst_entry *dst;
+ struct flowi6 *fl6;
+
+ if (__sk_dst_check(sk, np->dst_cookie))
+ return 1;
+
+ memset(fl, 0x00, sizeof(*fl));
+ fl6 = &fl->u.ip6;
+ fl6->saddr = sa->v6.sin6_addr;
+ fl6->fl6_sport = sa->v6.sin6_port;
+ fl6->daddr = da->v6.sin6_addr;
+ fl6->fl6_dport = da->v6.sin6_port;
+ fl6->flowi6_proto = IPPROTO_UDP;
+ fl6->flowi6_oif = quic_get_dev_if(sk, da);
+
+ if (inet6_test_bit(SNDFLOW, sk)) {
+ fl6->flowlabel = (da->v6.sin6_flowinfo & IPV6_FLOWINFO_MASK);
+ if (fl6->flowlabel & IPV6_FLOWLABEL_MASK) {
+ flowlabel = fl6_sock_lookup(sk, fl6->flowlabel);
+ if (IS_ERR(flowlabel))
+ return -EINVAL;
+ fl6_sock_release(flowlabel);
+ }
+ }
+ fl6->flowlabel = ip6_make_flowinfo(np->tclass, fl6->flowlabel);
+
+ fl6->flowi6_uid = sk_uid(sk);
+ fl6->flowi6_mark = sk->sk_mark;
+
+ rcu_read_lock();
+ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
+ rcu_read_unlock();
+
+ dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p);
+ if (IS_ERR(dst))
+ return PTR_ERR(dst);
+
+ if (quic_v6_is_any_addr(sa)) {
+ sa->v6.sin6_family = AF_INET6;
+ sa->v6.sin6_addr = fl6->saddr;
+ if ((ipv6_addr_type(&fl6->saddr) & IPV6_ADDR_LINKLOCAL))
+ sa->v6.sin6_scope_id = fl6->flowi6_oif;
+ }
+ ip6_dst_store(sk, dst, false, false);
+ return 0;
+}
+
+static void quic_v4_lower_xmit(struct sock *sk, struct sk_buff *skb,
+ struct flowi *fl)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ struct flowi4 *fl4 = &fl->u.ip4;
+ u8 tos = inet_sk(sk)->tos, ttl;
+ struct dst_entry *dst;
+ __be16 df = 0;
+
+ pr_debug("%s: skb: %p, len: %d, num: %lld, %pI4:%d -> %pI4:%d\n",
+ __func__, skb, skb->len, cb->number, &fl4->saddr,
+ ntohs(fl4->fl4_sport), &fl4->daddr, ntohs(fl4->fl4_dport));
+
+ dst = sk_dst_get(sk);
+ if (!dst) {
+ kfree_skb(skb);
+ return;
+ }
+ if (ip_dont_fragment(sk, dst) && !skb->ignore_df)
+ df = htons(IP_DF);
+
+ if (cb->ecn)
+ tos = (tos & ~INET_ECN_MASK) | cb->ecn;
+ ttl = (u8)ip4_dst_hoplimit(dst);
+ udp_tunnel_xmit_skb((struct rtable *)dst, sk, skb, fl4->saddr,
+ fl4->daddr, tos, ttl, df, fl4->fl4_sport,
+ fl4->fl4_dport, false, false, 0);
+}
+
+static void quic_v6_lower_xmit(struct sock *sk, struct sk_buff *skb,
+ struct flowi *fl)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ u8 tc = inet6_sk(sk)->tclass, ttl;
+ struct flowi6 *fl6 = &fl->u.ip6;
+ struct dst_entry *dst;
+ __be32 label;
+
+ pr_debug("%s: skb: %p, len: %d, num: %lld, %pI6c:%d -> %pI6c:%d\n",
+ __func__, skb, skb->len, cb->number, &fl6->saddr,
+ ntohs(fl6->fl6_sport), &fl6->daddr, ntohs(fl6->fl6_dport));
+
+ dst = sk_dst_get(sk);
+ if (!dst) {
+ kfree_skb(skb);
+ return;
+ }
+
+ if (cb->ecn)
+ tc = (tc & ~INET_ECN_MASK) | cb->ecn;
+ ttl = (u8)ip6_dst_hoplimit(dst);
+ label = ip6_make_flowlabel(sock_net(sk), skb, fl6->flowlabel, true,
+ fl6);
+ udp_tunnel6_xmit_skb(dst, sk, skb, NULL, &fl6->saddr, &fl6->daddr, tc,
+ ttl, label, fl6->fl6_sport, fl6->fl6_dport, false,
+ 0);
+}
+
+static void quic_v4_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,
+ union quic_addr *sa)
+{
+ struct udphdr *uh = udp_hdr(skb);
+
+ sa->v4.sin_family = AF_INET;
+ sa->v4.sin_port = uh->source;
+ sa->v4.sin_addr.s_addr = ip_hdr(skb)->saddr;
+
+ da->v4.sin_family = AF_INET;
+ da->v4.sin_port = uh->dest;
+ da->v4.sin_addr.s_addr = ip_hdr(skb)->daddr;
+}
+
+static void quic_v6_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,
+ union quic_addr *sa)
+{
+ struct udphdr *uh = udp_hdr(skb);
+
+ sa->v6.sin6_family = AF_INET6;
+ sa->v6.sin6_port = uh->source;
+ sa->v6.sin6_addr = ipv6_hdr(skb)->saddr;
+ sa->v6.sin6_scope_id = skb->skb_iif;
+
+ da->v6.sin6_family = AF_INET6;
+ da->v6.sin6_port = uh->dest;
+ da->v6.sin6_addr = ipv6_hdr(skb)->daddr;
+ da->v6.sin6_scope_id = skb->skb_iif;
+}
+
+static int quic_v4_get_mtu_info(struct sk_buff *skb, u32 *info)
+{
+ struct icmphdr *hdr;
+
+ hdr = (struct icmphdr *)(skb_network_header(skb) -
+ sizeof(struct icmphdr));
+ if (hdr->type == ICMP_DEST_UNREACH && hdr->code == ICMP_FRAG_NEEDED) {
+ *info = ntohs(hdr->un.frag.mtu);
+ return 0;
+ }
+
+ /* Defer other types' processing to UDP error handler. */
+ return -EINVAL;
+}
+
+static int quic_v6_get_mtu_info(struct sk_buff *skb, u32 *info)
+{
+ struct icmp6hdr *hdr;
+
+ hdr = (struct icmp6hdr *)(skb_network_header(skb) -
+ sizeof(struct icmp6hdr));
+ if (hdr->icmp6_type == ICMPV6_PKT_TOOBIG) {
+ *info = ntohl(hdr->icmp6_mtu);
+ return 0;
+ }
+
+ /* Defer other types' processing to UDP error handler. */
+ return -EINVAL;
+}
+
+static bool quic_v4_cmp_sk_addr(struct sock *sk, union quic_addr *a,
+ union quic_addr *addr)
+{
+ if (a->v4.sin_port != addr->v4.sin_port)
+ return false;
+ if (a->v4.sin_family != addr->v4.sin_family)
+ return false;
+ /* Match only if socket is also ANY-bound. */
+ if (addr->v4.sin_addr.s_addr == htonl(INADDR_ANY))
+ return a->v4.sin_addr.s_addr == htonl(INADDR_ANY);
+ if (a->v4.sin_addr.s_addr == htonl(INADDR_ANY))
+ return true;
+ return a->v4.sin_addr.s_addr == addr->v4.sin_addr.s_addr;
+}
+
+static bool quic_v6_cmp_sk_addr(struct sock *sk, union quic_addr *a,
+ union quic_addr *addr)
+{
+ if (a->sa.sa_family == AF_INET && addr->sa.sa_family == AF_INET)
+ return quic_v4_cmp_sk_addr(sk, a, addr);
+
+ if (a->v4.sin_port != addr->v4.sin_port)
+ return false;
+
+ if (a->sa.sa_family != addr->sa.sa_family) {
+ if (ipv6_only_sock(sk) || a->sa.sa_family == AF_INET)
+ return false;
+ return quic_is_any_addr(a);
+ }
+
+ /* Match only if socket is also ANY-bound. */
+ if (ipv6_addr_any(&addr->v6.sin6_addr))
+ return ipv6_addr_any(&a->v6.sin6_addr);
+ if (ipv6_addr_any(&a->v6.sin6_addr))
+ return true;
+ if (!ipv6_addr_equal(&a->v6.sin6_addr, &addr->v6.sin6_addr))
+ return false;
+ if ((ipv6_addr_type(&a->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) &&
+ a->v6.sin6_scope_id && addr->v6.sin6_scope_id &&
+ a->v6.sin6_scope_id != addr->v6.sin6_scope_id)
+ return false;
+ return true;
+}
+
+static int quic_v4_get_sk_addr(struct socket *sock, struct sockaddr *uaddr,
+ int peer)
+{
+ return inet_getname(sock, uaddr, peer);
+}
+
+static int quic_v6_get_sk_addr(struct socket *sock, struct sockaddr *uaddr,
+ int peer)
+{
+ union quic_addr *a = quic_addr(uaddr);
+ int ret;
+
+ ret = inet6_getname(sock, uaddr, peer);
+ if (ret < 0)
+ return ret;
+
+ if (a->sa.sa_family == AF_INET6 &&
+ ipv6_addr_v4mapped(&a->v6.sin6_addr)) {
+ a->v4.sin_family = AF_INET;
+ a->v4.sin_port = a->v6.sin6_port;
+ a->v4.sin_addr.s_addr = a->v6.sin6_addr.s6_addr32[3];
+ }
+
+ if (a->sa.sa_family == AF_INET) {
+ memset(a->v4.sin_zero, 0, sizeof(a->v4.sin_zero));
+ return sizeof(struct sockaddr_in);
+ }
+ return sizeof(struct sockaddr_in6);
+}
+
+#define quic_af_ipv4(a) ((a)->sa.sa_family == AF_INET)
+
+u32 quic_encap_len(union quic_addr *a)
+{
+ return (quic_af_ipv4(a) ? sizeof(struct iphdr) :
+ sizeof(struct ipv6hdr)) +
+ sizeof(struct udphdr);
+}
+
+bool quic_is_any_addr(union quic_addr *a)
+{
+ return quic_af_ipv4(a) ? quic_v4_is_any_addr(a) :
+ quic_v6_is_any_addr(a);
+}
+
+void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,
+ union quic_addr *a)
+{
+ quic_af_ipv4(a) ? quic_v4_udp_conf_init(sk, conf, a) :
+ quic_v6_udp_conf_init(sk, conf, a);
+}
+
+int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa,
+ struct flowi *fl)
+{
+ if (sa->sa.sa_family && da->sa.sa_family != sa->sa.sa_family)
+ return -EINVAL;
+
+ return quic_af_ipv4(da) ? quic_v4_flow_route(sk, da, sa, fl) :
+ quic_v6_flow_route(sk, da, sa, fl);
+}
+
+void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da,
+ struct flowi *fl)
+{
+ local_bh_disable();
+ quic_af_ipv4(da) ? quic_v4_lower_xmit(sk, skb, fl) :
+ quic_v6_lower_xmit(sk, skb, fl);
+ local_bh_enable();
+}
+
+#define quic_skb_ipv4(skb) (ip_hdr(skb)->version == 4)
+
+void quic_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,
+ union quic_addr *sa)
+{
+ memset(sa, 0, sizeof(*sa));
+ memset(da, 0, sizeof(*da));
+ quic_skb_ipv4(skb) ? quic_v4_get_msg_addrs(skb, da, sa) :
+ quic_v6_get_msg_addrs(skb, da, sa);
+}
+
+int quic_get_mtu_info(struct sk_buff *skb, u32 *info)
+{
+ return quic_skb_ipv4(skb) ? quic_v4_get_mtu_info(skb, info) :
+ quic_v6_get_mtu_info(skb, info);
+}
+
+#define quic_pf_ipv4(sk) ((sk)->sk_family == PF_INET)
+
+bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a,
+ union quic_addr *addr)
+{
+ return quic_pf_ipv4(sk) ? quic_v4_cmp_sk_addr(sk, a, addr) :
+ quic_v6_cmp_sk_addr(sk, a, addr);
+}
+
+int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, int peer)
+{
+ return quic_pf_ipv4(sock->sk) ? quic_v4_get_sk_addr(sock, a, peer) :
+ quic_v6_get_sk_addr(sock, a, peer);
+}
+
+int quic_get_dev_if(struct sock *sk, union quic_addr *a)
+{
+ if (!quic_af_ipv4(a) &&
+ ipv6_addr_type(&a->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL &&
+ a->v6.sin6_scope_id)
+ return a->v6.sin6_scope_id;
+
+ return sk->sk_bound_dev_if;
+}
+
+void quic_set_skb_iif(struct sk_buff *skb)
+{
+ /* Save the inet/inet6 iif before skb dst/cb are cleared. */
+ skb->skb_iif = quic_skb_ipv4(skb) ? inet_iif(skb) : inet6_iif(skb);
+}
+
+int quic_common_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ return quic_pf_ipv4(sk) ?
+ ip_setsockopt(sk, level, optname, optval, optlen) :
+ ipv6_setsockopt(sk, level, optname, optval, optlen);
+}
+
+int quic_common_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ return quic_pf_ipv4(sk) ?
+ ip_getsockopt(sk, level, optname, optval, optlen) :
+ ipv6_getsockopt(sk, level, optname, optval, optlen);
+}
+
+bool quic_sk_accept_pmtu(struct sock *sk, struct sk_buff *skb)
+{
+ return quic_skb_ipv4(skb) ? ip_sk_accept_pmtu(sk) :
+ ip6_sk_accept_pmtu(sk);
+}
+
+void quic_sk_destruct(struct sock *sk)
+{
+ quic_pf_ipv4(sk) ? inet_sock_destruct(sk) : inet6_sock_destruct(sk);
+}
diff --git a/net/quic/family.h b/net/quic/family.h
new file mode 100644
index 0000000000000..02342c4b7c99e
--- /dev/null
+++ b/net/quic/family.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_PORT_LEN 2
+#define QUIC_ADDR4_LEN 4
+#define QUIC_ADDR6_LEN 16
+
+#define QUIC_PREF_ADDR_LEN \
+ (QUIC_ADDR4_LEN + QUIC_PORT_LEN + QUIC_ADDR6_LEN + QUIC_PORT_LEN)
+
+bool quic_is_any_addr(union quic_addr *a);
+u32 quic_encap_len(union quic_addr *a);
+
+void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da,
+ struct flowi *fl);
+int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa,
+ struct flowi *fl);
+void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,
+ union quic_addr *a);
+
+void quic_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,
+ union quic_addr *sa);
+int quic_get_mtu_info(struct sk_buff *skb, u32 *info);
+
+bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a,
+ union quic_addr *addr);
+int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, int peer);
+
+int quic_get_dev_if(struct sock *sk, union quic_addr *a);
+void quic_set_skb_iif(struct sk_buff *skb);
+
+int quic_common_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen);
+int quic_common_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen);
+bool quic_sk_accept_pmtu(struct sock *sk, struct sk_buff *skb);
+void quic_sk_destruct(struct sock *sk);
diff --git a/net/quic/packet.c b/net/quic/packet.c
new file mode 100644
index 0000000000000..db87c32d7469d
--- /dev/null
+++ b/net/quic/packet.c
@@ -0,0 +1,1055 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include "socket.h"
+
+#define QUIC_HLEN 1
+
+#define QUIC_LONG_HLEN(dcid, scid) \
+ (QUIC_HLEN + QUIC_VERSION_LEN + 1 + (dcid)->len + 1 + (scid)->len)
+
+#define QUIC_VERSION_NUM 2
+
+/* Supported QUIC versions and their compatible versions. Used for Compatible
+ * Version Negotiation in rfc9368#section-2.3.
+ */
+static u32 quic_versions[QUIC_VERSION_NUM][4] = {
+ /* Version, Compatible Versions */
+ { QUIC_VERSION_V1, QUIC_VERSION_V2, QUIC_VERSION_V1, 0 },
+ { QUIC_VERSION_V2, QUIC_VERSION_V2, QUIC_VERSION_V1, 0 },
+};
+
+/* Get the compatible version list for a given QUIC version. */
+u32 *quic_packet_compatible_versions(u32 version)
+{
+ u8 i;
+
+ for (i = 0; i < QUIC_VERSION_NUM; i++)
+ if (version == quic_versions[i][0])
+ return quic_versions[i];
+ return NULL;
+}
+
+/* Convert version-specific type to internal standard packet type. */
+static u8 quic_packet_version_get_type(u32 version, u8 type)
+{
+ if (version == QUIC_VERSION_V1)
+ return type;
+
+ switch (type) {
+ case QUIC_PACKET_INITIAL_V2:
+ return QUIC_PACKET_INITIAL;
+ case QUIC_PACKET_0RTT_V2:
+ return QUIC_PACKET_0RTT;
+ case QUIC_PACKET_HANDSHAKE_V2:
+ return QUIC_PACKET_HANDSHAKE;
+ case QUIC_PACKET_RETRY_V2:
+ return QUIC_PACKET_RETRY;
+ default:
+ return QUIC_PACKET_INVALID;
+ }
+}
+
+/* Extracts a QUIC Connection ID from a buffer in the long header packet. */
+static int quic_packet_get_connid(struct quic_conn_id *connid, u8 **pp,
+ u32 *plen)
+{
+ u64 len;
+
+ if (!quic_get_int(pp, plen, &len, 1) ||
+ len > *plen || len > QUIC_CONN_ID_MAX_LEN)
+ return -EINVAL;
+
+ quic_conn_id_update(connid, *pp, len);
+ *plen -= len;
+ *pp += len;
+ return 0;
+}
+
+/* Parse QUIC version and connection IDs (DCID and SCID) from a Long header
+ * packet buffer.
+ */
+static int quic_packet_get_long_header(struct quic_conn_id *dcid,
+ struct quic_conn_id *scid, u32 *version,
+ u8 **pp, u32 *plen)
+{
+ int err;
+ u64 v;
+
+ *pp += QUIC_HLEN;
+ *plen -= QUIC_HLEN;
+
+ if (!quic_get_int(pp, plen, &v, QUIC_VERSION_LEN))
+ return -EINVAL;
+ if (version)
+ *version = v;
+
+ err = quic_packet_get_connid(dcid, pp, plen);
+ if (err)
+ return err;
+ if (!scid)
+ return 0;
+ return quic_packet_get_connid(scid, pp, plen);
+}
+
+/* Extracts a QUIC token from a buffer in the Client Initial packet. */
+static int quic_packet_get_token(struct quic_data *token, u8 **pp, u32 *plen)
+{
+ u64 len;
+
+ if (!quic_get_var(pp, plen, &len) || len > *plen)
+ return -EINVAL;
+ quic_data(token, *pp, len);
+ *plen -= len;
+ *pp += len;
+ return 0;
+}
+
+/* Process PMTU reduction event on a QUIC socket. */
+void quic_packet_rcv_err_pmtu(struct sock *sk)
+{
+ struct quic_path_group *paths = quic_paths(sk);
+ struct quic_packet *packet = quic_packet(sk);
+ u32 pathmtu, info, taglen;
+ struct dst_entry *dst;
+ bool reset_timer;
+
+ if (quic_is_closed(sk))
+ return;
+
+ info = clamp(paths->mtu_info, QUIC_PATH_MIN_PMTU, QUIC_PATH_MAX_PMTU);
+ /* If PLPMTUD is not enabled, update MSS using route and ICMP info. */
+ if (!paths->plpmtud_interval) {
+ if (quic_packet_route(sk))
+ return;
+
+ dst = __sk_dst_get(sk);
+ if (dst)
+ dst->ops->update_pmtu(dst, sk, NULL, info, true);
+ quic_packet_mss_update(sk, info - packet->hlen);
+ return;
+ }
+ /* PLPMTUD is enabled: adjust to smaller PMTU, subtract headers and
+ * AEAD tag. Also notify the QUIC path layer for possible state
+ * changes and probing.
+ */
+ packet->level = QUIC_CRYPTO_APP;
+ taglen = quic_packet_taglen(packet);
+ info = info - packet->hlen - taglen;
+ pathmtu = quic_path_pl_toobig(paths, info, &reset_timer);
+ if (reset_timer)
+ quic_timer_reset(sk, QUIC_TIMER_PMTU, paths->plpmtud_interval);
+ if (pathmtu)
+ quic_packet_mss_update(sk, pathmtu + taglen);
+}
+
+/* Handle ICMP Toobig packet and update QUIC socket path MTU. */
+static int quic_packet_rcv_err(struct sock *sk, struct sk_buff *skb)
+{
+ union quic_addr daddr, saddr;
+ u32 info;
+
+ /* ICMP embeds the original outgoing QUIC packet, so saddr/daddr are
+ * reversed when parsed. Only address-based socket lookup is possible
+ * in this case.
+ */
+ quic_get_msg_addrs(skb, &saddr, &daddr);
+ sk = quic_sock_lookup(skb, &daddr, &saddr, sk, NULL);
+ if (!sk)
+ return -ENOENT;
+
+ if (quic_get_mtu_info(skb, &info) || !quic_sk_accept_pmtu(sk, skb)) {
+ sock_put(sk);
+ return 0;
+ }
+
+ /* Success: update socket path MTU info. */
+ bh_lock_sock(sk);
+ quic_paths(sk)->mtu_info = info;
+ if (sock_owned_by_user(sk)) {
+ /* Socket locked by userspace. Defer MTU processing via
+ * release_cb. Hold socket reference to prevent it being
+ * freed before deferral.
+ */
+ if (!test_and_set_bit(QUIC_MTU_REDUCED_DEFERRED,
+ &sk->sk_tsq_flags))
+ sock_hold(sk);
+ goto out;
+ }
+ /* Otherwise, process the MTU reduction now. */
+ quic_packet_rcv_err_pmtu(sk);
+out:
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ return 1;
+}
+
+/* Queue a packet for later processing when sleeping is allowed. */
+static int quic_packet_deferred_schedule(struct sk_buff *skb)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ struct sock *sk = skb->sk;
+ int len = skb->truesize;
+
+ if (cb->backlog)
+ return 0;
+
+ if (sk_rmem_alloc_get(sk) + len > sk->sk_rcvbuf ||
+ !__sk_rmem_schedule(sk, len, false)) {
+ QUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ cb->backlog = 1;
+ skb_set_owner_r(skb, sk);
+ __skb_queue_tail(&quic_packet(sk)->deferred_list, skb);
+
+ sock_hold(sk);
+ if (!queue_work(quic_wq, quic_work(sk)))
+ sock_put(sk);
+ return 1;
+}
+
+#define TLS_MT_CLIENT_HELLO 1
+#define TLS_EXT_alpn 16
+
+/* TLS Client Hello Msg:
+ *
+ * uint16 ProtocolVersion;
+ * opaque Random[32];
+ * uint8 CipherSuite[2];
+ *
+ * struct {
+ * ExtensionType extension_type;
+ * opaque extension_data<0..2^16-1>;
+ * } Extension;
+ *
+ * struct {
+ * ProtocolVersion legacy_version = 0x0303;
+ * Random rand;
+ * opaque legacy_session_id<0..32>;
+ * CipherSuite cipher_suites<2..2^16-2>;
+ * opaque legacy_compression_methods<1..2^8-1>;
+ * Extension extensions<8..2^16-1>;
+ * } ClientHello;
+ */
+
+#define TLS_CH_RANDOM_LEN 32
+#define TLS_CH_VERSION_LEN 2
+#define TLS_MAX_EXTENSIONS 128
+
+#define QUIC_FRAME_CRYPTO 0x06
+
+/* Decrypt Initial packet and extract ALPN from TLS ClientHello for ALPN-based
+ * socket demultiplexing. Marks packet as decrypted (cb->resume = 1) to avoid
+ * redundant decryption later.
+ */
+static int quic_packet_get_alpn(struct sk_buff *skb, struct quic_data *alpn)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ int err, found = 0, exts = 0;
+ struct quic_crypto *crypto;
+ struct quic_packet *packet;
+ struct sock *sk = skb->sk;
+ u64 length, offset, type;
+ struct net *net;
+ u32 len;
+ u8 *p;
+
+ /* Install initial keys for decryption. */
+ crypto = quic_crypto(sk, QUIC_CRYPTO_INITIAL);
+ packet = quic_packet(sk);
+ err = quic_crypto_initial_keys_install(crypto, &packet->dcid,
+ packet->version, true);
+ if (err)
+ return err;
+ cb->sync = 1;
+ net = sock_net(sk);
+ err = quic_crypto_decrypt(crypto, skb, GFP_KERNEL);
+ if (err) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_DECDROP);
+ return err;
+ }
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_DECFASTPATHS);
+ cb->resume = 1; /* Mark this packet as already decrypted. */
+
+ /* Find the QUIC CRYPTO frame. */
+ p = skb->data + cb->number_offset + cb->number_len;
+ len = cb->length - cb->number_len - QUIC_TAG_LEN;
+ for (; len && !(*p); p++, len--) /* Skip the padding frame. */
+ ;
+ if (!len-- || *p++ != QUIC_FRAME_CRYPTO)
+ return 0;
+ if (!quic_get_var(&p, &len, &offset) || offset)
+ return 0;
+ if (!quic_get_var(&p, &len, &length) || length > (u64)len)
+ return 0;
+ if (len > (u32)length) /* Cap len to crypto frame length. */
+ len = length;
+
+ err = -EINVAL;
+ /* Verify handshake message type (ClientHello) and its length. */
+ if (!quic_get_int(&p, &len, &type, 1) || type != TLS_MT_CLIENT_HELLO)
+ return err;
+ if (!quic_get_int(&p, &len, &length, 3) ||
+ len < TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN ||
+ length < TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN)
+ return err;
+ if (len > (u32)length) /* Cap len to handshake msg length. */
+ len = length;
+ /* Skip legacy_version (2 bytes) + random (32 bytes). */
+ p += TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN;
+ len -= TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN;
+ /* legacy_session_id_len must be zero (QUIC requirement). */
+ if (!quic_get_int(&p, &len, &length, 1) || length)
+ return err;
+
+ /* Skip cipher_suites (2 bytes length + variable data). */
+ if (!quic_get_int(&p, &len, &length, 2) || length > (u64)len)
+ return err;
+ len -= length;
+ p += length;
+
+ /* Skip legacy_compression_methods (1 byte length + variable data). */
+ if (!quic_get_int(&p, &len, &length, 1) || length > (u64)len)
+ return err;
+ len -= length;
+ p += length;
+
+ /* Read TLS extensions length (2 bytes). */
+ if (!quic_get_int(&p, &len, &length, 2))
+ return err;
+ if (len > (u32)length) /* Limit len to extensions length if larger. */
+ len = length;
+ while (len >= 4) { /* Scan extensions for ALPN (TLS_EXT_alpn). */
+ if (exts++ >= TLS_MAX_EXTENSIONS)
+ return err;
+ if (!quic_get_int(&p, &len, &type, 2))
+ break;
+ if (!quic_get_int(&p, &len, &length, 2))
+ break;
+ if (len < (u32)length) /* Incomplete TLS extensions. */
+ return 0;
+ if (type == TLS_EXT_alpn) { /* Found ALPN extension. */
+ if (length > QUIC_ALPN_MAX_LEN)
+ return err;
+ len = length;
+ found = 1;
+ break;
+ }
+ /* Skip non-ALPN extensions. */
+ p += length;
+ len -= length;
+ }
+ if (!found) { /* No ALPN ext: set alpn->len = 0 and alpn->data = p. */
+ quic_data(alpn, p, 0);
+ return 0;
+ }
+
+ /* Parse ALPN protocols list length (2 bytes). */
+ if (!quic_get_int(&p, &len, &length, 2) || length > (u64)len)
+ return err;
+ quic_data(alpn, p, length); /* Store ALPN list in alpn->data. */
+ len = length;
+ while (len) { /* Validate ALPN protocols list format. */
+ if (!quic_get_int(&p, &len, &length, 1) || length > (u64)len) {
+ /* Bad ALPN: set alpn->len = 0, alpn->data = NULL. */
+ quic_data(alpn, NULL, 0);
+ return err;
+ }
+ len -= length;
+ p += length;
+ }
+ pr_debug("%s: alpn_len: %d\n", __func__, alpn->len);
+ return 0;
+}
+
+/* Determine the QUIC socket associated with an incoming packet. */
+static struct sock *quic_packet_get_sock(struct sk_buff *skb, struct sock *usk)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ struct quic_conn_id dcid = {}, *conn_id;
+ struct net *net = sock_net(usk);
+ union quic_addr daddr, saddr;
+ struct quic_data alpns = {};
+ struct sock *sk = NULL;
+ u32 len = skb->len;
+ u8 *p = skb->data;
+ int err;
+
+ if (skb->len < QUIC_HLEN)
+ return ERR_PTR(-EINVAL);
+
+ if (quic_hdr(skb)->form == QUIC_PACKET_FORM_SHORT) {
+ /* Short header path. */
+ if (skb->len < QUIC_HLEN + QUIC_CONN_ID_DEF_LEN)
+ return ERR_PTR(-EINVAL);
+ /* Fast path: look up QUIC connection by fixed-length DCID
+ * (Currently, only QUIC_CONN_ID_DEF_LEN-length SCIDs are used).
+ */
+ conn_id = quic_conn_id_lookup(net, skb->data + QUIC_HLEN,
+ QUIC_CONN_ID_DEF_LEN);
+ if (conn_id) {
+ cb->seqno = quic_conn_id_number(conn_id);
+ /* Return associated socket. */
+ return quic_conn_id_sk(conn_id);
+ }
+
+ /* Fallback: listener socket lookup
+ * (May be used to send a stateless reset from a listen socket).
+ */
+ quic_get_msg_addrs(skb, &daddr, &saddr);
+ sk = quic_listen_sock_lookup(skb, &daddr, &saddr, usk, &alpns);
+ if (sk)
+ return sk;
+ /* Final fallback: address-based connection lookup
+ * (May be used to receive a stateless reset).
+ */
+ sk = quic_sock_lookup(skb, &daddr, &saddr, usk, NULL);
+ if (!sk)
+ return ERR_PTR(-ENOENT);
+ return sk;
+ }
+
+ /* Long header path. */
+ err = quic_packet_get_long_header(&dcid, NULL, NULL, &p, &len);
+ if (err)
+ return ERR_PTR(err);
+ /* Fast path: look up QUIC connection by parsed DCID. */
+ conn_id = quic_conn_id_lookup(net, dcid.data, dcid.len);
+ if (conn_id) {
+ cb->seqno = quic_conn_id_number(conn_id);
+ return quic_conn_id_sk(conn_id); /* Return associated socket. */
+ }
+
+ /* Fallback: address + DCID lookup
+ * (May be used for 0-RTT or a follow-up Client Initial packet).
+ */
+ quic_get_msg_addrs(skb, &daddr, &saddr);
+ sk = quic_sock_lookup(skb, &daddr, &saddr, usk, &dcid);
+ if (sk)
+ return sk;
+ /* Final fallback: listener socket lookup
+ * (Used for receiving the first Client Initial packet).
+ */
+ sk = quic_listen_sock_lookup(skb, &daddr, &saddr, usk, &alpns);
+ if (!sk)
+ return ERR_PTR(-ENOENT);
+ return sk;
+}
+
+/* Entry point for processing received QUIC packets. */
+int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp)
+{
+ struct net *net = sock_net(sk);
+ int err;
+
+ if (unlikely(icmp))
+ return quic_packet_rcv_err(sk, skb);
+
+ if (skb_linearize(skb)) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ /* Look up socket from socket or connection IDs hash tables. */
+ sk = quic_packet_get_sock(skb, sk);
+ if (IS_ERR(sk)) {
+ err = PTR_ERR(sk);
+ goto err;
+ }
+
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ /* Socket is busy (owned by user context): queue to backlog. */
+ err = sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf));
+ if (err) {
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ goto err;
+ }
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_RCVBACKLOGS);
+ } else {
+ /* Socket not busy: process immediately. */
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_RCVFASTPATHS);
+ sk->sk_backlog_rcv(sk, skb); /* quic_backlog_rcv(). */
+ }
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ return 0;
+err:
+ pr_debug("%s: failed, len: %d, err: %d\n", __func__, skb->len, err);
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_RCVDROP);
+ kfree_skb(skb);
+ return err;
+}
+
+static int quic_packet_retry_create_and_xmit(struct sock *sk)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_packet_version_create_and_xmit(struct sock *sk, gfp_t gfp)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_packet_stateless_reset_create_and_xmit(struct sock *sk, u32 len,
+ gfp_t gfp)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_packet_refuse_close_create_and_xmit(struct sock *sk,
+ u32 errcode)
+{
+ return -EOPNOTSUPP;
+}
+
+/* Process an incoming packet on a listening QUIC socket.
+ *
+ * Depending on the packet type and state, this may involve creating a request
+ * socket for a new connection, responding with a Stateless Reset for
+ * unexpected Handshake or 1-RTT packets, issuing a Retry packet for address
+ * validation when needed, or sending a Version Negotiation packet if the
+ * client's QUIC version is unsupported.
+ */
+static int quic_packet_listen_process(struct sock *sk, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ u32 version, errcode, toff, len = skb->len;
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ u8 *p = skb->data, type, retry = 0;
+ struct net *net = sock_net(sk);
+ struct quic_conn_id odcid = {};
+ struct quic_request_sock *req;
+ struct quic_data alpns = {};
+ struct quic_crypto *crypto;
+ struct quic_data token;
+ u64 length;
+ int err;
+
+ if (quic_hshdr(skb)->form == QUIC_PACKET_FORM_SHORT) {
+ /* rfc9000#section-10.3:
+ *
+ * An endpoint MAY send a Stateless Reset in response to
+ * receiving a packet that it cannot associate with an active
+ * connection.
+ */
+ if (len < QUIC_HLEN + QUIC_CONN_ID_DEF_LEN) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ /* Read Destination address (packet->saddr) and Source address
+ * (packet->daddr).
+ */
+ quic_get_msg_addrs(skb, &packet->saddr, &packet->daddr);
+ /* We currently only issue Connection ID with size
+ * QUIC_CONN_ID_DEF_LEN.
+ */
+ quic_conn_id_update(&packet->dcid,
+ (u8 *)quic_hdr(skb) + QUIC_HLEN,
+ QUIC_CONN_ID_DEF_LEN);
+ /* Send a Stateless Reset for this 1-RTT packet. */
+ err = quic_packet_stateless_reset_create_and_xmit(sk, len, gfp);
+ consume_skb(skb);
+ return err;
+ }
+
+ /* Read VERSION, Destination Connection ID and Source Connection ID. */
+ err = quic_packet_get_long_header(&packet->dcid, &packet->scid,
+ &version, &p, &len);
+ if (err) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return err;
+ }
+
+ /* Read Destination address (packet->saddr) and Source address
+ * (packet->daddr).
+ */
+ quic_get_msg_addrs(skb, &packet->saddr, &packet->daddr);
+ req = quic_request_sock_lookup(sk);
+ if (req) /* If request sock already exists, enqueue packet directly. */
+ goto out;
+
+ if (quic_accept_sock_exists(sk, skb))
+ return 0; /* Already handled by matched accept socket. */
+
+ /* rfc9000#section-6.1:
+ *
+ * An endpoint MUST NOT send a Version Negotiation packet in response
+ * to receiving a Version Negotiation packet.
+ */
+ if (!version) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ if (!quic_packet_compatible_versions(version)) {
+ /* rfc9000#section-6.1:
+ *
+ * If the version selected by the client is not acceptable to
+ * the server, the server responds with a Version Negotiation
+ * packet. This includes a list of versions that the server
+ * will accept.
+ */
+ err = quic_packet_version_create_and_xmit(sk, gfp);
+ consume_skb(skb);
+ return err;
+ }
+
+ /* Read Packet Type. */
+ type = quic_packet_version_get_type(version, quic_hshdr(skb)->type);
+ if (type != QUIC_PACKET_INITIAL) { /* Send a Stateless Reset. */
+ err = quic_packet_stateless_reset_create_and_xmit(sk, skb->len,
+ gfp);
+ consume_skb(skb);
+ return err;
+ }
+
+ /* This Destination Connection ID MUST be at least 8 bytes in length. */
+ if (packet->dcid.len < QUIC_CONN_ID_DEF_LEN) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ err = quic_packet_get_token(&token, &p, &len); /* Read Token. */
+ if (err) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return err;
+ }
+ if (token.len)
+ toff = token.data - skb->data;
+
+ /* Associate skb with sk to ensure sk is valid if skb is delayed to
+ * process in workqueue.
+ */
+ WARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk));
+ packet->version = version;
+ if (!cb->resume && static_branch_unlikely(&quic_alpn_demux_key)) {
+ if (quic_packet_deferred_schedule(skb))
+ return -EINPROGRESS;
+ if (!quic_get_var(&p, &len, &length) || length > (u64)len) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ cb->length = (u16)length;
+ cb->number_offset = (u16)(p - skb->data);
+ err = quic_packet_get_alpn(skb, &alpns);
+ if (err) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return err;
+ }
+ if (quic_listen_sock_switch(skb, &alpns))
+ return 0; /* Switched to different listen socket. */
+ if (token.len) /* Update after skb->data may change. */
+ token.data = skb->data + toff;
+ }
+
+ /* Save original DCID for future token validation or Retry logic. */
+ quic_conn_id_update(&odcid, packet->dcid.data, packet->dcid.len);
+ /* If configured to validate client addresses, handle token logic. */
+ if (packet->validate_peer_address) {
+ if (quic_packet_deferred_schedule(skb))
+ return 0;
+ if (!token.len) {
+ /* rfc9000#section-8.1.2:
+ *
+ * Upon receiving the client's Initial packet, the
+ * server can request address validation by sending a
+ * Retry packet containing a token.
+ */
+ err = quic_packet_retry_create_and_xmit(sk);
+ consume_skb(skb);
+ return err;
+ }
+
+ /* Distinguish token source: Retry packet or NEW_TOKEN frame. */
+ retry = *(u8 *)token.data == QUIC_TOKEN_FLAG_RETRY;
+
+ /* Verify Token. */
+ crypto = quic_crypto(sk, QUIC_CRYPTO_INITIAL);
+ err = quic_crypto_verify_token(crypto, &packet->daddr,
+ sizeof(packet->daddr),
+ &odcid, token.data, token.len);
+ if (err) {
+ if (!retry) {
+ err = quic_packet_retry_create_and_xmit(sk);
+ consume_skb(skb);
+ return err;
+ }
+ /* rfc9000#section-8.1.3:
+ *
+ * If a server receives a client Initial that contains
+ * an invalid Retry token but is otherwise valid, it
+ * knows the client will not accept another Retry
+ * token. The server SHOULD immediately close the
+ * connection with an INVALID_TOKEN error.
+ */
+ errcode = QUIC_TRANSPORT_ERROR_INVALID_TOKEN;
+ quic_packet_refuse_close_create_and_xmit(sk, errcode);
+ consume_skb(skb);
+ return err;
+ }
+ }
+
+ /* Add request sock for this new QUIC connection. */
+ req = quic_request_sock_create(sk, &odcid, retry, gfp);
+ if (IS_ERR(req)) {
+ if (quic_packet_deferred_schedule(skb))
+ return 0;
+ /* rfc9000#section-5.2.2:
+ *
+ * If a server refuses to accept a new connection, it SHOULD
+ * send an Initial packet containing a CONNECTION_CLOSE frame
+ * with error code CONNECTION_REFUSED.
+ */
+ errcode = QUIC_TRANSPORT_ERROR_CONNECTION_REFUSED;
+ quic_packet_refuse_close_create_and_xmit(sk, errcode);
+ consume_skb(skb);
+ return PTR_ERR(req);
+ }
+out:
+ /* Add to backlog list and wake blocked accept() calls */
+ return quic_request_sock_backlog_tail(sk, req, skb);
+}
+
+static int quic_packet_handshake_process(struct sock *sk, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ kfree_skb(skb);
+ return -EOPNOTSUPP;
+}
+
+static int quic_packet_app_process(struct sock *sk, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ kfree_skb(skb);
+ return -EOPNOTSUPP;
+}
+
+int quic_packet_process(struct sock *sk, struct sk_buff *skb, gfp_t gfp)
+{
+ if (quic_is_closed(sk)) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (quic_is_listen(sk))
+ return quic_packet_listen_process(sk, skb, gfp);
+
+ if (quic_hdr(skb)->form == QUIC_PACKET_FORM_LONG)
+ return quic_packet_handshake_process(sk, skb, gfp);
+
+ return quic_packet_app_process(sk, skb, gfp);
+}
+
+/* Work function to process packets in the backlog queue. */
+static void quic_packet_deferred_work(struct work_struct *work)
+{
+ struct quic_sock *qs = container_of(work, struct quic_sock, work);
+ struct sock *sk = &qs->inet.sk;
+ struct sk_buff_head *head;
+ struct sk_buff *skb;
+
+ lock_sock(sk);
+ head = &quic_packet(sk)->deferred_list;
+ while ((skb = __skb_dequeue(head)) != NULL)
+ quic_packet_process(sk, skb, GFP_KERNEL);
+ release_sock(sk);
+ sock_put(sk);
+}
+
+/* Make these fixed for easy coding. */
+#define QUIC_PACKET_NUMBER_LEN QUIC_PN_MAX_LEN
+#define QUIC_PACKET_LENGTH_LEN 4
+
+static struct sk_buff *quic_packet_handshake_create(struct sock *sk, gfp_t gfp)
+{
+ return NULL;
+}
+
+static int quic_packet_number_check(struct sock *sk, gfp_t gfp)
+{
+ return 0;
+}
+
+static struct sk_buff *quic_packet_app_create(struct sock *sk, gfp_t gfp)
+{
+ return NULL;
+}
+
+/* Update the MSS and inform congestion control. */
+void quic_packet_mss_update(struct sock *sk, u32 mss)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct quic_cong *cong = quic_cong(sk);
+
+ packet->mss[QUIC_PACKET_MSS_NORMAL] = (u16)mss;
+ quic_cong_set_mss(cong, packet->mss[QUIC_PACKET_MSS_NORMAL]);
+}
+
+/* Perform routing for the QUIC packet on the specified path, update header
+ * length and MSS accordingly, reset path and start PMTU timer.
+ */
+int quic_packet_route(struct sock *sk)
+{
+ struct quic_path_group *paths = quic_paths(sk);
+ struct quic_packet *packet = quic_packet(sk);
+ union quic_addr *sa, *da;
+ u32 pmtu;
+ int err;
+
+ da = quic_path_daddr(paths, packet->path);
+ sa = quic_path_saddr(paths, packet->path);
+ err = quic_flow_route(sk, da, sa, &paths->fl);
+ if (err)
+ return err < 0 ? err : 0;
+
+ packet->hlen = quic_encap_len(da);
+ pmtu = clamp(dst_mtu(__sk_dst_get(sk)),
+ QUIC_PATH_MIN_PMTU, QUIC_PATH_MAX_PMTU);
+ quic_packet_mss_update(sk, pmtu - packet->hlen);
+
+ quic_path_pl_reset(paths);
+ quic_timer_reset(sk, QUIC_TIMER_PMTU, paths->plpmtud_interval);
+ return 0;
+}
+
+/* Return QUIC packet header overhead for the given level and path. Includes
+ * packet number, connection IDs, and for long headers also version, length,
+ * and Initial token (if present). Excludes payload.
+ */
+u16 quic_packet_overhead(struct sock *sk, u8 level, u8 path)
+{
+ struct quic_conn_id_set *source = quic_source(sk);
+ struct quic_conn_id_set *dest = quic_dest(sk);
+ u16 len = QUIC_HLEN;
+
+ len += QUIC_PACKET_NUMBER_LEN; /* Packet number length. */
+ len += quic_conn_id_choose(dest, path)->len; /* DCID length. */
+ if (level == QUIC_CRYPTO_APP)
+ return len;
+
+ len += 1; /* Length byte for DCID. */
+ /* Length byte + SCID length. */
+ len += 1 + quic_conn_id_active(source)->len;
+ /* Include token for Initial packets. */
+ if (level == QUIC_CRYPTO_INITIAL)
+ len += quic_var_len(quic_token(sk)->len) + quic_token(sk)->len;
+ len += QUIC_VERSION_LEN; /* Version length. */
+ len += QUIC_PACKET_LENGTH_LEN; /* Packet length field. */
+
+ return len;
+}
+
+/* Configure the QUIC packet header and routing based on encryption level and
+ * path.
+ */
+int quic_packet_config(struct sock *sk, u8 level, u8 path)
+{
+ struct quic_packet *packet = quic_packet(sk);
+
+ /* If packet already has data, no need to reconfigure. */
+ if (!quic_packet_empty(packet))
+ return 0;
+
+ packet->path_validating = 0;
+ packet->ipfragok = 0;
+ packet->padding = 0;
+ packet->frames = 0;
+
+ packet->level = level;
+ packet->overhead = quic_packet_overhead(sk, level, path);
+ packet->len = packet->overhead + quic_packet_taglen(packet);
+
+ /* Allow fragmentation for handshake packets if PLPMTUD is enabled, as
+ * MTU discovery does not rely on ICMP Packet Too Big once PLPMTUD is
+ * enabled.
+ */
+ packet->ipfragok = level && !!quic_paths(sk)->plpmtud_interval;
+
+ if (packet->path != path) {
+ /* Path changed; update and reset routing cache */
+ packet->path = path;
+ __sk_dst_reset(sk);
+ }
+
+ /* Perform routing and MSS update for the configured packet. */
+ return quic_packet_route(sk);
+}
+
+static void quic_packet_encrypt_done(struct sk_buff *skb, int err)
+{
+ /* Free it for now, future patches will implement the actual deferred
+ * transmission logic.
+ */
+ kfree_skb(skb);
+}
+
+/* Coalescing Packets. */
+static int quic_packet_bundle(struct sock *sk, struct sk_buff *skb)
+{
+ struct quic_skb_cb *head_cb, *cb = QUIC_SKB_CB(skb);
+ struct quic_packet *packet = quic_packet(sk);
+ struct sk_buff *p;
+
+ if (!packet->head) /* First packet to bundle: initialize the head. */
+ goto init;
+
+ /* If bundling would exceed MSS, flush the current bundle. */
+ if (packet->head->len + skb->len >
+ packet->mss[QUIC_PACKET_MSS_NORMAL]) {
+ quic_packet_flush(sk);
+ goto init;
+ }
+ /* Bundle it and update metadata for the aggregate skb. */
+ skb_orphan(skb);
+ p = packet->head;
+ head_cb = QUIC_SKB_CB(p);
+ if (head_cb->last == p)
+ skb_shinfo(p)->frag_list = skb;
+ else
+ head_cb->last->next = skb;
+ p->data_len += skb->len;
+ p->truesize += skb->truesize;
+ p->len += skb->len;
+ head_cb->last = skb;
+ head_cb->ecn |= cb->ecn; /* Merge ECN flags. */
+
+out:
+ /* rfc9000#section-12.2: Packets with a short header (Section 17.3) do
+ * not contain a Length field and so cannot be followed by other
+ * packets in the same UDP datagram.
+ *
+ * so Return 1 to flush if it is a Short header packet.
+ */
+ return !cb->level;
+init:
+ packet->head = skb;
+ cb->last = skb;
+ goto out;
+}
+
+/* Transmit a QUIC packet, possibly encrypting and bundling it. */
+static int quic_packet_xmit(struct sock *sk, struct sk_buff *skb, gfp_t gfp)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ struct net *net = sock_net(sk);
+ int err;
+
+ /* Associate skb with sk to ensure sk is valid during async encryption
+ * completion.
+ */
+ WARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk));
+
+ /* Skip encryption if taglen == 0 (e.g., disable_1rtt_encryption). */
+ if (!packet->taglen[quic_hdr(skb)->form])
+ goto xmit;
+
+ cb->crypto_done = quic_packet_encrypt_done;
+ err = quic_crypto_encrypt(quic_crypto(sk, packet->level), skb, gfp);
+ if (err) {
+ if (err != -EINPROGRESS) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_ENCDROP);
+ kfree_skb(skb);
+ return err;
+ }
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_ENCBACKLOGS);
+ return err;
+ }
+ if (!cb->resume) /* Encryption completes synchronously. */
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_ENCFASTPATHS);
+
+xmit:
+ if (quic_packet_bundle(sk, skb))
+ quic_packet_flush(sk);
+ return 0;
+}
+
+/* Create and transmit a new QUIC packet. */
+int quic_packet_create_and_xmit(struct sock *sk, gfp_t gfp)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct sk_buff *skb;
+ int err;
+
+ err = quic_packet_number_check(sk, gfp);
+ if (err)
+ goto err;
+
+ if (packet->level)
+ skb = quic_packet_handshake_create(sk, gfp);
+ else
+ skb = quic_packet_app_create(sk, gfp);
+ if (!skb) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ err = quic_packet_xmit(sk, skb, gfp);
+ if (err && err != -EINPROGRESS)
+ goto err;
+
+ return 0;
+err:
+ pr_debug("%s: err: %d\n", __func__, err);
+ return err;
+}
+
+/* Flush any coalesced/bundled QUIC packets. */
+void quic_packet_flush(struct sock *sk)
+{
+ struct quic_path_group *paths = quic_paths(sk);
+ struct quic_packet *packet = quic_packet(sk);
+
+ if (packet->head) {
+ quic_lower_xmit(sk, packet->head,
+ quic_path_daddr(paths, packet->path),
+ &paths->fl);
+ packet->head = NULL;
+ }
+}
+
+void quic_packet_init(struct sock *sk)
+{
+ struct quic_packet *packet = quic_packet(sk);
+
+ INIT_LIST_HEAD(&packet->frame_list);
+ skb_queue_head_init(&packet->deferred_list);
+ skb_queue_head_init(&packet->backlog_list);
+ INIT_WORK(quic_work(sk), quic_packet_deferred_work);
+
+ packet->taglen[QUIC_PACKET_FORM_SHORT] = QUIC_TAG_LEN;
+ packet->taglen[QUIC_PACKET_FORM_LONG] = QUIC_TAG_LEN;
+ packet->mss[QUIC_PACKET_MSS_NORMAL] = QUIC_MIN_UDP_PAYLOAD;
+ packet->mss[QUIC_PACKET_MSS_DGRAM] = QUIC_MIN_UDP_PAYLOAD;
+
+ packet->version = QUIC_VERSION_V1;
+}
+
+void quic_packet_free(struct sock *sk)
+{
+ struct quic_packet *packet = quic_packet(sk);
+
+ flush_work(quic_work(sk));
+ __skb_queue_purge(&packet->deferred_list);
+ __skb_queue_purge(&packet->backlog_list);
+}
diff --git a/net/quic/packet.h b/net/quic/packet.h
new file mode 100644
index 0000000000000..7f12bb53927b8
--- /dev/null
+++ b/net/quic/packet.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+struct quic_packet {
+ struct quic_conn_id dcid; /* Dest Conn ID from received packet */
+ struct quic_conn_id scid; /* Source Conn ID from received packet */
+ union quic_addr daddr; /* Dest address from received packet */
+ union quic_addr saddr; /* Source address from received packet */
+
+ struct sk_buff_head deferred_list; /* Packets deferred to work queue */
+ struct sk_buff_head backlog_list; /* Packets waiting for crypto keys */
+ struct list_head frame_list; /* Frames to pack into packet for send */
+ struct sk_buff *head; /* Head skb for packet bundling on send */
+ u32 version; /* QUIC version used/selected during handshake */
+ u16 overhead; /* QUIC header length excluding frames */
+ u8 taglen[2]; /* Tag length for short and long packets */
+ u16 padding; /* Total padding bytes to append after frames */
+ u16 frames; /* Number of ack-eliciting frames */
+ u16 mss[2]; /* MSS for datagram and non-datagram packets */
+ u16 hlen; /* UDP + IP header length for sending */
+ u16 len; /* QUIC packet length including taglen for sending */
+
+ u8 validate_peer_address:1; /* Temporary; will move to quic_outqueue */
+ u8 path_validating:1; /* Packet contains path_validating frames */
+ u8 ack_eliciting:1; /* Packet contains ack-eliciting frames */
+ u8 ack_immediate:1; /* Send ACK immediately (skip ack_delay timer) */
+ u8 non_probing:1; /* Packet contains non-probing frames */
+ u8 has_sack:1; /* Packet contains ACK frames */
+ u8 ipfragok:1; /* Allow IP fragmentation */
+ u8 path:1; /* Path identifier used to send this packet */
+ u8 level; /* Encryption level used */
+};
+
+#define QUIC_PACKET_INITIAL_V1 0
+#define QUIC_PACKET_0RTT_V1 1
+#define QUIC_PACKET_HANDSHAKE_V1 2
+#define QUIC_PACKET_RETRY_V1 3
+
+#define QUIC_PACKET_INITIAL_V2 1
+#define QUIC_PACKET_0RTT_V2 2
+#define QUIC_PACKET_HANDSHAKE_V2 3
+#define QUIC_PACKET_RETRY_V2 0
+
+#define QUIC_PACKET_INITIAL QUIC_PACKET_INITIAL_V1
+#define QUIC_PACKET_0RTT QUIC_PACKET_0RTT_V1
+#define QUIC_PACKET_HANDSHAKE QUIC_PACKET_HANDSHAKE_V1
+#define QUIC_PACKET_RETRY QUIC_PACKET_RETRY_V1
+
+#define QUIC_PACKET_INVALID 0xff
+
+#define QUIC_VERSION_LEN 4
+
+#define QUIC_ALPN_MAX_LEN 128
+
+#define QUIC_PACKET_MSS_NORMAL 0
+#define QUIC_PACKET_MSS_DGRAM 1
+
+#define QUIC_PACKET_FORM_SHORT 0
+#define QUIC_PACKET_FORM_LONG 1
+
+static inline u8 quic_packet_taglen(struct quic_packet *packet)
+{
+ return packet->taglen[packet->level != QUIC_CRYPTO_APP];
+}
+
+static inline void quic_packet_set_taglen(struct quic_packet *packet, u8 taglen)
+{
+ packet->taglen[QUIC_PACKET_FORM_SHORT] = taglen;
+}
+
+static inline u32 quic_packet_mss(struct quic_packet *packet)
+{
+ return packet->mss[QUIC_PACKET_MSS_NORMAL] - quic_packet_taglen(packet);
+}
+
+static inline u32 quic_packet_max_payload(struct quic_packet *packet)
+{
+ return packet->mss[QUIC_PACKET_MSS_NORMAL] - packet->overhead -
+ quic_packet_taglen(packet);
+}
+
+static inline u32 quic_packet_max_payload_dgram(struct quic_packet *packet)
+{
+ return packet->mss[QUIC_PACKET_MSS_DGRAM] - packet->overhead -
+ quic_packet_taglen(packet);
+}
+
+static inline bool quic_packet_empty(struct quic_packet *packet)
+{
+ return list_empty(&packet->frame_list);
+}
+
+static inline void quic_packet_reset(struct quic_packet *packet)
+{
+ packet->level = 0;
+ packet->has_sack = 0;
+ packet->non_probing = 0;
+ packet->ack_eliciting = 0;
+ packet->ack_immediate = 0;
+}
+
+int quic_packet_process(struct sock *sk, struct sk_buff *skb, gfp_t gfp);
+u16 quic_packet_overhead(struct sock *sk, u8 level, u8 path);
+int quic_packet_config(struct sock *sk, u8 level, u8 path);
+
+int quic_packet_create_and_xmit(struct sock *sk, gfp_t gfp);
+int quic_packet_route(struct sock *sk);
+
+void quic_packet_mss_update(struct sock *sk, u32 mss);
+void quic_packet_flush(struct sock *sk);
+void quic_packet_init(struct sock *sk);
+void quic_packet_free(struct sock *sk);
+
+u32 *quic_packet_compatible_versions(u32 version);
+
+void quic_packet_rcv_err_pmtu(struct sock *sk);
diff --git a/net/quic/path.c b/net/quic/path.c
new file mode 100644
index 0000000000000..a41b99dd31eff
--- /dev/null
+++ b/net/quic/path.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/udp_tunnel.h>
+#include <linux/quic.h>
+
+#include "common.h"
+#include "family.h"
+#include "path.h"
+
+static int quic_udp_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ quic_set_skb_iif(skb);
+
+ memset(skb->cb, 0, sizeof(skb->cb));
+ QUIC_SKB_CB(skb)->seqno = -1;
+ QUIC_SKB_CB(skb)->time = quic_ktime_get_us();
+
+ skb_pull(skb, sizeof(struct udphdr));
+ skb_dst_force(skb);
+ quic_packet_rcv(sk, skb, false);
+ /* .encap_rcv must return 0 if skb was either consumed or dropped. */
+ return 0;
+}
+
+static int quic_udp_err(struct sock *sk, struct sk_buff *skb)
+{
+ return quic_packet_rcv(sk, skb, true);
+}
+
+static void quic_udp_sock_put_work(struct work_struct *work)
+{
+ struct quic_udp_sock *us = container_of(work, struct quic_udp_sock,
+ work);
+ struct quic_uhash_head *head;
+ struct sock *sk = us->sk;
+
+ /* Hold the sock to safely access it in quic_udp_sock_lookup() even
+ * after udp_tunnel_sock_release(). The release must occur before
+ * __hlist_del() so a new UDP tunnel socket can be created for the same
+ * address and port if quic_udp_sock_lookup() fails to find one.
+ *
+ * Note: udp_tunnel_sock_release() cannot be called under the mutex due
+ * to some lockdep warnings.
+ */
+ sock_hold(sk);
+ udp_tunnel_sock_release(sk);
+
+ head = quic_udp_sock_head(sock_net(sk), ntohs(us->addr.v4.sin_port));
+ mutex_lock(&head->lock);
+ __hlist_del(&us->node);
+ mutex_unlock(&head->lock);
+
+ sock_put(sk);
+ kfree(us);
+}
+
+static struct quic_udp_sock *quic_udp_sock_create(struct sock *sk,
+ union quic_addr *a)
+{
+ struct udp_tunnel_sock_cfg tuncfg = {};
+ struct udp_port_cfg udp_conf = {};
+ struct net *net = sock_net(sk);
+ struct quic_uhash_head *head;
+ struct quic_udp_sock *us;
+ struct socket *sock;
+ int err;
+
+ us = kmalloc_obj(*us);
+ if (!us)
+ return ERR_PTR(-ENOMEM);
+
+ quic_udp_conf_init(sk, &udp_conf, a);
+ err = udp_sock_create(net, &udp_conf, &sock);
+ if (err) {
+ pr_debug("%s: failed to create udp sock\n", __func__);
+ kfree(us);
+ return ERR_PTR(err);
+ }
+
+ tuncfg.encap_type = 1;
+ tuncfg.encap_rcv = quic_udp_rcv;
+ tuncfg.encap_err_lookup = quic_udp_err;
+ setup_udp_tunnel_sock(net, sock->sk, &tuncfg);
+
+ refcount_set(&us->refcnt, 1);
+ us->sk = sock->sk;
+ memcpy(&us->addr, a, sizeof(*a));
+ us->bind_ifindex = udp_conf.bind_ifindex;
+
+ INIT_WORK(&us->work, quic_udp_sock_put_work);
+ head = quic_udp_sock_head(net, ntohs(a->v4.sin_port));
+ hlist_add_head(&us->node, &head->head);
+
+ return us;
+}
+
+static bool quic_udp_sock_get(struct quic_udp_sock *us)
+{
+ return refcount_inc_not_zero(&us->refcnt);
+}
+
+static void quic_udp_sock_put(struct quic_udp_sock *us)
+{
+ /* The UDP socket may be freed in atomic RX context during connection
+ * migration; defer the release to a workqueue.
+ */
+ if (refcount_dec_and_test(&us->refcnt))
+ queue_work(quic_wq, &us->work);
+}
+
+/* Lookup a quic_udp_sock in the global hash table by port or address. If 'a'
+ * is provided, it searches for a socket whose local address matches 'a' and,
+ * if applicable, matches the device binding. If 'a' is NULL, it searches only
+ * by port.
+ */
+static struct quic_udp_sock *quic_udp_sock_lookup(struct sock *sk,
+ union quic_addr *a, u16 port)
+{
+ struct net *net = sock_net(sk);
+ struct quic_uhash_head *head;
+ struct quic_udp_sock *us;
+
+ head = quic_udp_sock_head(net, port);
+ hlist_for_each_entry(us, &head->head, node) {
+ if (net != sock_net(us->sk))
+ continue;
+ if (a) {
+ if (quic_cmp_sk_addr(us->sk, &us->addr, a) &&
+ us->bind_ifindex == quic_get_dev_if(sk, a))
+ return us;
+ continue;
+ }
+ if (ntohs(us->addr.v4.sin_port) == port)
+ return us;
+ }
+ return NULL;
+}
+
+static void quic_path_set_udp_sk(struct quic_path *path,
+ struct quic_udp_sock *us)
+{
+ if (path->udp_sk)
+ quic_udp_sock_put(path->udp_sk);
+
+ path->udp_sk = us;
+ if (!us) {
+ path->usk = NULL;
+ memset(&path->uaddr, 0, sizeof(path->uaddr));
+ return;
+ }
+ path->usk = us->sk;
+ memcpy(&path->uaddr, &us->addr, sizeof(us->addr));
+}
+
+/* Binds a QUIC path to a local port and sets up a UDP socket. */
+int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path)
+{
+ union quic_addr *a = quic_path_saddr(paths, path);
+ int rover, low, high, remaining;
+ struct net *net = sock_net(sk);
+ struct quic_uhash_head *head;
+ struct quic_udp_sock *us;
+ u16 port;
+
+ port = ntohs(a->v4.sin_port);
+ if (port) {
+ if (inet_port_requires_bind_service(net, port) &&
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
+ return -EACCES;
+ head = quic_udp_sock_head(net, port);
+ mutex_lock(&head->lock);
+ us = quic_udp_sock_lookup(sk, a, port);
+ if (us) {
+ if (!uid_eq(sk->sk_uid, us->sk->sk_uid)) {
+ mutex_unlock(&head->lock);
+ return -EADDRINUSE;
+ }
+ /* Allow reuse of an existing UDP tunnel socket.
+ * However, if it is in the middle of asynchronous
+ * teardown (via workqueue), it is temporarily unusable.
+ * Return -EAGAIN (not -EADDRINUSE) to signal the caller
+ * to retry soon.
+ */
+ if (!quic_udp_sock_get(us)) {
+ mutex_unlock(&head->lock);
+ return -EAGAIN;
+ }
+ } else {
+ us = quic_udp_sock_create(sk, a);
+ if (IS_ERR(us)) {
+ mutex_unlock(&head->lock);
+ return PTR_ERR(us);
+ }
+ }
+ mutex_unlock(&head->lock);
+ quic_path_set_udp_sk(&paths->path[path], us);
+ return 0;
+ }
+
+ inet_sk_get_local_port_range(sk, &low, &high);
+ remaining = (high - low) + 1;
+ rover = get_random_u32_below(remaining) + low;
+ do {
+ rover++;
+ if (rover < low || rover > high)
+ rover = low;
+ port = (u16)rover;
+ if (inet_is_local_reserved_port(net, port))
+ continue;
+
+ head = quic_udp_sock_head(net, port);
+ mutex_lock(&head->lock);
+ if (quic_udp_sock_lookup(sk, NULL, port)) {
+ mutex_unlock(&head->lock);
+ cond_resched();
+ continue;
+ }
+ a->v4.sin_port = htons(port);
+ us = quic_udp_sock_create(sk, a);
+ if (IS_ERR(us)) {
+ a->v4.sin_port = 0;
+ mutex_unlock(&head->lock);
+ if (PTR_ERR(us) == -EADDRINUSE) {
+ cond_resched();
+ continue;
+ }
+ return PTR_ERR(us);
+ }
+ mutex_unlock(&head->lock);
+
+ quic_path_set_udp_sk(&paths->path[path], us);
+ __sk_dst_reset(sk);
+ return 0;
+ } while (--remaining > 0);
+
+ return -EADDRINUSE;
+}
+
+/* Swaps the active and alternate QUIC paths.
+ *
+ * Promotes the alternate path (path[1]) to become the new active path
+ * (path[0]). If the alternate path has a valid UDP socket, the entire path is
+ * swapped. Otherwise, only the destination address is exchanged, assuming the
+ * source address is the same and no rebind is needed.
+ *
+ * This is typically used during path migration or alternate path promotion.
+ */
+void quic_path_swap(struct quic_path_group *paths)
+{
+ struct quic_path path = paths->path[0];
+
+ paths->alt_probes = 0;
+ paths->alt_state = QUIC_PATH_ALT_SWAPPED;
+
+ /* Protect path[0] modifications with seqcount for RCU readers in
+ * quic_sock_lookup(). The seqcount allows readers to detect torn reads
+ * during the non-atomic structure assignment.
+ */
+ local_bh_disable();
+ write_seqcount_begin(&paths->path_seq);
+ if (paths->path[1].udp_sk) {
+ paths->path[0] = paths->path[1];
+ paths->path[1] = path;
+ goto out;
+ }
+
+ paths->path[0].daddr = paths->path[1].daddr;
+ paths->path[1].daddr = path.daddr;
+out:
+ write_seqcount_end(&paths->path_seq);
+ local_bh_enable();
+}
+
+/* Frees resources associated with a QUIC path.
+ *
+ * This is used for cleanup during error handling or when the path is no longer
+ * needed.
+ */
+void quic_path_unbind(struct sock *sk, struct quic_path_group *paths, u8 path)
+{
+ paths->alt_probes = 0;
+ paths->alt_state = QUIC_PATH_ALT_NONE;
+
+ quic_path_set_udp_sk(&paths->path[path], NULL);
+
+ memset(quic_path_daddr(paths, path), 0, sizeof(union quic_addr));
+ memset(quic_path_saddr(paths, path), 0, sizeof(union quic_addr));
+}
+
+/* Detects and records a potential alternate path.
+ *
+ * If the new source or destination address differs from the active path, and
+ * alternate path detection is not disabled, the function updates the alternate
+ * path slot (path[1]) with the new addresses.
+ *
+ * This is typically called on packet receive to detect new possible network
+ * paths (e.g., NAT rebinding, mobility).
+ *
+ * Returns true if a new alternate path was detected and updated, false
+ * otherwise.
+ */
+bool quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa,
+ union quic_addr *da, struct sock *sk)
+{
+ bool remote = !quic_cmp_sk_addr(sk, quic_path_daddr(paths, 0), da);
+ bool local = !quic_cmp_sk_addr(sk, quic_path_saddr(paths, 0), sa);
+
+ if (!local && !remote)
+ return false;
+
+ if ((local && paths->disable_saddr_alt) ||
+ (remote && paths->disable_daddr_alt))
+ return false;
+
+ if (!quic_path_saddr(paths, 1)->v4.sin_port)
+ quic_path_set_saddr(paths, 1, sa);
+
+ if (!quic_cmp_sk_addr(sk, quic_path_saddr(paths, 1), sa))
+ return false;
+
+ if (!quic_path_daddr(paths, 1)->v4.sin_port)
+ quic_path_set_daddr(paths, 1, da);
+
+ return quic_cmp_sk_addr(sk, quic_path_daddr(paths, 1), da);
+}
+
+void quic_path_get_param(struct quic_path_group *paths,
+ struct quic_transport_param *p)
+{
+ if (p->remote) {
+ p->disable_active_migration = paths->disable_saddr_alt;
+ return;
+ }
+ p->disable_active_migration = paths->disable_daddr_alt;
+}
+
+void quic_path_set_param(struct quic_path_group *paths,
+ struct quic_transport_param *p)
+{
+ if (p->remote) {
+ paths->disable_saddr_alt = !!p->disable_active_migration;
+ return;
+ }
+ paths->disable_daddr_alt = !!p->disable_active_migration;
+}
+
+/* State Machine defined in rfc8899#section-5.2 */
+enum quic_plpmtud_state {
+ QUIC_PL_DISABLED,
+ QUIC_PL_BASE,
+ QUIC_PL_SEARCH,
+ QUIC_PL_COMPLETE,
+ QUIC_PL_ERROR,
+};
+
+#define QUIC_BASE_PLPMTU 1200
+#define QUIC_MAX_PLPMTU 9000
+#define QUIC_MIN_PLPMTU 512
+
+#define QUIC_MAX_PROBES 3
+
+#define QUIC_PL_BIG_STEP 32
+#define QUIC_PL_MIN_STEP 4
+
+/* Handle PLPMTUD probe failure on a QUIC path.
+ *
+ * Called immediately after sending a probe packet in QUIC Path MTU Discovery.
+ * Tracks probe count and manages state transitions based on the number of
+ * probes sent and current PLPMTUD state (BASE, SEARCH, COMPLETE, ERROR).
+ * Detects probe failures and black holes, adjusting PMTU and probe sizes
+ * accordingly.
+ *
+ * Return: New PMTU value if updated, else 0.
+ */
+u32 quic_path_pl_send(struct quic_path_group *paths, s64 number)
+{
+ u32 pathmtu = 0;
+
+ paths->pl.number = number;
+ if (paths->pl.probe_count < QUIC_MAX_PROBES)
+ goto out;
+
+ paths->pl.probe_count = 0;
+ if (paths->pl.state == QUIC_PL_BASE) {
+ if (paths->pl.probe_size == QUIC_BASE_PLPMTU) {
+ /* BASE_PLPMTU Confirming Failed: Base -> Error. */
+ paths->pl.state = QUIC_PL_ERROR;
+
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ }
+ } else if (paths->pl.state == QUIC_PL_SEARCH) {
+ if (paths->pl.pmtu == paths->pl.probe_size) {
+ /* Black Hole Detected: Search -> Base. */
+ paths->pl.state = QUIC_PL_BASE;
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+ paths->pl.probe_high = 0;
+
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ } else { /* Normal probe failure. */
+ paths->pl.probe_high = paths->pl.probe_size;
+ paths->pl.probe_size = paths->pl.pmtu;
+ }
+ } else if (paths->pl.state == QUIC_PL_COMPLETE) {
+ if (paths->pl.pmtu == paths->pl.probe_size) {
+ /* Black Hole Detected: Search Complete -> Base. */
+ paths->pl.state = QUIC_PL_BASE;
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+
+ /* probe_high already reset when entering COMPLETE. */
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ }
+ }
+
+out:
+ pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, high: %d\n",
+ __func__, paths, paths->pl.state, paths->pl.pmtu,
+ paths->pl.probe_size, paths->pl.probe_high);
+ paths->pl.probe_count++;
+ return pathmtu;
+}
+
+/* Handle successful reception of a PMTU probe.
+ *
+ * Called when a probe packet is acknowledged. Updates probe size and
+ * transitions state if needed (e.g., from SEARCH to COMPLETE). Expands PMTU
+ * using binary or linear search depending on state.
+ *
+ * Return: New PMTU to apply if search completes, or 0 if no change.
+ */
+u32 quic_path_pl_recv(struct quic_path_group *paths, bool *raise_timer,
+ bool *complete)
+{
+ u32 pathmtu = 0;
+ u16 next;
+
+ pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, high: %d\n",
+ __func__, paths, paths->pl.state, paths->pl.pmtu,
+ paths->pl.probe_size, paths->pl.probe_high);
+
+ *raise_timer = false;
+ paths->pl.number = 0;
+ paths->pl.pmtu = paths->pl.probe_size;
+ paths->pl.probe_count = 0;
+ if (paths->pl.state == QUIC_PL_BASE) {
+ paths->pl.state = QUIC_PL_SEARCH; /* Base -> Search */
+ paths->pl.probe_size += QUIC_PL_BIG_STEP;
+ } else if (paths->pl.state == QUIC_PL_ERROR) {
+ paths->pl.state = QUIC_PL_SEARCH; /* Error -> Search */
+
+ paths->pl.pmtu = paths->pl.probe_size;
+ pathmtu = (u32)paths->pl.pmtu;
+ paths->pl.probe_size += QUIC_PL_BIG_STEP;
+ } else if (paths->pl.state == QUIC_PL_SEARCH) {
+ if (!paths->pl.probe_high) {
+ if (paths->pl.probe_size < QUIC_MAX_PLPMTU) {
+ next = paths->pl.probe_size + QUIC_PL_BIG_STEP;
+ paths->pl.probe_size =
+ min_t(u16, next, QUIC_MAX_PLPMTU);
+ *complete = false;
+ return 0;
+ }
+ paths->pl.probe_high = QUIC_MAX_PLPMTU;
+ }
+ paths->pl.probe_size += QUIC_PL_MIN_STEP;
+ if (paths->pl.probe_size >= paths->pl.probe_high) {
+ paths->pl.probe_high = 0;
+ /* Search -> Search Complete */
+ paths->pl.state = QUIC_PL_COMPLETE;
+
+ paths->pl.probe_size = paths->pl.pmtu;
+ pathmtu = (u32)paths->pl.pmtu;
+ *raise_timer = true;
+ }
+ } else if (paths->pl.state == QUIC_PL_COMPLETE) {
+ /* Raise probe_size after 30 * interval in Search Complete;
+ * Search Complete -> Search.
+ */
+ paths->pl.state = QUIC_PL_SEARCH;
+ next = paths->pl.probe_size + QUIC_PL_MIN_STEP;
+ paths->pl.probe_size = min_t(u16, next, QUIC_MAX_PLPMTU);
+ }
+
+ *complete = (paths->pl.state == QUIC_PL_COMPLETE);
+ return pathmtu;
+}
+
+/* Handle ICMP "Packet Too Big" messages.
+ *
+ * Responds to an incoming ICMP error by reducing the probe size or falling
+ * back to a safe baseline PMTU depending on current state. Also handles cases
+ * where the PMTU hint lies between probe and current PMTU.
+ *
+ * Return: New PMTU to apply if state changes, or 0 if no change.
+ */
+u32 quic_path_pl_toobig(struct quic_path_group *paths, u32 pmtu,
+ bool *reset_timer)
+{
+ u32 pathmtu = 0;
+
+ pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, ptb: %d\n",
+ __func__, paths, paths->pl.state, paths->pl.pmtu,
+ paths->pl.probe_size, pmtu);
+
+ *reset_timer = false;
+ if (pmtu < QUIC_MIN_PLPMTU || pmtu >= (u32)paths->pl.probe_size)
+ return pathmtu;
+
+ if (paths->pl.state == QUIC_PL_BASE) {
+ if (pmtu < QUIC_BASE_PLPMTU) {
+ paths->pl.state = QUIC_PL_ERROR; /* Base -> Error */
+
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ }
+ } else if (paths->pl.state == QUIC_PL_SEARCH) {
+ if (pmtu >= QUIC_BASE_PLPMTU && pmtu < (u32)paths->pl.pmtu) {
+ paths->pl.state = QUIC_PL_BASE; /* Search -> Base */
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+ paths->pl.probe_count = 0;
+
+ paths->pl.probe_high = 0;
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ } else if (pmtu > (u32)paths->pl.pmtu &&
+ pmtu < (u32)paths->pl.probe_size) {
+ paths->pl.probe_size = (u16)pmtu;
+ paths->pl.probe_count = 0;
+ }
+ } else if (paths->pl.state == QUIC_PL_COMPLETE) {
+ if (pmtu >= QUIC_BASE_PLPMTU && pmtu < (u32)paths->pl.pmtu) {
+ paths->pl.state = QUIC_PL_BASE; /* Complete -> Base */
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+ paths->pl.probe_count = 0;
+
+ paths->pl.probe_high = 0;
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ *reset_timer = true;
+ }
+ }
+ return pathmtu;
+}
+
+/* Reset PLPMTUD state for a path.
+ *
+ * Resets all PLPMTUD-related state to its initial configuration. Called when
+ * a new path is initialized or when recovering from errors.
+ */
+void quic_path_pl_reset(struct quic_path_group *paths)
+{
+ paths->pl.number = 0;
+ paths->pl.probe_high = 0;
+ paths->pl.probe_count = 0;
+ paths->pl.state = QUIC_PL_BASE;
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+}
+
+/* Check if a packet number confirms PLPMTUD probe.
+ *
+ * Checks whether the last probe (tracked by .number) has been acknowledged.
+ * If the probe number lies within the ACK range, confirmation is successful.
+ *
+ * Return: true if probe is confirmed, false otherwise.
+ */
+bool quic_path_pl_confirm(struct quic_path_group *paths, s64 largest,
+ s64 smallest)
+{
+ return paths->pl.number && paths->pl.number >= smallest &&
+ paths->pl.number <= largest;
+}
+
+void quic_path_init(struct quic_path_group *paths)
+{
+ seqcount_init(&paths->path_seq);
+}
diff --git a/net/quic/path.h b/net/quic/path.h
new file mode 100644
index 0000000000000..45c3c6bb9d973
--- /dev/null
+++ b/net/quic/path.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_PATH_MIN_PMTU 1200U
+#define QUIC_PATH_MAX_PMTU 65535U
+
+#define QUIC_MIN_UDP_PAYLOAD 1200
+#define QUIC_MAX_UDP_PAYLOAD 65527
+
+#define QUIC_PATH_ENTROPY_LEN 8
+
+#define QUIC_PMTUD_RAISE_TIMER_FACTOR 30
+
+extern struct workqueue_struct *quic_wq;
+
+/* Connection Migration State Machine:
+ *
+ * +--------+ recv non-probing, free old path +----------+
+ * | NONE | <-------------------------------------- | SWAPPED |
+ * +--------+ +----------+
+ * | ^ \ ^
+ * | \ \ |
+ * | \ \ new path detected, | recv
+ * | \ \ has another DCID, | Path
+ * | \ \ snd Path Challenge | Response
+ * | \ ------------------------------- |
+ * | ------------------------------- \ |
+ * | new path detected, Path \ \ |
+ * | has no other DCID, Challenge \ \ |
+ * | request a new DCID failed \ \ |
+ * v \ v |
+ * +----------+ +----------+
+ * | PENDING | ------------------------------------> | PROBING |
+ * +----------+ recv a new DCID, snd Path Challenge +----------+
+ */
+enum {
+ QUIC_PATH_ALT_NONE,
+ QUIC_PATH_ALT_PENDING, /* Waiting for new dest conn ID for migration */
+ QUIC_PATH_ALT_PROBING, /* Validating alternate path (PATH_CHALLENGE) */
+ QUIC_PATH_ALT_SWAPPED, /* Alternate path is now active; roles swapped */
+};
+
+struct quic_udp_sock {
+ struct work_struct work; /* Workqueue to destroy UDP tunnel socket */
+ struct hlist_node node; /* Node in addr-based UDP socket hash table */
+ union quic_addr addr; /* Source addr of underlying UDP tunnel socket */
+ int bind_ifindex;
+ refcount_t refcnt;
+ struct sock *sk; /* Underlying UDP tunnel socket */
+};
+
+struct quic_path {
+ union quic_addr daddr; /* Destination address */
+ union quic_addr saddr; /* Source address */
+
+ /* Wrapped UDP socket for receiving QUIC */
+ struct quic_udp_sock *udp_sk;
+ /* Cached UDP tunnel socket and source addr for RCU access */
+ union quic_addr uaddr;
+ struct sock *usk;
+};
+
+struct quic_path_group {
+ /* Connection ID validation during handshake (rfc9000#section-7.3) */
+ struct quic_conn_id retry_dcid; /* Source CID from Retry packet */
+ struct quic_conn_id orig_dcid; /* Destination CID from first Initial */
+
+ /* Path validation (rfc9000#section-8.2) */
+ u8 entropy[QUIC_PATH_ENTROPY_LEN]; /* Entropy for PATH_CHALLENGE */
+ struct quic_path path[2]; /* Active path (0) and alternate path (1) */
+ seqcount_t path_seq; /* Protects path[0] during swap */
+ struct flowi fl; /* Flow info from routing decisions */
+
+ /* Anti-amplification limit (rfc9000#section-8) */
+ u32 ampl_sndlen; /* Bytes sent before address is validated */
+ u32 ampl_rcvlen; /* Bytes received to lift amplification limit */
+
+ /* MTU discovery handling */
+ struct { /* PLPMTUD probing (rfc8899) */
+ s64 number; /* Packet number used for current probe */
+ u16 pmtu; /* Confirmed path MTU */
+
+ u16 probe_size; /* Current probe packet size */
+ u16 probe_high; /* Highest failed probe size */
+ u8 probe_count; /* Retry count for current probe_size */
+ u8 state; /* Probe state machine (rfc8899#section-5.2) */
+ } pl;
+ u32 mtu_info; /* PMTU value from received ICMP, pending apply */
+
+ u32 plpmtud_interval; /* Time interval for the PLPMTUD probe timer */
+ u32 keepalive_interval; /* Time interval to maintain path liveness */
+
+ u8 ecn_probes; /* ECN probe counter */
+ u8 validated:1; /* Path validated with PATH_RESPONSE */
+ u8 blocked:1; /* Blocked by anti-amplification limit */
+ u8 version:1; /* Version negotiation performed */
+ u8 retry:1; /* Retry used in initial packet */
+
+ /* Connection Migration (rfc9000#section-9) */
+ u8 disable_saddr_alt:1; /* Remote disable_active_migration parameter */
+ u8 disable_daddr_alt:1; /* Local disable_active_migration parameter */
+ u8 pref_addr:1; /* Preferred address offered (rfc9000#section-18.2) */
+ u8 alt_probes; /* Number of PATH_CHALLENGE probes sent */
+ u8 alt_state; /* Connection migration state (see above) */
+};
+
+static inline union quic_addr *quic_path_saddr(struct quic_path_group *paths,
+ u8 path)
+{
+ return &paths->path[path].saddr;
+}
+
+static inline void quic_path_set_saddr(struct quic_path_group *paths, u8 path,
+ union quic_addr *addr)
+{
+ memcpy(quic_path_saddr(paths, path), addr, sizeof(*addr));
+}
+
+static inline union quic_addr *quic_path_daddr(struct quic_path_group *paths,
+ u8 path)
+{
+ return &paths->path[path].daddr;
+}
+
+static inline void quic_path_set_daddr(struct quic_path_group *paths, u8 path,
+ union quic_addr *addr)
+{
+ memcpy(quic_path_daddr(paths, path), addr, sizeof(*addr));
+}
+
+static inline union quic_addr *quic_path_uaddr(struct quic_path_group *paths,
+ u8 path)
+{
+ return &paths->path[path].uaddr;
+}
+
+static inline struct sock *quic_path_usock(struct quic_path_group *paths,
+ u8 path)
+{
+ return paths->path[path].usk;
+}
+
+static inline bool quic_path_alt_state(struct quic_path_group *paths, u8 state)
+{
+ return paths->alt_state == state;
+}
+
+static inline void quic_path_set_alt_state(struct quic_path_group *paths,
+ u8 state)
+{
+ paths->alt_state = state;
+}
+
+/* Returns the destination Connection ID (DCID) used for identifying the
+ * connection. Per rfc9000#section-7.3, handshake packets are considered part
+ * of the same connection if their DCID matches the one returned here.
+ */
+static inline struct quic_conn_id *
+quic_path_orig_dcid(struct quic_path_group *paths)
+{
+ return paths->retry ? &paths->retry_dcid : &paths->orig_dcid;
+}
+
+int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp);
+void quic_path_init(struct quic_path_group *paths);
+
+bool quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa,
+ union quic_addr *da, struct sock *sk);
+int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path);
+void quic_path_unbind(struct sock *sk, struct quic_path_group *paths, u8 path);
+void quic_path_swap(struct quic_path_group *paths);
+
+u32 quic_path_pl_recv(struct quic_path_group *paths, bool *raise_timer,
+ bool *complete);
+u32 quic_path_pl_toobig(struct quic_path_group *paths, u32 pmtu,
+ bool *reset_timer);
+u32 quic_path_pl_send(struct quic_path_group *paths, s64 number);
+
+void quic_path_get_param(struct quic_path_group *paths,
+ struct quic_transport_param *p);
+void quic_path_set_param(struct quic_path_group *paths,
+ struct quic_transport_param *p);
+bool quic_path_pl_confirm(struct quic_path_group *paths,
+ s64 largest, s64 smallest);
+void quic_path_pl_reset(struct quic_path_group *paths);
diff --git a/net/quic/pnspace.c b/net/quic/pnspace.c
new file mode 100644
index 0000000000000..c7c4d0be2d980
--- /dev/null
+++ b/net/quic/pnspace.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <linux/slab.h>
+
+#include "common.h"
+#include "pnspace.h"
+
+int quic_pnspace_init(struct quic_pnspace *space)
+{
+ space->pn_map = kzalloc(BITS_TO_BYTES(QUIC_PN_MAP_INITIAL), GFP_KERNEL);
+ if (!space->pn_map)
+ return -ENOMEM;
+ space->pn_map_len = QUIC_PN_MAP_INITIAL;
+
+ space->max_time_limit = QUIC_PNSPACE_TIME_LIMIT;
+ space->next_pn = QUIC_PNSPACE_NEXT_PN;
+ space->max_pn_acked_seen = -1;
+ space->base_pn = -1;
+ return 0;
+}
+
+void quic_pnspace_free(struct quic_pnspace *space)
+{
+ space->pn_map_len = 0;
+ kfree(space->pn_map);
+ space->pn_map = NULL;
+}
+
+/* Expand the bitmap tracking received packet numbers. Ensures the pn_map
+ * bitmap can cover at least @size packet numbers. Allocates a larger bitmap,
+ * copies existing data, and updates metadata.
+ *
+ * Return: 0 on success, or a negative errno value on failure.
+ */
+static int quic_pnspace_grow(struct quic_pnspace *space, u16 size, gfp_t gfp)
+{
+ u16 len, inc, offset;
+ unsigned long *new;
+
+ inc = ALIGN((size - space->pn_map_len), BITS_PER_LONG) +
+ QUIC_PN_MAP_INCREMENT;
+ len = (u16)min(space->pn_map_len + inc, QUIC_PN_MAP_SIZE);
+
+ new = kzalloc(BITS_TO_BYTES(len), gfp);
+ if (!new)
+ return -ENOMEM;
+
+ offset = (u16)(space->max_pn_seen + 1 - space->base_pn);
+ bitmap_copy(new, space->pn_map, offset);
+ kfree(space->pn_map);
+ space->pn_map = new;
+ space->pn_map_len = len;
+
+ return 0;
+}
+
+/* Check if a packet number has been received.
+ *
+ * Returns: 0 if the packet number has not been received. 1 if it has already
+ * been received. -EINVAL if the packet number is invalid (out of range 0 to
+ * QUIC_PN_MAX) or too old to track (below min_pn_seen).
+ */
+int quic_pnspace_check(struct quic_pnspace *space, s64 pn)
+{
+ if (pn > QUIC_PN_MAX || pn < 0)
+ return -EINVAL;
+
+ if (space->base_pn == -1) /* No packet number received yet. */
+ return 0;
+
+ if (pn < space->min_pn_seen)
+ return -EINVAL;
+
+ if (pn < space->base_pn)
+ return 1;
+ if (pn - space->base_pn < space->pn_map_len &&
+ test_bit(pn - space->base_pn, space->pn_map))
+ return 1;
+
+ return 0;
+}
+
+/* Advance base_pn past contiguous received packet numbers. Finds the next gap
+ * (unreceived packet) beyond @pn, shifts the bitmap, and updates base_pn
+ * accordingly.
+ */
+static void quic_pnspace_move(struct quic_pnspace *space, s64 pn)
+{
+ u16 offset;
+
+ offset = (u16)(pn + 1 - space->base_pn);
+ offset = (u16)find_next_zero_bit(space->pn_map, space->pn_map_len,
+ offset);
+ space->base_pn += offset;
+ bitmap_shift_right(space->pn_map, space->pn_map, offset,
+ space->pn_map_len);
+}
+
+/* Mark a packet number as received. Updates the packet number map to record
+ * reception of @pn. Advances base_pn if possible, and updates max/min/last
+ * seen fields as needed.
+ *
+ * Returns: 0 on success or if the packet was already marked, or a negative
+ * error returned by bitmap growth when expanding the map.
+ */
+int quic_pnspace_mark(struct quic_pnspace *space, s64 pn, gfp_t gfp)
+{
+ s64 last_max_pn_seen, off;
+ u64 last_max_pn_time;
+ bool has_gap;
+ int err;
+
+ if (space->base_pn == -1) {
+ /* Initialize base_pn based on the peer's first packet number
+ * since peer's packet numbers may start at a non-zero value.
+ */
+ quic_pnspace_set_base_pn(space, pn + 1);
+ return 0;
+ }
+
+ /* Ignore packets with number less than current base (already
+ * processed).
+ */
+ if (pn < space->base_pn)
+ return 0;
+
+ /* If offset is beyond current map length, try to grow the bitmap to
+ * accommodate.
+ */
+ off = pn - space->base_pn;
+ if (off >= space->pn_map_len) {
+ if (off >= QUIC_PN_MAP_SIZE) {
+ bitmap_zero(space->pn_map, space->pn_map_len);
+ quic_pnspace_set_base_pn(space, pn + 1);
+ return 0;
+ }
+ err = quic_pnspace_grow(space, off + 1, gfp);
+ if (err)
+ return err;
+ }
+
+ has_gap = quic_pnspace_has_gap(space);
+ if (space->max_pn_seen < pn) {
+ space->max_pn_seen = pn;
+ space->max_pn_time = space->time;
+ }
+
+ if (space->base_pn == pn) { /* PN is next expected packet. */
+ if (has_gap) /* Advance to next gap. */
+ quic_pnspace_move(space, pn);
+ else /* Fast path: increment base_pn if no gaps. */
+ space->base_pn++;
+ } else { /* Mark this packet as received in the bitmap. */
+ set_bit(off, space->pn_map);
+ }
+
+ /* Only update min and last_max_pn_seen if this packet is the current
+ * max_pn.
+ */
+ if (space->max_pn_seen != pn)
+ return 0;
+
+ /* Check if enough time has elapsed or enough packets have been
+ * received to update tracking.
+ */
+ last_max_pn_seen = min_t(s64, space->last_max_pn_seen, space->base_pn);
+ last_max_pn_time = space->last_max_pn_time;
+ if (space->max_pn_time < last_max_pn_time + space->max_time_limit &&
+ space->max_pn_seen <= last_max_pn_seen + QUIC_PN_MAP_LIMIT)
+ return 0;
+
+ /* Advance base_pn if last_max_pn_seen is ahead of current base_pn.
+ * This is needed because QUIC doesn't retransmit packets;
+ * retransmitted frames are carried in new packets, so we move forward.
+ */
+ if (space->last_max_pn_seen + 1 > space->base_pn)
+ quic_pnspace_move(space, space->last_max_pn_seen);
+
+ space->min_pn_seen = space->last_max_pn_seen;
+ space->last_max_pn_seen = space->max_pn_seen;
+ space->last_max_pn_time = space->max_pn_time;
+ return 0;
+}
+
+/* Find the next gap in received packet numbers. Scans pn_map for a gap
+ * starting from *@iter. A gap is a contiguous block of unreceived packets
+ * between received ones.
+ *
+ * Returns: true if a gap was found, false if no more gaps exist or are
+ * relevant.
+ */
+static bool quic_pnspace_next_gap_ack(const struct quic_pnspace *space,
+ s64 *iter, u16 *start, u16 *end)
+{
+ u16 start_ = 0, end_ = 0, offset = (u16)(*iter - space->base_pn);
+
+ start_ = (u16)find_next_zero_bit(space->pn_map, space->pn_map_len,
+ offset);
+ if (space->max_pn_seen <= space->base_pn + start_)
+ return false;
+
+ end_ = (u16)find_next_bit(space->pn_map, space->pn_map_len, start_);
+ if (space->max_pn_seen <= space->base_pn + end_ - 1)
+ return false;
+
+ *start = start_ + 1;
+ *end = end_;
+ *iter = space->base_pn + *end;
+ return true;
+}
+
+/* Generate gap acknowledgment blocks (GABs). GABs describe ranges of
+ * unacknowledged packets between received ones, and are used in ACK frames.
+ *
+ * This function uses a sliding window approach to ensure the most recent gaps
+ * are preserved when the total number exceeds QUIC_PN_MAP_MAX_GABS. If there
+ * are more gaps than the limit, the oldest gaps are merged into a single gap,
+ * and the newest (QUIC_PN_MAP_MAX_GABS) gaps are preserved individually.
+ *
+ * Returns: Number of generated GABs (up to QUIC_PN_MAP_MAX_GABS).
+ */
+u16 quic_pnspace_num_gabs(struct quic_pnspace *space,
+ struct quic_gap_ack_block *gabs)
+{
+ struct quic_gap_ack_block tmp[QUIC_PN_MAP_MAX_GABS];
+ u16 start, end, mstart = 0, ngaps = 0, i = 0;
+ s64 iter;
+
+ if (!quic_pnspace_has_gap(space))
+ return 0;
+
+ iter = space->base_pn;
+
+ /* Scan all gaps using a sliding window to keep the newest ones. */
+ while (quic_pnspace_next_gap_ack(space, &iter, &start, &end)) {
+ if (ngaps < QUIC_PN_MAP_MAX_GABS) {
+ if (ngaps == 0)
+ mstart = start;
+ gabs[ngaps].start = start;
+ gabs[ngaps].end = end;
+ ngaps++;
+ continue;
+ }
+ gabs[i].start = start;
+ gabs[i].end = end;
+ i = (i + 1) % QUIC_PN_MAP_MAX_GABS;
+ }
+ if (i == 0) {
+ if (ngaps == QUIC_PN_MAP_MAX_GABS)
+ gabs[0].start = mstart;
+ return ngaps;
+ }
+
+ /* Overflow occurred: merge all discarded gaps with the gap at
+ * position i (which hasn't been overwritten yet) into one.
+ */
+ gabs[i].start = mstart;
+ memcpy(tmp, gabs, ngaps * sizeof(*gabs));
+ memcpy(gabs, &tmp[i], (ngaps - i) * sizeof(*gabs));
+ memcpy(gabs + (ngaps - i), tmp, i * sizeof(*gabs));
+
+ return ngaps;
+}
diff --git a/net/quic/pnspace.h b/net/quic/pnspace.h
new file mode 100644
index 0000000000000..15ce6d2ef7269
--- /dev/null
+++ b/net/quic/pnspace.h
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_PN_MAP_MAX_GABS 32
+
+#define QUIC_PN_MAP_INITIAL 64
+#define QUIC_PN_MAP_INCREMENT QUIC_PN_MAP_INITIAL
+#define QUIC_PN_MAP_SIZE 4096
+#define QUIC_PN_MAP_LIMIT (QUIC_PN_MAP_SIZE * 3 / 4)
+
+#define QUIC_PNSPACE_MAX (QUIC_CRYPTO_MAX - 1)
+#define QUIC_PNSPACE_NEXT_PN 0
+#define QUIC_PNSPACE_TIME_LIMIT (333000 * 3)
+
+enum {
+ QUIC_ECN_ECT1,
+ QUIC_ECN_ECT0,
+ QUIC_ECN_CE,
+ QUIC_ECN_MAX
+};
+
+enum {
+ QUIC_ECN_LOCAL, /* ECN bits from incoming IP headers */
+ QUIC_ECN_PEER, /* ECN bits reported by peer in ACK frames */
+ QUIC_ECN_ACKED, /* ECN bits from packets newly ACKed */
+ QUIC_ECN_DIR_MAX
+};
+
+/* Represents a gap (range of missing packets) in the ACK map. The values are
+ * offsets from base_pn, with both 'start' and 'end' being +1.
+ */
+struct quic_gap_ack_block {
+ u16 start;
+ u16 end;
+};
+
+/* Packet Number Map (pn_map) Layout:
+ *
+ * min_pn_seen -->++-----------------------+---------------------+---
+ * base_pn -----^ last_max_pn_seen --^ max_pn_seen --^
+ *
+ * Map Advancement Logic:
+ * - min_pn_seen = last_max_pn_seen;
+ * - base_pn = first zero bit after last_max_pn_seen;
+ * - last_max_pn_seen = max_pn_seen;
+ * - last_max_pn_time = current time;
+ *
+ * Conditions to Advance pn_map:
+ * - (max_pn_time - last_max_pn_time) >= max_time_limit, or
+ * - (max_pn_seen - last_max_pn_seen) > QUIC_PN_MAP_LIMIT
+ *
+ * Gap Search Range:
+ * - From (base_pn - 1) to max_pn_seen
+ */
+struct quic_pnspace {
+ /* ECN counters indexed by dir and ECN codepoint (ECT1, ECT0, CE) */
+ u64 ecn_count[QUIC_ECN_DIR_MAX][QUIC_ECN_MAX];
+ unsigned long *pn_map; /* Received PN bitmap for ACK generation */
+ u16 pn_map_len; /* Length of the PN bit map (in bits) */
+ u8 need_sack; /* Flag indicating a SACK frame should be sent */
+ u8 sack_path; /* Path used for sending the SACK frame */
+ u8 sack_pending; /* Delayed ACK pending */
+
+ s64 last_max_pn_seen; /* Largest PN seen before pn_map advance */
+ u64 last_max_pn_time; /* Timestamp last_max_pn_seen was received */
+ s64 min_pn_seen; /* Smallest PN received */
+ s64 max_pn_seen; /* Largest PN received */
+ u64 max_pn_time; /* Timestamp max_pn_seen was received */
+ s64 base_pn; /* PN corresponding to the start of the pn_map */
+ u64 time; /* Cached now, or latest socket accept timestamp */
+
+ s64 max_pn_acked_seen; /* Largest PN ACKed by peer */
+ u64 max_pn_acked_time; /* Timestamp max_pn_acked_seen was ACKed */
+ u64 last_sent_time; /* Timestamp last ack-eliciting packet sent */
+ u64 loss_time; /* Timestamp the packet can be declared lost */
+ s64 next_pn; /* Next PN to send */
+
+ u32 max_time_limit; /* Time threshold to trigger pn_map advance */
+ u32 inflight; /* Ack-eliciting bytes in flight */
+};
+
+static inline void
+quic_pnspace_set_max_pn_acked_seen(struct quic_pnspace *space,
+ s64 max_pn_acked_seen)
+{
+ if (space->max_pn_acked_seen >= max_pn_acked_seen)
+ return;
+ space->max_pn_acked_seen = max_pn_acked_seen;
+ space->max_pn_acked_time = quic_ktime_get_us();
+}
+
+static inline void quic_pnspace_set_base_pn(struct quic_pnspace *space, s64 pn)
+{
+ space->base_pn = pn;
+ space->max_pn_seen = space->base_pn - 1;
+ space->last_max_pn_seen = space->max_pn_seen;
+ space->min_pn_seen = space->max_pn_seen;
+
+ space->max_pn_time = space->time;
+ space->last_max_pn_time = space->max_pn_time;
+}
+
+static inline bool quic_pnspace_has_gap(const struct quic_pnspace *space)
+{
+ return space->base_pn != space->max_pn_seen + 1;
+}
+
+static inline void quic_pnspace_inc_ecn_acked(struct quic_pnspace *space,
+ u8 ecn)
+{
+ if (!ecn)
+ return;
+ space->ecn_count[QUIC_ECN_ACKED][ecn - 1]++;
+}
+
+static inline void quic_pnspace_reset_ecn_acked(struct quic_pnspace *space)
+{
+ space->ecn_count[QUIC_ECN_ACKED][QUIC_ECN_ECT0] = 0;
+ space->ecn_count[QUIC_ECN_ACKED][QUIC_ECN_ECT1] = 0;
+ space->ecn_count[QUIC_ECN_ACKED][QUIC_ECN_CE] = 0;
+}
+
+static inline void quic_pnspace_inc_ecn_local(struct quic_pnspace *space,
+ u8 ecn)
+{
+ if (!ecn)
+ return;
+ space->ecn_count[QUIC_ECN_LOCAL][ecn - 1]++;
+}
+
+/* Check if any ECN-marked packets were received. */
+static inline bool quic_pnspace_has_ecn_local(struct quic_pnspace *space)
+{
+ return space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT0] ||
+ space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT1] ||
+ space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_CE];
+}
+
+/* Validate ECN counts received in an ACK. */
+static inline bool quic_pnspace_validate_ecn(struct quic_pnspace *space,
+ u64 *ecn_count)
+{
+ u64 *acked = space->ecn_count[QUIC_ECN_ACKED];
+ u64 *peer = space->ecn_count[QUIC_ECN_PEER];
+ u64 ect0, ect1, ce;
+
+ if (peer[QUIC_ECN_ECT0] > ecn_count[QUIC_ECN_ECT0] ||
+ peer[QUIC_ECN_ECT1] > ecn_count[QUIC_ECN_ECT1] ||
+ peer[QUIC_ECN_CE] > ecn_count[QUIC_ECN_CE])
+ return false;
+
+ /* rfc9000#section-13.4.2.1:
+ *
+ * ECN validation also fails if the sum of the increase in ECT(0) and
+ * ECN-CE counts is less than the number of newly acknowledged packets
+ * that were originally sent with an ECT(0) marking (Same for ECT(1)).
+ */
+ ect0 = ecn_count[QUIC_ECN_ECT0] - peer[QUIC_ECN_ECT0];
+ ect1 = ecn_count[QUIC_ECN_ECT1] - peer[QUIC_ECN_ECT1];
+ ce = ecn_count[QUIC_ECN_CE] - peer[QUIC_ECN_CE];
+
+ return ect0 + ce >= acked[QUIC_ECN_ECT0] &&
+ ect1 + ce >= acked[QUIC_ECN_ECT1];
+}
+
+/* Updates the stored ECN counters based on values received in the peer's ACK
+ * frame. Each counter is updated only if the new value is higher.
+ *
+ * Returns: true if CE count was increased (congestion indicated), false
+ * otherwise.
+ */
+static inline bool quic_pnspace_set_ecn_peer(struct quic_pnspace *space,
+ u64 *ecn_count)
+{
+ u64 *count = space->ecn_count[QUIC_ECN_PEER];
+
+ if (count[QUIC_ECN_ECT0] < ecn_count[QUIC_ECN_ECT0])
+ count[QUIC_ECN_ECT0] = ecn_count[QUIC_ECN_ECT0];
+ if (count[QUIC_ECN_ECT1] < ecn_count[QUIC_ECN_ECT1])
+ count[QUIC_ECN_ECT1] = ecn_count[QUIC_ECN_ECT1];
+ if (count[QUIC_ECN_CE] < ecn_count[QUIC_ECN_CE]) {
+ count[QUIC_ECN_CE] = ecn_count[QUIC_ECN_CE];
+ return true;
+ }
+ return false;
+}
+
+u16 quic_pnspace_num_gabs(struct quic_pnspace *space,
+ struct quic_gap_ack_block *gabs);
+int quic_pnspace_check(struct quic_pnspace *space, s64 pn);
+int quic_pnspace_mark(struct quic_pnspace *space, s64 pn, gfp_t gfp);
+
+void quic_pnspace_free(struct quic_pnspace *space);
+int quic_pnspace_init(struct quic_pnspace *space);
diff --git a/net/quic/protocol.c b/net/quic/protocol.c
new file mode 100644
index 0000000000000..fceb4d5723a3b
--- /dev/null
+++ b/net/quic/protocol.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/inet_common.h>
+#include <linux/proc_fs.h>
+#include <net/protocol.h>
+#include <net/rps.h>
+#include <net/tls.h>
+
+#include "socket.h"
+
+static unsigned int quic_net_id __read_mostly;
+
+struct percpu_counter quic_sockets_allocated;
+struct workqueue_struct *quic_wq;
+
+DEFINE_STATIC_KEY_FALSE(quic_alpn_demux_key);
+
+long sysctl_quic_mem[3];
+int sysctl_quic_rmem[3];
+int sysctl_quic_wmem[3];
+
+static int quic_inet_connect(struct socket *sock, struct sockaddr_unsized *addr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (addr_len < (int)sizeof(addr->sa_family))
+ return -EINVAL;
+
+ return sk->sk_prot->connect(sk, addr, addr_len);
+}
+
+static int quic_inet_listen(struct socket *sock, int backlog)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_inet_getname(struct socket *sock, struct sockaddr *uaddr,
+ int peer)
+{
+ return quic_get_sk_addr(sock, uaddr, peer);
+}
+
+static __poll_t quic_inet_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ return 0;
+}
+
+struct quic_net *quic_net(struct net *net)
+{
+ return net_generic(net, quic_net_id);
+}
+
+#if IS_ENABLED(CONFIG_PROC_FS)
+static const struct snmp_mib quic_snmp_list[] = {
+ SNMP_MIB_ITEM("QuicConnCurrentEstabs", QUIC_MIB_CONN_CURRENTESTABS),
+ SNMP_MIB_ITEM("QuicConnPassiveEstabs", QUIC_MIB_CONN_PASSIVEESTABS),
+ SNMP_MIB_ITEM("QuicConnActiveEstabs", QUIC_MIB_CONN_ACTIVEESTABS),
+ SNMP_MIB_ITEM("QuicPktRcvFastpaths", QUIC_MIB_PKT_RCVFASTPATHS),
+ SNMP_MIB_ITEM("QuicPktDecFastpaths", QUIC_MIB_PKT_DECFASTPATHS),
+ SNMP_MIB_ITEM("QuicPktEncFastpaths", QUIC_MIB_PKT_ENCFASTPATHS),
+ SNMP_MIB_ITEM("QuicPktRcvBacklogs", QUIC_MIB_PKT_RCVBACKLOGS),
+ SNMP_MIB_ITEM("QuicPktDecBacklogs", QUIC_MIB_PKT_DECBACKLOGS),
+ SNMP_MIB_ITEM("QuicPktEncBacklogs", QUIC_MIB_PKT_ENCBACKLOGS),
+ SNMP_MIB_ITEM("QuicPktInvHdrDrop", QUIC_MIB_PKT_INVHDRDROP),
+ SNMP_MIB_ITEM("QuicPktInvNumDrop", QUIC_MIB_PKT_INVNUMDROP),
+ SNMP_MIB_ITEM("QuicPktInvFrmDrop", QUIC_MIB_PKT_INVFRMDROP),
+ SNMP_MIB_ITEM("QuicPktRcvDrop", QUIC_MIB_PKT_RCVDROP),
+ SNMP_MIB_ITEM("QuicPktDecDrop", QUIC_MIB_PKT_DECDROP),
+ SNMP_MIB_ITEM("QuicPktEncDrop", QUIC_MIB_PKT_ENCDROP),
+ SNMP_MIB_ITEM("QuicFrmRcvBufDrop", QUIC_MIB_FRM_RCVBUFDROP),
+ SNMP_MIB_ITEM("QuicFrmRetrans", QUIC_MIB_FRM_RETRANS),
+ SNMP_MIB_ITEM("QuicFrmOutCloses", QUIC_MIB_FRM_OUTCLOSES),
+ SNMP_MIB_ITEM("QuicFrmInCloses", QUIC_MIB_FRM_INCLOSES),
+};
+
+static int quic_snmp_seq_show(struct seq_file *seq, void *v)
+{
+ unsigned long buff[ARRAY_SIZE(quic_snmp_list)];
+ const int cnt = ARRAY_SIZE(quic_snmp_list);
+ struct net *net = seq->private;
+ u32 idx;
+
+ memset(buff, 0, sizeof(buff));
+
+ snmp_get_cpu_field_batch_cnt(buff, quic_snmp_list, cnt,
+ quic_net(net)->stat);
+ for (idx = 0; idx < cnt; idx++)
+ seq_printf(seq, "%-32s\t%lu\n", quic_snmp_list[idx].name,
+ buff[idx]);
+
+ return 0;
+}
+
+static int quic_net_proc_init(struct net *net)
+{
+ quic_net(net)->proc_net = proc_net_mkdir(net, "quic", net->proc_net);
+ if (!quic_net(net)->proc_net)
+ return -ENOMEM;
+
+ if (!proc_create_net_single("snmp", 0444, quic_net(net)->proc_net,
+ quic_snmp_seq_show, NULL))
+ goto free;
+ return 0;
+free:
+ remove_proc_subtree("quic", net->proc_net);
+ quic_net(net)->proc_net = NULL;
+ return -ENOMEM;
+}
+
+static void quic_net_proc_exit(struct net *net)
+{
+ remove_proc_subtree("quic", net->proc_net);
+ quic_net(net)->proc_net = NULL;
+}
+#endif
+
+static const struct proto_ops quic_proto_ops = {
+ .family = PF_INET,
+ .owner = THIS_MODULE,
+ .release = inet_release,
+ .bind = inet_bind,
+ .connect = quic_inet_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = inet_accept,
+ .getname = quic_inet_getname,
+ .poll = quic_inet_poll,
+ .ioctl = inet_ioctl,
+ .gettstamp = sock_gettstamp,
+ .listen = quic_inet_listen,
+ .shutdown = inet_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = inet_sendmsg,
+ .recvmsg = inet_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static struct inet_protosw quic_stream_protosw = {
+ .type = SOCK_STREAM,
+ .protocol = IPPROTO_QUIC,
+ .prot = &quic_prot,
+ .ops = &quic_proto_ops,
+};
+
+static struct inet_protosw quic_dgram_protosw = {
+ .type = SOCK_DGRAM,
+ .protocol = IPPROTO_QUIC,
+ .prot = &quic_prot,
+ .ops = &quic_proto_ops,
+};
+
+static const struct proto_ops quicv6_proto_ops = {
+ .family = PF_INET6,
+ .owner = THIS_MODULE,
+ .release = inet6_release,
+ .bind = inet6_bind,
+ .connect = quic_inet_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = inet_accept,
+ .getname = quic_inet_getname,
+ .poll = quic_inet_poll,
+ .ioctl = inet6_ioctl,
+ .gettstamp = sock_gettstamp,
+ .listen = quic_inet_listen,
+ .shutdown = inet_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = inet_sendmsg,
+ .recvmsg = inet_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static struct inet_protosw quicv6_stream_protosw = {
+ .type = SOCK_STREAM,
+ .protocol = IPPROTO_QUIC,
+ .prot = &quicv6_prot,
+ .ops = &quicv6_proto_ops,
+};
+
+static struct inet_protosw quicv6_dgram_protosw = {
+ .type = SOCK_DGRAM,
+ .protocol = IPPROTO_QUIC,
+ .prot = &quicv6_prot,
+ .ops = &quicv6_proto_ops,
+};
+
+static int quic_protosw_init(void)
+{
+ int err;
+
+ err = proto_register(&quic_prot, 1);
+ if (err)
+ return err;
+
+ err = proto_register(&quicv6_prot, 1);
+ if (err) {
+ proto_unregister(&quic_prot);
+ return err;
+ }
+
+ inet_register_protosw(&quic_stream_protosw);
+ inet_register_protosw(&quic_dgram_protosw);
+ inet6_register_protosw(&quicv6_stream_protosw);
+ inet6_register_protosw(&quicv6_dgram_protosw);
+
+ return 0;
+}
+
+static void quic_protosw_exit(void)
+{
+ inet_unregister_protosw(&quic_dgram_protosw);
+ inet_unregister_protosw(&quic_stream_protosw);
+ proto_unregister(&quic_prot);
+
+ inet6_unregister_protosw(&quicv6_dgram_protosw);
+ inet6_unregister_protosw(&quicv6_stream_protosw);
+ proto_unregister(&quicv6_prot);
+}
+
+static int __net_init quic_net_init(struct net *net)
+{
+ struct quic_net *qn = quic_net(net);
+ int err = 0;
+
+ qn->stat = alloc_percpu(struct quic_mib);
+ if (!qn->stat)
+ return -ENOMEM;
+
+#if IS_ENABLED(CONFIG_PROC_FS)
+ err = quic_net_proc_init(net);
+ if (err) {
+ free_percpu(qn->stat);
+ qn->stat = NULL;
+ }
+#endif
+ return err;
+}
+
+static void __net_exit quic_net_exit(struct net *net)
+{
+ struct quic_net *qn = quic_net(net);
+
+#if IS_ENABLED(CONFIG_PROC_FS)
+ quic_net_proc_exit(net);
+#endif
+ free_percpu(qn->stat);
+ qn->stat = NULL;
+}
+
+static struct pernet_operations quic_net_ops = {
+ .init = quic_net_init,
+ .exit = quic_net_exit,
+ .id = &quic_net_id,
+ .size = sizeof(struct quic_net),
+};
+
+#if IS_ENABLED(CONFIG_SYSCTL)
+static struct ctl_table_header *quic_sysctl_header;
+
+static struct ctl_table quic_table[] = {
+ {
+ .procname = "quic_mem",
+ .data = &sysctl_quic_mem,
+ .maxlen = sizeof(sysctl_quic_mem),
+ .mode = 0644,
+ .proc_handler = proc_doulongvec_minmax
+ },
+ {
+ .procname = "quic_rmem",
+ .data = &sysctl_quic_rmem,
+ .maxlen = sizeof(sysctl_quic_rmem),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ONE,
+ },
+ {
+ .procname = "quic_wmem",
+ .data = &sysctl_quic_wmem,
+ .maxlen = sizeof(sysctl_quic_wmem),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ONE,
+ },
+};
+
+static void quic_sysctl_register(void)
+{
+ quic_sysctl_header = register_net_sysctl(&init_net, "net/quic",
+ quic_table);
+}
+
+static void quic_sysctl_unregister(void)
+{
+ unregister_net_sysctl_table(quic_sysctl_header);
+}
+#endif
+
+static __init int quic_init(void)
+{
+ int max_share, err = -ENOMEM;
+ unsigned long limit;
+
+ BUILD_BUG_ON(sizeof(struct quic_skb_cb) >
+ sizeof_field(struct sk_buff, cb));
+
+ /* Set QUIC memory limits based on available system memory, similar to
+ * sctp_init().
+ */
+ limit = nr_free_buffer_pages() / 8;
+ limit = max(limit, 128UL);
+ sysctl_quic_mem[0] = (long)limit / 4 * 3;
+ sysctl_quic_mem[1] = (long)limit;
+ sysctl_quic_mem[2] = sysctl_quic_mem[0] * 2;
+
+ limit = (sysctl_quic_mem[1]) << (PAGE_SHIFT - 7);
+ max_share = min(4UL * 1024 * 1024, limit);
+
+ sysctl_quic_rmem[0] = PAGE_SIZE;
+ sysctl_quic_rmem[1] = 1024 * 1024;
+ sysctl_quic_rmem[2] = max(sysctl_quic_rmem[1], max_share);
+
+ sysctl_quic_wmem[0] = PAGE_SIZE;
+ sysctl_quic_wmem[1] = 16 * 1024;
+ sysctl_quic_wmem[2] = max(64 * 1024, max_share);
+
+ err = percpu_counter_init(&quic_sockets_allocated, 0, GFP_KERNEL);
+ if (err)
+ goto err_percpu_counter;
+
+ err = quic_hash_tables_init();
+ if (err)
+ goto err_hash;
+
+ /* Allocate an unbound workqueue for UDP socket destruction and backlog
+ * packet processing.
+ */
+ quic_wq = alloc_workqueue("quic_workqueue", WQ_UNBOUND, 0);
+ if (!quic_wq) {
+ err = -ENOMEM;
+ goto err_wq;
+ }
+
+ err = register_pernet_subsys(&quic_net_ops);
+ if (err)
+ goto err_def_ops;
+
+ err = quic_protosw_init();
+ if (err)
+ goto err_protosw;
+
+#if IS_ENABLED(CONFIG_SYSCTL)
+ quic_sysctl_register();
+#endif
+ pr_info("quic: init\n");
+ return 0;
+
+err_protosw:
+ unregister_pernet_subsys(&quic_net_ops);
+err_def_ops:
+ destroy_workqueue(quic_wq);
+err_wq:
+ quic_hash_tables_destroy();
+err_hash:
+ percpu_counter_destroy(&quic_sockets_allocated);
+err_percpu_counter:
+ return err;
+}
+
+static __exit void quic_exit(void)
+{
+#if IS_ENABLED(CONFIG_SYSCTL)
+ quic_sysctl_unregister();
+#endif
+ quic_protosw_exit();
+ unregister_pernet_subsys(&quic_net_ops);
+ flush_workqueue(quic_wq);
+ destroy_workqueue(quic_wq);
+ quic_hash_tables_destroy();
+ percpu_counter_destroy(&quic_sockets_allocated);
+ rcu_barrier();
+ pr_info("quic: exit\n");
+}
+
+module_init(quic_init);
+module_exit(quic_exit);
+
+MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); /* IPPROTO_QUIC == 261 */
+MODULE_ALIAS_NET_PF_PROTO(PF_INET6, 261);
+MODULE_AUTHOR("Xin Long <lucien.xin@gmail.com>");
+MODULE_DESCRIPTION("Support for the QUIC protocol (RFC9000)");
+MODULE_LICENSE("GPL");
diff --git a/net/quic/protocol.h b/net/quic/protocol.h
new file mode 100644
index 0000000000000..fbd0fe39eccc6
--- /dev/null
+++ b/net/quic/protocol.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+extern struct percpu_counter quic_sockets_allocated;
+
+DECLARE_STATIC_KEY_FALSE(quic_alpn_demux_key);
+
+extern long sysctl_quic_mem[3];
+extern int sysctl_quic_rmem[3];
+extern int sysctl_quic_wmem[3];
+
+enum {
+ QUIC_MIB_NUM = 0,
+ QUIC_MIB_CONN_CURRENTESTABS, /* Current established connections */
+ QUIC_MIB_CONN_PASSIVEESTABS, /* Passively established connections */
+ QUIC_MIB_CONN_ACTIVEESTABS, /* Actively established connections */
+ QUIC_MIB_PKT_RCVFASTPATHS, /* Packets received on fast path */
+ QUIC_MIB_PKT_DECFASTPATHS, /* Packets decrypted on fast path */
+ QUIC_MIB_PKT_ENCFASTPATHS, /* Packets encrypted on fast path */
+ QUIC_MIB_PKT_RCVBACKLOGS, /* Packets processed via backlog */
+ QUIC_MIB_PKT_DECBACKLOGS, /* Packets decrypted in backlog */
+ QUIC_MIB_PKT_ENCBACKLOGS, /* Packets encrypted in backlog */
+ QUIC_MIB_PKT_INVHDRDROP, /* Dropped: invalid packet header */
+ QUIC_MIB_PKT_INVNUMDROP, /* Dropped: invalid packet number */
+ QUIC_MIB_PKT_INVFRMDROP, /* Dropped: invalid frame */
+ QUIC_MIB_PKT_RCVDROP, /* Dropped on receive (general) */
+ QUIC_MIB_PKT_DECDROP, /* Dropped: decryption failure */
+ QUIC_MIB_PKT_ENCDROP, /* Dropped: encryption failure */
+ QUIC_MIB_FRM_RCVBUFDROP, /* Frames dropped: recv buf limit */
+ QUIC_MIB_FRM_RETRANS, /* Frames retransmitted */
+ QUIC_MIB_FRM_OUTCLOSES, /* CONNECTION_CLOSE frames sent */
+ QUIC_MIB_FRM_INCLOSES, /* CONNECTION_CLOSE frames rcvd */
+ QUIC_MIB_MAX
+};
+
+struct quic_mib {
+ unsigned long mibs[QUIC_MIB_MAX]; /* Counters indexed by QUIC_MIB_* */
+};
+
+struct quic_net {
+ DEFINE_SNMP_STAT(struct quic_mib, stat); /* Per-net QUIC MIB stats */
+#if IS_ENABLED(CONFIG_PROC_FS)
+ struct proc_dir_entry *proc_net; /* procfs entry for QUIC stats */
+#endif
+};
+
+struct quic_net *quic_net(struct net *net);
+
+#define QUIC_INC_STATS(net, field) SNMP_INC_STATS(quic_net(net)->stat, field)
+#define QUIC_DEC_STATS(net, field) SNMP_DEC_STATS(quic_net(net)->stat, field)
diff --git a/net/quic/socket.c b/net/quic/socket.c
new file mode 100644
index 0000000000000..0fcb123d31bba
--- /dev/null
+++ b/net/quic/socket.c
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/inet_common.h>
+#include <net/tls.h>
+
+#include "socket.h"
+
+static DEFINE_PER_CPU(int, quic_memory_per_cpu_fw_alloc);
+static unsigned long quic_memory_pressure;
+static atomic_long_t quic_memory_allocated;
+
+static void quic_enter_memory_pressure(struct sock *sk)
+{
+ WRITE_ONCE(quic_memory_pressure, 1);
+}
+
+/* Check if a matching request sock already exists. Match is based on
+ * source/destination addresses and DCID.
+ */
+struct quic_request_sock *quic_request_sock_lookup(struct sock *sk)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct quic_request_sock *req;
+
+ list_for_each_entry(req, quic_reqs(sk), list) {
+ if (!memcmp(&req->saddr, &packet->saddr, sizeof(req->saddr)) &&
+ !memcmp(&req->daddr, &packet->daddr, sizeof(req->daddr)) &&
+ !quic_conn_id_cmp(&req->dcid, &packet->dcid))
+ return req;
+ }
+ return NULL;
+}
+
+/* Create and enqueue a QUIC request sock for a new incoming connection. */
+struct quic_request_sock *quic_request_sock_create(struct sock *sk,
+ struct quic_conn_id *odcid,
+ u8 retry, gfp_t gfp)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct quic_request_sock *req;
+
+ if (sk_acceptq_is_full(sk)) /* Refuse if accept queue full. */
+ return ERR_PTR(-ENOBUFS);
+
+ req = kmalloc_obj(*req, gfp);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ req->version = packet->version;
+ req->daddr = packet->daddr;
+ req->saddr = packet->saddr;
+ req->scid = packet->scid;
+ req->dcid = packet->dcid;
+ req->orig_dcid = *odcid;
+ req->retry = retry;
+
+ skb_queue_head_init(&req->backlog_list);
+ req->blen = 0;
+
+ /* Enqueue request into listen socket’s pending list for accept(). */
+ list_add_tail(&req->list, quic_reqs(sk));
+ sk_acceptq_added(sk);
+ return req;
+}
+
+int quic_request_sock_backlog_tail(struct sock *sk,
+ struct quic_request_sock *req,
+ struct sk_buff *skb)
+{
+ u32 limit = sk->sk_rcvbuf / sk->sk_max_ack_backlog;
+ int len = skb->truesize;
+
+ limit = max_t(u32, limit, QUIC_MIN_UDP_PAYLOAD * 4);
+ if (req->blen + len > limit || !__sk_rmem_schedule(sk, len, false)) {
+ QUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+
+ QUIC_SKB_CB(skb)->backlog = 1;
+ skb_set_owner_r(skb, sk);
+ __skb_queue_tail(&req->backlog_list, skb);
+ req->blen += len;
+
+ sk->sk_data_ready(sk);
+ return 0;
+}
+
+/* Check if a matching accept socket exists. This is needed because an accept
+ * socket might have been created after this packet was enqueued in the listen
+ * socket's backlog.
+ */
+bool quic_accept_sock_exists(struct sock *sk, struct sk_buff *skb)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ bool exist = false;
+
+ /* Skip if packet is newer than the last accept socket creation time.
+ * No matching socket could exist in this case.
+ */
+ if (QUIC_SKB_CB(skb)->time >
+ quic_pnspace(sk, QUIC_CRYPTO_INITIAL)->time)
+ return exist;
+
+ /* Look up accepted socket matching packet addresses and DCID. */
+ local_bh_disable();
+ sk = quic_sock_lookup(skb, &packet->saddr, &packet->daddr,
+ quic_path_usock(quic_paths(sk), 0),
+ &packet->dcid);
+ if (!sk)
+ goto out;
+
+ /* Found a matching accept socket. Process packet with this socket. */
+ skb_orphan(skb);
+ bh_lock_sock_nested(sk);
+ if (sock_owned_by_user(sk)) {
+ /* Socket is busy (owned by user context): queue to backlog. */
+ if (sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf))) {
+ QUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);
+ kfree_skb(skb);
+ }
+ } else {
+ /* Socket not busy: process immediately. */
+ sk->sk_backlog_rcv(sk, skb); /* quic_packet_process(). */
+ }
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ exist = true;
+out:
+ local_bh_enable();
+ return exist;
+}
+
+/* Lookup a connected QUIC socket based on address and dest connection ID.
+ *
+ * This function searches the established (non-listening) QUIC socket table for
+ * a socket that matches the source and dest addresses and, optionally, the
+ * dest connection ID (DCID). The value returned by quic_path_orig_dcid() might
+ * be the original dest connection ID from the ClientHello or the Source
+ * Connection ID from a Retry packet before.
+ *
+ * The DCID is provided from a handshake packet when searching by source
+ * connection ID fails, such as when the peer has not yet received server's
+ * response and updated the DCID.
+ *
+ * Return: A pointer to the matching connected socket, or NULL if no match is
+ * found.
+ */
+struct sock *quic_sock_lookup(struct sk_buff *skb, union quic_addr *sa,
+ union quic_addr *da, struct sock *usk,
+ struct quic_conn_id *dcid)
+{
+ union quic_addr *path_sa, *path_da;
+ struct net *net = sock_net(usk);
+ struct quic_path_group *paths;
+ struct hlist_nulls_node *node;
+ struct quic_shash_head *head;
+ struct sock *sk = NULL, *tmp;
+ struct quic_conn_id *odcid;
+ unsigned int hash, seq;
+ bool match;
+
+ hash = quic_sock_hash(net, sa, da);
+ head = quic_sock_head(hash);
+
+ rcu_read_lock();
+begin:
+ sk_nulls_for_each_rcu(tmp, node, &head->head) {
+ if (net != sock_net(tmp))
+ continue;
+ paths = quic_paths(tmp);
+ odcid = quic_path_orig_dcid(paths);
+
+ /* Protect path[0] reads with seqcount retry to detect torn
+ * reads during concurrent quic_path_swap(). The seqcount
+ * ensures we either see a consistent old or new path, never
+ * a mix of both.
+ */
+ do {
+ seq = read_seqcount_begin(&paths->path_seq);
+ path_sa = quic_path_saddr(paths, 0);
+ path_da = quic_path_daddr(paths, 0);
+ match = (quic_cmp_sk_addr(tmp, path_sa, sa) &&
+ quic_cmp_sk_addr(tmp, path_da, da) &&
+ quic_path_usock(paths, 0) == usk &&
+ (!dcid || !quic_conn_id_cmp(odcid, dcid)));
+ } while (read_seqcount_retry(&paths->path_seq, seq));
+
+ if (match) {
+ sk = tmp;
+ break;
+ }
+ }
+ /* If the final nulls value differs from the expected one, restart the
+ * lookup as the node may have been rehashed (e.g., due to connection
+ * migration).
+ */
+ if (!sk && get_nulls_value(node) != hash)
+ goto begin;
+
+ if (sk && unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
+ sk = NULL;
+ rcu_read_unlock();
+ return sk;
+}
+
+/* Find the listening QUIC socket for an incoming packet.
+ *
+ * This function searches the QUIC socket table for a listening socket that
+ * matches the dest address and port, and the ALPN(s) if presented in the
+ * ClientHello. If multiple listening sockets are bound to the same address,
+ * port, and ALPN(s) (e.g., via SO_REUSEPORT), this function selects a socket
+ * from the reuseport group.
+ *
+ * Return: A pointer to the matching listening socket, or NULL if no match is
+ * found.
+ */
+struct sock *quic_listen_sock_lookup(struct sk_buff *skb, union quic_addr *sa,
+ union quic_addr *da, struct sock *usk,
+ struct quic_data *alpns)
+{
+ struct net *net = sock_net(usk);
+ struct hlist_nulls_node *node;
+ struct sock *sk = NULL, *tmp;
+ struct quic_shash_head *head;
+ struct quic_data alpn;
+ union quic_addr *a;
+ u32 hash, len;
+ u64 length;
+ u8 *p;
+
+ hash = quic_listen_sock_hash(net, ntohs(sa->v4.sin_port));
+ head = quic_listen_sock_head(hash);
+
+ rcu_read_lock();
+ /* Iterate sockets, checking ALPN requirements. Address specificity
+ * always takes precedence over ALPN preference order.
+ */
+ sk_nulls_for_each_rcu(tmp, node, &head->head) {
+ bool alpn_match = false;
+
+ a = quic_path_saddr(quic_paths(tmp), 0);
+ if (net != sock_net(tmp) || !quic_cmp_sk_addr(tmp, a, sa) ||
+ quic_path_usock(quic_paths(tmp), 0) != usk)
+ continue;
+
+ if (!alpns->len) {
+ /* No ALPN extension or empty ALPN list.
+ * If alpns->data is NULL, match any socket.
+ * If alpns->data is set (empty ALPN), only match
+ * sockets with no ALPN configured.
+ */
+ alpn_match = (!alpns->data || !quic_alpn(tmp)->len);
+ } else {
+ /* Check if any client ALPN matches this socket. */
+ for (p = alpns->data, len = alpns->len; len;
+ len -= length, p += length) {
+ quic_get_int(&p, &len, &length, 1);
+ quic_data(&alpn, p, length);
+ if (quic_data_has(quic_alpn(tmp), &alpn)) {
+ alpn_match = true;
+ break;
+ }
+ }
+ }
+
+ if (alpn_match) {
+ if (!quic_is_any_addr(a)) {
+ /* Specific address - best match. */
+ sk = tmp;
+ goto out;
+ }
+ /* ANY address - keep as candidate. */
+ if (!sk || a->sa.sa_family == sa->sa.sa_family)
+ sk = tmp;
+ }
+ }
+ /* No need to check get_nulls_value(node) != hash for !sk, as
+ * hashtable size is fixed and a listen sk can not rehashed.
+ */
+out:
+ if (sk && sk->sk_reuseport)
+ sk = reuseport_select_sock(sk, quic_addr_hash(net, da), skb, 1);
+
+ if (sk && unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
+ sk = NULL;
+ rcu_read_unlock();
+ return sk;
+}
+
+/* Switch packet to a different listening socket based on ALPN matching.
+ *
+ * When ALPN demultiplexing is enabled, this function attempts to find a
+ * listening socket that matches the parsed ALPN. If a different socket is
+ * found, the packet is switched to that socket for processing.
+ *
+ * Return: true if switched to a different socket, false otherwise.
+ */
+bool quic_listen_sock_switch(struct sk_buff *skb, struct quic_data *alpns)
+{
+ struct sock *nsk, *sk = skb->sk;
+ struct quic_packet *packet;
+
+ if (!alpns->data)
+ return false;
+
+ local_bh_disable();
+ packet = quic_packet(sk);
+ nsk = quic_listen_sock_lookup(skb, &packet->saddr, &packet->daddr,
+ quic_path_usock(quic_paths(sk), 0),
+ alpns);
+ if (!nsk)
+ goto out;
+ if (nsk == sk) {
+ sock_put(nsk);
+ goto out;
+ }
+ local_bh_enable();
+ release_sock(sk);
+
+ skb_orphan(skb);
+
+ lock_sock(nsk);
+ nsk->sk_backlog_rcv(nsk, skb); /* quic_packet_process(). */
+ release_sock(nsk);
+ sock_put(nsk);
+
+ lock_sock(sk);
+ return true;
+out:
+ local_bh_enable();
+ return false;
+}
+
+static void quic_write_space(struct sock *sk)
+{
+ __poll_t mask = EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
+ struct socket_wq *wq;
+
+ /* Do not check sock_writeable(). Also wakes stream-open waiters
+ * blocked on stream limits, where sock_writeable() may be false.
+ */
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (skwq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, mask);
+ sk_wake_async_rcu(sk, SOCK_WAKE_SPACE, POLL_OUT);
+ rcu_read_unlock();
+}
+
+static void quic_sock_destruct(struct sock *sk)
+{
+ u8 i;
+
+ /* Deferred crypto free for async encryption/decryption. */
+ for (i = 0; i < QUIC_CRYPTO_MAX; i++)
+ quic_crypto_free(quic_crypto(sk, i));
+
+ /* Deferred ALPN free for RCU readers in quic_listen_sock_lookup(). */
+ quic_data_free(quic_alpn(sk));
+
+ quic_sk_destruct(sk);
+}
+
+static int quic_init_sock(struct sock *sk)
+{
+ u8 i;
+
+ sk->sk_destruct = quic_sock_destruct;
+ sk->sk_write_space = quic_write_space;
+ sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
+
+ sk_sockets_allocated_inc(sk);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ INIT_LIST_HEAD(quic_reqs(sk));
+
+ quic_conn_id_set_init(quic_source(sk), true);
+ quic_conn_id_set_init(quic_dest(sk), false);
+ quic_path_init(quic_paths(sk));
+ quic_cong_init(quic_cong(sk));
+
+ quic_timer_init(sk);
+ quic_packet_init(sk);
+
+ if (quic_stream_init(quic_streams(sk)))
+ return -ENOMEM;
+
+ for (i = 0; i < QUIC_PNSPACE_MAX; i++) {
+ if (quic_pnspace_init(quic_pnspace(sk, i)))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void quic_destroy_sock(struct sock *sk)
+{
+ u8 i;
+
+ quic_timer_free(sk);
+ quic_packet_free(sk);
+
+ for (i = 0; i < QUIC_PNSPACE_MAX; i++)
+ quic_pnspace_free(quic_pnspace(sk, i));
+
+ quic_path_unbind(sk, quic_paths(sk), 0);
+ quic_path_unbind(sk, quic_paths(sk), 1);
+
+ quic_conn_id_set_free(quic_source(sk));
+ quic_conn_id_set_free(quic_dest(sk));
+
+ quic_stream_free(quic_streams(sk));
+
+ quic_data_free(quic_ticket(sk));
+ quic_data_free(quic_token(sk));
+
+ sk_sockets_allocated_dec(sk);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+}
+
+static int quic_bind(struct sock *sk, struct sockaddr_unsized *addr,
+ int addr_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_connect(struct sock *sk, struct sockaddr_unsized *addr,
+ int addr_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_hash(struct sock *sk)
+{
+ return 0;
+}
+
+static void quic_unhash(struct sock *sk)
+{
+}
+
+static int quic_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct sock *quic_accept(struct sock *sk, struct proto_accept_arg *arg)
+{
+ arg->err = -EOPNOTSUPP;
+ return NULL;
+}
+
+static void quic_close(struct sock *sk, long timeout)
+{
+ lock_sock(sk);
+
+ quic_set_state(sk, QUIC_SS_CLOSED);
+
+ release_sock(sk);
+
+ sk_common_release(sk);
+}
+
+/**
+ * quic_do_setsockopt - set a QUIC socket option
+ * @sk: socket to configure
+ * @optname: option name (QUIC-level)
+ * @optval: user buffer containing the option value
+ * @optlen: size of the option value
+ *
+ * Sets a QUIC socket option on a given socket.
+ *
+ * Return:
+ * - On success, 0 is returned.
+ * - On error, a negative error value is returned.
+ */
+int quic_do_setsockopt(struct sock *sk, int optname, sockptr_t optval,
+ unsigned int optlen)
+{
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(quic_do_setsockopt);
+
+static int quic_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ if (level != SOL_QUIC)
+ return quic_common_setsockopt(sk, level, optname, optval,
+ optlen);
+
+ return quic_do_setsockopt(sk, optname, optval, optlen);
+}
+
+/**
+ * quic_do_getsockopt - get a QUIC socket option
+ * @sk: socket to query
+ * @optname: option name (QUIC-level)
+ * @optval: user buffer to receive the option value
+ * @optlen: pointer to buffer size; updated with actual size on return
+ *
+ * Gets a QUIC socket option from a given socket.
+ *
+ * Return:
+ * - On success, 0 is returned.
+ * - On error, a negative error value is returned.
+ */
+int quic_do_getsockopt(struct sock *sk, int optname, sockptr_t optval,
+ sockptr_t optlen)
+{
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(quic_do_getsockopt);
+
+static int quic_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ if (level != SOL_QUIC)
+ return quic_common_getsockopt(sk, level, optname, optval,
+ optlen);
+
+ return quic_do_getsockopt(sk, optname, USER_SOCKPTR(optval),
+ USER_SOCKPTR(optlen));
+}
+
+static void quic_release_cb(struct sock *sk)
+{
+ /* Similar to tcp_release_cb(). */
+ unsigned long nflags, flags = smp_load_acquire(&sk->sk_tsq_flags);
+
+ do {
+ if (!(flags & QUIC_DEFERRED_ALL))
+ return;
+ nflags = flags & ~QUIC_DEFERRED_ALL;
+ } while (!try_cmpxchg(&sk->sk_tsq_flags, &flags, nflags));
+
+ if (flags & QUIC_F_MTU_REDUCED_DEFERRED) {
+ quic_packet_rcv_err_pmtu(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_LOSS_DEFERRED) {
+ quic_timer_loss_handler(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_SACK_DEFERRED) {
+ quic_timer_sack_handler(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_PATH_DEFERRED) {
+ quic_timer_path_handler(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_PMTU_DEFERRED) {
+ quic_timer_pmtu_handler(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_PACE_DEFERRED) {
+ quic_timer_pace_handler(sk);
+ __sock_put(sk);
+ }
+}
+
+static int quic_disconnect(struct sock *sk, int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static void quic_shutdown(struct sock *sk, int how)
+{
+ quic_set_state(sk, QUIC_SS_CLOSED);
+}
+
+static int quic_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ return quic_packet_process(sk, skb, GFP_ATOMIC);
+}
+
+struct proto quic_prot = {
+ .name = "QUIC",
+ .owner = THIS_MODULE,
+ .init = quic_init_sock,
+ .destroy = quic_destroy_sock,
+ .shutdown = quic_shutdown,
+ .setsockopt = quic_setsockopt,
+ .getsockopt = quic_getsockopt,
+ .connect = quic_connect,
+ .bind = quic_bind,
+ .close = quic_close,
+ .disconnect = quic_disconnect,
+ .sendmsg = quic_sendmsg,
+ .recvmsg = quic_recvmsg,
+ .accept = quic_accept,
+ .hash = quic_hash,
+ .unhash = quic_unhash,
+ .backlog_rcv = quic_backlog_rcv,
+ .release_cb = quic_release_cb,
+ .no_autobind = true,
+ .obj_size = sizeof(struct quic_sock),
+ .sysctl_mem = sysctl_quic_mem,
+ .sysctl_rmem = sysctl_quic_rmem,
+ .sysctl_wmem = sysctl_quic_wmem,
+ .memory_pressure = &quic_memory_pressure,
+ .enter_memory_pressure = quic_enter_memory_pressure,
+ .memory_allocated = &quic_memory_allocated,
+ .per_cpu_fw_alloc = &quic_memory_per_cpu_fw_alloc,
+ .sockets_allocated = &quic_sockets_allocated,
+};
+
+struct proto quicv6_prot = {
+ .name = "QUICv6",
+ .owner = THIS_MODULE,
+ .init = quic_init_sock,
+ .destroy = quic_destroy_sock,
+ .shutdown = quic_shutdown,
+ .setsockopt = quic_setsockopt,
+ .getsockopt = quic_getsockopt,
+ .connect = quic_connect,
+ .bind = quic_bind,
+ .close = quic_close,
+ .disconnect = quic_disconnect,
+ .sendmsg = quic_sendmsg,
+ .recvmsg = quic_recvmsg,
+ .accept = quic_accept,
+ .hash = quic_hash,
+ .unhash = quic_unhash,
+ .backlog_rcv = quic_backlog_rcv,
+ .release_cb = quic_release_cb,
+ .no_autobind = true,
+ .obj_size = sizeof(struct quic6_sock),
+ .ipv6_pinfo_offset = offsetof(struct quic6_sock, inet6),
+ .sysctl_mem = sysctl_quic_mem,
+ .sysctl_rmem = sysctl_quic_rmem,
+ .sysctl_wmem = sysctl_quic_wmem,
+ .memory_pressure = &quic_memory_pressure,
+ .enter_memory_pressure = quic_enter_memory_pressure,
+ .memory_allocated = &quic_memory_allocated,
+ .per_cpu_fw_alloc = &quic_memory_per_cpu_fw_alloc,
+ .sockets_allocated = &quic_sockets_allocated,
+};
diff --git a/net/quic/socket.h b/net/quic/socket.h
new file mode 100644
index 0000000000000..9fe78c0d4ae57
--- /dev/null
+++ b/net/quic/socket.h
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/udp_tunnel.h>
+#include <linux/quic.h>
+
+#include "common.h"
+#include "pnspace.h"
+#include "family.h"
+#include "stream.h"
+#include "connid.h"
+#include "crypto.h"
+#include "path.h"
+#include "cong.h"
+
+#include "packet.h"
+
+#include "protocol.h"
+#include "timer.h"
+
+extern struct proto quic_prot;
+extern struct proto quicv6_prot;
+
+enum quic_state {
+ QUIC_SS_CLOSED = TCP_CLOSE,
+ QUIC_SS_LISTENING = TCP_LISTEN,
+ QUIC_SS_ESTABLISHING = TCP_SYN_RECV,
+ QUIC_SS_ESTABLISHED = TCP_ESTABLISHED,
+};
+
+enum quic_tsq_enum {
+ QUIC_MTU_REDUCED_DEFERRED,
+ QUIC_LOSS_DEFERRED,
+ QUIC_SACK_DEFERRED,
+ QUIC_PATH_DEFERRED,
+ QUIC_PMTU_DEFERRED,
+ QUIC_PACE_DEFERRED,
+};
+
+enum quic_tsq_flags {
+ QUIC_F_MTU_REDUCED_DEFERRED = BIT(QUIC_MTU_REDUCED_DEFERRED),
+ QUIC_F_LOSS_DEFERRED = BIT(QUIC_LOSS_DEFERRED),
+ QUIC_F_SACK_DEFERRED = BIT(QUIC_SACK_DEFERRED),
+ QUIC_F_PATH_DEFERRED = BIT(QUIC_PATH_DEFERRED),
+ QUIC_F_PMTU_DEFERRED = BIT(QUIC_PMTU_DEFERRED),
+ QUIC_F_PACE_DEFERRED = BIT(QUIC_PACE_DEFERRED),
+};
+
+#define QUIC_DEFERRED_ALL (QUIC_F_MTU_REDUCED_DEFERRED | \
+ QUIC_F_LOSS_DEFERRED | \
+ QUIC_F_SACK_DEFERRED | \
+ QUIC_F_PATH_DEFERRED | \
+ QUIC_F_PMTU_DEFERRED | \
+ QUIC_F_PACE_DEFERRED)
+
+struct quic_request_sock {
+ struct list_head list;
+
+ struct quic_conn_id dcid;
+ struct quic_conn_id scid;
+ union quic_addr daddr;
+ union quic_addr saddr;
+
+ struct quic_conn_id orig_dcid;
+ u32 version;
+ u8 retry;
+
+ struct sk_buff_head backlog_list;
+ u32 blen;
+};
+
+struct quic_sock {
+ struct inet_sock inet;
+ struct list_head reqs;
+ struct work_struct work;
+
+ struct quic_data ticket;
+ struct quic_data token;
+ struct quic_data alpn;
+
+ struct quic_stream_table streams;
+ struct quic_conn_id_set source;
+ struct quic_conn_id_set dest;
+ struct quic_path_group paths;
+ struct quic_cong cong;
+ struct quic_pnspace space[QUIC_PNSPACE_MAX];
+ struct quic_crypto crypto[QUIC_CRYPTO_MAX];
+
+ struct quic_packet packet;
+ struct quic_timer timers[QUIC_TIMER_MAX];
+};
+
+struct quic6_sock {
+ struct quic_sock quic;
+ struct ipv6_pinfo inet6;
+};
+
+static inline struct quic_sock *quic_sk(const struct sock *sk)
+{
+ return (struct quic_sock *)sk;
+}
+
+static inline struct list_head *quic_reqs(const struct sock *sk)
+{
+ return &quic_sk(sk)->reqs;
+}
+
+static inline struct work_struct *quic_work(const struct sock *sk)
+{
+ return &quic_sk(sk)->work;
+}
+
+static inline struct quic_data *quic_token(const struct sock *sk)
+{
+ return &quic_sk(sk)->token;
+}
+
+static inline struct quic_data *quic_ticket(const struct sock *sk)
+{
+ return &quic_sk(sk)->ticket;
+}
+
+static inline struct quic_data *quic_alpn(const struct sock *sk)
+{
+ return &quic_sk(sk)->alpn;
+}
+
+static inline struct quic_stream_table *quic_streams(const struct sock *sk)
+{
+ return &quic_sk(sk)->streams;
+}
+
+static inline struct quic_conn_id_set *quic_source(const struct sock *sk)
+{
+ return &quic_sk(sk)->source;
+}
+
+static inline struct quic_conn_id_set *quic_dest(const struct sock *sk)
+{
+ return &quic_sk(sk)->dest;
+}
+
+static inline struct quic_path_group *quic_paths(const struct sock *sk)
+{
+ return &quic_sk(sk)->paths;
+}
+
+static inline bool quic_is_serv(const struct sock *sk)
+{
+ return !!sk->sk_max_ack_backlog;
+}
+
+static inline struct quic_cong *quic_cong(const struct sock *sk)
+{
+ return &quic_sk(sk)->cong;
+}
+
+static inline struct quic_pnspace *quic_pnspace(const struct sock *sk, u8 level)
+{
+ return &quic_sk(sk)->space[level % QUIC_CRYPTO_EARLY];
+}
+
+static inline struct quic_crypto *quic_crypto(const struct sock *sk, u8 level)
+{
+ return &quic_sk(sk)->crypto[level];
+}
+
+static inline struct quic_packet *quic_packet(const struct sock *sk)
+{
+ return &quic_sk(sk)->packet;
+}
+
+static inline void *quic_timer(const struct sock *sk, u8 type)
+{
+ return (void *)&quic_sk(sk)->timers[type];
+}
+
+static inline bool quic_is_establishing(struct sock *sk)
+{
+ return sk->sk_state == QUIC_SS_ESTABLISHING;
+}
+
+static inline bool quic_is_established(struct sock *sk)
+{
+ return sk->sk_state == QUIC_SS_ESTABLISHED;
+}
+
+static inline bool quic_is_listen(struct sock *sk)
+{
+ return sk->sk_state == QUIC_SS_LISTENING;
+}
+
+static inline bool quic_is_closed(struct sock *sk)
+{
+ return sk->sk_state == QUIC_SS_CLOSED;
+}
+
+static inline void quic_set_state(struct sock *sk, int state)
+{
+ struct net *net = sock_net(sk);
+ int mib;
+
+ if (sk->sk_state == state)
+ return;
+
+ if (state == QUIC_SS_ESTABLISHED) {
+ mib = quic_is_serv(sk) ? QUIC_MIB_CONN_PASSIVEESTABS :
+ QUIC_MIB_CONN_ACTIVEESTABS;
+ QUIC_INC_STATS(net, mib);
+ QUIC_INC_STATS(net, QUIC_MIB_CONN_CURRENTESTABS);
+ } else if (quic_is_established(sk)) {
+ QUIC_DEC_STATS(net, QUIC_MIB_CONN_CURRENTESTABS);
+ }
+
+ inet_sk_set_state(sk, state);
+ sk->sk_state_change(sk);
+}
+
+struct sock *quic_listen_sock_lookup(struct sk_buff *skb, union quic_addr *sa,
+ union quic_addr *da, struct sock *usk,
+ struct quic_data *alpns);
+struct sock *quic_sock_lookup(struct sk_buff *skb, union quic_addr *sa,
+ union quic_addr *da, struct sock *usk,
+ struct quic_conn_id *dcid);
+
+bool quic_listen_sock_switch(struct sk_buff *skb, struct quic_data *alpns);
+bool quic_accept_sock_exists(struct sock *sk, struct sk_buff *skb);
+
+struct quic_request_sock *quic_request_sock_create(struct sock *sk,
+ struct quic_conn_id *odcid,
+ u8 retry, gfp_t gfp);
+int quic_request_sock_backlog_tail(struct sock *sk,
+ struct quic_request_sock *req,
+ struct sk_buff *skb);
+struct quic_request_sock *quic_request_sock_lookup(struct sock *sk);
diff --git a/net/quic/stream.c b/net/quic/stream.c
new file mode 100644
index 0000000000000..6b34b117103a8
--- /dev/null
+++ b/net/quic/stream.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <linux/quic.h>
+
+#include "common.h"
+#include "stream.h"
+
+/* Check if a stream ID is valid for sending or receiving. */
+static bool quic_stream_id_valid(s64 stream_id, bool is_serv, bool send)
+{
+ u8 type = (stream_id & QUIC_STREAM_TYPE_MASK);
+
+ if (send) {
+ if (is_serv)
+ return type != QUIC_STREAM_TYPE_CLIENT_UNI;
+ return type != QUIC_STREAM_TYPE_SERVER_UNI;
+ }
+ if (is_serv)
+ return type != QUIC_STREAM_TYPE_SERVER_UNI;
+ return type != QUIC_STREAM_TYPE_CLIENT_UNI;
+}
+
+/* Check if a stream ID was initiated locally. */
+static bool quic_stream_id_local(s64 stream_id, bool is_serv)
+{
+ return is_serv ^ !(stream_id & QUIC_STREAM_TYPE_SERVER_MASK);
+}
+
+/* Check if a stream ID represents a unidirectional stream. */
+static bool quic_stream_id_uni(s64 stream_id)
+{
+ return stream_id & QUIC_STREAM_TYPE_UNI_MASK;
+}
+
+#define QUIC_STREAM_HT_SIZE 64
+
+static struct hlist_head *quic_stream_head(struct quic_stream_table *streams,
+ s64 stream_id)
+{
+ /* Skip the SERVER initiator bit, which is constant per endpoint. */
+ return &streams->head[(stream_id >> 1) & (QUIC_STREAM_HT_SIZE - 1)];
+}
+
+struct quic_stream *quic_stream_find(struct quic_stream_table *streams,
+ s64 stream_id)
+{
+ struct hlist_head *head = quic_stream_head(streams, stream_id);
+ struct quic_stream *stream;
+
+ hlist_for_each_entry(stream, head, node) {
+ if (stream->id == stream_id)
+ break;
+ }
+ return stream;
+}
+
+static void quic_stream_add(struct quic_stream_table *streams,
+ struct quic_stream *stream)
+{
+ struct hlist_head *head;
+
+ head = quic_stream_head(streams, stream->id);
+ hlist_add_head(&stream->node, head);
+}
+
+static void quic_stream_delete(struct quic_stream *stream)
+{
+ hlist_del_init(&stream->node);
+ kfree(stream);
+}
+
+/* Create and register new streams for sending or receiving. */
+static struct quic_stream *quic_stream_create(struct quic_stream_table *streams,
+ s64 max_stream_id, bool send,
+ bool is_serv, gfp_t gfp)
+{
+ struct quic_stream *pos, *stream = NULL;
+ struct quic_stream_limits *limits;
+ struct hlist_node *tmp;
+ HLIST_HEAD(head);
+ s64 stream_id;
+ u32 count = 0;
+
+ gfp |= __GFP_ACCOUNT;
+ limits = send ? &streams->send : &streams->recv;
+ stream_id = limits->next_bidi_stream_id;
+ if (quic_stream_id_uni(max_stream_id))
+ stream_id = limits->next_uni_stream_id;
+
+ /* rfc9000#section-2.1: A stream ID that is used out of order results in
+ * all streams of that type with lower-numbered stream IDs also being
+ * opened.
+ */
+ while (stream_id <= max_stream_id) {
+ stream = kzalloc_obj(*stream, gfp);
+ if (!stream)
+ goto free;
+
+ stream->id = stream_id;
+ if (quic_stream_id_uni(stream_id)) {
+ if (send) {
+ stream->send.max_bytes =
+ limits->max_stream_data_uni;
+ } else {
+ stream->recv.max_bytes =
+ limits->max_stream_data_uni;
+ stream->recv.window = stream->recv.max_bytes;
+ }
+ hlist_add_head(&stream->node, &head);
+ stream_id += QUIC_STREAM_ID_STEP;
+ continue;
+ }
+
+ if (quic_stream_id_local(stream_id, is_serv)) {
+ stream->send.max_bytes =
+ streams->send.max_stream_data_bidi_remote;
+ stream->recv.max_bytes =
+ streams->recv.max_stream_data_bidi_local;
+ } else {
+ stream->send.max_bytes =
+ streams->send.max_stream_data_bidi_local;
+ stream->recv.max_bytes =
+ streams->recv.max_stream_data_bidi_remote;
+ }
+ stream->recv.window = stream->recv.max_bytes;
+ hlist_add_head(&stream->node, &head);
+ stream_id += QUIC_STREAM_ID_STEP;
+ }
+
+ hlist_for_each_entry_safe(pos, tmp, &head, node) {
+ hlist_del_init(&pos->node);
+ quic_stream_add(streams, pos);
+ count++;
+ }
+
+ /* Streams must be opened sequentially. Update the next stream ID so the
+ * correct starting point is known if an out-of-order open is requested.
+ * Note overflow of next_uni/bidi_stream_id is impossible with s64.
+ */
+ if (quic_stream_id_uni(stream_id)) {
+ limits->next_uni_stream_id = stream_id;
+ limits->streams_uni += count;
+ return stream;
+ }
+
+ limits->next_bidi_stream_id = stream_id;
+ limits->streams_bidi += count;
+ return stream;
+
+free:
+ hlist_for_each_entry_safe(pos, tmp, &head, node) {
+ hlist_del_init(&pos->node);
+ kfree(pos);
+ }
+ return NULL;
+}
+
+/* Check if a send or receive stream ID is already closed. */
+static bool quic_stream_id_closed(struct quic_stream_table *streams,
+ s64 stream_id, bool send)
+{
+ struct quic_stream_limits *limits = send ? &streams->send :
+ &streams->recv;
+
+ if (quic_stream_id_uni(stream_id))
+ return stream_id < limits->next_uni_stream_id;
+ return stream_id < limits->next_bidi_stream_id;
+}
+
+/* Check if a stream ID would exceed local (recv) or peer (send) limits. */
+bool quic_stream_id_exceeds(struct quic_stream_table *streams, s64 stream_id,
+ bool send)
+{
+ u64 nstreams;
+
+ if (!send) {
+ /* recv.max_uni/bidi_stream_id is updated in
+ * quic_stream_max_streams_update() already based on
+ * next_uni/bidi_stream_id, max_streams_uni/bidi, and
+ * streams_uni/bidi, so only recv.max_uni/bidi_stream_id needs
+ * to be checked.
+ */
+ if (quic_stream_id_uni(stream_id))
+ return stream_id > streams->recv.max_uni_stream_id;
+
+ return stream_id > streams->recv.max_bidi_stream_id;
+ }
+
+ if (quic_stream_id_uni(stream_id)) {
+ if (stream_id > streams->send.max_uni_stream_id)
+ return true;
+ stream_id -= streams->send.next_uni_stream_id;
+ nstreams = quic_stream_id_to_streams(stream_id);
+
+ return nstreams + streams->send.streams_uni >
+ streams->send.max_streams_uni;
+ }
+
+ if (stream_id > streams->send.max_bidi_stream_id)
+ return true;
+ stream_id -= streams->send.next_bidi_stream_id;
+ nstreams = quic_stream_id_to_streams(stream_id);
+
+ return nstreams + streams->send.streams_bidi >
+ streams->send.max_streams_bidi;
+}
+
+/* Get or create a send or recv stream by ID. Requires sock lock held. */
+struct quic_stream *quic_stream_get(struct quic_stream_table *streams,
+ s64 stream_id, u32 flags, bool is_serv,
+ bool send, gfp_t gfp)
+{
+ struct quic_stream *stream;
+
+ if (!quic_stream_id_valid(stream_id, is_serv, send))
+ return ERR_PTR(-EINVAL);
+
+ stream = quic_stream_find(streams, stream_id);
+ if (stream)
+ return stream;
+
+ if (!send && quic_stream_id_local(stream_id, is_serv)) {
+ if (quic_stream_id_closed(streams, stream_id, !send))
+ return ERR_PTR(-ENOSTR);
+ return ERR_PTR(-EINVAL);
+ }
+ if (send && !quic_stream_id_local(stream_id, is_serv))
+ return ERR_PTR(-EINVAL);
+
+ if (quic_stream_id_closed(streams, stream_id, send))
+ return ERR_PTR(-ENOSTR);
+
+ if (!(flags & MSG_QUIC_STREAM_NEW))
+ return ERR_PTR(-EINVAL);
+
+ if (quic_stream_id_exceeds(streams, stream_id, send))
+ return ERR_PTR(-EAGAIN);
+
+ stream = quic_stream_create(streams, stream_id, send, is_serv, gfp);
+ if (!stream)
+ return ERR_PTR(-ENOMEM);
+
+ if (send || quic_stream_id_valid(stream_id, is_serv, !send))
+ streams->send.active_stream_id = stream_id;
+
+ return stream;
+}
+
+/* Release or clean up a send or recv stream. This function updates stream
+ * counters and state when a send stream has either successfully sent all data
+ * or has been reset, or when a recv stream has either received all data or has
+ * been reset. Requires sock lock held.
+ */
+void quic_stream_put(struct quic_stream_table *streams,
+ struct quic_stream *stream, bool is_serv, bool send)
+{
+ if (quic_stream_id_uni(stream->id)) {
+ /* For uni streams, decrement uni count and delete stream. */
+ if (send) {
+ streams->send.streams_uni--;
+ quic_stream_delete(stream);
+ return;
+ }
+ streams->recv.streams_uni--;
+ streams->recv.uni_pending = 1;
+ quic_stream_delete(stream);
+ return;
+ }
+
+ /* For bidi streams, proceed only if both send and receive in a final
+ * state.
+ */
+ if (send) {
+ if (stream->recv.state < QUIC_STREAM_RECV_STATE_RECVD)
+ return;
+ } else {
+ if (stream->send.state != QUIC_STREAM_SEND_STATE_RECVD &&
+ stream->send.state != QUIC_STREAM_SEND_STATE_RESET_RECVD)
+ return;
+ }
+ if (quic_stream_id_local(stream->id, is_serv)) {
+ /* Local-initiated stream: decrement send.bidi count. */
+ streams->send.streams_bidi--;
+ } else {
+ /* Remote-initiated stream: decrement recv.bidi count. */
+ streams->recv.streams_bidi--;
+ streams->recv.bidi_pending = 1;
+ }
+ quic_stream_delete(stream);
+}
+
+/* Updates the maximum allowed incoming stream IDs if any streams were recently
+ * closed. Recalculates the max_uni and max_bidi stream ID limits based on the
+ * number of open streams and whether any were marked for deletion.
+ *
+ * Returns true if either max_uni or max_bidi was updated, indicating that a
+ * MAX_STREAMS_UNI or MAX_STREAMS_BIDI frame should be sent to the peer.
+ */
+bool quic_stream_max_streams_update(struct quic_stream_table *streams,
+ s64 *max_uni, s64 *max_bidi)
+{
+ s64 max, rem;
+
+ *max_uni = 0;
+ *max_bidi = 0;
+ if (streams->recv.uni_pending) {
+ rem = streams->recv.max_streams_uni - streams->recv.streams_uni;
+ max = streams->recv.next_uni_stream_id - QUIC_STREAM_ID_STEP +
+ (rem << QUIC_STREAM_TYPE_BITS);
+
+ streams->recv.max_uni_stream_id = max;
+ *max_uni = quic_stream_id_to_streams(max);
+ streams->recv.uni_pending = 0;
+ }
+ if (streams->recv.bidi_pending) {
+ rem = streams->recv.max_streams_bidi -
+ streams->recv.streams_bidi;
+ max = streams->recv.next_bidi_stream_id - QUIC_STREAM_ID_STEP +
+ (rem << QUIC_STREAM_TYPE_BITS);
+
+ streams->recv.max_bidi_stream_id = max;
+ *max_bidi = quic_stream_id_to_streams(max);
+ streams->recv.bidi_pending = 0;
+ }
+
+ return *max_uni || *max_bidi;
+}
+
+int quic_stream_init(struct quic_stream_table *streams)
+{
+ struct hlist_head *head;
+ int i;
+
+ head = kmalloc_objs(*head, QUIC_STREAM_HT_SIZE);
+ if (!head)
+ return -ENOMEM;
+ for (i = 0; i < QUIC_STREAM_HT_SIZE; i++)
+ INIT_HLIST_HEAD(&head[i]);
+ streams->head = head;
+ return 0;
+}
+
+void quic_stream_free(struct quic_stream_table *streams)
+{
+ struct quic_stream *stream;
+ struct hlist_head *head;
+ struct hlist_node *tmp;
+ int i;
+
+ if (!streams->head)
+ return;
+
+ for (i = 0; i < QUIC_STREAM_HT_SIZE; i++) {
+ head = &streams->head[i];
+ hlist_for_each_entry_safe(stream, tmp, head, node)
+ quic_stream_delete(stream);
+ }
+ kfree(streams->head);
+ streams->head = NULL;
+}
+
+/* Populate transport parameters from stream hash table. */
+void quic_stream_get_param(struct quic_stream_table *streams,
+ struct quic_transport_param *p)
+{
+ struct quic_stream_limits *limits = p->remote ? &streams->send :
+ &streams->recv;
+
+ p->max_stream_data_bidi_remote = limits->max_stream_data_bidi_remote;
+ p->max_stream_data_bidi_local = limits->max_stream_data_bidi_local;
+ p->max_stream_data_uni = limits->max_stream_data_uni;
+ p->max_streams_bidi = limits->max_streams_bidi;
+ p->max_streams_uni = limits->max_streams_uni;
+}
+
+/* Configure stream hashtable from transport parameters. */
+void quic_stream_set_param(struct quic_stream_table *streams,
+ struct quic_transport_param *p, bool is_serv)
+{
+ struct quic_stream_limits *limits = p->remote ? &streams->send :
+ &streams->recv;
+ u8 bidi_type, uni_type;
+
+ limits->max_stream_data_bidi_local = p->max_stream_data_bidi_local;
+ limits->max_stream_data_bidi_remote = p->max_stream_data_bidi_remote;
+ limits->max_stream_data_uni = p->max_stream_data_uni;
+ limits->max_streams_bidi = p->max_streams_bidi;
+ limits->max_streams_uni = p->max_streams_uni;
+ limits->active_stream_id = -1;
+
+ if (p->remote ^ is_serv) {
+ bidi_type = QUIC_STREAM_TYPE_CLIENT_BIDI;
+ uni_type = QUIC_STREAM_TYPE_CLIENT_UNI;
+ } else {
+ bidi_type = QUIC_STREAM_TYPE_SERVER_BIDI;
+ uni_type = QUIC_STREAM_TYPE_SERVER_UNI;
+ }
+
+ limits->max_bidi_stream_id =
+ quic_stream_streams_to_id(p->max_streams_bidi, bidi_type);
+ limits->next_bidi_stream_id = bidi_type;
+
+ limits->max_uni_stream_id =
+ quic_stream_streams_to_id(p->max_streams_uni, uni_type);
+ limits->next_uni_stream_id = uni_type;
+}
diff --git a/net/quic/stream.h b/net/quic/stream.h
new file mode 100644
index 0000000000000..d915712f3d5fb
--- /dev/null
+++ b/net/quic/stream.h
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_DEF_STREAMS 100
+#define QUIC_MAX_STREAMS 4096ULL
+
+/*
+ * rfc9000#section-2.1:
+ *
+ * The least significant bit (0x01) of the stream ID identifies the initiator
+ * of the stream. Client-initiated streams have even-numbered stream IDs
+ * (with the bit set to 0), and server-initiated streams have odd-numbered
+ * stream IDs (with the bit set to 1).
+ *
+ * The second least significant bit (0x02) of the stream ID distinguishes
+ * between bidirectional streams (with the bit set to 0) and unidirectional
+ * streams (with the bit set to 1).
+ */
+#define QUIC_STREAM_TYPE_BITS 2
+#define QUIC_STREAM_ID_STEP BIT(QUIC_STREAM_TYPE_BITS)
+
+#define QUIC_STREAM_TYPE_CLIENT_BIDI 0x00
+#define QUIC_STREAM_TYPE_SERVER_BIDI 0x01
+#define QUIC_STREAM_TYPE_CLIENT_UNI 0x02
+#define QUIC_STREAM_TYPE_SERVER_UNI 0x03
+
+struct quic_stream {
+ struct hlist_node node;
+ s64 id; /* Stream ID as defined in RFC 9000 Section 2.1 */
+ struct {
+ /* Sending-side stream level flow control */
+ u64 last_max_bytes; /* Max send offset advertised by peer */
+ u64 max_bytes; /* Max offset allowed to send */
+ u64 bytes; /* Bytes already sent to peer */
+
+ u32 errcode; /* App error code for RESET_STREAM */
+ u32 frags; /* STREAM frames sent but not yet acked */
+ u8 state; /* Send stream state, per rfc9000#section-3.1 */
+
+ u8 data_blocked; /* True if flow control blocks sending */
+ } send;
+ struct {
+ /* Receiving-side stream level flow control */
+ u64 max_bytes; /* Max offset peer can send */
+ u64 window; /* Remaining receive window */
+ u64 bytes; /* Bytes consumed by app */
+
+ u64 highest; /* Highest received offset */
+ u64 offset; /* Data buffered or consumed */
+ u64 finalsz; /* Final stream size if FIN received */
+
+ u32 frags; /* STREAM frames pending reassembly */
+ u8 state; /* Receive stream state, per rfc9000#section-3.2 */
+
+ u8 stop_sent; /* True if STOP_SENDING has been sent */
+ } recv;
+};
+
+struct quic_stream_limits {
+ /* Stream limit parameters defined in rfc9000#section-18.2:
+ *
+ * - initial_max_stream_data_bidi_remote
+ * - initial_max_stream_data_bidi_local
+ * - initial_max_stream_data_uni
+ * - initial_max_streams_bidi
+ * - initial_max_streams_uni
+ */
+ u64 max_stream_data_bidi_remote;
+ u64 max_stream_data_bidi_local;
+ u64 max_stream_data_uni;
+ u64 max_streams_bidi;
+ u64 max_streams_uni;
+
+ s64 next_bidi_stream_id; /* Next bidi stream ID to open or accept */
+ s64 next_uni_stream_id; /* Next uni stream ID to open or accept */
+ s64 max_bidi_stream_id; /* Highest allowed bidi stream ID */
+ s64 max_uni_stream_id; /* Highest allowed uni stream ID */
+ s64 active_stream_id; /* Most recently opened stream ID */
+
+ u8 bidi_blocked; /* STREAMS_BLOCKED_BIDI sent, awaiting ACK */
+ u8 uni_blocked; /* STREAMS_BLOCKED_UNI sent, awaiting ACK */
+ u8 bidi_pending; /* MAX_STREAMS_BIDI needs to be sent */
+ u8 uni_pending; /* MAX_STREAMS_UNI needs to be sent */
+
+ u16 streams_bidi; /* Number of open bidi streams */
+ u16 streams_uni; /* Number of open uni streams */
+};
+
+struct quic_stream_table {
+ struct hlist_head *head; /* Hash table storing all active streams */
+
+ struct quic_stream_limits send; /* Limits advertised by peer */
+ struct quic_stream_limits recv; /* Limits we advertise to peer */
+};
+
+static inline u64 quic_stream_id_to_streams(s64 stream_id)
+{
+ return (u64)(stream_id >> QUIC_STREAM_TYPE_BITS) + 1;
+}
+
+static inline s64 quic_stream_streams_to_id(u64 streams, u8 type)
+{
+ if (!streams)
+ return -1;
+ return (s64)((streams - 1) << QUIC_STREAM_TYPE_BITS) | type;
+}
+
+struct quic_stream *quic_stream_get(struct quic_stream_table *streams,
+ s64 stream_id, u32 flags, bool is_serv,
+ bool send, gfp_t gfp);
+void quic_stream_put(struct quic_stream_table *streams,
+ struct quic_stream *stream, bool is_serv, bool send);
+
+bool quic_stream_max_streams_update(struct quic_stream_table *streams,
+ s64 *max_uni, s64 *max_bidi);
+bool quic_stream_id_exceeds(struct quic_stream_table *streams,
+ s64 stream_id, bool send);
+struct quic_stream *quic_stream_find(struct quic_stream_table *streams,
+ s64 stream_id);
+
+void quic_stream_get_param(struct quic_stream_table *streams,
+ struct quic_transport_param *p);
+void quic_stream_set_param(struct quic_stream_table *streams,
+ struct quic_transport_param *p, bool is_serv);
+void quic_stream_free(struct quic_stream_table *streams);
+int quic_stream_init(struct quic_stream_table *streams);
diff --git a/net/quic/timer.c b/net/quic/timer.c
new file mode 100644
index 0000000000000..0dd6d6580bbd9
--- /dev/null
+++ b/net/quic/timer.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include "socket.h"
+
+static void quic_timer_timeout(struct quic_timer *t, int type, int defer_bit,
+ void (*handler)(struct sock *sk))
+{
+ struct quic_sock *qs = container_of(t, struct quic_sock, timers[type]);
+ struct sock *sk = &qs->inet.sk;
+
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ if (!test_and_set_bit(defer_bit, &sk->sk_tsq_flags))
+ sock_hold(sk);
+ goto out;
+ }
+
+ handler(sk);
+out:
+ bh_unlock_sock(sk);
+ sock_put(sk);
+}
+
+void quic_timer_sack_handler(struct sock *sk)
+{
+}
+
+static void quic_timer_sack_timeout(struct timer_list *t)
+{
+ quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_SACK,
+ QUIC_SACK_DEFERRED, quic_timer_sack_handler);
+}
+
+void quic_timer_loss_handler(struct sock *sk)
+{
+}
+
+static void quic_timer_loss_timeout(struct timer_list *t)
+{
+ quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_LOSS,
+ QUIC_LOSS_DEFERRED, quic_timer_loss_handler);
+}
+
+void quic_timer_path_handler(struct sock *sk)
+{
+}
+
+static void quic_timer_path_timeout(struct timer_list *t)
+{
+ quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PATH,
+ QUIC_PATH_DEFERRED, quic_timer_path_handler);
+}
+
+void quic_timer_pmtu_handler(struct sock *sk)
+{
+}
+
+static void quic_timer_pmtu_timeout(struct timer_list *t)
+{
+ quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PMTU,
+ QUIC_PMTU_DEFERRED, quic_timer_pmtu_handler);
+}
+
+void quic_timer_pace_handler(struct sock *sk)
+{
+}
+
+static enum hrtimer_restart quic_timer_pace_timeout(struct hrtimer *hr)
+{
+ quic_timer_timeout((struct quic_timer *)hr, QUIC_TIMER_PACE,
+ QUIC_PACE_DEFERRED, quic_timer_pace_handler);
+ return HRTIMER_NORESTART;
+}
+
+void quic_timer_reset(struct sock *sk, u8 type, u64 timeout)
+{
+ struct timer_list *t = quic_timer(sk, type);
+
+ /* Note that type must never be QUIC_TIMER_PACE for this helper. */
+ if (WARN_ON_ONCE(type == QUIC_TIMER_PACE))
+ return;
+ if (timeout && !mod_timer(t, jiffies + usecs_to_jiffies(timeout)))
+ sock_hold(sk);
+}
+
+void quic_timer_start(struct sock *sk, u8 type, u64 timeout)
+{
+ struct timer_list *t;
+ struct hrtimer *hr;
+
+ if (type == QUIC_TIMER_PACE) {
+ hr = quic_timer(sk, type);
+
+ if (!hrtimer_is_queued(hr)) {
+ hrtimer_start(hr, ns_to_ktime(timeout),
+ HRTIMER_MODE_ABS_PINNED_SOFT);
+ sock_hold(sk);
+ }
+ return;
+ }
+
+ t = quic_timer(sk, type);
+ if (timeout && !timer_pending(t)) {
+ if (!mod_timer(t, jiffies + usecs_to_jiffies(timeout)))
+ sock_hold(sk);
+ }
+}
+
+void quic_timer_stop(struct sock *sk, u8 type)
+{
+ if (type == QUIC_TIMER_PACE) {
+ if (hrtimer_try_to_cancel(quic_timer(sk, type)) == 1)
+ sock_put(sk);
+ return;
+ }
+ if (timer_delete(quic_timer(sk, type)))
+ sock_put(sk);
+}
+
+void quic_timer_init(struct sock *sk)
+{
+ timer_setup(quic_timer(sk, QUIC_TIMER_LOSS), quic_timer_loss_timeout,
+ 0);
+ timer_setup(quic_timer(sk, QUIC_TIMER_SACK), quic_timer_sack_timeout,
+ 0);
+ timer_setup(quic_timer(sk, QUIC_TIMER_PATH), quic_timer_path_timeout,
+ 0);
+ timer_setup(quic_timer(sk, QUIC_TIMER_PMTU), quic_timer_pmtu_timeout,
+ 0);
+ /* Use hrtimer for pace timer, ensuring precise control over send
+ * timing.
+ */
+ hrtimer_setup(quic_timer(sk, QUIC_TIMER_PACE), quic_timer_pace_timeout,
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED_SOFT);
+}
+
+void quic_timer_free(struct sock *sk)
+{
+ quic_timer_stop(sk, QUIC_TIMER_LOSS);
+ quic_timer_stop(sk, QUIC_TIMER_SACK);
+ quic_timer_stop(sk, QUIC_TIMER_PATH);
+ quic_timer_stop(sk, QUIC_TIMER_PMTU);
+ quic_timer_stop(sk, QUIC_TIMER_PACE);
+}
diff --git a/net/quic/timer.h b/net/quic/timer.h
new file mode 100644
index 0000000000000..4f63660376027
--- /dev/null
+++ b/net/quic/timer.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+enum {
+ QUIC_TIMER_LOSS, /* Loss detection timer: retransmit on packet loss */
+ QUIC_TIMER_SACK, /* ACK delay timer, also used as idle timer alias */
+ QUIC_TIMER_PATH, /* Path validation timer: verifies path connectivity */
+ QUIC_TIMER_PMTU, /* PLPMTUD probing timer */
+ QUIC_TIMER_PACE, /* Pacing timer: controls packet transmission pacing */
+ QUIC_TIMER_MAX,
+ QUIC_TIMER_IDLE = QUIC_TIMER_SACK,
+};
+
+struct quic_timer {
+ union {
+ struct timer_list t;
+ struct hrtimer hr;
+ };
+};
+
+#define QUIC_MIN_PROBE_TIMEOUT 5000000
+
+#define QUIC_MIN_PATH_TIMEOUT 1500000
+
+#define QUIC_MIN_IDLE_TIMEOUT 1000000
+#define QUIC_DEF_IDLE_TIMEOUT 30000000
+
+void quic_timer_reset(struct sock *sk, u8 type, u64 timeout);
+void quic_timer_start(struct sock *sk, u8 type, u64 timeout);
+void quic_timer_stop(struct sock *sk, u8 type);
+void quic_timer_init(struct sock *sk);
+void quic_timer_free(struct sock *sk);
+
+void quic_timer_loss_handler(struct sock *sk);
+void quic_timer_pace_handler(struct sock *sk);
+void quic_timer_path_handler(struct sock *sk);
+void quic_timer_sack_handler(struct sock *sk);
+void quic_timer_pmtu_handler(struct sock *sk);
diff --git a/tools/include/uapi/linux/in.h b/tools/include/uapi/linux/in.h
index ced0fc3c3aa53..e4072152f2e62 100644
--- a/tools/include/uapi/linux/in.h
+++ b/tools/include/uapi/linux/in.h
@@ -85,6 +85,8 @@ enum {
#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_SMC = 256, /* Shared Memory Communications */
#define IPPROTO_SMC IPPROTO_SMC
+ IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed Secure Transport */
+#define IPPROTO_QUIC IPPROTO_QUIC
IPPROTO_MPTCP = 262, /* Multipath TCP connection */
#define IPPROTO_MPTCP IPPROTO_MPTCP
IPPROTO_MAX
diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h
index 2a8d7b14f1d11..9d6ec1b2c50ba 100644
--- a/tools/perf/trace/beauty/include/linux/socket.h
+++ b/tools/perf/trace/beauty/include/linux/socket.h
@@ -400,6 +400,7 @@ struct ucred {
#define SOL_MCTP 285
#define SOL_SMC 286
#define SOL_VSOCK 287
+#define SOL_QUIC 288
/* IPX options */
#define IPX_TYPE 1
diff --git a/usr/include/Makefile b/usr/include/Makefile
index ee69dd9d970e5..8b4133d38da2d 100644
--- a/usr/include/Makefile
+++ b/usr/include/Makefile
@@ -31,6 +31,7 @@ no-header-test += linux/omap3isp.h
no-header-test += linux/omapfb.h
no-header-test += linux/patchkey.h
no-header-test += linux/phonet.h
+no-header-test += linux/quic.h
no-header-test += linux/sctp.h
no-header-test += linux/sysctl.h
no-header-test += linux/usb/audio.h
|
| 3/2 | 2026/09/14 17:48 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 109435 output: 20 thoughts: 1636 Thoughts: **QUIC Kernel Module Deep Dive** Okay, so this patch introduces QUIC support in the Linux kernel, a new network protocol with protocol numbers 261 (IPPROTO_QUIC) and 288 (SOL_QUIC). This looks like a complete implementation, with a suite of accompanying files under `net/quic/` covering core functionalities like congestion control, connection ID handling, crypto, packet processing, and stream management. I see the usual suspects: `common`, `cong`, `connid`, `crypto`, `family`, `packet`, `path`, `pnspace`, `protocol`, `socket`, `stream`, and `timer`. There are also the expected header file additions in `include/uapi/linux/quic.h` and `include/linux/quic.h`, as well as modifications to existing header files. The Kconfig entry, `IP_QUIC`, marks this as experimental, which is expected for something like QUIC, especially in its initial kernel implementation. It's dependent on `INET`, `IPV6`, and selected crypto features. Interesting that it selects `NET_UDP_TUNNEL`, implying some interaction with UDP tunnels. Now, let's look at reachability. Can a user-space application actually *use* this? Based on the `protocol.c` file, it looks like it's registering as an inet protocol family. It exposes `SOCK_STREAM` and `SOCK_DGRAM` socket types with `IPPROTO_QUIC` protocol in both IPv4 and IPv6, which is consistent with the initial description of QUIC. This is good, meaning user applications can create these sockets. When a socket is created, `quic_init_sock()` is called. There are also sysctl entries for configuration and a procfs entry for statistics, and packet reception through `quic_udp_rcv()` and `quic_packet_rcv()`. The question is, how does this interact with UDP tunnels? Peering into `socket.c`, `quic_init_sock` initializes the usual socket structure elements - destructor, write space, and sets the `SOCK_USE_WRITE_QUEUE` flag. It also initializes all the QUIC components: conn id, path, congestion control, timers, packet processing, and streams. It all looks as expected. However, the more I look, the more puzzling this gets. `quic_bind`, `quic_connect`, `quic_sendmsg`, `quic_recvmsg`, `quic_accept` *all* return `-EOPNOTSUPP`. `quic_do_setsockopt` and `quic_do_getsockopt` do the same! What?! This is highly unusual for a functional network protocol implementation. The code has clearly defined the kernel API, but all of the API calls in `net/quic/socket.c` which would need to be implemented by a network protocol, returns that they are not supported. I suspect this is *incomplete*. Considering the function implementations within `socket.c` all return `-EOPNOTSUPP`, this suggests the code is *stubbed out*. I'm going to scan the diff to look for a multi-part series. The prompt diff is the entire patch series. It includes documentation and code changes. I'm going to fire up grep to see if I can figure out how this *should* be working by checking where `quic_path_bind` would be called. Maybe something is missing from the prompt. |
| 4/2 | 2026/09/14 17:48 | tool | grepper |
0mArgs:{
"Expression": "quic_path_bind"
}Results: {
"Output": "net/quic/path.c=149=static void quic_path_set_udp_sk(struct quic_path *path,\n--\nnet/quic/path.c-165-/* Binds a QUIC path to a local port and sets up a UDP socket. */\nnet/quic/path.c:166:int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path)\nnet/quic/path.c-167-{\n--\nnet/quic/path.h=174=bool quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa,\nnet/quic/path.h-175-\t\t\t union quic_addr *da, struct sock *sk);\nnet/quic/path.h:176:int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path);\nnet/quic/path.h-177-void quic_path_unbind(struct sock *sk, struct quic_path_group *paths, u8 path);\n"
} |
| 5/2 | 2026/09/14 17:48 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 112133 output: 21 thoughts: 692 Thoughts: **Initial Assessment of QUIC Code Reachability** Okay, so I'm looking at this code and immediately something jumps out. `quic_path_bind` is defined in `net/quic/path.c` and declared in `net/quic/path.h`, but it's *never* called in this commit? That seems… odd. Where does that leave `quic_udp_rcv`? I need to trace this. Ah, `quic_udp_rcv` is referenced in `quic_udp_sock_create`, and that function is, in turn, called by `quic_path_bind`. Which, again, *isn't called*. A dead end? Okay, let's step back. I don't have a git log tool, but I do know the HEAD commit is 85c120c4d16fd7ce68c939879ba590511ca36840. So I need to evaluate what this commit actually *does* in terms of user-space reachability. Can a user-space application actually *do* anything with this code right now? Let's check. Can someone call `socket(AF_INET, SOCK_STREAM, IPPROTO_QUIC)`? Yep! Looks like `quic_init_sock()` gets executed. And therefore `close(fd)` calls can lead to `quic_close()`, `quic_destroy_sock()`, and `quic_sock_destruct()` getting called. Okay, that's something. Can someone call `setsockopt(fd, SOL_IP, ...)` or `SOL_IPV6`? Yup, `quic_setsockopt` -> `quic_common_setsockopt` -> `ip_setsockopt` or `ipv6_setsockopt` is available! And, `shutdown(fd)` does `quic_shutdown` -> `quic_set_state(sk, QUIC_SS_CLOSED)`. We can read `/proc/net/quic/snmp` via `quic_snmp_seq_show`. And `/proc/sys/net/quic/quic_mem`, `quic_rmem`, and `quic_wmem` are accessible via sysctl. Alright, so the base socket operations are covered. And, `quic_hash_tables_init`, `quic_init` / `quic_exit` are obviously module init/exit. But wait… what about the *core* QUIC logic? Is `quic_packet_rcv`, `quic_packet_process`, `quic_crypto_*`, `quic_stream_*`, `quic_pnspace_*`, `quic_cong_*`, `quic_conn_id_*` *actually* reachable right now? That's the real question. I need to grep for where these critical functions are called. Are any of those functions, like `quic_packet_rcv` or `quic_stream_get`, etc. being called at all? That's the critical piece here. This initial analysis makes me think that the core QUIC functionality is likely not being activated. |
| 6/2 | 2026/09/14 17:48 | tool | grepper |
0mArgs:{
"Expression": "quic_packet_rcv"
}Results: {
"Output": "net/quic/packet.c=105=static int quic_packet_get_token(struct quic_data *token, u8 **pp, u32 *plen)\n--\nnet/quic/packet.c-117-/* Process PMTU reduction event on a QUIC socket. */\nnet/quic/packet.c:118:void quic_packet_rcv_err_pmtu(struct sock *sk)\nnet/quic/packet.c-119-{\n--\nnet/quic/packet.c-155-/* Handle ICMP Toobig packet and update QUIC socket path MTU. */\nnet/quic/packet.c:156:static int quic_packet_rcv_err(struct sock *sk, struct sk_buff *skb)\nnet/quic/packet.c-157-{\n--\nnet/quic/packet.c-188-\t/* Otherwise, process the MTU reduction now. */\nnet/quic/packet.c:189:\tquic_packet_rcv_err_pmtu(sk);\nnet/quic/packet.c-190-out:\n--\nnet/quic/packet.c=377=static struct sock *quic_packet_get_sock(struct sk_buff *skb, struct sock *usk)\n--\nnet/quic/packet.c-450-/* Entry point for processing received QUIC packets. */\nnet/quic/packet.c:451:int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp)\nnet/quic/packet.c-452-{\n--\nnet/quic/packet.c-456-\tif (unlikely(icmp))\nnet/quic/packet.c:457:\t\treturn quic_packet_rcv_err(sk, skb);\nnet/quic/packet.c-458-\n--\nnet/quic/packet.h=121=u32 *quic_packet_compatible_versions(u32 version);\nnet/quic/packet.h-122-\nnet/quic/packet.h:123:void quic_packet_rcv_err_pmtu(struct sock *sk);\n--\nnet/quic/path.c=20=static int quic_udp_rcv(struct sock *sk, struct sk_buff *skb)\n--\nnet/quic/path.c-29-\tskb_dst_force(skb);\nnet/quic/path.c:30:\tquic_packet_rcv(sk, skb, false);\nnet/quic/path.c-31-\t/* .encap_rcv must return 0 if skb was either consumed or dropped. */\n--\nnet/quic/path.c=35=static int quic_udp_err(struct sock *sk, struct sk_buff *skb)\nnet/quic/path.c-36-{\nnet/quic/path.c:37:\treturn quic_packet_rcv(sk, skb, true);\nnet/quic/path.c-38-}\n--\nnet/quic/path.h=166=quic_path_orig_dcid(struct quic_path_group *paths)\n--\nnet/quic/path.h-170-\nnet/quic/path.h:171:int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp);\nnet/quic/path.h-172-void quic_path_init(struct quic_path_group *paths);\n--\nnet/quic/socket.c=541=static void quic_release_cb(struct sock *sk)\n--\nnet/quic/socket.c-552-\tif (flags \u0026 QUIC_F_MTU_REDUCED_DEFERRED) {\nnet/quic/socket.c:553:\t\tquic_packet_rcv_err_pmtu(sk);\nnet/quic/socket.c-554-\t\t__sock_put(sk);\n"
} |
| 7/2 | 2026/09/14 17:48 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 114580 output: 19 thoughts: 373 Thoughts: **My Analysis of QUIC Packet Reception**
Okay, so I'm digging into how QUIC packets are received and processed, and something seems off. `quic_packet_rcv` is the core function, but it's only called from `quic_udp_rcv` and `quic_udp_err`. That's the entry point, that's what's going to matter. Where are those registered? Let's trace it back. Right, in `net/quic/path.c`, `quic_udp_sock_create()` sets them up: it sets `tuncfg.encap_rcv` to `quic_udp_rcv` and `tuncfg.encap_err_lookup` to `quic_udp_err`. And it's calling `setup_udp_tunnel_sock`.
But the key is how this encapsulation is setup: `quic_udp_sock_create()` is *only* called by `quic_path_bind()`. That makes sense...but now I have to know where *that* is called from. And it looks like, it's never called.
Wait... maybe `quic_path_bind` is called as part of `bind()`? Let me check the socket code in `net/quic/socket.c`. Ah, here it is:
```c
static int quic_bind(struct sock *sk, struct sockaddr_unsized *addr,
int addr_len)
{
return -EOPNOTSUPP;
}
```
It's stubbed out. Returns `-EOPNOTSUPP`! It's not supposed to be used. So, if `quic_bind` isn't called, what is?
Wait, what about `quic_proto_ops.bind`?
`quic_proto_ops.bind = inet_bind`.
So, on a QUIC socket, `bind` maps to `inet_bind`. I need to figure out what `inet_bind` does when called on a socket. I'll need to check the code, maybe grep for `inet_bind` to see where it gets used, or just go directly to `net/ipv4/af_inet.c`. Something's not right. This QUIC setup seems completely broken.
|
| 8/2 | 2026/09/14 17:49 | tool | grepper |
0mArgs:{
"Expression": "int inet_bind\\("
}Results: {
"Output": "include/net/inet_common.h=43=void inet_sock_destruct(struct sock *sk);\ninclude/net/inet_common.h:44:int inet_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len);\ninclude/net/inet_common.h-45-int inet_bind_sk(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len);\n--\nnet/ipv4/af_inet.c=449=int inet_bind_sk(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len)\n--\nnet/ipv4/af_inet.c-471-\nnet/ipv4/af_inet.c:472:int inet_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len)\nnet/ipv4/af_inet.c-473-{\n"
} |
| 9/2 | 2026/09/14 17:49 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 115717 output: 38 thoughts: 23 Thoughts: Here's a summary of my thought process, as requested: **Diving into `af_inet.c`** Okay, time to dig in. My intuition suggests that the issue I'm tracking down might be related to the IPv4 address handling within the kernel's network stack. Let's start by looking at `net/ipv4/af_inet.c` and specifically, I want to zero in on line 472. That area often deals with socket creation or address binding, perhaps socket options. I'll need to correlate what's happening around that line with the specific behavior I'm investigating. I suspect it's going to be a key point. This should give me a good entry point to understand the interactions in more detail. |
| 10/2 | 2026/09/14 17:49 | tool | read-file |
0mArgs:{
"File": "net/ipv4/af_inet.c",
"FirstLine": 448,
"LineCount": 40
}Results: {
"Contents": " 448:\t\n 449:\tint inet_bind_sk(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len)\n 450:\t{\n 451:\t\tu32 flags = BIND_WITH_LOCK;\n 452:\t\tint err;\n 453:\t\n 454:\t\t/* If the socket has its own bind function then use it. (RAW) */\n 455:\t\tif (sk-\u003esk_prot-\u003ebind) {\n 456:\t\t\treturn sk-\u003esk_prot-\u003ebind(sk, uaddr, addr_len);\n 457:\t\t}\n 458:\t\tif (addr_len \u003c sizeof(struct sockaddr_in))\n 459:\t\t\treturn -EINVAL;\n 460:\t\n 461:\t\t/* BPF prog is run before any checks are done so that if the prog\n 462:\t\t * changes context in a wrong way it will be caught.\n 463:\t\t */\n 464:\t\terr = BPF_CGROUP_RUN_PROG_INET_BIND_LOCK(sk, uaddr, \u0026addr_len,\n 465:\t\t\t\t\t\t\t CGROUP_INET4_BIND, \u0026flags);\n 466:\t\tif (err)\n 467:\t\t\treturn err;\n 468:\t\n 469:\t\treturn __inet_bind(sk, uaddr, addr_len, flags);\n 470:\t}\n 471:\t\n 472:\tint inet_bind(struct socket *sock, struct sockaddr_unsized *uaddr, int addr_len)\n 473:\t{\n 474:\t\treturn inet_bind_sk(sock-\u003esk, uaddr, addr_len);\n 475:\t}\n 476:\tEXPORT_SYMBOL(inet_bind);\n 477:\t\n 478:\tint __inet_bind(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len,\n 479:\t\t\tu32 flags)\n 480:\t{\n 481:\t\tstruct sockaddr_in *addr = (struct sockaddr_in *)uaddr;\n 482:\t\tstruct inet_sock *inet = inet_sk(sk);\n 483:\t\tstruct net *net = sock_net(sk);\n 484:\t\tunsigned short snum;\n 485:\t\tint chk_addr_ret;\n 486:\t\tu32 tb_id = RT_TABLE_LOCAL;\n 487:\t\tint err;\n"
} |
| 11/2 | 2026/09/14 17:49 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 116588 output: 20 thoughts: 2119 Thoughts: **QUIC Socket Exploration - A Deep Dive** Okay, so I'm diving into this QUIC implementation and immediately hitting some snags. I started by tracing the standard socket operations. `inet_bind_sk` calls `quic_bind`, `quic_connect`, `quic_listen`, `quic_sendmsg`, and `quic_recvmsg` and all return `-EOPNOTSUPP`. That's a bit of a wall. `setsockopt` with `SOL_QUIC` also hits a dead end with `-EOPNOTSUPP` through `quic_do_setsockopt`. However, I see `quic_common_setsockopt` getting called, which then hands off to `ip_setsockopt` and `ipv6_setsockopt`, indicating that lower-level IP options might be accessible. Alright, so I need to understand what _is_ reachable. I can create a socket using `socket(AF_INET, SOCK_STREAM, IPPROTO_QUIC)`. That calls `inet_create`, which uses `quic_stream_protosw`, allocating memory and calling `quic_init_sock`. `quic_init_sock` initializes the socket's internal data structures, including timers, packets, congestion control, connection IDs, and streams, which is a good sign! It sets up the destructors as well, `quic_sock_destruct` being the final free point. When closing (`close(fd)`), `quic_close` is called which sets the state to `QUIC_SS_CLOSED` and then calls `quic_destroy_sock`, which frees the resources. But there are some oddities. I notice I can get to `setsockopt` with `SOL_IP` via `quic_common_setsockopt` and `inet_ioctl`, but what about `shutdown`? `quic_shutdown` just sets the state to `QUIC_SS_CLOSED`, similar to `quic_close`. Now, I'm thinking about the bigger picture. I need to figure out which other parts of the code are actually used. My first thought is to dive into the other functions defined in `net/quic/` that are not the obvious. I went through the `net/quic/common.c` file and saw several functions, like `quic_sock_lookup`, `quic_listen_sock_lookup` and others dealing with socket lookups and hashing. These are called by the packet receive functions like `quic_packet_rcv`, which is the entry point for QUIC packets. The question is how to get those packets processed, and what's the connection from a user-created socket to them. `quic_packet_rcv` is called by `quic_udp_rcv`, which gets registered with `udp_tunnel_sock_cfg`, set by `quic_udp_sock_create`. But where is `quic_udp_sock_create` called from? It is called from `quic_path_bind`... but `quic_path_bind` appears nowhere else! That's a huge problem. I don't see any calls to `quic_path_bind`. It is defined only in the path code, and the only use of `quic_path_bind` is within `net/quic/path.c`. I did another `git grep` and got the same result. The code path for actually _receiving_ packets is not present. I'm missing a critical piece. I need to figure out what sets `QUIC_F_*` or `QUIC_*_DEFERRED` flags to see if there's any deferred action to perform and that's it! I need to trace back from the receive path! |
| 12/2 | 2026/09/14 17:49 | tool | grepper |
0mArgs:{
"Expression": "QUIC_.*DEFERRED"
}Results: {
"Output": "net/quic/packet.c=156=static int quic_packet_rcv_err(struct sock *sk, struct sk_buff *skb)\n--\nnet/quic/packet.c-182-\t\t */\nnet/quic/packet.c:183:\t\tif (!test_and_set_bit(QUIC_MTU_REDUCED_DEFERRED,\nnet/quic/packet.c-184-\t\t\t\t \u0026sk-\u003esk_tsq_flags))\n--\nnet/quic/socket.c=541=static void quic_release_cb(struct sock *sk)\n--\nnet/quic/socket.c-546-\tdo {\nnet/quic/socket.c:547:\t\tif (!(flags \u0026 QUIC_DEFERRED_ALL))\nnet/quic/socket.c-548-\t\t\treturn;\nnet/quic/socket.c:549:\t\tnflags = flags \u0026 ~QUIC_DEFERRED_ALL;\nnet/quic/socket.c-550-\t} while (!try_cmpxchg(\u0026sk-\u003esk_tsq_flags, \u0026flags, nflags));\nnet/quic/socket.c-551-\nnet/quic/socket.c:552:\tif (flags \u0026 QUIC_F_MTU_REDUCED_DEFERRED) {\nnet/quic/socket.c-553-\t\tquic_packet_rcv_err_pmtu(sk);\n--\nnet/quic/socket.c-555-\t}\nnet/quic/socket.c:556:\tif (flags \u0026 QUIC_F_LOSS_DEFERRED) {\nnet/quic/socket.c-557-\t\tquic_timer_loss_handler(sk);\n--\nnet/quic/socket.c-559-\t}\nnet/quic/socket.c:560:\tif (flags \u0026 QUIC_F_SACK_DEFERRED) {\nnet/quic/socket.c-561-\t\tquic_timer_sack_handler(sk);\n--\nnet/quic/socket.c-563-\t}\nnet/quic/socket.c:564:\tif (flags \u0026 QUIC_F_PATH_DEFERRED) {\nnet/quic/socket.c-565-\t\tquic_timer_path_handler(sk);\n--\nnet/quic/socket.c-567-\t}\nnet/quic/socket.c:568:\tif (flags \u0026 QUIC_F_PMTU_DEFERRED) {\nnet/quic/socket.c-569-\t\tquic_timer_pmtu_handler(sk);\n--\nnet/quic/socket.c-571-\t}\nnet/quic/socket.c:572:\tif (flags \u0026 QUIC_F_PACE_DEFERRED) {\nnet/quic/socket.c-573-\t\tquic_timer_pace_handler(sk);\n--\nnet/quic/socket.h=38=enum quic_tsq_enum {\nnet/quic/socket.h:39:\tQUIC_MTU_REDUCED_DEFERRED,\nnet/quic/socket.h:40:\tQUIC_LOSS_DEFERRED,\nnet/quic/socket.h:41:\tQUIC_SACK_DEFERRED,\nnet/quic/socket.h:42:\tQUIC_PATH_DEFERRED,\nnet/quic/socket.h:43:\tQUIC_PMTU_DEFERRED,\nnet/quic/socket.h:44:\tQUIC_PACE_DEFERRED,\nnet/quic/socket.h-45-};\n--\nnet/quic/socket.h=47=enum quic_tsq_flags {\nnet/quic/socket.h:48:\tQUIC_F_MTU_REDUCED_DEFERRED\t= BIT(QUIC_MTU_REDUCED_DEFERRED),\nnet/quic/socket.h:49:\tQUIC_F_LOSS_DEFERRED\t\t= BIT(QUIC_LOSS_DEFERRED),\nnet/quic/socket.h:50:\tQUIC_F_SACK_DEFERRED\t\t= BIT(QUIC_SACK_DEFERRED),\nnet/quic/socket.h:51:\tQUIC_F_PATH_DEFERRED\t\t= BIT(QUIC_PATH_DEFERRED),\nnet/quic/socket.h:52:\tQUIC_F_PMTU_DEFERRED\t\t= BIT(QUIC_PMTU_DEFERRED),\nnet/quic/socket.h:53:\tQUIC_F_PACE_DEFERRED\t\t= BIT(QUIC_PACE_DEFERRED),\nnet/quic/socket.h-54-};\nnet/quic/socket.h-55-\nnet/quic/socket.h:56:#define QUIC_DEFERRED_ALL (QUIC_F_MTU_REDUCED_DEFERRED |\t\\\nnet/quic/socket.h:57:\t\t\t QUIC_F_LOSS_DEFERRED |\t\t\\\nnet/quic/socket.h:58:\t\t\t QUIC_F_SACK_DEFERRED |\t\t\\\nnet/quic/socket.h:59:\t\t\t QUIC_F_PATH_DEFERRED |\t\t\\\nnet/quic/socket.h:60:\t\t\t QUIC_F_PMTU_DEFERRED |\t\t\\\nnet/quic/socket.h:61:\t\t\t QUIC_F_PACE_DEFERRED)\nnet/quic/socket.h-62-\n--\nnet/quic/timer.c=38=static void quic_timer_sack_timeout(struct timer_list *t)\n--\nnet/quic/timer.c-40-\tquic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_SACK,\nnet/quic/timer.c:41:\t\t\t QUIC_SACK_DEFERRED, quic_timer_sack_handler);\nnet/quic/timer.c-42-}\n--\nnet/quic/timer.c=48=static void quic_timer_loss_timeout(struct timer_list *t)\n--\nnet/quic/timer.c-50-\tquic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_LOSS,\nnet/quic/timer.c:51:\t\t\t QUIC_LOSS_DEFERRED, quic_timer_loss_handler);\nnet/quic/timer.c-52-}\n--\nnet/quic/timer.c=58=static void quic_timer_path_timeout(struct timer_list *t)\n--\nnet/quic/timer.c-60-\tquic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PATH,\nnet/quic/timer.c:61:\t\t\t QUIC_PATH_DEFERRED, quic_timer_path_handler);\nnet/quic/timer.c-62-}\n--\nnet/quic/timer.c=68=static void quic_timer_pmtu_timeout(struct timer_list *t)\n--\nnet/quic/timer.c-70-\tquic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PMTU,\nnet/quic/timer.c:71:\t\t\t QUIC_PMTU_DEFERRED, quic_timer_pmtu_handler);\nnet/quic/timer.c-72-}\n--\nnet/quic/timer.c=78=static enum hrtimer_restart quic_timer_pace_timeout(struct hrtimer *hr)\n--\nnet/quic/timer.c-80-\tquic_timer_timeout((struct quic_timer *)hr, QUIC_TIMER_PACE,\nnet/quic/timer.c:81:\t\t\t QUIC_PACE_DEFERRED, quic_timer_pace_handler);\nnet/quic/timer.c-82-\treturn HRTIMER_NORESTART;\n"
} |
| 13/2 | 2026/09/14 17:49 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 121530 output: 23 thoughts: 162 Thoughts: **Analyzing QUIC Timer Management** Okay, let's break this down. My initial observation is that the deferred bits— those flags marking timeouts for various QUIC functionalities like loss detection, SACK, path validation, PMTU discovery, and pacing—are being set upon timer expirations. That's as expected. The timeouts are clearly linked to these specific timer types: `quic_timer_loss_timeout`, `quic_timer_sack_timeout`, and so on. Now, the critical question is, *when* do these timers actually get triggered? How do they get started or reset? I need to understand the control flow. I'm searching for where `quic_timer_start` or `quic_timer_reset` get called. I immediately see the calls in `quic_packet_rcv_err_pmtu` and `quic_packet_route`. Aha, hold on. I need to make sure I understand the full scope of timer interactions. It's a key point and worth double-checking – is there a *new* timer start action added by this particular patch? Let's verify by grepping for `quic_timer_start` and `quic_timer_reset` to ensure I'm not missing anything. I need to be thorough. |
| 14/2 | 2026/09/14 17:49 | tool | grepper |
0mArgs:{
"Expression": "quic_timer_(start|reset)"
}Results: {
"Output": "net/quic/packet.c=118=void quic_packet_rcv_err_pmtu(struct sock *sk)\n--\nnet/quic/packet.c-149-\tif (reset_timer)\nnet/quic/packet.c:150:\t\tquic_timer_reset(sk, QUIC_TIMER_PMTU, paths-\u003eplpmtud_interval);\nnet/quic/packet.c-151-\tif (pathmtu)\n--\nnet/quic/packet.c=811=int quic_packet_route(struct sock *sk)\n--\nnet/quic/packet.c-830-\tquic_path_pl_reset(paths);\nnet/quic/packet.c:831:\tquic_timer_reset(sk, QUIC_TIMER_PMTU, paths-\u003eplpmtud_interval);\nnet/quic/packet.c-832-\treturn 0;\n--\nnet/quic/timer.c=78=static enum hrtimer_restart quic_timer_pace_timeout(struct hrtimer *hr)\n--\nnet/quic/timer.c-84-\nnet/quic/timer.c:85:void quic_timer_reset(struct sock *sk, u8 type, u64 timeout)\nnet/quic/timer.c-86-{\n--\nnet/quic/timer.c-95-\nnet/quic/timer.c:96:void quic_timer_start(struct sock *sk, u8 type, u64 timeout)\nnet/quic/timer.c-97-{\n--\nnet/quic/timer.h=21=struct quic_timer {\n--\nnet/quic/timer.h-34-\nnet/quic/timer.h:35:void quic_timer_reset(struct sock *sk, u8 type, u64 timeout);\nnet/quic/timer.h:36:void quic_timer_start(struct sock *sk, u8 type, u64 timeout);\nnet/quic/timer.h-37-void quic_timer_stop(struct sock *sk, u8 type);\n"
} |
| 15/2 | 2026/09/14 17:49 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 122542 output: 20 thoughts: 90 Thoughts: Here's my thought process, as requested: **Investigating QUIC Packet Routing** Okay, so I'm looking at `net/quic/packet.c` and I've noticed `quic_packet_route`. It's getting called from `quic_packet_config` and `quic_packet_rcv_err_pmtu`. That's interesting, but I need to figure out the broader context. Specifically, I need to understand what's triggering `quic_packet_config`. Where does *it* come from? I could trace through the code, but a quick `grep` will probably be faster to map out all the callers of `quic_packet_config`. Let's see what that turns up. I need the full picture to understand the flow and potential issues with the routing. |
| 16/2 | 2026/09/14 17:49 | tool | grepper |
0mArgs:{
"Expression": "quic_packet_config"
}Results: {
"Output": "net/quic/packet.c=839=u16 quic_packet_overhead(struct sock *sk, u8 level, u8 path)\n--\nnet/quic/packet.c-864- */\nnet/quic/packet.c:865:int quic_packet_config(struct sock *sk, u8 level, u8 path)\nnet/quic/packet.c-866-{\n--\nnet/quic/packet.h=110=u16 quic_packet_overhead(struct sock *sk, u8 level, u8 path);\nnet/quic/packet.h:111:int quic_packet_config(struct sock *sk, u8 level, u8 path);\nnet/quic/packet.h-112-\n"
} |
| 17/2 | 2026/09/14 17:49 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 123034 output: 21 thoughts: 2023 Thoughts: **QUIC Implementation Analysis: Fuzzing Feasibility** Okay, let's break this down. No, nobody's calling `quic_packet_config` directly, but that's not the point. This patch is clearly laying the foundation for an in-kernel QUIC implementation. And from an expert perspective, this is important to assess for vulnerabilities. First, I see the core pieces: documentation, maintainer files, header files, and the critical kernel build system integration (Kconfig, Makefiles, etc.). Then, we have the meat of it: all the QUIC-specific code – common functionality, congestion control, connection ID management, crypto primitives, socket families, packet handling, path management, PN space, protocol logic, socket operations, streams, and timers. That’s a large and complex surface, ripe for fuzzing. Now, let's recall the fuzzing guidelines. Reachability is key. The fuzzing can only find vulnerabilities in code that is executable in standard virtualized environments. I need to make sure this code is indeed reachable. "New or modified assertions in reachable code must be fuzzed" is something that I always keep in mind. Is this code reachable? YES! The new QUIC protocol family is exposed via standard socket calls: `socket(PF_INET, SOCK_STREAM, IPPROTO_QUIC)` and `socket(PF_INET6, SOCK_STREAM, IPPROTO_QUIC)` and `SOCK_DGRAM`. I have to see how it initializes and cleans up. The `socket()` call triggers QUIC-specific initialization routines (`quic_init_sock`, various "init" functions for the various components like cong, packet, timer, etc.). On close, we get cleanup via `quic_close`, `quic_destroy_sock`, and other "free" functions. We also have sysctl and procfs interfaces and IP option handling through `setsockopt` / `getsockopt`, which is really interesting and needs to be carefully scrutinized. And, importantly, shutdown, connect, bind, sendmsg, recvmsg, listen, accept, etc. are implemented as "not supported", all of which require thorough examination! So there are several interesting code paths. This is a very interesting piece! The important part is can any bugs happen in socket, close, setsockopt, etc., on these QUIC sockets? The `quic_setsockopt` function is really interesting here. I noticed it can fall back to `ip_setsockopt` or `ipv6_setsockopt` when called with `SOL_IP` level. This is a crucial point for fuzzing! Does `ip_setsockopt` and `ipv6_setsockopt` assume certain structures in `sk`? It relies on the presence of `inet` and `inet6` structures within the QUIC socket structures. And, in the `quicv6_prot` struct there is `ipv6_pinfo_offset`. Is this offset correct? I also need to verify that `inet6_create` initializes these structures properly! This is where things get interesting and where subtle vulnerabilities might hide. So, in conclusion, this patch is clearly worth fuzzing and I'm very excited to dive deeper and see what happens here! |
| 18/2 | 2026/09/14 17:50 | tool | grepper |
0mArgs:{
"Expression": "ipv6_pinfo_offset"
}Results: {
"Output": "include/net/sock.h=1291=struct proto {\n--\ninclude/net/sock.h-1384-\tunsigned int\t\tfreeptr_offset;\ninclude/net/sock.h:1385:\tunsigned int\t\tipv6_pinfo_offset;\ninclude/net/sock.h-1386-\tslab_flags_t\t\tslab_flags;\n--\nnet/ipv6/af_inet6.c=91=static struct ipv6_pinfo *inet6_sk_generic(struct sock *sk)\nnet/ipv6/af_inet6.c-92-{\nnet/ipv6/af_inet6.c:93:\tconst int offset = sk-\u003esk_prot-\u003eipv6_pinfo_offset;\nnet/ipv6/af_inet6.c-94-\n--\nnet/ipv6/ping.c=196=struct proto pingv6_prot = {\n--\nnet/ipv6/ping.c-213-\t.obj_size =\tsizeof(struct raw6_sock),\nnet/ipv6/ping.c:214:\t.ipv6_pinfo_offset = offsetof(struct raw6_sock, inet6),\nnet/ipv6/ping.c-215-};\n--\nnet/ipv6/raw.c=1207=struct proto rawv6_prot = {\n--\nnet/ipv6/raw.c-1224-\t.obj_size\t = sizeof(struct raw6_sock),\nnet/ipv6/raw.c:1225:\t.ipv6_pinfo_offset = offsetof(struct raw6_sock, inet6),\nnet/ipv6/raw.c-1226-\t.useroffset\t = offsetof(struct raw6_sock, filter),\n--\nnet/ipv6/tcp_ipv6.c=2276=struct proto tcpv6_prot = {\n--\nnet/ipv6/tcp_ipv6.c-2319-\t\t\t\t\t tcp.inet_conn.icsk_inet.sk.sk_freeptr),\nnet/ipv6/tcp_ipv6.c:2320:\t.ipv6_pinfo_offset = offsetof(struct tcp6_sock, inet6),\nnet/ipv6/tcp_ipv6.c-2321-\t.slab_flags\t\t= SLAB_TYPESAFE_BY_RCU,\n--\nnet/ipv6/udp.c=1902=struct proto udpv6_prot = {\n--\nnet/ipv6/udp.c-1933-\t.obj_size\t\t= sizeof(struct udp6_sock),\nnet/ipv6/udp.c:1934:\t.ipv6_pinfo_offset = offsetof(struct udp6_sock, inet6),\nnet/ipv6/udp.c-1935-\t.diag_destroy\t\t= udp_abort,\n--\nnet/l2tp/l2tp_ip6.c=736=static struct proto l2tp_ip6_prot = {\n--\nnet/l2tp/l2tp_ip6.c-753-\t.obj_size\t = sizeof(struct l2tp_ip6_sock),\nnet/l2tp/l2tp_ip6.c:754:\t.ipv6_pinfo_offset = offsetof(struct l2tp_ip6_sock, inet6),\nnet/l2tp/l2tp_ip6.c-755-};\n--\nnet/mptcp/protocol.c=4870=int __init mptcp_proto_v6_init(void)\n--\nnet/mptcp/protocol.c-4879-\tmptcp_v6_prot.obj_size = sizeof(struct mptcp6_sock);\nnet/mptcp/protocol.c:4880:\tmptcp_v6_prot.ipv6_pinfo_offset = offsetof(struct mptcp6_sock, np);\nnet/mptcp/protocol.c-4881-\n--\nnet/quic/socket.c=624=struct proto quicv6_prot = {\n--\nnet/quic/socket.c-644-\t.obj_size\t= sizeof(struct quic6_sock),\nnet/quic/socket.c:645:\t.ipv6_pinfo_offset\t= offsetof(struct quic6_sock, inet6),\nnet/quic/socket.c-646-\t.sysctl_mem\t\t= sysctl_quic_mem,\n--\nnet/sctp/socket.c=9709=struct proto sctpv6_prot = {\n--\nnet/sctp/socket.c-9730-\t.obj_size\t= sizeof(struct sctp6_sock),\nnet/sctp/socket.c:9731:\t.ipv6_pinfo_offset = offsetof(struct sctp6_sock, inet6),\nnet/sctp/socket.c-9732-\t.useroffset\t= offsetof(struct sctp6_sock, sctp.subscribe),\n--\nnet/smc/smc_inet.c=70=static struct proto smc_inet6_prot = {\n--\nnet/smc/smc_inet.c-80-\t.slab_flags\t= SLAB_TYPESAFE_BY_RCU,\nnet/smc/smc_inet.c:81:\t.ipv6_pinfo_offset\t= offsetof(struct smc6_sock, inet6),\nnet/smc/smc_inet.c-82-};\n"
} |
| 19/2 | 2026/09/14 17:50 | llm | patch-evaluator |
1mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 127163 output: 39 thoughts: 3436 Thoughts: **QUIC Patch Series Triage: Worth Fuzzing?** Okay, let's break this down. My initial instinct is to assess whether this QUIC implementation, even at this early stage, is ripe for fuzzing. I'm looking at `net/quic/socket.c`, `net/quic/family.c` and considering the various functions involved. First, I see the socket lifecycle is being set up with `quic_sock_destruct`, `quic_init_sock`, and `quic_destroy_sock`. Crucially, `quic_init_sock` indicates the creation and initialization of QUIC-specific data structures within the socket: streams, PNspaces, timers, congestion control, paths, connection IDs, and packets. The destruction side looks fairly standard using `inet_sock_destruct` and `inet6_sock_destruct`, depending on the address family of the socket. `quic_sock_destruct` handles the cleanup of QUIC-specific resources through the `quic_crypto_free` and `quic_data_free` functions. I'm especially interested in the error handling within the initialization routines, specifically what happens if `quic_pnspace_init` fails. The code appears to handle these failures, but it's important to make sure there are no subtle leaks or issues in the error paths. I see this is covered during `destroy`. Next, I need to check the fuzzing criteria. The patch adds a new protocol (`IPPROTO_QUIC`) accessible via the socket API. Bingo! This is a core part of the criteria. It touches memory management, networking, and potentially BPF if socket options are used. I know from the code that syzkaller will be able to generate calls like `socket$inet(0x2, 0x1, 0x105)` very easily and that this registers with `inet_register_protosw`. Now, the obvious question: Can the fuzzer actually *reach* the new code? I'm initially concerned that bind/send are stubbed out. Right now, there isn't data transfer. This means the immediate impact is limited to the socket lifecycle, sysctl, procfs and socket options. The critical path seems to be `quic_packet_rcv` within `quic_packet.c`. I note that this is reached through a UDP tunnel (`quic_udp_rcv`) which is registered via `quic_udp_sock_create()` which is only called by `quic_path_bind()`. However, `quic_path_bind` is not even called by the current commit. I don't see any other obvious paths, or ways it's reachable. Looking at the "WorthFuzzing" criteria, it's clear: I *must* consider this patch for fuzzing. It is a new network protocol. I need to carefully look at all the modified code for the negative criteria, and it doesn't fit neatly into the "unreachable code". The key is *reachable*. Even though data transfer is not implemented, the socket lifecycle is. And that's what's getting created, destructed, and initialized. I look at `quic_init_sock` and `quic_sock_destruct` and `quic_destroy_sock` to check for subtleties and invariants. The error handling is there, and the cleanup seems sane. I also notice the use of `QUIC_DEFERRED_ALL` in `quic_release_cb`. I must check that the mask isn't being abused if `setsockopt` is called with IPv6 options on an IPv4 QUIC socket. And here is where I uncover a good case, where `quic_common_setsockopt` calls `ipv6_setsockopt` even with the wrong socket family! This is a good point to start fuzzing, looking for a potential crash. In summary: Even without a working data path, this patch introduces a new, reachable kernel code path through the socket API. Syzkaller and other fuzzers *can* exercise `socket()` creation/destruction, and `setsockopt`/`getsockopt`, memory allocation and cleanup, and sysctl/procfs interactions with the QUIC implementation. Therefore, it is definitively *WorthFuzzing*. |
| 20/2 | 2026/09/14 17:51 | tool | grepper |
0mArgs:{
"Expression": "int ipv6_setsockopt",
"PathPrefix": "net/ipv6/ipv6_sockglue.c"
}Results: {
"Output": "net/ipv6/ipv6_sockglue.c=376=int do_ipv6_setsockopt(struct sock *sk, int level, int optname,\n--\nnet/ipv6/ipv6_sockglue.c-874-\nnet/ipv6/ipv6_sockglue.c:875:int ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,\nnet/ipv6/ipv6_sockglue.c-876-\t\t unsigned int optlen)\n"
} |
| 21/2 | 2026/09/14 17:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 131719 output: 40 thoughts: 25 Thoughts: Here's a summary of my thought process, as if I were the one thinking it: **Diving into IPv6 Socket Glue** Okay, time to get my hands dirty. I'm going to jump into the `net/ipv6/ipv6_sockglue.c` file. Specifically, I'm aiming for around line 875. I'm pretty sure that's where I need to be to understand the nuances of how IPv6 sockets are glued together, potentially looking for the specific area of kernel code. I'm focusing in that vicinity based on the problem I'm trying to solve - I have a strong hunch that the issue I'm debugging is related to the socket handling within the IPv6 stack. I'm expecting to find a specific data structure, or an interesting function call, related to the management of these sockets and related flags. Let's see what we uncover. |
| 22/2 | 2026/09/14 17:51 | tool | read-file |
0mArgs:{
"File": "net/ipv6/ipv6_sockglue.c",
"FirstLine": 870,
"LineCount": 40
}Results: {
"Contents": " 870:\te_inval:\n 871:\t\tretv = -EINVAL;\n 872:\t\tgoto unlock;\n 873:\t}\n 874:\t\n 875:\tint ipv6_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,\n 876:\t\t\t unsigned int optlen)\n 877:\t{\n 878:\t\tint err;\n 879:\t\n 880:\t\tif (level == SOL_IP \u0026\u0026 sk-\u003esk_type != SOCK_RAW)\n 881:\t\t\treturn ip_setsockopt(sk, level, optname, optval, optlen);\n 882:\t\n 883:\t\tif (level != SOL_IPV6)\n 884:\t\t\treturn -ENOPROTOOPT;\n 885:\t\n 886:\t\terr = do_ipv6_setsockopt(sk, level, optname, optval, optlen);\n 887:\t#ifdef CONFIG_NETFILTER\n 888:\t\t/* we need to exclude all possible ENOPROTOOPTs except default case */\n 889:\t\tif (err == -ENOPROTOOPT \u0026\u0026 optname != IPV6_IPSEC_POLICY \u0026\u0026\n 890:\t\t\t\toptname != IPV6_XFRM_POLICY)\n 891:\t\t\terr = nf_setsockopt(sk, PF_INET6, optname, optval, optlen);\n 892:\t#endif\n 893:\t\treturn err;\n 894:\t}\n 895:\tEXPORT_SYMBOL(ipv6_setsockopt);\n 896:\t\n 897:\tstatic int ipv6_getsockopt_sticky(struct sock *sk, struct ipv6_txoptions *opt,\n 898:\t\t\t\t\t int optname, sockptr_t optval, int len)\n 899:\t{\n 900:\t\tstruct ipv6_opt_hdr *hdr;\n 901:\t\n 902:\t\tif (!opt)\n 903:\t\t\treturn 0;\n 904:\t\n 905:\t\tswitch (optname) {\n 906:\t\tcase IPV6_HOPOPTS:\n 907:\t\t\thdr = opt-\u003ehopopt;\n 908:\t\t\tbreak;\n 909:\t\tcase IPV6_RTHDRDSTOPTS:\n"
} |
| 23/2 | 2026/09/14 17:51 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 132596 output: 444 thoughts: 2913 Thoughts: **QUIC Fuzzing Verdict: Worthy!**
Okay, here's my assessment of this patch. First off, if we try setting an IPv6-specific socket option (`SOL_IPV6`) on an IPv4 socket, we get an `-ENOPROTOOPT` error, which seems normal. But, more importantly, *this is QUIC*! This patch introduces a brand new transport protocol, `IPPROTO_QUIC`, right into the kernel. It’s a substantial addition, including protocol structures, options, sysctls, procfs, and of course all the relevant socket calls in userspace.
The code touches essential networking areas: `net/quic/`, `net/Kconfig`, `net/Makefile`, and the `include/linux/quic.h` header, so fuzzing this is a no-brainer. The patch adds new transport protocol in the Linux kernel and has all configurations needed to enable it. We can easily fuzz this with standard syzkaller syscalls.
Given this, the analysis boils down to a few key points:
1. **WorthFuzzing**: Absolutely YES.
2. **EnableConfigs**: We need `IP_QUIC`, `INET`, and `IPV6` enabled. The `Kconfig` files show these are required, and the code contains `#if` and `#ifdef` guards, common in fuzzable kernels.
3. **FocusSymbols**: Now for the important part—which symbols to focus on. We *don't* want generic functions like allocators. We *do* want the entry points and the specific logic being added or altered. I need to make sure the symbols are actual entrypoints and not just static inlines. So, I need to focus on these non-static functions from `net/quic/socket.c`, `net/quic/protocol.c`, `net/quic/family.c`, `net/quic/common.c`, `net/quic/stream.c`, `net/quic/pnspace.c`, `net/quic/timer.c`, `net/quic/packet.c`, `net/quic/connid.c`, `net/quic/cong.c`, `net/quic/crypto.c`, and `net/quic/path.c`:
* `quic_init_sock`
* `quic_destroy_sock`
* `quic_sock_destruct`
* `quic_do_setsockopt`
* `quic_do_getsockopt`
* `quic_common_setsockopt`
* `quic_common_getsockopt`
* `quic_setsockopt`
* `quic_getsockopt`
* `quic_release_cb`
* `quic_stream_init`
* `quic_stream_free`
* `quic_pnspace_init`
* `quic_pnspace_free`
* `quic_conn_id_set_init`
* `quic_conn_id_set_free`
* `quic_packet_init`
* `quic_packet_free`
* `quic_timer_init`
* `quic_timer_free`
* `quic_cong_init`
* `quic_path_init`
* `quic_path_unbind`
* `quic_crypto_free`
* `quic_snmp_seq_show`
I've double-checked these and confirmed they're specific to QUIC and should be directly exercisable by fuzzer syscalls like `socket`, `setsockopt`, `getsockopt`, etc.
The rationale is clear. The patch introduces QUIC protocol support directly reachable from userspace via socket calls, `close()`, and `setsockopt()`, as well as procfs interfaces. These are excellent targets for fuzzing to uncover potential issues in memory management, reference counting, and state handling. All conditions are satisfied, so I'm calling it: worth fuzzing!
|
| 24/2 | 2026/09/14 17:52 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"IP_QUIC",
"INET",
"IPV6"
],
"FocusSymbols": [
"quic_init_sock",
"quic_destroy_sock",
"quic_sock_destruct",
"quic_do_setsockopt",
"quic_do_getsockopt",
"quic_common_setsockopt",
"quic_common_getsockopt",
"quic_setsockopt",
"quic_getsockopt",
"quic_release_cb",
"quic_stream_init",
"quic_stream_free",
"quic_pnspace_init",
"quic_pnspace_free",
"quic_conn_id_set_init",
"quic_conn_id_set_free",
"quic_packet_init",
"quic_packet_free",
"quic_timer_init",
"quic_timer_free",
"quic_cong_init",
"quic_path_init",
"quic_path_unbind",
"quic_crypto_free",
"quic_snmp_seq_show"
],
"Reasoning": "The patch introduces the foundational in-kernel implementation of the QUIC transport protocol (IPPROTO_QUIC == 261, SOL_QUIC == 288), registering AF_INET and AF_INET6 SOCK_STREAM and SOCK_DGRAM protocol switches. Although higher-level features like packet exchange and full stream I/O are partially stubbed out with -EOPNOTSUPP at this stage, the core socket creation, initialization, teardown, timer setup, memory allocation accounting, socket options, procfs stats, and sysctl interfaces are fully reachable and executable from unprivileged userspace via socket() and standard socket syscalls. Fuzzing is warranted to test resource allocation, lifecycle management, and socket state handling.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"IP_QUIC",
"INET",
"IPV6"
],
"FocusSymbols": [
"quic_init_sock",
"quic_destroy_sock",
"quic_sock_destruct",
"quic_do_setsockopt",
"quic_do_getsockopt",
"quic_common_setsockopt",
"quic_common_getsockopt",
"quic_setsockopt",
"quic_getsockopt",
"quic_release_cb",
"quic_stream_init",
"quic_stream_free",
"quic_pnspace_init",
"quic_pnspace_free",
"quic_conn_id_set_init",
"quic_conn_id_set_free",
"quic_packet_init",
"quic_packet_free",
"quic_timer_init",
"quic_timer_free",
"quic_cong_init",
"quic_path_init",
"quic_path_unbind",
"quic_crypto_free",
"quic_snmp_seq_show"
],
"Reasoning": "The patch introduces the foundational in-kernel implementation of the QUIC transport protocol (IPPROTO_QUIC == 261, SOL_QUIC == 288), registering AF_INET and AF_INET6 SOCK_STREAM and SOCK_DGRAM protocol switches. Although higher-level features like packet exchange and full stream I/O are partially stubbed out with -EOPNOTSUPP at this stage, the core socket creation, initialization, teardown, timer setup, memory allocation accounting, socket options, procfs stats, and sysctl interfaces are fully reachable and executable from unprivileged userspace via socket() and standard socket syscalls. Fuzzing is warranted to test resource allocation, lifecycle management, and socket state handling.",
"WorthFuzzing": true
} |
| 25/1 | 2026/09/14 17:52 | action | If |
3mArgs:{
"WorthFuzzing": true
}Results: null |
| 26/2 | 2026/09/14 17:52 | agent | kmsan-evaluator |
3mModel:core Args: null Results: {
"KMSANReasoning": "The patch series introduces initial protocol support and foundational structures for QUIC (RFC 9000), including socket operations, packet framing, connection ID handling, congestion control, and cryptographic wrappers.\n\nKMSAN applicability was evaluated against the new protocol logic:\n1. Kernel-to-user info-leaks: QUIC-specific socket options (quic_do_getsockopt), message reception (quic_recvmsg), accept, connect, and bind are currently stubs returning -EOPNOTSUPP. Address retrieval (quic_get_sk_addr) delegates to inet_getname / inet6_getname, which properly populate and clear padding (e.g. sin_zero) in sockaddr structures.\n2. Uninitialized memory access: Heap allocations across the subsystem either use kzalloc (e.g. stream structures, pn_map bitmaps, connection ID structures) or explicitly initialize every struct field upon allocation (quic_request_sock, quic_udp_sock). Stack buffers in cryptographic and framing routines are explicitly zeroed or bounded by parsed lengths before consumption.\n3. Control flow: Variable parsing helpers (quic_get_int, quic_get_var, quic_packet_get_long_header) check buffer bounds before reading and update outputs only on success.\n\nAny potential bugs in this patch series (such as bounds errors in packet parsing, use-after-free in reference-counted UDP tunnel sockets or RCU-managed connection IDs, or lock hierarchy issues) are covered by standard KASAN and LOCKDEP. There are no uninitialized memory risks requiring a dedicated KMSAN session.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 85c120c4d16fd7ce68c939879ba590511ca36840
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Sep 14 17:48:10 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index 208f46967ee59..6dd9d6970cec4 100644
--- a/Documentation/networking/ip-sysctl.rst
+++ b/Documentation/networking/ip-sysctl.rst
@@ -3809,6 +3809,45 @@ l3mdev_accept - BOOLEAN
Default: 1 (enabled)
+``/proc/sys/net/quic/*`` Variables
+===================================
+
+quic_mem - vector of 3 LONGs: min, pressure, max
+ Number of pages allowed for queueing by all QUIC sockets.
+
+ min: below this number of pages QUIC is not bothered about its
+ memory appetite.
+
+ pressure: when amount of memory allocated by QUIC exceeds this number
+ of pages, QUIC moderates its memory consumption and enters memory
+ pressure mode, which is exited when memory consumption falls
+ under "min".
+
+ max: number of pages allowed for queueing by all QUIC sockets.
+
+ Defaults are calculated at boot time from amount of available
+ memory.
+
+quic_rmem - vector of 3 INTEGERs: min, default, max
+ Only the first value ("min") is used, "default" and "max" are
+ ignored.
+
+ min: Minimal size of receive buffer used by QUIC sockets.
+ It is guaranteed to each QUIC socket, even under moderate memory
+ pressure.
+
+ Default: 4K
+
+quic_wmem - vector of 3 INTEGERs: min, default, max
+ Only the first value ("min") is used, "default" and "max" are
+ ignored.
+
+ min: Amount of memory reserved for send buffers for QUIC sockets.
+ Each QUIC socket has rights to use it due to fact of its birth.
+
+ Default: 4K
+
+
``/proc/sys/net/core/*``
========================
diff --git a/MAINTAINERS b/MAINTAINERS
index 0e04d92d1b098..ba5b5249e8fed 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22666,6 +22666,15 @@ L: linux-wireless@vger.kernel.org
S: Maintained
F: drivers/net/wireless/quantenna/
+QUIC PROTOCOL
+M: Xin Long <lucien.xin@gmail.com>
+L: quic@lists.linux.dev
+S: Maintained
+W: https://github.com/lxin/quic
+F: include/linux/quic.h
+F: include/uapi/linux/quic.h
+F: net/quic/
+
RADEON and AMDGPU DRM DRIVERS
M: Alex Deucher <alexander.deucher@amd.com>
M: Christian König <christian.koenig@amd.com>
diff --git a/include/linux/quic.h b/include/linux/quic.h
new file mode 100644
index 0000000000000..51c099e9547f5
--- /dev/null
+++ b/include/linux/quic.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#ifndef _LINUX_QUIC_H
+#define _LINUX_QUIC_H
+
+#include <linux/sockptr.h>
+#include <uapi/linux/quic.h>
+
+struct sock;
+
+#if IS_REACHABLE(CONFIG_IP_QUIC)
+int quic_do_setsockopt(struct sock *sk, int optname, sockptr_t optval,
+ unsigned int optlen);
+int quic_do_getsockopt(struct sock *sk, int optname, sockptr_t optval,
+ sockptr_t optlen);
+#else
+static inline int quic_do_setsockopt(struct sock *sk, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int quic_do_getsockopt(struct sock *sk, int optname,
+ sockptr_t optval, sockptr_t optlen)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+#endif
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 5a5eb12501032..aeea3baa96732 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -400,6 +400,7 @@ struct ucred {
#define SOL_MCTP 285
#define SOL_SMC 286
#define SOL_VSOCK 287
+#define SOL_QUIC 288
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/trace/events/sock.h b/include/trace/events/sock.h
index b5310439536e0..ed43fe6d2705b 100644
--- a/include/trace/events/sock.h
+++ b/include/trace/events/sock.h
@@ -20,7 +20,8 @@
#define inet_protocol_names \
EM(IPPROTO_TCP) \
EM(IPPROTO_SCTP) \
- EMe(IPPROTO_MPTCP)
+ EM(IPPROTO_MPTCP) \
+ EMe(IPPROTO_QUIC)
#define tcp_state_names \
EM(TCP_ESTABLISHED) \
diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h
index ced0fc3c3aa53..e4072152f2e62 100644
--- a/include/uapi/linux/in.h
+++ b/include/uapi/linux/in.h
@@ -85,6 +85,8 @@ enum {
#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_SMC = 256, /* Shared Memory Communications */
#define IPPROTO_SMC IPPROTO_SMC
+ IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed Secure Transport */
+#define IPPROTO_QUIC IPPROTO_QUIC
IPPROTO_MPTCP = 262, /* Multipath TCP connection */
#define IPPROTO_MPTCP IPPROTO_MPTCP
IPPROTO_MAX
diff --git a/include/uapi/linux/quic.h b/include/uapi/linux/quic.h
new file mode 100644
index 0000000000000..ae37d6a7bc36f
--- /dev/null
+++ b/include/uapi/linux/quic.h
@@ -0,0 +1,241 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#ifndef _UAPI_LINUX_QUIC_H
+#define _UAPI_LINUX_QUIC_H
+
+#include <linux/types.h>
+#ifdef __KERNEL__
+#include <linux/socket.h>
+#else
+#include <sys/socket.h> /* for MSG_* flags */
+#endif
+
+/* NOTE: Structure descriptions are specified in:
+ * https://datatracker.ietf.org/doc/html/draft-lxin-quic-socket-apis
+ */
+
+/* Send or Receive Options APIs */
+enum quic_cmsg_type {
+ QUIC_STREAM_INFO,
+ QUIC_HANDSHAKE_INFO,
+};
+
+#define QUIC_STREAM_TYPE_SERVER_MASK 0x01
+#define QUIC_STREAM_TYPE_UNI_MASK 0x02
+#define QUIC_STREAM_TYPE_MASK 0x03
+
+enum quic_msg_flags {
+ /* flags for stream_flags */
+ MSG_QUIC_STREAM_NEW = MSG_SYN,
+ MSG_QUIC_STREAM_FIN = MSG_FIN,
+ MSG_QUIC_STREAM_UNI = MSG_CONFIRM,
+ MSG_QUIC_STREAM_DONTWAIT = MSG_EOR,
+ MSG_QUIC_STREAM_SNDBLOCK = MSG_ERRQUEUE,
+
+ /* extended flags for msg_flags */
+ MSG_QUIC_DATAGRAM = MSG_RST,
+ MSG_QUIC_NOTIFICATION = MSG_MORE,
+};
+
+enum quic_crypto_level {
+ QUIC_CRYPTO_APP,
+ QUIC_CRYPTO_INITIAL,
+ QUIC_CRYPTO_HANDSHAKE,
+ QUIC_CRYPTO_EARLY,
+ QUIC_CRYPTO_MAX,
+};
+
+struct quic_handshake_info {
+ __u8 crypto_level;
+};
+
+struct quic_stream_info {
+ __s64 stream_id;
+ __u32 stream_flags;
+};
+
+/* Socket Options APIs */
+#define QUIC_SOCKOPT_EVENT 0
+#define QUIC_SOCKOPT_STREAM_OPEN 1
+#define QUIC_SOCKOPT_STREAM_RESET 2
+#define QUIC_SOCKOPT_STREAM_STOP_SENDING 3
+#define QUIC_SOCKOPT_CONNECTION_ID 4
+#define QUIC_SOCKOPT_CONNECTION_CLOSE 5
+#define QUIC_SOCKOPT_CONNECTION_MIGRATION 6
+#define QUIC_SOCKOPT_KEY_UPDATE 7
+#define QUIC_SOCKOPT_TRANSPORT_PARAM 8
+#define QUIC_SOCKOPT_CONFIG 9
+#define QUIC_SOCKOPT_TOKEN 10
+#define QUIC_SOCKOPT_ALPN 11
+#define QUIC_SOCKOPT_SESSION_TICKET 12
+#define QUIC_SOCKOPT_CRYPTO_SECRET 13
+#define QUIC_SOCKOPT_TRANSPORT_PARAM_EXT 14
+
+#define QUIC_VERSION_V1 0x1
+#define QUIC_VERSION_V2 0x6b3343cf
+
+struct quic_transport_param {
+ __u8 remote;
+ __u8 disable_active_migration;
+ __u8 grease_quic_bit;
+ __u8 stateless_reset;
+ __u8 disable_1rtt_encryption;
+ __u8 disable_compatible_version;
+ __u8 active_connection_id_limit;
+ __u8 ack_delay_exponent;
+ __u16 max_datagram_frame_size;
+ __u16 max_udp_payload_size;
+ __u32 max_idle_timeout;
+ __u32 max_ack_delay;
+ __u16 max_streams_bidi;
+ __u16 max_streams_uni;
+ __u64 max_data;
+ __u64 max_stream_data_bidi_local;
+ __u64 max_stream_data_bidi_remote;
+ __u64 max_stream_data_uni;
+};
+
+struct quic_config {
+ __u32 version;
+ __u32 plpmtud_probe_interval;
+ __u32 initial_smoothed_rtt;
+ __u32 payload_cipher_type;
+ __u8 congestion_control_algo;
+ __u8 validate_peer_address;
+ __u8 stream_data_nodelay;
+ __u8 receive_session_ticket;
+ __u8 certificate_request;
+ __u8 reserved[3];
+ __u32 keepalive_probe_interval;
+};
+
+struct quic_crypto_secret {
+ __u8 send; /* send or recv */
+ __u8 level; /* crypto level */
+ __u16 reserved;
+ __u32 type; /* TLS_CIPHER_* */
+#define QUIC_CRYPTO_SECRET_BUFFER_SIZE 48
+ __u8 secret[QUIC_CRYPTO_SECRET_BUFFER_SIZE];
+};
+
+enum quic_cong_algo {
+ QUIC_CONG_ALG_RENO,
+ QUIC_CONG_ALG_MAX,
+};
+
+struct quic_errinfo {
+ __s64 stream_id;
+ __u32 errcode;
+};
+
+struct quic_connection_id_info {
+ __u8 dest;
+ __u8 reserved[3];
+ __u32 active;
+ __u32 prior_to;
+};
+
+struct quic_event_option {
+ __u8 type;
+ __u8 on;
+};
+
+/* Event APIs */
+enum quic_event_type {
+ QUIC_EVENT_NONE,
+ QUIC_EVENT_STREAM_UPDATE,
+ QUIC_EVENT_STREAM_MAX_DATA,
+ QUIC_EVENT_STREAM_MAX_STREAM,
+ QUIC_EVENT_CONNECTION_ID,
+ QUIC_EVENT_CONNECTION_CLOSE,
+ QUIC_EVENT_CONNECTION_MIGRATION,
+ QUIC_EVENT_KEY_UPDATE,
+ QUIC_EVENT_NEW_TOKEN,
+ QUIC_EVENT_NEW_SESSION_TICKET,
+ QUIC_EVENT_MAX,
+};
+
+enum {
+ QUIC_STREAM_SEND_STATE_READY,
+ QUIC_STREAM_SEND_STATE_SEND,
+ QUIC_STREAM_SEND_STATE_SENT,
+ QUIC_STREAM_SEND_STATE_RECVD,
+ QUIC_STREAM_SEND_STATE_RESET_SENT,
+ QUIC_STREAM_SEND_STATE_RESET_RECVD,
+
+ QUIC_STREAM_RECV_STATE_RECV,
+ QUIC_STREAM_RECV_STATE_SIZE_KNOWN,
+ QUIC_STREAM_RECV_STATE_RECVD,
+ QUIC_STREAM_RECV_STATE_READ,
+ QUIC_STREAM_RECV_STATE_RESET_RECVD,
+ QUIC_STREAM_RECV_STATE_RESET_READ,
+};
+
+struct quic_stream_update {
+ __s64 id;
+ __u8 state;
+ __u8 reserved[3];
+ __u32 errcode;
+ __u64 finalsz;
+};
+
+struct quic_stream_max_data {
+ __s64 id;
+ __u64 max_data;
+};
+
+struct quic_connection_close {
+ __u32 errcode;
+ __u8 frame;
+ __u8 reserved[3];
+#define QUIC_CLOSE_PHRASE_BUFFER_SIZE 64
+ __u8 phrase[QUIC_CLOSE_PHRASE_BUFFER_SIZE];
+};
+
+union quic_event {
+ struct quic_stream_update update;
+ struct quic_stream_max_data max_data;
+ struct quic_connection_close close;
+ struct quic_connection_id_info info;
+ __u64 max_stream;
+ __u8 local_migration;
+ __u8 key_update_phase;
+};
+
+enum {
+ QUIC_TRANSPORT_ERROR_NONE = 0x00,
+ QUIC_TRANSPORT_ERROR_INTERNAL = 0x01,
+ QUIC_TRANSPORT_ERROR_CONNECTION_REFUSED = 0x02,
+ QUIC_TRANSPORT_ERROR_FLOW_CONTROL = 0x03,
+ QUIC_TRANSPORT_ERROR_STREAM_LIMIT = 0x04,
+ QUIC_TRANSPORT_ERROR_STREAM_STATE = 0x05,
+ QUIC_TRANSPORT_ERROR_FINAL_SIZE = 0x06,
+ QUIC_TRANSPORT_ERROR_FRAME_ENCODING = 0x07,
+ QUIC_TRANSPORT_ERROR_TRANSPORT_PARAM = 0x08,
+ QUIC_TRANSPORT_ERROR_CONNECTION_ID_LIMIT = 0x09,
+ QUIC_TRANSPORT_ERROR_PROTOCOL_VIOLATION = 0x0a,
+ QUIC_TRANSPORT_ERROR_INVALID_TOKEN = 0x0b,
+ QUIC_TRANSPORT_ERROR_APPLICATION = 0x0c,
+ QUIC_TRANSPORT_ERROR_CRYPTO_BUF_EXCEEDED = 0x0d,
+ QUIC_TRANSPORT_ERROR_KEY_UPDATE = 0x0e,
+ QUIC_TRANSPORT_ERROR_AEAD_LIMIT_REACHED = 0x0f,
+ QUIC_TRANSPORT_ERROR_NO_VIABLE_PATH = 0x10,
+ QUIC_TRANSPORT_ERROR_VERSION_NEGOTIATION = 0x11,
+
+ /* The cryptographic handshake failed. A range of 256 values is reserved
+ * for carrying error codes specific to the cryptographic handshake that
+ * is used. Codes for errors occurring when TLS is used for the
+ * cryptographic handshake are described in Section 4.8 of [QUIC-TLS].
+ */
+ QUIC_TRANSPORT_ERROR_CRYPTO = 0x0100,
+};
+
+#endif /* _UAPI_LINUX_QUIC_H */
diff --git a/net/Kconfig b/net/Kconfig
index e384773935515..ab0d82e108ea9 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -251,6 +251,7 @@ source "net/bridge/netfilter/Kconfig"
endif # if NETFILTER
+source "net/quic/Kconfig"
source "net/sctp/Kconfig"
source "net/rds/Kconfig"
source "net/tipc/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index 5b2dd7f07a85e..e2b25ede017d7 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_PHONET) += phonet/
ifneq ($(CONFIG_VLAN_8021Q),)
obj-y += 8021q/
endif
+obj-$(CONFIG_IP_QUIC) += quic/
obj-$(CONFIG_IP_SCTP) += sctp/
obj-$(CONFIG_RDS) += rds/
obj-$(CONFIG_WIRELESS) += wireless/
diff --git a/net/quic/Kconfig b/net/quic/Kconfig
new file mode 100644
index 0000000000000..602e6d89eafdd
--- /dev/null
+++ b/net/quic/Kconfig
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# QUIC configuration
+#
+
+menuconfig IP_QUIC
+ tristate "QUIC: A UDP-Based Multiplexed Secure Transport (Experimental)"
+ depends on INET
+ depends on IPV6
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_AES
+ select CRYPTO_GCM
+ select CRYPTO_CCM
+ select CRYPTO_CHACHA20POLY1305
+ select NET_UDP_TUNNEL
+ default n
+ help
+ QUIC: A UDP-Based Multiplexed and Secure Transport
+
+ From rfc9000 <https://www.rfc-editor.org/rfc/rfc9000.html>.
+
+ QUIC provides applications with flow-controlled streams for structured
+ communication, low-latency connection establishment, and network path
+ migration. QUIC includes security measures that ensure
+ confidentiality, integrity, and availability in a range of deployment
+ circumstances. Accompanying documents describe the integration of
+ TLS for key negotiation, loss detection, and an exemplary congestion
+ control algorithm.
+
+ To compile this protocol support as a module, choose M here: the
+ module will be called quic. Debug messages are handled by the
+ kernel's dynamic debugging framework.
+
+ If in doubt, say N.
diff --git a/net/quic/Makefile b/net/quic/Makefile
new file mode 100644
index 0000000000000..0f903f4a7ff1f
--- /dev/null
+++ b/net/quic/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Makefile for QUIC support code.
+#
+
+obj-$(CONFIG_IP_QUIC) += quic.o
+
+quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o \
+ cong.o pnspace.o crypto.o timer.o packet.o
diff --git a/net/quic/common.c b/net/quic/common.c
new file mode 100644
index 0000000000000..51d71b6733c7d
--- /dev/null
+++ b/net/quic/common.c
@@ -0,0 +1,565 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <linux/unaligned.h>
+#include <net/netns/hash.h>
+#include <linux/vmalloc.h>
+#include <linux/jhash.h>
+
+#include "common.h"
+
+#define QUIC_VARINT_2BYTE_PREFIX 0x40
+#define QUIC_VARINT_4BYTE_PREFIX 0x80
+#define QUIC_VARINT_8BYTE_PREFIX 0xc0
+
+#define QUIC_VARINT_LENGTH(p) BIT((*(p)) >> 6)
+
+struct quic_hashinfo {
+ struct quic_shash_table shash; /* Source connection ID hashtable */
+ struct quic_shash_table lhash; /* Listening sock hashtable */
+ struct quic_shash_table chash; /* Connection sock hashtable */
+ struct quic_uhash_table uhash; /* UDP sock hashtable */
+};
+
+static struct quic_hashinfo quic_hashinfo;
+
+u32 quic_sock_hash_size(void)
+{
+ return quic_hashinfo.chash.size;
+}
+
+u32 quic_sock_hash(struct net *net, union quic_addr *s, union quic_addr *d)
+{
+ u32 ports = ((__force u32)s->v4.sin_port) << 16 |
+ (__force u32)d->v4.sin_port;
+ u32 saddr = (s->sa.sa_family == AF_INET6) ?
+ jhash(&s->v6.sin6_addr, 16, 0) :
+ (__force u32)s->v4.sin_addr.s_addr;
+ u32 daddr = (d->sa.sa_family == AF_INET6) ?
+ jhash(&d->v6.sin6_addr, 16, 0) :
+ (__force u32)d->v4.sin_addr.s_addr;
+ u32 hash = jhash_3words(saddr, daddr, ports, net_hash_mix(net));
+
+ return hash & (quic_sock_hash_size() - 1);
+}
+
+struct quic_shash_head *quic_sock_head(u32 hash)
+{
+ return &quic_hashinfo.chash.hash[hash];
+}
+
+u32 quic_listen_sock_hash_size(void)
+{
+ return quic_hashinfo.lhash.size;
+}
+
+u32 quic_listen_sock_hash(struct net *net, u16 port)
+{
+ u32 hash = jhash_1word((__force u32)port, net_hash_mix(net));
+
+ return hash & (quic_listen_sock_hash_size() - 1);
+}
+
+struct quic_shash_head *quic_listen_sock_head(u32 hash)
+{
+ return &quic_hashinfo.lhash.hash[hash];
+}
+
+struct quic_shash_head *quic_source_conn_id_head(struct net *net, u8 *scid,
+ u32 len)
+{
+ u32 hash = jhash_1word(jhash(scid, len, 0), net_hash_mix(net));
+ struct quic_shash_table *ht = &quic_hashinfo.shash;
+
+ return &ht->hash[hash & (ht->size - 1)];
+}
+
+struct quic_uhash_head *quic_udp_sock_head(struct net *net, u16 port)
+{
+ u32 hash = jhash_1word((__force u32)port, net_hash_mix(net));
+ struct quic_uhash_table *ht = &quic_hashinfo.uhash;
+
+ return &ht->hash[hash & (ht->size - 1)];
+}
+
+u32 quic_addr_hash(struct net *net, union quic_addr *a)
+{
+ u32 addr = (a->sa.sa_family == AF_INET6) ?
+ jhash(&a->v6.sin6_addr, 16, 0) :
+ (__force u32)a->v4.sin_addr.s_addr;
+
+ return jhash_2words(addr, (__force u32)a->v4.sin_port,
+ net_hash_mix(net));
+}
+
+void quic_hash_tables_destroy(void)
+{
+ vfree(quic_hashinfo.shash.hash);
+ vfree(quic_hashinfo.lhash.hash);
+ vfree(quic_hashinfo.chash.hash);
+ vfree(quic_hashinfo.uhash.hash);
+}
+
+static int quic_shash_table_init(struct quic_shash_table *ht, u32 size)
+{
+ int i;
+
+ ht->hash = vmalloc_array(size, sizeof(struct quic_shash_head));
+ if (!ht->hash)
+ return -ENOMEM;
+
+ ht->size = size;
+ for (i = 0; i < ht->size; i++) {
+ spin_lock_init(&ht->hash[i].lock);
+ INIT_HLIST_NULLS_HEAD(&ht->hash[i].head, i);
+ }
+ return 0;
+}
+
+static int quic_uhash_table_init(struct quic_uhash_table *ht, u32 size)
+{
+ int i;
+
+ ht->hash = vmalloc_array(size, sizeof(struct quic_uhash_head));
+ if (!ht->hash)
+ return -ENOMEM;
+
+ ht->size = size;
+ for (i = 0; i < ht->size; i++) {
+ mutex_init(&ht->hash[i].lock);
+ INIT_HLIST_HEAD(&ht->hash[i].head);
+ }
+ return 0;
+}
+
+int quic_hash_tables_init(void)
+{
+ unsigned long nr_pages = totalram_pages();
+ unsigned long limit;
+ u32 size;
+ int err;
+
+ /* Scale hash table size based on system memory, similar to SCTP. */
+ if (nr_pages >= (128 * 1024))
+ limit = nr_pages >> (22 - PAGE_SHIFT);
+ else
+ limit = nr_pages >> (24 - PAGE_SHIFT);
+
+ limit = roundup_pow_of_two(limit ?: 1);
+
+ /* Source connection ID table (fast lookup, larger size) */
+ size = min_t(unsigned long, limit, 64 * 1024UL);
+ err = quic_shash_table_init(&quic_hashinfo.shash, size);
+ if (err)
+ goto err;
+ size = min_t(unsigned long, limit, 16 * 1024UL);
+ err = quic_shash_table_init(&quic_hashinfo.lhash, size);
+ if (err)
+ goto err;
+ err = quic_shash_table_init(&quic_hashinfo.chash, size);
+ if (err)
+ goto err;
+ err = quic_uhash_table_init(&quic_hashinfo.uhash, size);
+ if (err)
+ goto err;
+ return 0;
+err:
+ quic_hash_tables_destroy();
+ return err;
+}
+
+/* Returns the number of bytes required to encode a QUIC variable-length
+ * integer.
+ */
+u8 quic_var_len(u64 n)
+{
+ if (n <= QUIC_VARINT_1BYTE_MAX)
+ return 1;
+ if (n <= QUIC_VARINT_2BYTE_MAX)
+ return 2;
+ if (n <= QUIC_VARINT_4BYTE_MAX)
+ return 4;
+ return 8;
+}
+
+/* Decodes a QUIC variable-length integer from a buffer. */
+u8 quic_get_var(u8 **pp, u32 *plen, u64 *val)
+{
+ u8 *p = *pp, len;
+ u64 v = 0;
+
+ if (!*plen)
+ return 0;
+
+ len = QUIC_VARINT_LENGTH(p);
+ if (*plen < len)
+ return 0;
+
+ switch (len) {
+ case 1:
+ v = *p;
+ break;
+ case 2:
+ v = get_unaligned_be16(p) & QUIC_VARINT_2BYTE_MAX;
+ break;
+ case 4:
+ v = get_unaligned_be32(p) & QUIC_VARINT_4BYTE_MAX;
+ break;
+ case 8:
+ v = get_unaligned_be64(p) & QUIC_VARINT_8BYTE_MAX;
+ break;
+ default:
+ return 0;
+ }
+
+ *plen -= len;
+ *pp = p + len;
+ *val = v;
+ return len;
+}
+
+/* Reads a fixed-length integer from the buffer. */
+u32 quic_get_int(u8 **pp, u32 *plen, u64 *val, u32 len)
+{
+ u8 *p = *pp;
+ u64 v = 0;
+
+ if (*plen < len)
+ return 0;
+
+ switch (len) {
+ case 1:
+ v = *p;
+ break;
+ case 2:
+ v = get_unaligned_be16(p);
+ break;
+ case 3:
+ v = get_unaligned_be24(p);
+ break;
+ case 4:
+ v = get_unaligned_be32(p);
+ break;
+ case 8:
+ v = get_unaligned_be64(p);
+ break;
+ default:
+ return 0;
+ }
+ *plen -= len;
+ *pp = p + len;
+ *val = v;
+ return len;
+}
+
+u32 quic_get_data(u8 **pp, u32 *plen, u8 *data, u32 len)
+{
+ if (*plen < len)
+ return 0;
+
+ memcpy(data, *pp, len);
+ *pp += len;
+ *plen -= len;
+
+ return len;
+}
+
+/* Encodes a value into the QUIC variable-length integer format. */
+u8 *quic_put_var(u8 *p, u64 num)
+{
+ if (num <= QUIC_VARINT_1BYTE_MAX) {
+ *p++ = (u8)num;
+ return p;
+ }
+ if (num <= QUIC_VARINT_2BYTE_MAX) {
+ put_unaligned_be16((u16)num, p);
+ *p |= QUIC_VARINT_2BYTE_PREFIX;
+ return p + 2;
+ }
+ if (num <= QUIC_VARINT_4BYTE_MAX) {
+ put_unaligned_be32((u32)num, p);
+ *p |= QUIC_VARINT_4BYTE_PREFIX;
+ return p + 4;
+ }
+ put_unaligned_be64(num, p);
+ *p |= QUIC_VARINT_8BYTE_PREFIX;
+ return p + 8;
+}
+
+/* Writes a fixed-length integer to the buffer in network byte order. */
+u8 *quic_put_int(u8 *p, u64 num, u8 len)
+{
+ switch (len) {
+ case 1:
+ *p++ = (u8)num;
+ return p;
+ case 2:
+ put_unaligned_be16((u16)num, p);
+ return p + 2;
+ case 4:
+ put_unaligned_be32((u32)num, p);
+ return p + 4;
+ case 8:
+ put_unaligned_be64(num, p);
+ return p + 8;
+ default:
+ return NULL;
+ }
+}
+
+/* Encodes a value as a variable-length integer with explicit length. */
+u8 *quic_put_varint(u8 *p, u64 num, u8 len)
+{
+ switch (len) {
+ case 1:
+ *p++ = (u8)num;
+ return p;
+ case 2:
+ put_unaligned_be16((u16)num, p);
+ *p |= QUIC_VARINT_2BYTE_PREFIX;
+ return p + 2;
+ case 4:
+ put_unaligned_be32((u32)num, p);
+ *p |= QUIC_VARINT_4BYTE_PREFIX;
+ return p + 4;
+ default:
+ return NULL;
+ }
+}
+
+u8 *quic_put_data(u8 *p, u8 *data, u32 len)
+{
+ if (!len)
+ return p;
+
+ memcpy(p, data, len);
+ return p + len;
+}
+
+/* Writes a transport parameter as two varints: ID and value length, followed
+ * by value.
+ */
+u8 *quic_put_param(u8 *p, u16 id, u64 value)
+{
+ p = quic_put_var(p, id);
+ p = quic_put_var(p, quic_var_len(value));
+ return quic_put_var(p, value);
+}
+
+/* Reads a QUIC transport parameter value. */
+u8 quic_get_param(u64 *pdest, u8 **pp, u32 *plen)
+{
+ u64 valuelen;
+
+ if (!quic_get_var(pp, plen, &valuelen))
+ return 0;
+
+ if (*plen < valuelen)
+ return 0;
+
+ if (quic_get_var(pp, plen, pdest) != valuelen)
+ return 0;
+
+ return (u8)valuelen;
+}
+
+/* rfc9000#section-a.3: DecodePacketNumber()
+ *
+ * Reconstructs the full packet number from a truncated one.
+ */
+s64 quic_get_num(s64 max_pkt_num, s64 pkt_num, u32 n)
+{
+ s64 expected = max_pkt_num + 1;
+ s64 win = BIT_ULL(n * 8);
+ s64 hwin = win / 2;
+ s64 mask = win - 1;
+ s64 cand;
+
+ cand = (expected & ~mask) | pkt_num;
+ if (cand <= expected - hwin && cand < BIT_ULL(QUIC_PN_BITS) - win)
+ return cand + win;
+ if (cand > expected + hwin && cand >= win)
+ return cand - win;
+ return cand;
+}
+
+int quic_data_dup(struct quic_data *to, u8 *data, u32 len, gfp_t gfp)
+{
+ if (!len) {
+ quic_data_free(to);
+ return 0;
+ }
+
+ data = kmemdup(data, len, gfp);
+ if (!data)
+ return -ENOMEM;
+
+ kfree(to->data);
+ to->data = data;
+ to->len = len;
+ return 0;
+}
+
+int quic_data_append(struct quic_data *to, u8 *data, u32 len, gfp_t gfp)
+{
+ u8 *p;
+
+ if (!len)
+ return 0;
+
+ if (to->len > U32_MAX - len)
+ return -EOVERFLOW;
+
+ p = kmalloc(to->len + len, gfp);
+ if (!p)
+ return -ENOMEM;
+ p = quic_put_data(p, to->data, to->len);
+ p = quic_put_data(p, data, len);
+
+ kfree(to->data);
+ to->len = to->len + len;
+ to->data = p - to->len;
+ return 0;
+}
+
+/* Check whether 'd2' is equal to any element inside the list 'd1'.
+ *
+ * 'd1' is assumed to be a sequence of length-prefixed elements. Each element
+ * is compared to 'd2' using 'quic_data_cmp()'.
+ *
+ * Returns true if a match is found, false otherwise.
+ */
+bool quic_data_has(struct quic_data *d1, struct quic_data *d2)
+{
+ struct quic_data d;
+ u64 length;
+ u32 len;
+ u8 *p;
+
+ for (p = d1->data, len = d1->len; len; len -= length, p += length) {
+ if (!quic_get_int(&p, &len, &length, 1) || len < length)
+ return false;
+ quic_data(&d, p, length);
+ if (!quic_data_cmp(&d, d2))
+ return true;
+ }
+ return false;
+}
+
+/* Check if any element of 'd1' is present in the list 'd2'.
+ *
+ * Iterates through each element in 'd1', and uses 'quic_data_has()' to check
+ * for its presence in 'd2'.
+ *
+ * Returns true if any match is found, false otherwise.
+ */
+bool quic_data_match(struct quic_data *d1, struct quic_data *d2)
+{
+ struct quic_data d;
+ u64 length;
+ u32 len;
+ u8 *p;
+
+ for (p = d1->data, len = d1->len; len; len -= length, p += length) {
+ if (!quic_get_int(&p, &len, &length, 1) || len < length)
+ return false;
+ quic_data(&d, p, length);
+ if (quic_data_has(d2, &d))
+ return true;
+ }
+ return false;
+}
+
+/* Serialize a list of 'quic_data' elements into a comma-separated string.
+ *
+ * Each element in 'from' is length-prefixed. This function copies their raw
+ * content into the output buffer 'to', inserting commas in between. The
+ * resulting string length is written to '*plen'.
+ */
+int quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from)
+{
+ u32 remlen = *plen;
+ struct quic_data d;
+ u8 *data = to, *p;
+ u64 length;
+ u32 len;
+
+ p = from->data;
+ len = from->len;
+ while (len) {
+ if (!quic_get_int(&p, &len, &length, 1) || len < length)
+ return -EINVAL;
+
+ quic_data(&d, p, length);
+ if (d.len > remlen)
+ return -EOVERFLOW;
+
+ data = quic_put_data(data, d.data, d.len);
+ remlen -= d.len;
+ p += d.len;
+ len -= d.len;
+ if (len) {
+ if (!remlen)
+ return -EOVERFLOW;
+ data = quic_put_int(data, ',', 1);
+ remlen--;
+ }
+ }
+ *plen = data - to;
+ return 0;
+}
+
+/* Parse a comma-separated string into a 'quic_data' list format.
+ *
+ * Each comma-separated token is turned into a length-prefixed element. The
+ * first byte of each element stores the length. Elements are stored in
+ * 'to->data', and 'to->len' is updated.
+ */
+int quic_data_from_string(struct quic_data *to, u8 *from, u32 len)
+{
+ u32 remlen = to->len;
+ struct quic_data d;
+ u8 *p = to->data;
+
+ to->len = 0;
+ while (len) {
+ while (len && *from == ' ') {
+ from++;
+ len--;
+ }
+ if (!len)
+ break;
+ if (!remlen)
+ return -EOVERFLOW;
+ d.data = p++;
+ d.len = 0;
+ remlen--;
+ while (len) {
+ if (*from == ',') {
+ from++;
+ len--;
+ break;
+ }
+ if (!remlen)
+ return -EOVERFLOW;
+ *p++ = *from++;
+ len--;
+ d.len++;
+ remlen--;
+ }
+ if (d.len > U8_MAX)
+ return -EINVAL;
+ *d.data = (u8)(d.len);
+ to->len += d.len + 1;
+ }
+ return 0;
+}
diff --git a/net/quic/common.h b/net/quic/common.h
new file mode 100644
index 0000000000000..6cb2b1b89cfb1
--- /dev/null
+++ b/net/quic/common.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/net_namespace.h>
+
+#define QUIC_MAX_ACK_DELAY (16384 * 1000)
+#define QUIC_DEF_ACK_DELAY 25000
+
+#define QUIC_STREAM_BIT_FIN 0x01
+#define QUIC_STREAM_BIT_LEN 0x02
+#define QUIC_STREAM_BIT_OFF 0x04
+#define QUIC_STREAM_BIT_MASK 0x08
+
+#define QUIC_CONN_ID_MAX_LEN 20
+#define QUIC_CONN_ID_DEF_LEN 8
+
+#define QUIC_PN_MAX_LEN 4 /* For encoded packet number */
+#define QUIC_PN_BITS 62
+#define QUIC_PN_MAX (BIT_ULL(QUIC_PN_BITS) - 1)
+
+#define QUIC_VARINT_1BYTE_MAX 0x3fULL
+#define QUIC_VARINT_2BYTE_MAX 0x3fffULL
+#define QUIC_VARINT_4BYTE_MAX 0x3fffffffULL
+#define QUIC_VARINT_8BYTE_MAX 0x3fffffffffffffffULL
+
+struct quic_conn_id {
+ u8 data[QUIC_CONN_ID_MAX_LEN];
+ u8 len;
+};
+
+static inline void quic_conn_id_update(struct quic_conn_id *conn_id, u8 *data,
+ u32 len)
+{
+ /* The caller must ensure len does not exceed QUIC_CONN_ID_MAX_LEN. */
+ if (WARN_ON_ONCE(len > QUIC_CONN_ID_MAX_LEN))
+ return;
+ memcpy(conn_id->data, data, len);
+ conn_id->len = (u8)len;
+}
+
+struct quic_skb_cb {
+ /* Callback and temporary context when encryption/decryption completes
+ * in async mode
+ */
+ void (*crypto_done)(struct sk_buff *skb, int err);
+ void *crypto_ctx;
+ union {
+ struct sk_buff *last; /* Last packet in bundle on TX */
+ u64 time; /* Arrival timestamp in UDP tunnel on RX */
+ };
+ s64 number; /* Parsed packet number, or the largest previously seen */
+ u32 seqno; /* Dest connection ID number on RX */
+ u16 length; /* Payload length + packet number length */
+
+ u16 number_offset; /* Offset of packet number field */
+ u8 number_len; /* Length of the packet number field */
+ u8 level; /* Encryption level: Initial, Handshake, App, or Early */
+
+ u16 errcode; /* Error code on packet processing failure */
+ u8 errframe; /* Frame type causing packet processing failure */
+
+ u8 key_update:1; /* Key update triggered by this packet */
+ u8 key_phase:1; /* Key phase used (0 or 1) */
+ u8 backlog:1; /* Enqueued into backlog list */
+ u8 resume:1; /* Crypto already processed (encrypted or decrypted) */
+ u8 path:1; /* Packet arrived from a new or migrating path */
+ u8 sync:1; /* Force synchronous crypto (process context only) */
+ u8 ecn:2; /* ECN marking used on TX */
+};
+
+#define QUIC_SKB_CB(skb) ((struct quic_skb_cb *)&((skb)->cb[0]))
+
+struct quichdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 pnl:2,
+ key:1,
+ reserved:2,
+ spin:1,
+ fixed:1,
+ form:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 form:1,
+ fixed:1,
+ spin:1,
+ reserved:2,
+ key:1,
+ pnl:2;
+#endif
+};
+
+static inline struct quichdr *quic_hdr(struct sk_buff *skb)
+{
+ return (struct quichdr *)skb->data;
+}
+
+struct quichshdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 pnl:2,
+ reserved:2,
+ type:2,
+ fixed:1,
+ form:1;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 form:1,
+ fixed:1,
+ type:2,
+ reserved:2,
+ pnl:2;
+#endif
+};
+
+static inline struct quichshdr *quic_hshdr(struct sk_buff *skb)
+{
+ return (struct quichshdr *)skb->data;
+}
+
+union quic_addr {
+ struct sockaddr_in6 v6;
+ struct sockaddr_in v4;
+ struct sockaddr sa;
+};
+
+static inline union quic_addr *quic_addr(const void *addr)
+{
+ return (union quic_addr *)addr;
+}
+
+struct quic_shash_head {
+ struct hlist_nulls_head head;
+ spinlock_t lock; /* Protects 'head' in atomic context */
+};
+
+struct quic_shash_table {
+ struct quic_shash_head *hash;
+ u32 size;
+};
+
+struct quic_uhash_head {
+ struct hlist_head head;
+ struct mutex lock; /* Protects 'head' in process context */
+};
+
+struct quic_uhash_table {
+ struct quic_uhash_head *hash;
+ u32 size;
+};
+
+struct quic_data {
+ u8 *data;
+ u32 len;
+};
+
+static inline struct quic_data *quic_data(struct quic_data *d, u8 *data,
+ u32 len)
+{
+ d->data = data;
+ d->len = len;
+ return d;
+}
+
+static inline int quic_data_cmp(struct quic_data *d1, struct quic_data *d2)
+{
+ return d1->len != d2->len || memcmp(d1->data, d2->data, d1->len);
+}
+
+static inline void quic_data_free(struct quic_data *d)
+{
+ kfree(d->data);
+ d->data = NULL;
+ d->len = 0;
+}
+
+static inline u64 quic_ktime_get_us(void)
+{
+ return ktime_to_us(ktime_get());
+}
+
+u32 quic_sock_hash(struct net *net, union quic_addr *s, union quic_addr *d);
+struct quic_shash_head *quic_sock_head(u32 hash);
+u32 quic_sock_hash_size(void);
+
+u32 quic_listen_sock_hash(struct net *net, u16 port);
+struct quic_shash_head *quic_listen_sock_head(u32 hash);
+u32 quic_listen_sock_hash_size(void);
+
+struct quic_shash_head *quic_source_conn_id_head(struct net *net, u8 *scid,
+ u32 len);
+struct quic_uhash_head *quic_udp_sock_head(struct net *net, u16 port);
+u32 quic_addr_hash(struct net *net, union quic_addr *a);
+
+void quic_hash_tables_destroy(void);
+int quic_hash_tables_init(void);
+
+u32 quic_get_data(u8 **pp, u32 *plen, u8 *data, u32 len);
+u32 quic_get_int(u8 **pp, u32 *plen, u64 *val, u32 len);
+s64 quic_get_num(s64 max_pkt_num, s64 pkt_num, u32 n);
+u8 quic_get_param(u64 *pdest, u8 **pp, u32 *plen);
+u8 quic_get_var(u8 **pp, u32 *plen, u64 *val);
+u8 quic_var_len(u64 n);
+
+u8 *quic_put_param(u8 *p, u16 id, u64 value);
+u8 *quic_put_data(u8 *p, u8 *data, u32 len);
+u8 *quic_put_varint(u8 *p, u64 num, u8 len);
+u8 *quic_put_int(u8 *p, u64 num, u8 len);
+u8 *quic_put_var(u8 *p, u64 num);
+
+int quic_data_from_string(struct quic_data *to, u8 *from, u32 len);
+int quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from);
+
+int quic_data_append(struct quic_data *to, u8 *data, u32 len, gfp_t gfp);
+int quic_data_dup(struct quic_data *to, u8 *data, u32 len, gfp_t gfp);
+bool quic_data_match(struct quic_data *d1, struct quic_data *d2);
+bool quic_data_has(struct quic_data *d1, struct quic_data *d2);
diff --git a/net/quic/cong.c b/net/quic/cong.c
new file mode 100644
index 0000000000000..78f28bbdfb374
--- /dev/null
+++ b/net/quic/cong.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <linux/quic.h>
+
+#include "common.h"
+#include "cong.h"
+
+/* NEW RENO APIs */
+static void quic_reno_handle_packet_lost(struct quic_cong *cong)
+{
+ switch (cong->state) {
+ case QUIC_CONG_SLOW_START:
+ pr_debug("%s: slow_start -> recovery, cwnd: %u, ssth: %u\n",
+ __func__, cong->window, cong->ssthresh);
+ break;
+ case QUIC_CONG_RECOVERY_PERIOD:
+ return;
+ case QUIC_CONG_CONGESTION_AVOIDANCE:
+ pr_debug("%s: cong_avoid -> recovery, cwnd: %u, ssth: %u\n",
+ __func__, cong->window, cong->ssthresh);
+ break;
+ default:
+ pr_debug("%s: wrong congestion state: %d\n", __func__,
+ cong->state);
+ return;
+ }
+
+ cong->recovery_time = cong->time;
+ cong->state = QUIC_CONG_RECOVERY_PERIOD;
+ cong->ssthresh = max(cong->window >> 1U, cong->min_window);
+ cong->window = cong->ssthresh;
+}
+
+static void quic_reno_on_packet_lost(struct quic_cong *cong, u64 time,
+ u32 bytes, s64 number)
+{
+ quic_reno_handle_packet_lost(cong);
+}
+
+static void quic_reno_on_packet_acked(struct quic_cong *cong, u64 time,
+ u32 bytes, s64 number)
+{
+ u64 new_window;
+
+ switch (cong->state) {
+ case QUIC_CONG_SLOW_START:
+ new_window = (u64)cong->window + bytes;
+ cong->window = min_t(u64, new_window, cong->max_window);
+ if (cong->window < cong->ssthresh)
+ break;
+ cong->state = QUIC_CONG_CONGESTION_AVOIDANCE;
+ pr_debug("%s: slow_start -> cong_avoid, cwnd: %u, ssth: %u\n",
+ __func__, cong->window, cong->ssthresh);
+ break;
+ case QUIC_CONG_RECOVERY_PERIOD:
+ if (cong->recovery_time >= time)
+ break;
+ cong->state = QUIC_CONG_CONGESTION_AVOIDANCE;
+ pr_debug("%s: recovery -> cong_avoid, cwnd: %u, ssth: %u\n",
+ __func__, cong->window, cong->ssthresh);
+ fallthrough;
+ case QUIC_CONG_CONGESTION_AVOIDANCE:
+ /* cong->window is never zero; it is initialized by
+ * quic_packet_route() during connect/accept.
+ */
+ new_window = div64_ul((u64)cong->mss * bytes, cong->window) +
+ cong->window;
+ cong->window = min_t(u64, new_window, cong->max_window);
+ break;
+ default:
+ pr_debug("%s: wrong congestion state: %d\n", __func__,
+ cong->state);
+ return;
+ }
+}
+
+static void quic_reno_on_process_ecn(struct quic_cong *cong)
+{
+ quic_reno_handle_packet_lost(cong);
+}
+
+static void quic_reno_on_init(struct quic_cong *cong)
+{
+}
+
+static const struct quic_cong_ops quic_congs[] = {
+ { /* QUIC_CONG_ALG_RENO */
+ .on_packet_acked = quic_reno_on_packet_acked,
+ .on_packet_lost = quic_reno_on_packet_lost,
+ .on_process_ecn = quic_reno_on_process_ecn,
+ .on_init = quic_reno_on_init,
+ },
+};
+
+static bool quic_cong_check_persistent_congestion(struct quic_cong *cong,
+ u64 time)
+{
+ u32 ssthresh;
+
+ time -= cong->pc_start_time;
+
+ /* rfc9002#section-7.6.1:
+ * (smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay) *
+ * kPersistentCongestionThreshold
+ */
+ ssthresh = cong->smoothed_rtt +
+ max(4 * cong->rttvar, QUIC_KGRANULARITY);
+ ssthresh = (ssthresh + cong->max_ack_delay) *
+ QUIC_KPERSISTENT_CONGESTION_THRESHOLD;
+
+ return time > ssthresh;
+}
+
+/* COMMON APIs */
+void quic_cong_on_packet_lost(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number)
+{
+ if (cong->pc_start_time && time > cong->pc_start_time &&
+ quic_cong_check_persistent_congestion(cong, time)) {
+ cong->pc_start_time = 0;
+ cong->min_rtt_valid = 0;
+ cong->window = cong->min_window;
+ cong->state = QUIC_CONG_SLOW_START;
+ return;
+ }
+
+ if (!cong->pc_start_time && cong->is_rtt_set)
+ cong->pc_start_time = time;
+
+ cong->ops->on_packet_lost(cong, time, bytes, number);
+}
+
+void quic_cong_on_packet_acked(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number)
+{
+ /* When a packet is acked, if time - cong->pc_start_time <= duration
+ * threshold, it means the acked packet was sent within the persistent
+ * congestion window.
+ *
+ * This breaks the condition in rfc9002#section-7.6.2:
+ *
+ * - across all packet number spaces, none of the packets sent between
+ * the send times of these two packets are acknowledged;
+ *
+ * so pc_start_time is reset to 0.
+ */
+ if (cong->pc_start_time && time > cong->pc_start_time &&
+ !quic_cong_check_persistent_congestion(cong, time))
+ cong->pc_start_time = 0;
+
+ cong->ops->on_packet_acked(cong, time, bytes, number);
+}
+
+void quic_cong_on_process_ecn(struct quic_cong *cong)
+{
+ cong->ops->on_process_ecn(cong);
+}
+
+/* Update Probe Timeout (PTO) and loss detection delay based on RTT stats. */
+static void quic_cong_pto_update(struct quic_cong *cong)
+{
+ u32 loss_delay;
+
+ /* rfc9002#section-6.2.1:
+ * PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay
+ *
+ * Calculate the base PTO here, excluding max_ack_delay. max_ack_delay
+ * is added when calculating the PTO for the App packet number space.
+ */
+ cong->pto = cong->smoothed_rtt +
+ max(4 * cong->rttvar, QUIC_KGRANULARITY);
+
+ /* rfc9002#section-6.1.2:
+ * max(kTimeThreshold * max(smoothed_rtt, latest_rtt), kGranularity)
+ */
+ loss_delay = QUIC_KTIME_THRESHOLD(max(cong->smoothed_rtt,
+ cong->latest_rtt));
+ cong->loss_delay = max(loss_delay, QUIC_KGRANULARITY);
+
+ pr_debug("%s: update pto: %u\n", __func__, cong->pto);
+}
+
+/* Update pacing timestamp after sending 'bytes' bytes.
+ *
+ * This function tracks when the next packet is allowed to be sent based on
+ * pacing rate.
+ */
+static void quic_cong_update_pacing_time(struct quic_cong *cong, u32 bytes)
+{
+ u64 prior_time, credit, len_ns, rate = cong->pacing_rate;
+
+ if (!rate)
+ return;
+
+ prior_time = cong->pacing_time;
+ cong->pacing_time = max(cong->pacing_time, ktime_get_ns());
+ credit = cong->pacing_time - prior_time;
+
+ /* take into account OS jitter */
+ len_ns = div64_u64((u64)bytes * NSEC_PER_SEC, rate);
+ len_ns -= min_t(u64, len_ns / 2, credit);
+ cong->pacing_time += len_ns;
+}
+
+/* Compute and update the pacing rate based on congestion window and smoothed
+ * RTT.
+ */
+static void quic_cong_pace_update(struct quic_cong *cong, u64 max_rate)
+{
+ u64 rate;
+
+ if (unlikely(!cong->smoothed_rtt))
+ return;
+
+ /* rate = N * congestion_window / smoothed_rtt */
+ rate = div64_ul((u64)cong->window * USEC_PER_SEC * 2,
+ cong->smoothed_rtt);
+
+ cong->pacing_rate = min_t(u64, rate, max_rate);
+ pr_debug("%s: update pacing rate: %llu, max rate: %llu, srtt: %u\n",
+ __func__, cong->pacing_rate, max_rate, cong->smoothed_rtt);
+}
+
+void quic_cong_on_packet_sent(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number)
+{
+ if (!bytes)
+ return;
+ if (cong->ops->on_packet_sent)
+ cong->ops->on_packet_sent(cong, time, bytes, number);
+ quic_cong_update_pacing_time(cong, bytes);
+}
+
+void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u64 max_rate)
+{
+ if (!bytes)
+ return;
+ if (cong->ops->on_ack_recv)
+ cong->ops->on_ack_recv(cong, bytes, max_rate);
+ quic_cong_pace_update(cong, max_rate);
+}
+
+/* rfc9002#section-5: Estimating the Round-Trip Time */
+void quic_cong_rtt_update(struct quic_cong *cong, u64 time, u32 ack_delay)
+{
+ u32 adjusted_rtt, rttvar_sample;
+
+ /* Ignore RTT sample if ACK delay is suspiciously large. */
+ if (ack_delay > cong->max_ack_delay * 2 ||
+ cong->time - time > QUIC_RTT_MAX)
+ return;
+
+ /* rfc9002#section-5.1:
+ * latest_rtt = ack_time - send_time_of_largest_acked
+ */
+ cong->latest_rtt = cong->time - time;
+
+ /* rfc9002#section-5.2: Estimating min_rtt */
+ if (!cong->min_rtt_valid) {
+ cong->min_rtt = cong->latest_rtt;
+ cong->min_rtt_valid = 1;
+ }
+ if (cong->min_rtt > cong->latest_rtt)
+ cong->min_rtt = cong->latest_rtt;
+
+ if (!cong->is_rtt_set) {
+ /* rfc9002#section-5.3:
+ * smoothed_rtt = latest_rtt
+ * rttvar = latest_rtt / 2
+ */
+ cong->smoothed_rtt = cong->latest_rtt;
+ cong->rttvar = cong->smoothed_rtt / 2;
+ quic_cong_pto_update(cong);
+ cong->is_rtt_set = 1;
+ return;
+ }
+
+ /* rfc9002#section-5.3:
+ * adjusted_rtt = latest_rtt
+ * if (latest_rtt >= min_rtt + ack_delay):
+ * adjusted_rtt = latest_rtt - ack_delay
+ * smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt
+ * rttvar_sample = abs(smoothed_rtt - adjusted_rtt)
+ * rttvar = 3/4 * rttvar + 1/4 * rttvar_sample
+ */
+ adjusted_rtt = cong->latest_rtt;
+ if (cong->latest_rtt >= cong->min_rtt + ack_delay)
+ adjusted_rtt = cong->latest_rtt - ack_delay;
+
+ cong->smoothed_rtt = (cong->smoothed_rtt * 7 + adjusted_rtt) / 8;
+ rttvar_sample = abs_diff(cong->smoothed_rtt, adjusted_rtt);
+ cong->rttvar = (cong->rttvar * 3 + rttvar_sample) / 4;
+ quic_cong_pto_update(cong);
+
+ if (cong->ops->on_rtt_update)
+ cong->ops->on_rtt_update(cong);
+}
+
+void quic_cong_set_algo(struct quic_cong *cong, u8 algo)
+{
+ /* The caller must ensure algo < QUIC_CONG_ALG_MAX. */
+ if (WARN_ON_ONCE(algo >= QUIC_CONG_ALG_MAX))
+ return;
+ cong->algo = algo;
+ cong->state = QUIC_CONG_SLOW_START;
+ cong->ssthresh = U32_MAX;
+ cong->ops = &quic_congs[algo];
+ cong->ops->on_init(cong);
+}
+
+void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt)
+{
+ /* rfc9002#section-5.3:
+ * smoothed_rtt = kInitialRtt
+ * rttvar = kInitialRtt / 2
+ */
+ cong->initial_srtt = srtt;
+ cong->latest_rtt = srtt;
+ cong->smoothed_rtt = cong->latest_rtt;
+ cong->rttvar = cong->smoothed_rtt / 2;
+ quic_cong_pto_update(cong);
+}
+
+void quic_cong_init(struct quic_cong *cong)
+{
+ cong->max_ack_delay = QUIC_DEF_ACK_DELAY;
+ cong->max_window = S32_MAX / 4;
+ quic_cong_set_algo(cong, QUIC_CONG_ALG_RENO);
+ quic_cong_set_srtt(cong, QUIC_RTT_INIT);
+}
diff --git a/net/quic/cong.h b/net/quic/cong.h
new file mode 100644
index 0000000000000..0d3697eb916a0
--- /dev/null
+++ b/net/quic/cong.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_KPERSISTENT_CONGESTION_THRESHOLD 3
+#define QUIC_KPACKET_THRESHOLD 3
+#define QUIC_KTIME_THRESHOLD(rtt) ((rtt) * 9 / 8)
+#define QUIC_KGRANULARITY 1000U
+
+#define QUIC_RTT_INIT 333000U
+#define QUIC_RTT_MAX 6000000U
+#define QUIC_RTT_MIN QUIC_KGRANULARITY
+
+/* rfc9002#section-7.3: Congestion Control States
+ *
+ * New path or +------------+
+ * persistent congestion | Slow |
+ * (O)---------------------->| Start |
+ * +------------+
+ * |
+ * Loss or |
+ * ECN-CE increase |
+ * v
+ * +------------+ Loss or +------------+
+ * | Congestion | ECN-CE increase | Recovery |
+ * | Avoidance |------------------>| Period |
+ * +------------+ +------------+
+ * ^ |
+ * | |
+ * +----------------------------+
+ * Acknowledgment of packet
+ * sent during recovery
+ */
+enum quic_cong_state {
+ QUIC_CONG_SLOW_START,
+ QUIC_CONG_RECOVERY_PERIOD,
+ QUIC_CONG_CONGESTION_AVOIDANCE,
+};
+
+struct quic_cong {
+ /* RTT tracking */
+ u32 max_ack_delay; /* max_ack_delay from rfc9000#section-18.2 */
+ u32 smoothed_rtt; /* Smoothed RTT */
+ u32 latest_rtt; /* Latest RTT sample */
+ u32 min_rtt; /* Lowest observed RTT */
+ u32 rttvar; /* RTT variation */
+ u32 pto; /* Probe timeout */
+
+ /* Timing & pacing */
+ u64 pc_start_time; /* Persistent congestion tracking timestamp */
+ u64 recovery_time; /* Recovery period start timestamp */
+ u64 pacing_rate; /* Packet sending speed Bytes/sec */
+ u64 pacing_time; /* Next scheduled send timestamp (ns) */
+ u64 time; /* Cached current timestamp */
+
+ /* Congestion window */
+ u32 max_window; /* Max growth cap */
+ u32 min_window; /* Min window limit */
+ u32 loss_delay; /* Time before marking loss */
+ u32 ssthresh; /* Slow start threshold */
+ u32 window; /* Bytes in flight allowed */
+ u32 mss; /* QUIC MSS (excl. UDP) */
+
+ /* Algorithm-specific */
+ const struct quic_cong_ops *ops;
+ u64 priv[8]; /* Algo private data */
+
+ u32 initial_srtt; /* Initial smoothed RTT */
+ u8 algo; /* Congestion control algorithm */
+
+ /* Flags & state */
+ u8 min_rtt_valid; /* min_rtt initialized */
+ u8 is_rtt_set; /* RTT samples exist */
+ u8 state; /* State machine in rfc9002#section-7.3 */
+};
+
+/* Hooks for congestion control algorithms */
+struct quic_cong_ops {
+ void (*on_packet_acked)(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+ void (*on_packet_lost)(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+ void (*on_process_ecn)(struct quic_cong *cong);
+ void (*on_init)(struct quic_cong *cong);
+
+ /* Optional callbacks */
+ void (*on_packet_sent)(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+ void (*on_ack_recv)(struct quic_cong *cong, u32 bytes, u64 max_rate);
+ void (*on_rtt_update)(struct quic_cong *cong);
+};
+
+static inline void quic_cong_set_mss(struct quic_cong *cong, u32 mss)
+{
+ if (cong->mss == mss)
+ return;
+
+ /* rfc9002#section-7.2: Initial and Minimum Congestion Window */
+ cong->mss = mss;
+ cong->min_window = mss * 2;
+
+ if (!cong->window)
+ cong->window = min(mss * 10, 14720U);
+ if (cong->window < cong->min_window)
+ cong->window = cong->min_window;
+}
+
+static inline void *quic_cong_priv(struct quic_cong *cong)
+{
+ return (void *)cong->priv;
+}
+
+void quic_cong_on_packet_acked(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+void quic_cong_on_packet_lost(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+void quic_cong_on_process_ecn(struct quic_cong *cong);
+
+void quic_cong_on_packet_sent(struct quic_cong *cong, u64 time, u32 bytes,
+ s64 number);
+void quic_cong_on_ack_recv(struct quic_cong *cong, u32 bytes, u64 max_rate);
+void quic_cong_rtt_update(struct quic_cong *cong, u64 time, u32 ack_delay);
+
+void quic_cong_set_srtt(struct quic_cong *cong, u32 srtt);
+void quic_cong_set_algo(struct quic_cong *cong, u8 algo);
+void quic_cong_init(struct quic_cong *cong);
diff --git a/net/quic/connid.c b/net/quic/connid.c
new file mode 100644
index 0000000000000..e726a1d02c970
--- /dev/null
+++ b/net/quic/connid.c
@@ -0,0 +1,283 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <crypto/utils.h>
+#include <linux/quic.h>
+#include <net/sock.h>
+
+#include "common.h"
+#include "connid.h"
+
+/* Lookup a source connection ID (scid) in the global source connection ID hash
+ * table.
+ */
+struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len)
+{
+ struct quic_shash_head *head = quic_source_conn_id_head(net, scid, len);
+ struct quic_source_conn_id *s_conn_id;
+ struct quic_conn_id *conn_id = NULL;
+ struct hlist_nulls_node *node;
+
+ WARN_ON_ONCE(!rcu_read_lock_held());
+
+ hlist_nulls_for_each_entry_rcu(s_conn_id, node, &head->head, node) {
+ if (net != sock_net(s_conn_id->sk))
+ continue;
+ if (s_conn_id->common.id.len != len ||
+ memcmp(scid, &s_conn_id->common.id.data, len))
+ continue;
+ if (likely(refcount_inc_not_zero(&s_conn_id->sk->sk_refcnt)))
+ conn_id = &s_conn_id->common.id;
+ break;
+ }
+ /* No need to check get_nulls_value(node) != hash for !conn_id, as
+ * hashtable size is fixed and a conn_id can not rehashed.
+ */
+ return conn_id;
+}
+
+/* Check if a given stateless reset token exists in any connection ID in the
+ * connection ID set.
+ */
+bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token)
+{
+ struct quic_common_conn_id *common;
+ struct quic_dest_conn_id *dcid;
+
+ dcid = (struct quic_dest_conn_id *)id_set->active;
+ if (memchr_inv(dcid->token, 0, QUIC_CONN_ID_TOKEN_LEN) &&
+ !crypto_memneq(dcid->token, token, QUIC_CONN_ID_TOKEN_LEN))
+ return true; /* Fast path. */
+
+ list_for_each_entry(common, &id_set->head, list) {
+ dcid = (struct quic_dest_conn_id *)common;
+ if (common == id_set->active)
+ continue;
+ if (memchr_inv(dcid->token, 0, QUIC_CONN_ID_TOKEN_LEN) &&
+ !crypto_memneq(dcid->token, token, QUIC_CONN_ID_TOKEN_LEN))
+ return true;
+ }
+ return false;
+}
+
+static void quic_source_conn_id_free_rcu(struct rcu_head *head)
+{
+ struct quic_source_conn_id *s_conn_id;
+
+ s_conn_id = container_of(head, struct quic_source_conn_id, rcu);
+ kfree(s_conn_id);
+}
+
+static void quic_source_conn_id_free(struct quic_source_conn_id *s_conn_id)
+{
+ u8 *data = s_conn_id->common.id.data;
+ u32 len = s_conn_id->common.id.len;
+ struct quic_shash_head *head;
+
+ head = quic_source_conn_id_head(sock_net(s_conn_id->sk), data, len);
+ spin_lock_bh(&head->lock);
+ hlist_nulls_del_init_rcu(&s_conn_id->node);
+ spin_unlock_bh(&head->lock);
+
+ /* Freeing is deferred via RCU to avoid use-after-free during
+ * concurrent lookups.
+ */
+ call_rcu(&s_conn_id->rcu, quic_source_conn_id_free_rcu);
+}
+
+static void quic_conn_id_del(struct quic_common_conn_id *common)
+{
+ list_del(&common->list);
+ if (!common->hashed) {
+ kfree(common);
+ return;
+ }
+ quic_source_conn_id_free((struct quic_source_conn_id *)common);
+}
+
+/* Add a connection ID with sequence number and associated private data to the
+ * connection ID set.
+ */
+int quic_conn_id_add(struct quic_conn_id_set *id_set,
+ struct quic_conn_id *conn_id, u32 number, void *data,
+ gfp_t gfp)
+{
+ bool dest = id_set->entry_size == sizeof(struct quic_dest_conn_id);
+ struct quic_source_conn_id *s_conn_id, *pos;
+ struct quic_dest_conn_id *d_conn_id;
+ struct quic_common_conn_id *common;
+ struct hlist_nulls_node *node;
+ struct quic_shash_head *head;
+ struct list_head *list;
+ struct net *net;
+
+ /* Locate insertion point to keep list ordered by number. */
+ list = &id_set->head;
+ list_for_each_entry(common, list, list) {
+ if (number == common->number) {
+ if (quic_conn_id_cmp(&common->id, conn_id))
+ return -EINVAL;
+ if (dest && data) {
+ d_conn_id = (struct quic_dest_conn_id *)common;
+ if (crypto_memneq(d_conn_id->token, data,
+ QUIC_CONN_ID_TOKEN_LEN))
+ return -EINVAL;
+ }
+ return 0; /* Ignore if it already exists on the list. */
+ }
+ if (number < common->number) {
+ list = &common->list;
+ break;
+ }
+ }
+
+ if (conn_id->len > QUIC_CONN_ID_MAX_LEN)
+ return -EINVAL;
+ common = kzalloc(id_set->entry_size, gfp);
+ if (!common)
+ return -ENOMEM;
+ common->id = *conn_id;
+ common->number = number;
+ if (dest) {
+ /* For destination connection IDs, copy the stateless reset
+ * token if available.
+ */
+ if (data) {
+ d_conn_id = (struct quic_dest_conn_id *)common;
+ memcpy(d_conn_id->token, data, QUIC_CONN_ID_TOKEN_LEN);
+ }
+ } else {
+ /* For source connection IDs, mark as hashed and insert into
+ * the global source connection ID hashtable.
+ */
+ common->hashed = 1;
+ s_conn_id = (struct quic_source_conn_id *)common;
+ s_conn_id->sk = data;
+ net = sock_net(s_conn_id->sk);
+
+ head = quic_source_conn_id_head(net, common->id.data,
+ common->id.len);
+ spin_lock_bh(&head->lock);
+
+ /* Check for collision before inserting */
+ hlist_nulls_for_each_entry(pos, node, &head->head, node) {
+ if (net != sock_net(pos->sk))
+ continue;
+ if (quic_conn_id_cmp(&pos->common.id, &common->id))
+ continue;
+ spin_unlock_bh(&head->lock);
+ kfree(common);
+ WARN_ON_ONCE(1);
+ return -EEXIST;
+ }
+
+ hlist_nulls_add_head_rcu(&s_conn_id->node, &head->head);
+ spin_unlock_bh(&head->lock);
+ }
+ list_add_tail(&common->list, list);
+
+ if (number == quic_conn_id_last_number(id_set) + 1) {
+ if (!id_set->active)
+ id_set->active = common;
+ id_set->count++;
+
+ /* Increment count for consecutive following IDs. */
+ list_for_each_entry_continue(common, &id_set->head, list) {
+ if (common->number != ++number)
+ break;
+ id_set->count++;
+ }
+ }
+ return 0;
+}
+
+/* Remove consecutive connection IDs from the set with sequence numbers less
+ * than or equal to a number.
+ */
+void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number)
+{
+ struct quic_common_conn_id *common, *tmp;
+ struct list_head *list;
+
+ /* The number must be less than the sequence number of the last
+ * consecutive connection ID in the set.
+ */
+ if (WARN_ON_ONCE(number >= quic_conn_id_last_number(id_set)))
+ return;
+ list = &id_set->head;
+ list_for_each_entry_safe(common, tmp, list, list) {
+ if (common->number > number)
+ break;
+ if (id_set->active == common)
+ id_set->active = tmp;
+ if (id_set->alt == common)
+ id_set->alt = tmp;
+ quic_conn_id_del(common);
+ id_set->count--;
+ }
+}
+
+struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set,
+ u32 number)
+{
+ struct quic_common_conn_id *common;
+
+ list_for_each_entry(common, &id_set->head, list) {
+ if (common->number > number)
+ break;
+ if (common->number == number)
+ return &common->id;
+ }
+ return NULL;
+}
+
+void quic_conn_id_update_active(struct quic_conn_id_set *id_set, u32 number)
+{
+ struct quic_conn_id *conn_id;
+
+ if (number == id_set->active->number)
+ return;
+ conn_id = quic_conn_id_find(id_set, number);
+ if (!conn_id)
+ return;
+ quic_conn_id_set_active(id_set, conn_id);
+}
+
+void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source)
+{
+ id_set->entry_size = source ? sizeof(struct quic_source_conn_id) :
+ sizeof(struct quic_dest_conn_id);
+ INIT_LIST_HEAD(&id_set->head);
+}
+
+void quic_conn_id_set_free(struct quic_conn_id_set *id_set)
+{
+ struct quic_common_conn_id *common, *tmp;
+
+ list_for_each_entry_safe(common, tmp, &id_set->head, list)
+ quic_conn_id_del(common);
+ id_set->count = 0;
+ id_set->alt = NULL;
+ id_set->active = NULL;
+}
+
+void quic_conn_id_get_param(struct quic_conn_id_set *id_set,
+ struct quic_transport_param *p)
+{
+ p->active_connection_id_limit = id_set->max_count;
+}
+
+void quic_conn_id_set_param(struct quic_conn_id_set *id_set,
+ struct quic_transport_param *p)
+{
+ id_set->max_count = p->active_connection_id_limit;
+}
diff --git a/net/quic/connid.h b/net/quic/connid.h
new file mode 100644
index 0000000000000..abad396a6ad6a
--- /dev/null
+++ b/net/quic/connid.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_CONN_ID_LIMIT 8
+#define QUIC_CONN_ID_DEF 7
+#define QUIC_CONN_ID_LEAST 2
+
+#define QUIC_CONN_ID_TOKEN_LEN 16
+
+/* Common fields shared by both source and destination Connection IDs */
+struct quic_common_conn_id {
+ struct quic_conn_id id; /* Connection ID value and its length */
+ struct list_head list; /* List node for connection ID management */
+ u32 number; /* Sequence number assigned to this Connection ID */
+ u8 hashed; /* Non-zero if stored in source_conn_id hash table */
+};
+
+struct quic_source_conn_id {
+ struct quic_common_conn_id common;
+ struct hlist_nulls_node node; /* Hash table node for fast lookup */
+ struct rcu_head rcu; /* RCU header for deferred destruction */
+ struct sock *sk; /* Socket associated with this Connection ID */
+};
+
+struct quic_dest_conn_id {
+ struct quic_common_conn_id common;
+ /* Stateless reset token in rfc9000#section-10.3 */
+ u8 token[QUIC_CONN_ID_TOKEN_LEN];
+};
+
+struct quic_conn_id_set {
+ /* Connection ID in use on the current path */
+ struct quic_common_conn_id *active;
+ /* Connection ID to use for a new path (e.g., after migration) */
+ struct quic_common_conn_id *alt;
+ struct list_head head; /* List head of available connection IDs */
+ u8 entry_size; /* Size of each connection ID entry in the list */
+ u8 max_count; /* active_connection_id_limit in rfc9000#section-18.2 */
+ u8 count; /* Current number of connection IDs in the list */
+};
+
+static inline u32 quic_conn_id_first_number(struct quic_conn_id_set *id_set)
+{
+ struct quic_common_conn_id *common;
+
+ /* The id_set is guaranteed to be non-empty when called (sk is not in
+ * CLOSE state).
+ */
+ common = list_first_entry(&id_set->head, struct quic_common_conn_id,
+ list);
+ return common->number;
+}
+
+static inline u32 quic_conn_id_last_number(struct quic_conn_id_set *id_set)
+{
+ return quic_conn_id_first_number(id_set) + id_set->count - 1;
+}
+
+static inline void quic_conn_id_generate(struct quic_conn_id *conn_id)
+{
+ get_random_bytes(conn_id->data, QUIC_CONN_ID_DEF_LEN);
+ conn_id->len = QUIC_CONN_ID_DEF_LEN;
+}
+
+/* Select an alternate destination Connection ID for a new path (e.g., after
+ * migration).
+ */
+static inline bool quic_conn_id_select_alt(struct quic_conn_id_set *id_set,
+ bool active)
+{
+ if (id_set->alt)
+ return true;
+ /* NAT rebinding: peer keeps using the current source conn_id.
+ * In this case, continue using the same dest conn_id for the new path.
+ */
+ if (active) {
+ id_set->alt = id_set->active;
+ return true;
+ }
+ /* Treat the prev conn_ids as used.
+ * Try selecting the next conn_id in the list, unless at the end.
+ */
+ if (id_set->active->number != quic_conn_id_last_number(id_set)) {
+ id_set->alt = list_next_entry(id_set->active, list);
+ return true;
+ }
+ /* If there's only one conn_id in the list, reuse the active one. */
+ if (id_set->active->number == quic_conn_id_first_number(id_set)) {
+ id_set->alt = id_set->active;
+ return true;
+ }
+ /* No alternate conn_id could be selected. Caller should send a
+ * QUIC_FRAME_RETIRE_CONNECTION_ID frame to request new connection IDs
+ * from the peer.
+ */
+ return false;
+}
+
+static inline void quic_conn_id_set_alt(struct quic_conn_id_set *id_set,
+ struct quic_conn_id *alt)
+{
+ id_set->alt = (struct quic_common_conn_id *)alt;
+}
+
+/* Swap the active and alternate destination Connection IDs after path
+ * migration completes, since the path has already been switched accordingly.
+ */
+static inline void quic_conn_id_swap_active(struct quic_conn_id_set *id_set)
+{
+ void *active = id_set->active;
+
+ id_set->active = id_set->alt;
+ id_set->alt = active;
+}
+
+/* Choose which destination Connection ID to use for a new path migration if
+ * alt is true.
+ */
+static inline struct quic_conn_id *
+quic_conn_id_choose(struct quic_conn_id_set *id_set, u8 alt)
+{
+ return (alt && id_set->alt) ? &id_set->alt->id : &id_set->active->id;
+}
+
+static inline struct quic_conn_id *
+quic_conn_id_active(struct quic_conn_id_set *id_set)
+{
+ return &id_set->active->id;
+}
+
+static inline void quic_conn_id_set_active(struct quic_conn_id_set *id_set,
+ struct quic_conn_id *active)
+{
+ id_set->active = (struct quic_common_conn_id *)active;
+}
+
+static inline u32 quic_conn_id_number(struct quic_conn_id *conn_id)
+{
+ return ((struct quic_common_conn_id *)conn_id)->number;
+}
+
+static inline struct sock *quic_conn_id_sk(struct quic_conn_id *conn_id)
+{
+ return ((struct quic_source_conn_id *)conn_id)->sk;
+}
+
+static inline void quic_conn_id_set_token(struct quic_conn_id *conn_id,
+ u8 *token)
+{
+ memcpy(((struct quic_dest_conn_id *)conn_id)->token, token,
+ QUIC_CONN_ID_TOKEN_LEN);
+}
+
+static inline int quic_conn_id_cmp(struct quic_conn_id *a,
+ struct quic_conn_id *b)
+{
+ return a->len != b->len || memcmp(a->data, b->data, a->len);
+}
+
+int quic_conn_id_add(struct quic_conn_id_set *id_set,
+ struct quic_conn_id *conn_id, u32 number, void *data,
+ gfp_t gfp);
+bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token);
+void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number);
+
+struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set,
+ u32 number);
+struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len);
+void quic_conn_id_update_active(struct quic_conn_id_set *id_set, u32 number);
+
+void quic_conn_id_get_param(struct quic_conn_id_set *id_set,
+ struct quic_transport_param *p);
+void quic_conn_id_set_param(struct quic_conn_id_set *id_set,
+ struct quic_transport_param *p);
+void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source);
+void quic_conn_id_set_free(struct quic_conn_id_set *id_set);
diff --git a/net/quic/crypto.c b/net/quic/crypto.c
new file mode 100644
index 0000000000000..d62c6c4bea7de
--- /dev/null
+++ b/net/quic/crypto.c
@@ -0,0 +1,1251 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <crypto/skcipher.h>
+#include <linux/skbuff.h>
+#include <crypto/utils.h>
+#include <crypto/aead.h>
+#include <crypto/hash.h>
+#include <linux/quic.h>
+#include <net/tls.h>
+
+#include "common.h"
+#include "crypto.h"
+
+/* HKDF-Extract. */
+static int quic_crypto_hkdf_extract(struct crypto_shash *tfm,
+ struct quic_data *srt,
+ struct quic_data *hash,
+ struct quic_data *key)
+{
+ int err;
+
+ err = crypto_shash_setkey(tfm, srt->data, srt->len);
+ if (err)
+ return err;
+
+ return crypto_shash_tfm_digest(tfm, hash->data, hash->len, key->data);
+}
+
+#define QUIC_MAX_INFO_LEN 256
+
+/* HKDF-Expand-Label. */
+static int quic_crypto_hkdf_expand(struct crypto_shash *tfm,
+ struct quic_data *srt,
+ struct quic_data *label,
+ struct quic_data *key)
+{
+ u8 info[QUIC_MAX_INFO_LEN], *p = info, tmp[QUIC_SECRET_LEN];
+ unsigned int i, infolen, hashlen = srt->len;
+ SHASH_DESC_ON_STACK(desc, tfm);
+ u8 LABEL[] = "tls13 ", cnt = 1;
+ const u8 *prev = NULL;
+ int err;
+
+ /* rfc8446#section-7.1:
+ *
+ * HKDF-Expand-Label(Secret, Label, Context, Length) =
+ * HKDF-Expand(Secret, HkdfLabel, Length)
+ *
+ * Where HkdfLabel is specified as:
+ *
+ * struct {
+ * uint16 length = Length;
+ * opaque label<7..255> = "tls13 " + Label;
+ * opaque context<0..255> = Context;
+ * } HkdfLabel;
+ */
+ put_unaligned_be16(key->len, p);
+ p += 2;
+ *p++ = (u8)(sizeof(LABEL) - 1 + label->len);
+ p = quic_put_data(p, LABEL, sizeof(LABEL) - 1);
+ p = quic_put_data(p, label->data, label->len);
+ *p++ = 0;
+ infolen = (unsigned int)(p - info);
+
+ err = crypto_shash_setkey(tfm, srt->data, srt->len);
+ if (err)
+ return err;
+ desc->tfm = tfm;
+
+ for (i = 0; i < key->len; i += hashlen) {
+ err = crypto_shash_init(desc);
+ if (err)
+ goto out;
+
+ if (prev) {
+ err = crypto_shash_update(desc, prev, hashlen);
+ if (err)
+ goto out;
+ }
+
+ err = crypto_shash_update(desc, info, infolen);
+ if (err)
+ goto out;
+
+ BUILD_BUG_ON(sizeof(cnt) != 1);
+ if (key->len - i < hashlen) {
+ err = crypto_shash_finup(desc, &cnt, 1, tmp);
+ if (err)
+ goto out;
+ memcpy(&key->data[i], tmp, key->len - i);
+ memzero_explicit(tmp, sizeof(tmp));
+ } else {
+ err = crypto_shash_finup(desc, &cnt, 1, &key->data[i]);
+ if (err)
+ goto out;
+ }
+ cnt++;
+ prev = &key->data[i];
+ }
+out:
+ shash_desc_zero(desc);
+ memzero_explicit(tmp, sizeof(tmp));
+ return err;
+}
+
+#define KEY_LABEL_V1 "quic key"
+#define IV_LABEL_V1 "quic iv"
+#define HP_KEY_LABEL_V1 "quic hp"
+
+#define KU_LABEL_V1 "quic ku"
+
+/* rfc9369#section-3.3.2:
+ *
+ * The labels used in rfc9001 to derive packet protection keys, header
+ * protection keys, Retry Integrity Tag keys, and key updates change from "quic
+ * key" to "quicv2 key", from "quic iv" to "quicv2 iv", from "quic hp" to
+ * "quicv2 hp", and from "quic ku" to "quicv2 ku".
+ */
+#define KEY_LABEL_V2 "quicv2 key"
+#define IV_LABEL_V2 "quicv2 iv"
+#define HP_KEY_LABEL_V2 "quicv2 hp"
+
+#define KU_LABEL_V2 "quicv2 ku"
+
+/* Packet Protection Keys. */
+static int quic_crypto_keys_derive(struct crypto_shash *tfm,
+ struct quic_data *s, struct quic_data *k,
+ struct quic_data *i, struct quic_data *hp_k,
+ u32 version)
+{
+ struct quic_data hp_k_l = {HP_KEY_LABEL_V1, strlen(HP_KEY_LABEL_V1)};
+ struct quic_data k_l = {KEY_LABEL_V1, strlen(KEY_LABEL_V1)};
+ struct quic_data i_l = {IV_LABEL_V1, strlen(IV_LABEL_V1)};
+ int err;
+
+ /* rfc9001#section-5.1:
+ *
+ * The current encryption level secret and the label "quic key" are
+ * input to the KDF to produce the AEAD key; the label "quic iv" is
+ * used to derive the Initialization Vector (IV). The header protection
+ * key uses the "quic hp" label. Using these labels provides key
+ * separation between QUIC and TLS.
+ */
+ if (version == QUIC_VERSION_V2) {
+ quic_data(&hp_k_l, HP_KEY_LABEL_V2, strlen(HP_KEY_LABEL_V2));
+ quic_data(&k_l, KEY_LABEL_V2, strlen(KEY_LABEL_V2));
+ quic_data(&i_l, IV_LABEL_V2, strlen(IV_LABEL_V2));
+ }
+
+ err = quic_crypto_hkdf_expand(tfm, s, &k_l, k);
+ if (err)
+ return err;
+ err = quic_crypto_hkdf_expand(tfm, s, &i_l, i);
+ if (err)
+ return err;
+ /* Don't change hp key for key update. */
+ if (!hp_k)
+ return 0;
+
+ return quic_crypto_hkdf_expand(tfm, s, &hp_k_l, hp_k);
+}
+
+/* Derive and install reception (RX) or transmission (TX) packet protection
+ * keys for the current key phase. This installs AEAD protection key, IV, and
+ * optionally header protection key.
+ */
+static int quic_crypto_keys_derive_and_install(struct quic_crypto *crypto,
+ bool rx, u8 phase)
+{
+ struct quic_data srt = {}, k, iv, hp_k = {}, *hp = NULL;
+ u8 key[QUIC_KEY_LEN], hp_key[QUIC_KEY_LEN] = {};
+ u32 keylen, ivlen = QUIC_IV_LEN;
+ struct crypto_skcipher *hp_tfm;
+ struct crypto_aead *tfm;
+ int err;
+
+ keylen = crypto->cipher->keylen;
+ quic_data(&k, key, keylen);
+
+ if (rx) {
+ quic_data(&srt, crypto->rx_secret[phase],
+ crypto->cipher->secretlen);
+ quic_data(&iv, crypto->rx_iv[phase], ivlen);
+ tfm = crypto->rx_tfm[phase];
+ hp_tfm = crypto->rx_hp_tfm;
+ } else {
+ quic_data(&srt, crypto->tx_secret[phase],
+ crypto->cipher->secretlen);
+ quic_data(&iv, crypto->tx_iv[phase], ivlen);
+ tfm = crypto->tx_tfm[phase];
+ hp_tfm = crypto->tx_hp_tfm;
+ }
+
+ /* Only derive header protection key when not in key update. */
+ if (crypto->key_phase == phase)
+ hp = quic_data(&hp_k, hp_key, keylen);
+ err = quic_crypto_keys_derive(crypto->secret_tfm, &srt, &k, &iv, hp,
+ crypto->version);
+ if (err)
+ goto out;
+ err = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);
+ if (err)
+ goto out;
+ err = crypto_aead_setkey(tfm, key, keylen);
+ if (err)
+ goto out;
+ if (hp) {
+ err = crypto_skcipher_setkey(hp_tfm, hp_key, keylen);
+ if (err)
+ goto out;
+ }
+out:
+ memzero_explicit(key, sizeof(key));
+ memzero_explicit(hp_key, sizeof(hp_key));
+ return err;
+}
+
+#define QUIC_CIPHER_MIN TLS_CIPHER_AES_GCM_128
+#define QUIC_CIPHER_MAX TLS_CIPHER_CHACHA20_POLY1305
+
+#define TLS_CIPHER_AES_GCM_128_SECRET_SIZE 32
+#define TLS_CIPHER_AES_GCM_256_SECRET_SIZE 48
+#define TLS_CIPHER_AES_CCM_128_SECRET_SIZE 32
+#define TLS_CIPHER_CHACHA20_POLY1305_SECRET_SIZE 32
+
+#define CIPHER_DESC(type, aead_n, skc_n, sha_n)[type - QUIC_CIPHER_MIN] = { \
+ .secretlen = type ## _SECRET_SIZE, \
+ .keylen = type ## _KEY_SIZE, \
+ .aead = aead_n, \
+ .skc = skc_n, \
+ .shash = sha_n, \
+}
+
+static const struct quic_cipher
+ciphers[QUIC_CIPHER_MAX + 1 - QUIC_CIPHER_MIN] = {
+ CIPHER_DESC(TLS_CIPHER_AES_GCM_128,
+ "gcm(aes)", "ecb(aes)", "hmac(sha256)"),
+ CIPHER_DESC(TLS_CIPHER_AES_GCM_256,
+ "gcm(aes)", "ecb(aes)", "hmac(sha384)"),
+ CIPHER_DESC(TLS_CIPHER_AES_CCM_128,
+ "ccm(aes)", "ecb(aes)", "hmac(sha256)"),
+ CIPHER_DESC(TLS_CIPHER_CHACHA20_POLY1305,
+ "rfc7539(chacha20,poly1305)", "chacha20", "hmac(sha256)"),
+};
+
+static bool quic_crypto_is_cipher_ccm(struct quic_crypto *crypto)
+{
+ return crypto->cipher_type == TLS_CIPHER_AES_CCM_128;
+}
+
+static bool quic_crypto_is_cipher_chacha(struct quic_crypto *crypto)
+{
+ return crypto->cipher_type == TLS_CIPHER_CHACHA20_POLY1305;
+}
+
+static void *quic_crypto_skcipher_mem_alloc(struct crypto_skcipher *tfm,
+ u32 mask_size, u8 **iv,
+ struct skcipher_request **req,
+ gfp_t gfp)
+{
+ unsigned int iv_size, req_size;
+ unsigned int len;
+ u8 *mem;
+
+ iv_size = crypto_skcipher_ivsize(tfm);
+ req_size = sizeof(**req) + crypto_skcipher_reqsize(tfm);
+
+ len = mask_size;
+ len += iv_size;
+ len += crypto_skcipher_alignmask(tfm) &
+ ~(crypto_tfm_ctx_alignment() - 1);
+ len = ALIGN(len, crypto_tfm_ctx_alignment());
+ len += req_size;
+
+ mem = kzalloc(len, gfp);
+ if (!mem)
+ return NULL;
+
+ *iv = (u8 *)PTR_ALIGN(mem + mask_size,
+ crypto_skcipher_alignmask(tfm) + 1);
+ *req = (struct skcipher_request *)PTR_ALIGN(*iv + iv_size,
+ crypto_tfm_ctx_alignment());
+
+ return (void *)mem;
+}
+
+/* Extracts and reconstructs the packet number from an incoming QUIC packet. */
+static int quic_crypto_get_number(struct sk_buff *skb)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ s64 number_max = cb->number;
+ u32 len = cb->length;
+ u8 *p;
+
+ /* rfc9000#section-17.1:
+ *
+ * Once header protection is removed, the packet number is decoded by
+ * finding the packet number value that is closest to the next expected
+ * packet. The next expected packet is the highest received packet
+ * number plus one.
+ */
+ p = (u8 *)quic_hdr(skb) + cb->number_offset;
+ if (!quic_get_int(&p, &len, &cb->number, cb->number_len))
+ return -EINVAL;
+ cb->number = quic_get_num(number_max, cb->number, cb->number_len);
+ return 0;
+}
+
+#define QUIC_SAMPLE_LEN 16
+
+#define QUIC_HEADER_FORM_BIT 0x80
+#define QUIC_LONG_HEADER_MASK 0x0f
+#define QUIC_SHORT_HEADER_MASK 0x1f
+
+/* Header Protection. */
+static int quic_crypto_header_protect(struct quic_crypto *crypto,
+ struct sk_buff *skb, bool enc, gfp_t gfp)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ u8 *mask, *iv, *p, h_mask, chacha;
+ struct skcipher_request *req;
+ struct crypto_skcipher *tfm;
+ struct sk_buff *trailer;
+ struct scatterlist sg;
+ int err, i;
+
+ chacha = quic_crypto_is_cipher_chacha(crypto);
+ if (!enc) {
+ tfm = crypto->rx_hp_tfm;
+ if (cb->length < QUIC_PN_MAX_LEN + QUIC_SAMPLE_LEN)
+ return -EINVAL;
+
+ err = skb_cow_data(skb, 0, &trailer);
+ if (err < 0)
+ return err;
+ } else {
+ tfm = crypto->tx_hp_tfm;
+ }
+
+ mask = quic_crypto_skcipher_mem_alloc(tfm, QUIC_SAMPLE_LEN, &iv, &req,
+ gfp);
+ if (!mask)
+ return -ENOMEM;
+
+ /* rfc9001#section-5.4.2: Header Protection Sample:
+ *
+ * # pn_offset is the start of the Packet Number field.
+ * sample_offset = pn_offset + 4
+ *
+ * sample = packet[sample_offset..sample_offset+sample_length]
+ *
+ * rfc9001#section-5.4.3: AES-Based Header Protection:
+ *
+ * header_protection(hp_key, sample):
+ * mask = AES-ECB(hp_key, sample)
+ *
+ * rfc9001#section-5.4.4: ChaCha20-Based Header Protection:
+ *
+ * header_protection(hp_key, sample):
+ * counter = sample[0..3]
+ * nonce = sample[4..15]
+ * mask = ChaCha20(hp_key, counter, nonce, {0,0,0,0,0})
+ */
+ p = skb->data + cb->number_offset + QUIC_PN_MAX_LEN;
+ memcpy((chacha ? iv : mask), p, QUIC_SAMPLE_LEN);
+ sg_init_one(&sg, mask, QUIC_SAMPLE_LEN);
+ skcipher_request_set_tfm(req, tfm);
+ skcipher_request_set_crypt(req, &sg, &sg, QUIC_SAMPLE_LEN, iv);
+ err = crypto_skcipher_encrypt(req);
+ if (err)
+ goto out;
+
+ /* rfc9001#section-5.4.1:
+ *
+ * mask = header_protection(hp_key, sample)
+ *
+ * pn_length = (packet[0] & 0x03) + 1
+ * if (packet[0] & 0x80) == 0x80:
+ * # Long header: 4 bits masked
+ * packet[0] ^= mask[0] & 0x0f
+ * else:
+ * # Short header: 5 bits masked
+ * packet[0] ^= mask[0] & 0x1f
+ *
+ * # pn_offset is the start of the Packet Number field.
+ * packet[pn_offset:pn_offset+pn_length] ^= mask[1:1+pn_length]
+ */
+ p = skb->data;
+ h_mask = ((*p & QUIC_HEADER_FORM_BIT) == QUIC_HEADER_FORM_BIT) ?
+ QUIC_LONG_HEADER_MASK : QUIC_SHORT_HEADER_MASK;
+ *p = (u8)(*p ^ (mask[0] & h_mask));
+ if (!enc) {
+ if (!quic_hdr(skb)->form)
+ cb->key_phase = quic_hdr(skb)->key;
+ cb->number_len = quic_hdr(skb)->pnl + 1;
+ }
+ p += cb->number_offset;
+ for (i = 1; i <= cb->number_len; i++)
+ *p++ ^= mask[i];
+
+ if (!enc)
+ err = quic_crypto_get_number(skb);
+out:
+ kfree_sensitive(mask);
+ return err;
+}
+
+static void *quic_crypto_aead_mem_alloc(struct crypto_aead *tfm, u32 ctx_size,
+ u8 **iv, struct aead_request **req,
+ struct scatterlist **sg, u32 nsg,
+ gfp_t gfp)
+{
+ unsigned int iv_size, req_size;
+ unsigned int len;
+ u8 *mem;
+
+ iv_size = crypto_aead_ivsize(tfm);
+ req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
+
+ len = ctx_size;
+ len += iv_size;
+ len += crypto_aead_alignmask(tfm) & ~(crypto_tfm_ctx_alignment() - 1);
+ len = ALIGN(len, crypto_tfm_ctx_alignment());
+ len += req_size;
+ len = ALIGN(len, __alignof__(struct scatterlist));
+ len += nsg * sizeof(**sg);
+
+ mem = kzalloc(len, gfp);
+ if (!mem)
+ return NULL;
+
+ *iv = (u8 *)PTR_ALIGN(mem + ctx_size, crypto_aead_alignmask(tfm) + 1);
+ *req = (struct aead_request *)PTR_ALIGN(*iv + iv_size,
+ crypto_tfm_ctx_alignment());
+ *sg = (struct scatterlist *)PTR_ALIGN((u8 *)*req + req_size,
+ __alignof__(struct scatterlist));
+
+ return (void *)mem;
+}
+
+static void quic_crypto_done(void *data, int err)
+{
+ struct sk_buff *skb = data;
+ struct quic_crypto *crypto;
+ struct quic_skb_cb *cb;
+
+ if (err == -EINPROGRESS)
+ return;
+
+ cb = QUIC_SKB_CB(skb);
+ crypto = *(struct quic_crypto **)cb->crypto_ctx;
+ atomic_dec(&crypto->async_pending[cb->key_phase]);
+
+ kfree_sensitive(cb->crypto_ctx);
+ cb->crypto_done(skb, err);
+}
+
+/* AEAD Usage. */
+static int quic_crypto_payload_protect(struct quic_crypto *crypto,
+ struct sk_buff *skb, bool enc, gfp_t gfp)
+{
+ u8 *base_iv, *iv, i, nonce[QUIC_IV_LEN], ccm, phase;
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ u32 len, hlen, sglen, nsg;
+ struct aead_request *req;
+ struct crypto_aead *tfm;
+ struct sk_buff *trailer;
+ struct scatterlist *sg;
+ void *ctx;
+ __be64 n;
+ int err;
+
+ ccm = quic_crypto_is_cipher_ccm(crypto);
+ phase = cb->key_phase;
+ hlen = cb->number_offset + cb->number_len;
+ if (enc) {
+ tfm = crypto->tx_tfm[phase];
+ base_iv = crypto->tx_iv[phase];
+ len = skb->len;
+ err = skb_cow_data(skb, QUIC_TAG_LEN, &trailer);
+ if (err < 0)
+ return err;
+ pskb_put(skb, trailer, QUIC_TAG_LEN);
+ if (!quic_hdr(skb)->form)
+ quic_hdr(skb)->key = phase;
+ sglen = skb->len;
+ nsg = (u32)err;
+ } else {
+ tfm = crypto->rx_tfm[phase];
+ base_iv = crypto->rx_iv[phase];
+ len = cb->length + cb->number_offset;
+ if (len - hlen < QUIC_TAG_LEN)
+ return -EINVAL;
+ sglen = len;
+ nsg = 1;
+ }
+
+ ctx = quic_crypto_aead_mem_alloc(tfm, sizeof(void *), &iv, &req, &sg,
+ nsg, gfp);
+ if (!ctx)
+ return -ENOMEM;
+
+ sg_init_table(sg, nsg);
+ err = skb_to_sgvec(skb, sg, 0, sglen);
+ if (err < 0)
+ goto out;
+
+ /* rfc9001#section-5.3:
+ *
+ * The associated data, A, for the AEAD is the contents of the QUIC
+ * header, starting from the first byte of either the short or long
+ * header, up to and including the unprotected packet number.
+ *
+ * The nonce, N, is formed by combining the packet protection IV with
+ * the packet number. The 62 bits of the reconstructed QUIC packet
+ * number in network byte order are left-padded with zeros to the size
+ * of the IV. The exclusive OR of the padded packet number and the IV
+ * forms the AEAD nonce.
+ */
+ memcpy(nonce, base_iv, QUIC_IV_LEN);
+ n = cpu_to_be64(cb->number);
+ for (i = 0; i < sizeof(n); i++)
+ nonce[QUIC_IV_LEN - sizeof(n) + i] ^= ((u8 *)&n)[i];
+
+ /* For CCM based ciphers, first byte of IV is a constant. */
+ iv[0] = TLS_AES_CCM_IV_B0_BYTE;
+ memcpy(&iv[ccm], nonce, QUIC_IV_LEN);
+ aead_request_set_tfm(req, tfm);
+ aead_request_set_ad(req, hlen);
+ aead_request_set_crypt(req, sg, sg, len - hlen, iv);
+ if (cb->sync) {
+ DECLARE_CRYPTO_WAIT(wait);
+
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+ err = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
+ if (err == -EINPROGRESS || err == -EBUSY)
+ err = crypto_wait_req(err, &wait);
+ goto out;
+ }
+
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ quic_crypto_done, skb);
+ *(struct quic_crypto **)ctx = crypto;
+ atomic_inc(&crypto->async_pending[phase]);
+ cb->crypto_ctx = ctx; /* Async free context for quic_crypto_done() */
+ err = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
+ if (err == -EINPROGRESS || err == -EBUSY) {
+ memzero_explicit(nonce, sizeof(nonce));
+ return -EINPROGRESS;
+ }
+ atomic_dec(&crypto->async_pending[phase]);
+ cb->crypto_ctx = NULL;
+
+out:
+ kfree_sensitive(ctx);
+ memzero_explicit(nonce, sizeof(nonce));
+ return err;
+}
+
+/* Encrypts a QUIC packet before transmission. This function performs AEAD
+ * encryption of the packet payload and applies header protection. It handles
+ * key phase tracking and key update timing.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int quic_crypto_encrypt(struct quic_crypto *crypto, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ int err;
+
+ /* Packet payload is already encrypted (e.g., resumed from async),
+ * proceed to header protection only.
+ */
+ if (cb->resume)
+ goto out;
+
+ cb->key_phase = crypto->key_phase;
+ /* If a key update is pending and this is the first packet using the
+ * new key, save the current time. Later used to clear old keys after
+ * some time has passed (see quic_crypto_decrypt()).
+ */
+ if (crypto->key_pending && !crypto->key_update_send_time)
+ crypto->key_update_send_time = quic_ktime_get_us();
+
+ err = quic_crypto_payload_protect(crypto, skb, true, gfp);
+ if (err)
+ return err;
+out:
+ return quic_crypto_header_protect(crypto, skb, true, gfp);
+}
+
+/* Decrypts a QUIC packet after reception. This function removes header
+ * protection, decrypts the payload, and processes any key updates if the key
+ * phase bit changes.
+ *
+ * Return: 0 on success, or a negative error code.
+ */
+int quic_crypto_decrypt(struct quic_crypto *crypto, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ int err = 0;
+ u64 time;
+ u8 phase;
+
+ /* Payload was decrypted asynchronously. Proceed with parsing packet
+ * number and key phase.
+ */
+ if (cb->resume) {
+ err = quic_crypto_get_number(skb);
+ if (err)
+ return err;
+ goto out;
+ }
+ if (!cb->number_len) { /* Packet header not yet decrypted. */
+ err = quic_crypto_header_protect(crypto, skb, false, gfp);
+ if (err) {
+ pr_debug("%s: hd decrypt err %d\n", __func__, err);
+ return err;
+ }
+ }
+
+ /* rfc9001#section-6:
+ *
+ * The Key Phase bit allows a recipient to detect a change in keying
+ * material without needing to receive the first packet that triggered
+ * the change. An endpoint that notices a changed Key Phase bit updates
+ * keys and decrypts the packet that contains the changed value.
+ */
+ phase = cb->key_phase;
+ if (phase != crypto->key_phase && !crypto->key_pending) {
+ if (!crypto->send_ready) /* Not ready for key update. */
+ return -EINVAL;
+ if (!cb->backlog) /* Key update requires process context. */
+ return -EKEYREVOKED;
+ /* Cannot do key update while async crypto is in progress. */
+ if (unlikely(atomic_read(&crypto->async_pending[phase])))
+ return -EBUSY;
+ err = quic_crypto_key_update(crypto); /* Perform key update. */
+ if (err) {
+ cb->errcode = QUIC_TRANSPORT_ERROR_KEY_UPDATE;
+ return err;
+ }
+ cb->sync = 1;
+ cb->key_update = 1; /* Mark packet as triggering key update. */
+ }
+
+ err = quic_crypto_payload_protect(crypto, skb, false, gfp);
+ if (err) {
+ if (err == -EINPROGRESS)
+ return err;
+ /* When using the old keys can not decrypt the packets, the
+ * peer might start another key_update. Thus, clear the last
+ * key_pending so that next packets will trigger the new
+ * key-update.
+ */
+ if (crypto->key_pending && phase != crypto->key_phase) {
+ crypto->key_pending = 0;
+ crypto->key_update_time = 0;
+ crypto->key_update_send_time = 0;
+ }
+ return err;
+ }
+
+out:
+ /* rfc9001#section-6.2:
+ *
+ * If a packet is successfully processed using the next key and IV,
+ * then the peer has initiated a key update.
+ */
+ if (cb->key_update) {
+ crypto->key_pending = 1;
+ crypto->key_derived = 0;
+ crypto->key_phase = !crypto->key_phase;
+ }
+ /* rfc9001#section-6.1:
+ *
+ * An endpoint MUST retain old keys until it has successfully
+ * unprotected a packet sent using the new keys. An endpoint SHOULD
+ * retain old keys for some time after unprotecting a packet sent using
+ * the new keys.
+ */
+ if (crypto->key_pending && cb->key_phase == crypto->key_phase) {
+ time = crypto->key_update_send_time;
+ if (time &&
+ quic_ktime_get_us() - time >= crypto->key_update_time) {
+ crypto->key_pending = 0;
+ crypto->key_update_time = 0;
+ crypto->key_update_send_time = 0;
+ }
+ }
+ return err;
+}
+
+int quic_crypto_set_cipher(struct quic_crypto *crypto, u32 type)
+{
+ const struct quic_cipher *cipher;
+ void *tfm;
+ int err;
+
+ if (type < QUIC_CIPHER_MIN || type > QUIC_CIPHER_MAX)
+ return -EINVAL;
+
+ cipher = &ciphers[type - QUIC_CIPHER_MIN];
+ tfm = crypto_alloc_shash(cipher->shash, 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+ crypto->secret_tfm = tfm;
+
+ /* Allocate AEAD and HP transform for each RX key phase. */
+ tfm = crypto_alloc_aead(cipher->aead, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->rx_tfm[0] = tfm;
+ tfm = crypto_alloc_aead(cipher->aead, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->rx_tfm[1] = tfm;
+ tfm = crypto_alloc_sync_skcipher(cipher->skc, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->rx_hp_tfm = tfm;
+
+ /* Allocate AEAD and HP transform for each TX key phase. */
+ tfm = crypto_alloc_aead(cipher->aead, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->tx_tfm[0] = tfm;
+ tfm = crypto_alloc_aead(cipher->aead, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->tx_tfm[1] = tfm;
+ tfm = crypto_alloc_sync_skcipher(cipher->skc, 0, 0);
+ if (IS_ERR(tfm)) {
+ err = PTR_ERR(tfm);
+ goto err;
+ }
+ crypto->tx_hp_tfm = tfm;
+
+ crypto->cipher = cipher;
+ crypto->cipher_type = type;
+ return 0;
+err:
+ quic_crypto_free(crypto);
+ return err;
+}
+
+int quic_crypto_set_secret(struct quic_crypto *crypto,
+ struct quic_crypto_secret *srt, u32 version)
+{
+ const struct quic_cipher *cipher;
+ u8 phase = crypto->key_phase;
+ int err;
+
+ /* If no cipher has been initialized yet, set it up. */
+ if (!crypto->cipher) {
+ err = quic_crypto_set_cipher(crypto, srt->type);
+ if (err)
+ return err;
+ }
+ cipher = crypto->cipher;
+
+ /* Handle RX path setup. */
+ if (!srt->send) {
+ crypto->version = version;
+ memcpy(crypto->rx_secret[phase], srt->secret,
+ cipher->secretlen);
+ err = quic_crypto_keys_derive_and_install(crypto, true, phase);
+ if (err)
+ return err;
+ crypto->recv_ready = 1;
+ return 0;
+ }
+
+ /* Handle TX path setup. */
+ crypto->version = version;
+ memcpy(crypto->tx_secret[phase], srt->secret, cipher->secretlen);
+ err = quic_crypto_keys_derive_and_install(crypto, false, phase);
+ if (err)
+ return err;
+ crypto->send_ready = 1;
+ return 0;
+}
+
+/* Save token secret in Initial TX secret (phase 1) for token generation. */
+int quic_crypto_set_token_secret(struct quic_crypto *crypto)
+{
+ /* Reuse TX AEAD (phase 1) in Initial crypto. */
+ u8 key[TLS_CIPHER_AES_GCM_128_KEY_SIZE], *srt = crypto->tx_secret[1];
+ struct crypto_aead *tfm = crypto->tx_tfm[1];
+ struct quic_data s = {}, k, i;
+ int err;
+
+ if (!memchr_inv(srt, 0, TLS_CIPHER_AES_GCM_128_SECRET_SIZE))
+ get_random_bytes(srt, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+
+ quic_data(&s, srt, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ quic_data(&k, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+ quic_data(&i, crypto->tx_iv[1], QUIC_IV_LEN);
+ err = quic_crypto_keys_derive(crypto->secret_tfm, &s, &k, &i, NULL,
+ QUIC_VERSION_V1);
+ if (err)
+ goto out;
+ err = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);
+ if (err)
+ goto out;
+ err = crypto_aead_setkey(tfm, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+out:
+ memzero_explicit(key, sizeof(key));
+ return err;
+}
+
+/* Initiating a Key Update. */
+int quic_crypto_key_update(struct quic_crypto *crypto)
+{
+ struct quic_data l = {KU_LABEL_V1, strlen(KU_LABEL_V1)};
+ u8 phase = crypto->key_phase;
+ struct quic_data k, srt;
+ u32 secret_len;
+ int err;
+
+ if (crypto->key_pending || !crypto->recv_ready)
+ return -EINVAL;
+ if (crypto->key_derived)
+ return 0;
+
+ /* rfc9001#section-6.1:
+ *
+ * Endpoints maintain separate read and write secrets for packet
+ * protection. An endpoint initiates a key update by updating its
+ * packet protection write secret and using that to protect new
+ * packets. The endpoint creates a new write secret from the existing
+ * write secret. This uses the KDF function provided by TLS with a
+ * label of "quic ku". The corresponding key and IV are created from
+ * that secret. The header protection key is not updated.
+ *
+ * For example, to update write keys with TLS 1.3, HKDF-Expand-Label is
+ * used as:
+ * secret_<n+1> = HKDF-Expand-Label(secret_<n>, "quic ku",
+ * "", Hash.length)
+ */
+ secret_len = crypto->cipher->secretlen;
+ if (crypto->version == QUIC_VERSION_V2)
+ quic_data(&l, KU_LABEL_V2, strlen(KU_LABEL_V2));
+
+ quic_data(&srt, crypto->tx_secret[phase], secret_len);
+ quic_data(&k, crypto->tx_secret[!phase], secret_len);
+ err = quic_crypto_hkdf_expand(crypto->secret_tfm, &srt, &l, &k);
+ if (err)
+ return err;
+ err = quic_crypto_keys_derive_and_install(crypto, false, !phase);
+ if (err)
+ return err;
+
+ quic_data(&srt, crypto->rx_secret[phase], secret_len);
+ quic_data(&k, crypto->rx_secret[!phase], secret_len);
+ err = quic_crypto_hkdf_expand(crypto->secret_tfm, &srt, &l, &k);
+ if (err)
+ return err;
+ err = quic_crypto_keys_derive_and_install(crypto, true, !phase);
+ if (err)
+ return err;
+
+ crypto->key_derived = 1;
+ return 0;
+}
+
+void quic_crypto_free(struct quic_crypto *crypto)
+{
+ if (crypto->rx_tfm[0])
+ crypto_free_aead(crypto->rx_tfm[0]);
+ if (crypto->rx_tfm[1])
+ crypto_free_aead(crypto->rx_tfm[1]);
+ if (crypto->tx_tfm[0])
+ crypto_free_aead(crypto->tx_tfm[0]);
+ if (crypto->tx_tfm[1])
+ crypto_free_aead(crypto->tx_tfm[1]);
+ if (crypto->secret_tfm)
+ crypto_free_shash(crypto->secret_tfm);
+ if (crypto->rx_hp_tfm)
+ crypto_free_skcipher(crypto->rx_hp_tfm);
+ if (crypto->tx_hp_tfm)
+ crypto_free_skcipher(crypto->tx_hp_tfm);
+
+ memzero_explicit(crypto, offsetof(struct quic_crypto, send_offset));
+}
+
+#define QUIC_INITIAL_SALT_V1 \
+ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17" \
+ "\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb\x7f\x0a"
+
+#define QUIC_INITIAL_SALT_V2 \
+ "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93" \
+ "\x81\xbe\x6e\x26\x9d\xcb\xf9\xbd\x2e\xd9"
+
+#define QUIC_INITIAL_SALT_LEN 20
+
+/* Initial Secrets. */
+int quic_crypto_initial_keys_install(struct quic_crypto *crypto,
+ struct quic_conn_id *conn_id,
+ u32 version, bool is_serv)
+{
+ u8 secret[TLS_CIPHER_AES_GCM_128_SECRET_SIZE];
+ struct quic_data salt, s, k, l, dcid;
+ struct quic_crypto_secret srt = {};
+ char *tl, *rl, *sal;
+ int err;
+
+ /* rfc9001#section-5.2:
+ *
+ * The secret used by clients to construct Initial packets uses the PRK
+ * and the label "client in" as input to the HKDF-Expand-Label function
+ * from TLS [TLS13] to produce a 32-byte secret. Packets constructed by
+ * the server use the same process with the label "server in". The hash
+ * function for HKDF when deriving initial secrets and keys is SHA-256
+ * [SHA].
+ *
+ * This process in pseudocode is:
+ *
+ * initial_salt = 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a
+ * initial_secret = HKDF-Extract(initial_salt,
+ * client_dst_connection_id)
+ *
+ * client_initial_secret = HKDF-Expand-Label(initial_secret,
+ * "client in", "",
+ * Hash.length)
+ * server_initial_secret = HKDF-Expand-Label(initial_secret,
+ * "server in", "",
+ * Hash.length)
+ */
+ if (is_serv) {
+ rl = "client in";
+ tl = "server in";
+ } else {
+ tl = "client in";
+ rl = "server in";
+ }
+ sal = QUIC_INITIAL_SALT_V1;
+ if (version == QUIC_VERSION_V2)
+ sal = QUIC_INITIAL_SALT_V2;
+ quic_data(&salt, sal, QUIC_INITIAL_SALT_LEN);
+ quic_data(&dcid, conn_id->data, conn_id->len);
+ quic_data(&s, secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ err = quic_crypto_hkdf_extract(crypto->secret_tfm, &salt, &dcid, &s);
+ if (err)
+ goto out;
+
+ quic_data(&l, tl, strlen(tl));
+ quic_data(&k, srt.secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ srt.type = TLS_CIPHER_AES_GCM_128;
+ srt.send = 1;
+ err = quic_crypto_hkdf_expand(crypto->secret_tfm, &s, &l, &k);
+ if (err)
+ goto out;
+ err = quic_crypto_set_secret(crypto, &srt, version);
+ if (err)
+ goto out;
+
+ quic_data(&l, rl, strlen(rl));
+ quic_data(&k, srt.secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ srt.type = TLS_CIPHER_AES_GCM_128;
+ srt.send = 0;
+ err = quic_crypto_hkdf_expand(crypto->secret_tfm, &s, &l, &k);
+ if (err)
+ goto out;
+ err = quic_crypto_set_secret(crypto, &srt, version);
+out:
+ memzero_explicit(secret, sizeof(secret));
+ memzero_explicit(&srt, sizeof(srt));
+ return err;
+}
+
+#define QUIC_RETRY_KEY_V1 \
+ "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"
+#define QUIC_RETRY_KEY_V2 \
+ "\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92"
+
+#define QUIC_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"
+#define QUIC_RETRY_NONCE_V2 "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a"
+
+/* Retry Packet Integrity. */
+int quic_crypto_get_retry_tag(struct quic_crypto *crypto, struct sk_buff *skb,
+ struct quic_conn_id *odcid, u32 version, u8 *tag)
+{
+ /* Reuse RX AEAD (phase 1) in Initial crypto. */
+ struct crypto_aead *tfm = crypto->rx_tfm[1];
+ u8 *pseudo_retry, *p, *iv, *key;
+ DECLARE_CRYPTO_WAIT(wait);
+ struct aead_request *req;
+ struct scatterlist *sg;
+ u32 plen;
+ int err;
+
+ /* The caller must ensure skb->len > QUIC_TAG_LEN. */
+ if (skb->len <= QUIC_TAG_LEN)
+ return -EINVAL;
+
+ /* rfc9001#section-5.8:
+ *
+ * The Retry Integrity Tag is a 128-bit field that is computed as the
+ * output of AEAD_AES_128_GCM used with the following inputs:
+ *
+ * - The secret key, K, is 128 bits equal to
+ * 0xbe0c690b9f66575a1d766b54e368c84e.
+ * - The nonce, N, is 96 bits equal to 0x461599d35d632bf2239825bb.
+ * - The plaintext, P, is empty.
+ * - The associated data, A, is the contents of the Retry
+ * Pseudo-Packet,
+ *
+ * The Retry Pseudo-Packet is not sent over the wire. It is computed by
+ * taking the transmitted Retry packet, removing the Retry Integrity
+ * Tag, and prepending the two following fields: ODCID Length +
+ * Original Destination Connection ID (ODCID).
+ */
+ err = crypto_aead_setauthsize(tfm, QUIC_TAG_LEN);
+ if (err)
+ return err;
+ key = QUIC_RETRY_KEY_V1;
+ if (version == QUIC_VERSION_V2)
+ key = QUIC_RETRY_KEY_V2;
+ err = crypto_aead_setkey(tfm, key, TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+ if (err)
+ return err;
+
+ plen = 1 + odcid->len + skb->len - QUIC_TAG_LEN;
+ pseudo_retry = quic_crypto_aead_mem_alloc(tfm, plen + QUIC_TAG_LEN, &iv,
+ &req, &sg, 1, GFP_KERNEL);
+ if (!pseudo_retry)
+ return -ENOMEM;
+
+ p = pseudo_retry;
+ p = quic_put_int(p, odcid->len, 1);
+ p = quic_put_data(p, odcid->data, odcid->len);
+ p = quic_put_data(p, skb->data, skb->len - QUIC_TAG_LEN);
+ sg_init_one(sg, pseudo_retry, plen + QUIC_TAG_LEN);
+
+ memcpy(iv, QUIC_RETRY_NONCE_V1, QUIC_IV_LEN);
+ if (version == QUIC_VERSION_V2)
+ memcpy(iv, QUIC_RETRY_NONCE_V2, QUIC_IV_LEN);
+ aead_request_set_tfm(req, tfm);
+ aead_request_set_ad(req, plen);
+ aead_request_set_crypt(req, sg, sg, 0, iv);
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+ err = crypto_aead_encrypt(req);
+ if (err == -EINPROGRESS || err == -EBUSY)
+ err = crypto_wait_req(err, &wait);
+ if (!err)
+ memcpy(tag, p, QUIC_TAG_LEN);
+
+ kfree_sensitive(pseudo_retry);
+ return err;
+}
+
+/* Derives a key and IV using HKDF, configures the AEAD transform and performs
+ * AEAD encryption/decryption for the provided token.
+ */
+static int quic_crypto_token_protect(struct quic_crypto *crypto, u8 *token,
+ u32 len, u32 adlen, bool enc)
+{
+ /* Reuse TX AEAD (phase 1) in Initial crypto. */
+ struct crypto_aead *tfm = crypto->tx_tfm[1];
+ u32 extra = enc ? QUIC_TAG_LEN : 0, tslen;
+ DECLARE_CRYPTO_WAIT(wait);
+ struct aead_request *req;
+ struct scatterlist *sg;
+ void *ctx = NULL;
+ u8 *nonce, *p, i;
+ __be64 n;
+ int err;
+ u64 ts;
+
+ ctx = quic_crypto_aead_mem_alloc(tfm, 0, &nonce, &req, &sg, 1,
+ GFP_KERNEL);
+ if (!ctx) {
+ err = -ENOMEM;
+ goto out;
+ }
+ memcpy(nonce, crypto->tx_iv[1], QUIC_IV_LEN);
+
+ tslen = sizeof(ts);
+ p = token + adlen - tslen;
+ quic_get_int(&p, &tslen, &ts, tslen);
+
+ n = cpu_to_be64(ts);
+ for (i = 0; i < sizeof(n); i++)
+ nonce[QUIC_IV_LEN - sizeof(n) + i] ^= ((u8 *)&n)[i];
+
+ sg_init_one(sg, token, len);
+ aead_request_set_tfm(req, tfm);
+ aead_request_set_ad(req, adlen);
+ aead_request_set_crypt(req, sg, sg, len - adlen - extra, nonce);
+ aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &wait);
+ err = enc ? crypto_aead_encrypt(req) : crypto_aead_decrypt(req);
+ if (err == -EINPROGRESS || err == -EBUSY)
+ err = crypto_wait_req(err, &wait);
+
+out:
+ kfree_sensitive(ctx);
+ return err;
+}
+
+/* Generate a token for Retry or address validation.
+ *
+ * Builds a token with the format: [flag][client address][timestamp][original
+ * DCID][auth tag]
+ *
+ * Encrypts the token (excluding the first flag byte) using AES-GCM with a key
+ * and IV derived via HKDF. The original DCID is stored to be recovered later
+ * from a Client Initial packet. Ensures the token is bound to the client
+ * address and time, preventing reuse or tampering.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,
+ u32 addrlen, struct quic_conn_id *conn_id,
+ u8 *token, u32 *tlen)
+{
+ u8 *token_buf, *p, flag = *token;
+ u64 ts = quic_ktime_get_us();
+ u32 len, tslen = sizeof(ts);
+ int err;
+
+ len = sizeof(flag) + addrlen + tslen + conn_id->len + QUIC_TAG_LEN;
+ token_buf = kmalloc(len, GFP_KERNEL);
+ if (!token_buf)
+ return -ENOMEM;
+
+ p = token_buf;
+ p = quic_put_int(p, flag, sizeof(flag));
+ p = quic_put_data(p, addr, addrlen);
+ p = quic_put_int(p, ts, tslen);
+ quic_put_data(p, conn_id->data, conn_id->len);
+
+ err = quic_crypto_token_protect(crypto, token_buf, len,
+ sizeof(flag) + addrlen + tslen, true);
+ if (err)
+ goto out;
+
+ memcpy(token, token_buf, len);
+ *tlen = len;
+out:
+ kfree_sensitive(token_buf);
+ return err;
+}
+
+/* Validate a Retry or address validation token.
+ *
+ * Decrypts the token using derived key and IV. Checks that the decrypted
+ * address matches the provided address, validates the embedded timestamp
+ * against current time with a version-specific timeout. If applicable, it
+ * extracts and returns the original destination connection ID (ODCID) for
+ * Retry packets.
+ *
+ * Returns 0 if the token is valid, -EINVAL if invalid, or another negative
+ * error code.
+ */
+int quic_crypto_verify_token(struct quic_crypto *crypto, void *addr,
+ u32 addrlen, struct quic_conn_id *conn_id,
+ u8 *token, u32 len)
+{
+ u64 t, ts = quic_ktime_get_us(), timeout = QUIC_TOKEN_TIMEOUT_RETRY;
+ u8 *token_buf, *p, flag;
+ u32 tslen = sizeof(ts);
+ int err;
+
+ if (len < sizeof(flag) + addrlen + tslen + QUIC_TAG_LEN)
+ return -EINVAL;
+ token_buf = kmemdup(token, len, GFP_KERNEL);
+ if (!token_buf)
+ return -ENOMEM;
+
+ err = quic_crypto_token_protect(crypto, token_buf, len,
+ sizeof(flag) + addrlen + tslen, false);
+ if (err)
+ goto out;
+
+ err = -EINVAL;
+ p = token_buf;
+ flag = *p++;
+ len -= sizeof(flag);
+ if (crypto_memneq(p, addr, addrlen))
+ goto out;
+
+ p += addrlen;
+ len -= addrlen;
+ if (flag == QUIC_TOKEN_FLAG_REGULAR)
+ timeout = QUIC_TOKEN_TIMEOUT_REGULAR;
+ if (!quic_get_int(&p, &len, &t, tslen) || t > ts || ts - t > timeout)
+ goto out;
+
+ len -= QUIC_TAG_LEN;
+ if (len > QUIC_CONN_ID_MAX_LEN)
+ goto out;
+
+ if (flag == QUIC_TOKEN_FLAG_RETRY)
+ quic_conn_id_update(conn_id, p, len);
+ err = 0;
+out:
+ kfree_sensitive(token_buf);
+ return err;
+}
+
+/* Derive a secret using HKDF-Extract and HKDF-Expand with the given label.
+ * Used to generate a stateless reset token or session resumption master key.
+ */
+int quic_crypto_derive_secret(struct quic_crypto *crypto, void *data, u32 len,
+ char *label, u8 *srt, u32 srt_len)
+{
+ struct crypto_shash *tfm = crypto->secret_tfm;
+ u8 secret[TLS_CIPHER_AES_GCM_128_SECRET_SIZE];
+ struct quic_data salt, s, l, k;
+ int err;
+
+ quic_data(&salt, data, len);
+ quic_data(&k, crypto->tx_secret[1], TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ quic_data(&s, secret, TLS_CIPHER_AES_GCM_128_SECRET_SIZE);
+ err = quic_crypto_hkdf_extract(tfm, &salt, &k, &s);
+ if (err)
+ goto out;
+
+ quic_data(&l, label, strlen(label));
+ quic_data(&k, srt, srt_len);
+ err = quic_crypto_hkdf_expand(tfm, &s, &l, &k);
+out:
+ memzero_explicit(secret, sizeof(secret));
+ return err;
+}
diff --git a/net/quic/crypto.h b/net/quic/crypto.h
new file mode 100644
index 0000000000000..77281a824f721
--- /dev/null
+++ b/net/quic/crypto.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_TAG_LEN 16
+#define QUIC_IV_LEN 12
+#define QUIC_KEY_LEN 32
+#define QUIC_SECRET_LEN 48
+
+#define QUIC_TOKEN_FLAG_REGULAR 0
+#define QUIC_TOKEN_FLAG_RETRY 1
+#define QUIC_TOKEN_TIMEOUT_RETRY 3000000
+#define QUIC_TOKEN_TIMEOUT_REGULAR 600000000
+
+struct quic_cipher {
+ u32 secretlen; /* Length of the traffic secret */
+ u32 keylen; /* Length of the AEAD key */
+
+ char *shash; /* Name of hash algorithm used for key derivation */
+ char *aead; /* Name of AEAD algorithm used for payload en/decryption */
+ char *skc; /* Name of cipher algorithm used for header protection */
+};
+
+struct quic_crypto {
+ struct crypto_skcipher *tx_hp_tfm; /* TX header protection tfm */
+ struct crypto_skcipher *rx_hp_tfm; /* RX header protection tfm */
+ struct crypto_shash *secret_tfm; /* Key derivation (HKDF) tfm */
+ struct crypto_aead *tx_tfm[2]; /* AEAD tfm for TX (key phase 0 and 1) */
+ struct crypto_aead *rx_tfm[2]; /* AEAD tfm for RX (key phase 0 and 1) */
+
+ const struct quic_cipher *cipher; /* Cipher info (selected cipher) */
+ u32 cipher_type; /* Cipher suite (e.g., AES_GCM_128, etc.) */
+
+ u8 tx_secret[2][QUIC_SECRET_LEN]; /* TX secret (key phase 0 and 1) */
+ u8 rx_secret[2][QUIC_SECRET_LEN]; /* RX secret (key phase 0 and 1) */
+ u8 tx_iv[2][QUIC_IV_LEN]; /* IVs for TX (key phase 0 and 1) */
+ u8 rx_iv[2][QUIC_IV_LEN]; /* IVs for RX (key phase 0 and 1) */
+ atomic_t async_pending[2]; /* Async pending count (key phase 0 and 1) */
+
+ /* Timestamp 1st packet sent after key update */
+ u64 key_update_send_time;
+ u64 key_update_time; /* Timestamp old keys retained after key update */
+ u32 version; /* QUIC version in use */
+
+ u8 ticket_ready:1; /* True if a session ticket is ready to read */
+ u8 key_pending:1; /* A key update is in progress */
+ u8 key_derived:1; /* Key derived for the key update */
+ u8 send_ready:1; /* TX encryption context is initialized */
+ u8 recv_ready:1; /* RX decryption context is initialized */
+ u8 key_phase:1; /* Current key phase being used (0 or 1) */
+
+ u64 send_offset; /* Number of handshake bytes sent by user */
+ u64 recv_offset; /* Number of handshake bytes read by user */
+};
+
+int quic_crypto_set_secret(struct quic_crypto *crypto,
+ struct quic_crypto_secret *srt, u32 version);
+int quic_crypto_set_cipher(struct quic_crypto *crypto, u32 type);
+int quic_crypto_key_update(struct quic_crypto *crypto);
+
+int quic_crypto_encrypt(struct quic_crypto *crypto, struct sk_buff *skb,
+ gfp_t gfp);
+int quic_crypto_decrypt(struct quic_crypto *crypto, struct sk_buff *skb,
+ gfp_t gfp);
+
+int quic_crypto_derive_secret(struct quic_crypto *crypto, void *data, u32 len,
+ char *label, u8 *srt, u32 srt_len);
+int quic_crypto_initial_keys_install(struct quic_crypto *crypto,
+ struct quic_conn_id *conn_id,
+ u32 version, bool is_serv);
+int quic_crypto_set_token_secret(struct quic_crypto *crypto);
+
+int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,
+ u32 addrlen, struct quic_conn_id *conn_id,
+ u8 *token, u32 *tlen);
+int quic_crypto_get_retry_tag(struct quic_crypto *crypto, struct sk_buff *skb,
+ struct quic_conn_id *odcid, u32 version, u8 *tag);
+int quic_crypto_verify_token(struct quic_crypto *crypto, void *addr,
+ u32 addrlen, struct quic_conn_id *conn_id,
+ u8 *token, u32 len);
+
+void quic_crypto_free(struct quic_crypto *crypto);
diff --git a/net/quic/family.c b/net/quic/family.c
new file mode 100644
index 0000000000000..f61a8203b2e17
--- /dev/null
+++ b/net/quic/family.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/inet_common.h>
+#include <net/udp_tunnel.h>
+#include <linux/icmp.h>
+
+#include "common.h"
+#include "family.h"
+
+static bool quic_v4_is_any_addr(union quic_addr *addr)
+{
+ return addr->v4.sin_addr.s_addr == htonl(INADDR_ANY);
+}
+
+static bool quic_v6_is_any_addr(union quic_addr *addr)
+{
+ return ipv6_addr_any(&addr->v6.sin6_addr);
+}
+
+static void quic_v4_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,
+ union quic_addr *a)
+{
+ conf->family = AF_INET;
+ conf->local_ip.s_addr = a->v4.sin_addr.s_addr;
+ conf->local_udp_port = a->v4.sin_port;
+ conf->bind_ifindex = sk->sk_bound_dev_if;
+}
+
+static void quic_v6_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,
+ union quic_addr *a)
+{
+ conf->family = AF_INET6;
+ conf->local_ip6 = a->v6.sin6_addr;
+ conf->local_udp_port = a->v6.sin6_port;
+ conf->use_udp6_rx_checksums = true;
+ conf->use_udp6_tx_checksums = true;
+ conf->ipv6_v6only = ipv6_only_sock(sk);
+ conf->bind_ifindex = quic_get_dev_if(sk, a);
+}
+
+static int quic_v4_flow_route(struct sock *sk, union quic_addr *da,
+ union quic_addr *sa, struct flowi *fl)
+{
+ struct flowi4 *fl4;
+ struct rtable *rt;
+
+ if (__sk_dst_check(sk, 0))
+ return 1;
+
+ memset(fl, 0x00, sizeof(*fl));
+ fl4 = &fl->u.ip4;
+ fl4->saddr = sa->v4.sin_addr.s_addr;
+ fl4->fl4_sport = sa->v4.sin_port;
+ fl4->daddr = da->v4.sin_addr.s_addr;
+ fl4->fl4_dport = da->v4.sin_port;
+ fl4->flowi4_proto = IPPROTO_UDP;
+ fl4->flowi4_oif = quic_get_dev_if(sk, da);
+
+ fl4->flowi4_scope = ip_sock_rt_scope(sk);
+ fl4->flowi4_dscp = inet_sk_dscp(inet_sk(sk));
+
+ fl4->flowi4_uid = sk_uid(sk);
+ fl4->flowi4_mark = sk->sk_mark;
+
+ rt = ip_route_output_flow(sock_net(sk), fl4, sk);
+ if (IS_ERR(rt))
+ return PTR_ERR(rt);
+
+ if (quic_v4_is_any_addr(sa)) {
+ sa->v4.sin_family = AF_INET;
+ sa->v4.sin_addr.s_addr = fl4->saddr;
+ }
+ sk_setup_caps(sk, &rt->dst);
+ return 0;
+}
+
+static int quic_v6_flow_route(struct sock *sk, union quic_addr *da,
+ union quic_addr *sa, struct flowi *fl)
+{
+ struct ipv6_pinfo *np = inet6_sk(sk);
+ struct in6_addr *final_p, final;
+ struct ip6_flowlabel *flowlabel;
+ struct dst_entry *dst;
+ struct flowi6 *fl6;
+
+ if (__sk_dst_check(sk, np->dst_cookie))
+ return 1;
+
+ memset(fl, 0x00, sizeof(*fl));
+ fl6 = &fl->u.ip6;
+ fl6->saddr = sa->v6.sin6_addr;
+ fl6->fl6_sport = sa->v6.sin6_port;
+ fl6->daddr = da->v6.sin6_addr;
+ fl6->fl6_dport = da->v6.sin6_port;
+ fl6->flowi6_proto = IPPROTO_UDP;
+ fl6->flowi6_oif = quic_get_dev_if(sk, da);
+
+ if (inet6_test_bit(SNDFLOW, sk)) {
+ fl6->flowlabel = (da->v6.sin6_flowinfo & IPV6_FLOWINFO_MASK);
+ if (fl6->flowlabel & IPV6_FLOWLABEL_MASK) {
+ flowlabel = fl6_sock_lookup(sk, fl6->flowlabel);
+ if (IS_ERR(flowlabel))
+ return -EINVAL;
+ fl6_sock_release(flowlabel);
+ }
+ }
+ fl6->flowlabel = ip6_make_flowinfo(np->tclass, fl6->flowlabel);
+
+ fl6->flowi6_uid = sk_uid(sk);
+ fl6->flowi6_mark = sk->sk_mark;
+
+ rcu_read_lock();
+ final_p = fl6_update_dst(fl6, rcu_dereference(np->opt), &final);
+ rcu_read_unlock();
+
+ dst = ip6_dst_lookup_flow(sock_net(sk), sk, fl6, final_p);
+ if (IS_ERR(dst))
+ return PTR_ERR(dst);
+
+ if (quic_v6_is_any_addr(sa)) {
+ sa->v6.sin6_family = AF_INET6;
+ sa->v6.sin6_addr = fl6->saddr;
+ if ((ipv6_addr_type(&fl6->saddr) & IPV6_ADDR_LINKLOCAL))
+ sa->v6.sin6_scope_id = fl6->flowi6_oif;
+ }
+ ip6_dst_store(sk, dst, false, false);
+ return 0;
+}
+
+static void quic_v4_lower_xmit(struct sock *sk, struct sk_buff *skb,
+ struct flowi *fl)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ struct flowi4 *fl4 = &fl->u.ip4;
+ u8 tos = inet_sk(sk)->tos, ttl;
+ struct dst_entry *dst;
+ __be16 df = 0;
+
+ pr_debug("%s: skb: %p, len: %d, num: %lld, %pI4:%d -> %pI4:%d\n",
+ __func__, skb, skb->len, cb->number, &fl4->saddr,
+ ntohs(fl4->fl4_sport), &fl4->daddr, ntohs(fl4->fl4_dport));
+
+ dst = sk_dst_get(sk);
+ if (!dst) {
+ kfree_skb(skb);
+ return;
+ }
+ if (ip_dont_fragment(sk, dst) && !skb->ignore_df)
+ df = htons(IP_DF);
+
+ if (cb->ecn)
+ tos = (tos & ~INET_ECN_MASK) | cb->ecn;
+ ttl = (u8)ip4_dst_hoplimit(dst);
+ udp_tunnel_xmit_skb((struct rtable *)dst, sk, skb, fl4->saddr,
+ fl4->daddr, tos, ttl, df, fl4->fl4_sport,
+ fl4->fl4_dport, false, false, 0);
+}
+
+static void quic_v6_lower_xmit(struct sock *sk, struct sk_buff *skb,
+ struct flowi *fl)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ u8 tc = inet6_sk(sk)->tclass, ttl;
+ struct flowi6 *fl6 = &fl->u.ip6;
+ struct dst_entry *dst;
+ __be32 label;
+
+ pr_debug("%s: skb: %p, len: %d, num: %lld, %pI6c:%d -> %pI6c:%d\n",
+ __func__, skb, skb->len, cb->number, &fl6->saddr,
+ ntohs(fl6->fl6_sport), &fl6->daddr, ntohs(fl6->fl6_dport));
+
+ dst = sk_dst_get(sk);
+ if (!dst) {
+ kfree_skb(skb);
+ return;
+ }
+
+ if (cb->ecn)
+ tc = (tc & ~INET_ECN_MASK) | cb->ecn;
+ ttl = (u8)ip6_dst_hoplimit(dst);
+ label = ip6_make_flowlabel(sock_net(sk), skb, fl6->flowlabel, true,
+ fl6);
+ udp_tunnel6_xmit_skb(dst, sk, skb, NULL, &fl6->saddr, &fl6->daddr, tc,
+ ttl, label, fl6->fl6_sport, fl6->fl6_dport, false,
+ 0);
+}
+
+static void quic_v4_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,
+ union quic_addr *sa)
+{
+ struct udphdr *uh = udp_hdr(skb);
+
+ sa->v4.sin_family = AF_INET;
+ sa->v4.sin_port = uh->source;
+ sa->v4.sin_addr.s_addr = ip_hdr(skb)->saddr;
+
+ da->v4.sin_family = AF_INET;
+ da->v4.sin_port = uh->dest;
+ da->v4.sin_addr.s_addr = ip_hdr(skb)->daddr;
+}
+
+static void quic_v6_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,
+ union quic_addr *sa)
+{
+ struct udphdr *uh = udp_hdr(skb);
+
+ sa->v6.sin6_family = AF_INET6;
+ sa->v6.sin6_port = uh->source;
+ sa->v6.sin6_addr = ipv6_hdr(skb)->saddr;
+ sa->v6.sin6_scope_id = skb->skb_iif;
+
+ da->v6.sin6_family = AF_INET6;
+ da->v6.sin6_port = uh->dest;
+ da->v6.sin6_addr = ipv6_hdr(skb)->daddr;
+ da->v6.sin6_scope_id = skb->skb_iif;
+}
+
+static int quic_v4_get_mtu_info(struct sk_buff *skb, u32 *info)
+{
+ struct icmphdr *hdr;
+
+ hdr = (struct icmphdr *)(skb_network_header(skb) -
+ sizeof(struct icmphdr));
+ if (hdr->type == ICMP_DEST_UNREACH && hdr->code == ICMP_FRAG_NEEDED) {
+ *info = ntohs(hdr->un.frag.mtu);
+ return 0;
+ }
+
+ /* Defer other types' processing to UDP error handler. */
+ return -EINVAL;
+}
+
+static int quic_v6_get_mtu_info(struct sk_buff *skb, u32 *info)
+{
+ struct icmp6hdr *hdr;
+
+ hdr = (struct icmp6hdr *)(skb_network_header(skb) -
+ sizeof(struct icmp6hdr));
+ if (hdr->icmp6_type == ICMPV6_PKT_TOOBIG) {
+ *info = ntohl(hdr->icmp6_mtu);
+ return 0;
+ }
+
+ /* Defer other types' processing to UDP error handler. */
+ return -EINVAL;
+}
+
+static bool quic_v4_cmp_sk_addr(struct sock *sk, union quic_addr *a,
+ union quic_addr *addr)
+{
+ if (a->v4.sin_port != addr->v4.sin_port)
+ return false;
+ if (a->v4.sin_family != addr->v4.sin_family)
+ return false;
+ /* Match only if socket is also ANY-bound. */
+ if (addr->v4.sin_addr.s_addr == htonl(INADDR_ANY))
+ return a->v4.sin_addr.s_addr == htonl(INADDR_ANY);
+ if (a->v4.sin_addr.s_addr == htonl(INADDR_ANY))
+ return true;
+ return a->v4.sin_addr.s_addr == addr->v4.sin_addr.s_addr;
+}
+
+static bool quic_v6_cmp_sk_addr(struct sock *sk, union quic_addr *a,
+ union quic_addr *addr)
+{
+ if (a->sa.sa_family == AF_INET && addr->sa.sa_family == AF_INET)
+ return quic_v4_cmp_sk_addr(sk, a, addr);
+
+ if (a->v4.sin_port != addr->v4.sin_port)
+ return false;
+
+ if (a->sa.sa_family != addr->sa.sa_family) {
+ if (ipv6_only_sock(sk) || a->sa.sa_family == AF_INET)
+ return false;
+ return quic_is_any_addr(a);
+ }
+
+ /* Match only if socket is also ANY-bound. */
+ if (ipv6_addr_any(&addr->v6.sin6_addr))
+ return ipv6_addr_any(&a->v6.sin6_addr);
+ if (ipv6_addr_any(&a->v6.sin6_addr))
+ return true;
+ if (!ipv6_addr_equal(&a->v6.sin6_addr, &addr->v6.sin6_addr))
+ return false;
+ if ((ipv6_addr_type(&a->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) &&
+ a->v6.sin6_scope_id && addr->v6.sin6_scope_id &&
+ a->v6.sin6_scope_id != addr->v6.sin6_scope_id)
+ return false;
+ return true;
+}
+
+static int quic_v4_get_sk_addr(struct socket *sock, struct sockaddr *uaddr,
+ int peer)
+{
+ return inet_getname(sock, uaddr, peer);
+}
+
+static int quic_v6_get_sk_addr(struct socket *sock, struct sockaddr *uaddr,
+ int peer)
+{
+ union quic_addr *a = quic_addr(uaddr);
+ int ret;
+
+ ret = inet6_getname(sock, uaddr, peer);
+ if (ret < 0)
+ return ret;
+
+ if (a->sa.sa_family == AF_INET6 &&
+ ipv6_addr_v4mapped(&a->v6.sin6_addr)) {
+ a->v4.sin_family = AF_INET;
+ a->v4.sin_port = a->v6.sin6_port;
+ a->v4.sin_addr.s_addr = a->v6.sin6_addr.s6_addr32[3];
+ }
+
+ if (a->sa.sa_family == AF_INET) {
+ memset(a->v4.sin_zero, 0, sizeof(a->v4.sin_zero));
+ return sizeof(struct sockaddr_in);
+ }
+ return sizeof(struct sockaddr_in6);
+}
+
+#define quic_af_ipv4(a) ((a)->sa.sa_family == AF_INET)
+
+u32 quic_encap_len(union quic_addr *a)
+{
+ return (quic_af_ipv4(a) ? sizeof(struct iphdr) :
+ sizeof(struct ipv6hdr)) +
+ sizeof(struct udphdr);
+}
+
+bool quic_is_any_addr(union quic_addr *a)
+{
+ return quic_af_ipv4(a) ? quic_v4_is_any_addr(a) :
+ quic_v6_is_any_addr(a);
+}
+
+void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,
+ union quic_addr *a)
+{
+ quic_af_ipv4(a) ? quic_v4_udp_conf_init(sk, conf, a) :
+ quic_v6_udp_conf_init(sk, conf, a);
+}
+
+int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa,
+ struct flowi *fl)
+{
+ if (sa->sa.sa_family && da->sa.sa_family != sa->sa.sa_family)
+ return -EINVAL;
+
+ return quic_af_ipv4(da) ? quic_v4_flow_route(sk, da, sa, fl) :
+ quic_v6_flow_route(sk, da, sa, fl);
+}
+
+void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da,
+ struct flowi *fl)
+{
+ local_bh_disable();
+ quic_af_ipv4(da) ? quic_v4_lower_xmit(sk, skb, fl) :
+ quic_v6_lower_xmit(sk, skb, fl);
+ local_bh_enable();
+}
+
+#define quic_skb_ipv4(skb) (ip_hdr(skb)->version == 4)
+
+void quic_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,
+ union quic_addr *sa)
+{
+ memset(sa, 0, sizeof(*sa));
+ memset(da, 0, sizeof(*da));
+ quic_skb_ipv4(skb) ? quic_v4_get_msg_addrs(skb, da, sa) :
+ quic_v6_get_msg_addrs(skb, da, sa);
+}
+
+int quic_get_mtu_info(struct sk_buff *skb, u32 *info)
+{
+ return quic_skb_ipv4(skb) ? quic_v4_get_mtu_info(skb, info) :
+ quic_v6_get_mtu_info(skb, info);
+}
+
+#define quic_pf_ipv4(sk) ((sk)->sk_family == PF_INET)
+
+bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a,
+ union quic_addr *addr)
+{
+ return quic_pf_ipv4(sk) ? quic_v4_cmp_sk_addr(sk, a, addr) :
+ quic_v6_cmp_sk_addr(sk, a, addr);
+}
+
+int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, int peer)
+{
+ return quic_pf_ipv4(sock->sk) ? quic_v4_get_sk_addr(sock, a, peer) :
+ quic_v6_get_sk_addr(sock, a, peer);
+}
+
+int quic_get_dev_if(struct sock *sk, union quic_addr *a)
+{
+ if (!quic_af_ipv4(a) &&
+ ipv6_addr_type(&a->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL &&
+ a->v6.sin6_scope_id)
+ return a->v6.sin6_scope_id;
+
+ return sk->sk_bound_dev_if;
+}
+
+void quic_set_skb_iif(struct sk_buff *skb)
+{
+ /* Save the inet/inet6 iif before skb dst/cb are cleared. */
+ skb->skb_iif = quic_skb_ipv4(skb) ? inet_iif(skb) : inet6_iif(skb);
+}
+
+int quic_common_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ return quic_pf_ipv4(sk) ?
+ ip_setsockopt(sk, level, optname, optval, optlen) :
+ ipv6_setsockopt(sk, level, optname, optval, optlen);
+}
+
+int quic_common_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ return quic_pf_ipv4(sk) ?
+ ip_getsockopt(sk, level, optname, optval, optlen) :
+ ipv6_getsockopt(sk, level, optname, optval, optlen);
+}
+
+bool quic_sk_accept_pmtu(struct sock *sk, struct sk_buff *skb)
+{
+ return quic_skb_ipv4(skb) ? ip_sk_accept_pmtu(sk) :
+ ip6_sk_accept_pmtu(sk);
+}
+
+void quic_sk_destruct(struct sock *sk)
+{
+ quic_pf_ipv4(sk) ? inet_sock_destruct(sk) : inet6_sock_destruct(sk);
+}
diff --git a/net/quic/family.h b/net/quic/family.h
new file mode 100644
index 0000000000000..02342c4b7c99e
--- /dev/null
+++ b/net/quic/family.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_PORT_LEN 2
+#define QUIC_ADDR4_LEN 4
+#define QUIC_ADDR6_LEN 16
+
+#define QUIC_PREF_ADDR_LEN \
+ (QUIC_ADDR4_LEN + QUIC_PORT_LEN + QUIC_ADDR6_LEN + QUIC_PORT_LEN)
+
+bool quic_is_any_addr(union quic_addr *a);
+u32 quic_encap_len(union quic_addr *a);
+
+void quic_lower_xmit(struct sock *sk, struct sk_buff *skb, union quic_addr *da,
+ struct flowi *fl);
+int quic_flow_route(struct sock *sk, union quic_addr *da, union quic_addr *sa,
+ struct flowi *fl);
+void quic_udp_conf_init(struct sock *sk, struct udp_port_cfg *conf,
+ union quic_addr *a);
+
+void quic_get_msg_addrs(struct sk_buff *skb, union quic_addr *da,
+ union quic_addr *sa);
+int quic_get_mtu_info(struct sk_buff *skb, u32 *info);
+
+bool quic_cmp_sk_addr(struct sock *sk, union quic_addr *a,
+ union quic_addr *addr);
+int quic_get_sk_addr(struct socket *sock, struct sockaddr *a, int peer);
+
+int quic_get_dev_if(struct sock *sk, union quic_addr *a);
+void quic_set_skb_iif(struct sk_buff *skb);
+
+int quic_common_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen);
+int quic_common_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen);
+bool quic_sk_accept_pmtu(struct sock *sk, struct sk_buff *skb);
+void quic_sk_destruct(struct sock *sk);
diff --git a/net/quic/packet.c b/net/quic/packet.c
new file mode 100644
index 0000000000000..db87c32d7469d
--- /dev/null
+++ b/net/quic/packet.c
@@ -0,0 +1,1055 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include "socket.h"
+
+#define QUIC_HLEN 1
+
+#define QUIC_LONG_HLEN(dcid, scid) \
+ (QUIC_HLEN + QUIC_VERSION_LEN + 1 + (dcid)->len + 1 + (scid)->len)
+
+#define QUIC_VERSION_NUM 2
+
+/* Supported QUIC versions and their compatible versions. Used for Compatible
+ * Version Negotiation in rfc9368#section-2.3.
+ */
+static u32 quic_versions[QUIC_VERSION_NUM][4] = {
+ /* Version, Compatible Versions */
+ { QUIC_VERSION_V1, QUIC_VERSION_V2, QUIC_VERSION_V1, 0 },
+ { QUIC_VERSION_V2, QUIC_VERSION_V2, QUIC_VERSION_V1, 0 },
+};
+
+/* Get the compatible version list for a given QUIC version. */
+u32 *quic_packet_compatible_versions(u32 version)
+{
+ u8 i;
+
+ for (i = 0; i < QUIC_VERSION_NUM; i++)
+ if (version == quic_versions[i][0])
+ return quic_versions[i];
+ return NULL;
+}
+
+/* Convert version-specific type to internal standard packet type. */
+static u8 quic_packet_version_get_type(u32 version, u8 type)
+{
+ if (version == QUIC_VERSION_V1)
+ return type;
+
+ switch (type) {
+ case QUIC_PACKET_INITIAL_V2:
+ return QUIC_PACKET_INITIAL;
+ case QUIC_PACKET_0RTT_V2:
+ return QUIC_PACKET_0RTT;
+ case QUIC_PACKET_HANDSHAKE_V2:
+ return QUIC_PACKET_HANDSHAKE;
+ case QUIC_PACKET_RETRY_V2:
+ return QUIC_PACKET_RETRY;
+ default:
+ return QUIC_PACKET_INVALID;
+ }
+}
+
+/* Extracts a QUIC Connection ID from a buffer in the long header packet. */
+static int quic_packet_get_connid(struct quic_conn_id *connid, u8 **pp,
+ u32 *plen)
+{
+ u64 len;
+
+ if (!quic_get_int(pp, plen, &len, 1) ||
+ len > *plen || len > QUIC_CONN_ID_MAX_LEN)
+ return -EINVAL;
+
+ quic_conn_id_update(connid, *pp, len);
+ *plen -= len;
+ *pp += len;
+ return 0;
+}
+
+/* Parse QUIC version and connection IDs (DCID and SCID) from a Long header
+ * packet buffer.
+ */
+static int quic_packet_get_long_header(struct quic_conn_id *dcid,
+ struct quic_conn_id *scid, u32 *version,
+ u8 **pp, u32 *plen)
+{
+ int err;
+ u64 v;
+
+ *pp += QUIC_HLEN;
+ *plen -= QUIC_HLEN;
+
+ if (!quic_get_int(pp, plen, &v, QUIC_VERSION_LEN))
+ return -EINVAL;
+ if (version)
+ *version = v;
+
+ err = quic_packet_get_connid(dcid, pp, plen);
+ if (err)
+ return err;
+ if (!scid)
+ return 0;
+ return quic_packet_get_connid(scid, pp, plen);
+}
+
+/* Extracts a QUIC token from a buffer in the Client Initial packet. */
+static int quic_packet_get_token(struct quic_data *token, u8 **pp, u32 *plen)
+{
+ u64 len;
+
+ if (!quic_get_var(pp, plen, &len) || len > *plen)
+ return -EINVAL;
+ quic_data(token, *pp, len);
+ *plen -= len;
+ *pp += len;
+ return 0;
+}
+
+/* Process PMTU reduction event on a QUIC socket. */
+void quic_packet_rcv_err_pmtu(struct sock *sk)
+{
+ struct quic_path_group *paths = quic_paths(sk);
+ struct quic_packet *packet = quic_packet(sk);
+ u32 pathmtu, info, taglen;
+ struct dst_entry *dst;
+ bool reset_timer;
+
+ if (quic_is_closed(sk))
+ return;
+
+ info = clamp(paths->mtu_info, QUIC_PATH_MIN_PMTU, QUIC_PATH_MAX_PMTU);
+ /* If PLPMTUD is not enabled, update MSS using route and ICMP info. */
+ if (!paths->plpmtud_interval) {
+ if (quic_packet_route(sk))
+ return;
+
+ dst = __sk_dst_get(sk);
+ if (dst)
+ dst->ops->update_pmtu(dst, sk, NULL, info, true);
+ quic_packet_mss_update(sk, info - packet->hlen);
+ return;
+ }
+ /* PLPMTUD is enabled: adjust to smaller PMTU, subtract headers and
+ * AEAD tag. Also notify the QUIC path layer for possible state
+ * changes and probing.
+ */
+ packet->level = QUIC_CRYPTO_APP;
+ taglen = quic_packet_taglen(packet);
+ info = info - packet->hlen - taglen;
+ pathmtu = quic_path_pl_toobig(paths, info, &reset_timer);
+ if (reset_timer)
+ quic_timer_reset(sk, QUIC_TIMER_PMTU, paths->plpmtud_interval);
+ if (pathmtu)
+ quic_packet_mss_update(sk, pathmtu + taglen);
+}
+
+/* Handle ICMP Toobig packet and update QUIC socket path MTU. */
+static int quic_packet_rcv_err(struct sock *sk, struct sk_buff *skb)
+{
+ union quic_addr daddr, saddr;
+ u32 info;
+
+ /* ICMP embeds the original outgoing QUIC packet, so saddr/daddr are
+ * reversed when parsed. Only address-based socket lookup is possible
+ * in this case.
+ */
+ quic_get_msg_addrs(skb, &saddr, &daddr);
+ sk = quic_sock_lookup(skb, &daddr, &saddr, sk, NULL);
+ if (!sk)
+ return -ENOENT;
+
+ if (quic_get_mtu_info(skb, &info) || !quic_sk_accept_pmtu(sk, skb)) {
+ sock_put(sk);
+ return 0;
+ }
+
+ /* Success: update socket path MTU info. */
+ bh_lock_sock(sk);
+ quic_paths(sk)->mtu_info = info;
+ if (sock_owned_by_user(sk)) {
+ /* Socket locked by userspace. Defer MTU processing via
+ * release_cb. Hold socket reference to prevent it being
+ * freed before deferral.
+ */
+ if (!test_and_set_bit(QUIC_MTU_REDUCED_DEFERRED,
+ &sk->sk_tsq_flags))
+ sock_hold(sk);
+ goto out;
+ }
+ /* Otherwise, process the MTU reduction now. */
+ quic_packet_rcv_err_pmtu(sk);
+out:
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ return 1;
+}
+
+/* Queue a packet for later processing when sleeping is allowed. */
+static int quic_packet_deferred_schedule(struct sk_buff *skb)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ struct sock *sk = skb->sk;
+ int len = skb->truesize;
+
+ if (cb->backlog)
+ return 0;
+
+ if (sk_rmem_alloc_get(sk) + len > sk->sk_rcvbuf ||
+ !__sk_rmem_schedule(sk, len, false)) {
+ QUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+ cb->backlog = 1;
+ skb_set_owner_r(skb, sk);
+ __skb_queue_tail(&quic_packet(sk)->deferred_list, skb);
+
+ sock_hold(sk);
+ if (!queue_work(quic_wq, quic_work(sk)))
+ sock_put(sk);
+ return 1;
+}
+
+#define TLS_MT_CLIENT_HELLO 1
+#define TLS_EXT_alpn 16
+
+/* TLS Client Hello Msg:
+ *
+ * uint16 ProtocolVersion;
+ * opaque Random[32];
+ * uint8 CipherSuite[2];
+ *
+ * struct {
+ * ExtensionType extension_type;
+ * opaque extension_data<0..2^16-1>;
+ * } Extension;
+ *
+ * struct {
+ * ProtocolVersion legacy_version = 0x0303;
+ * Random rand;
+ * opaque legacy_session_id<0..32>;
+ * CipherSuite cipher_suites<2..2^16-2>;
+ * opaque legacy_compression_methods<1..2^8-1>;
+ * Extension extensions<8..2^16-1>;
+ * } ClientHello;
+ */
+
+#define TLS_CH_RANDOM_LEN 32
+#define TLS_CH_VERSION_LEN 2
+#define TLS_MAX_EXTENSIONS 128
+
+#define QUIC_FRAME_CRYPTO 0x06
+
+/* Decrypt Initial packet and extract ALPN from TLS ClientHello for ALPN-based
+ * socket demultiplexing. Marks packet as decrypted (cb->resume = 1) to avoid
+ * redundant decryption later.
+ */
+static int quic_packet_get_alpn(struct sk_buff *skb, struct quic_data *alpn)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ int err, found = 0, exts = 0;
+ struct quic_crypto *crypto;
+ struct quic_packet *packet;
+ struct sock *sk = skb->sk;
+ u64 length, offset, type;
+ struct net *net;
+ u32 len;
+ u8 *p;
+
+ /* Install initial keys for decryption. */
+ crypto = quic_crypto(sk, QUIC_CRYPTO_INITIAL);
+ packet = quic_packet(sk);
+ err = quic_crypto_initial_keys_install(crypto, &packet->dcid,
+ packet->version, true);
+ if (err)
+ return err;
+ cb->sync = 1;
+ net = sock_net(sk);
+ err = quic_crypto_decrypt(crypto, skb, GFP_KERNEL);
+ if (err) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_DECDROP);
+ return err;
+ }
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_DECFASTPATHS);
+ cb->resume = 1; /* Mark this packet as already decrypted. */
+
+ /* Find the QUIC CRYPTO frame. */
+ p = skb->data + cb->number_offset + cb->number_len;
+ len = cb->length - cb->number_len - QUIC_TAG_LEN;
+ for (; len && !(*p); p++, len--) /* Skip the padding frame. */
+ ;
+ if (!len-- || *p++ != QUIC_FRAME_CRYPTO)
+ return 0;
+ if (!quic_get_var(&p, &len, &offset) || offset)
+ return 0;
+ if (!quic_get_var(&p, &len, &length) || length > (u64)len)
+ return 0;
+ if (len > (u32)length) /* Cap len to crypto frame length. */
+ len = length;
+
+ err = -EINVAL;
+ /* Verify handshake message type (ClientHello) and its length. */
+ if (!quic_get_int(&p, &len, &type, 1) || type != TLS_MT_CLIENT_HELLO)
+ return err;
+ if (!quic_get_int(&p, &len, &length, 3) ||
+ len < TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN ||
+ length < TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN)
+ return err;
+ if (len > (u32)length) /* Cap len to handshake msg length. */
+ len = length;
+ /* Skip legacy_version (2 bytes) + random (32 bytes). */
+ p += TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN;
+ len -= TLS_CH_RANDOM_LEN + TLS_CH_VERSION_LEN;
+ /* legacy_session_id_len must be zero (QUIC requirement). */
+ if (!quic_get_int(&p, &len, &length, 1) || length)
+ return err;
+
+ /* Skip cipher_suites (2 bytes length + variable data). */
+ if (!quic_get_int(&p, &len, &length, 2) || length > (u64)len)
+ return err;
+ len -= length;
+ p += length;
+
+ /* Skip legacy_compression_methods (1 byte length + variable data). */
+ if (!quic_get_int(&p, &len, &length, 1) || length > (u64)len)
+ return err;
+ len -= length;
+ p += length;
+
+ /* Read TLS extensions length (2 bytes). */
+ if (!quic_get_int(&p, &len, &length, 2))
+ return err;
+ if (len > (u32)length) /* Limit len to extensions length if larger. */
+ len = length;
+ while (len >= 4) { /* Scan extensions for ALPN (TLS_EXT_alpn). */
+ if (exts++ >= TLS_MAX_EXTENSIONS)
+ return err;
+ if (!quic_get_int(&p, &len, &type, 2))
+ break;
+ if (!quic_get_int(&p, &len, &length, 2))
+ break;
+ if (len < (u32)length) /* Incomplete TLS extensions. */
+ return 0;
+ if (type == TLS_EXT_alpn) { /* Found ALPN extension. */
+ if (length > QUIC_ALPN_MAX_LEN)
+ return err;
+ len = length;
+ found = 1;
+ break;
+ }
+ /* Skip non-ALPN extensions. */
+ p += length;
+ len -= length;
+ }
+ if (!found) { /* No ALPN ext: set alpn->len = 0 and alpn->data = p. */
+ quic_data(alpn, p, 0);
+ return 0;
+ }
+
+ /* Parse ALPN protocols list length (2 bytes). */
+ if (!quic_get_int(&p, &len, &length, 2) || length > (u64)len)
+ return err;
+ quic_data(alpn, p, length); /* Store ALPN list in alpn->data. */
+ len = length;
+ while (len) { /* Validate ALPN protocols list format. */
+ if (!quic_get_int(&p, &len, &length, 1) || length > (u64)len) {
+ /* Bad ALPN: set alpn->len = 0, alpn->data = NULL. */
+ quic_data(alpn, NULL, 0);
+ return err;
+ }
+ len -= length;
+ p += length;
+ }
+ pr_debug("%s: alpn_len: %d\n", __func__, alpn->len);
+ return 0;
+}
+
+/* Determine the QUIC socket associated with an incoming packet. */
+static struct sock *quic_packet_get_sock(struct sk_buff *skb, struct sock *usk)
+{
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ struct quic_conn_id dcid = {}, *conn_id;
+ struct net *net = sock_net(usk);
+ union quic_addr daddr, saddr;
+ struct quic_data alpns = {};
+ struct sock *sk = NULL;
+ u32 len = skb->len;
+ u8 *p = skb->data;
+ int err;
+
+ if (skb->len < QUIC_HLEN)
+ return ERR_PTR(-EINVAL);
+
+ if (quic_hdr(skb)->form == QUIC_PACKET_FORM_SHORT) {
+ /* Short header path. */
+ if (skb->len < QUIC_HLEN + QUIC_CONN_ID_DEF_LEN)
+ return ERR_PTR(-EINVAL);
+ /* Fast path: look up QUIC connection by fixed-length DCID
+ * (Currently, only QUIC_CONN_ID_DEF_LEN-length SCIDs are used).
+ */
+ conn_id = quic_conn_id_lookup(net, skb->data + QUIC_HLEN,
+ QUIC_CONN_ID_DEF_LEN);
+ if (conn_id) {
+ cb->seqno = quic_conn_id_number(conn_id);
+ /* Return associated socket. */
+ return quic_conn_id_sk(conn_id);
+ }
+
+ /* Fallback: listener socket lookup
+ * (May be used to send a stateless reset from a listen socket).
+ */
+ quic_get_msg_addrs(skb, &daddr, &saddr);
+ sk = quic_listen_sock_lookup(skb, &daddr, &saddr, usk, &alpns);
+ if (sk)
+ return sk;
+ /* Final fallback: address-based connection lookup
+ * (May be used to receive a stateless reset).
+ */
+ sk = quic_sock_lookup(skb, &daddr, &saddr, usk, NULL);
+ if (!sk)
+ return ERR_PTR(-ENOENT);
+ return sk;
+ }
+
+ /* Long header path. */
+ err = quic_packet_get_long_header(&dcid, NULL, NULL, &p, &len);
+ if (err)
+ return ERR_PTR(err);
+ /* Fast path: look up QUIC connection by parsed DCID. */
+ conn_id = quic_conn_id_lookup(net, dcid.data, dcid.len);
+ if (conn_id) {
+ cb->seqno = quic_conn_id_number(conn_id);
+ return quic_conn_id_sk(conn_id); /* Return associated socket. */
+ }
+
+ /* Fallback: address + DCID lookup
+ * (May be used for 0-RTT or a follow-up Client Initial packet).
+ */
+ quic_get_msg_addrs(skb, &daddr, &saddr);
+ sk = quic_sock_lookup(skb, &daddr, &saddr, usk, &dcid);
+ if (sk)
+ return sk;
+ /* Final fallback: listener socket lookup
+ * (Used for receiving the first Client Initial packet).
+ */
+ sk = quic_listen_sock_lookup(skb, &daddr, &saddr, usk, &alpns);
+ if (!sk)
+ return ERR_PTR(-ENOENT);
+ return sk;
+}
+
+/* Entry point for processing received QUIC packets. */
+int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp)
+{
+ struct net *net = sock_net(sk);
+ int err;
+
+ if (unlikely(icmp))
+ return quic_packet_rcv_err(sk, skb);
+
+ if (skb_linearize(skb)) {
+ err = -EINVAL;
+ goto err;
+ }
+
+ /* Look up socket from socket or connection IDs hash tables. */
+ sk = quic_packet_get_sock(skb, sk);
+ if (IS_ERR(sk)) {
+ err = PTR_ERR(sk);
+ goto err;
+ }
+
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ /* Socket is busy (owned by user context): queue to backlog. */
+ err = sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf));
+ if (err) {
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ goto err;
+ }
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_RCVBACKLOGS);
+ } else {
+ /* Socket not busy: process immediately. */
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_RCVFASTPATHS);
+ sk->sk_backlog_rcv(sk, skb); /* quic_backlog_rcv(). */
+ }
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ return 0;
+err:
+ pr_debug("%s: failed, len: %d, err: %d\n", __func__, skb->len, err);
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_RCVDROP);
+ kfree_skb(skb);
+ return err;
+}
+
+static int quic_packet_retry_create_and_xmit(struct sock *sk)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_packet_version_create_and_xmit(struct sock *sk, gfp_t gfp)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_packet_stateless_reset_create_and_xmit(struct sock *sk, u32 len,
+ gfp_t gfp)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_packet_refuse_close_create_and_xmit(struct sock *sk,
+ u32 errcode)
+{
+ return -EOPNOTSUPP;
+}
+
+/* Process an incoming packet on a listening QUIC socket.
+ *
+ * Depending on the packet type and state, this may involve creating a request
+ * socket for a new connection, responding with a Stateless Reset for
+ * unexpected Handshake or 1-RTT packets, issuing a Retry packet for address
+ * validation when needed, or sending a Version Negotiation packet if the
+ * client's QUIC version is unsupported.
+ */
+static int quic_packet_listen_process(struct sock *sk, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ u32 version, errcode, toff, len = skb->len;
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ u8 *p = skb->data, type, retry = 0;
+ struct net *net = sock_net(sk);
+ struct quic_conn_id odcid = {};
+ struct quic_request_sock *req;
+ struct quic_data alpns = {};
+ struct quic_crypto *crypto;
+ struct quic_data token;
+ u64 length;
+ int err;
+
+ if (quic_hshdr(skb)->form == QUIC_PACKET_FORM_SHORT) {
+ /* rfc9000#section-10.3:
+ *
+ * An endpoint MAY send a Stateless Reset in response to
+ * receiving a packet that it cannot associate with an active
+ * connection.
+ */
+ if (len < QUIC_HLEN + QUIC_CONN_ID_DEF_LEN) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ /* Read Destination address (packet->saddr) and Source address
+ * (packet->daddr).
+ */
+ quic_get_msg_addrs(skb, &packet->saddr, &packet->daddr);
+ /* We currently only issue Connection ID with size
+ * QUIC_CONN_ID_DEF_LEN.
+ */
+ quic_conn_id_update(&packet->dcid,
+ (u8 *)quic_hdr(skb) + QUIC_HLEN,
+ QUIC_CONN_ID_DEF_LEN);
+ /* Send a Stateless Reset for this 1-RTT packet. */
+ err = quic_packet_stateless_reset_create_and_xmit(sk, len, gfp);
+ consume_skb(skb);
+ return err;
+ }
+
+ /* Read VERSION, Destination Connection ID and Source Connection ID. */
+ err = quic_packet_get_long_header(&packet->dcid, &packet->scid,
+ &version, &p, &len);
+ if (err) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return err;
+ }
+
+ /* Read Destination address (packet->saddr) and Source address
+ * (packet->daddr).
+ */
+ quic_get_msg_addrs(skb, &packet->saddr, &packet->daddr);
+ req = quic_request_sock_lookup(sk);
+ if (req) /* If request sock already exists, enqueue packet directly. */
+ goto out;
+
+ if (quic_accept_sock_exists(sk, skb))
+ return 0; /* Already handled by matched accept socket. */
+
+ /* rfc9000#section-6.1:
+ *
+ * An endpoint MUST NOT send a Version Negotiation packet in response
+ * to receiving a Version Negotiation packet.
+ */
+ if (!version) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ if (!quic_packet_compatible_versions(version)) {
+ /* rfc9000#section-6.1:
+ *
+ * If the version selected by the client is not acceptable to
+ * the server, the server responds with a Version Negotiation
+ * packet. This includes a list of versions that the server
+ * will accept.
+ */
+ err = quic_packet_version_create_and_xmit(sk, gfp);
+ consume_skb(skb);
+ return err;
+ }
+
+ /* Read Packet Type. */
+ type = quic_packet_version_get_type(version, quic_hshdr(skb)->type);
+ if (type != QUIC_PACKET_INITIAL) { /* Send a Stateless Reset. */
+ err = quic_packet_stateless_reset_create_and_xmit(sk, skb->len,
+ gfp);
+ consume_skb(skb);
+ return err;
+ }
+
+ /* This Destination Connection ID MUST be at least 8 bytes in length. */
+ if (packet->dcid.len < QUIC_CONN_ID_DEF_LEN) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ err = quic_packet_get_token(&token, &p, &len); /* Read Token. */
+ if (err) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return err;
+ }
+ if (token.len)
+ toff = token.data - skb->data;
+
+ /* Associate skb with sk to ensure sk is valid if skb is delayed to
+ * process in workqueue.
+ */
+ WARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk));
+ packet->version = version;
+ if (!cb->resume && static_branch_unlikely(&quic_alpn_demux_key)) {
+ if (quic_packet_deferred_schedule(skb))
+ return -EINPROGRESS;
+ if (!quic_get_var(&p, &len, &length) || length > (u64)len) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ cb->length = (u16)length;
+ cb->number_offset = (u16)(p - skb->data);
+ err = quic_packet_get_alpn(skb, &alpns);
+ if (err) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_INVHDRDROP);
+ kfree_skb(skb);
+ return err;
+ }
+ if (quic_listen_sock_switch(skb, &alpns))
+ return 0; /* Switched to different listen socket. */
+ if (token.len) /* Update after skb->data may change. */
+ token.data = skb->data + toff;
+ }
+
+ /* Save original DCID for future token validation or Retry logic. */
+ quic_conn_id_update(&odcid, packet->dcid.data, packet->dcid.len);
+ /* If configured to validate client addresses, handle token logic. */
+ if (packet->validate_peer_address) {
+ if (quic_packet_deferred_schedule(skb))
+ return 0;
+ if (!token.len) {
+ /* rfc9000#section-8.1.2:
+ *
+ * Upon receiving the client's Initial packet, the
+ * server can request address validation by sending a
+ * Retry packet containing a token.
+ */
+ err = quic_packet_retry_create_and_xmit(sk);
+ consume_skb(skb);
+ return err;
+ }
+
+ /* Distinguish token source: Retry packet or NEW_TOKEN frame. */
+ retry = *(u8 *)token.data == QUIC_TOKEN_FLAG_RETRY;
+
+ /* Verify Token. */
+ crypto = quic_crypto(sk, QUIC_CRYPTO_INITIAL);
+ err = quic_crypto_verify_token(crypto, &packet->daddr,
+ sizeof(packet->daddr),
+ &odcid, token.data, token.len);
+ if (err) {
+ if (!retry) {
+ err = quic_packet_retry_create_and_xmit(sk);
+ consume_skb(skb);
+ return err;
+ }
+ /* rfc9000#section-8.1.3:
+ *
+ * If a server receives a client Initial that contains
+ * an invalid Retry token but is otherwise valid, it
+ * knows the client will not accept another Retry
+ * token. The server SHOULD immediately close the
+ * connection with an INVALID_TOKEN error.
+ */
+ errcode = QUIC_TRANSPORT_ERROR_INVALID_TOKEN;
+ quic_packet_refuse_close_create_and_xmit(sk, errcode);
+ consume_skb(skb);
+ return err;
+ }
+ }
+
+ /* Add request sock for this new QUIC connection. */
+ req = quic_request_sock_create(sk, &odcid, retry, gfp);
+ if (IS_ERR(req)) {
+ if (quic_packet_deferred_schedule(skb))
+ return 0;
+ /* rfc9000#section-5.2.2:
+ *
+ * If a server refuses to accept a new connection, it SHOULD
+ * send an Initial packet containing a CONNECTION_CLOSE frame
+ * with error code CONNECTION_REFUSED.
+ */
+ errcode = QUIC_TRANSPORT_ERROR_CONNECTION_REFUSED;
+ quic_packet_refuse_close_create_and_xmit(sk, errcode);
+ consume_skb(skb);
+ return PTR_ERR(req);
+ }
+out:
+ /* Add to backlog list and wake blocked accept() calls */
+ return quic_request_sock_backlog_tail(sk, req, skb);
+}
+
+static int quic_packet_handshake_process(struct sock *sk, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ kfree_skb(skb);
+ return -EOPNOTSUPP;
+}
+
+static int quic_packet_app_process(struct sock *sk, struct sk_buff *skb,
+ gfp_t gfp)
+{
+ kfree_skb(skb);
+ return -EOPNOTSUPP;
+}
+
+int quic_packet_process(struct sock *sk, struct sk_buff *skb, gfp_t gfp)
+{
+ if (quic_is_closed(sk)) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (quic_is_listen(sk))
+ return quic_packet_listen_process(sk, skb, gfp);
+
+ if (quic_hdr(skb)->form == QUIC_PACKET_FORM_LONG)
+ return quic_packet_handshake_process(sk, skb, gfp);
+
+ return quic_packet_app_process(sk, skb, gfp);
+}
+
+/* Work function to process packets in the backlog queue. */
+static void quic_packet_deferred_work(struct work_struct *work)
+{
+ struct quic_sock *qs = container_of(work, struct quic_sock, work);
+ struct sock *sk = &qs->inet.sk;
+ struct sk_buff_head *head;
+ struct sk_buff *skb;
+
+ lock_sock(sk);
+ head = &quic_packet(sk)->deferred_list;
+ while ((skb = __skb_dequeue(head)) != NULL)
+ quic_packet_process(sk, skb, GFP_KERNEL);
+ release_sock(sk);
+ sock_put(sk);
+}
+
+/* Make these fixed for easy coding. */
+#define QUIC_PACKET_NUMBER_LEN QUIC_PN_MAX_LEN
+#define QUIC_PACKET_LENGTH_LEN 4
+
+static struct sk_buff *quic_packet_handshake_create(struct sock *sk, gfp_t gfp)
+{
+ return NULL;
+}
+
+static int quic_packet_number_check(struct sock *sk, gfp_t gfp)
+{
+ return 0;
+}
+
+static struct sk_buff *quic_packet_app_create(struct sock *sk, gfp_t gfp)
+{
+ return NULL;
+}
+
+/* Update the MSS and inform congestion control. */
+void quic_packet_mss_update(struct sock *sk, u32 mss)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct quic_cong *cong = quic_cong(sk);
+
+ packet->mss[QUIC_PACKET_MSS_NORMAL] = (u16)mss;
+ quic_cong_set_mss(cong, packet->mss[QUIC_PACKET_MSS_NORMAL]);
+}
+
+/* Perform routing for the QUIC packet on the specified path, update header
+ * length and MSS accordingly, reset path and start PMTU timer.
+ */
+int quic_packet_route(struct sock *sk)
+{
+ struct quic_path_group *paths = quic_paths(sk);
+ struct quic_packet *packet = quic_packet(sk);
+ union quic_addr *sa, *da;
+ u32 pmtu;
+ int err;
+
+ da = quic_path_daddr(paths, packet->path);
+ sa = quic_path_saddr(paths, packet->path);
+ err = quic_flow_route(sk, da, sa, &paths->fl);
+ if (err)
+ return err < 0 ? err : 0;
+
+ packet->hlen = quic_encap_len(da);
+ pmtu = clamp(dst_mtu(__sk_dst_get(sk)),
+ QUIC_PATH_MIN_PMTU, QUIC_PATH_MAX_PMTU);
+ quic_packet_mss_update(sk, pmtu - packet->hlen);
+
+ quic_path_pl_reset(paths);
+ quic_timer_reset(sk, QUIC_TIMER_PMTU, paths->plpmtud_interval);
+ return 0;
+}
+
+/* Return QUIC packet header overhead for the given level and path. Includes
+ * packet number, connection IDs, and for long headers also version, length,
+ * and Initial token (if present). Excludes payload.
+ */
+u16 quic_packet_overhead(struct sock *sk, u8 level, u8 path)
+{
+ struct quic_conn_id_set *source = quic_source(sk);
+ struct quic_conn_id_set *dest = quic_dest(sk);
+ u16 len = QUIC_HLEN;
+
+ len += QUIC_PACKET_NUMBER_LEN; /* Packet number length. */
+ len += quic_conn_id_choose(dest, path)->len; /* DCID length. */
+ if (level == QUIC_CRYPTO_APP)
+ return len;
+
+ len += 1; /* Length byte for DCID. */
+ /* Length byte + SCID length. */
+ len += 1 + quic_conn_id_active(source)->len;
+ /* Include token for Initial packets. */
+ if (level == QUIC_CRYPTO_INITIAL)
+ len += quic_var_len(quic_token(sk)->len) + quic_token(sk)->len;
+ len += QUIC_VERSION_LEN; /* Version length. */
+ len += QUIC_PACKET_LENGTH_LEN; /* Packet length field. */
+
+ return len;
+}
+
+/* Configure the QUIC packet header and routing based on encryption level and
+ * path.
+ */
+int quic_packet_config(struct sock *sk, u8 level, u8 path)
+{
+ struct quic_packet *packet = quic_packet(sk);
+
+ /* If packet already has data, no need to reconfigure. */
+ if (!quic_packet_empty(packet))
+ return 0;
+
+ packet->path_validating = 0;
+ packet->ipfragok = 0;
+ packet->padding = 0;
+ packet->frames = 0;
+
+ packet->level = level;
+ packet->overhead = quic_packet_overhead(sk, level, path);
+ packet->len = packet->overhead + quic_packet_taglen(packet);
+
+ /* Allow fragmentation for handshake packets if PLPMTUD is enabled, as
+ * MTU discovery does not rely on ICMP Packet Too Big once PLPMTUD is
+ * enabled.
+ */
+ packet->ipfragok = level && !!quic_paths(sk)->plpmtud_interval;
+
+ if (packet->path != path) {
+ /* Path changed; update and reset routing cache */
+ packet->path = path;
+ __sk_dst_reset(sk);
+ }
+
+ /* Perform routing and MSS update for the configured packet. */
+ return quic_packet_route(sk);
+}
+
+static void quic_packet_encrypt_done(struct sk_buff *skb, int err)
+{
+ /* Free it for now, future patches will implement the actual deferred
+ * transmission logic.
+ */
+ kfree_skb(skb);
+}
+
+/* Coalescing Packets. */
+static int quic_packet_bundle(struct sock *sk, struct sk_buff *skb)
+{
+ struct quic_skb_cb *head_cb, *cb = QUIC_SKB_CB(skb);
+ struct quic_packet *packet = quic_packet(sk);
+ struct sk_buff *p;
+
+ if (!packet->head) /* First packet to bundle: initialize the head. */
+ goto init;
+
+ /* If bundling would exceed MSS, flush the current bundle. */
+ if (packet->head->len + skb->len >
+ packet->mss[QUIC_PACKET_MSS_NORMAL]) {
+ quic_packet_flush(sk);
+ goto init;
+ }
+ /* Bundle it and update metadata for the aggregate skb. */
+ skb_orphan(skb);
+ p = packet->head;
+ head_cb = QUIC_SKB_CB(p);
+ if (head_cb->last == p)
+ skb_shinfo(p)->frag_list = skb;
+ else
+ head_cb->last->next = skb;
+ p->data_len += skb->len;
+ p->truesize += skb->truesize;
+ p->len += skb->len;
+ head_cb->last = skb;
+ head_cb->ecn |= cb->ecn; /* Merge ECN flags. */
+
+out:
+ /* rfc9000#section-12.2: Packets with a short header (Section 17.3) do
+ * not contain a Length field and so cannot be followed by other
+ * packets in the same UDP datagram.
+ *
+ * so Return 1 to flush if it is a Short header packet.
+ */
+ return !cb->level;
+init:
+ packet->head = skb;
+ cb->last = skb;
+ goto out;
+}
+
+/* Transmit a QUIC packet, possibly encrypting and bundling it. */
+static int quic_packet_xmit(struct sock *sk, struct sk_buff *skb, gfp_t gfp)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct quic_skb_cb *cb = QUIC_SKB_CB(skb);
+ struct net *net = sock_net(sk);
+ int err;
+
+ /* Associate skb with sk to ensure sk is valid during async encryption
+ * completion.
+ */
+ WARN_ON_ONCE(!skb_set_owner_sk_safe(skb, sk));
+
+ /* Skip encryption if taglen == 0 (e.g., disable_1rtt_encryption). */
+ if (!packet->taglen[quic_hdr(skb)->form])
+ goto xmit;
+
+ cb->crypto_done = quic_packet_encrypt_done;
+ err = quic_crypto_encrypt(quic_crypto(sk, packet->level), skb, gfp);
+ if (err) {
+ if (err != -EINPROGRESS) {
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_ENCDROP);
+ kfree_skb(skb);
+ return err;
+ }
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_ENCBACKLOGS);
+ return err;
+ }
+ if (!cb->resume) /* Encryption completes synchronously. */
+ QUIC_INC_STATS(net, QUIC_MIB_PKT_ENCFASTPATHS);
+
+xmit:
+ if (quic_packet_bundle(sk, skb))
+ quic_packet_flush(sk);
+ return 0;
+}
+
+/* Create and transmit a new QUIC packet. */
+int quic_packet_create_and_xmit(struct sock *sk, gfp_t gfp)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct sk_buff *skb;
+ int err;
+
+ err = quic_packet_number_check(sk, gfp);
+ if (err)
+ goto err;
+
+ if (packet->level)
+ skb = quic_packet_handshake_create(sk, gfp);
+ else
+ skb = quic_packet_app_create(sk, gfp);
+ if (!skb) {
+ err = -ENOMEM;
+ goto err;
+ }
+
+ err = quic_packet_xmit(sk, skb, gfp);
+ if (err && err != -EINPROGRESS)
+ goto err;
+
+ return 0;
+err:
+ pr_debug("%s: err: %d\n", __func__, err);
+ return err;
+}
+
+/* Flush any coalesced/bundled QUIC packets. */
+void quic_packet_flush(struct sock *sk)
+{
+ struct quic_path_group *paths = quic_paths(sk);
+ struct quic_packet *packet = quic_packet(sk);
+
+ if (packet->head) {
+ quic_lower_xmit(sk, packet->head,
+ quic_path_daddr(paths, packet->path),
+ &paths->fl);
+ packet->head = NULL;
+ }
+}
+
+void quic_packet_init(struct sock *sk)
+{
+ struct quic_packet *packet = quic_packet(sk);
+
+ INIT_LIST_HEAD(&packet->frame_list);
+ skb_queue_head_init(&packet->deferred_list);
+ skb_queue_head_init(&packet->backlog_list);
+ INIT_WORK(quic_work(sk), quic_packet_deferred_work);
+
+ packet->taglen[QUIC_PACKET_FORM_SHORT] = QUIC_TAG_LEN;
+ packet->taglen[QUIC_PACKET_FORM_LONG] = QUIC_TAG_LEN;
+ packet->mss[QUIC_PACKET_MSS_NORMAL] = QUIC_MIN_UDP_PAYLOAD;
+ packet->mss[QUIC_PACKET_MSS_DGRAM] = QUIC_MIN_UDP_PAYLOAD;
+
+ packet->version = QUIC_VERSION_V1;
+}
+
+void quic_packet_free(struct sock *sk)
+{
+ struct quic_packet *packet = quic_packet(sk);
+
+ flush_work(quic_work(sk));
+ __skb_queue_purge(&packet->deferred_list);
+ __skb_queue_purge(&packet->backlog_list);
+}
diff --git a/net/quic/packet.h b/net/quic/packet.h
new file mode 100644
index 0000000000000..7f12bb53927b8
--- /dev/null
+++ b/net/quic/packet.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+struct quic_packet {
+ struct quic_conn_id dcid; /* Dest Conn ID from received packet */
+ struct quic_conn_id scid; /* Source Conn ID from received packet */
+ union quic_addr daddr; /* Dest address from received packet */
+ union quic_addr saddr; /* Source address from received packet */
+
+ struct sk_buff_head deferred_list; /* Packets deferred to work queue */
+ struct sk_buff_head backlog_list; /* Packets waiting for crypto keys */
+ struct list_head frame_list; /* Frames to pack into packet for send */
+ struct sk_buff *head; /* Head skb for packet bundling on send */
+ u32 version; /* QUIC version used/selected during handshake */
+ u16 overhead; /* QUIC header length excluding frames */
+ u8 taglen[2]; /* Tag length for short and long packets */
+ u16 padding; /* Total padding bytes to append after frames */
+ u16 frames; /* Number of ack-eliciting frames */
+ u16 mss[2]; /* MSS for datagram and non-datagram packets */
+ u16 hlen; /* UDP + IP header length for sending */
+ u16 len; /* QUIC packet length including taglen for sending */
+
+ u8 validate_peer_address:1; /* Temporary; will move to quic_outqueue */
+ u8 path_validating:1; /* Packet contains path_validating frames */
+ u8 ack_eliciting:1; /* Packet contains ack-eliciting frames */
+ u8 ack_immediate:1; /* Send ACK immediately (skip ack_delay timer) */
+ u8 non_probing:1; /* Packet contains non-probing frames */
+ u8 has_sack:1; /* Packet contains ACK frames */
+ u8 ipfragok:1; /* Allow IP fragmentation */
+ u8 path:1; /* Path identifier used to send this packet */
+ u8 level; /* Encryption level used */
+};
+
+#define QUIC_PACKET_INITIAL_V1 0
+#define QUIC_PACKET_0RTT_V1 1
+#define QUIC_PACKET_HANDSHAKE_V1 2
+#define QUIC_PACKET_RETRY_V1 3
+
+#define QUIC_PACKET_INITIAL_V2 1
+#define QUIC_PACKET_0RTT_V2 2
+#define QUIC_PACKET_HANDSHAKE_V2 3
+#define QUIC_PACKET_RETRY_V2 0
+
+#define QUIC_PACKET_INITIAL QUIC_PACKET_INITIAL_V1
+#define QUIC_PACKET_0RTT QUIC_PACKET_0RTT_V1
+#define QUIC_PACKET_HANDSHAKE QUIC_PACKET_HANDSHAKE_V1
+#define QUIC_PACKET_RETRY QUIC_PACKET_RETRY_V1
+
+#define QUIC_PACKET_INVALID 0xff
+
+#define QUIC_VERSION_LEN 4
+
+#define QUIC_ALPN_MAX_LEN 128
+
+#define QUIC_PACKET_MSS_NORMAL 0
+#define QUIC_PACKET_MSS_DGRAM 1
+
+#define QUIC_PACKET_FORM_SHORT 0
+#define QUIC_PACKET_FORM_LONG 1
+
+static inline u8 quic_packet_taglen(struct quic_packet *packet)
+{
+ return packet->taglen[packet->level != QUIC_CRYPTO_APP];
+}
+
+static inline void quic_packet_set_taglen(struct quic_packet *packet, u8 taglen)
+{
+ packet->taglen[QUIC_PACKET_FORM_SHORT] = taglen;
+}
+
+static inline u32 quic_packet_mss(struct quic_packet *packet)
+{
+ return packet->mss[QUIC_PACKET_MSS_NORMAL] - quic_packet_taglen(packet);
+}
+
+static inline u32 quic_packet_max_payload(struct quic_packet *packet)
+{
+ return packet->mss[QUIC_PACKET_MSS_NORMAL] - packet->overhead -
+ quic_packet_taglen(packet);
+}
+
+static inline u32 quic_packet_max_payload_dgram(struct quic_packet *packet)
+{
+ return packet->mss[QUIC_PACKET_MSS_DGRAM] - packet->overhead -
+ quic_packet_taglen(packet);
+}
+
+static inline bool quic_packet_empty(struct quic_packet *packet)
+{
+ return list_empty(&packet->frame_list);
+}
+
+static inline void quic_packet_reset(struct quic_packet *packet)
+{
+ packet->level = 0;
+ packet->has_sack = 0;
+ packet->non_probing = 0;
+ packet->ack_eliciting = 0;
+ packet->ack_immediate = 0;
+}
+
+int quic_packet_process(struct sock *sk, struct sk_buff *skb, gfp_t gfp);
+u16 quic_packet_overhead(struct sock *sk, u8 level, u8 path);
+int quic_packet_config(struct sock *sk, u8 level, u8 path);
+
+int quic_packet_create_and_xmit(struct sock *sk, gfp_t gfp);
+int quic_packet_route(struct sock *sk);
+
+void quic_packet_mss_update(struct sock *sk, u32 mss);
+void quic_packet_flush(struct sock *sk);
+void quic_packet_init(struct sock *sk);
+void quic_packet_free(struct sock *sk);
+
+u32 *quic_packet_compatible_versions(u32 version);
+
+void quic_packet_rcv_err_pmtu(struct sock *sk);
diff --git a/net/quic/path.c b/net/quic/path.c
new file mode 100644
index 0000000000000..a41b99dd31eff
--- /dev/null
+++ b/net/quic/path.c
@@ -0,0 +1,589 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/udp_tunnel.h>
+#include <linux/quic.h>
+
+#include "common.h"
+#include "family.h"
+#include "path.h"
+
+static int quic_udp_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ quic_set_skb_iif(skb);
+
+ memset(skb->cb, 0, sizeof(skb->cb));
+ QUIC_SKB_CB(skb)->seqno = -1;
+ QUIC_SKB_CB(skb)->time = quic_ktime_get_us();
+
+ skb_pull(skb, sizeof(struct udphdr));
+ skb_dst_force(skb);
+ quic_packet_rcv(sk, skb, false);
+ /* .encap_rcv must return 0 if skb was either consumed or dropped. */
+ return 0;
+}
+
+static int quic_udp_err(struct sock *sk, struct sk_buff *skb)
+{
+ return quic_packet_rcv(sk, skb, true);
+}
+
+static void quic_udp_sock_put_work(struct work_struct *work)
+{
+ struct quic_udp_sock *us = container_of(work, struct quic_udp_sock,
+ work);
+ struct quic_uhash_head *head;
+ struct sock *sk = us->sk;
+
+ /* Hold the sock to safely access it in quic_udp_sock_lookup() even
+ * after udp_tunnel_sock_release(). The release must occur before
+ * __hlist_del() so a new UDP tunnel socket can be created for the same
+ * address and port if quic_udp_sock_lookup() fails to find one.
+ *
+ * Note: udp_tunnel_sock_release() cannot be called under the mutex due
+ * to some lockdep warnings.
+ */
+ sock_hold(sk);
+ udp_tunnel_sock_release(sk);
+
+ head = quic_udp_sock_head(sock_net(sk), ntohs(us->addr.v4.sin_port));
+ mutex_lock(&head->lock);
+ __hlist_del(&us->node);
+ mutex_unlock(&head->lock);
+
+ sock_put(sk);
+ kfree(us);
+}
+
+static struct quic_udp_sock *quic_udp_sock_create(struct sock *sk,
+ union quic_addr *a)
+{
+ struct udp_tunnel_sock_cfg tuncfg = {};
+ struct udp_port_cfg udp_conf = {};
+ struct net *net = sock_net(sk);
+ struct quic_uhash_head *head;
+ struct quic_udp_sock *us;
+ struct socket *sock;
+ int err;
+
+ us = kmalloc_obj(*us);
+ if (!us)
+ return ERR_PTR(-ENOMEM);
+
+ quic_udp_conf_init(sk, &udp_conf, a);
+ err = udp_sock_create(net, &udp_conf, &sock);
+ if (err) {
+ pr_debug("%s: failed to create udp sock\n", __func__);
+ kfree(us);
+ return ERR_PTR(err);
+ }
+
+ tuncfg.encap_type = 1;
+ tuncfg.encap_rcv = quic_udp_rcv;
+ tuncfg.encap_err_lookup = quic_udp_err;
+ setup_udp_tunnel_sock(net, sock->sk, &tuncfg);
+
+ refcount_set(&us->refcnt, 1);
+ us->sk = sock->sk;
+ memcpy(&us->addr, a, sizeof(*a));
+ us->bind_ifindex = udp_conf.bind_ifindex;
+
+ INIT_WORK(&us->work, quic_udp_sock_put_work);
+ head = quic_udp_sock_head(net, ntohs(a->v4.sin_port));
+ hlist_add_head(&us->node, &head->head);
+
+ return us;
+}
+
+static bool quic_udp_sock_get(struct quic_udp_sock *us)
+{
+ return refcount_inc_not_zero(&us->refcnt);
+}
+
+static void quic_udp_sock_put(struct quic_udp_sock *us)
+{
+ /* The UDP socket may be freed in atomic RX context during connection
+ * migration; defer the release to a workqueue.
+ */
+ if (refcount_dec_and_test(&us->refcnt))
+ queue_work(quic_wq, &us->work);
+}
+
+/* Lookup a quic_udp_sock in the global hash table by port or address. If 'a'
+ * is provided, it searches for a socket whose local address matches 'a' and,
+ * if applicable, matches the device binding. If 'a' is NULL, it searches only
+ * by port.
+ */
+static struct quic_udp_sock *quic_udp_sock_lookup(struct sock *sk,
+ union quic_addr *a, u16 port)
+{
+ struct net *net = sock_net(sk);
+ struct quic_uhash_head *head;
+ struct quic_udp_sock *us;
+
+ head = quic_udp_sock_head(net, port);
+ hlist_for_each_entry(us, &head->head, node) {
+ if (net != sock_net(us->sk))
+ continue;
+ if (a) {
+ if (quic_cmp_sk_addr(us->sk, &us->addr, a) &&
+ us->bind_ifindex == quic_get_dev_if(sk, a))
+ return us;
+ continue;
+ }
+ if (ntohs(us->addr.v4.sin_port) == port)
+ return us;
+ }
+ return NULL;
+}
+
+static void quic_path_set_udp_sk(struct quic_path *path,
+ struct quic_udp_sock *us)
+{
+ if (path->udp_sk)
+ quic_udp_sock_put(path->udp_sk);
+
+ path->udp_sk = us;
+ if (!us) {
+ path->usk = NULL;
+ memset(&path->uaddr, 0, sizeof(path->uaddr));
+ return;
+ }
+ path->usk = us->sk;
+ memcpy(&path->uaddr, &us->addr, sizeof(us->addr));
+}
+
+/* Binds a QUIC path to a local port and sets up a UDP socket. */
+int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path)
+{
+ union quic_addr *a = quic_path_saddr(paths, path);
+ int rover, low, high, remaining;
+ struct net *net = sock_net(sk);
+ struct quic_uhash_head *head;
+ struct quic_udp_sock *us;
+ u16 port;
+
+ port = ntohs(a->v4.sin_port);
+ if (port) {
+ if (inet_port_requires_bind_service(net, port) &&
+ !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE))
+ return -EACCES;
+ head = quic_udp_sock_head(net, port);
+ mutex_lock(&head->lock);
+ us = quic_udp_sock_lookup(sk, a, port);
+ if (us) {
+ if (!uid_eq(sk->sk_uid, us->sk->sk_uid)) {
+ mutex_unlock(&head->lock);
+ return -EADDRINUSE;
+ }
+ /* Allow reuse of an existing UDP tunnel socket.
+ * However, if it is in the middle of asynchronous
+ * teardown (via workqueue), it is temporarily unusable.
+ * Return -EAGAIN (not -EADDRINUSE) to signal the caller
+ * to retry soon.
+ */
+ if (!quic_udp_sock_get(us)) {
+ mutex_unlock(&head->lock);
+ return -EAGAIN;
+ }
+ } else {
+ us = quic_udp_sock_create(sk, a);
+ if (IS_ERR(us)) {
+ mutex_unlock(&head->lock);
+ return PTR_ERR(us);
+ }
+ }
+ mutex_unlock(&head->lock);
+ quic_path_set_udp_sk(&paths->path[path], us);
+ return 0;
+ }
+
+ inet_sk_get_local_port_range(sk, &low, &high);
+ remaining = (high - low) + 1;
+ rover = get_random_u32_below(remaining) + low;
+ do {
+ rover++;
+ if (rover < low || rover > high)
+ rover = low;
+ port = (u16)rover;
+ if (inet_is_local_reserved_port(net, port))
+ continue;
+
+ head = quic_udp_sock_head(net, port);
+ mutex_lock(&head->lock);
+ if (quic_udp_sock_lookup(sk, NULL, port)) {
+ mutex_unlock(&head->lock);
+ cond_resched();
+ continue;
+ }
+ a->v4.sin_port = htons(port);
+ us = quic_udp_sock_create(sk, a);
+ if (IS_ERR(us)) {
+ a->v4.sin_port = 0;
+ mutex_unlock(&head->lock);
+ if (PTR_ERR(us) == -EADDRINUSE) {
+ cond_resched();
+ continue;
+ }
+ return PTR_ERR(us);
+ }
+ mutex_unlock(&head->lock);
+
+ quic_path_set_udp_sk(&paths->path[path], us);
+ __sk_dst_reset(sk);
+ return 0;
+ } while (--remaining > 0);
+
+ return -EADDRINUSE;
+}
+
+/* Swaps the active and alternate QUIC paths.
+ *
+ * Promotes the alternate path (path[1]) to become the new active path
+ * (path[0]). If the alternate path has a valid UDP socket, the entire path is
+ * swapped. Otherwise, only the destination address is exchanged, assuming the
+ * source address is the same and no rebind is needed.
+ *
+ * This is typically used during path migration or alternate path promotion.
+ */
+void quic_path_swap(struct quic_path_group *paths)
+{
+ struct quic_path path = paths->path[0];
+
+ paths->alt_probes = 0;
+ paths->alt_state = QUIC_PATH_ALT_SWAPPED;
+
+ /* Protect path[0] modifications with seqcount for RCU readers in
+ * quic_sock_lookup(). The seqcount allows readers to detect torn reads
+ * during the non-atomic structure assignment.
+ */
+ local_bh_disable();
+ write_seqcount_begin(&paths->path_seq);
+ if (paths->path[1].udp_sk) {
+ paths->path[0] = paths->path[1];
+ paths->path[1] = path;
+ goto out;
+ }
+
+ paths->path[0].daddr = paths->path[1].daddr;
+ paths->path[1].daddr = path.daddr;
+out:
+ write_seqcount_end(&paths->path_seq);
+ local_bh_enable();
+}
+
+/* Frees resources associated with a QUIC path.
+ *
+ * This is used for cleanup during error handling or when the path is no longer
+ * needed.
+ */
+void quic_path_unbind(struct sock *sk, struct quic_path_group *paths, u8 path)
+{
+ paths->alt_probes = 0;
+ paths->alt_state = QUIC_PATH_ALT_NONE;
+
+ quic_path_set_udp_sk(&paths->path[path], NULL);
+
+ memset(quic_path_daddr(paths, path), 0, sizeof(union quic_addr));
+ memset(quic_path_saddr(paths, path), 0, sizeof(union quic_addr));
+}
+
+/* Detects and records a potential alternate path.
+ *
+ * If the new source or destination address differs from the active path, and
+ * alternate path detection is not disabled, the function updates the alternate
+ * path slot (path[1]) with the new addresses.
+ *
+ * This is typically called on packet receive to detect new possible network
+ * paths (e.g., NAT rebinding, mobility).
+ *
+ * Returns true if a new alternate path was detected and updated, false
+ * otherwise.
+ */
+bool quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa,
+ union quic_addr *da, struct sock *sk)
+{
+ bool remote = !quic_cmp_sk_addr(sk, quic_path_daddr(paths, 0), da);
+ bool local = !quic_cmp_sk_addr(sk, quic_path_saddr(paths, 0), sa);
+
+ if (!local && !remote)
+ return false;
+
+ if ((local && paths->disable_saddr_alt) ||
+ (remote && paths->disable_daddr_alt))
+ return false;
+
+ if (!quic_path_saddr(paths, 1)->v4.sin_port)
+ quic_path_set_saddr(paths, 1, sa);
+
+ if (!quic_cmp_sk_addr(sk, quic_path_saddr(paths, 1), sa))
+ return false;
+
+ if (!quic_path_daddr(paths, 1)->v4.sin_port)
+ quic_path_set_daddr(paths, 1, da);
+
+ return quic_cmp_sk_addr(sk, quic_path_daddr(paths, 1), da);
+}
+
+void quic_path_get_param(struct quic_path_group *paths,
+ struct quic_transport_param *p)
+{
+ if (p->remote) {
+ p->disable_active_migration = paths->disable_saddr_alt;
+ return;
+ }
+ p->disable_active_migration = paths->disable_daddr_alt;
+}
+
+void quic_path_set_param(struct quic_path_group *paths,
+ struct quic_transport_param *p)
+{
+ if (p->remote) {
+ paths->disable_saddr_alt = !!p->disable_active_migration;
+ return;
+ }
+ paths->disable_daddr_alt = !!p->disable_active_migration;
+}
+
+/* State Machine defined in rfc8899#section-5.2 */
+enum quic_plpmtud_state {
+ QUIC_PL_DISABLED,
+ QUIC_PL_BASE,
+ QUIC_PL_SEARCH,
+ QUIC_PL_COMPLETE,
+ QUIC_PL_ERROR,
+};
+
+#define QUIC_BASE_PLPMTU 1200
+#define QUIC_MAX_PLPMTU 9000
+#define QUIC_MIN_PLPMTU 512
+
+#define QUIC_MAX_PROBES 3
+
+#define QUIC_PL_BIG_STEP 32
+#define QUIC_PL_MIN_STEP 4
+
+/* Handle PLPMTUD probe failure on a QUIC path.
+ *
+ * Called immediately after sending a probe packet in QUIC Path MTU Discovery.
+ * Tracks probe count and manages state transitions based on the number of
+ * probes sent and current PLPMTUD state (BASE, SEARCH, COMPLETE, ERROR).
+ * Detects probe failures and black holes, adjusting PMTU and probe sizes
+ * accordingly.
+ *
+ * Return: New PMTU value if updated, else 0.
+ */
+u32 quic_path_pl_send(struct quic_path_group *paths, s64 number)
+{
+ u32 pathmtu = 0;
+
+ paths->pl.number = number;
+ if (paths->pl.probe_count < QUIC_MAX_PROBES)
+ goto out;
+
+ paths->pl.probe_count = 0;
+ if (paths->pl.state == QUIC_PL_BASE) {
+ if (paths->pl.probe_size == QUIC_BASE_PLPMTU) {
+ /* BASE_PLPMTU Confirming Failed: Base -> Error. */
+ paths->pl.state = QUIC_PL_ERROR;
+
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ }
+ } else if (paths->pl.state == QUIC_PL_SEARCH) {
+ if (paths->pl.pmtu == paths->pl.probe_size) {
+ /* Black Hole Detected: Search -> Base. */
+ paths->pl.state = QUIC_PL_BASE;
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+ paths->pl.probe_high = 0;
+
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ } else { /* Normal probe failure. */
+ paths->pl.probe_high = paths->pl.probe_size;
+ paths->pl.probe_size = paths->pl.pmtu;
+ }
+ } else if (paths->pl.state == QUIC_PL_COMPLETE) {
+ if (paths->pl.pmtu == paths->pl.probe_size) {
+ /* Black Hole Detected: Search Complete -> Base. */
+ paths->pl.state = QUIC_PL_BASE;
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+
+ /* probe_high already reset when entering COMPLETE. */
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ }
+ }
+
+out:
+ pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, high: %d\n",
+ __func__, paths, paths->pl.state, paths->pl.pmtu,
+ paths->pl.probe_size, paths->pl.probe_high);
+ paths->pl.probe_count++;
+ return pathmtu;
+}
+
+/* Handle successful reception of a PMTU probe.
+ *
+ * Called when a probe packet is acknowledged. Updates probe size and
+ * transitions state if needed (e.g., from SEARCH to COMPLETE). Expands PMTU
+ * using binary or linear search depending on state.
+ *
+ * Return: New PMTU to apply if search completes, or 0 if no change.
+ */
+u32 quic_path_pl_recv(struct quic_path_group *paths, bool *raise_timer,
+ bool *complete)
+{
+ u32 pathmtu = 0;
+ u16 next;
+
+ pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, high: %d\n",
+ __func__, paths, paths->pl.state, paths->pl.pmtu,
+ paths->pl.probe_size, paths->pl.probe_high);
+
+ *raise_timer = false;
+ paths->pl.number = 0;
+ paths->pl.pmtu = paths->pl.probe_size;
+ paths->pl.probe_count = 0;
+ if (paths->pl.state == QUIC_PL_BASE) {
+ paths->pl.state = QUIC_PL_SEARCH; /* Base -> Search */
+ paths->pl.probe_size += QUIC_PL_BIG_STEP;
+ } else if (paths->pl.state == QUIC_PL_ERROR) {
+ paths->pl.state = QUIC_PL_SEARCH; /* Error -> Search */
+
+ paths->pl.pmtu = paths->pl.probe_size;
+ pathmtu = (u32)paths->pl.pmtu;
+ paths->pl.probe_size += QUIC_PL_BIG_STEP;
+ } else if (paths->pl.state == QUIC_PL_SEARCH) {
+ if (!paths->pl.probe_high) {
+ if (paths->pl.probe_size < QUIC_MAX_PLPMTU) {
+ next = paths->pl.probe_size + QUIC_PL_BIG_STEP;
+ paths->pl.probe_size =
+ min_t(u16, next, QUIC_MAX_PLPMTU);
+ *complete = false;
+ return 0;
+ }
+ paths->pl.probe_high = QUIC_MAX_PLPMTU;
+ }
+ paths->pl.probe_size += QUIC_PL_MIN_STEP;
+ if (paths->pl.probe_size >= paths->pl.probe_high) {
+ paths->pl.probe_high = 0;
+ /* Search -> Search Complete */
+ paths->pl.state = QUIC_PL_COMPLETE;
+
+ paths->pl.probe_size = paths->pl.pmtu;
+ pathmtu = (u32)paths->pl.pmtu;
+ *raise_timer = true;
+ }
+ } else if (paths->pl.state == QUIC_PL_COMPLETE) {
+ /* Raise probe_size after 30 * interval in Search Complete;
+ * Search Complete -> Search.
+ */
+ paths->pl.state = QUIC_PL_SEARCH;
+ next = paths->pl.probe_size + QUIC_PL_MIN_STEP;
+ paths->pl.probe_size = min_t(u16, next, QUIC_MAX_PLPMTU);
+ }
+
+ *complete = (paths->pl.state == QUIC_PL_COMPLETE);
+ return pathmtu;
+}
+
+/* Handle ICMP "Packet Too Big" messages.
+ *
+ * Responds to an incoming ICMP error by reducing the probe size or falling
+ * back to a safe baseline PMTU depending on current state. Also handles cases
+ * where the PMTU hint lies between probe and current PMTU.
+ *
+ * Return: New PMTU to apply if state changes, or 0 if no change.
+ */
+u32 quic_path_pl_toobig(struct quic_path_group *paths, u32 pmtu,
+ bool *reset_timer)
+{
+ u32 pathmtu = 0;
+
+ pr_debug("%s: dst: %p, state: %d, pmtu: %d, size: %d, ptb: %d\n",
+ __func__, paths, paths->pl.state, paths->pl.pmtu,
+ paths->pl.probe_size, pmtu);
+
+ *reset_timer = false;
+ if (pmtu < QUIC_MIN_PLPMTU || pmtu >= (u32)paths->pl.probe_size)
+ return pathmtu;
+
+ if (paths->pl.state == QUIC_PL_BASE) {
+ if (pmtu < QUIC_BASE_PLPMTU) {
+ paths->pl.state = QUIC_PL_ERROR; /* Base -> Error */
+
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ }
+ } else if (paths->pl.state == QUIC_PL_SEARCH) {
+ if (pmtu >= QUIC_BASE_PLPMTU && pmtu < (u32)paths->pl.pmtu) {
+ paths->pl.state = QUIC_PL_BASE; /* Search -> Base */
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+ paths->pl.probe_count = 0;
+
+ paths->pl.probe_high = 0;
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ } else if (pmtu > (u32)paths->pl.pmtu &&
+ pmtu < (u32)paths->pl.probe_size) {
+ paths->pl.probe_size = (u16)pmtu;
+ paths->pl.probe_count = 0;
+ }
+ } else if (paths->pl.state == QUIC_PL_COMPLETE) {
+ if (pmtu >= QUIC_BASE_PLPMTU && pmtu < (u32)paths->pl.pmtu) {
+ paths->pl.state = QUIC_PL_BASE; /* Complete -> Base */
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+ paths->pl.probe_count = 0;
+
+ paths->pl.probe_high = 0;
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ pathmtu = QUIC_BASE_PLPMTU;
+ *reset_timer = true;
+ }
+ }
+ return pathmtu;
+}
+
+/* Reset PLPMTUD state for a path.
+ *
+ * Resets all PLPMTUD-related state to its initial configuration. Called when
+ * a new path is initialized or when recovering from errors.
+ */
+void quic_path_pl_reset(struct quic_path_group *paths)
+{
+ paths->pl.number = 0;
+ paths->pl.probe_high = 0;
+ paths->pl.probe_count = 0;
+ paths->pl.state = QUIC_PL_BASE;
+ paths->pl.pmtu = QUIC_BASE_PLPMTU;
+ paths->pl.probe_size = QUIC_BASE_PLPMTU;
+}
+
+/* Check if a packet number confirms PLPMTUD probe.
+ *
+ * Checks whether the last probe (tracked by .number) has been acknowledged.
+ * If the probe number lies within the ACK range, confirmation is successful.
+ *
+ * Return: true if probe is confirmed, false otherwise.
+ */
+bool quic_path_pl_confirm(struct quic_path_group *paths, s64 largest,
+ s64 smallest)
+{
+ return paths->pl.number && paths->pl.number >= smallest &&
+ paths->pl.number <= largest;
+}
+
+void quic_path_init(struct quic_path_group *paths)
+{
+ seqcount_init(&paths->path_seq);
+}
diff --git a/net/quic/path.h b/net/quic/path.h
new file mode 100644
index 0000000000000..45c3c6bb9d973
--- /dev/null
+++ b/net/quic/path.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_PATH_MIN_PMTU 1200U
+#define QUIC_PATH_MAX_PMTU 65535U
+
+#define QUIC_MIN_UDP_PAYLOAD 1200
+#define QUIC_MAX_UDP_PAYLOAD 65527
+
+#define QUIC_PATH_ENTROPY_LEN 8
+
+#define QUIC_PMTUD_RAISE_TIMER_FACTOR 30
+
+extern struct workqueue_struct *quic_wq;
+
+/* Connection Migration State Machine:
+ *
+ * +--------+ recv non-probing, free old path +----------+
+ * | NONE | <-------------------------------------- | SWAPPED |
+ * +--------+ +----------+
+ * | ^ \ ^
+ * | \ \ |
+ * | \ \ new path detected, | recv
+ * | \ \ has another DCID, | Path
+ * | \ \ snd Path Challenge | Response
+ * | \ ------------------------------- |
+ * | ------------------------------- \ |
+ * | new path detected, Path \ \ |
+ * | has no other DCID, Challenge \ \ |
+ * | request a new DCID failed \ \ |
+ * v \ v |
+ * +----------+ +----------+
+ * | PENDING | ------------------------------------> | PROBING |
+ * +----------+ recv a new DCID, snd Path Challenge +----------+
+ */
+enum {
+ QUIC_PATH_ALT_NONE,
+ QUIC_PATH_ALT_PENDING, /* Waiting for new dest conn ID for migration */
+ QUIC_PATH_ALT_PROBING, /* Validating alternate path (PATH_CHALLENGE) */
+ QUIC_PATH_ALT_SWAPPED, /* Alternate path is now active; roles swapped */
+};
+
+struct quic_udp_sock {
+ struct work_struct work; /* Workqueue to destroy UDP tunnel socket */
+ struct hlist_node node; /* Node in addr-based UDP socket hash table */
+ union quic_addr addr; /* Source addr of underlying UDP tunnel socket */
+ int bind_ifindex;
+ refcount_t refcnt;
+ struct sock *sk; /* Underlying UDP tunnel socket */
+};
+
+struct quic_path {
+ union quic_addr daddr; /* Destination address */
+ union quic_addr saddr; /* Source address */
+
+ /* Wrapped UDP socket for receiving QUIC */
+ struct quic_udp_sock *udp_sk;
+ /* Cached UDP tunnel socket and source addr for RCU access */
+ union quic_addr uaddr;
+ struct sock *usk;
+};
+
+struct quic_path_group {
+ /* Connection ID validation during handshake (rfc9000#section-7.3) */
+ struct quic_conn_id retry_dcid; /* Source CID from Retry packet */
+ struct quic_conn_id orig_dcid; /* Destination CID from first Initial */
+
+ /* Path validation (rfc9000#section-8.2) */
+ u8 entropy[QUIC_PATH_ENTROPY_LEN]; /* Entropy for PATH_CHALLENGE */
+ struct quic_path path[2]; /* Active path (0) and alternate path (1) */
+ seqcount_t path_seq; /* Protects path[0] during swap */
+ struct flowi fl; /* Flow info from routing decisions */
+
+ /* Anti-amplification limit (rfc9000#section-8) */
+ u32 ampl_sndlen; /* Bytes sent before address is validated */
+ u32 ampl_rcvlen; /* Bytes received to lift amplification limit */
+
+ /* MTU discovery handling */
+ struct { /* PLPMTUD probing (rfc8899) */
+ s64 number; /* Packet number used for current probe */
+ u16 pmtu; /* Confirmed path MTU */
+
+ u16 probe_size; /* Current probe packet size */
+ u16 probe_high; /* Highest failed probe size */
+ u8 probe_count; /* Retry count for current probe_size */
+ u8 state; /* Probe state machine (rfc8899#section-5.2) */
+ } pl;
+ u32 mtu_info; /* PMTU value from received ICMP, pending apply */
+
+ u32 plpmtud_interval; /* Time interval for the PLPMTUD probe timer */
+ u32 keepalive_interval; /* Time interval to maintain path liveness */
+
+ u8 ecn_probes; /* ECN probe counter */
+ u8 validated:1; /* Path validated with PATH_RESPONSE */
+ u8 blocked:1; /* Blocked by anti-amplification limit */
+ u8 version:1; /* Version negotiation performed */
+ u8 retry:1; /* Retry used in initial packet */
+
+ /* Connection Migration (rfc9000#section-9) */
+ u8 disable_saddr_alt:1; /* Remote disable_active_migration parameter */
+ u8 disable_daddr_alt:1; /* Local disable_active_migration parameter */
+ u8 pref_addr:1; /* Preferred address offered (rfc9000#section-18.2) */
+ u8 alt_probes; /* Number of PATH_CHALLENGE probes sent */
+ u8 alt_state; /* Connection migration state (see above) */
+};
+
+static inline union quic_addr *quic_path_saddr(struct quic_path_group *paths,
+ u8 path)
+{
+ return &paths->path[path].saddr;
+}
+
+static inline void quic_path_set_saddr(struct quic_path_group *paths, u8 path,
+ union quic_addr *addr)
+{
+ memcpy(quic_path_saddr(paths, path), addr, sizeof(*addr));
+}
+
+static inline union quic_addr *quic_path_daddr(struct quic_path_group *paths,
+ u8 path)
+{
+ return &paths->path[path].daddr;
+}
+
+static inline void quic_path_set_daddr(struct quic_path_group *paths, u8 path,
+ union quic_addr *addr)
+{
+ memcpy(quic_path_daddr(paths, path), addr, sizeof(*addr));
+}
+
+static inline union quic_addr *quic_path_uaddr(struct quic_path_group *paths,
+ u8 path)
+{
+ return &paths->path[path].uaddr;
+}
+
+static inline struct sock *quic_path_usock(struct quic_path_group *paths,
+ u8 path)
+{
+ return paths->path[path].usk;
+}
+
+static inline bool quic_path_alt_state(struct quic_path_group *paths, u8 state)
+{
+ return paths->alt_state == state;
+}
+
+static inline void quic_path_set_alt_state(struct quic_path_group *paths,
+ u8 state)
+{
+ paths->alt_state = state;
+}
+
+/* Returns the destination Connection ID (DCID) used for identifying the
+ * connection. Per rfc9000#section-7.3, handshake packets are considered part
+ * of the same connection if their DCID matches the one returned here.
+ */
+static inline struct quic_conn_id *
+quic_path_orig_dcid(struct quic_path_group *paths)
+{
+ return paths->retry ? &paths->retry_dcid : &paths->orig_dcid;
+}
+
+int quic_packet_rcv(struct sock *sk, struct sk_buff *skb, bool icmp);
+void quic_path_init(struct quic_path_group *paths);
+
+bool quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa,
+ union quic_addr *da, struct sock *sk);
+int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path);
+void quic_path_unbind(struct sock *sk, struct quic_path_group *paths, u8 path);
+void quic_path_swap(struct quic_path_group *paths);
+
+u32 quic_path_pl_recv(struct quic_path_group *paths, bool *raise_timer,
+ bool *complete);
+u32 quic_path_pl_toobig(struct quic_path_group *paths, u32 pmtu,
+ bool *reset_timer);
+u32 quic_path_pl_send(struct quic_path_group *paths, s64 number);
+
+void quic_path_get_param(struct quic_path_group *paths,
+ struct quic_transport_param *p);
+void quic_path_set_param(struct quic_path_group *paths,
+ struct quic_transport_param *p);
+bool quic_path_pl_confirm(struct quic_path_group *paths,
+ s64 largest, s64 smallest);
+void quic_path_pl_reset(struct quic_path_group *paths);
diff --git a/net/quic/pnspace.c b/net/quic/pnspace.c
new file mode 100644
index 0000000000000..c7c4d0be2d980
--- /dev/null
+++ b/net/quic/pnspace.c
@@ -0,0 +1,273 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <linux/slab.h>
+
+#include "common.h"
+#include "pnspace.h"
+
+int quic_pnspace_init(struct quic_pnspace *space)
+{
+ space->pn_map = kzalloc(BITS_TO_BYTES(QUIC_PN_MAP_INITIAL), GFP_KERNEL);
+ if (!space->pn_map)
+ return -ENOMEM;
+ space->pn_map_len = QUIC_PN_MAP_INITIAL;
+
+ space->max_time_limit = QUIC_PNSPACE_TIME_LIMIT;
+ space->next_pn = QUIC_PNSPACE_NEXT_PN;
+ space->max_pn_acked_seen = -1;
+ space->base_pn = -1;
+ return 0;
+}
+
+void quic_pnspace_free(struct quic_pnspace *space)
+{
+ space->pn_map_len = 0;
+ kfree(space->pn_map);
+ space->pn_map = NULL;
+}
+
+/* Expand the bitmap tracking received packet numbers. Ensures the pn_map
+ * bitmap can cover at least @size packet numbers. Allocates a larger bitmap,
+ * copies existing data, and updates metadata.
+ *
+ * Return: 0 on success, or a negative errno value on failure.
+ */
+static int quic_pnspace_grow(struct quic_pnspace *space, u16 size, gfp_t gfp)
+{
+ u16 len, inc, offset;
+ unsigned long *new;
+
+ inc = ALIGN((size - space->pn_map_len), BITS_PER_LONG) +
+ QUIC_PN_MAP_INCREMENT;
+ len = (u16)min(space->pn_map_len + inc, QUIC_PN_MAP_SIZE);
+
+ new = kzalloc(BITS_TO_BYTES(len), gfp);
+ if (!new)
+ return -ENOMEM;
+
+ offset = (u16)(space->max_pn_seen + 1 - space->base_pn);
+ bitmap_copy(new, space->pn_map, offset);
+ kfree(space->pn_map);
+ space->pn_map = new;
+ space->pn_map_len = len;
+
+ return 0;
+}
+
+/* Check if a packet number has been received.
+ *
+ * Returns: 0 if the packet number has not been received. 1 if it has already
+ * been received. -EINVAL if the packet number is invalid (out of range 0 to
+ * QUIC_PN_MAX) or too old to track (below min_pn_seen).
+ */
+int quic_pnspace_check(struct quic_pnspace *space, s64 pn)
+{
+ if (pn > QUIC_PN_MAX || pn < 0)
+ return -EINVAL;
+
+ if (space->base_pn == -1) /* No packet number received yet. */
+ return 0;
+
+ if (pn < space->min_pn_seen)
+ return -EINVAL;
+
+ if (pn < space->base_pn)
+ return 1;
+ if (pn - space->base_pn < space->pn_map_len &&
+ test_bit(pn - space->base_pn, space->pn_map))
+ return 1;
+
+ return 0;
+}
+
+/* Advance base_pn past contiguous received packet numbers. Finds the next gap
+ * (unreceived packet) beyond @pn, shifts the bitmap, and updates base_pn
+ * accordingly.
+ */
+static void quic_pnspace_move(struct quic_pnspace *space, s64 pn)
+{
+ u16 offset;
+
+ offset = (u16)(pn + 1 - space->base_pn);
+ offset = (u16)find_next_zero_bit(space->pn_map, space->pn_map_len,
+ offset);
+ space->base_pn += offset;
+ bitmap_shift_right(space->pn_map, space->pn_map, offset,
+ space->pn_map_len);
+}
+
+/* Mark a packet number as received. Updates the packet number map to record
+ * reception of @pn. Advances base_pn if possible, and updates max/min/last
+ * seen fields as needed.
+ *
+ * Returns: 0 on success or if the packet was already marked, or a negative
+ * error returned by bitmap growth when expanding the map.
+ */
+int quic_pnspace_mark(struct quic_pnspace *space, s64 pn, gfp_t gfp)
+{
+ s64 last_max_pn_seen, off;
+ u64 last_max_pn_time;
+ bool has_gap;
+ int err;
+
+ if (space->base_pn == -1) {
+ /* Initialize base_pn based on the peer's first packet number
+ * since peer's packet numbers may start at a non-zero value.
+ */
+ quic_pnspace_set_base_pn(space, pn + 1);
+ return 0;
+ }
+
+ /* Ignore packets with number less than current base (already
+ * processed).
+ */
+ if (pn < space->base_pn)
+ return 0;
+
+ /* If offset is beyond current map length, try to grow the bitmap to
+ * accommodate.
+ */
+ off = pn - space->base_pn;
+ if (off >= space->pn_map_len) {
+ if (off >= QUIC_PN_MAP_SIZE) {
+ bitmap_zero(space->pn_map, space->pn_map_len);
+ quic_pnspace_set_base_pn(space, pn + 1);
+ return 0;
+ }
+ err = quic_pnspace_grow(space, off + 1, gfp);
+ if (err)
+ return err;
+ }
+
+ has_gap = quic_pnspace_has_gap(space);
+ if (space->max_pn_seen < pn) {
+ space->max_pn_seen = pn;
+ space->max_pn_time = space->time;
+ }
+
+ if (space->base_pn == pn) { /* PN is next expected packet. */
+ if (has_gap) /* Advance to next gap. */
+ quic_pnspace_move(space, pn);
+ else /* Fast path: increment base_pn if no gaps. */
+ space->base_pn++;
+ } else { /* Mark this packet as received in the bitmap. */
+ set_bit(off, space->pn_map);
+ }
+
+ /* Only update min and last_max_pn_seen if this packet is the current
+ * max_pn.
+ */
+ if (space->max_pn_seen != pn)
+ return 0;
+
+ /* Check if enough time has elapsed or enough packets have been
+ * received to update tracking.
+ */
+ last_max_pn_seen = min_t(s64, space->last_max_pn_seen, space->base_pn);
+ last_max_pn_time = space->last_max_pn_time;
+ if (space->max_pn_time < last_max_pn_time + space->max_time_limit &&
+ space->max_pn_seen <= last_max_pn_seen + QUIC_PN_MAP_LIMIT)
+ return 0;
+
+ /* Advance base_pn if last_max_pn_seen is ahead of current base_pn.
+ * This is needed because QUIC doesn't retransmit packets;
+ * retransmitted frames are carried in new packets, so we move forward.
+ */
+ if (space->last_max_pn_seen + 1 > space->base_pn)
+ quic_pnspace_move(space, space->last_max_pn_seen);
+
+ space->min_pn_seen = space->last_max_pn_seen;
+ space->last_max_pn_seen = space->max_pn_seen;
+ space->last_max_pn_time = space->max_pn_time;
+ return 0;
+}
+
+/* Find the next gap in received packet numbers. Scans pn_map for a gap
+ * starting from *@iter. A gap is a contiguous block of unreceived packets
+ * between received ones.
+ *
+ * Returns: true if a gap was found, false if no more gaps exist or are
+ * relevant.
+ */
+static bool quic_pnspace_next_gap_ack(const struct quic_pnspace *space,
+ s64 *iter, u16 *start, u16 *end)
+{
+ u16 start_ = 0, end_ = 0, offset = (u16)(*iter - space->base_pn);
+
+ start_ = (u16)find_next_zero_bit(space->pn_map, space->pn_map_len,
+ offset);
+ if (space->max_pn_seen <= space->base_pn + start_)
+ return false;
+
+ end_ = (u16)find_next_bit(space->pn_map, space->pn_map_len, start_);
+ if (space->max_pn_seen <= space->base_pn + end_ - 1)
+ return false;
+
+ *start = start_ + 1;
+ *end = end_;
+ *iter = space->base_pn + *end;
+ return true;
+}
+
+/* Generate gap acknowledgment blocks (GABs). GABs describe ranges of
+ * unacknowledged packets between received ones, and are used in ACK frames.
+ *
+ * This function uses a sliding window approach to ensure the most recent gaps
+ * are preserved when the total number exceeds QUIC_PN_MAP_MAX_GABS. If there
+ * are more gaps than the limit, the oldest gaps are merged into a single gap,
+ * and the newest (QUIC_PN_MAP_MAX_GABS) gaps are preserved individually.
+ *
+ * Returns: Number of generated GABs (up to QUIC_PN_MAP_MAX_GABS).
+ */
+u16 quic_pnspace_num_gabs(struct quic_pnspace *space,
+ struct quic_gap_ack_block *gabs)
+{
+ struct quic_gap_ack_block tmp[QUIC_PN_MAP_MAX_GABS];
+ u16 start, end, mstart = 0, ngaps = 0, i = 0;
+ s64 iter;
+
+ if (!quic_pnspace_has_gap(space))
+ return 0;
+
+ iter = space->base_pn;
+
+ /* Scan all gaps using a sliding window to keep the newest ones. */
+ while (quic_pnspace_next_gap_ack(space, &iter, &start, &end)) {
+ if (ngaps < QUIC_PN_MAP_MAX_GABS) {
+ if (ngaps == 0)
+ mstart = start;
+ gabs[ngaps].start = start;
+ gabs[ngaps].end = end;
+ ngaps++;
+ continue;
+ }
+ gabs[i].start = start;
+ gabs[i].end = end;
+ i = (i + 1) % QUIC_PN_MAP_MAX_GABS;
+ }
+ if (i == 0) {
+ if (ngaps == QUIC_PN_MAP_MAX_GABS)
+ gabs[0].start = mstart;
+ return ngaps;
+ }
+
+ /* Overflow occurred: merge all discarded gaps with the gap at
+ * position i (which hasn't been overwritten yet) into one.
+ */
+ gabs[i].start = mstart;
+ memcpy(tmp, gabs, ngaps * sizeof(*gabs));
+ memcpy(gabs, &tmp[i], (ngaps - i) * sizeof(*gabs));
+ memcpy(gabs + (ngaps - i), tmp, i * sizeof(*gabs));
+
+ return ngaps;
+}
diff --git a/net/quic/pnspace.h b/net/quic/pnspace.h
new file mode 100644
index 0000000000000..15ce6d2ef7269
--- /dev/null
+++ b/net/quic/pnspace.h
@@ -0,0 +1,201 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_PN_MAP_MAX_GABS 32
+
+#define QUIC_PN_MAP_INITIAL 64
+#define QUIC_PN_MAP_INCREMENT QUIC_PN_MAP_INITIAL
+#define QUIC_PN_MAP_SIZE 4096
+#define QUIC_PN_MAP_LIMIT (QUIC_PN_MAP_SIZE * 3 / 4)
+
+#define QUIC_PNSPACE_MAX (QUIC_CRYPTO_MAX - 1)
+#define QUIC_PNSPACE_NEXT_PN 0
+#define QUIC_PNSPACE_TIME_LIMIT (333000 * 3)
+
+enum {
+ QUIC_ECN_ECT1,
+ QUIC_ECN_ECT0,
+ QUIC_ECN_CE,
+ QUIC_ECN_MAX
+};
+
+enum {
+ QUIC_ECN_LOCAL, /* ECN bits from incoming IP headers */
+ QUIC_ECN_PEER, /* ECN bits reported by peer in ACK frames */
+ QUIC_ECN_ACKED, /* ECN bits from packets newly ACKed */
+ QUIC_ECN_DIR_MAX
+};
+
+/* Represents a gap (range of missing packets) in the ACK map. The values are
+ * offsets from base_pn, with both 'start' and 'end' being +1.
+ */
+struct quic_gap_ack_block {
+ u16 start;
+ u16 end;
+};
+
+/* Packet Number Map (pn_map) Layout:
+ *
+ * min_pn_seen -->++-----------------------+---------------------+---
+ * base_pn -----^ last_max_pn_seen --^ max_pn_seen --^
+ *
+ * Map Advancement Logic:
+ * - min_pn_seen = last_max_pn_seen;
+ * - base_pn = first zero bit after last_max_pn_seen;
+ * - last_max_pn_seen = max_pn_seen;
+ * - last_max_pn_time = current time;
+ *
+ * Conditions to Advance pn_map:
+ * - (max_pn_time - last_max_pn_time) >= max_time_limit, or
+ * - (max_pn_seen - last_max_pn_seen) > QUIC_PN_MAP_LIMIT
+ *
+ * Gap Search Range:
+ * - From (base_pn - 1) to max_pn_seen
+ */
+struct quic_pnspace {
+ /* ECN counters indexed by dir and ECN codepoint (ECT1, ECT0, CE) */
+ u64 ecn_count[QUIC_ECN_DIR_MAX][QUIC_ECN_MAX];
+ unsigned long *pn_map; /* Received PN bitmap for ACK generation */
+ u16 pn_map_len; /* Length of the PN bit map (in bits) */
+ u8 need_sack; /* Flag indicating a SACK frame should be sent */
+ u8 sack_path; /* Path used for sending the SACK frame */
+ u8 sack_pending; /* Delayed ACK pending */
+
+ s64 last_max_pn_seen; /* Largest PN seen before pn_map advance */
+ u64 last_max_pn_time; /* Timestamp last_max_pn_seen was received */
+ s64 min_pn_seen; /* Smallest PN received */
+ s64 max_pn_seen; /* Largest PN received */
+ u64 max_pn_time; /* Timestamp max_pn_seen was received */
+ s64 base_pn; /* PN corresponding to the start of the pn_map */
+ u64 time; /* Cached now, or latest socket accept timestamp */
+
+ s64 max_pn_acked_seen; /* Largest PN ACKed by peer */
+ u64 max_pn_acked_time; /* Timestamp max_pn_acked_seen was ACKed */
+ u64 last_sent_time; /* Timestamp last ack-eliciting packet sent */
+ u64 loss_time; /* Timestamp the packet can be declared lost */
+ s64 next_pn; /* Next PN to send */
+
+ u32 max_time_limit; /* Time threshold to trigger pn_map advance */
+ u32 inflight; /* Ack-eliciting bytes in flight */
+};
+
+static inline void
+quic_pnspace_set_max_pn_acked_seen(struct quic_pnspace *space,
+ s64 max_pn_acked_seen)
+{
+ if (space->max_pn_acked_seen >= max_pn_acked_seen)
+ return;
+ space->max_pn_acked_seen = max_pn_acked_seen;
+ space->max_pn_acked_time = quic_ktime_get_us();
+}
+
+static inline void quic_pnspace_set_base_pn(struct quic_pnspace *space, s64 pn)
+{
+ space->base_pn = pn;
+ space->max_pn_seen = space->base_pn - 1;
+ space->last_max_pn_seen = space->max_pn_seen;
+ space->min_pn_seen = space->max_pn_seen;
+
+ space->max_pn_time = space->time;
+ space->last_max_pn_time = space->max_pn_time;
+}
+
+static inline bool quic_pnspace_has_gap(const struct quic_pnspace *space)
+{
+ return space->base_pn != space->max_pn_seen + 1;
+}
+
+static inline void quic_pnspace_inc_ecn_acked(struct quic_pnspace *space,
+ u8 ecn)
+{
+ if (!ecn)
+ return;
+ space->ecn_count[QUIC_ECN_ACKED][ecn - 1]++;
+}
+
+static inline void quic_pnspace_reset_ecn_acked(struct quic_pnspace *space)
+{
+ space->ecn_count[QUIC_ECN_ACKED][QUIC_ECN_ECT0] = 0;
+ space->ecn_count[QUIC_ECN_ACKED][QUIC_ECN_ECT1] = 0;
+ space->ecn_count[QUIC_ECN_ACKED][QUIC_ECN_CE] = 0;
+}
+
+static inline void quic_pnspace_inc_ecn_local(struct quic_pnspace *space,
+ u8 ecn)
+{
+ if (!ecn)
+ return;
+ space->ecn_count[QUIC_ECN_LOCAL][ecn - 1]++;
+}
+
+/* Check if any ECN-marked packets were received. */
+static inline bool quic_pnspace_has_ecn_local(struct quic_pnspace *space)
+{
+ return space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT0] ||
+ space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_ECT1] ||
+ space->ecn_count[QUIC_ECN_LOCAL][QUIC_ECN_CE];
+}
+
+/* Validate ECN counts received in an ACK. */
+static inline bool quic_pnspace_validate_ecn(struct quic_pnspace *space,
+ u64 *ecn_count)
+{
+ u64 *acked = space->ecn_count[QUIC_ECN_ACKED];
+ u64 *peer = space->ecn_count[QUIC_ECN_PEER];
+ u64 ect0, ect1, ce;
+
+ if (peer[QUIC_ECN_ECT0] > ecn_count[QUIC_ECN_ECT0] ||
+ peer[QUIC_ECN_ECT1] > ecn_count[QUIC_ECN_ECT1] ||
+ peer[QUIC_ECN_CE] > ecn_count[QUIC_ECN_CE])
+ return false;
+
+ /* rfc9000#section-13.4.2.1:
+ *
+ * ECN validation also fails if the sum of the increase in ECT(0) and
+ * ECN-CE counts is less than the number of newly acknowledged packets
+ * that were originally sent with an ECT(0) marking (Same for ECT(1)).
+ */
+ ect0 = ecn_count[QUIC_ECN_ECT0] - peer[QUIC_ECN_ECT0];
+ ect1 = ecn_count[QUIC_ECN_ECT1] - peer[QUIC_ECN_ECT1];
+ ce = ecn_count[QUIC_ECN_CE] - peer[QUIC_ECN_CE];
+
+ return ect0 + ce >= acked[QUIC_ECN_ECT0] &&
+ ect1 + ce >= acked[QUIC_ECN_ECT1];
+}
+
+/* Updates the stored ECN counters based on values received in the peer's ACK
+ * frame. Each counter is updated only if the new value is higher.
+ *
+ * Returns: true if CE count was increased (congestion indicated), false
+ * otherwise.
+ */
+static inline bool quic_pnspace_set_ecn_peer(struct quic_pnspace *space,
+ u64 *ecn_count)
+{
+ u64 *count = space->ecn_count[QUIC_ECN_PEER];
+
+ if (count[QUIC_ECN_ECT0] < ecn_count[QUIC_ECN_ECT0])
+ count[QUIC_ECN_ECT0] = ecn_count[QUIC_ECN_ECT0];
+ if (count[QUIC_ECN_ECT1] < ecn_count[QUIC_ECN_ECT1])
+ count[QUIC_ECN_ECT1] = ecn_count[QUIC_ECN_ECT1];
+ if (count[QUIC_ECN_CE] < ecn_count[QUIC_ECN_CE]) {
+ count[QUIC_ECN_CE] = ecn_count[QUIC_ECN_CE];
+ return true;
+ }
+ return false;
+}
+
+u16 quic_pnspace_num_gabs(struct quic_pnspace *space,
+ struct quic_gap_ack_block *gabs);
+int quic_pnspace_check(struct quic_pnspace *space, s64 pn);
+int quic_pnspace_mark(struct quic_pnspace *space, s64 pn, gfp_t gfp);
+
+void quic_pnspace_free(struct quic_pnspace *space);
+int quic_pnspace_init(struct quic_pnspace *space);
diff --git a/net/quic/protocol.c b/net/quic/protocol.c
new file mode 100644
index 0000000000000..fceb4d5723a3b
--- /dev/null
+++ b/net/quic/protocol.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/inet_common.h>
+#include <linux/proc_fs.h>
+#include <net/protocol.h>
+#include <net/rps.h>
+#include <net/tls.h>
+
+#include "socket.h"
+
+static unsigned int quic_net_id __read_mostly;
+
+struct percpu_counter quic_sockets_allocated;
+struct workqueue_struct *quic_wq;
+
+DEFINE_STATIC_KEY_FALSE(quic_alpn_demux_key);
+
+long sysctl_quic_mem[3];
+int sysctl_quic_rmem[3];
+int sysctl_quic_wmem[3];
+
+static int quic_inet_connect(struct socket *sock, struct sockaddr_unsized *addr,
+ int addr_len, int flags)
+{
+ struct sock *sk = sock->sk;
+
+ if (addr_len < (int)sizeof(addr->sa_family))
+ return -EINVAL;
+
+ return sk->sk_prot->connect(sk, addr, addr_len);
+}
+
+static int quic_inet_listen(struct socket *sock, int backlog)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_inet_getname(struct socket *sock, struct sockaddr *uaddr,
+ int peer)
+{
+ return quic_get_sk_addr(sock, uaddr, peer);
+}
+
+static __poll_t quic_inet_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ return 0;
+}
+
+struct quic_net *quic_net(struct net *net)
+{
+ return net_generic(net, quic_net_id);
+}
+
+#if IS_ENABLED(CONFIG_PROC_FS)
+static const struct snmp_mib quic_snmp_list[] = {
+ SNMP_MIB_ITEM("QuicConnCurrentEstabs", QUIC_MIB_CONN_CURRENTESTABS),
+ SNMP_MIB_ITEM("QuicConnPassiveEstabs", QUIC_MIB_CONN_PASSIVEESTABS),
+ SNMP_MIB_ITEM("QuicConnActiveEstabs", QUIC_MIB_CONN_ACTIVEESTABS),
+ SNMP_MIB_ITEM("QuicPktRcvFastpaths", QUIC_MIB_PKT_RCVFASTPATHS),
+ SNMP_MIB_ITEM("QuicPktDecFastpaths", QUIC_MIB_PKT_DECFASTPATHS),
+ SNMP_MIB_ITEM("QuicPktEncFastpaths", QUIC_MIB_PKT_ENCFASTPATHS),
+ SNMP_MIB_ITEM("QuicPktRcvBacklogs", QUIC_MIB_PKT_RCVBACKLOGS),
+ SNMP_MIB_ITEM("QuicPktDecBacklogs", QUIC_MIB_PKT_DECBACKLOGS),
+ SNMP_MIB_ITEM("QuicPktEncBacklogs", QUIC_MIB_PKT_ENCBACKLOGS),
+ SNMP_MIB_ITEM("QuicPktInvHdrDrop", QUIC_MIB_PKT_INVHDRDROP),
+ SNMP_MIB_ITEM("QuicPktInvNumDrop", QUIC_MIB_PKT_INVNUMDROP),
+ SNMP_MIB_ITEM("QuicPktInvFrmDrop", QUIC_MIB_PKT_INVFRMDROP),
+ SNMP_MIB_ITEM("QuicPktRcvDrop", QUIC_MIB_PKT_RCVDROP),
+ SNMP_MIB_ITEM("QuicPktDecDrop", QUIC_MIB_PKT_DECDROP),
+ SNMP_MIB_ITEM("QuicPktEncDrop", QUIC_MIB_PKT_ENCDROP),
+ SNMP_MIB_ITEM("QuicFrmRcvBufDrop", QUIC_MIB_FRM_RCVBUFDROP),
+ SNMP_MIB_ITEM("QuicFrmRetrans", QUIC_MIB_FRM_RETRANS),
+ SNMP_MIB_ITEM("QuicFrmOutCloses", QUIC_MIB_FRM_OUTCLOSES),
+ SNMP_MIB_ITEM("QuicFrmInCloses", QUIC_MIB_FRM_INCLOSES),
+};
+
+static int quic_snmp_seq_show(struct seq_file *seq, void *v)
+{
+ unsigned long buff[ARRAY_SIZE(quic_snmp_list)];
+ const int cnt = ARRAY_SIZE(quic_snmp_list);
+ struct net *net = seq->private;
+ u32 idx;
+
+ memset(buff, 0, sizeof(buff));
+
+ snmp_get_cpu_field_batch_cnt(buff, quic_snmp_list, cnt,
+ quic_net(net)->stat);
+ for (idx = 0; idx < cnt; idx++)
+ seq_printf(seq, "%-32s\t%lu\n", quic_snmp_list[idx].name,
+ buff[idx]);
+
+ return 0;
+}
+
+static int quic_net_proc_init(struct net *net)
+{
+ quic_net(net)->proc_net = proc_net_mkdir(net, "quic", net->proc_net);
+ if (!quic_net(net)->proc_net)
+ return -ENOMEM;
+
+ if (!proc_create_net_single("snmp", 0444, quic_net(net)->proc_net,
+ quic_snmp_seq_show, NULL))
+ goto free;
+ return 0;
+free:
+ remove_proc_subtree("quic", net->proc_net);
+ quic_net(net)->proc_net = NULL;
+ return -ENOMEM;
+}
+
+static void quic_net_proc_exit(struct net *net)
+{
+ remove_proc_subtree("quic", net->proc_net);
+ quic_net(net)->proc_net = NULL;
+}
+#endif
+
+static const struct proto_ops quic_proto_ops = {
+ .family = PF_INET,
+ .owner = THIS_MODULE,
+ .release = inet_release,
+ .bind = inet_bind,
+ .connect = quic_inet_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = inet_accept,
+ .getname = quic_inet_getname,
+ .poll = quic_inet_poll,
+ .ioctl = inet_ioctl,
+ .gettstamp = sock_gettstamp,
+ .listen = quic_inet_listen,
+ .shutdown = inet_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = inet_sendmsg,
+ .recvmsg = inet_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static struct inet_protosw quic_stream_protosw = {
+ .type = SOCK_STREAM,
+ .protocol = IPPROTO_QUIC,
+ .prot = &quic_prot,
+ .ops = &quic_proto_ops,
+};
+
+static struct inet_protosw quic_dgram_protosw = {
+ .type = SOCK_DGRAM,
+ .protocol = IPPROTO_QUIC,
+ .prot = &quic_prot,
+ .ops = &quic_proto_ops,
+};
+
+static const struct proto_ops quicv6_proto_ops = {
+ .family = PF_INET6,
+ .owner = THIS_MODULE,
+ .release = inet6_release,
+ .bind = inet6_bind,
+ .connect = quic_inet_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = inet_accept,
+ .getname = quic_inet_getname,
+ .poll = quic_inet_poll,
+ .ioctl = inet6_ioctl,
+ .gettstamp = sock_gettstamp,
+ .listen = quic_inet_listen,
+ .shutdown = inet_shutdown,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
+ .sendmsg = inet_sendmsg,
+ .recvmsg = inet_recvmsg,
+ .mmap = sock_no_mmap,
+};
+
+static struct inet_protosw quicv6_stream_protosw = {
+ .type = SOCK_STREAM,
+ .protocol = IPPROTO_QUIC,
+ .prot = &quicv6_prot,
+ .ops = &quicv6_proto_ops,
+};
+
+static struct inet_protosw quicv6_dgram_protosw = {
+ .type = SOCK_DGRAM,
+ .protocol = IPPROTO_QUIC,
+ .prot = &quicv6_prot,
+ .ops = &quicv6_proto_ops,
+};
+
+static int quic_protosw_init(void)
+{
+ int err;
+
+ err = proto_register(&quic_prot, 1);
+ if (err)
+ return err;
+
+ err = proto_register(&quicv6_prot, 1);
+ if (err) {
+ proto_unregister(&quic_prot);
+ return err;
+ }
+
+ inet_register_protosw(&quic_stream_protosw);
+ inet_register_protosw(&quic_dgram_protosw);
+ inet6_register_protosw(&quicv6_stream_protosw);
+ inet6_register_protosw(&quicv6_dgram_protosw);
+
+ return 0;
+}
+
+static void quic_protosw_exit(void)
+{
+ inet_unregister_protosw(&quic_dgram_protosw);
+ inet_unregister_protosw(&quic_stream_protosw);
+ proto_unregister(&quic_prot);
+
+ inet6_unregister_protosw(&quicv6_dgram_protosw);
+ inet6_unregister_protosw(&quicv6_stream_protosw);
+ proto_unregister(&quicv6_prot);
+}
+
+static int __net_init quic_net_init(struct net *net)
+{
+ struct quic_net *qn = quic_net(net);
+ int err = 0;
+
+ qn->stat = alloc_percpu(struct quic_mib);
+ if (!qn->stat)
+ return -ENOMEM;
+
+#if IS_ENABLED(CONFIG_PROC_FS)
+ err = quic_net_proc_init(net);
+ if (err) {
+ free_percpu(qn->stat);
+ qn->stat = NULL;
+ }
+#endif
+ return err;
+}
+
+static void __net_exit quic_net_exit(struct net *net)
+{
+ struct quic_net *qn = quic_net(net);
+
+#if IS_ENABLED(CONFIG_PROC_FS)
+ quic_net_proc_exit(net);
+#endif
+ free_percpu(qn->stat);
+ qn->stat = NULL;
+}
+
+static struct pernet_operations quic_net_ops = {
+ .init = quic_net_init,
+ .exit = quic_net_exit,
+ .id = &quic_net_id,
+ .size = sizeof(struct quic_net),
+};
+
+#if IS_ENABLED(CONFIG_SYSCTL)
+static struct ctl_table_header *quic_sysctl_header;
+
+static struct ctl_table quic_table[] = {
+ {
+ .procname = "quic_mem",
+ .data = &sysctl_quic_mem,
+ .maxlen = sizeof(sysctl_quic_mem),
+ .mode = 0644,
+ .proc_handler = proc_doulongvec_minmax
+ },
+ {
+ .procname = "quic_rmem",
+ .data = &sysctl_quic_rmem,
+ .maxlen = sizeof(sysctl_quic_rmem),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ONE,
+ },
+ {
+ .procname = "quic_wmem",
+ .data = &sysctl_quic_wmem,
+ .maxlen = sizeof(sysctl_quic_wmem),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = SYSCTL_ONE,
+ },
+};
+
+static void quic_sysctl_register(void)
+{
+ quic_sysctl_header = register_net_sysctl(&init_net, "net/quic",
+ quic_table);
+}
+
+static void quic_sysctl_unregister(void)
+{
+ unregister_net_sysctl_table(quic_sysctl_header);
+}
+#endif
+
+static __init int quic_init(void)
+{
+ int max_share, err = -ENOMEM;
+ unsigned long limit;
+
+ BUILD_BUG_ON(sizeof(struct quic_skb_cb) >
+ sizeof_field(struct sk_buff, cb));
+
+ /* Set QUIC memory limits based on available system memory, similar to
+ * sctp_init().
+ */
+ limit = nr_free_buffer_pages() / 8;
+ limit = max(limit, 128UL);
+ sysctl_quic_mem[0] = (long)limit / 4 * 3;
+ sysctl_quic_mem[1] = (long)limit;
+ sysctl_quic_mem[2] = sysctl_quic_mem[0] * 2;
+
+ limit = (sysctl_quic_mem[1]) << (PAGE_SHIFT - 7);
+ max_share = min(4UL * 1024 * 1024, limit);
+
+ sysctl_quic_rmem[0] = PAGE_SIZE;
+ sysctl_quic_rmem[1] = 1024 * 1024;
+ sysctl_quic_rmem[2] = max(sysctl_quic_rmem[1], max_share);
+
+ sysctl_quic_wmem[0] = PAGE_SIZE;
+ sysctl_quic_wmem[1] = 16 * 1024;
+ sysctl_quic_wmem[2] = max(64 * 1024, max_share);
+
+ err = percpu_counter_init(&quic_sockets_allocated, 0, GFP_KERNEL);
+ if (err)
+ goto err_percpu_counter;
+
+ err = quic_hash_tables_init();
+ if (err)
+ goto err_hash;
+
+ /* Allocate an unbound workqueue for UDP socket destruction and backlog
+ * packet processing.
+ */
+ quic_wq = alloc_workqueue("quic_workqueue", WQ_UNBOUND, 0);
+ if (!quic_wq) {
+ err = -ENOMEM;
+ goto err_wq;
+ }
+
+ err = register_pernet_subsys(&quic_net_ops);
+ if (err)
+ goto err_def_ops;
+
+ err = quic_protosw_init();
+ if (err)
+ goto err_protosw;
+
+#if IS_ENABLED(CONFIG_SYSCTL)
+ quic_sysctl_register();
+#endif
+ pr_info("quic: init\n");
+ return 0;
+
+err_protosw:
+ unregister_pernet_subsys(&quic_net_ops);
+err_def_ops:
+ destroy_workqueue(quic_wq);
+err_wq:
+ quic_hash_tables_destroy();
+err_hash:
+ percpu_counter_destroy(&quic_sockets_allocated);
+err_percpu_counter:
+ return err;
+}
+
+static __exit void quic_exit(void)
+{
+#if IS_ENABLED(CONFIG_SYSCTL)
+ quic_sysctl_unregister();
+#endif
+ quic_protosw_exit();
+ unregister_pernet_subsys(&quic_net_ops);
+ flush_workqueue(quic_wq);
+ destroy_workqueue(quic_wq);
+ quic_hash_tables_destroy();
+ percpu_counter_destroy(&quic_sockets_allocated);
+ rcu_barrier();
+ pr_info("quic: exit\n");
+}
+
+module_init(quic_init);
+module_exit(quic_exit);
+
+MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); /* IPPROTO_QUIC == 261 */
+MODULE_ALIAS_NET_PF_PROTO(PF_INET6, 261);
+MODULE_AUTHOR("Xin Long <lucien.xin@gmail.com>");
+MODULE_DESCRIPTION("Support for the QUIC protocol (RFC9000)");
+MODULE_LICENSE("GPL");
diff --git a/net/quic/protocol.h b/net/quic/protocol.h
new file mode 100644
index 0000000000000..fbd0fe39eccc6
--- /dev/null
+++ b/net/quic/protocol.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+extern struct percpu_counter quic_sockets_allocated;
+
+DECLARE_STATIC_KEY_FALSE(quic_alpn_demux_key);
+
+extern long sysctl_quic_mem[3];
+extern int sysctl_quic_rmem[3];
+extern int sysctl_quic_wmem[3];
+
+enum {
+ QUIC_MIB_NUM = 0,
+ QUIC_MIB_CONN_CURRENTESTABS, /* Current established connections */
+ QUIC_MIB_CONN_PASSIVEESTABS, /* Passively established connections */
+ QUIC_MIB_CONN_ACTIVEESTABS, /* Actively established connections */
+ QUIC_MIB_PKT_RCVFASTPATHS, /* Packets received on fast path */
+ QUIC_MIB_PKT_DECFASTPATHS, /* Packets decrypted on fast path */
+ QUIC_MIB_PKT_ENCFASTPATHS, /* Packets encrypted on fast path */
+ QUIC_MIB_PKT_RCVBACKLOGS, /* Packets processed via backlog */
+ QUIC_MIB_PKT_DECBACKLOGS, /* Packets decrypted in backlog */
+ QUIC_MIB_PKT_ENCBACKLOGS, /* Packets encrypted in backlog */
+ QUIC_MIB_PKT_INVHDRDROP, /* Dropped: invalid packet header */
+ QUIC_MIB_PKT_INVNUMDROP, /* Dropped: invalid packet number */
+ QUIC_MIB_PKT_INVFRMDROP, /* Dropped: invalid frame */
+ QUIC_MIB_PKT_RCVDROP, /* Dropped on receive (general) */
+ QUIC_MIB_PKT_DECDROP, /* Dropped: decryption failure */
+ QUIC_MIB_PKT_ENCDROP, /* Dropped: encryption failure */
+ QUIC_MIB_FRM_RCVBUFDROP, /* Frames dropped: recv buf limit */
+ QUIC_MIB_FRM_RETRANS, /* Frames retransmitted */
+ QUIC_MIB_FRM_OUTCLOSES, /* CONNECTION_CLOSE frames sent */
+ QUIC_MIB_FRM_INCLOSES, /* CONNECTION_CLOSE frames rcvd */
+ QUIC_MIB_MAX
+};
+
+struct quic_mib {
+ unsigned long mibs[QUIC_MIB_MAX]; /* Counters indexed by QUIC_MIB_* */
+};
+
+struct quic_net {
+ DEFINE_SNMP_STAT(struct quic_mib, stat); /* Per-net QUIC MIB stats */
+#if IS_ENABLED(CONFIG_PROC_FS)
+ struct proc_dir_entry *proc_net; /* procfs entry for QUIC stats */
+#endif
+};
+
+struct quic_net *quic_net(struct net *net);
+
+#define QUIC_INC_STATS(net, field) SNMP_INC_STATS(quic_net(net)->stat, field)
+#define QUIC_DEC_STATS(net, field) SNMP_DEC_STATS(quic_net(net)->stat, field)
diff --git a/net/quic/socket.c b/net/quic/socket.c
new file mode 100644
index 0000000000000..0fcb123d31bba
--- /dev/null
+++ b/net/quic/socket.c
@@ -0,0 +1,654 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/inet_common.h>
+#include <net/tls.h>
+
+#include "socket.h"
+
+static DEFINE_PER_CPU(int, quic_memory_per_cpu_fw_alloc);
+static unsigned long quic_memory_pressure;
+static atomic_long_t quic_memory_allocated;
+
+static void quic_enter_memory_pressure(struct sock *sk)
+{
+ WRITE_ONCE(quic_memory_pressure, 1);
+}
+
+/* Check if a matching request sock already exists. Match is based on
+ * source/destination addresses and DCID.
+ */
+struct quic_request_sock *quic_request_sock_lookup(struct sock *sk)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct quic_request_sock *req;
+
+ list_for_each_entry(req, quic_reqs(sk), list) {
+ if (!memcmp(&req->saddr, &packet->saddr, sizeof(req->saddr)) &&
+ !memcmp(&req->daddr, &packet->daddr, sizeof(req->daddr)) &&
+ !quic_conn_id_cmp(&req->dcid, &packet->dcid))
+ return req;
+ }
+ return NULL;
+}
+
+/* Create and enqueue a QUIC request sock for a new incoming connection. */
+struct quic_request_sock *quic_request_sock_create(struct sock *sk,
+ struct quic_conn_id *odcid,
+ u8 retry, gfp_t gfp)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ struct quic_request_sock *req;
+
+ if (sk_acceptq_is_full(sk)) /* Refuse if accept queue full. */
+ return ERR_PTR(-ENOBUFS);
+
+ req = kmalloc_obj(*req, gfp);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+
+ req->version = packet->version;
+ req->daddr = packet->daddr;
+ req->saddr = packet->saddr;
+ req->scid = packet->scid;
+ req->dcid = packet->dcid;
+ req->orig_dcid = *odcid;
+ req->retry = retry;
+
+ skb_queue_head_init(&req->backlog_list);
+ req->blen = 0;
+
+ /* Enqueue request into listen socket’s pending list for accept(). */
+ list_add_tail(&req->list, quic_reqs(sk));
+ sk_acceptq_added(sk);
+ return req;
+}
+
+int quic_request_sock_backlog_tail(struct sock *sk,
+ struct quic_request_sock *req,
+ struct sk_buff *skb)
+{
+ u32 limit = sk->sk_rcvbuf / sk->sk_max_ack_backlog;
+ int len = skb->truesize;
+
+ limit = max_t(u32, limit, QUIC_MIN_UDP_PAYLOAD * 4);
+ if (req->blen + len > limit || !__sk_rmem_schedule(sk, len, false)) {
+ QUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);
+ kfree_skb(skb);
+ return -ENOBUFS;
+ }
+
+ QUIC_SKB_CB(skb)->backlog = 1;
+ skb_set_owner_r(skb, sk);
+ __skb_queue_tail(&req->backlog_list, skb);
+ req->blen += len;
+
+ sk->sk_data_ready(sk);
+ return 0;
+}
+
+/* Check if a matching accept socket exists. This is needed because an accept
+ * socket might have been created after this packet was enqueued in the listen
+ * socket's backlog.
+ */
+bool quic_accept_sock_exists(struct sock *sk, struct sk_buff *skb)
+{
+ struct quic_packet *packet = quic_packet(sk);
+ bool exist = false;
+
+ /* Skip if packet is newer than the last accept socket creation time.
+ * No matching socket could exist in this case.
+ */
+ if (QUIC_SKB_CB(skb)->time >
+ quic_pnspace(sk, QUIC_CRYPTO_INITIAL)->time)
+ return exist;
+
+ /* Look up accepted socket matching packet addresses and DCID. */
+ local_bh_disable();
+ sk = quic_sock_lookup(skb, &packet->saddr, &packet->daddr,
+ quic_path_usock(quic_paths(sk), 0),
+ &packet->dcid);
+ if (!sk)
+ goto out;
+
+ /* Found a matching accept socket. Process packet with this socket. */
+ skb_orphan(skb);
+ bh_lock_sock_nested(sk);
+ if (sock_owned_by_user(sk)) {
+ /* Socket is busy (owned by user context): queue to backlog. */
+ if (sk_add_backlog(sk, skb, READ_ONCE(sk->sk_rcvbuf))) {
+ QUIC_INC_STATS(sock_net(sk), QUIC_MIB_PKT_RCVDROP);
+ kfree_skb(skb);
+ }
+ } else {
+ /* Socket not busy: process immediately. */
+ sk->sk_backlog_rcv(sk, skb); /* quic_packet_process(). */
+ }
+ bh_unlock_sock(sk);
+ sock_put(sk);
+ exist = true;
+out:
+ local_bh_enable();
+ return exist;
+}
+
+/* Lookup a connected QUIC socket based on address and dest connection ID.
+ *
+ * This function searches the established (non-listening) QUIC socket table for
+ * a socket that matches the source and dest addresses and, optionally, the
+ * dest connection ID (DCID). The value returned by quic_path_orig_dcid() might
+ * be the original dest connection ID from the ClientHello or the Source
+ * Connection ID from a Retry packet before.
+ *
+ * The DCID is provided from a handshake packet when searching by source
+ * connection ID fails, such as when the peer has not yet received server's
+ * response and updated the DCID.
+ *
+ * Return: A pointer to the matching connected socket, or NULL if no match is
+ * found.
+ */
+struct sock *quic_sock_lookup(struct sk_buff *skb, union quic_addr *sa,
+ union quic_addr *da, struct sock *usk,
+ struct quic_conn_id *dcid)
+{
+ union quic_addr *path_sa, *path_da;
+ struct net *net = sock_net(usk);
+ struct quic_path_group *paths;
+ struct hlist_nulls_node *node;
+ struct quic_shash_head *head;
+ struct sock *sk = NULL, *tmp;
+ struct quic_conn_id *odcid;
+ unsigned int hash, seq;
+ bool match;
+
+ hash = quic_sock_hash(net, sa, da);
+ head = quic_sock_head(hash);
+
+ rcu_read_lock();
+begin:
+ sk_nulls_for_each_rcu(tmp, node, &head->head) {
+ if (net != sock_net(tmp))
+ continue;
+ paths = quic_paths(tmp);
+ odcid = quic_path_orig_dcid(paths);
+
+ /* Protect path[0] reads with seqcount retry to detect torn
+ * reads during concurrent quic_path_swap(). The seqcount
+ * ensures we either see a consistent old or new path, never
+ * a mix of both.
+ */
+ do {
+ seq = read_seqcount_begin(&paths->path_seq);
+ path_sa = quic_path_saddr(paths, 0);
+ path_da = quic_path_daddr(paths, 0);
+ match = (quic_cmp_sk_addr(tmp, path_sa, sa) &&
+ quic_cmp_sk_addr(tmp, path_da, da) &&
+ quic_path_usock(paths, 0) == usk &&
+ (!dcid || !quic_conn_id_cmp(odcid, dcid)));
+ } while (read_seqcount_retry(&paths->path_seq, seq));
+
+ if (match) {
+ sk = tmp;
+ break;
+ }
+ }
+ /* If the final nulls value differs from the expected one, restart the
+ * lookup as the node may have been rehashed (e.g., due to connection
+ * migration).
+ */
+ if (!sk && get_nulls_value(node) != hash)
+ goto begin;
+
+ if (sk && unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
+ sk = NULL;
+ rcu_read_unlock();
+ return sk;
+}
+
+/* Find the listening QUIC socket for an incoming packet.
+ *
+ * This function searches the QUIC socket table for a listening socket that
+ * matches the dest address and port, and the ALPN(s) if presented in the
+ * ClientHello. If multiple listening sockets are bound to the same address,
+ * port, and ALPN(s) (e.g., via SO_REUSEPORT), this function selects a socket
+ * from the reuseport group.
+ *
+ * Return: A pointer to the matching listening socket, or NULL if no match is
+ * found.
+ */
+struct sock *quic_listen_sock_lookup(struct sk_buff *skb, union quic_addr *sa,
+ union quic_addr *da, struct sock *usk,
+ struct quic_data *alpns)
+{
+ struct net *net = sock_net(usk);
+ struct hlist_nulls_node *node;
+ struct sock *sk = NULL, *tmp;
+ struct quic_shash_head *head;
+ struct quic_data alpn;
+ union quic_addr *a;
+ u32 hash, len;
+ u64 length;
+ u8 *p;
+
+ hash = quic_listen_sock_hash(net, ntohs(sa->v4.sin_port));
+ head = quic_listen_sock_head(hash);
+
+ rcu_read_lock();
+ /* Iterate sockets, checking ALPN requirements. Address specificity
+ * always takes precedence over ALPN preference order.
+ */
+ sk_nulls_for_each_rcu(tmp, node, &head->head) {
+ bool alpn_match = false;
+
+ a = quic_path_saddr(quic_paths(tmp), 0);
+ if (net != sock_net(tmp) || !quic_cmp_sk_addr(tmp, a, sa) ||
+ quic_path_usock(quic_paths(tmp), 0) != usk)
+ continue;
+
+ if (!alpns->len) {
+ /* No ALPN extension or empty ALPN list.
+ * If alpns->data is NULL, match any socket.
+ * If alpns->data is set (empty ALPN), only match
+ * sockets with no ALPN configured.
+ */
+ alpn_match = (!alpns->data || !quic_alpn(tmp)->len);
+ } else {
+ /* Check if any client ALPN matches this socket. */
+ for (p = alpns->data, len = alpns->len; len;
+ len -= length, p += length) {
+ quic_get_int(&p, &len, &length, 1);
+ quic_data(&alpn, p, length);
+ if (quic_data_has(quic_alpn(tmp), &alpn)) {
+ alpn_match = true;
+ break;
+ }
+ }
+ }
+
+ if (alpn_match) {
+ if (!quic_is_any_addr(a)) {
+ /* Specific address - best match. */
+ sk = tmp;
+ goto out;
+ }
+ /* ANY address - keep as candidate. */
+ if (!sk || a->sa.sa_family == sa->sa.sa_family)
+ sk = tmp;
+ }
+ }
+ /* No need to check get_nulls_value(node) != hash for !sk, as
+ * hashtable size is fixed and a listen sk can not rehashed.
+ */
+out:
+ if (sk && sk->sk_reuseport)
+ sk = reuseport_select_sock(sk, quic_addr_hash(net, da), skb, 1);
+
+ if (sk && unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
+ sk = NULL;
+ rcu_read_unlock();
+ return sk;
+}
+
+/* Switch packet to a different listening socket based on ALPN matching.
+ *
+ * When ALPN demultiplexing is enabled, this function attempts to find a
+ * listening socket that matches the parsed ALPN. If a different socket is
+ * found, the packet is switched to that socket for processing.
+ *
+ * Return: true if switched to a different socket, false otherwise.
+ */
+bool quic_listen_sock_switch(struct sk_buff *skb, struct quic_data *alpns)
+{
+ struct sock *nsk, *sk = skb->sk;
+ struct quic_packet *packet;
+
+ if (!alpns->data)
+ return false;
+
+ local_bh_disable();
+ packet = quic_packet(sk);
+ nsk = quic_listen_sock_lookup(skb, &packet->saddr, &packet->daddr,
+ quic_path_usock(quic_paths(sk), 0),
+ alpns);
+ if (!nsk)
+ goto out;
+ if (nsk == sk) {
+ sock_put(nsk);
+ goto out;
+ }
+ local_bh_enable();
+ release_sock(sk);
+
+ skb_orphan(skb);
+
+ lock_sock(nsk);
+ nsk->sk_backlog_rcv(nsk, skb); /* quic_packet_process(). */
+ release_sock(nsk);
+ sock_put(nsk);
+
+ lock_sock(sk);
+ return true;
+out:
+ local_bh_enable();
+ return false;
+}
+
+static void quic_write_space(struct sock *sk)
+{
+ __poll_t mask = EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
+ struct socket_wq *wq;
+
+ /* Do not check sock_writeable(). Also wakes stream-open waiters
+ * blocked on stream limits, where sock_writeable() may be false.
+ */
+ rcu_read_lock();
+ wq = rcu_dereference(sk->sk_wq);
+ if (skwq_has_sleeper(wq))
+ wake_up_interruptible_sync_poll(&wq->wait, mask);
+ sk_wake_async_rcu(sk, SOCK_WAKE_SPACE, POLL_OUT);
+ rcu_read_unlock();
+}
+
+static void quic_sock_destruct(struct sock *sk)
+{
+ u8 i;
+
+ /* Deferred crypto free for async encryption/decryption. */
+ for (i = 0; i < QUIC_CRYPTO_MAX; i++)
+ quic_crypto_free(quic_crypto(sk, i));
+
+ /* Deferred ALPN free for RCU readers in quic_listen_sock_lookup(). */
+ quic_data_free(quic_alpn(sk));
+
+ quic_sk_destruct(sk);
+}
+
+static int quic_init_sock(struct sock *sk)
+{
+ u8 i;
+
+ sk->sk_destruct = quic_sock_destruct;
+ sk->sk_write_space = quic_write_space;
+ sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
+
+ sk_sockets_allocated_inc(sk);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);
+ INIT_LIST_HEAD(quic_reqs(sk));
+
+ quic_conn_id_set_init(quic_source(sk), true);
+ quic_conn_id_set_init(quic_dest(sk), false);
+ quic_path_init(quic_paths(sk));
+ quic_cong_init(quic_cong(sk));
+
+ quic_timer_init(sk);
+ quic_packet_init(sk);
+
+ if (quic_stream_init(quic_streams(sk)))
+ return -ENOMEM;
+
+ for (i = 0; i < QUIC_PNSPACE_MAX; i++) {
+ if (quic_pnspace_init(quic_pnspace(sk, i)))
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void quic_destroy_sock(struct sock *sk)
+{
+ u8 i;
+
+ quic_timer_free(sk);
+ quic_packet_free(sk);
+
+ for (i = 0; i < QUIC_PNSPACE_MAX; i++)
+ quic_pnspace_free(quic_pnspace(sk, i));
+
+ quic_path_unbind(sk, quic_paths(sk), 0);
+ quic_path_unbind(sk, quic_paths(sk), 1);
+
+ quic_conn_id_set_free(quic_source(sk));
+ quic_conn_id_set_free(quic_dest(sk));
+
+ quic_stream_free(quic_streams(sk));
+
+ quic_data_free(quic_ticket(sk));
+ quic_data_free(quic_token(sk));
+
+ sk_sockets_allocated_dec(sk);
+ sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
+}
+
+static int quic_bind(struct sock *sk, struct sockaddr_unsized *addr,
+ int addr_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_connect(struct sock *sk, struct sockaddr_unsized *addr,
+ int addr_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_hash(struct sock *sk)
+{
+ return 0;
+}
+
+static void quic_unhash(struct sock *sk)
+{
+}
+
+static int quic_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int quic_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
+ int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static struct sock *quic_accept(struct sock *sk, struct proto_accept_arg *arg)
+{
+ arg->err = -EOPNOTSUPP;
+ return NULL;
+}
+
+static void quic_close(struct sock *sk, long timeout)
+{
+ lock_sock(sk);
+
+ quic_set_state(sk, QUIC_SS_CLOSED);
+
+ release_sock(sk);
+
+ sk_common_release(sk);
+}
+
+/**
+ * quic_do_setsockopt - set a QUIC socket option
+ * @sk: socket to configure
+ * @optname: option name (QUIC-level)
+ * @optval: user buffer containing the option value
+ * @optlen: size of the option value
+ *
+ * Sets a QUIC socket option on a given socket.
+ *
+ * Return:
+ * - On success, 0 is returned.
+ * - On error, a negative error value is returned.
+ */
+int quic_do_setsockopt(struct sock *sk, int optname, sockptr_t optval,
+ unsigned int optlen)
+{
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(quic_do_setsockopt);
+
+static int quic_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ if (level != SOL_QUIC)
+ return quic_common_setsockopt(sk, level, optname, optval,
+ optlen);
+
+ return quic_do_setsockopt(sk, optname, optval, optlen);
+}
+
+/**
+ * quic_do_getsockopt - get a QUIC socket option
+ * @sk: socket to query
+ * @optname: option name (QUIC-level)
+ * @optval: user buffer to receive the option value
+ * @optlen: pointer to buffer size; updated with actual size on return
+ *
+ * Gets a QUIC socket option from a given socket.
+ *
+ * Return:
+ * - On success, 0 is returned.
+ * - On error, a negative error value is returned.
+ */
+int quic_do_getsockopt(struct sock *sk, int optname, sockptr_t optval,
+ sockptr_t optlen)
+{
+ return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL_GPL(quic_do_getsockopt);
+
+static int quic_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ if (level != SOL_QUIC)
+ return quic_common_getsockopt(sk, level, optname, optval,
+ optlen);
+
+ return quic_do_getsockopt(sk, optname, USER_SOCKPTR(optval),
+ USER_SOCKPTR(optlen));
+}
+
+static void quic_release_cb(struct sock *sk)
+{
+ /* Similar to tcp_release_cb(). */
+ unsigned long nflags, flags = smp_load_acquire(&sk->sk_tsq_flags);
+
+ do {
+ if (!(flags & QUIC_DEFERRED_ALL))
+ return;
+ nflags = flags & ~QUIC_DEFERRED_ALL;
+ } while (!try_cmpxchg(&sk->sk_tsq_flags, &flags, nflags));
+
+ if (flags & QUIC_F_MTU_REDUCED_DEFERRED) {
+ quic_packet_rcv_err_pmtu(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_LOSS_DEFERRED) {
+ quic_timer_loss_handler(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_SACK_DEFERRED) {
+ quic_timer_sack_handler(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_PATH_DEFERRED) {
+ quic_timer_path_handler(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_PMTU_DEFERRED) {
+ quic_timer_pmtu_handler(sk);
+ __sock_put(sk);
+ }
+ if (flags & QUIC_F_PACE_DEFERRED) {
+ quic_timer_pace_handler(sk);
+ __sock_put(sk);
+ }
+}
+
+static int quic_disconnect(struct sock *sk, int flags)
+{
+ return -EOPNOTSUPP;
+}
+
+static void quic_shutdown(struct sock *sk, int how)
+{
+ quic_set_state(sk, QUIC_SS_CLOSED);
+}
+
+static int quic_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ return quic_packet_process(sk, skb, GFP_ATOMIC);
+}
+
+struct proto quic_prot = {
+ .name = "QUIC",
+ .owner = THIS_MODULE,
+ .init = quic_init_sock,
+ .destroy = quic_destroy_sock,
+ .shutdown = quic_shutdown,
+ .setsockopt = quic_setsockopt,
+ .getsockopt = quic_getsockopt,
+ .connect = quic_connect,
+ .bind = quic_bind,
+ .close = quic_close,
+ .disconnect = quic_disconnect,
+ .sendmsg = quic_sendmsg,
+ .recvmsg = quic_recvmsg,
+ .accept = quic_accept,
+ .hash = quic_hash,
+ .unhash = quic_unhash,
+ .backlog_rcv = quic_backlog_rcv,
+ .release_cb = quic_release_cb,
+ .no_autobind = true,
+ .obj_size = sizeof(struct quic_sock),
+ .sysctl_mem = sysctl_quic_mem,
+ .sysctl_rmem = sysctl_quic_rmem,
+ .sysctl_wmem = sysctl_quic_wmem,
+ .memory_pressure = &quic_memory_pressure,
+ .enter_memory_pressure = quic_enter_memory_pressure,
+ .memory_allocated = &quic_memory_allocated,
+ .per_cpu_fw_alloc = &quic_memory_per_cpu_fw_alloc,
+ .sockets_allocated = &quic_sockets_allocated,
+};
+
+struct proto quicv6_prot = {
+ .name = "QUICv6",
+ .owner = THIS_MODULE,
+ .init = quic_init_sock,
+ .destroy = quic_destroy_sock,
+ .shutdown = quic_shutdown,
+ .setsockopt = quic_setsockopt,
+ .getsockopt = quic_getsockopt,
+ .connect = quic_connect,
+ .bind = quic_bind,
+ .close = quic_close,
+ .disconnect = quic_disconnect,
+ .sendmsg = quic_sendmsg,
+ .recvmsg = quic_recvmsg,
+ .accept = quic_accept,
+ .hash = quic_hash,
+ .unhash = quic_unhash,
+ .backlog_rcv = quic_backlog_rcv,
+ .release_cb = quic_release_cb,
+ .no_autobind = true,
+ .obj_size = sizeof(struct quic6_sock),
+ .ipv6_pinfo_offset = offsetof(struct quic6_sock, inet6),
+ .sysctl_mem = sysctl_quic_mem,
+ .sysctl_rmem = sysctl_quic_rmem,
+ .sysctl_wmem = sysctl_quic_wmem,
+ .memory_pressure = &quic_memory_pressure,
+ .enter_memory_pressure = quic_enter_memory_pressure,
+ .memory_allocated = &quic_memory_allocated,
+ .per_cpu_fw_alloc = &quic_memory_per_cpu_fw_alloc,
+ .sockets_allocated = &quic_sockets_allocated,
+};
diff --git a/net/quic/socket.h b/net/quic/socket.h
new file mode 100644
index 0000000000000..9fe78c0d4ae57
--- /dev/null
+++ b/net/quic/socket.h
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <net/udp_tunnel.h>
+#include <linux/quic.h>
+
+#include "common.h"
+#include "pnspace.h"
+#include "family.h"
+#include "stream.h"
+#include "connid.h"
+#include "crypto.h"
+#include "path.h"
+#include "cong.h"
+
+#include "packet.h"
+
+#include "protocol.h"
+#include "timer.h"
+
+extern struct proto quic_prot;
+extern struct proto quicv6_prot;
+
+enum quic_state {
+ QUIC_SS_CLOSED = TCP_CLOSE,
+ QUIC_SS_LISTENING = TCP_LISTEN,
+ QUIC_SS_ESTABLISHING = TCP_SYN_RECV,
+ QUIC_SS_ESTABLISHED = TCP_ESTABLISHED,
+};
+
+enum quic_tsq_enum {
+ QUIC_MTU_REDUCED_DEFERRED,
+ QUIC_LOSS_DEFERRED,
+ QUIC_SACK_DEFERRED,
+ QUIC_PATH_DEFERRED,
+ QUIC_PMTU_DEFERRED,
+ QUIC_PACE_DEFERRED,
+};
+
+enum quic_tsq_flags {
+ QUIC_F_MTU_REDUCED_DEFERRED = BIT(QUIC_MTU_REDUCED_DEFERRED),
+ QUIC_F_LOSS_DEFERRED = BIT(QUIC_LOSS_DEFERRED),
+ QUIC_F_SACK_DEFERRED = BIT(QUIC_SACK_DEFERRED),
+ QUIC_F_PATH_DEFERRED = BIT(QUIC_PATH_DEFERRED),
+ QUIC_F_PMTU_DEFERRED = BIT(QUIC_PMTU_DEFERRED),
+ QUIC_F_PACE_DEFERRED = BIT(QUIC_PACE_DEFERRED),
+};
+
+#define QUIC_DEFERRED_ALL (QUIC_F_MTU_REDUCED_DEFERRED | \
+ QUIC_F_LOSS_DEFERRED | \
+ QUIC_F_SACK_DEFERRED | \
+ QUIC_F_PATH_DEFERRED | \
+ QUIC_F_PMTU_DEFERRED | \
+ QUIC_F_PACE_DEFERRED)
+
+struct quic_request_sock {
+ struct list_head list;
+
+ struct quic_conn_id dcid;
+ struct quic_conn_id scid;
+ union quic_addr daddr;
+ union quic_addr saddr;
+
+ struct quic_conn_id orig_dcid;
+ u32 version;
+ u8 retry;
+
+ struct sk_buff_head backlog_list;
+ u32 blen;
+};
+
+struct quic_sock {
+ struct inet_sock inet;
+ struct list_head reqs;
+ struct work_struct work;
+
+ struct quic_data ticket;
+ struct quic_data token;
+ struct quic_data alpn;
+
+ struct quic_stream_table streams;
+ struct quic_conn_id_set source;
+ struct quic_conn_id_set dest;
+ struct quic_path_group paths;
+ struct quic_cong cong;
+ struct quic_pnspace space[QUIC_PNSPACE_MAX];
+ struct quic_crypto crypto[QUIC_CRYPTO_MAX];
+
+ struct quic_packet packet;
+ struct quic_timer timers[QUIC_TIMER_MAX];
+};
+
+struct quic6_sock {
+ struct quic_sock quic;
+ struct ipv6_pinfo inet6;
+};
+
+static inline struct quic_sock *quic_sk(const struct sock *sk)
+{
+ return (struct quic_sock *)sk;
+}
+
+static inline struct list_head *quic_reqs(const struct sock *sk)
+{
+ return &quic_sk(sk)->reqs;
+}
+
+static inline struct work_struct *quic_work(const struct sock *sk)
+{
+ return &quic_sk(sk)->work;
+}
+
+static inline struct quic_data *quic_token(const struct sock *sk)
+{
+ return &quic_sk(sk)->token;
+}
+
+static inline struct quic_data *quic_ticket(const struct sock *sk)
+{
+ return &quic_sk(sk)->ticket;
+}
+
+static inline struct quic_data *quic_alpn(const struct sock *sk)
+{
+ return &quic_sk(sk)->alpn;
+}
+
+static inline struct quic_stream_table *quic_streams(const struct sock *sk)
+{
+ return &quic_sk(sk)->streams;
+}
+
+static inline struct quic_conn_id_set *quic_source(const struct sock *sk)
+{
+ return &quic_sk(sk)->source;
+}
+
+static inline struct quic_conn_id_set *quic_dest(const struct sock *sk)
+{
+ return &quic_sk(sk)->dest;
+}
+
+static inline struct quic_path_group *quic_paths(const struct sock *sk)
+{
+ return &quic_sk(sk)->paths;
+}
+
+static inline bool quic_is_serv(const struct sock *sk)
+{
+ return !!sk->sk_max_ack_backlog;
+}
+
+static inline struct quic_cong *quic_cong(const struct sock *sk)
+{
+ return &quic_sk(sk)->cong;
+}
+
+static inline struct quic_pnspace *quic_pnspace(const struct sock *sk, u8 level)
+{
+ return &quic_sk(sk)->space[level % QUIC_CRYPTO_EARLY];
+}
+
+static inline struct quic_crypto *quic_crypto(const struct sock *sk, u8 level)
+{
+ return &quic_sk(sk)->crypto[level];
+}
+
+static inline struct quic_packet *quic_packet(const struct sock *sk)
+{
+ return &quic_sk(sk)->packet;
+}
+
+static inline void *quic_timer(const struct sock *sk, u8 type)
+{
+ return (void *)&quic_sk(sk)->timers[type];
+}
+
+static inline bool quic_is_establishing(struct sock *sk)
+{
+ return sk->sk_state == QUIC_SS_ESTABLISHING;
+}
+
+static inline bool quic_is_established(struct sock *sk)
+{
+ return sk->sk_state == QUIC_SS_ESTABLISHED;
+}
+
+static inline bool quic_is_listen(struct sock *sk)
+{
+ return sk->sk_state == QUIC_SS_LISTENING;
+}
+
+static inline bool quic_is_closed(struct sock *sk)
+{
+ return sk->sk_state == QUIC_SS_CLOSED;
+}
+
+static inline void quic_set_state(struct sock *sk, int state)
+{
+ struct net *net = sock_net(sk);
+ int mib;
+
+ if (sk->sk_state == state)
+ return;
+
+ if (state == QUIC_SS_ESTABLISHED) {
+ mib = quic_is_serv(sk) ? QUIC_MIB_CONN_PASSIVEESTABS :
+ QUIC_MIB_CONN_ACTIVEESTABS;
+ QUIC_INC_STATS(net, mib);
+ QUIC_INC_STATS(net, QUIC_MIB_CONN_CURRENTESTABS);
+ } else if (quic_is_established(sk)) {
+ QUIC_DEC_STATS(net, QUIC_MIB_CONN_CURRENTESTABS);
+ }
+
+ inet_sk_set_state(sk, state);
+ sk->sk_state_change(sk);
+}
+
+struct sock *quic_listen_sock_lookup(struct sk_buff *skb, union quic_addr *sa,
+ union quic_addr *da, struct sock *usk,
+ struct quic_data *alpns);
+struct sock *quic_sock_lookup(struct sk_buff *skb, union quic_addr *sa,
+ union quic_addr *da, struct sock *usk,
+ struct quic_conn_id *dcid);
+
+bool quic_listen_sock_switch(struct sk_buff *skb, struct quic_data *alpns);
+bool quic_accept_sock_exists(struct sock *sk, struct sk_buff *skb);
+
+struct quic_request_sock *quic_request_sock_create(struct sock *sk,
+ struct quic_conn_id *odcid,
+ u8 retry, gfp_t gfp);
+int quic_request_sock_backlog_tail(struct sock *sk,
+ struct quic_request_sock *req,
+ struct sk_buff *skb);
+struct quic_request_sock *quic_request_sock_lookup(struct sock *sk);
diff --git a/net/quic/stream.c b/net/quic/stream.c
new file mode 100644
index 0000000000000..6b34b117103a8
--- /dev/null
+++ b/net/quic/stream.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include <linux/quic.h>
+
+#include "common.h"
+#include "stream.h"
+
+/* Check if a stream ID is valid for sending or receiving. */
+static bool quic_stream_id_valid(s64 stream_id, bool is_serv, bool send)
+{
+ u8 type = (stream_id & QUIC_STREAM_TYPE_MASK);
+
+ if (send) {
+ if (is_serv)
+ return type != QUIC_STREAM_TYPE_CLIENT_UNI;
+ return type != QUIC_STREAM_TYPE_SERVER_UNI;
+ }
+ if (is_serv)
+ return type != QUIC_STREAM_TYPE_SERVER_UNI;
+ return type != QUIC_STREAM_TYPE_CLIENT_UNI;
+}
+
+/* Check if a stream ID was initiated locally. */
+static bool quic_stream_id_local(s64 stream_id, bool is_serv)
+{
+ return is_serv ^ !(stream_id & QUIC_STREAM_TYPE_SERVER_MASK);
+}
+
+/* Check if a stream ID represents a unidirectional stream. */
+static bool quic_stream_id_uni(s64 stream_id)
+{
+ return stream_id & QUIC_STREAM_TYPE_UNI_MASK;
+}
+
+#define QUIC_STREAM_HT_SIZE 64
+
+static struct hlist_head *quic_stream_head(struct quic_stream_table *streams,
+ s64 stream_id)
+{
+ /* Skip the SERVER initiator bit, which is constant per endpoint. */
+ return &streams->head[(stream_id >> 1) & (QUIC_STREAM_HT_SIZE - 1)];
+}
+
+struct quic_stream *quic_stream_find(struct quic_stream_table *streams,
+ s64 stream_id)
+{
+ struct hlist_head *head = quic_stream_head(streams, stream_id);
+ struct quic_stream *stream;
+
+ hlist_for_each_entry(stream, head, node) {
+ if (stream->id == stream_id)
+ break;
+ }
+ return stream;
+}
+
+static void quic_stream_add(struct quic_stream_table *streams,
+ struct quic_stream *stream)
+{
+ struct hlist_head *head;
+
+ head = quic_stream_head(streams, stream->id);
+ hlist_add_head(&stream->node, head);
+}
+
+static void quic_stream_delete(struct quic_stream *stream)
+{
+ hlist_del_init(&stream->node);
+ kfree(stream);
+}
+
+/* Create and register new streams for sending or receiving. */
+static struct quic_stream *quic_stream_create(struct quic_stream_table *streams,
+ s64 max_stream_id, bool send,
+ bool is_serv, gfp_t gfp)
+{
+ struct quic_stream *pos, *stream = NULL;
+ struct quic_stream_limits *limits;
+ struct hlist_node *tmp;
+ HLIST_HEAD(head);
+ s64 stream_id;
+ u32 count = 0;
+
+ gfp |= __GFP_ACCOUNT;
+ limits = send ? &streams->send : &streams->recv;
+ stream_id = limits->next_bidi_stream_id;
+ if (quic_stream_id_uni(max_stream_id))
+ stream_id = limits->next_uni_stream_id;
+
+ /* rfc9000#section-2.1: A stream ID that is used out of order results in
+ * all streams of that type with lower-numbered stream IDs also being
+ * opened.
+ */
+ while (stream_id <= max_stream_id) {
+ stream = kzalloc_obj(*stream, gfp);
+ if (!stream)
+ goto free;
+
+ stream->id = stream_id;
+ if (quic_stream_id_uni(stream_id)) {
+ if (send) {
+ stream->send.max_bytes =
+ limits->max_stream_data_uni;
+ } else {
+ stream->recv.max_bytes =
+ limits->max_stream_data_uni;
+ stream->recv.window = stream->recv.max_bytes;
+ }
+ hlist_add_head(&stream->node, &head);
+ stream_id += QUIC_STREAM_ID_STEP;
+ continue;
+ }
+
+ if (quic_stream_id_local(stream_id, is_serv)) {
+ stream->send.max_bytes =
+ streams->send.max_stream_data_bidi_remote;
+ stream->recv.max_bytes =
+ streams->recv.max_stream_data_bidi_local;
+ } else {
+ stream->send.max_bytes =
+ streams->send.max_stream_data_bidi_local;
+ stream->recv.max_bytes =
+ streams->recv.max_stream_data_bidi_remote;
+ }
+ stream->recv.window = stream->recv.max_bytes;
+ hlist_add_head(&stream->node, &head);
+ stream_id += QUIC_STREAM_ID_STEP;
+ }
+
+ hlist_for_each_entry_safe(pos, tmp, &head, node) {
+ hlist_del_init(&pos->node);
+ quic_stream_add(streams, pos);
+ count++;
+ }
+
+ /* Streams must be opened sequentially. Update the next stream ID so the
+ * correct starting point is known if an out-of-order open is requested.
+ * Note overflow of next_uni/bidi_stream_id is impossible with s64.
+ */
+ if (quic_stream_id_uni(stream_id)) {
+ limits->next_uni_stream_id = stream_id;
+ limits->streams_uni += count;
+ return stream;
+ }
+
+ limits->next_bidi_stream_id = stream_id;
+ limits->streams_bidi += count;
+ return stream;
+
+free:
+ hlist_for_each_entry_safe(pos, tmp, &head, node) {
+ hlist_del_init(&pos->node);
+ kfree(pos);
+ }
+ return NULL;
+}
+
+/* Check if a send or receive stream ID is already closed. */
+static bool quic_stream_id_closed(struct quic_stream_table *streams,
+ s64 stream_id, bool send)
+{
+ struct quic_stream_limits *limits = send ? &streams->send :
+ &streams->recv;
+
+ if (quic_stream_id_uni(stream_id))
+ return stream_id < limits->next_uni_stream_id;
+ return stream_id < limits->next_bidi_stream_id;
+}
+
+/* Check if a stream ID would exceed local (recv) or peer (send) limits. */
+bool quic_stream_id_exceeds(struct quic_stream_table *streams, s64 stream_id,
+ bool send)
+{
+ u64 nstreams;
+
+ if (!send) {
+ /* recv.max_uni/bidi_stream_id is updated in
+ * quic_stream_max_streams_update() already based on
+ * next_uni/bidi_stream_id, max_streams_uni/bidi, and
+ * streams_uni/bidi, so only recv.max_uni/bidi_stream_id needs
+ * to be checked.
+ */
+ if (quic_stream_id_uni(stream_id))
+ return stream_id > streams->recv.max_uni_stream_id;
+
+ return stream_id > streams->recv.max_bidi_stream_id;
+ }
+
+ if (quic_stream_id_uni(stream_id)) {
+ if (stream_id > streams->send.max_uni_stream_id)
+ return true;
+ stream_id -= streams->send.next_uni_stream_id;
+ nstreams = quic_stream_id_to_streams(stream_id);
+
+ return nstreams + streams->send.streams_uni >
+ streams->send.max_streams_uni;
+ }
+
+ if (stream_id > streams->send.max_bidi_stream_id)
+ return true;
+ stream_id -= streams->send.next_bidi_stream_id;
+ nstreams = quic_stream_id_to_streams(stream_id);
+
+ return nstreams + streams->send.streams_bidi >
+ streams->send.max_streams_bidi;
+}
+
+/* Get or create a send or recv stream by ID. Requires sock lock held. */
+struct quic_stream *quic_stream_get(struct quic_stream_table *streams,
+ s64 stream_id, u32 flags, bool is_serv,
+ bool send, gfp_t gfp)
+{
+ struct quic_stream *stream;
+
+ if (!quic_stream_id_valid(stream_id, is_serv, send))
+ return ERR_PTR(-EINVAL);
+
+ stream = quic_stream_find(streams, stream_id);
+ if (stream)
+ return stream;
+
+ if (!send && quic_stream_id_local(stream_id, is_serv)) {
+ if (quic_stream_id_closed(streams, stream_id, !send))
+ return ERR_PTR(-ENOSTR);
+ return ERR_PTR(-EINVAL);
+ }
+ if (send && !quic_stream_id_local(stream_id, is_serv))
+ return ERR_PTR(-EINVAL);
+
+ if (quic_stream_id_closed(streams, stream_id, send))
+ return ERR_PTR(-ENOSTR);
+
+ if (!(flags & MSG_QUIC_STREAM_NEW))
+ return ERR_PTR(-EINVAL);
+
+ if (quic_stream_id_exceeds(streams, stream_id, send))
+ return ERR_PTR(-EAGAIN);
+
+ stream = quic_stream_create(streams, stream_id, send, is_serv, gfp);
+ if (!stream)
+ return ERR_PTR(-ENOMEM);
+
+ if (send || quic_stream_id_valid(stream_id, is_serv, !send))
+ streams->send.active_stream_id = stream_id;
+
+ return stream;
+}
+
+/* Release or clean up a send or recv stream. This function updates stream
+ * counters and state when a send stream has either successfully sent all data
+ * or has been reset, or when a recv stream has either received all data or has
+ * been reset. Requires sock lock held.
+ */
+void quic_stream_put(struct quic_stream_table *streams,
+ struct quic_stream *stream, bool is_serv, bool send)
+{
+ if (quic_stream_id_uni(stream->id)) {
+ /* For uni streams, decrement uni count and delete stream. */
+ if (send) {
+ streams->send.streams_uni--;
+ quic_stream_delete(stream);
+ return;
+ }
+ streams->recv.streams_uni--;
+ streams->recv.uni_pending = 1;
+ quic_stream_delete(stream);
+ return;
+ }
+
+ /* For bidi streams, proceed only if both send and receive in a final
+ * state.
+ */
+ if (send) {
+ if (stream->recv.state < QUIC_STREAM_RECV_STATE_RECVD)
+ return;
+ } else {
+ if (stream->send.state != QUIC_STREAM_SEND_STATE_RECVD &&
+ stream->send.state != QUIC_STREAM_SEND_STATE_RESET_RECVD)
+ return;
+ }
+ if (quic_stream_id_local(stream->id, is_serv)) {
+ /* Local-initiated stream: decrement send.bidi count. */
+ streams->send.streams_bidi--;
+ } else {
+ /* Remote-initiated stream: decrement recv.bidi count. */
+ streams->recv.streams_bidi--;
+ streams->recv.bidi_pending = 1;
+ }
+ quic_stream_delete(stream);
+}
+
+/* Updates the maximum allowed incoming stream IDs if any streams were recently
+ * closed. Recalculates the max_uni and max_bidi stream ID limits based on the
+ * number of open streams and whether any were marked for deletion.
+ *
+ * Returns true if either max_uni or max_bidi was updated, indicating that a
+ * MAX_STREAMS_UNI or MAX_STREAMS_BIDI frame should be sent to the peer.
+ */
+bool quic_stream_max_streams_update(struct quic_stream_table *streams,
+ s64 *max_uni, s64 *max_bidi)
+{
+ s64 max, rem;
+
+ *max_uni = 0;
+ *max_bidi = 0;
+ if (streams->recv.uni_pending) {
+ rem = streams->recv.max_streams_uni - streams->recv.streams_uni;
+ max = streams->recv.next_uni_stream_id - QUIC_STREAM_ID_STEP +
+ (rem << QUIC_STREAM_TYPE_BITS);
+
+ streams->recv.max_uni_stream_id = max;
+ *max_uni = quic_stream_id_to_streams(max);
+ streams->recv.uni_pending = 0;
+ }
+ if (streams->recv.bidi_pending) {
+ rem = streams->recv.max_streams_bidi -
+ streams->recv.streams_bidi;
+ max = streams->recv.next_bidi_stream_id - QUIC_STREAM_ID_STEP +
+ (rem << QUIC_STREAM_TYPE_BITS);
+
+ streams->recv.max_bidi_stream_id = max;
+ *max_bidi = quic_stream_id_to_streams(max);
+ streams->recv.bidi_pending = 0;
+ }
+
+ return *max_uni || *max_bidi;
+}
+
+int quic_stream_init(struct quic_stream_table *streams)
+{
+ struct hlist_head *head;
+ int i;
+
+ head = kmalloc_objs(*head, QUIC_STREAM_HT_SIZE);
+ if (!head)
+ return -ENOMEM;
+ for (i = 0; i < QUIC_STREAM_HT_SIZE; i++)
+ INIT_HLIST_HEAD(&head[i]);
+ streams->head = head;
+ return 0;
+}
+
+void quic_stream_free(struct quic_stream_table *streams)
+{
+ struct quic_stream *stream;
+ struct hlist_head *head;
+ struct hlist_node *tmp;
+ int i;
+
+ if (!streams->head)
+ return;
+
+ for (i = 0; i < QUIC_STREAM_HT_SIZE; i++) {
+ head = &streams->head[i];
+ hlist_for_each_entry_safe(stream, tmp, head, node)
+ quic_stream_delete(stream);
+ }
+ kfree(streams->head);
+ streams->head = NULL;
+}
+
+/* Populate transport parameters from stream hash table. */
+void quic_stream_get_param(struct quic_stream_table *streams,
+ struct quic_transport_param *p)
+{
+ struct quic_stream_limits *limits = p->remote ? &streams->send :
+ &streams->recv;
+
+ p->max_stream_data_bidi_remote = limits->max_stream_data_bidi_remote;
+ p->max_stream_data_bidi_local = limits->max_stream_data_bidi_local;
+ p->max_stream_data_uni = limits->max_stream_data_uni;
+ p->max_streams_bidi = limits->max_streams_bidi;
+ p->max_streams_uni = limits->max_streams_uni;
+}
+
+/* Configure stream hashtable from transport parameters. */
+void quic_stream_set_param(struct quic_stream_table *streams,
+ struct quic_transport_param *p, bool is_serv)
+{
+ struct quic_stream_limits *limits = p->remote ? &streams->send :
+ &streams->recv;
+ u8 bidi_type, uni_type;
+
+ limits->max_stream_data_bidi_local = p->max_stream_data_bidi_local;
+ limits->max_stream_data_bidi_remote = p->max_stream_data_bidi_remote;
+ limits->max_stream_data_uni = p->max_stream_data_uni;
+ limits->max_streams_bidi = p->max_streams_bidi;
+ limits->max_streams_uni = p->max_streams_uni;
+ limits->active_stream_id = -1;
+
+ if (p->remote ^ is_serv) {
+ bidi_type = QUIC_STREAM_TYPE_CLIENT_BIDI;
+ uni_type = QUIC_STREAM_TYPE_CLIENT_UNI;
+ } else {
+ bidi_type = QUIC_STREAM_TYPE_SERVER_BIDI;
+ uni_type = QUIC_STREAM_TYPE_SERVER_UNI;
+ }
+
+ limits->max_bidi_stream_id =
+ quic_stream_streams_to_id(p->max_streams_bidi, bidi_type);
+ limits->next_bidi_stream_id = bidi_type;
+
+ limits->max_uni_stream_id =
+ quic_stream_streams_to_id(p->max_streams_uni, uni_type);
+ limits->next_uni_stream_id = uni_type;
+}
diff --git a/net/quic/stream.h b/net/quic/stream.h
new file mode 100644
index 0000000000000..d915712f3d5fb
--- /dev/null
+++ b/net/quic/stream.h
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#define QUIC_DEF_STREAMS 100
+#define QUIC_MAX_STREAMS 4096ULL
+
+/*
+ * rfc9000#section-2.1:
+ *
+ * The least significant bit (0x01) of the stream ID identifies the initiator
+ * of the stream. Client-initiated streams have even-numbered stream IDs
+ * (with the bit set to 0), and server-initiated streams have odd-numbered
+ * stream IDs (with the bit set to 1).
+ *
+ * The second least significant bit (0x02) of the stream ID distinguishes
+ * between bidirectional streams (with the bit set to 0) and unidirectional
+ * streams (with the bit set to 1).
+ */
+#define QUIC_STREAM_TYPE_BITS 2
+#define QUIC_STREAM_ID_STEP BIT(QUIC_STREAM_TYPE_BITS)
+
+#define QUIC_STREAM_TYPE_CLIENT_BIDI 0x00
+#define QUIC_STREAM_TYPE_SERVER_BIDI 0x01
+#define QUIC_STREAM_TYPE_CLIENT_UNI 0x02
+#define QUIC_STREAM_TYPE_SERVER_UNI 0x03
+
+struct quic_stream {
+ struct hlist_node node;
+ s64 id; /* Stream ID as defined in RFC 9000 Section 2.1 */
+ struct {
+ /* Sending-side stream level flow control */
+ u64 last_max_bytes; /* Max send offset advertised by peer */
+ u64 max_bytes; /* Max offset allowed to send */
+ u64 bytes; /* Bytes already sent to peer */
+
+ u32 errcode; /* App error code for RESET_STREAM */
+ u32 frags; /* STREAM frames sent but not yet acked */
+ u8 state; /* Send stream state, per rfc9000#section-3.1 */
+
+ u8 data_blocked; /* True if flow control blocks sending */
+ } send;
+ struct {
+ /* Receiving-side stream level flow control */
+ u64 max_bytes; /* Max offset peer can send */
+ u64 window; /* Remaining receive window */
+ u64 bytes; /* Bytes consumed by app */
+
+ u64 highest; /* Highest received offset */
+ u64 offset; /* Data buffered or consumed */
+ u64 finalsz; /* Final stream size if FIN received */
+
+ u32 frags; /* STREAM frames pending reassembly */
+ u8 state; /* Receive stream state, per rfc9000#section-3.2 */
+
+ u8 stop_sent; /* True if STOP_SENDING has been sent */
+ } recv;
+};
+
+struct quic_stream_limits {
+ /* Stream limit parameters defined in rfc9000#section-18.2:
+ *
+ * - initial_max_stream_data_bidi_remote
+ * - initial_max_stream_data_bidi_local
+ * - initial_max_stream_data_uni
+ * - initial_max_streams_bidi
+ * - initial_max_streams_uni
+ */
+ u64 max_stream_data_bidi_remote;
+ u64 max_stream_data_bidi_local;
+ u64 max_stream_data_uni;
+ u64 max_streams_bidi;
+ u64 max_streams_uni;
+
+ s64 next_bidi_stream_id; /* Next bidi stream ID to open or accept */
+ s64 next_uni_stream_id; /* Next uni stream ID to open or accept */
+ s64 max_bidi_stream_id; /* Highest allowed bidi stream ID */
+ s64 max_uni_stream_id; /* Highest allowed uni stream ID */
+ s64 active_stream_id; /* Most recently opened stream ID */
+
+ u8 bidi_blocked; /* STREAMS_BLOCKED_BIDI sent, awaiting ACK */
+ u8 uni_blocked; /* STREAMS_BLOCKED_UNI sent, awaiting ACK */
+ u8 bidi_pending; /* MAX_STREAMS_BIDI needs to be sent */
+ u8 uni_pending; /* MAX_STREAMS_UNI needs to be sent */
+
+ u16 streams_bidi; /* Number of open bidi streams */
+ u16 streams_uni; /* Number of open uni streams */
+};
+
+struct quic_stream_table {
+ struct hlist_head *head; /* Hash table storing all active streams */
+
+ struct quic_stream_limits send; /* Limits advertised by peer */
+ struct quic_stream_limits recv; /* Limits we advertise to peer */
+};
+
+static inline u64 quic_stream_id_to_streams(s64 stream_id)
+{
+ return (u64)(stream_id >> QUIC_STREAM_TYPE_BITS) + 1;
+}
+
+static inline s64 quic_stream_streams_to_id(u64 streams, u8 type)
+{
+ if (!streams)
+ return -1;
+ return (s64)((streams - 1) << QUIC_STREAM_TYPE_BITS) | type;
+}
+
+struct quic_stream *quic_stream_get(struct quic_stream_table *streams,
+ s64 stream_id, u32 flags, bool is_serv,
+ bool send, gfp_t gfp);
+void quic_stream_put(struct quic_stream_table *streams,
+ struct quic_stream *stream, bool is_serv, bool send);
+
+bool quic_stream_max_streams_update(struct quic_stream_table *streams,
+ s64 *max_uni, s64 *max_bidi);
+bool quic_stream_id_exceeds(struct quic_stream_table *streams,
+ s64 stream_id, bool send);
+struct quic_stream *quic_stream_find(struct quic_stream_table *streams,
+ s64 stream_id);
+
+void quic_stream_get_param(struct quic_stream_table *streams,
+ struct quic_transport_param *p);
+void quic_stream_set_param(struct quic_stream_table *streams,
+ struct quic_transport_param *p, bool is_serv);
+void quic_stream_free(struct quic_stream_table *streams);
+int quic_stream_init(struct quic_stream_table *streams);
diff --git a/net/quic/timer.c b/net/quic/timer.c
new file mode 100644
index 0000000000000..0dd6d6580bbd9
--- /dev/null
+++ b/net/quic/timer.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Initialization/cleanup for QUIC protocol support.
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+#include "socket.h"
+
+static void quic_timer_timeout(struct quic_timer *t, int type, int defer_bit,
+ void (*handler)(struct sock *sk))
+{
+ struct quic_sock *qs = container_of(t, struct quic_sock, timers[type]);
+ struct sock *sk = &qs->inet.sk;
+
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ if (!test_and_set_bit(defer_bit, &sk->sk_tsq_flags))
+ sock_hold(sk);
+ goto out;
+ }
+
+ handler(sk);
+out:
+ bh_unlock_sock(sk);
+ sock_put(sk);
+}
+
+void quic_timer_sack_handler(struct sock *sk)
+{
+}
+
+static void quic_timer_sack_timeout(struct timer_list *t)
+{
+ quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_SACK,
+ QUIC_SACK_DEFERRED, quic_timer_sack_handler);
+}
+
+void quic_timer_loss_handler(struct sock *sk)
+{
+}
+
+static void quic_timer_loss_timeout(struct timer_list *t)
+{
+ quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_LOSS,
+ QUIC_LOSS_DEFERRED, quic_timer_loss_handler);
+}
+
+void quic_timer_path_handler(struct sock *sk)
+{
+}
+
+static void quic_timer_path_timeout(struct timer_list *t)
+{
+ quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PATH,
+ QUIC_PATH_DEFERRED, quic_timer_path_handler);
+}
+
+void quic_timer_pmtu_handler(struct sock *sk)
+{
+}
+
+static void quic_timer_pmtu_timeout(struct timer_list *t)
+{
+ quic_timer_timeout((struct quic_timer *)t, QUIC_TIMER_PMTU,
+ QUIC_PMTU_DEFERRED, quic_timer_pmtu_handler);
+}
+
+void quic_timer_pace_handler(struct sock *sk)
+{
+}
+
+static enum hrtimer_restart quic_timer_pace_timeout(struct hrtimer *hr)
+{
+ quic_timer_timeout((struct quic_timer *)hr, QUIC_TIMER_PACE,
+ QUIC_PACE_DEFERRED, quic_timer_pace_handler);
+ return HRTIMER_NORESTART;
+}
+
+void quic_timer_reset(struct sock *sk, u8 type, u64 timeout)
+{
+ struct timer_list *t = quic_timer(sk, type);
+
+ /* Note that type must never be QUIC_TIMER_PACE for this helper. */
+ if (WARN_ON_ONCE(type == QUIC_TIMER_PACE))
+ return;
+ if (timeout && !mod_timer(t, jiffies + usecs_to_jiffies(timeout)))
+ sock_hold(sk);
+}
+
+void quic_timer_start(struct sock *sk, u8 type, u64 timeout)
+{
+ struct timer_list *t;
+ struct hrtimer *hr;
+
+ if (type == QUIC_TIMER_PACE) {
+ hr = quic_timer(sk, type);
+
+ if (!hrtimer_is_queued(hr)) {
+ hrtimer_start(hr, ns_to_ktime(timeout),
+ HRTIMER_MODE_ABS_PINNED_SOFT);
+ sock_hold(sk);
+ }
+ return;
+ }
+
+ t = quic_timer(sk, type);
+ if (timeout && !timer_pending(t)) {
+ if (!mod_timer(t, jiffies + usecs_to_jiffies(timeout)))
+ sock_hold(sk);
+ }
+}
+
+void quic_timer_stop(struct sock *sk, u8 type)
+{
+ if (type == QUIC_TIMER_PACE) {
+ if (hrtimer_try_to_cancel(quic_timer(sk, type)) == 1)
+ sock_put(sk);
+ return;
+ }
+ if (timer_delete(quic_timer(sk, type)))
+ sock_put(sk);
+}
+
+void quic_timer_init(struct sock *sk)
+{
+ timer_setup(quic_timer(sk, QUIC_TIMER_LOSS), quic_timer_loss_timeout,
+ 0);
+ timer_setup(quic_timer(sk, QUIC_TIMER_SACK), quic_timer_sack_timeout,
+ 0);
+ timer_setup(quic_timer(sk, QUIC_TIMER_PATH), quic_timer_path_timeout,
+ 0);
+ timer_setup(quic_timer(sk, QUIC_TIMER_PMTU), quic_timer_pmtu_timeout,
+ 0);
+ /* Use hrtimer for pace timer, ensuring precise control over send
+ * timing.
+ */
+ hrtimer_setup(quic_timer(sk, QUIC_TIMER_PACE), quic_timer_pace_timeout,
+ CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED_SOFT);
+}
+
+void quic_timer_free(struct sock *sk)
+{
+ quic_timer_stop(sk, QUIC_TIMER_LOSS);
+ quic_timer_stop(sk, QUIC_TIMER_SACK);
+ quic_timer_stop(sk, QUIC_TIMER_PATH);
+ quic_timer_stop(sk, QUIC_TIMER_PMTU);
+ quic_timer_stop(sk, QUIC_TIMER_PACE);
+}
diff --git a/net/quic/timer.h b/net/quic/timer.h
new file mode 100644
index 0000000000000..4f63660376027
--- /dev/null
+++ b/net/quic/timer.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* QUIC kernel implementation
+ * (C) Copyright Red Hat Corp. 2023
+ *
+ * This file is part of the QUIC kernel implementation
+ *
+ * Written or modified by:
+ * Xin Long <lucien.xin@gmail.com>
+ */
+
+enum {
+ QUIC_TIMER_LOSS, /* Loss detection timer: retransmit on packet loss */
+ QUIC_TIMER_SACK, /* ACK delay timer, also used as idle timer alias */
+ QUIC_TIMER_PATH, /* Path validation timer: verifies path connectivity */
+ QUIC_TIMER_PMTU, /* PLPMTUD probing timer */
+ QUIC_TIMER_PACE, /* Pacing timer: controls packet transmission pacing */
+ QUIC_TIMER_MAX,
+ QUIC_TIMER_IDLE = QUIC_TIMER_SACK,
+};
+
+struct quic_timer {
+ union {
+ struct timer_list t;
+ struct hrtimer hr;
+ };
+};
+
+#define QUIC_MIN_PROBE_TIMEOUT 5000000
+
+#define QUIC_MIN_PATH_TIMEOUT 1500000
+
+#define QUIC_MIN_IDLE_TIMEOUT 1000000
+#define QUIC_DEF_IDLE_TIMEOUT 30000000
+
+void quic_timer_reset(struct sock *sk, u8 type, u64 timeout);
+void quic_timer_start(struct sock *sk, u8 type, u64 timeout);
+void quic_timer_stop(struct sock *sk, u8 type);
+void quic_timer_init(struct sock *sk);
+void quic_timer_free(struct sock *sk);
+
+void quic_timer_loss_handler(struct sock *sk);
+void quic_timer_pace_handler(struct sock *sk);
+void quic_timer_path_handler(struct sock *sk);
+void quic_timer_sack_handler(struct sock *sk);
+void quic_timer_pmtu_handler(struct sock *sk);
diff --git a/tools/include/uapi/linux/in.h b/tools/include/uapi/linux/in.h
index ced0fc3c3aa53..e4072152f2e62 100644
--- a/tools/include/uapi/linux/in.h
+++ b/tools/include/uapi/linux/in.h
@@ -85,6 +85,8 @@ enum {
#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_SMC = 256, /* Shared Memory Communications */
#define IPPROTO_SMC IPPROTO_SMC
+ IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed Secure Transport */
+#define IPPROTO_QUIC IPPROTO_QUIC
IPPROTO_MPTCP = 262, /* Multipath TCP connection */
#define IPPROTO_MPTCP IPPROTO_MPTCP
IPPROTO_MAX
diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h
index 2a8d7b14f1d11..9d6ec1b2c50ba 100644
--- a/tools/perf/trace/beauty/include/linux/socket.h
+++ b/tools/perf/trace/beauty/include/linux/socket.h
@@ -400,6 +400,7 @@ struct ucred {
#define SOL_MCTP 285
#define SOL_SMC 286
#define SOL_VSOCK 287
+#define SOL_QUIC 288
/* IPX options */
#define IPX_TYPE 1
diff --git a/usr/include/Makefile b/usr/include/Makefile
index ee69dd9d970e5..8b4133d38da2d 100644
--- a/usr/include/Makefile
+++ b/usr/include/Makefile
@@ -31,6 +31,7 @@ no-header-test += linux/omap3isp.h
no-header-test += linux/omapfb.h
no-header-test += linux/patchkey.h
no-header-test += linux/phonet.h
+no-header-test += linux/quic.h
no-header-test += linux/sctp.h
no-header-test += linux/sysctl.h
no-header-test += linux/usb/audio.h
|
| 27/3 | 2026/09/14 17:52 | llm | kmsan-evaluator |
2mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 108647 output: 33 thoughts: 1285 Thoughts: **KMSAN Assessment for QUIC Protocol Implementation** Alright, let's dive into this patch introducing QUIC (IPPROTO_QUIC) to the kernel. My initial assessment hinges on whether KMSAN is warranted, and the rules are clear: KMSAN is a significant resource commitment, only justified when standard bug detectors (KASAN, LOCKDEP, etc.) can't catch uninitialized memory risks. We need to be meticulous. I've got the diff open, and I'm methodically going through the new `net/quic/` directory. It's comprehensive, touching all the core networking aspects: common functionality, congestion control, connection IDs, crypto, family-specific code, packet handling, path management, PN spaces, the protocol itself, socket operations, stream management, timers, and the essential UAPI and kernel-space header files. My first thought is, are there any immediate red flags? Socket options, recvmsg, getsockopt, copy_to_user – those are the usual suspects for uninitialized memory exposure. I immediately scan `socket.c` and notice that basic operations like `setsockopt`, `getsockopt`, `sendmsg`, `recvmsg`, `accept`, `bind`, and `connect` are all returning `-EOPNOTSUPP`. That's a good initial sign, meaning they're not yet implemented and thus unlikely to have those vulnerabilities. Now, my radar is up for `copy_to_user` and related functions. I need to be more thorough and examine every single code path. Then I recall a crucial point from the expert guidance. The `inet_getname` and `inet6_getname` calls within `family.c` could be a risk, since they're involved in retrieving socket addresses and copying that data from the kernel to user space through functions like `__sys_getsockname` and `__sys_getpeername`. Specifically, `quic_v4_get_sk_addr` and `quic_v6_get_sk_addr`. I'm going to inspect these routines very closely. Looks like both use `inet_getname` and `inet6_getname` respectively. The `quic_v6_get_sk_addr` has additional logic for IPv4-mapped IPv6 addresses, zeroing `sin_zero`. That looks standard and correct. I will need to be very sure about the usage of `inet_getname` and `inet6_getname`, and make sure the sizes are correct. To be extra certain, I need to perform a search for `copy_to_user` and `put_user` across the entire `net/quic/` directory, and the wider diff. That will definitively rule out any lurking instances. I will check for these using `grepper`, to be doubly sure that no copy or put operation has been missed. |
| 28/3 | 2026/09/14 17:54 | tool | grepper |
0mError:no matches Args: {
"Expression": "copy_to_user|put_user",
"PathPrefix": "net/quic"
}Results: {
"error": "no matches"
} |
| 29/3 | 2026/09/14 17:54 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 110592 output: 27 thoughts: 47 Thoughts: **Investigating Potential Data Transmission Methods in QUIC** Okay, so I'm looking into how QUIC handles sending data directly to the user space, and it seems like there isn't a dedicated `copy_to_user` or `put_user` function within the `net/quic/` directory. That's not entirely unexpected, but it does mean I need to explore alternative approaches. My immediate thought is to check if `optval` or `getsockopt` are leveraged within `net/quic/` for this purpose. These options can be a very effective way to communicate data or information across the kernel-user boundary, and given QUIC's design, it's quite plausible. I'll focus my investigation on those areas. |
| 30/3 | 2026/09/14 17:54 | tool | grepper |
0mArgs:{
"Expression": "getsockopt",
"PathPrefix": "net/quic"
}Results: {
"Output": "net/quic/family.c=421=int quic_common_setsockopt(struct sock *sk, int level, int optname,\n--\nnet/quic/family.c-428-\nnet/quic/family.c:429:int quic_common_getsockopt(struct sock *sk, int level, int optname,\nnet/quic/family.c-430-\t\t\t char __user *optval, int __user *optlen)\n--\nnet/quic/family.c-432-\treturn quic_pf_ipv4(sk) ?\nnet/quic/family.c:433:\t ip_getsockopt(sk, level, optname, optval, optlen) :\nnet/quic/family.c:434:\t ipv6_getsockopt(sk, level, optname, optval, optlen);\nnet/quic/family.c-435-}\n--\nnet/quic/family.h=39=int quic_common_setsockopt(struct sock *sk, int level, int optname,\nnet/quic/family.h-40-\t\t\t sockptr_t optval, unsigned int optlen);\nnet/quic/family.h:41:int quic_common_getsockopt(struct sock *sk, int level, int optname,\nnet/quic/family.h-42-\t\t\t char __user *optval, int __user *optlen);\n--\nnet/quic/protocol.c=129=static const struct proto_ops quic_proto_ops = {\n--\nnet/quic/protocol.c-143-\t.setsockopt\t = sock_common_setsockopt,\nnet/quic/protocol.c:144:\t.getsockopt\t = sock_common_getsockopt,\nnet/quic/protocol.c-145-\t.sendmsg\t = inet_sendmsg,\n--\nnet/quic/protocol.c=164=static const struct proto_ops quicv6_proto_ops = {\n--\nnet/quic/protocol.c-178-\t.setsockopt\t = sock_common_setsockopt,\nnet/quic/protocol.c:179:\t.getsockopt\t = sock_common_getsockopt,\nnet/quic/protocol.c-180-\t.sendmsg\t = inet_sendmsg,\n--\nnet/quic/socket.c=500=static int quic_setsockopt(struct sock *sk, int level, int optname,\n--\nnet/quic/socket.c-510-/**\nnet/quic/socket.c:511: * quic_do_getsockopt - get a QUIC socket option\nnet/quic/socket.c-512- * @sk: socket to query\n--\nnet/quic/socket.c-522- */\nnet/quic/socket.c:523:int quic_do_getsockopt(struct sock *sk, int optname, sockptr_t optval,\nnet/quic/socket.c-524-\t\t sockptr_t optlen)\n--\nnet/quic/socket.c-527-}\nnet/quic/socket.c:528:EXPORT_SYMBOL_GPL(quic_do_getsockopt);\nnet/quic/socket.c-529-\nnet/quic/socket.c:530:static int quic_getsockopt(struct sock *sk, int level, int optname,\nnet/quic/socket.c-531-\t\t\t char __user *optval, int __user *optlen)\n--\nnet/quic/socket.c-533-\tif (level != SOL_QUIC)\nnet/quic/socket.c:534:\t\treturn quic_common_getsockopt(sk, level, optname, optval,\nnet/quic/socket.c-535-\t\t\t\t\t optlen);\nnet/quic/socket.c-536-\nnet/quic/socket.c:537:\treturn quic_do_getsockopt(sk, optname, USER_SOCKPTR(optval),\nnet/quic/socket.c-538-\t\t\t\t USER_SOCKPTR(optlen));\n--\nnet/quic/socket.c=593=struct proto quic_prot = {\n--\nnet/quic/socket.c-599-\t.setsockopt\t= quic_setsockopt,\nnet/quic/socket.c:600:\t.getsockopt\t= quic_getsockopt,\nnet/quic/socket.c-601-\t.connect\t= quic_connect,\n--\nnet/quic/socket.c=624=struct proto quicv6_prot = {\n--\nnet/quic/socket.c-630-\t.setsockopt\t= quic_setsockopt,\nnet/quic/socket.c:631:\t.getsockopt\t= quic_getsockopt,\nnet/quic/socket.c-632-\t.connect\t= quic_connect,\n"
} |
| 31/3 | 2026/09/14 17:54 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 112124 output: 26 thoughts: 114 Thoughts: **My Analysis of `quic_do_getsockopt` and Potential Memory Issues** Okay, so `quic_do_getsockopt` just flat out returns `-EOPNOTSUPP`. That's not exactly surprising, but I need to make sure there aren't any hidden problems lurking around. Let's dig in a bit deeper. My primary concern is uninitialized memory. I need to know: Is there any scenario where uninitialized memory within the QUIC networking code is being read, used in a conditional statement, or involved in any sort of calculation? That's a classic source of hard-to-debug vulnerabilities, and I need to rule it out. My immediate approach is to check every file within the `net/quic/` directory. I'll need to meticulously review each one, but before I get too far into that, I should streamline this. Let me see... I'll search for `kmalloc` specifically within `net/quic/`. That's it! `kmalloc` will allocate *uninitialized* memory! That's my main target. Right, so the next step is to get a list of all instances of `kmalloc` in the `net/quic/` directory. That's where I need to focus my efforts. |
| 32/3 | 2026/09/14 17:54 | tool | grepper |
0mArgs:{
"Expression": "kmalloc",
"PathPrefix": "net/quic"
}Results: {
"Output": "net/quic/common.c=412=int quic_data_append(struct quic_data *to, u8 *data, u32 len, gfp_t gfp)\n--\nnet/quic/common.c-421-\nnet/quic/common.c:422:\tp = kmalloc(to-\u003elen + len, gfp);\nnet/quic/common.c-423-\tif (!p)\n--\nnet/quic/crypto.c=1138=int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,\n--\nnet/quic/crypto.c-1147-\tlen = sizeof(flag) + addrlen + tslen + conn_id-\u003elen + QUIC_TAG_LEN;\nnet/quic/crypto.c:1148:\ttoken_buf = kmalloc(len, GFP_KERNEL);\nnet/quic/crypto.c-1149-\tif (!token_buf)\n--\nnet/quic/path.c=67=static struct quic_udp_sock *quic_udp_sock_create(struct sock *sk,\n--\nnet/quic/path.c-77-\nnet/quic/path.c:78:\tus = kmalloc_obj(*us);\nnet/quic/path.c-79-\tif (!us)\n--\nnet/quic/socket.c=45=struct quic_request_sock *quic_request_sock_create(struct sock *sk,\n--\nnet/quic/socket.c-54-\nnet/quic/socket.c:55:\treq = kmalloc_obj(*req, gfp);\nnet/quic/socket.c-56-\tif (!req)\n--\nnet/quic/stream.c=339=int quic_stream_init(struct quic_stream_table *streams)\n--\nnet/quic/stream.c-343-\nnet/quic/stream.c:344:\thead = kmalloc_objs(*head, QUIC_STREAM_HT_SIZE);\nnet/quic/stream.c-345-\tif (!head)\n"
} |
| 33/3 | 2026/09/14 17:54 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 113091 output: 32 thoughts: 1399 Thoughts: **Analyzing kmalloc Usage in QUIC Token Generation** Okay, let's break down these `kmalloc` occurrences. First up, `quic_data_append`. Here, the entire allocated buffer is initialized, so that's good. No obvious issues there, just straightforward appending of data. Next, `quic_crypto_generate_token`... hold on a minute! Let me retrace my steps here. The `len` calculation is critical: `len = sizeof(flag) + addrlen + tslen + conn_id->len + QUIC_TAG_LEN;`. Then we allocate a buffer of that calculated `len`, and populate it in chunks: `flag`, `addr`, timestamp, and `conn_id->data`. Then... there are `QUIC_TAG_LEN` bytes at the *end* of the allocated buffer. That stands out! Is that tag used? Now, let's see, what is `quic_crypto_token_protect` doing, because that's the next logical piece. Let's pull up the implementation of `quic_crypto_token_protect`. Aha! I see `aead_request_set_crypt(req, sg, sg, len - adlen - extra, nonce);`. In an AEAD encryption context, the ciphertext and *authentication tag* are written to the `dst` buffer, which is `sg` in this case. The plaintext length is calculated as `len - adlen - extra`. And `extra` is `QUIC_TAG_LEN` for encryption. So, it looks like `quic_crypto_token_protect` *does* append the authentication tag to the buffer after the other data. The tag is appended into the `QUIC_TAG_LEN` bytes at the end of the buffer! I can see the final `token` is being read! Now I just need to make sure that the `quic_crypto_generate_token` isn't called somewhere unexpected. Let's see where that function is used. |
| 34/3 | 2026/09/14 17:54 | tool | grepper |
0mArgs:{
"Expression": "quic_crypto_generate_token",
"PathPrefix": "net/quic"
}Results: {
"Output": "net/quic/crypto.c=1080=static int quic_crypto_token_protect(struct quic_crypto *crypto, u8 *token,\n--\nnet/quic/crypto.c-1137- */\nnet/quic/crypto.c:1138:int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,\nnet/quic/crypto.c-1139-\t\t\t u32 addrlen, struct quic_conn_id *conn_id,\n--\nnet/quic/crypto.h=77=int quic_crypto_set_token_secret(struct quic_crypto *crypto);\nnet/quic/crypto.h-78-\nnet/quic/crypto.h:79:int quic_crypto_generate_token(struct quic_crypto *crypto, void *addr,\nnet/quic/crypto.h-80-\t\t\t u32 addrlen, struct quic_conn_id *conn_id,\n"
} |
| 35/3 | 2026/09/14 17:54 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 115219 output: 26 thoughts: 1453 Thoughts: **Initial Assessment and Verification**
Okay, so `quic_crypto_generate_token` isn't used here, that's not the focus of this check. Time to get to the details.
Alright, let's step through this. We're looking at potential uninitialized memory reads. First, we go to `quic_path.c` and specifically `quic_udp_sock_create`. Here, `us = kmalloc_obj(*us);` is the key. Then, we check the definition of `struct quic_udp_sock` in `path.h`. Good, we can see every member is initialized immediately after allocation: `refcnt`, `sk`, `addr`, `bind_ifindex`, `work`, and `node` are all accounted for. Nothing's missed here; it's all clean.
Next up, `socket.c` and `quic_request_sock_create`. Again, `req = kmalloc_obj(*req, gfp);` grabs our attention. Then, we check `socket.h` for `struct quic_request_sock`, and every member appears to be initialized in the function: `version`, `daddr`, `saddr`, `scid`, `dcid`, `orig_dcid`, `retry`, `backlog_list`, `blen`, and `list` are all assigned a value. No uninitialized reads there.
Finally, we go to `stream.c`. Here we have a `kmalloc_objs` call. We check that every element in the array is initialized using `INIT_HLIST_HEAD`. Excellent, we are not going to see any problems here.
Okay, let's take a step back. I should double check for `kzalloc` or `= {}`, to look for potential vulnerabilities. No, now it seems that every single variable is handled.
Looks like everything is initialized properly within these code sections. It's safe to proceed.
|
| 36/3 | 2026/09/14 17:55 | tool | grepper |
0mArgs:{
"Expression": "kzalloc",
"PathPrefix": "net/quic"
}Results: {
"Output": "net/quic/connid.c=110=int quic_conn_id_add(struct quic_conn_id_set *id_set,\n--\nnet/quic/connid.c-144-\t\treturn -EINVAL;\nnet/quic/connid.c:145:\tcommon = kzalloc(id_set-\u003eentry_size, gfp);\nnet/quic/connid.c-146-\tif (!common)\n--\nnet/quic/crypto.c=266=static void *quic_crypto_skcipher_mem_alloc(struct crypto_skcipher *tfm,\n--\nnet/quic/crypto.c-284-\nnet/quic/crypto.c:285:\tmem = kzalloc(len, gfp);\nnet/quic/crypto.c-286-\tif (!mem)\n--\nnet/quic/crypto.c=418=static void *quic_crypto_aead_mem_alloc(struct crypto_aead *tfm, u32 ctx_size,\n--\nnet/quic/crypto.c-437-\nnet/quic/crypto.c:438:\tmem = kzalloc(len, gfp);\nnet/quic/crypto.c-439-\tif (!mem)\n--\nnet/quic/pnspace.c=18=int quic_pnspace_init(struct quic_pnspace *space)\nnet/quic/pnspace.c-19-{\nnet/quic/pnspace.c:20:\tspace-\u003epn_map = kzalloc(BITS_TO_BYTES(QUIC_PN_MAP_INITIAL), GFP_KERNEL);\nnet/quic/pnspace.c-21-\tif (!space-\u003epn_map)\n--\nnet/quic/pnspace.c=45=static int quic_pnspace_grow(struct quic_pnspace *space, u16 size, gfp_t gfp)\n--\nnet/quic/pnspace.c-53-\nnet/quic/pnspace.c:54:\tnew = kzalloc(BITS_TO_BYTES(len), gfp);\nnet/quic/pnspace.c-55-\tif (!new)\n--\nnet/quic/stream.c=83=static struct quic_stream *quic_stream_create(struct quic_stream_table *streams,\n--\nnet/quic/stream.c-104-\twhile (stream_id \u003c= max_stream_id) {\nnet/quic/stream.c:105:\t\tstream = kzalloc_obj(*stream, gfp);\nnet/quic/stream.c-106-\t\tif (!stream)\n"
} |
| 37/3 | 2026/09/14 17:55 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 117795 output: 47 thoughts: 8739 Thoughts: **My Analysis of Potential Uninitialized Memory Reads in QUIC Code**
Okay, let's break this down systematically. My primary concern is ensuring that sensitive or crucial data isn't being read before it's properly initialized. I'm focusing on stack allocations, as those are the most likely candidates. I've gone through the relevant `net/quic/` code and here's my assessment:
* **`kzalloc` vs. Stack Allocations:** I've noted that `kzalloc` is widely used, which is good because it guarantees zero-initialization. The potential weak spot is stack-allocated variables, especially structs and arrays. I need to trace the initialization and usage of these.
* **`crypto.c` Deep Dive:**
1. **`quic_crypto_hkdf_expand`:**
* `info` and `tmp`: `info` is populated sequentially and `crypto_shash_update` uses `infolen` to avoid uninitialized data, `tmp` is written before read
* `desc`: `shash_desc_zero(desc)` ensures clear of struct.
2. **`quic_crypto_keys_derive`:**
* `k_l`, `hp_k_l`, `i_l`: These are all initialized directly, so no issue.
3. **`quic_crypto_keys_derive_and_install`:**
* `k`, `iv`, `hp_k`: These are filled by `quic_crypto_keys_derive`.
4. **`quic_crypto_initial_keys_install`:**
* `secret`: This is initialized by `quic_crypto_hkdf_extract`, so no concern.
5. **`quic_crypto_token_protect`:** This is the most interesting function. I need to be sure that the `ts` variable is initialized before use.
* `ts`: The code reads `ts` from the token. `quic_get_int` is used to get the value of `ts`. I carefully checked `quic_get_int`:
* I saw `tslen = sizeof(ts);` (ts is a u64, so tslen is 8).
* I analyzed the math to ensure that the code path for the *plen < len case is never taken. It is not.
* I confirmed that in `quic_crypto_token_protect` and the functions that call it, `quic_get_int`'s success is ensured, so `ts` is always initialized by `quic_get_int`.
* I checked also `quic_crypto_verify_token`, and if the value is not correctly taken, it's short circuited.
* **`family.c` Zero-Initialization:** `memset` is used to zero initialize.
* **`packet.c` Vulnerability Analysis:**
* `quic_packet_listen_process`:
* `toff`: Is conditionally set based on `token.len`. If non-zero, it is initialized based on the token. Otherwise, `toff` is never used. Thus, no uninitialized read.
* `version`: Initialized by `quic_packet_get_long_header`. The value is set on `quic_get_int` success.
* **`quic_skb_cb` and Zeroing:** I confirmed that `memset` is used for `skb->cb` zeroing in `quic_udp_rcv`. And the size check with `BUILD_BUG_ON` ensures no buffer overflows.
* **Packet Creation:** I analyzed several packet creation functions, and I don't see any explicit memory allocation functions that might lead to an issue.
* **`quic_packet_get_alpn` Analysis:**
* `alpns`: The stack-allocated `alpns` struct (a `quic_data` struct) is initialized to zero when declared, and its fields are populated safely in `quic_packet_get_alpn`.
* **`quic_pnspace.c` Gap Tracking:**
* `quic_pnspace_num_gabs`: The `tmp` array in `quic_pnspace_num_gabs` is used to hold ack blocks. It is fully overwritten by `memcpy` calls. Specifically, the copies only read data that was properly filled into `gabs`. There is no uninitialized read.
* **`protocol.c` SNMP Statistics:**
|
| 38/3 | 2026/09/14 17:55 | tool | grepper |
0mArgs:{
"Expression": "quic_config|quic_transport_param|quic_stream_info|quic_event",
"PathPrefix": "net/quic"
}Results: {
"Output": "net/quic/connid.c=273=void quic_conn_id_get_param(struct quic_conn_id_set *id_set,\nnet/quic/connid.c:274:\t\t\t struct quic_transport_param *p)\nnet/quic/connid.c-275-{\n--\nnet/quic/connid.c=279=void quic_conn_id_set_param(struct quic_conn_id_set *id_set,\nnet/quic/connid.c:280:\t\t\t struct quic_transport_param *p)\nnet/quic/connid.c-281-{\n--\nnet/quic/connid.h=178=void quic_conn_id_get_param(struct quic_conn_id_set *id_set,\nnet/quic/connid.h:179:\t\t\t struct quic_transport_param *p);\nnet/quic/connid.h-180-void quic_conn_id_set_param(struct quic_conn_id_set *id_set,\nnet/quic/connid.h:181:\t\t\t struct quic_transport_param *p);\nnet/quic/connid.h-182-void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source);\n--\nnet/quic/path.c=337=void quic_path_get_param(struct quic_path_group *paths,\nnet/quic/path.c:338:\t\t\t struct quic_transport_param *p)\nnet/quic/path.c-339-{\n--\nnet/quic/path.c=347=void quic_path_set_param(struct quic_path_group *paths,\nnet/quic/path.c:348:\t\t\t struct quic_transport_param *p)\nnet/quic/path.c-349-{\n--\nnet/quic/path.h=186=void quic_path_get_param(struct quic_path_group *paths,\nnet/quic/path.h:187:\t\t\t struct quic_transport_param *p);\nnet/quic/path.h-188-void quic_path_set_param(struct quic_path_group *paths,\nnet/quic/path.h:189:\t\t\t struct quic_transport_param *p);\nnet/quic/path.h-190-bool quic_path_pl_confirm(struct quic_path_group *paths,\n--\nnet/quic/stream.c=373=void quic_stream_get_param(struct quic_stream_table *streams,\nnet/quic/stream.c:374:\t\t\t struct quic_transport_param *p)\nnet/quic/stream.c-375-{\n--\nnet/quic/stream.c=387=void quic_stream_set_param(struct quic_stream_table *streams,\nnet/quic/stream.c:388:\t\t\t struct quic_transport_param *p, bool is_serv)\nnet/quic/stream.c-389-{\n--\nnet/quic/stream.h=128=void quic_stream_get_param(struct quic_stream_table *streams,\nnet/quic/stream.h:129:\t\t\t struct quic_transport_param *p);\nnet/quic/stream.h-130-void quic_stream_set_param(struct quic_stream_table *streams,\nnet/quic/stream.h:131:\t\t\t struct quic_transport_param *p, bool is_serv);\nnet/quic/stream.h-132-void quic_stream_free(struct quic_stream_table *streams);\n"
} |
| 39/3 | 2026/09/14 17:55 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 128588 output: 30 thoughts: 543 Thoughts: **My Initial Assessment of the IPPROTO_QUIC Implementation** Okay, so I've been digging into this IPPROTO_QUIC implementation, and my initial impression is...underwhelming. From what I can see, `quic_transport_param` is only touched by internal helpers like `quic_conn_id_get_param`, `quic_path_get_param`, and so on. That makes sense, those are presumably internal data structures. But then I see that `quic_do_getsockopt`, `quic_do_setsockopt`, `quic_sendmsg`, `quic_recvmsg`, `quic_connect`, `quic_bind`, and `quic_accept` are all just stubs, returning `-EOPNOTSUPP`. Essentially, they're placeholders. So, what *can* userspace actually do right now with a QUIC socket? Let's see: 1. We *can* create a socket using `socket(AF_INET, SOCK_STREAM/DGRAM, IPPROTO_QUIC)` (or AF\_INET6), which triggers `quic_init_sock`. Okay, that's something. 2. `close()` on that socket, calling `quic_close` and then `quic_destroy_sock`, that also works. 3. We can call `shutdown()` on the socket, that is present. 4. `getsockname()` and `getpeername()` work, and they call `quic_inet_getname`, which, in turn, calls `quic_get_sk_addr`, and then delegates to `inet_getname` or `inet6_getname`. Good, basic socket info retrieval. 5. `setsockopt` and `getsockopt` with SOL\_IP or SOL\_IPV6 options appear to work, since these are delegated to the regular IP socket options (`ip_setsockopt` / `ip_getsockopt`). That seems logical. 6. We can `poll` the socket, but `quic_inet_poll` currently just returns 0, so that does not seem useful in the current implementation. 7. The procfs `/proc/net/quic/snmp` file can be read. 8. The sysctl parameters under `/proc/sys/net/quic/` are also readable/writable. But that's it? I'm missing something fundamental here. Now, the big question: what about actual network input? Can incoming UDP packets even *reach* QUIC? I see that QUIC registers UDP tunnel sockets when a path is bound, but `quic_bind` is a stub. So, the question remains: where are these "paths" created? Are there any that are created without the stub `quic_bind`? I need to check where `quic_path_bind` is actually called. I need to trace this further. The current implementation looks incredibly incomplete. |
| 40/3 | 2026/09/14 17:55 | tool | grepper |
0mArgs:{
"Expression": "quic_path_bind",
"PathPrefix": "net/quic"
}Results: {
"Output": "net/quic/path.c=149=static void quic_path_set_udp_sk(struct quic_path *path,\n--\nnet/quic/path.c-165-/* Binds a QUIC path to a local port and sets up a UDP socket. */\nnet/quic/path.c:166:int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path)\nnet/quic/path.c-167-{\n--\nnet/quic/path.h=174=bool quic_path_detect_alt(struct quic_path_group *paths, union quic_addr *sa,\nnet/quic/path.h-175-\t\t\t union quic_addr *da, struct sock *sk);\nnet/quic/path.h:176:int quic_path_bind(struct sock *sk, struct quic_path_group *paths, u8 path);\nnet/quic/path.h-177-void quic_path_unbind(struct sock *sk, struct quic_path_group *paths, u8 path);\n"
} |
| 41/3 | 2026/09/14 17:55 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 130102 output: 20 thoughts: 853 Thoughts: **Analyzing the QUIC Socket Implementation**
Okay, first things first, `quic_path_bind`... it's just *defined* in this patch, I don't see any calls to it. Curious. Now, let's look at `quic_packet_rcv`. Is it being registered with UDP, Netfilter, or anything else? Nope, not here.
Alright, let's step back and look at the bigger picture within this patch. I see `setup_udp_tunnel_sock` is only called from `quic_udp_sock_create`, which, in turn, is *only* called by `quic_path_bind`. And then I see that `quic_path_bind` is *never* called, because `quic_bind` returns `-EOPNOTSUPP`. Hmm, that's a dead end.
I should mention: `quic_packet_rcv` isn't registered with a `netinet` protocol handler either. That makes sense, because QUIC runs *over* UDP, not directly on IP, so `IPPROTO_QUIC` is for `socket(..., IPPROTO_QUIC)`, not an IP protocol handler.
Okay, let's switch gears and check `quic_inet_connect` in `protocol.c`.
```c
static int quic_inet_connect(struct socket *sock, struct sockaddr_unsized *addr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
if (addr_len < (int)sizeof(addr->sa_family))
return -EINVAL;
return sk->sk_prot->connect(sk, addr, addr_len);
}
```
Okay, `sk->sk_prot->connect` is `quic_connect`, and, as I recall, that function returns `-EOPNOTSUPP`. That's not helpful at all.
What about `quic_inet_listen`? Is it used? Nope. It also returns `-EOPNOTSUPP`. Looks like we are dead in the water.
Alright, let's pivot and look at `quic_v6_get_sk_addr`. What is that being used for?
```c
static int quic_v6_get_sk_addr(struct socket *sock, struct sockaddr *uaddr,
int peer)
{
union quic_addr *a = quic_addr(uaddr);
int ret;
ret = inet6_getname(sock, uaddr, peer);
if (ret < 0)
return ret;
if (a->sa.sa_family == AF_INET6 &&
ipv6_addr_v4mapped(&a->v6.sin6_addr)) {
a->v4.sin_family = AF_INET;
a->v4.sin_port = a->v6.sin6_port;
a->v4.sin_addr.s_addr = a->v6.sin6_addr.s6_addr32[3];
}
if (a->sa.sa_family == AF_INET) {
memset(a->v4.sin_zero, 0, sizeof(a->v4.sin_zero));
return sizeof(struct sockaddr_in);
}
return sizeof(struct sockaddr_in6);
}
```
Okay, so let's trace this. When the user calls `getsockname(fd, ...)` or `getpeername(fd, ...)` on an `AF_INET6` `IPPROTO_QUIC` socket, in `net/socket.c`, `__sys_getsockname` is involved:
```c
struct sockaddr_storage address;
err = sock->ops->getname(sock, (struct sockaddr *)&address, 0);
if (err >= 0)
err = move_addr_to_user(&address, err, uaddr, uaddr_len);
```
Here, `sock->ops->getname` calls `inet6_getname`. So, `inet6_getname` fills `address` (or part of it).
Now, does `inet6_getname` initialize the whole `struct sockaddr_in6`? I really need to check what exactly `inet6_getname` *does* in the kernel.
|
| 42/3 | 2026/09/14 17:55 | tool | grepper |
0mArgs:{
"Expression": "int inet6_getname"
}Results: {
"Output": "include/net/ipv6.h=1170=int inet6_bind_sk(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len);\ninclude/net/ipv6.h:1171:int inet6_getname(struct socket *sock, struct sockaddr *uaddr,\ninclude/net/ipv6.h-1172-\t\t int peer);\n--\nnet/ipv6/af_inet6.c=475=void inet6_cleanup_sock(struct sock *sk)\n--\nnet/ipv6/af_inet6.c-503- */\nnet/ipv6/af_inet6.c:504:int inet6_getname(struct socket *sock, struct sockaddr *uaddr,\nnet/ipv6/af_inet6.c-505-\t\t int peer)\n"
} |
| 43/3 | 2026/09/14 17:55 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 132175 output: 39 thoughts: 11 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already well-versed in networking and system internals: **Diving into inet6_getname** Alright, let's get down to brass tacks. The thought process starts and ends with `inet6_getname`. That immediately tells me this is about IPv6 address retrieval, likely within the kernel's networking stack. I'm assuming a C context, probably looking at a system call implementation or a function deeply involved in socket operations. My immediate questions are: * **What's the context?** Am I debugging a socket issue? Analyzing network performance? Auditing for security vulnerabilities? Understanding IPv6 socket API internals? This influences how I'll approach the code. * **What's the objective?** Am I trying to figure out how the kernel translates an IP address into a human-readable name? Or is it more about internal data structures and their handling? Or something else entirely. * **What are the potential gotchas?** I need to be aware of the complexities of IPv6, including address scope, link-local addresses, multi-homing, and DNS interactions (if any). I also need to think about potential race conditions and concurrency issues if this is a multithreaded environment. * **Where is the code?** Is this in the kernel source, or a user-space library calling kernel functions? This affects how I would analyze the control flow and data structures involved. Basically, I'm mentally building a framework for understanding `inet6_getname`. I know that I will probably be looking for: * Data structures, particularly the `sockaddr_in6` structure, and how it is populated. * Error handling. What are the common error codes and what might trigger them? * Interaction with other kernel modules (like the routing table). * Potential for resource exhaustion (e.g., memory allocation within the function). * Concurrency considerations and locking mechanisms used. So, in short, I'm ready to dive into the code and start figuring out what's going on with IPv6 address retrieval at this level. Let's see what's what. |
| 44/3 | 2026/09/14 17:55 | tool | read-file |
0mArgs:{
"File": "net/ipv6/af_inet6.c",
"FirstLine": 504,
"LineCount": 40
}Results: {
"Contents": " 504:\tint inet6_getname(struct socket *sock, struct sockaddr *uaddr,\n 505:\t\t\t int peer)\n 506:\t{\n 507:\t\tstruct sockaddr_in6 *sin = (struct sockaddr_in6 *)uaddr;\n 508:\t\tint sin_addr_len = sizeof(*sin);\n 509:\t\tstruct sock *sk = sock-\u003esk;\n 510:\t\tstruct inet_sock *inet = inet_sk(sk);\n 511:\t\tstruct ipv6_pinfo *np = inet6_sk(sk);\n 512:\t\n 513:\t\tsin-\u003esin6_family = AF_INET6;\n 514:\t\tsin-\u003esin6_flowinfo = 0;\n 515:\t\tsin-\u003esin6_scope_id = 0;\n 516:\t\tlock_sock(sk);\n 517:\t\tif (peer) {\n 518:\t\t\tif (!inet-\u003einet_dport ||\n 519:\t\t\t (((1 \u003c\u003c sk-\u003esk_state) \u0026 (TCPF_CLOSE | TCPF_SYN_SENT)) \u0026\u0026\n 520:\t\t\t peer == 1)) {\n 521:\t\t\t\trelease_sock(sk);\n 522:\t\t\t\treturn -ENOTCONN;\n 523:\t\t\t}\n 524:\t\t\tsin-\u003esin6_port = inet-\u003einet_dport;\n 525:\t\t\tsin-\u003esin6_addr = sk-\u003esk_v6_daddr;\n 526:\t\t\tif (inet6_test_bit(SNDFLOW, sk))\n 527:\t\t\t\tsin-\u003esin6_flowinfo = np-\u003eflow_label;\n 528:\t\t\tBPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, \u0026sin_addr_len,\n 529:\t\t\t\t\t CGROUP_INET6_GETPEERNAME);\n 530:\t\t} else {\n 531:\t\t\tif (ipv6_addr_any(\u0026sk-\u003esk_v6_rcv_saddr))\n 532:\t\t\t\tsin-\u003esin6_addr = np-\u003esaddr;\n 533:\t\t\telse\n 534:\t\t\t\tsin-\u003esin6_addr = sk-\u003esk_v6_rcv_saddr;\n 535:\t\t\tsin-\u003esin6_port = inet-\u003einet_sport;\n 536:\t\t\tBPF_CGROUP_RUN_SA_PROG(sk, (struct sockaddr *)sin, \u0026sin_addr_len,\n 537:\t\t\t\t\t CGROUP_INET6_GETSOCKNAME);\n 538:\t\t}\n 539:\t\tsin-\u003esin6_scope_id = ipv6_iface_scope_id(\u0026sin-\u003esin6_addr,\n 540:\t\t\t\t\t\t\t sk-\u003esk_bound_dev_if);\n 541:\t\trelease_sock(sk);\n 542:\t\treturn sin_addr_len;\n 543:\t}\n"
} |
| 45/3 | 2026/09/14 17:55 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 133441 output: 19 thoughts: 223 Thoughts: **My Assessment of IPv6 Address Initialization** Okay, so I'm looking at how this `inet6_getname` function handles the `sockaddr_in6` structure. It's good to see that it's being diligent about initializing *every* single field in that structure: `sin6_family`, `sin6_flowinfo`, `sin6_scope_id`, `sin6_port`, and `sin6_addr` are all explicitly set. That's exactly what I'd expect; no room for potential vulnerabilities or undefined behavior. The code then handles v4-mapped addresses. If it's a v4-mapped address, then `quic_v6_get_sk_addr` is called to convert it to `sockaddr_in`. It also explicitly clears `sin_zero` with `memset`, ensuring that padding bytes are zeroed out in the `sockaddr_in` structure. The function then correctly returns the `sizeof` the `sockaddr_in` structure, which makes sense. There aren't any uninitialized fields or any other potential issues with padding. Wait a second, let's just make sure that `quic_v4_get_sk_addr` is doing the right thing. It looks like it's simply calling `inet_getname`, which I should probably check too. |
| 46/3 | 2026/09/14 17:55 | tool | grepper |
0mArgs:{
"Expression": "int inet_getname"
}Results: {
"Output": "include/net/inet_common.h=54=int __inet_bind(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len,\ninclude/net/inet_common.h-55-\t\tu32 flags);\ninclude/net/inet_common.h:56:int inet_getname(struct socket *sock, struct sockaddr *uaddr,\ninclude/net/inet_common.h-57-\t\t int peer);\n--\nnet/ipv4/af_inet.c=801=EXPORT_SYMBOL(inet_accept);\n--\nnet/ipv4/af_inet.c-805- */\nnet/ipv4/af_inet.c:806:int inet_getname(struct socket *sock, struct sockaddr *uaddr,\nnet/ipv4/af_inet.c-807-\t\t int peer)\n"
} |
| 47/3 | 2026/09/14 17:55 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 134190 output: 38 thoughts: 10 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Investigating `inet_getname`** Alright, let's dive into this `inet_getname` function. My initial thought is to quickly assess its purpose and implementation within the context of whatever system we're dealing with. Knowing the underlying OS and library version is paramount for this. I need to understand what specific networking stack it's likely using. My immediate questions are: Is this a wrapper around standard system calls like `getnameinfo` or `gethostbyaddr`? Or is it a custom function that adds some layer of abstraction or optimization? I'd expect it to handle the conversion of an IP address (v4 or v6) to a hostname, which is fairly standard functionality. I want to check for potential error handling. Does it return errors properly? Are there any known vulnerabilities related to buffer overflows or DNS spoofing, especially if it handles user-provided data directly? Does it offer any caching mechanisms to improve performance? If so, are there implications regarding DNS TTL and cache invalidation? The implementation will be key. I'll be particularly interested in: How is the IP address represented internally? Is it using network byte order, and does the function handle different address families correctly? I'll be looking for any signs of potential integer overflows if it deals with any numeric representation of the address. Also, are they using any kind of threading? Ultimately, I need to understand its behavior thoroughly to identify any potential gotchas, performance bottlenecks, or security vulnerabilities before I use it in this context. |
| 48/3 | 2026/09/14 17:56 | tool | read-file |
0mArgs:{
"File": "net/ipv4/af_inet.c",
"FirstLine": 806,
"LineCount": 40
}Results: {
"Contents": " 806:\tint inet_getname(struct socket *sock, struct sockaddr *uaddr,\n 807:\t\t\t int peer)\n 808:\t{\n 809:\t\tstruct sock *sk\t\t= sock-\u003esk;\n 810:\t\tstruct inet_sock *inet\t= inet_sk(sk);\n 811:\t\tDECLARE_SOCKADDR(struct sockaddr_in *, sin, uaddr);\n 812:\t\tint sin_addr_len = sizeof(*sin);\n 813:\t\n 814:\t\tsin-\u003esin_family = AF_INET;\n 815:\t\tlock_sock(sk);\n 816:\t\tif (peer) {\n 817:\t\t\tif (!inet-\u003einet_dport ||\n 818:\t\t\t (((1 \u003c\u003c sk-\u003esk_state) \u0026 (TCPF_CLOSE | TCPF_SYN_SENT)) \u0026\u0026\n 819:\t\t\t peer == 1)) {\n 820:\t\t\t\trelease_sock(sk);\n 821:\t\t\t\treturn -ENOTCONN;\n 822:\t\t\t}\n 823:\t\t\tsin-\u003esin_port = inet-\u003einet_dport;\n 824:\t\t\tsin-\u003esin_addr.s_addr = inet-\u003einet_daddr;\n 825:\t\t\tBPF_CGROUP_RUN_SA_PROG(sk, sin, \u0026sin_addr_len,\n 826:\t\t\t\t\t CGROUP_INET4_GETPEERNAME);\n 827:\t\t} else {\n 828:\t\t\t__be32 addr = inet-\u003einet_rcv_saddr;\n 829:\t\t\tif (!addr)\n 830:\t\t\t\taddr = inet-\u003einet_saddr;\n 831:\t\t\tsin-\u003esin_port = inet-\u003einet_sport;\n 832:\t\t\tsin-\u003esin_addr.s_addr = addr;\n 833:\t\t\tBPF_CGROUP_RUN_SA_PROG(sk, sin, \u0026sin_addr_len,\n 834:\t\t\t\t\t CGROUP_INET4_GETSOCKNAME);\n 835:\t\t}\n 836:\t\trelease_sock(sk);\n 837:\t\tmemset(sin-\u003esin_zero, 0, sizeof(sin-\u003esin_zero));\n 838:\t\treturn sin_addr_len;\n 839:\t}\n 840:\tEXPORT_SYMBOL(inet_getname);\n 841:\t\n 842:\tint inet_send_prepare(struct sock *sk)\n 843:\t{\n 844:\t\tsock_rps_record_flow(sk);\n 845:\t\n"
} |
| 49/3 | 2026/09/14 17:56 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 135237 output: 34 thoughts: 1330 Thoughts: **QUIC Data Structures and Initialization Deep Dive** Okay, so I'm looking at how QUIC handles its data structures and initialization, starting with the basics of `inet_getname` which sets the socket address family and other key network attributes, including zeroing out unused parts. But wait, I need to consider system-level configuration, specifically the sysctl variables. I see that `quic_table` defines the sysctl entries for memory and receive/write buffer settings (`quic_mem`, `quic_rmem`, `quic_wmem`). These are all initialized in `quic_init()`. Interestingly, each variable array is completely initialized at module initialization. Now I'm diving deeper to verify data safety. I'm focusing on potential uninitialized memory reads. I check `quic_snmp_seq_show`. It initializes a buffer `buff` with `memset`, then populates it via `snmp_get_cpu_field_batch_cnt`. The per-CPU statistics are allocated using `alloc_percpu` in `quic_net_init` and I remember that `alloc_percpu` zeroes the memory. So, the data from the per-cpu stats is safe. Next, I need to exhaustively search for potential issues in other functions that process user or packet data within `net/quic/`. So I'm examining `quic_data_from_string` and `quic_data_to_string` functions in `net/quic/common.c`. These appear to handle conversion between string representations and a `quic_data` structure. Now I need to check where these are called from. |
| 50/3 | 2026/09/14 17:56 | tool | grepper |
0mArgs:{
"Expression": "quic_data_(from|to)_string",
"PathPrefix": "net/quic"
}Results: {
"Output": "net/quic/common.c=465=bool quic_data_match(struct quic_data *d1, struct quic_data *d2)\n--\nnet/quic/common.c-487- */\nnet/quic/common.c:488:int quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from)\nnet/quic/common.c-489-{\n--\nnet/quic/common.c-526- */\nnet/quic/common.c:527:int quic_data_from_string(struct quic_data *to, u8 *from, u32 len)\nnet/quic/common.c-528-{\n--\nnet/quic/common.h=212=u8 *quic_put_var(u8 *p, u64 num);\nnet/quic/common.h-213-\nnet/quic/common.h:214:int quic_data_from_string(struct quic_data *to, u8 *from, u32 len);\nnet/quic/common.h:215:int quic_data_to_string(u8 *to, u32 *plen, struct quic_data *from);\nnet/quic/common.h-216-\n"
} |
| 51/3 | 2026/09/14 17:56 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 137308 output: 368 thoughts: 951 Thoughts: **KMSAN Assessment: A Thorough Review** Okay, so I need to decide if this patch series warrants a KMSAN fuzzing session. Let's make sure I've got this right. The guidelines are pretty clear: KMSAN is expensive, so it's only needed if KASAN, LOCKDEP, or other standard detectors *won't* catch a bug. Specifically, we only need it if there are UNINITIALIZED MEMORY risks that *only* KMSAN would detect. I need to apply the checklist: 1. **Kernel Structures to User Space?** Hmm, let's see. `quic_do_getsockopt`, `quic_recvmsg`, and `quic_accept` all immediately return `-EOPNOTSUPP`. That's good. The `getsockname`/`getpeername` delegates, but those functions completely initialize the `sockaddr` struct, including all the padding. `/proc/net/quic/snmp` reads zero-initialized percpu stats. And the sysctls, those read initialized longs and ints. Nope, I don't see any risk here. No info leaks to userspace. 2. **Uninitialized Variables in Conditionals?** Right, branching logic. I need to make sure there's no conditional logic that depends on uninitialized variables. Looking through the code, all allocated structs are either initialized using `kzalloc`, `memset`, or initialized member-by-member before use. Stack variables are *always* initialized or their values are checked via return values from getter functions, like `quic_get_int` and `quic_get_var`. No problem there. 3. **Complex Data Structures and Uninitialized Fields?** Okay, allocation is important. All heap allocations (`struct quic_request_sock`, `struct quic_udp_sock`) either use `kzalloc` or `memset` or assign all fields explicitly. The hash tables, allocated with `vmalloc_array`, are initialized correctly, with loops setting spinlocks/mutexes and list heads. The `pn_map` bitmap is allocated with `kzalloc` too. So all the allocations are being handled. 4. **Bounds Checks Exposing Uninitialized Memory?** This is about potential access to uninitialized parts of buffers. It looks like packet parsing rigorously bounds-checks the buffer lengths *before* extracting any data. Okay, that looks good. So, in summary, this patch series, which introduces the initial QUIC implementation, does **not** introduce uninitialized memory risks. All of the key areas are covered, all the relevant structures are initialized safely, there are no info-leaks, so I don't see a reason to enable KMSAN. KASAN, LOCKDEP, and UBSAN should handle the memory safety, reference counting, and locking aspects of this new protocol implementation. The code's focus is appropriate, and I've checked the reasoning. I'm confident. Time to call it. |
| 52/3 | 2026/09/14 17:56 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series introduces initial protocol support and foundational structures for QUIC (RFC 9000), including socket operations, packet framing, connection ID handling, congestion control, and cryptographic wrappers.\n\nKMSAN applicability was evaluated against the new protocol logic:\n1. Kernel-to-user info-leaks: QUIC-specific socket options (quic_do_getsockopt), message reception (quic_recvmsg), accept, connect, and bind are currently stubs returning -EOPNOTSUPP. Address retrieval (quic_get_sk_addr) delegates to inet_getname / inet6_getname, which properly populate and clear padding (e.g. sin_zero) in sockaddr structures.\n2. Uninitialized memory access: Heap allocations across the subsystem either use kzalloc (e.g. stream structures, pn_map bitmaps, connection ID structures) or explicitly initialize every struct field upon allocation (quic_request_sock, quic_udp_sock). Stack buffers in cryptographic and framing routines are explicitly zeroed or bounded by parsed lengths before consumption.\n3. Control flow: Variable parsing helpers (quic_get_int, quic_get_var, quic_packet_get_long_header) check buffer bounds before reading and update outputs only on success.\n\nAny potential bugs in this patch series (such as bounds errors in packet parsing, use-after-free in reference-counted UDP tunnel sockets or RCU-managed connection IDs, or lock hierarchy issues) are covered by standard KASAN and LOCKDEP. There are no uninitialized memory risks requiring a dedicated KMSAN session.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series introduces initial protocol support and foundational structures for QUIC (RFC 9000), including socket operations, packet framing, connection ID handling, congestion control, and cryptographic wrappers.\n\nKMSAN applicability was evaluated against the new protocol logic:\n1. Kernel-to-user info-leaks: QUIC-specific socket options (quic_do_getsockopt), message reception (quic_recvmsg), accept, connect, and bind are currently stubs returning -EOPNOTSUPP. Address retrieval (quic_get_sk_addr) delegates to inet_getname / inet6_getname, which properly populate and clear padding (e.g. sin_zero) in sockaddr structures.\n2. Uninitialized memory access: Heap allocations across the subsystem either use kzalloc (e.g. stream structures, pn_map bitmaps, connection ID structures) or explicitly initialize every struct field upon allocation (quic_request_sock, quic_udp_sock). Stack buffers in cryptographic and framing routines are explicitly zeroed or bounded by parsed lengths before consumption.\n3. Control flow: Variable parsing helpers (quic_get_int, quic_get_var, quic_packet_get_long_header) check buffer bounds before reading and update outputs only on success.\n\nAny potential bugs in this patch series (such as bounds errors in packet parsing, use-after-free in reference-counted UDP tunnel sockets or RCU-managed connection IDs, or lock hierarchy issues) are covered by standard KASAN and LOCKDEP. There are no uninitialized memory risks requiring a dedicated KMSAN session.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|