net: introduce QUIC infrastructure and core subcomponents
Subsystems (6)
cover net: introduce QUIC infrastructure and core subcomponents
Introduction
============
The QUIC protocol, as defined in RFC9000, offers a UDP-based, secure transport with flow-controlled streams for efficient communication, low-latency connection setup, and network path migration, ensuring confidentiality, integrity, and availability across various deployments.
This implementation introduces QUIC support in Linux Kernel, offering several key advantages:
- Seamless Integration for Kernel Subsystems: Kernel subsystems such as SMB and NFS can operate over QUIC seamlessly after the handshake, leveraging the net/handshake APIs.
- Standardized Socket APIs for QUIC: This implementation standardizes the socket APIs for QUIC, covering essential operations like listen, accept, connect, sendmsg, recvmsg, close, get/setsockopt, and getsock/peername().
- Efficient ALPN Routing: It incorporates ALPN routing within the kernel, efficiently directing incoming requests to the appropriate applications across different processes based on ALPN.
- Performance Enhancements: By minimizing data duplication through zero-copy techniques such as sendfile(), and paving the way for crypto offloading in NICs, this implementation enhances performance and prepares for future optimizations.
This implementation offers fundamental support for the following RFCs:
- RFC9000 - QUIC: A UDP-Based Multiplexed and Secure Transport
- RFC9001 - Using TLS to Secure QUIC
- RFC9002 - QUIC Loss Detection and Congestion Control
- RFC9221 - An Unreliable Datagram Extension to QUIC
- RFC9287 - Greasing the QUIC Bit
- RFC9368 - Compatible Version Negotiation for QUIC
- RFC9369 - QUIC Version 2
The socket APIs for QUIC follow the RFC draft [1]:
- The Sockets API Extensions for In-kernel QUIC Implementations
Implementation
==============
The core idea is to implement QUIC within the kernel, using a userspace handshake approach.
Only the processing and creation of raw TLS Handshake Messages are handled in userspace, facilitated by a TLS library like GnuTLS. These messages are exchanged between kernel and userspace via sendmsg() and recvmsg(), with cryptographic details conveyed through control messages (cmsg).
The entire QUIC protocol, aside from the TLS Handshake Messages processing and creation, is managed within the kernel. Rather than using a Upper Layer Protocol (ULP) layer, this implementation establishes a socket of type IPPROTO_QUIC (similar to IPPROTO_MPTCP), operating over UDP tunnels.
For kernel consumers, they can initiate a handshake request from the kernel to userspace using the existing net/handshake netlink. The userspace component, such as tlshd service [2], then manages the processing of the QUIC handshake request.
- Handshake Architecture:
┌──────┐ ┌──────┐
│ APP1 │ │ APP2 │ ...
└──────┘ └──────┘
┌──────────────────────────────────────────┐
│ {quic_client/server_handshake()} │<─────────────┐
└──────────────────────────────────────────┘ ┌─────────────┐
{send/recvmsg()} {set/getsockopt()} │ tlshd │
[CMSG handshake_info] [SOCKOPT_CRYPTO_SECRET] └─────────────┘
[SOCKOPT_TRANSPORT_PARAM_EXT] │ ^
│ ^ │ ^ │ │
Userspace │ │ │ │ │ │
──────────────│─│──────────────────│─│──────────────────│───│───────
Kernel │ │ │ │ │ │
v │ v │ v │
┌──────────────────┬───────────────────────┐ ┌─────────────┐
│ protocol, timer, │ socket (IPPROTO_QUIC) │<──┐ │ handshake │
│ ├───────────────────────┤ │ │netlink APIs │
│ common, family, │ outqueue | inqueue │ │ └─────────────┘
│ ├───────────────────────┤ │ │ │
│ stream, connid, │ frame │ │ ┌─────┐ ┌─────┐
│ ├───────────────────────┤ │ │ │ │ │
│ path, pnspace, │ packet │ │───│ SMB │ │ NFS │...
│ ├───────────────────────┤ │ │ │ │ │
│ cong, crypto │ UDP tunnels │ │ └─────┘ └─────┘
└──────────────────┴───────────────────────┘ └──────┴───────┘- User Data Architecture:
┌──────┐ ┌──────┐
│ APP1 │ │ APP2 │ ...
└──────┘ └──────┘
{send/recvmsg()} {set/getsockopt()} {recvmsg()}
[CMSG stream_info] [SOCKOPT_KEY_UPDATE] [EVENT conn update]
[SOCKOPT_CONNECTION_MIGRATION] [EVENT stream update]
[SOCKOPT_STREAM_OPEN/RESET/STOP]
│ ^ │ ^ ^
Userspace │ │ │ │ │
──────────────│─│───────────────│─│─────────────────────│───────────
Kernel │ │ │ │ │
v │ v │ ┌──────────────────┘
┌──────────────────┬───────────────────────┐
│ protocol, timer, │ socket (IPPROTO_QUIC) │<──┐{kernel_send/recvmsg()}
│ ├───────────────────────┤ │{kernel_set/getsockopt()}
│ common, family, │ outqueue | inqueue │ │{kernel_recvmsg()}
│ ├───────────────────────┤ │
│ stream, connid, │ frame │ │ ┌─────┐ ┌─────┐
│ ├───────────────────────┤ │ │ │ │ │
│ path, pnspace, │ packet │ │───│ SMB │ │ NFS │...
│ ├───────────────────────┤ │ │ │ │ │
│ cong, crypto │ UDP tunnels │ │ └─────┘ └─────┘
└──────────────────┴───────────────────────┘ └──────┴───────┘Interface
=========
This implementation supports a mapping of QUIC into sockets APIs. Similar to TCP and SCTP, a typical Server and Client use the following system call sequence to communicate:
Client Server
────────────────────────────────────────────────────────────────────── sockfd = socket(IPPROTO_QUIC) listenfd = socket(IPPROTO_QUIC) bind(sockfd) bind(listenfd)
listen(listenfd)
connect(sockfd)
quic_client_handshake(sockfd)
sockfd = accecpt(listenfd) quic_server_handshake(sockfd, cert)
sendmsg(sockfd) recvmsg(sockfd)
close(sockfd) close(sockfd)
close(listenfd)
Please note that quic_client_handshake() and quic_server_handshake() functions are currently sourced from libquic [3]. These functions are responsible for receiving and processing the raw TLS handshake messages until the completion of the handshake process.
For utilization by kernel consumers, it is essential to have tlshd service [2] installed and running in userspace. This service receives and manages kernel handshake requests for kernel sockets. In the kernel, the APIs closely resemble those used in userspace:
Client Server
────────────────────────────────────────────────────────────────────────
__sock_create(IPPROTO_QUIC, &sock) __sock_create(IPPROTO_QUIC, &sock)
kernel_bind(sock) kernel_bind(sock)
kernel_listen(sock)
kernel_connect(sock)
tls_client_hello_x509(args:{sock})
kernel_accept(sock, &newsock)
tls_server_hello_x509(args:{newsock}) kernel_sendmsg(sock) kernel_recvmsg(newsock)
sock_release(sock) sock_release(newsock)
sock_release(sock)
Please be aware that tls_client_hello_x509() and tls_server_hello_x509() are APIs from net/handshake/. They are used to dispatch the handshake request to the userspace tlshd service and subsequently block until the handshake process is completed.
Use Cases
=========
- Samba
Stefan Metzmacher has submitted a merge request to integrate Linux QUIC into Samba for both client and server roles [4].
- tlshd
The tlshd daemon [2] facilitates Linux QUIC handshake requests from kernel sockets. This is essential for enabling protocols like SMB and NFS over QUIC.
- curl
Linux QUIC is being integrated into curl [5] for HTTP/3. Example usage:
# curl --http3-only https://nghttp2.org:4433/ # curl --http3-only https://www.google.com/ # curl --http3-only https://facebook.com/ # curl --http3-only https://outlook.office.com/ # curl --http3-only https://cloudflare-quic.com/
- httpd-portable
Moritz Buhl has deployed an HTTP/3 server over Linux QUIC [6] that is accessible via Firefox and curl:
Test Coverage
=============
The Coverage (gcov) of Functional and Interop Tests:
- Functional Tests
The libquic self-tests (make check) ran on all major architectures:
x86_64, i386, s390x, aarch64, ppc64le.
- Interop tests
Interoperability was validated using the QUIC Interop Runner [7] against all major userland QUIC stacks. Results are available at:
- Fuzzing via Syzkaller
Syzkaller has been running kernel fuzzing with QUIC for weeks using tests/syzkaller/ in libquic [3]..
- Performance Testing
Performance was benchmarked using iperf [8] over a 100G NIC with using various MTUs and packet sizes:
- QUIC vs. kTLS:
UNIT size:1024 size:4096 size:16384 size:65536
Gbits/sec QUIC | kTLS QUIC | kTLS QUIC | kTLS QUIC | kTLS
--------------------------------------------------------------------
mtu:1500 2.27 | 3.26 3.02 | 6.97 3.36 | 9.74 3.48 | 10.8
--------------------------------------------------------------------
mtu:9000 3.66 | 3.72 5.87 | 8.92 7.03 | 11.2 8.04 | 11.4 - QUIC(disable_1rtt_encryption) vs. TCP:
UNIT size:1024 size:4096 size:16384 size:65536
Gbits/sec QUIC | TCP QUIC | TCP QUIC | TCP QUIC | TCP
────────────────────────────────────────────────────────────────────
mtu:1500 3.09 | 4.59 4.46 | 14.2 5.07 | 21.3 5.18 | 23.9
────────────────────────────────────────────────────────────────────
mtu:9000 4.60 | 4.65 8.41 | 14.0 11.3 | 28.9 13.5 | 39.2The performance gap between QUIC and kTLS may be attributed to:
- The absence of Generic Segmentation Offload (GSO) for QUIC.
- An additional data copy on the transmission (TX) path.
- Extra encryption required for header protection in QUIC.
- A longer header length for the stream data in QUIC.
Patches
=======
Note: This implementation is organized into five parts and submitted across two patchsets for review. This patchset includes Parts 1–2, while Parts 3–5 will be submitted in a subsequent patchset. For complete series, see [9].
1. Infrastructure (2):
net: define IPPROTO_QUIC and SOL_QUIC constants
net: build socket infrastructure for QUIC protocol
2. Subcomponents (13):
quic: provide common utilities and data structures
quic: provide family ops for address and protocol
quic: provide quic.h header files for kernel and userspace
quic: add stream management
quic: add connection id management
quic: add path management
quic: add congestion control
quic: add packet number space
quic: add crypto key derivation and installation
quic: add crypto packet encryption and decryption
quic: add timer management
quic: add frame encoder and decoder base
quic: add packet builder and parser base
3. Data Processing (7):
quic: implement outqueue transmission and flow control
quic: implement outqueue sack and retransmission
quic: implement inqueue receiving and flow control
quic: implement frame creation functions
quic: implement frame processing functions
quic: implement packet creation functions
quic: implement packet processing functions
4. Socket APIs (6):
quic: support bind/listen/connect/accept/close()
quic: support sendmsg() and recvmsg()
quic: support socket options related to interaction after handshake
quic: support socket options related to settings prior to handshake
quic: support socket options related to setup during handshake
quic: support socket ioctls and socket dump via procfs
5. Example and Documentation (2):
quic: create sample test using handshake APIs for kernel consumers
Documentation: describe QUIC protocol interface in quic.rst
Notice:: The QUIC module is currently labeled as "EXPERIMENTAL".
All contributors are recognized in the respective patches with the tag of 'Signed-off-by:'. Special thanks to Moritz Buhl and Stefan Metzmacher whose practical use cases and insightful feedback, which have been instrumental in shaping the design and advancing the development.
References
==========
[1] https://datatracker.ietf.org/doc/html/draft-lxin-quic-socket-apis
[2] https://github.com/oracle/ktls-utils
[3] https://github.com/lxin/quic
[4] https://gitlab.com/samba-team/samba/-/merge_requests/4019
[5] https://github.com/moritzbuhl/curl/tree/linux_curl
[6] https://github.com/moritzbuhl/httpd-portable
[7] https://github.com/quic-interop/quic-interop-runner
[8] https://github.com/lxin/iperf
[9] https://github.com/lxin/net-next/commits/quic/
include/linux/quic.h | 19 + include/linux/socket.h | 1 + include/uapi/linux/in.h | 2 + include/uapi/linux/quic.h | 238 ++++++++ net/Kconfig | 1 + net/Makefile | 1 + net/quic/Kconfig | 35 ++ net/quic/Makefile | 9 + net/quic/common.c | 482 +++++++++++++++ net/quic/common.h | 219 +++++++ net/quic/cong.c | 700 +++++++++++++++++++++ net/quic/cong.h | 120 ++++ net/quic/connid.c | 218 +++++++ net/quic/connid.h | 162 +++++ net/quic/crypto.c | 1201 +++++++++++++++++++++++++++++++++++++ net/quic/crypto.h | 83 +++ net/quic/family.c | 666 ++++++++++++++++++++ net/quic/family.h | 40 ++ net/quic/frame.c | 558 +++++++++++++++++ net/quic/frame.h | 192 ++++++ net/quic/packet.c | 889 +++++++++++++++++++++++++++ net/quic/packet.h | 129 ++++ net/quic/path.c | 507 ++++++++++++++++ net/quic/path.h | 162 +++++ net/quic/pnspace.c | 224 +++++++ net/quic/pnspace.h | 150 +++++ net/quic/protocol.c | 404 +++++++++++++ net/quic/protocol.h | 58 ++ net/quic/socket.c | 424 +++++++++++++ net/quic/socket.h | 221 +++++++ net/quic/stream.c | 549 +++++++++++++++++ net/quic/stream.h | 135 +++++ net/quic/timer.c | 196 ++++++ net/quic/timer.h | 47 ++ 34 files changed, 9042 insertions(+) create mode 100644 include/linux/quic.h create mode 100644 include/uapi/linux/quic.h create mode 100644 net/quic/Kconfig create mode 100644 net/quic/Makefile create mode 100644 net/quic/common.c create mode 100644 net/quic/common.h create mode 100644 net/quic/cong.c create mode 100644 net/quic/cong.h create mode 100644 net/quic/connid.c create mode 100644 net/quic/connid.h create mode 100644 net/quic/crypto.c create mode 100644 net/quic/crypto.h create mode 100644 net/quic/family.c create mode 100644 net/quic/family.h create mode 100644 net/quic/frame.c create mode 100644 net/quic/frame.h create mode 100644 net/quic/packet.c create mode 100644 net/quic/packet.h create mode 100644 net/quic/path.c create mode 100644 net/quic/path.h create mode 100644 net/quic/pnspace.c create mode 100644 net/quic/pnspace.h create mode 100644 net/quic/protocol.c create mode 100644 net/quic/protocol.h create mode 100644 net/quic/socket.c create mode 100644 net/quic/socket.h create mode 100644 net/quic/stream.c create mode 100644 net/quic/stream.h create mode 100644 net/quic/timer.c create mode 100644 net/quic/timer.h
Signature · 1 line
2.47.1
PATCH 1/15 net: define IPPROTO_QUIC and SOL_QUIC constants
This patch adds IPPROTO_QUIC and SOL_QUIC constants to the networking subsystem. These definitions are essential for applications to set socket options and protocol identifiers related to the QUIC protocol.
QUIC does not possess a protocol number allocated from IANA, and like IPPROTO_MPTCP, IPPROTO_QUIC is merely a value used when opening a QUIC socket with:
socket(AF_INET, SOCK_STREAM, IPPROTO_QUIC);
Note we did not opt for UDP ULP for QUIC implementation due to several considerations:
- QUIC's connection Migration requires at least 2 UDP sockets for one QUIC connection at the same time, not to mention the multipath feature in one of its draft RFCs.
- In-Kernel QUIC, as a Transport Protocol, wants to provide users with the TCP or SCTP like Socket APIs, like connect()/listen()/accept()... Note that a single UDP socket might even be used for multiple QUIC connections.
The use of IPPROTO_QUIC type sockets over UDP tunnel will effectively address these challenges and provides a more flexible and scalable solution.
--- include/linux/socket.h | 1 + include/uapi/linux/in.h | 2 ++ 2 files changed, 3 insertions(+)
include/linux/socket.h +1 -0
@@ -386,6 +386,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
include/uapi/linux/in.h +2 -0
@@ -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 and Secure Transport */ +#define IPPROTO_QUIC IPPROTO_QUIC IPPROTO_MPTCP = 262, /* Multipath TCP connection */#define IPPROTO_MPTCP IPPROTO_MPTCPIPPROTO_MAX
Signature · 1 line
2.47.1
PATCH 2/15 net: build socket infrastructure for QUIC protocol
In reply to Xin LongThis patch lays the groundwork for QUIC socket support in the kernel. It defines the core structures and protocol hooks needed to create QUIC sockets, without implementing any protocol behavior at this stage.
Basic integration is included to allow building the module via CONFIG_IP_QUIC=m.
This provides the scaffolding necessary for adding actual QUIC socket behavior in follow-up patches.
--- net/Kconfig | 1 + net/Makefile | 1 + net/quic/Kconfig | 35 +++++ net/quic/Makefile | 8 + net/quic/protocol.c | 370 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/protocol.h | 55 +++++++ net/quic/socket.c | 213 +++++++++++++++++++++++++ net/quic/socket.h | 87 +++++++++++ 8 files changed, 770 insertions(+) create mode 100644 net/quic/Kconfig create mode 100644 net/quic/Makefile create mode 100644 net/quic/protocol.c create mode 100644 net/quic/protocol.h create mode 100644 net/quic/socket.c create mode 100644 net/quic/socket.h
net/Kconfig +1 -0
@@ -249,6 +249,7 @@ source "net/bridge/netfilter/Kconfig" endif +source "net/quic/Kconfig" source "net/sctp/Kconfig" source "net/rds/Kconfig" source "net/tipc/Kconfig"
net/Makefile +1 -0
@@ -42,6 +42,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/
net/quic/Kconfig new file +35 -0
@@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# QUIC configuration +# + +menuconfig IP_QUIC + tristate "QUIC: A UDP-Based Multiplexed and Secure Transport (Experimental)" + depends on INET + depends on IPV6 + select CRYPTO + select CRYPTO_HMAC + select CRYPTO_HKDF + select CRYPTO_AES + select CRYPTO_GCM + select CRYPTO_CCM + select CRYPTO_CHACHA20POLY1305 + select NET_UDP_TUNNEL + 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.
net/quic/Makefile new file +8 -0
@@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for QUIC support code. +# + +obj-$(CONFIG_IP_QUIC) += quic.o + +quic-y := protocol.o socket.o
net/quic/protocol.c new file +370 -0
370 lines not on this page · raw message
net/quic/protocol.h new file +55 -0
55 lines not on this page · raw message
net/quic/socket.c new file +213 -0
213 lines not on this page · raw message
net/quic/socket.h new file +87 -0
87 lines not on this page · raw message
Signature · 1 line
2.47.1
PATCH 3/15 quic: provide common utilities and data structures
In reply to Xin LongThis patch provides foundational data structures and utilities used throughout the QUIC stack.
It introduces packet header types, connection ID support, and address handling. Hash tables are added to manage socket lookup and connection ID mapping.
A flexible binary data type is provided, along with helpers for parsing, matching, and memory management. Helpers for encoding and decoding transport parameters and frames are also included.
--- net/quic/Makefile | 2 +- net/quic/common.c | 482 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/common.h | 219 ++++++++++++++++++++ net/quic/protocol.c | 6 + net/quic/socket.c | 4 + net/quic/socket.h | 21 ++ 6 files changed, 733 insertions(+), 1 deletion(-) create mode 100644 net/quic/common.c create mode 100644 net/quic/common.h
net/quic/Makefile +1 -1
@@ -5,4 +5,4 @@ obj-$(CONFIG_IP_QUIC) += quic.o -quic-y := protocol.o socket.o +quic-y := common.o protocol.o socket.o
net/quic/common.c new file +482 -0
482 lines not on this page · raw message
net/quic/common.h new file +219 -0
219 lines not on this page · raw message
net/quic/protocol.c +6 -0
@@ -327,6 +327,9 @@ static __init int quic_init(void)if (err) goto err_percpu_counter; +if (quic_hash_tables_init ()) + goto err_hash; + err =register_pernet_subsys (&quic_net_ops);if (err) goto err_def_ops;
@@ -344,6 +347,8 @@ static __init int quic_init(void) err_protosw:unregister_pernet_subsys (&quic_net_ops); err_def_ops: +quic_hash_tables_destroy (); +err_hash:percpu_counter_destroy (&quic_sockets_allocated); err_percpu_counter:return err;
@@ -356,6 +361,7 @@ static __exit void quic_exit(void)#endif quic_protosw_exit ();unregister_pernet_subsys (&quic_net_ops); +quic_hash_tables_destroy ();percpu_counter_destroy (&quic_sockets_allocated);pr_info ("quic: exit\n" ); }
net/quic/socket.c +4 -0
@@ -55,6 +55,10 @@ static int quic_init_sock(struct sock *sk)static void quic_destroy_sock (struct sock *sk) { +quic_data_free (quic_ticket (sk)); +quic_data_free (quic_token (sk)); +quic_data_free (quic_alpn (sk)); +local_bh_disable ();sk_sockets_allocated_dec (sk);sock_prot_inuse_add (sock_net (sk), sk->sk_prot,-1 );
net/quic/socket.h +21 -0
@@ -13,6 +13,8 @@#include <net/udp_tunnel.h> +#include "common.h" +#include "protocol.h" extern struct proto quic_prot;
@@ -28,6 +30,10 @@ enum quic_state {struct quic_sock {struct inet_sock inet;struct list_head reqs; + +struct quic_data ticket; +struct quic_data token; +struct quic_data alpn; };struct quic6_sock {
@@ -45,6 +51,21 @@ static inline struct list_head *quic_reqs(const struct sock *sk)return &quic_sk (sk)->reqs; } +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 bool quic_is_establishing (struct sock *sk) {return sk->sk_state ==QUIC_SS_ESTABLISHING ;
Signature · 1 line
2.47.1
PATCH 4/15 quic: provide family ops for address and protocol
In reply to Xin LongThis patch introduces two new abstraction structures to simplify handling of IPv4 and IPv6 differences across the QUIC stack:
- quic_addr_family_ops: for address comparison, flow routing, UDP config, MTU lookup, formatted output, etc.
- quic_proto_family_ops: for socket address helpers and preference.
With these additions, the QUIC core logic can remain agnostic of the address family and socket type, improving modularity and reducing repetitive checks throughout the codebase.
--- net/quic/Makefile | 2 +- net/quic/family.c | 666 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/family.h | 40 +++ net/quic/protocol.c | 2 +- net/quic/socket.c | 4 +- net/quic/socket.h | 1 + 6 files changed, 711 insertions(+), 4 deletions(-) create mode 100644 net/quic/family.c create mode 100644 net/quic/family.h
net/quic/Makefile +1 -1
@@ -5,4 +5,4 @@ obj-$(CONFIG_IP_QUIC) += quic.o -quic-y := common.o protocol.o socket.o +quic-y := common.o family.o protocol.o socket.o
net/quic/family.c new file +666 -0
666 lines not on this page · raw message
net/quic/family.h new file +40 -0
@@ -0,0 +1,40 @@ +/* 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 <[email protected]> +*/ + +#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) + +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); +int quic_is_any_addr (union quic_addr *a); +u32 quic_encap_len (union quic_addr *a); + +void quic_get_msg_addrs (union quic_addr *da,union quic_addr *sa,struct sk_buff *skb); +void quic_get_pref_addr (struct sock *sk,union quic_addr *addr,u8 **pp,u32 *plen); +void quic_set_pref_addr (struct sock *sk,u8 *p,union quic_addr *addr); +void quic_seq_dump_addr (struct seq_file *seq,union quic_addr *addr); + +int quic_get_user_addr (struct sock *sk,union quic_addr *a,struct sockaddr *addr,int addr_len); +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,bool peer); +void quic_set_sk_addr (struct sock *sk,union quic_addr *a,bool src); + +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); +int quic_get_mtu_info (struct sk_buff *skb,u32 *info); +void quic_set_sk_ecn (struct sock *sk,u8 ecn); +u8 quic_get_msg_ecn (struct sk_buff *skb);
net/quic/protocol.c +1 -1
@@ -47,7 +47,7 @@ static int quic_inet_listen(struct socket *sock, int backlog)static int quic_inet_getname (struct socket *sock,struct sockaddr *uaddr,int peer) { -return -EOPNOTSUPP ; +return quic_get_sk_addr (sock, uaddr, peer); }static __poll_t quic_inet_poll (struct file *file,struct socket *sock,poll_table *wait)
net/quic/socket.c +2 -2
@@ -121,7 +121,7 @@ static int quic_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval, unsigned int optlen) {if (level !=SOL_QUIC ) -return -EOPNOTSUPP ; +return quic_common_setsockopt (sk, level, optname, optval, optlen);return quic_do_setsockopt (sk, optname, optval, optlen); }
@@ -135,7 +135,7 @@ static int quic_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) {if (level !=SOL_QUIC ) -return -EOPNOTSUPP ; +return quic_common_getsockopt (sk, level, optname, optval, optlen);return quic_do_getsockopt (sk, optname,USER_SOCKPTR (optval),USER_SOCKPTR (optlen)); }
net/quic/socket.h +1 -0
@@ -14,6 +14,7 @@#include <net/udp_tunnel.h> #include "common.h" +#include "family.h" #include "protocol.h"
Signature · 1 line
2.47.1
PATCH 5/15 quic: provide quic.h header files for kernel and userspace
In reply to Xin LongThis commit adds quic.h to include/uapi/linux, providing the necessary definitions for the QUIC socket API. Exporting this header allows both user space applications and kernel subsystems to access QUIC-related control messages, socket options, and event/notification interfaces.
Since kernel_get/setsockopt() is no longer available to kernel consumers, a corresponding internal header, include/linux/quic.h, is added. This provides kernel subsystems with the necessary declarations to handle QUIC socket options directly.
Detailed descriptions of these structures are available in [1], and will be also provided when adding corresponding socket interfaces in the later patches.
[1] https://datatracker.ietf.org/doc/html/draft-lxin-quic-socket-apis
--- include/linux/quic.h | 19 +++ include/uapi/linux/quic.h | 238 ++++++++++++++++++++++++++++++++++++++ net/quic/socket.c | 38 ++++++ net/quic/socket.h | 7 ++ 4 files changed, 302 insertions(+) create mode 100644 include/linux/quic.h create mode 100644 include/uapi/linux/quic.h
include/linux/quic.h new file +19 -0
@@ -0,0 +1,19 @@ +/* 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 <[email protected]> +*/ + +#ifndef __linux_quic_h__ +#define __linux_quic_h__ + +#include <uapi/linux/quic.h> + +int quic_kernel_setsockopt (struct sock *sk,int optname,void *optval,unsigned int optlen); +int quic_kernel_getsockopt (struct sock *sk,int optname,void *optval,unsigned int *optlen); + +#endif
include/uapi/linux/quic.h new file +238 -0
238 lines not on this page · raw message
net/quic/socket.c +38 -0
@@ -126,6 +126,25 @@ static int quic_setsockopt(struct sock *sk, int level, int optname,return quic_do_setsockopt (sk, optname, optval, optlen); } +/** +* quic_kernel_setsockopt - set a QUIC socket option from within the kernel +* @sk: socket to configure +* @optname: option name (QUIC-level) +* @optval: pointer to the option value +* @optlen: size of the option value +* +* Sets a QUIC socket option on a kernel socket without involving user space. +* +* Return values: +* - On success, 0 is returned. +* - On error, a negative error value is returned. +*/ +int quic_kernel_setsockopt (struct sock *sk,int optname,void *optval,unsigned int optlen) +{ +return quic_do_setsockopt (sk, optname,KERNEL_SOCKPTR (optval), optlen); +} +EXPORT_SYMBOL_GPL (quic_kernel_setsockopt); +static int quic_do_getsockopt (struct sock *sk,int optname,sockptr_t optval,sockptr_t optlen) {return -EOPNOTSUPP ;
@@ -140,6 +159,25 @@ static int quic_getsockopt(struct sock *sk, int level, int optname,return quic_do_getsockopt (sk, optname,USER_SOCKPTR (optval),USER_SOCKPTR (optlen)); } +/** +* quic_kernel_getsockopt - get a QUIC socket option from within the kernel +* @sk: socket to query +* @optname: option name (QUIC-level) +* @optval: pointer to the buffer to receive the option value +* @optlen: pointer to the size of the buffer; updated to actual length on return +* +* Gets a QUIC socket option from a kernel socket, bypassing user space. +* +* Return values: +* - On success, 0 is returned. +* - On error, a negative error value is returned. +*/ +int quic_kernel_getsockopt (struct sock *sk,int optname,void *optval,unsigned int *optlen) +{ +return quic_do_getsockopt (sk, optname,KERNEL_SOCKPTR (optval),KERNEL_SOCKPTR (optlen)); +} +EXPORT_SYMBOL_GPL (quic_kernel_getsockopt); +static void quic_release_cb (struct sock *sk) { }
net/quic/socket.h +7 -0
@@ -12,6 +12,7 @@#define __net_quic_h__#include <net/udp_tunnel.h> +#include <linux/quic.h> #include "common.h" #include "family.h"
@@ -32,6 +33,7 @@ struct quic_sock {struct inet_sock inet;struct list_head reqs; +struct quic_config config;struct quic_data ticket;struct quic_data token;struct quic_data alpn;
@@ -52,6 +54,11 @@ static inline struct list_head *quic_reqs(const struct sock *sk)return &quic_sk (sk)->reqs; } +static inline struct quic_config *quic_config (const struct sock *sk) +{ +return &quic_sk (sk)->config; +} +static inline struct quic_data *quic_token (const struct sock *sk) {return &quic_sk (sk)->token;
Signature · 1 line
2.47.1
Jakub Kicinski
maintainerRe: [PATCH net-next 05/15] quic: provide quic.h header files for kernel and userspace
Quoted 5 lines · On Sat, 5 Jul 2025 15:31:44 -0400 Xin Long wrote:
This commit adds
quic.htoinclude/uapi/linux, providing the necessary definitions for the QUIC socket API. Exporting this header allows both user space applications and kernel subsystems to access QUIC-related control messages, socket options, and event/notification interfaces.
Quoted 8 lines
Since kernel_get/setsockopt() is no longer available to kernel consumers, a corresponding internal header,
include/linux/quic.h, is added. This provides kernel subsystems with the necessary declarations to handle QUIC socket options directly.Detailed descriptions of these structures are available in [1], and will be also provided when adding corresponding socket interfaces in the later patches.
Warning: net/quic/socket.c:142 No description found for return value of 'quic_kernel_setsockopt'
Warning: net/quic/socket.c:175 No description found for return value of 'quic_kernel_getsockopt'
Signature · 1 line
pw-bot: cr
Re: [PATCH net-next 05/15] quic: provide quic.h header files for kernel and userspace
Quoted 12 lines · Jakub Kicinski
Quoted 10 lines · On Sat, 5 Jul 2025 15:31:44 -0400 Xin Long wrote:
This commit adds
quic.htoinclude/uapi/linux, providing the necessary definitions for the QUIC socket API. Exporting this header allows both user space applications and kernel subsystems to access QUIC-related control messages, socket options, and event/notification interfaces.Since kernel_get/setsockopt() is no longer available to kernel consumers, a corresponding internal header,
include/linux/quic.h, is added. This provides kernel subsystems with the necessary declarations to handle QUIC socket options directly.
Quoted 6 lines
Quoted 3 lines
Detailed descriptions of these structures are available in [1], and will be also provided when adding corresponding socket interfaces in the later patches.
Warning:
net/quic/socket.c:142No description found for return value of 'quic_kernel_setsockopt'
Warning:net/quic/socket.c:175No description found for return value of 'quic_kernel_getsockopt'
It seems kernel-doc only recognizes "Return:", I will replace "Return values:" with it.
Thanks.
Re: [PATCH net-next 05/15] quic: provide quic.h header files for kernel and userspace
In reply to Xin Longinclude/uapi/linux/quic.h line 12
Quoted 2 lines · Xin Long
include/uapi/linux/quic.h new file +2
-0+#ifndef __uapi_quic_h__ +#define __uapi_quic_h__
I think capital letters are preferred for system headers, e.g.:_UAPI_LINUX_QUIC_H.
include/uapi/linux/quic.h line 55
Quoted 7 lines
include/uapi/linux/quic.h new file +7
-0+enum quic_crypto_level { +QUIC_CRYPTO_APP , +QUIC_CRYPTO_INITIAL , +QUIC_CRYPTO_HANDSHAKE , +QUIC_CRYPTO_EARLY , +QUIC_CRYPTO_MAX , +};
I would recommend that you assign explicit values to enums in uapi headers to avoid accidental changes if someone tries inserting in the middle of the list.
include/uapi/linux/quic.h line 89
Quoted 4 lines
include/uapi/linux/quic.h new file +4
-0+struct quic_transport_param { +uint8_t remote; +uint8_t disable_active_migration; +uint8_t grease_quic_bit;
I believe use of __u8, __u16, __s32 and similar is preferred to uint8_t, uint16_t, int32_t, etc. in UAPI headers.
include/uapi/linux/quic.h line 214
Quoted 4 lines
include/uapi/linux/quic.h new file +4
-0+enum { +QUIC_TRANSPORT_ERROR_NONE , +QUIC_TRANSPORT_ERROR_INTERNAL , +QUIC_TRANSPORT_ERROR_CONNECTION_REFUSED ,
Again, recommend assigning actual values.
David
Re: [PATCH net-next 05/15] quic: provide quic.h header files for kernel and userspace
Quoted 9 lines · David Howells
include/uapi/linux/quic.h line 12
Quoted 2 lines · Xin Long
include/uapi/linux/quic.h new file +2
-0+#ifndef __uapi_quic_h__ +#define __uapi_quic_h__I think capital letters are preferred for system headers, e.g.:
_UAPI_LINUX_QUIC_H.
Looks better, will update.
Quoted 11 lines
include/uapi/linux/quic.h line 55
Quoted 7 lines
include/uapi/linux/quic.h new file +7
-0+enum quic_crypto_level { +QUIC_CRYPTO_APP , +QUIC_CRYPTO_INITIAL , +QUIC_CRYPTO_HANDSHAKE , +QUIC_CRYPTO_EARLY , +QUIC_CRYPTO_MAX , +};I would recommend that you assign explicit values to enums in uapi headers to avoid accidental changes if someone tries inserting in the middle of the list.
That doesn't sound like an easy mistake to make. Assigning explicit values to zero-based, continuous enums isn’t commonly seen in other header files either.
But I will make such a change for QUIC_TRANSPORT_ enums, as their values are actually defined in:
https://datatracker.ietf.org/doc/html/rfc9000#section-20.1
Quoted 8 lines
include/uapi/linux/quic.h line 89
Quoted 4 lines
include/uapi/linux/quic.h new file +4
-0+struct quic_transport_param { +uint8_t remote; +uint8_t disable_active_migration; +uint8_t grease_quic_bit;I believe use of
__u8,__u16,__s32and similar is preferred touint8_t,uint16_t,int32_t, etc. in UAPI headers.
Make sense, will update.
Thanks for the suggestions.
Quoted 9 lines
include/uapi/linux/quic.h line 214
Quoted 4 lines
include/uapi/linux/quic.h new file +4
-0+enum { +QUIC_TRANSPORT_ERROR_NONE , +QUIC_TRANSPORT_ERROR_INTERNAL , +QUIC_TRANSPORT_ERROR_CONNECTION_REFUSED ,Again, recommend assigning actual values.
David
PATCH 6/15 quic: add stream management
In reply to Xin LongThis patch introduces 'struct quic_stream_table' for managing QUIC streams, each represented by 'struct quic_stream'.
It implements mechanisms for acquiring and releasing streams on both the send and receive paths, ensuring efficient lifecycle management during transmission and reception.
- quic_stream_send_get(): Acquire a send-side stream by ID and flags during TX path.
- quic_stream_recv_get(): Acquire a receive-side stream by ID during RX path.
- quic_stream_send_put(): Release a send-side stream when sending is done.
- quic_stream_recv_put(): Release a receive-side stream when receiving is done.
It includes logic to detect when stream ID limits are reached and when control frames should be sent to update or request limits from the peer.
- quic_stream_id_send_exceeds(): Determines whether a STREAMS_BLOCKED_UNI/BIDI frame should be sent to the peer.
- quic_stream_max_streams_update(): Determines whether a MAX_STREAMS_UNI/BIDI frame should be sent to the peer.
--- net/quic/Makefile | 2 +- net/quic/socket.c | 5 + net/quic/socket.h | 8 + net/quic/stream.c | 549 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/stream.h | 135 ++++++++++++ 5 files changed, 698 insertions(+), 1 deletion(-) create mode 100644 net/quic/stream.c create mode 100644 net/quic/stream.h
net/quic/Makefile +1 -1
@@ -5,4 +5,4 @@ obj-$(CONFIG_IP_QUIC) += quic.o -quic-y := common.o family.o protocol.o socket.o +quic-y := common.o family.o protocol.o socket.o stream.o
net/quic/socket.c +5 -0
@@ -42,6 +42,9 @@ static int quic_init_sock(struct sock *sk) sk->sk_write_space = quic_write_space;sock_set_flag (sk,SOCK_USE_WRITE_QUEUE ); +if (quic_stream_init (quic_streams (sk))) +return -ENOMEM ; +WRITE_ONCE (sk->sk_sndbuf,READ_ONCE (sysctl_quic_wmem[1 ]));WRITE_ONCE (sk->sk_rcvbuf,READ_ONCE (sysctl_quic_rmem[1 ]));
@@ -55,6 +58,8 @@ static int quic_init_sock(struct sock *sk)static void quic_destroy_sock (struct sock *sk) { +quic_stream_free (quic_streams (sk)); +quic_data_free (quic_ticket (sk));quic_data_free (quic_token (sk));quic_data_free (quic_alpn (sk));
net/quic/socket.h +8 -0
@@ -16,6 +16,7 @@#include "common.h" #include "family.h" +#include "stream.h" #include "protocol.h"
@@ -37,6 +38,8 @@ struct quic_sock {struct quic_data ticket;struct quic_data token;struct quic_data alpn; + +struct quic_stream_table streams; };struct quic6_sock {
@@ -74,6 +77,11 @@ 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 bool quic_is_establishing (struct sock *sk) {return sk->sk_state ==QUIC_SS_ESTABLISHING ;
net/quic/stream.c new file +549 -0
549 lines not on this page · raw message
net/quic/stream.h new file +135 -0
135 lines not on this page · raw message
Signature · 1 line
2.47.1
PATCH 7/15 quic: add connection id management
In reply to Xin LongThis patch introduces 'struct quic_conn_id_set' for managing Connection IDs (CIDs), which are represented by 'struct quic_source_conn_id' and 'struct quic_dest_conn_id'.
It provides helpers to add and remove CIDs from the set, and handles insertion of source CIDs into the global connection ID hash table when necessary.
- quic_conn_id_add(): Add a new Connection ID to the set, and inserts it to conn_id hash table if it is a source conn_id.
- quic_conn_id_remove(): Remove connection IDs the set with sequence numbers less than or equal to a number.
It also adds utilities to look up CIDs by value or sequence number, search the global hash table for incoming packets, and check for stateless reset tokens among destination CIDs. These functions are essential for RX path connection lookup and stateless reset processing.
- quic_conn_id_find(): Find a Connection ID in the set by seq number.
- quic_conn_id_lookup(): Lookup a Connection ID from global hash table using the ID value, typically used for socket lookup on the RX path.
- quic_conn_id_token_exists(): Check if a stateless reset token exists in any dest Connection ID (used during stateless reset processing).
--- net/quic/Makefile | 2 +- net/quic/connid.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/connid.h | 162 ++++++++++++++++++++++++++++++++++ net/quic/socket.c | 6 ++ net/quic/socket.h | 13 +++ 5 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 net/quic/connid.c create mode 100644 net/quic/connid.h
net/quic/Makefile +1 -1
@@ -5,4 +5,4 @@ obj-$(CONFIG_IP_QUIC) += quic.o -quic-y := common.o family.o protocol.o socket.o stream.o +quic-y := common.o family.o protocol.o socket.o stream.o connid.o
net/quic/connid.c new file +218 -0
218 lines not on this page · raw message
net/quic/connid.h new file +162 -0
162 lines not on this page · raw message
net/quic/socket.c +6 -0
@@ -42,6 +42,9 @@ static int quic_init_sock(struct sock *sk) sk->sk_write_space = quic_write_space;sock_set_flag (sk,SOCK_USE_WRITE_QUEUE ); +quic_conn_id_set_init (quic_source (sk),1 ); +quic_conn_id_set_init (quic_dest (sk),0 ); +if (quic_stream_init (quic_streams (sk)))return -ENOMEM ;
@@ -58,6 +61,9 @@ static int quic_init_sock(struct sock *sk)static void quic_destroy_sock (struct sock *sk) { +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));
net/quic/socket.h +13 -0
@@ -17,6 +17,7 @@#include "common.h" #include "family.h" #include "stream.h" +#include "connid.h" #include "protocol.h"
@@ -40,6 +41,8 @@ struct quic_sock {struct quic_data alpn;struct quic_stream_table streams; +struct quic_conn_id_set source; +struct quic_conn_id_set dest; };struct quic6_sock {
@@ -82,6 +85,16 @@ 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 bool quic_is_establishing (struct sock *sk) {return sk->sk_state ==QUIC_SS_ESTABLISHING ;
Signature · 1 line
2.47.1
PATCH 8/15 quic: add path management
In reply to Xin LongThis patch introduces 'quic_path_group' for managing paths, represented by 'struct quic_path'. A connection may use two paths simultaneously for connection migration.
Each path is associated with a UDP tunnel socket (sk), and a single UDP tunnel socket can be related to multiple paths from different sockets. These UDP tunnel sockets are wrapped in 'quic_udp_sock' structures and stored in a hash table.
It includes mechanisms to bind and unbind paths, detect alternative paths for migration, and swap paths to support seamless transition between networks.
- quic_path_bind(): Bind a path to a port and associate it with a UDP sk.
- quic_path_free(): Unbind a path from a port and disassociate it from a UDP sk.
- quic_path_swap(): Swap two paths to facilitate connection migration.
- quic_path_detect_alt(): Determine if a packet is using an alternative path, used for connection migration.
It also integrates basic support for Packetization Layer Path MTU
Discovery (PLPMTUD), using PING frames and ICMP feedback to adjust path MTU and handle probe confirmation or resets during routing changes.
- quic_path_pl_recv(): state transition and pmtu update after the probe packet is acked.
- quic_path_pl_toobig(): state transition and pmtu update after receiving a toobig or needfrag icmp packet.
- quic_path_pl_send(): state transition and pmtu update after sending a probe packet.
- quic_path_pl_reset(): restart the probing when path routing changes.
- quic_path_pl_confirm(): check if probe packet gets acked.
--- net/quic/Makefile | 2 +- net/quic/path.c | 507 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/path.h | 162 +++++++++++++++ net/quic/socket.c | 3 + net/quic/socket.h | 21 +- 5 files changed, 692 insertions(+), 3 deletions(-) create mode 100644 net/quic/path.c create mode 100644 net/quic/path.h
net/quic/Makefile +1 -1
@@ -5,4 +5,4 @@ obj-$(CONFIG_IP_QUIC) += quic.o -quic-y := common.o family.o protocol.o socket.o stream.o connid.o +quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o
net/quic/path.c new file +507 -0
507 lines not on this page · raw message
net/quic/path.h new file +162 -0
162 lines not on this page · raw message
net/quic/socket.c +3 -0
@@ -61,6 +61,9 @@ static int quic_init_sock(struct sock *sk)static void quic_destroy_sock (struct sock *sk) { +quic_path_free (sk,quic_paths (sk),0 ); +quic_path_free (sk,quic_paths (sk),1 ); +quic_conn_id_set_free (quic_source (sk));quic_conn_id_set_free (quic_dest (sk));
net/quic/socket.h +19 -2
@@ -18,6 +18,7 @@#include "family.h" #include "stream.h" #include "connid.h" +#include "path.h" #include "protocol.h"
@@ -43,6 +44,7 @@ struct quic_sock {struct quic_stream_table streams;struct quic_conn_id_set source;struct quic_conn_id_set dest; +struct quic_path_group paths; };struct quic6_sock {
@@ -95,6 +97,16 @@ 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 quic_paths (sk)->serv; +} +static inline bool quic_is_establishing (struct sock *sk) {return sk->sk_state ==QUIC_SS_ESTABLISHING ;
@@ -118,14 +130,19 @@ static inline bool quic_is_closed(struct sock *sk)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 ) +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)) + }else if (quic_is_established (sk)) {QUIC_DEC_STATS (net,QUIC_MIB_CONN_CURRENTESTABS ); + }if (state ==QUIC_SS_CLOSED ) sk->sk_prot->unhash (sk);
Signature · 1 line
2.47.1
PATCH 9/15 quic: add congestion control
In reply to Xin LongThis patch introduces 'quic_cong' for RTT measurement and congestion control. The 'quic_cong_ops' is added to define the congestion control algorithm.
It implements a congestion control state machine with slow start, congestion avoidance, and recovery phases, and introduces the New Reno and CUBIC algorithms.
The implementation updates RTT estimates when packets are acknowledged, reacts to loss and ECN signals, and adjusts the congestion window accordingly during packet transmission and acknowledgment processing.
- quic_cong_rtt_update(): Performs RTT measurement, invoked when a packet is acknowledged by the largest number in the ACK frame.
- quic_cong_on_packet_acked(): Invoked when a packet is acknowledged.
- quic_cong_on_packet_lost(): Invoked when a packet is marked as lost.
- quic_cong_on_process_ecn(): Invoked when an ACK_ECN frame is received.
- quic_cong_on_packet_sent(): Invoked when a packet is transmitted.
- quic_cong_on_ack_recv(): Invoked when an ACK frame is received.
--- net/quic/Makefile | 3 +- net/quic/cong.c | 700 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/cong.h | 120 ++++++++ net/quic/socket.c | 1 + net/quic/socket.h | 7 + 5 files changed, 830 insertions(+), 1 deletion(-) create mode 100644 net/quic/cong.c create mode 100644 net/quic/cong.h
net/quic/Makefile +2 -1
@@ -5,4 +5,5 @@ obj-$(CONFIG_IP_QUIC) += quic.o -quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o +quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o \ + cong.o
net/quic/cong.c new file +700 -0
700 lines not on this page · raw message
net/quic/cong.h new file +120 -0
120 lines not on this page · raw message
net/quic/socket.c +1 -0
@@ -44,6 +44,7 @@ static int quic_init_sock(struct sock *sk)quic_conn_id_set_init (quic_source (sk),1 );quic_conn_id_set_init (quic_dest (sk),0 ); +quic_cong_init (quic_cong (sk));if (quic_stream_init (quic_streams (sk)))return -ENOMEM ;
net/quic/socket.h +7 -0
@@ -19,6 +19,7 @@#include "stream.h" #include "connid.h" #include "path.h" +#include "cong.h" #include "protocol.h"
@@ -45,6 +46,7 @@ struct quic_sock {struct quic_conn_id_set source;struct quic_conn_id_set dest;struct quic_path_group paths; +struct quic_cong cong; };struct quic6_sock {
@@ -107,6 +109,11 @@ static inline bool quic_is_serv(const struct sock *sk)return quic_paths (sk)->serv; } +static inline struct quic_cong *quic_cong (const struct sock *sk) +{ +return &quic_sk (sk)->cong; +} +static inline bool quic_is_establishing (struct sock *sk) {return sk->sk_state ==QUIC_SS_ESTABLISHING ;
Signature · 1 line
2.47.1
PATCH 10/15 quic: add packet number space
In reply to Xin LongThis patch introduces 'quic_pnspace', which manages per packet number space members.
It maintains the next packet number to assign, tracks the total length of frames currently in flight, and records the time when the next packet may be considered lost. It also keeps track of the largest acknowledged packet number, the time it was acknowledged, and when the most recent ack eliciting packet was sent. These fields are useful for loss detection, RTT estimation, and congestion control.
To support ACK frame generation, quic_pnspace includes a packet number acknowledgment map (pn_ack_map) that tracks received packet numbers. Supporting functions are provided to validate and mark received packet numbers and compute the number of gap blocks needed during ACK frame construction.
- quic_pnspace_check(): Validates a received packet number.
- quic_pnspace_mark(): Marks a received packet number in the ACK map.
- quic_pnspace_num_gabs(): Returns the gap ACK blocks for constructing ACK frames.
Note QUIC uses separate packet number spaces for each encryption level (APP, INITIAL, HANDSHAKE, EARLY) except EARLY and all generations of APP keys use the same packet number space, as describe in rfc9002#section-4.1.
--- net/quic/Makefile | 2 +- net/quic/pnspace.c | 224 +++++++++++++++++++++++++++++++++++++++++++++ net/quic/pnspace.h | 150 ++++++++++++++++++++++++++++++ net/quic/socket.c | 12 +++ net/quic/socket.h | 7 ++ 5 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 net/quic/pnspace.c create mode 100644 net/quic/pnspace.h
net/quic/Makefile +1 -1
@@ -6,4 +6,4 @@ obj-$(CONFIG_IP_QUIC) += quic.o quic-y := common.o family.o protocol.o socket.o stream.o connid.o path.o \ - cong.o + cong.o pnspace.o
net/quic/pnspace.c new file +224 -0
224 lines not on this page · raw message
net/quic/pnspace.h new file +150 -0
150 lines not on this page · raw message
net/quic/socket.c +12 -0
@@ -38,6 +38,8 @@ static void quic_write_space(struct sock *sk)static int quic_init_sock (struct sock *sk) { +u8 i; + sk->sk_destruct = inet_sock_destruct; sk->sk_write_space = quic_write_space;sock_set_flag (sk,SOCK_USE_WRITE_QUEUE );
@@ -49,6 +51,11 @@ static int quic_init_sock(struct sock *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 ; + } +WRITE_ONCE (sk->sk_sndbuf,READ_ONCE (sysctl_quic_wmem[1 ]));WRITE_ONCE (sk->sk_rcvbuf,READ_ONCE (sysctl_quic_rmem[1 ]));
@@ -62,6 +69,11 @@ static int quic_init_sock(struct sock *sk)static void quic_destroy_sock (struct sock *sk) { +u8 i; + +for (i =0 ; i <QUIC_PNSPACE_MAX ; i++) +quic_pnspace_free (quic_pnspace (sk, i)); +quic_path_free (sk,quic_paths (sk),0 );quic_path_free (sk,quic_paths (sk),1 );
net/quic/socket.h +7 -0
@@ -14,6 +14,7 @@#include <net/udp_tunnel.h> #include <linux/quic.h> +#include "pnspace.h" #include "common.h" #include "family.h" #include "stream.h"
@@ -47,6 +48,7 @@ struct quic_sock {struct quic_conn_id_set dest;struct quic_path_group paths;struct quic_cong cong; +struct quic_pnspace space[QUIC_PNSPACE_MAX ]; };struct quic6_sock {
@@ -114,6 +116,11 @@ 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 bool quic_is_establishing (struct sock *sk) {return sk->sk_state ==QUIC_SS_ESTABLISHING ;
Signature · 1 line
2.47.1
PATCH 11/15 quic: add crypto key derivation and installation
In reply to Xin LongThis patch introduces 'quic_crypto', a component responsible for QUIC encryption key derivation and installation across the various key
levels: Initial, Handshake, 0-RTT (Early), and 1-RTT (Application).
It provides helpers to derive and install initial secrets, set traffic secrets and install the corresponding keys, and perform key updates to enable forward secrecy. Additionally, it implements stateless reset token generation, used to support connection reset without state.
- quic_crypto_initial_keys_install(): Derive and install initial keys.
- quic_crypto_set_cipher(): Allocate all transforms based on the cipher type provided.
- quic_crypto_set_secret(): Set the traffic secret and install derived keys.
- quic_crypto_key_update(): Rekey and install new keys to the !phase side.
- quic_crypto_generate_stateless_reset_token(): Generate token for stateless reset.
These mechanisms are essential for establishing and maintaining secure communication throughout the QUIC connection lifecycle.
--- net/quic/Makefile | 2 +- net/quic/crypto.c | 545 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/crypto.h | 73 ++++++ net/quic/protocol.c | 12 + net/quic/protocol.h | 2 + net/quic/socket.c | 2 + net/quic/socket.h | 7 + 7 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 net/quic/crypto.c create mode 100644 net/quic/crypto.h
net/quic/Makefile +1 -1
@@ -6,4 +6,4 @@ 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 + cong.o pnspace.o crypto.o
net/quic/crypto.c new file +545 -0
545 lines not on this page · raw message
net/quic/crypto.h new file +73 -0
73 lines not on this page · raw message
net/quic/protocol.c +12 -0
@@ -258,9 +258,18 @@ static int __net_init quic_net_init(struct net *net)if (!qn->stat)return -ENOMEM ; + err =quic_crypto_set_cipher (&qn->crypto,TLS_CIPHER_AES_GCM_128 ,CRYPTO_ALG_ASYNC ); +if (err) { +free_percpu (qn->stat); + qn->stat =NULL ; +return err; + } +spin_lock_init (&qn->lock); +#ifdef CONFIG_PROC_FS err =quic_net_proc_init (net);if (err) { +quic_crypto_free (&qn->crypto);free_percpu (qn->stat); qn->stat =NULL ; }
@@ -275,6 +284,7 @@ static void __net_exit quic_net_exit(struct net *net)#ifdef CONFIG_PROC_FS quic_net_proc_exit (net);#endif +quic_crypto_free (&qn->crypto);free_percpu (qn->stat); qn->stat =NULL ; }
@@ -323,6 +333,8 @@ static __init int quic_init(void) sysctl_quic_wmem[1 ] =16 *1024 ; sysctl_quic_wmem[2 ] =max (64 *1024 , max_share); +quic_crypto_init (); + err =percpu_counter_init (&quic_sockets_allocated,0 ,GFP_KERNEL );if (err) goto err_percpu_counter;
net/quic/protocol.h +2 -0
@@ -47,6 +47,8 @@ struct quic_net {#ifdef CONFIG_PROC_FS struct proc_dir_entry *proc_net;/* procfs entry for dumping QUIC socket stats */ #endif +struct quic_crypto crypto;/* Context for decrypting Initial packets for ALPN */ +spinlock_t lock;/* Lock protecting crypto context for Initial packet decryption */ };struct quic_net *quic_net (struct net *net);
net/quic/socket.c +2 -0
@@ -73,6 +73,8 @@ static void quic_destroy_sock(struct sock *sk)for (i =0 ; i <QUIC_PNSPACE_MAX ; i++)quic_pnspace_free (quic_pnspace (sk, i)); +for (i =0 ; i <QUIC_CRYPTO_MAX ; i++) +quic_crypto_free (quic_crypto (sk, i));quic_path_free (sk,quic_paths (sk),0 );quic_path_free (sk,quic_paths (sk),1 );
net/quic/socket.h +7 -0
@@ -19,6 +19,7 @@#include "family.h" #include "stream.h" #include "connid.h" +#include "crypto.h" #include "path.h" #include "cong.h"
@@ -49,6 +50,7 @@ struct quic_sock {struct quic_path_group paths;struct quic_cong cong;struct quic_pnspace space[QUIC_PNSPACE_MAX ]; +struct quic_crypto crypto[QUIC_CRYPTO_MAX ]; };struct quic6_sock {
@@ -121,6 +123,11 @@ 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 bool quic_is_establishing (struct sock *sk) {return sk->sk_state ==QUIC_SS_ESTABLISHING ;
Signature · 1 line
2.47.1
PATCH 12/15 quic: add crypto packet encryption and decryption
In reply to Xin LongThis patch adds core support for packet-level encryption and decryption using AEAD, including both payload protection and QUIC header protection. It introduces helpers to encrypt packets before transmission and to remove header protection and decrypt payloads upon reception, in line with QUIC's cryptographic requirements.
- quic_crypto_encrypt(): Perform header protection and payload encryption (TX).
- quic_crypto_decrypt(): Perform header protection removal and payload decryption (RX).
The patch also includes support for Retry token handling. It provides helpers to compute the Retry integrity tag, generate tokens for address validation, and verify tokens received from clients during the handshake phase.
- quic_crypto_get_retry_tag(): Compute tag for Retry packets.
- quic_crypto_generate_token(): Generate retry token.
- quic_crypto_verify_token(): Verify retry token.
These additions establish the cryptographic primitives necessary for secure QUIC packet exchange and address validation.
--- net/quic/crypto.c | 656 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/crypto.h | 10 + 2 files changed, 666 insertions(+)
net/quic/crypto.c +656 -0
@@ -201,6 +201,343 @@ static int quic_crypto_rx_keys_derive_and_install(struct quic_crypto *crypto)return 0 ; } +static void *quic_crypto_skcipher_mem_alloc (struct crypto_skcipher *tfm,u32 mask_size, +u8 **iv,struct skcipher_request **req) +{ +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_ATOMIC ); +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; +} + +#define QUIC_SAMPLE_LEN 16 +#define QUIC_MAX_PN_LEN 4 + +#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_encrypt (struct crypto_skcipher *tfm,struct sk_buff *skb,bool chacha) +{ +struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +struct skcipher_request *req; +struct scatterlist sg; +u8 *mask, *iv, *p; +int err, i; + + mask =quic_crypto_skcipher_mem_alloc (tfm,QUIC_SAMPLE_LEN , &iv, &req); +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}) +*/ +memcpy ((chacha ? iv : mask), skb->data + cb->number_offset +QUIC_MAX_PN_LEN , +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 err; + +/* 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; + *p = (u8)(*p ^ (mask[0 ] & (((*p &QUIC_HEADER_FORM_BIT ) ==QUIC_HEADER_FORM_BIT ) ? +QUIC_LONG_HEADER_MASK :QUIC_SHORT_HEADER_MASK ))); + p = skb->data + cb->number_offset; +for (i =1 ; i <= cb->number_len; i++) + *p++ ^= mask[i]; +err: +kfree (mask); +return err; +} + +/* Extracts and reconstructs the packet number from an incoming QUIC packet. */ +static void quic_crypto_get_header (struct sk_buff *skb) +{ +struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +struct quichdr *hdr =quic_hdr (skb); +u32 len =QUIC_MAX_PN_LEN ; +u8 *p = (u8 *)hdr; + +/* 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 += cb->number_offset; + cb->key_phase = hdr->key; + cb->number_len = hdr->pnl +1 ; +quic_get_int (&p, &len, &cb->number, cb->number_len); + cb->number =quic_get_num (cb->number_max, cb->number, cb->number_len); + +if (cb->number > cb->number_max) + cb->number_max = cb->number; +} + +#define QUIC_PN_LEN_BITS_MASK 0x03 + +static int quic_crypto_header_decrypt (struct crypto_skcipher *tfm,struct sk_buff *skb,bool chacha) +{ +struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +struct quichdr *hdr =quic_hdr (skb); +int err, i, len = cb->length; +struct skcipher_request *req; +struct scatterlist sg; +u8 *mask, *iv, *p; + + mask =quic_crypto_skcipher_mem_alloc (tfm,QUIC_SAMPLE_LEN , &iv, &req); +if (!mask) +return -ENOMEM ; + +if (len <QUIC_MAX_PN_LEN +QUIC_SAMPLE_LEN ) { + err = -EINVAL ; + goto err; + } + +/* Similar logic to quic_crypto_header_encrypt(). */ + p = (u8 *)hdr + cb->number_offset; +memcpy ((chacha ? iv : mask), p +QUIC_MAX_PN_LEN ,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 err; + + p = (u8 *)hdr; + *p = (u8)(*p ^ (mask[0 ] & (((*p &QUIC_HEADER_FORM_BIT ) ==QUIC_HEADER_FORM_BIT ) ? +QUIC_LONG_HEADER_MASK :QUIC_SHORT_HEADER_MASK ))); + cb->number_len = (*p &QUIC_PN_LEN_BITS_MASK ) +1 ; + p += cb->number_offset; +for (i =0 ; i < cb->number_len; ++i) + *(p + i) = *((u8 *)hdr + cb->number_offset + i) ^ mask[i +1 ]; +quic_crypto_get_header (skb); + +err: +kfree (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) +{ +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_ATOMIC ); +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_destruct_skb (struct sk_buff *skb) +{ +kfree (skb_shinfo (skb)->destructor_arg); +sock_efree (skb); +} + +static void quic_crypto_done (void *data,int err) +{ +struct sk_buff *skb = data; + +QUIC_SKB_CB (skb)->crypto_done (skb, err); +} + +/* AEAD Usage. */ +static int quic_crypto_payload_encrypt (struct crypto_aead *tfm,struct sk_buff *skb, +u8 *tx_iv,bool ccm) +{ +struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +struct quichdr *hdr =quic_hdr (skb); +u8 *iv, i, nonce[QUIC_IV_LEN ]; +struct aead_request *req; +struct sk_buff *trailer; +struct scatterlist *sg; +u32 nsg, hlen, len; +void *ctx; +__be64 n; +int err; + + len = skb->len; + err =skb_cow_data (skb,QUIC_TAG_LEN , &trailer); +if (err <0 ) +return err; + nsg = (u32 )err; +pskb_put (skb, trailer,QUIC_TAG_LEN ); + hdr->key = cb->key_phase; + + ctx =quic_crypto_aead_mem_alloc (tfm,0 , &iv, &req, &sg, nsg); +if (!ctx) +return -ENOMEM ; + +sg_init_table (sg, nsg); + err =skb_to_sgvec (skb, sg,0 , (int )skb->len); +if (err <0 ) + goto err; + +/* 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. +*/ + hlen = cb->number_offset + cb->number_len; +memcpy (nonce, tx_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); +aead_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , (void *)quic_crypto_done, skb); + + err =crypto_aead_encrypt (req); +if (err == -EINPROGRESS ) { +/* Will complete asynchronously; set destructor to free context. */ + skb->destructor = quic_crypto_destruct_skb; +skb_shinfo (skb)->destructor_arg = ctx; +return err; + } + +err: +kfree (ctx); +return err; +} + +static int quic_crypto_payload_decrypt (struct crypto_aead *tfm,struct sk_buff *skb, +u8 *rx_iv,bool ccm) +{ +struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +u8 *iv, i, nonce[QUIC_IV_LEN ]; +struct aead_request *req; +struct sk_buff *trailer; +int nsg, hlen, len, err; +struct scatterlist *sg; +void *ctx; +__be64 n; + + len = cb->length + cb->number_offset; + hlen = cb->number_offset + cb->number_len; +if (len - hlen <QUIC_TAG_LEN ) +return -EINVAL ; + nsg =skb_cow_data (skb,0 , &trailer); +if (nsg <0 ) +return nsg; + ctx =quic_crypto_aead_mem_alloc (tfm,0 , &iv, &req, &sg, nsg); +if (!ctx) +return -ENOMEM ; + +sg_init_table (sg, nsg); + err =skb_to_sgvec (skb, sg,0 , len); +if (err <0 ) + goto err; +skb_dst_force (skb); + +/* Similar logic to quic_crypto_payload_encrypt(). */ +memcpy (nonce, rx_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]; + + 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); +aead_request_set_callback (req,CRYPTO_TFM_REQ_MAY_BACKLOG , (void *)quic_crypto_done, skb); + + err =crypto_aead_decrypt (req); +if (err == -EINPROGRESS ) { + skb->destructor = quic_crypto_destruct_skb; +skb_shinfo (skb)->destructor_arg = ctx; +return err; + } +err: +kfree (ctx); +return err; +} +#define QUIC_CIPHER_MIN TLS_CIPHER_AES_GCM_128#define QUIC_CIPHER_MAX TLS_CIPHER_CHACHA20_POLY1305
@@ -225,6 +562,132 @@ static struct quic_cipher ciphers[QUIC_CIPHER_MAX + 1 - QUIC_CIPHER_MIN] = { "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 ; +} + +/* 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) +{ +u8 *iv, cha, ccm, phase = crypto->key_phase; +struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +int err; + + cb->key_phase = phase; + iv = crypto->tx_iv[phase]; +/* Packet payload is already encrypted (e.g., resumed from async), proceed to header +* protection only. +*/ +if (cb->resume) + goto out; + +/* 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 =jiffies_to_usecs (jiffies); + + ccm =quic_crypto_is_cipher_ccm (crypto); + err =quic_crypto_payload_encrypt (crypto->tx_tfm[phase], skb, iv, ccm); +if (err) +return err; +out: + cha =quic_crypto_is_cipher_chacha (crypto); +return quic_crypto_header_encrypt (crypto->tx_hp_tfm, skb, cha); +} + +/* 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) +{ +struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +u8 *iv, cha, ccm, phase; +int err =0 ; +u32 time; + +/* Payload was decrypted asynchronously. Proceed with parsing packet number and key +* phase. +*/ +if (cb->resume) { +quic_crypto_get_header (skb); + goto out; + } + + cha =quic_crypto_is_cipher_chacha (crypto); + err =quic_crypto_header_decrypt (crypto->rx_hp_tfm, skb, cha); +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. +*/ +if (cb->key_phase != crypto->key_phase && !crypto->key_pending) { +if (!crypto->send_ready)/* Not ready for key update. */ +return -EINVAL ; + err =quic_crypto_key_update (crypto);/* Perform a key update. */ +if (err) { + cb->errcode =QUIC_TRANSPORT_ERROR_KEY_UPDATE ; +return err; + } + cb->key_update =1 ;/* Mark packet as triggering key update. */ + } + + phase = cb->key_phase; + iv = crypto->rx_iv[phase]; + ccm =quic_crypto_is_cipher_ccm (crypto); + err =quic_crypto_payload_decrypt (crypto->rx_tfm[phase], skb, iv, ccm); +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 && cb->key_phase != crypto->key_phase) { + crypto->key_pending =0 ; + crypto->key_update_time =0 ; + } +return err; + } + +out: +/* 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 &&jiffies_to_usecs (jiffies) - time >= crypto->key_update_time) { + crypto->key_pending =0 ; + crypto->key_update_time =0 ; + } + } +return err; +} +int quic_crypto_set_cipher (struct quic_crypto *crypto,u32 type,u8 flag) {struct quic_cipher *cipher;
@@ -504,6 +967,199 @@ int quic_crypto_initial_keys_install(struct quic_crypto *crypto, struct quic_conreturn quic_crypto_set_secret (crypto, &srt, version,CRYPTO_ALG_ASYNC ); } +#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) +{ +struct crypto_aead *tfm = crypto->tag_tfm; +u8 *pseudo_retry, *p, *iv, *key; +struct aead_request *req; +struct scatterlist *sg; +u32 plen; +int err; + +/* 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 ); +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); + err =crypto_aead_encrypt (req); +if (!err) +memcpy (tag, p,QUIC_TAG_LEN ); +kfree (pseudo_retry); +return err; +} + +/* Generate a token for Retry or address validation. +* +* Builds a token with the format: [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 key[TLS_CIPHER_AES_GCM_128_KEY_SIZE ], iv[QUIC_IV_LEN ], *retry_token, *tx_iv, *p; +struct crypto_aead *tfm = crypto->tag_tfm; +u32 ts =jiffies_to_usecs (jiffies), len; +struct quic_data srt = {}, k, i; +struct aead_request *req; +struct scatterlist *sg; +int err; + +quic_data (&srt, quic_random_data,QUIC_RANDOM_DATA_LEN ); +quic_data (&k, key,TLS_CIPHER_AES_GCM_128_KEY_SIZE ); +quic_data (&i, iv,QUIC_IV_LEN ); + err =quic_crypto_keys_derive (crypto->secret_tfm, &srt, &k, &i,NULL ,QUIC_VERSION_V1 ); +if (err) +return err; + err =crypto_aead_setauthsize (tfm,QUIC_TAG_LEN ); +if (err) +return err; + err =crypto_aead_setkey (tfm, key,TLS_CIPHER_AES_GCM_128_KEY_SIZE ); +if (err) +return err; + token++; + len = addrlen +sizeof (ts) + conn_id->len +QUIC_TAG_LEN ; + retry_token =quic_crypto_aead_mem_alloc (tfm, len, &tx_iv, &req, &sg,1 ); +if (!retry_token) +return -ENOMEM ; + + p = retry_token; + p =quic_put_data (p, addr, addrlen); + p =quic_put_int (p, ts,sizeof (ts)); +quic_put_data (p, conn_id->data, conn_id->len); +sg_init_one (sg, retry_token, len); +aead_request_set_tfm (req, tfm); +aead_request_set_ad (req, addrlen); +aead_request_set_crypt (req, sg, sg, len - addrlen -QUIC_TAG_LEN , iv); + err =crypto_aead_encrypt (req); +if (!err) { +memcpy (token, retry_token, len); + *tlen = len +1 ; + } +kfree (retry_token); +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) +{ +u32 ts =jiffies_to_usecs (jiffies), timeout =QUIC_TOKEN_TIMEOUT_RETRY ; +u8 key[TLS_CIPHER_AES_GCM_128_KEY_SIZE ], iv[QUIC_IV_LEN ]; +u8 *retry_token, *rx_iv, *p, flag = *token; +struct crypto_aead *tfm = crypto->tag_tfm; +struct quic_data srt = {}, k, i; +struct aead_request *req; +struct scatterlist *sg; +int err; +u64 t; + +quic_data (&srt, quic_random_data,QUIC_RANDOM_DATA_LEN ); +quic_data (&k, key,TLS_CIPHER_AES_GCM_128_KEY_SIZE ); +quic_data (&i, iv,QUIC_IV_LEN ); + err =quic_crypto_keys_derive (crypto->secret_tfm, &srt, &k, &i,NULL ,QUIC_VERSION_V1 ); +if (err) +return err; + err =crypto_aead_setauthsize (tfm,QUIC_TAG_LEN ); +if (err) +return err; + err =crypto_aead_setkey (tfm, key,TLS_CIPHER_AES_GCM_128_KEY_SIZE ); +if (err) +return err; + len--; + token++; + retry_token =quic_crypto_aead_mem_alloc (tfm, len, &rx_iv, &req, &sg,1 ); +if (!retry_token) +return -ENOMEM ; + +memcpy (retry_token, token, len); +sg_init_one (sg, retry_token, len); +aead_request_set_tfm (req, tfm); +aead_request_set_ad (req, addrlen); +aead_request_set_crypt (req, sg, sg, len - addrlen, iv); + err =crypto_aead_decrypt (req); +if (err) + goto out; + + err = -EINVAL ; + p = retry_token; +if (memcmp (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,sizeof (ts)) || t + timeout < ts) + 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 (retry_token); +return err; +} +/* Generate a derived key using HKDF-Extract and HKDF-Expand with a given label. */ static int quic_crypto_generate_key (struct quic_crypto *crypto,void *data,u32 len,char *label,u8 *token,u32 key_len)
net/quic/crypto.h +10 -0
@@ -62,6 +62,9 @@ int quic_crypto_get_secret(struct quic_crypto *crypto, struct quic_crypto_secretint quic_crypto_set_cipher (struct quic_crypto *crypto,u32 type,u8 flag);int quic_crypto_key_update (struct quic_crypto *crypto); +int quic_crypto_encrypt (struct quic_crypto *crypto,struct sk_buff *skb); +int quic_crypto_decrypt (struct quic_crypto *crypto,struct sk_buff *skb); +int quic_crypto_initial_keys_install (struct quic_crypto *crypto,struct quic_conn_id *conn_id,u32 version,bool is_serv); int quic_crypto_generate_session_ticket_key(struct quic_crypto *crypto, void *data,
@@ -69,5 +72,12 @@ int quic_crypto_generate_session_ticket_key(struct quic_crypto *crypto, void *daint quic_crypto_generate_stateless_reset_token (struct quic_crypto *crypto,void *data,u32 len,u8 *key,u32 key_len); +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);void quic_crypto_init (void );
Signature · 1 line
2.47.1
PATCH 13/15 quic: add timer management
In reply to Xin LongThis patch introduces 'quic_timer' to unify and manage the five main timers used in QUIC: loss detection, delayed ACK, path validation, PMTU probing, and pacing. These timers are critical for driving retransmissions, connection liveness, and flow control.
Each timer type is initialized, started, reset, or stopped using a common set of operations.
- quic_timer_reset(): Reset a timer with type and timeout
- quic_timer_start(): Start a timer with type and timeout
- quic_timer_stop(): Stop a timer with type
Although handler functions for each timer are defined, they are currently placeholders; their logic will be implemented in upcoming patches for packet transmission and outqueue handling.
Deferred timer actions are also integrated through quic_release_cb(), which dispatches to the appropriate handler when timers expire.
--- net/quic/Makefile | 2 +- net/quic/socket.c | 33 ++++++++ net/quic/socket.h | 33 ++++++++ net/quic/timer.c | 196 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/timer.h | 47 +++++++++++ 5 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 net/quic/timer.c create mode 100644 net/quic/timer.h
net/quic/Makefile +1 -1
@@ -6,4 +6,4 @@ 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 + cong.o pnspace.o crypto.o timer.o
net/quic/socket.c +33 -0
@@ -48,6 +48,8 @@ static int quic_init_sock(struct sock *sk)quic_conn_id_set_init (quic_dest (sk),0 );quic_cong_init (quic_cong (sk)); +quic_timer_init (sk); +if (quic_stream_init (quic_streams (sk)))return -ENOMEM ;
@@ -71,6 +73,8 @@ static void quic_destroy_sock(struct sock *sk) {u8 i; +quic_timer_free (sk); +for (i =0 ; i <QUIC_PNSPACE_MAX ; i++)quic_pnspace_free (quic_pnspace (sk, i));for (i =0 ; i <QUIC_CRYPTO_MAX ; i++)
@@ -209,6 +213,35 @@ EXPORT_SYMBOL_GPL(quic_kernel_getsockopt);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_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_TSQ_DEFERRED ) { +quic_timer_pace_handler (sk); +__sock_put (sk); + } }static int quic_disconnect (struct sock *sk,int flags)
net/quic/socket.h +33 -0
@@ -24,6 +24,7 @@#include "cong.h" #include "protocol.h" +#include "timer.h" extern struct proto quic_prot;extern struct proto quicv6_prot;
@@ -35,6 +36,31 @@ enum quic_state { 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_TSQ_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_TSQ_DEFERRED =BIT (QUIC_TSQ_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_TSQ_DEFERRED) +struct quic_sock {struct inet_sock inet;struct list_head reqs;
@@ -51,6 +77,8 @@ struct quic_sock {struct quic_cong cong;struct quic_pnspace space[QUIC_PNSPACE_MAX ];struct quic_crypto crypto[QUIC_CRYPTO_MAX ]; + +struct quic_timer timers[QUIC_TIMER_MAX ]; };struct quic6_sock {
@@ -128,6 +156,11 @@ static inline struct quic_crypto *quic_crypto(const struct sock *sk, u8 level)return &quic_sk (sk)->crypto[level]; } +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 ;
net/quic/timer.c new file +196 -0
196 lines not on this page · raw message
net/quic/timer.h new file +47 -0
47 lines not on this page · raw message
Signature · 1 line
2.47.1
PATCH 14/15 quic: add frame encoder and decoder base
In reply to Xin LongThis patch introduces 'quic_frame' to represent QUIC frames and 'quic_frame_ops' to define the associated operations for encoding, processing, and acknowledgment.
This abstraction sets the foundation for flexible and modular frame handling. While core operations are defined, actual implementation will follow in subsequent patches once packet handling and inqueue/outqueue infrastructure are in place.
The patch introduces hooks for invoking frame-specific logic:
- quic_frame_create(): Invoke the .create operation of the frame.
- quic_frame_process(): Invoke the .process operation of the frame.
- quic_frame_ack(): Invoke the .ack operation of the frame.
To manage frame lifecycles, reference counting is used, supported by
- quic_frame_get(): Increment the reference count of a frame.
- quic_frame_put(): Decrement the reference count of a frame.
- quic_frame_alloc(): Allocate a frame and set its data.
Frames are allocated through quic_frame_alloc(), and a dedicated kmem_cache (quic_frame_cachep) is added to optimize memory usage.
For STREAM frames, additional data can be appended using
- quic_frame_stream_append(): Append more data to a STREAM frame.
--- net/quic/Makefile | 2 +- net/quic/frame.c | 558 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/frame.h | 192 +++++++++++++++ net/quic/protocol.c | 9 + net/quic/protocol.h | 1 + net/quic/socket.h | 2 + 6 files changed, 763 insertions(+), 1 deletion(-) create mode 100644 net/quic/frame.c create mode 100644 net/quic/frame.h
net/quic/Makefile +1 -1
@@ -6,4 +6,4 @@ 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 + cong.o pnspace.o crypto.o timer.o frame.o
net/quic/frame.c new file +558 -0
558 lines not on this page · raw message
net/quic/frame.h new file +192 -0
192 lines not on this page · raw message
net/quic/protocol.c +9 -0
@@ -21,6 +21,7 @@static unsigned int quic_net_id__read_mostly ; +struct kmem_cache *quic_frame_cachep__read_mostly ;struct percpu_counter quic_sockets_allocated;long sysctl_quic_mem[3 ];
@@ -335,6 +336,11 @@ static __init int quic_init(void)quic_crypto_init (); + quic_frame_cachep =kmem_cache_create ("quic_frame" ,sizeof (struct quic_frame ), +0 ,SLAB_HWCACHE_ALIGN ,NULL ); +if (!quic_frame_cachep) + goto err; + err =percpu_counter_init (&quic_sockets_allocated,0 ,GFP_KERNEL );if (err) goto err_percpu_counter;
@@ -363,6 +369,8 @@ static __init int quic_init(void) err_hash:percpu_counter_destroy (&quic_sockets_allocated); err_percpu_counter: +kmem_cache_destroy (quic_frame_cachep); +err:return err; }
@@ -375,6 +383,7 @@ static __exit void quic_exit(void)unregister_pernet_subsys (&quic_net_ops);quic_hash_tables_destroy ();percpu_counter_destroy (&quic_sockets_allocated); +kmem_cache_destroy (quic_frame_cachep);pr_info ("quic: exit\n" ); }
net/quic/protocol.h +1 -0
@@ -8,6 +8,7 @@* Xin Long <[email protected]> */ +extern struct kmem_cache *quic_frame_cachep__read_mostly ;extern struct percpu_counter quic_sockets_allocated;extern long sysctl_quic_mem[3 ];
net/quic/socket.h +2 -0
@@ -23,6 +23,8 @@#include "path.h" #include "cong.h" +#include "frame.h" +#include "protocol.h" #include "timer.h"
Signature · 1 line
2.47.1
PATCH 15/15 quic: add packet builder and parser base
In reply to Xin LongThis patch introduces 'quic_packet' to handle packing and unpacking of QUIC packets on both the transmit (TX) and receive (RX) paths.
On the TX path, it provides functionality for frame packing and packet construction. The packet configuration includes setting the path, calculating overhead, and verifying routing. Frames are appended to the packet before it is created with the queued frames.
Once assembled, the packet is encrypted, bundled, and sent out. There is also support to flush the packet when no additional frames remain. Functions to create application (short) and handshake (long) packets are currently placeholders for future implementation.
- quic_packet_config(): Set the path, compute overhead, and verify routing.
- quic_packet_tail(): Append a frame to the packet for transmission.
- quic_packet_create(): Create the packet with the queued frames.
- quic_packet_xmit(): Encrypt, bundle, and send out the packet.
- quic_packet_flush(): Send the packet if there's nothing left to bundle.
On the RX side, the patch introduces mechanisms to parse the ALPN from client Initial packets to determine the correct listener socket. Received packets are then routed and processed accordingly. Similar to the TX path, handling for application and handshake packets is not yet implemented.
- quic_packet_parse_alpn()`: Parse the ALPN from a client Initial packet, then locate the appropriate listener using the ALPN.
- quic_packet_rcv(): Locate the appropriate socket to handle the packet via quic_packet_process().
- quic_packet_process()`: Process the received packet.
In addition to packet flow, this patch adds support for ICMP-based MTU updates by locating the relevant socket and updating the stored PMTU accordingly.
- quic_packet_rcv_err_pmtu(): Find the socket and update the PMTU via quic_packet_mss_update().
--- net/quic/Makefile | 2 +- net/quic/packet.c | 889 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/packet.h | 129 +++++++ net/quic/protocol.c | 7 + net/quic/socket.c | 107 ++++++ net/quic/socket.h | 11 + 6 files changed, 1144 insertions(+), 1 deletion(-) create mode 100644 net/quic/packet.c create mode 100644 net/quic/packet.h
net/quic/Makefile +1 -1
@@ -6,4 +6,4 @@ 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 frame.o + cong.o pnspace.o crypto.o timer.o frame.o packet.o
net/quic/packet.c new file +889 -0
889 lines not on this page · raw message
net/quic/packet.h new file +129 -0
129 lines not on this page · raw message
net/quic/protocol.c +7 -0
@@ -352,6 +352,10 @@ static __init int quic_init(void)if (err) goto err_def_ops; + err =quic_path_init (quic_packet_rcv); +if (err) + goto err_path; + err =quic_protosw_init ();if (err) goto err_protosw;
@@ -363,6 +367,8 @@ static __init int quic_init(void)return 0 ; err_protosw: +quic_path_destroy (); +err_path:unregister_pernet_subsys (&quic_net_ops); err_def_ops:quic_hash_tables_destroy ();
@@ -380,6 +386,7 @@ static __exit void quic_exit(void)quic_sysctl_unregister ();#endif quic_protosw_exit (); +quic_path_destroy ();unregister_pernet_subsys (&quic_net_ops);quic_hash_tables_destroy ();percpu_counter_destroy (&quic_sockets_allocated);
net/quic/socket.c +107 -0
137 lines not on this page · raw message
net/quic/socket.h +11 -0
@@ -23,6 +23,7 @@#include "path.h" #include "cong.h" +#include "packet.h" #include "frame.h" #include "protocol.h"
@@ -80,6 +81,7 @@ struct quic_sock {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 ]; };
@@ -158,6 +160,11 @@ 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];
@@ -207,4 +214,8 @@ static inline void quic_set_state(struct sock *sk, int 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 quic_data *alpns); +struct sock *quic_sock_lookup (struct sk_buff *skb,union quic_addr *sa,union quic_addr *da, +struct quic_conn_id *dcid);#endif /* __net_quic_h__ */
Signature · 1 line
2.47.1
Quoted 158 lines · Xin Long
Introduction
============The QUIC protocol, as defined in RFC9000, offers a UDP-based, secure transport with flow-controlled streams for efficient communication, low-latency connection setup, and network path migration, ensuring confidentiality, integrity, and availability across various deployments.
This implementation introduces QUIC support in Linux Kernel, offering several key advantages:
- Seamless Integration for Kernel Subsystems: Kernel subsystems such as SMB and NFS can operate over QUIC seamlessly after the handshake, leveraging the net/handshake APIs.
- Standardized Socket APIs for QUIC: This implementation standardizes the socket APIs for QUIC, covering essential operations like listen, accept, connect, sendmsg, recvmsg, close, get/setsockopt, and getsock/peername().
- Efficient ALPN Routing: It incorporates ALPN routing within the kernel, efficiently directing incoming requests to the appropriate applications across different processes based on ALPN.
- Performance Enhancements: By minimizing data duplication through zero-copy techniques such as
sendfile(), and paving the way for crypto offloading in NICs, this implementation enhances performance and prepares for future optimizations.This implementation offers fundamental support for the following RFCs:
- RFC9000 - QUIC: A UDP-Based Multiplexed and Secure Transport
- RFC9001 - Using TLS to Secure QUIC
- RFC9002 - QUIC Loss Detection and Congestion Control
- RFC9221 - An Unreliable Datagram Extension to QUIC
- RFC9287 - Greasing the QUIC Bit
- RFC9368 - Compatible Version Negotiation for QUIC
- RFC9369 - QUIC Version 2The socket APIs for QUIC follow the RFC draft [1]:
- The Sockets API Extensions for In-kernel QUIC Implementations
Implementation
==============The core idea is to implement QUIC within the kernel, using a userspace handshake approach.
Only the processing and creation of raw TLS Handshake Messages are handled in userspace, facilitated by a TLS library like GnuTLS. These messages are exchanged between kernel and userspace via
sendmsg()andrecvmsg(), with cryptographic details conveyed through control messages (cmsg).The entire QUIC protocol, aside from the TLS Handshake Messages processing and creation, is managed within the kernel. Rather than using a Upper Layer Protocol (ULP) layer, this implementation establishes a socket of type
IPPROTO_QUIC(similar toIPPROTO_MPTCP), operating over UDP tunnels.For kernel consumers, they can initiate a handshake request from the kernel to userspace using the existing net/handshake netlink. The userspace component, such as tlshd service [2], then manages the processing of the QUIC handshake request.
- Handshake Architecture:
┌──────┐ ┌──────┐ │ APP1 │ │ APP2 │ ... └──────┘ └──────┘ ┌──────────────────────────────────────────┐ │ {quic_client/server_handshake()} │<─────────────┐ └──────────────────────────────────────────┘ ┌─────────────┐ {send/recvmsg()} {set/getsockopt()} │ tlshd │ [CMSG handshake_info] [SOCKOPT_CRYPTO_SECRET] └─────────────┘ [SOCKOPT_TRANSPORT_PARAM_EXT] │ ^ │ ^ │ ^ │ │ Userspace │ │ │ │ │ │ ──────────────│─│──────────────────│─│──────────────────│───│─────── Kernel │ │ │ │ │ │ v │ v │ v │ ┌──────────────────┬───────────────────────┐ ┌─────────────┐ │ protocol, timer, │ socket (IPPROTO_QUIC) │<──┐ │ handshake │ │ ├───────────────────────┤ │ │netlink APIs │ │ common, family, │ outqueue | inqueue │ │ └─────────────┘ │ ├───────────────────────┤ │ │ │ │ stream, connid, │ frame │ │ ┌─────┐ ┌─────┐ │ ├───────────────────────┤ │ │ │ │ │ │ path, pnspace, │ packet │ │───│ SMB │ │ NFS │... │ ├───────────────────────┤ │ │ │ │ │ │ cong, crypto │ UDP tunnels │ │ └─────┘ └─────┘ └──────────────────┴───────────────────────┘ └──────┴───────┘- User Data Architecture:
┌──────┐ ┌──────┐ │ APP1 │ │ APP2 │ ... └──────┘ └──────┘ {send/recvmsg()} {set/getsockopt()} {recvmsg()} [CMSG stream_info] [SOCKOPT_KEY_UPDATE] [EVENT conn update] [SOCKOPT_CONNECTION_MIGRATION] [EVENT stream update] [SOCKOPT_STREAM_OPEN/RESET/STOP] │ ^ │ ^ ^ Userspace │ │ │ │ │ ──────────────│─│───────────────│─│─────────────────────│─────────── Kernel │ │ │ │ │ v │ v │ ┌──────────────────┘ ┌──────────────────┬───────────────────────┐ │ protocol, timer, │ socket (IPPROTO_QUIC) │<──┐{kernel_send/recvmsg()} │ ├───────────────────────┤ │{kernel_set/getsockopt()} │ common, family, │ outqueue | inqueue │ │{kernel_recvmsg()} │ ├───────────────────────┤ │ │ stream, connid, │ frame │ │ ┌─────┐ ┌─────┐ │ ├───────────────────────┤ │ │ │ │ │ │ path, pnspace, │ packet │ │───│ SMB │ │ NFS │... │ ├───────────────────────┤ │ │ │ │ │ │ cong, crypto │ UDP tunnels │ │ └─────┘ └─────┘ └──────────────────┴───────────────────────┘ └──────┴───────┘Interface
=========This implementation supports a mapping of QUIC into sockets APIs. Similar to TCP and SCTP, a typical Server and Client use the following system call sequence to communicate:
Client Server
────────────────────────────────────────────────────────────────────── sockfd = socket(IPPROTO_QUIC) listenfd = socket(IPPROTO_QUIC) bind(sockfd) bind(listenfd)listen(listenfd)
connect(sockfd)
quic_client_handshake(sockfd)
sockfd = accecpt(listenfd)quic_server_handshake(sockfd, cert)sendmsg(sockfd) recvmsg(sockfd)
close(sockfd) close(sockfd)
close(listenfd)Please note that
quic_client_handshake()andquic_server_handshake()functions are currently sourced from libquic [3]. These functions are responsible for receiving and processing the raw TLS handshake messages until the completion of the handshake process.For utilization by kernel consumers, it is essential to have tlshd service [2] installed and running in userspace. This service receives and manages kernel handshake requests for kernel sockets. In the kernel, the APIs closely resemble those used in userspace:
Client Server
──────────────────────────────────────────────────────────────────────── __sock_create(IPPROTO_QUIC, &sock) __sock_create(IPPROTO_QUIC, &sock) kernel_bind(sock) kernel_bind(sock) kernel_listen(sock) kernel_connect(sock) tls_client_hello_x509(args:{sock}) kernel_accept(sock, &newsock) tls_server_hello_x509(args:{newsock})
Quoted 8 lines
kernel_sendmsg(sock)kernel_recvmsg(newsock)
sock_release(sock)sock_release(newsock)
sock_release(sock)Please be aware that
tls_client_hello_x509()andtls_server_hello_x509()are APIs fromnet/handshake/. They are used to dispatch the handshake request to the userspace tlshd service and subsequently block until the handshake process is completed.
Can you please put this (or something like this) into Documentation/ somewhere?
Thanks,
David
Quoted 166 lines · David Howells
Quoted 162 lines · Xin Long
Introduction
============The QUIC protocol, as defined in RFC9000, offers a UDP-based, secure transport with flow-controlled streams for efficient communication, low-latency connection setup, and network path migration, ensuring confidentiality, integrity, and availability across various deployments.
This implementation introduces QUIC support in Linux Kernel, offering several key advantages:
- Seamless Integration for Kernel Subsystems: Kernel subsystems such as SMB and NFS can operate over QUIC seamlessly after the handshake, leveraging the net/handshake APIs.
- Standardized Socket APIs for QUIC: This implementation standardizes the socket APIs for QUIC, covering essential operations like listen, accept, connect, sendmsg, recvmsg, close, get/setsockopt, and getsock/peername().
- Efficient ALPN Routing: It incorporates ALPN routing within the kernel, efficiently directing incoming requests to the appropriate applications across different processes based on ALPN.
- Performance Enhancements: By minimizing data duplication through zero-copy techniques such as
sendfile(), and paving the way for crypto offloading in NICs, this implementation enhances performance and prepares for future optimizations.This implementation offers fundamental support for the following RFCs:
- RFC9000 - QUIC: A UDP-Based Multiplexed and Secure Transport
- RFC9001 - Using TLS to Secure QUIC
- RFC9002 - QUIC Loss Detection and Congestion Control
- RFC9221 - An Unreliable Datagram Extension to QUIC
- RFC9287 - Greasing the QUIC Bit
- RFC9368 - Compatible Version Negotiation for QUIC
- RFC9369 - QUIC Version 2The socket APIs for QUIC follow the RFC draft [1]:
- The Sockets API Extensions for In-kernel QUIC Implementations
Implementation
==============The core idea is to implement QUIC within the kernel, using a userspace handshake approach.
Only the processing and creation of raw TLS Handshake Messages are handled in userspace, facilitated by a TLS library like GnuTLS. These messages are exchanged between kernel and userspace via
sendmsg()andrecvmsg(), with cryptographic details conveyed through control messages (cmsg).The entire QUIC protocol, aside from the TLS Handshake Messages processing and creation, is managed within the kernel. Rather than using a Upper Layer Protocol (ULP) layer, this implementation establishes a socket of type
IPPROTO_QUIC(similar toIPPROTO_MPTCP), operating over UDP tunnels.For kernel consumers, they can initiate a handshake request from the kernel to userspace using the existing net/handshake netlink. The userspace component, such as tlshd service [2], then manages the processing of the QUIC handshake request.
- Handshake Architecture:
┌──────┐ ┌──────┐ │ APP1 │ │ APP2 │ ... └──────┘ └──────┘ ┌──────────────────────────────────────────┐ │ {quic_client/server_handshake()} │<─────────────┐ └──────────────────────────────────────────┘ ┌─────────────┐ {send/recvmsg()} {set/getsockopt()} │ tlshd │ [CMSG handshake_info] [SOCKOPT_CRYPTO_SECRET] └─────────────┘ [SOCKOPT_TRANSPORT_PARAM_EXT] │ ^ │ ^ │ ^ │ │ Userspace │ │ │ │ │ │ ──────────────│─│──────────────────│─│──────────────────│───│─────── Kernel │ │ │ │ │ │ v │ v │ v │ ┌──────────────────┬───────────────────────┐ ┌─────────────┐ │ protocol, timer, │ socket (IPPROTO_QUIC) │<──┐ │ handshake │ │ ├───────────────────────┤ │ │netlink APIs │ │ common, family, │ outqueue | inqueue │ │ └─────────────┘ │ ├───────────────────────┤ │ │ │ │ stream, connid, │ frame │ │ ┌─────┐ ┌─────┐ │ ├───────────────────────┤ │ │ │ │ │ │ path, pnspace, │ packet │ │───│ SMB │ │ NFS │... │ ├───────────────────────┤ │ │ │ │ │ │ cong, crypto │ UDP tunnels │ │ └─────┘ └─────┘ └──────────────────┴───────────────────────┘ └──────┴───────┘- User Data Architecture:
┌──────┐ ┌──────┐ │ APP1 │ │ APP2 │ ... └──────┘ └──────┘ {send/recvmsg()} {set/getsockopt()} {recvmsg()} [CMSG stream_info] [SOCKOPT_KEY_UPDATE] [EVENT conn update] [SOCKOPT_CONNECTION_MIGRATION] [EVENT stream update] [SOCKOPT_STREAM_OPEN/RESET/STOP] │ ^ │ ^ ^ Userspace │ │ │ │ │ ──────────────│─│───────────────│─│─────────────────────│─────────── Kernel │ │ │ │ │ v │ v │ ┌──────────────────┘ ┌──────────────────┬───────────────────────┐ │ protocol, timer, │ socket (IPPROTO_QUIC) │<──┐{kernel_send/recvmsg()} │ ├───────────────────────┤ │{kernel_set/getsockopt()} │ common, family, │ outqueue | inqueue │ │{kernel_recvmsg()} │ ├───────────────────────┤ │ │ stream, connid, │ frame │ │ ┌─────┐ ┌─────┐ │ ├───────────────────────┤ │ │ │ │ │ │ path, pnspace, │ packet │ │───│ SMB │ │ NFS │... │ ├───────────────────────┤ │ │ │ │ │ │ cong, crypto │ UDP tunnels │ │ └─────┘ └─────┘ └──────────────────┴───────────────────────┘ └──────┴───────┘Interface
=========This implementation supports a mapping of QUIC into sockets APIs. Similar to TCP and SCTP, a typical Server and Client use the following system call sequence to communicate:
Client Server
────────────────────────────────────────────────────────────────────── sockfd = socket(IPPROTO_QUIC) listenfd = socket(IPPROTO_QUIC) bind(sockfd) bind(listenfd)listen(listenfd)
connect(sockfd)
quic_client_handshake(sockfd)
sockfd = accecpt(listenfd)quic_server_handshake(sockfd, cert)sendmsg(sockfd) recvmsg(sockfd)
close(sockfd) close(sockfd)
close(listenfd)Please note that
quic_client_handshake()andquic_server_handshake()functions are currently sourced from libquic [3]. These functions are responsible for receiving and processing the raw TLS handshake messages until the completion of the handshake process.For utilization by kernel consumers, it is essential to have tlshd service [2] installed and running in userspace. This service receives and manages kernel handshake requests for kernel sockets. In the kernel, the APIs closely resemble those used in userspace:
Client Server
──────────────────────────────────────────────────────────────────────── __sock_create(IPPROTO_QUIC, &sock) __sock_create(IPPROTO_QUIC, &sock) kernel_bind(sock) kernel_bind(sock) kernel_listen(sock) kernel_connect(sock) tls_client_hello_x509(args:{sock}) kernel_accept(sock, &newsock) tls_server_hello_x509(args:{newsock})
kernel_sendmsg(sock)kernel_recvmsg(newsock)
sock_release(sock)sock_release(newsock)
sock_release(sock)
Quoted 8 lines
Quoted 4 lines
Please be aware that
tls_client_hello_x509()andtls_server_hello_x509()are APIs fromnet/handshake/. They are used to dispatch the handshake request to the userspace tlshd service and subsequently block until the handshake process is completed.Can you please put this (or something like this) into
Documentation/somewhere?
Yes, there is a patch that adds Documentation/networking/quic.rst in the subsequent patchset, which I’ll post after this one. It addresses exactly what you pointed out:
https://github.com/lxin/net-next/commit/9f978448531b958f859bbd48dce8a703b256b25a
Thanks.
Thanks.
Quoted 5 lines · Xin Long
Yes, there is a patch that adds
Documentation/networking/quic.rstin the subsequent patchset, which I’ll post after this one. It addresses exactly what you pointed out:https://github.com/lxin/net-next/commit/9f978448531b958f859bbd48dce8a703b256b25a
Excellent, thanks!
David