net: introduce QUIC infrastructure and core subcomponents
Subsystems (6)
v4 cover net: introduce QUIC infrastructure and core subcomponents
Introduction
============
The QUIC protocol, defined in RFC 9000, is a secure, multiplexed transport built on top of UDP. It enables low-latency connection establishment, stream-based communication with flow control, and supports connection migration across network paths, while ensuring confidentiality, integrity, and availability.
This implementation introduces QUIC support in Linux Kernel, offering several key advantages:
- In-Kernel QUIC Support for Subsystems: Enables kernel subsystems such as SMB and NFS to operate over QUIC with minimal changes. Once the handshake is complete via the net/handshake APIs, data exchange proceeds over standard in-kernel transport interfaces.
- Standard Socket API Semantics: Implements core socket operations (listen(), accept(), connect(), sendmsg(), recvmsg(), close(), getsockopt(), setsockopt(), getsockname(), and getpeername()), allowing user space to interact with QUIC sockets in a familiar, POSIX-compliant way.
- ALPN-Based Connection Dispatching: Supports in-kernel ALPN (Application-Layer Protocol Negotiation) routing, allowing demultiplexing of QUIC connections across different user-space processes based on the ALPN identifiers.
- Performance Enhancements: Handles all control messages in-kernel to reduce syscall overhead, incorporates zero-copy mechanisms such as sendfile() minimize data movement, and is also structured to support future crypto hardware offloads.
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 central design is to implement QUIC within the kernel while delegating the handshake to userspace.
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 = accept(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 integrated 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) pass 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/
Changes in v2, v3, v4: See individual patch changelogs for details.
Xin Long (15):
net: define IPPROTO_QUIC and SOL_QUIC constants
net: build socket infrastructure for QUIC protocol
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
include/linux/quic.h | 19 + include/linux/socket.h | 1 + include/uapi/linux/in.h | 2 + include/uapi/linux/quic.h | 235 +++++++ net/Kconfig | 1 + net/Makefile | 1 + net/quic/Kconfig | 36 ++ net/quic/Makefile | 9 + net/quic/common.c | 577 +++++++++++++++++ net/quic/common.h | 205 ++++++ net/quic/cong.c | 307 +++++++++ net/quic/cong.h | 120 ++++ net/quic/connid.c | 222 +++++++ net/quic/connid.h | 162 +++++ net/quic/crypto.c | 1233 +++++++++++++++++++++++++++++++++++++ net/quic/crypto.h | 83 +++ net/quic/family.c | 585 ++++++++++++++++++ net/quic/family.h | 41 ++ net/quic/frame.c | 558 +++++++++++++++++ net/quic/frame.h | 195 ++++++ net/quic/packet.c | 956 ++++++++++++++++++++++++++++ net/quic/packet.h | 130 ++++ net/quic/path.c | 534 ++++++++++++++++ net/quic/path.h | 173 ++++++ net/quic/pnspace.c | 224 +++++++ net/quic/pnspace.h | 149 +++++ net/quic/protocol.c | 420 +++++++++++++ net/quic/protocol.h | 62 ++ net/quic/socket.c | 446 ++++++++++++++ net/quic/socket.h | 214 +++++++ net/quic/stream.c | 514 ++++++++++++++++ net/quic/stream.h | 136 ++++ net/quic/timer.c | 196 ++++++ net/quic/timer.h | 47 ++ 34 files changed, 8793 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 v4 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
Re: [PATCH net-next v4 01/15] net: define IPPROTO_QUIC and SOL_QUIC constants
Quoted 22 lines · Xin Long
This patch adds
IPPROTO_QUICandSOL_QUICconstants 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_QUICis 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.
Quoted 5 lines
The use of
IPPROTO_QUICtype sockets over UDP tunnel will effectively address these challenges and provides a more flexible and scalable solution.Signed-off-by Xin Long <[email protected]>
PATCH v4 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.
---
v3:
- Kconfig: add 'default n' for IP_QUIC (reported by Paolo).
- quic_disconnect(): return -EOPNOTSUPP (suggested by Paolo).
- quic_init/destroy_sock(): drop local_bh_disable/enable() calls (noted by Paolo).
- sysctl: add alpn_demux option to en/disable ALPN-based demux.
- SNMP: remove SNMP_MIB_SENTINEL, switch to snmp_get_cpu_field_batch_cnt() to align with latest net-next changes.
v4:
- Remove unnecessary READ_ONCE() in quic_inet_connect() (reported by
Paolo). --- net/Kconfig | 1 + net/Makefile | 1 + net/quic/Kconfig | 36 +++++ net/quic/Makefile | 8 + net/quic/protocol.c | 376 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/protocol.h | 56 +++++++ net/quic/socket.c | 207 ++++++++++++++++++++++++ net/quic/socket.h | 79 ++++++++++ 8 files changed, 764 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
@@ -251,6 +251,7 @@ source "net/bridge/netfilter/Kconfig" endif # if NETFILTER +source "net/quic/Kconfig" source "net/sctp/Kconfig" source "net/rds/Kconfig" source "net/tipc/Kconfig"
net/Makefile +1 -0
@@ -43,6 +43,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 +36 -0
@@ -0,0 +1,36 @@ +# 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 + default n + help + QUIC: A UDP-Based Multiplexed and Secure Transport + + From rfc9000 <https://www.rfc-editor.org/rfc/rfc9000.html>. + + QUIC provides applications with flow-controlled streams for structured + communication, low-latency connection establishment, and network path + migration. QUIC includes security measures that ensure + confidentiality, integrity, and availability in a range of deployment + circumstances. Accompanying documents describe the integration of + TLS for key negotiation, loss detection, and an exemplary congestion + control algorithm. + + To compile this protocol support as a module, choose M here: the + module will be called quic. Debug messages are handled by the + kernel's dynamic debugging framework. + + If in doubt, say N.
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 +376 -0
376 lines not on this page · raw message
net/quic/protocol.h new file +56 -0
56 lines not on this page · raw message
net/quic/socket.c new file +207 -0
207 lines not on this page · raw message
net/quic/socket.h new file +79 -0
79 lines not on this page · raw message
Signature · 1 line
2.47.1
Re: [PATCH net-next v4 02/15] net: build socket infrastructure for QUIC protocol
Hi Xin,
Quoted 12 lines
This 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.
Signed-off-by Pengtao He <[email protected]>Signed-off-by Xin Long <[email protected]>
...
net/quic/protocol.c line 373
Quoted 5 lines
net/quic/protocol.c new file +5
-0+module_init (quic_init); +module_exit (quic_exit); + +MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-261"); +MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-261");
Shouldn't this use MODULE_ALIAS_NET_PF_PROTO(PF_INET, IPPROTO_QUIC) instead?
metze
Re: [PATCH net-next v4 02/15] net: build socket infrastructure for QUIC protocol
Quoted 18 lines · Stefan Metzmacher
Hi Xin,
Quoted 12 lines
This 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.
Signed-off-by Pengtao He <[email protected]>Signed-off-by Xin Long <[email protected]>...
Quoted 9 lines
net/quic/protocol.c line 373
Quoted 5 lines
net/quic/protocol.c new file +5
-0+module_init (quic_init); +module_exit (quic_exit); + +MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-261"); +MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-261");Shouldn't this use
MODULE_ALIAS_NET_PF_PROTO(PF_INET, IPPROTO_QUIC)instead?
Hi, Stefan,
If we switch to using MODULE_ALIAS_NET_PF_PROTO(), we still need to keep using the numeric value 261:
MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); MODULE_ALIAS_NET_PF_PROTO(PF_INET6, 261);
IPPROTO_QUIC is defined as an enum, not a macro. Since MODULE_ALIAS_NET_PF_PROTO() relies on __stringify(proto), it can’t stringify enum values correctly, and it would generate:
alias: net-pf-10-proto-IPPROTO_QUIC
alias: net-pf-2-proto-IPPROTO_QUIC
instead of:
alias: net-pf-10-proto-261
alias: net-pf-2-proto-261
Thanks.
Re: [PATCH net-next v4 02/15] net: build socket infrastructure for QUIC protocol
Am 29.10.25 um 20:57 schrieb Xin Long:
Quoted 36 lines · Stefan Metzmacher
Quoted 18 lines
Hi Xin,
Quoted 12 lines
This 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.
Signed-off-by Pengtao He <[email protected]>Signed-off-by Xin Long <[email protected]>...
Quoted 9 lines
net/quic/protocol.c line 373
Quoted 5 lines
net/quic/protocol.c new file +5
-0+module_init (quic_init); +module_exit (quic_exit); + +MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-261"); +MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-261");Shouldn't this use
MODULE_ALIAS_NET_PF_PROTO(PF_INET, IPPROTO_QUIC)instead?Hi, Stefan,
If we switch to using
MODULE_ALIAS_NET_PF_PROTO(), we still need to keep using the numeric value 261:MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); MODULE_ALIAS_NET_PF_PROTO(PF_INET6, 261);
Quoted 6 lines
IPPROTO_QUICis defined as an enum, not a macro. SinceMODULE_ALIAS_NET_PF_PROTO()relies on__stringify(proto), it can’t stringify enum values correctly, and it would generate:alias: net-pf-10-proto-IPPROTO_QUIC
alias: net-pf-2-proto-IPPROTO_QUIC
Yes, now I remember...
Maybe we can use something like this:
- IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed and Secure Transport */
+#define __IPPROTO_QUIC 261 /* A UDP-Based Multiplexed and Secure Transport */
+ IPPROTO_QUIC = __IPPROTO_QUIC,
and then
MODULE_ALIAS_NET_PF_PROTO(PF_INET, __IPPROTO_QUIC)
In order to make things clearer.
What do you think?
metze
Re: [PATCH net-next v4 02/15] net: build socket infrastructure for QUIC protocol
Quoted 53 lines · Stefan Metzmacher
Am 29.10.25 um 20:57 schrieb Xin Long:
Quoted 36 lines · Stefan Metzmacher
Quoted 18 lines
Hi Xin,
Quoted 12 lines
This 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.
Signed-off-by Pengtao He <[email protected]>Signed-off-by Xin Long <[email protected]>...
Quoted 9 lines
net/quic/protocol.c line 373
Quoted 5 lines
net/quic/protocol.c new file +5
-0+module_init (quic_init); +module_exit (quic_exit); + +MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-261"); +MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-261");Shouldn't this use
MODULE_ALIAS_NET_PF_PROTO(PF_INET, IPPROTO_QUIC)instead?Hi, Stefan,
If we switch to using
MODULE_ALIAS_NET_PF_PROTO(), we still need to keep using the numeric value 261:MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); MODULE_ALIAS_NET_PF_PROTO(PF_INET6, 261);Quoted 6 lines
IPPROTO_QUICis defined as an enum, not a macro. SinceMODULE_ALIAS_NET_PF_PROTO()relies on__stringify(proto), it can’t stringify enum values correctly, and it would generate:alias: net-pf-10-proto-IPPROTO_QUIC
alias: net-pf-2-proto-IPPROTO_QUICYes, now I remember...
Maybe we can use something like this:
(quoted diff) +2
-1- IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed and Secure Transport */ +#define __IPPROTO_QUIC 261 /* A UDP-Based Multiplexed and Secure Transport */ + IPPROTO_QUIC = __IPPROTO_QUIC,
Quoted 8 lines
and then
MODULE_ALIAS_NET_PF_PROTO(PF_INET, __IPPROTO_QUIC)In order to make things clearer.
What do you think?
That might be a good idea to make things clearer later on.
But for now, I’d prefer not to add something special just for QUIC in include/uapi/linux/in.h. We can revisit it later together with SCTP, L2TP, and SMC to keep things consistent.
Thanks.
Re: [PATCH net-next v4 02/15] net: build socket infrastructure for QUIC protocol
Am 30.10.25 um 15:13 schrieb Xin Long:
Quoted 60 lines · Stefan Metzmacher
Quoted 59 lines
Am 29.10.25 um 20:57 schrieb Xin Long:
Quoted 36 lines · Stefan Metzmacher
Quoted 18 lines
Hi Xin,
Quoted 12 lines
This 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.
Signed-off-by Pengtao He <[email protected]>Signed-off-by Xin Long <[email protected]>...
Quoted 9 lines
net/quic/protocol.c line 373
Quoted 5 lines
net/quic/protocol.c new file +5
-0+module_init (quic_init); +module_exit (quic_exit); + +MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-261"); +MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-261");Shouldn't this use
MODULE_ALIAS_NET_PF_PROTO(PF_INET, IPPROTO_QUIC)instead?Hi, Stefan,
If we switch to using
MODULE_ALIAS_NET_PF_PROTO(), we still need to keep using the numeric value 261:MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); MODULE_ALIAS_NET_PF_PROTO(PF_INET6, 261);Quoted 6 lines
IPPROTO_QUICis defined as an enum, not a macro. SinceMODULE_ALIAS_NET_PF_PROTO()relies on__stringify(proto), it can’t stringify enum values correctly, and it would generate:alias: net-pf-10-proto-IPPROTO_QUIC
alias: net-pf-2-proto-IPPROTO_QUICYes, now I remember...
Maybe we can use something like this:
(quoted diff) +2
-1- IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed and Secure Transport */ +#define __IPPROTO_QUIC 261 /* A UDP-Based Multiplexed and Secure Transport */ + IPPROTO_QUIC = __IPPROTO_QUIC,and then
MODULE_ALIAS_NET_PF_PROTO(PF_INET, __IPPROTO_QUIC)In order to make things clearer.
Quoted 7 lines
Quoted 2 lines
What do you think?
That might be a good idea to make things clearer later on.
But for now, I’d prefer not to add something special just for QUIC in
include/uapi/linux/in.h. We can revisit it later together with SCTP, L2TP, and SMC to keep things consistent.
Ok, maybe this would do it for now?
MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); /* IPPROTO_QUIC == 261 */
I'll do the same for IPPROTO_SMBDIRECT...
metze
Re: [PATCH net-next v4 02/15] net: build socket infrastructure for QUIC protocol
Quoted 66 lines · Stefan Metzmacher
Am 30.10.25 um 15:13 schrieb Xin Long:
Quoted 64 lines · Stefan Metzmacher
Quoted 53 lines
Am 29.10.25 um 20:57 schrieb Xin Long:
Quoted 36 lines · Stefan Metzmacher
Quoted 18 lines
Hi Xin,
Quoted 12 lines
This 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.
Signed-off-by Pengtao He <[email protected]>Signed-off-by Xin Long <[email protected]>...
Quoted 9 lines
net/quic/protocol.c line 373
Quoted 5 lines
net/quic/protocol.c new file +5
-0+module_init (quic_init); +module_exit (quic_exit); + +MODULE_ALIAS("net-pf-" __stringify(PF_INET) "-proto-261"); +MODULE_ALIAS("net-pf-" __stringify(PF_INET6) "-proto-261");Shouldn't this use
MODULE_ALIAS_NET_PF_PROTO(PF_INET, IPPROTO_QUIC)instead?Hi, Stefan,
If we switch to using
MODULE_ALIAS_NET_PF_PROTO(), we still need to keep using the numeric value 261:MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); MODULE_ALIAS_NET_PF_PROTO(PF_INET6, 261);Quoted 6 lines
IPPROTO_QUICis defined as an enum, not a macro. SinceMODULE_ALIAS_NET_PF_PROTO()relies on__stringify(proto), it can’t stringify enum values correctly, and it would generate:alias: net-pf-10-proto-IPPROTO_QUIC
alias: net-pf-2-proto-IPPROTO_QUICYes, now I remember...
Maybe we can use something like this:
(quoted diff) +2
-1- IPPROTO_QUIC = 261, /* A UDP-Based Multiplexed and Secure Transport */ +#define __IPPROTO_QUIC 261 /* A UDP-Based Multiplexed and Secure Transport */ + IPPROTO_QUIC = __IPPROTO_QUIC,Quoted 8 lines
and then
MODULE_ALIAS_NET_PF_PROTO(PF_INET, __IPPROTO_QUIC)In order to make things clearer.
What do you think?
That might be a good idea to make things clearer later on.
Quoted 8 lines
Quoted 3 lines
But for now, I’d prefer not to add something special just for QUIC in
include/uapi/linux/in.h. We can revisit it later together with SCTP, L2TP, and SMC to keep things consistent.Ok, maybe this would do it for now?
MODULE_ALIAS_NET_PF_PROTO(PF_INET, 261); /*IPPROTO_QUIC== 261 */
Yep, fine by me. :-)
Quoted 3 lines
I'll do the same for
IPPROTO_SMBDIRECT...metze
Paolo Abeni
maintainerRe: [PATCH net-next v4 02/15] net: build socket infrastructure for QUIC protocol
In reply to Xin LongOn 10/29/25 3:35 PM, Xin Long wrote:
[...]
Quoted 21 lines
net/quic/protocol.c new file +21
-0+static struct ctl_table quic_table[] = { + { + .procname ="quic_mem" , + .data = &sysctl_quic_mem, + .maxlen =sizeof (sysctl_quic_mem), + .mode =0644 , + .proc_handler = proc_doulongvec_minmax + }, + { + .procname ="quic_rmem" , + .data = &sysctl_quic_rmem, + .maxlen =sizeof (sysctl_quic_rmem), + .mode =0644 , + .proc_handler = proc_dointvec, + }, + { + .procname ="quic_wmem" , + .data = &sysctl_quic_wmem, + .maxlen =sizeof (sysctl_quic_wmem), + .mode =0644 , + .proc_handler = proc_dointvec,
net/quic/protocol.c line 85
Quoted 10 lines
net/quic/protocol.c new file +10
-0+ }, + { + .procname = "alpn_demux", + .data = &sysctl_quic_alpn_demux, + .maxlen = sizeof(sysctl_quic_alpn_demux), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + },
I'm sorry for not nothing this before, but please add a Documentation entry for the above sysctl.
/P
Re: [PATCH net-next v4 02/15] net: build socket infrastructure for QUIC protocol
Quoted 35 lines · Paolo Abeni
On 10/29/25 3:35 PM, Xin Long wrote:
[...]Quoted 21 lines
net/quic/protocol.c new file +21
-0+static struct ctl_table quic_table[] = { + { + .procname ="quic_mem" , + .data = &sysctl_quic_mem, + .maxlen =sizeof (sysctl_quic_mem), + .mode =0644 , + .proc_handler = proc_doulongvec_minmax + }, + { + .procname ="quic_rmem" , + .data = &sysctl_quic_rmem, + .maxlen =sizeof (sysctl_quic_rmem), + .mode =0644 , + .proc_handler = proc_dointvec, + }, + { + .procname ="quic_wmem" , + .data = &sysctl_quic_wmem, + .maxlen =sizeof (sysctl_quic_wmem), + .mode =0644 , + .proc_handler = proc_dointvec,net/quic/protocol.c line 85
Quoted 10 lines
net/quic/protocol.c new file +10
-0+ }, + { + .procname = "alpn_demux", + .data = &sysctl_quic_alpn_demux, + .maxlen = sizeof(sysctl_quic_alpn_demux), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + },
Quoted 3 lines
I'm sorry for not nothing this before, but please add a Documentation entry for the above sysctl.
Sure, will do, thanks.
PATCH v4 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.
---
v3:
- Rework hashtables: split into two types and size them based on totalram_pages(), similar to SCTP (reported by Paolo).
- struct quic_shash_table: use rwlock instead of spinlock.
- quic_data_from/to_string(): add safety and common-case checks (noted by Paolo).
v4:
- Handle the error returned by quic_hash_tables_init() properly (reported by Simon).
- Use vmalloc() to simplify hashtable allocations (suggested by Paolo).
- Replace rwlock_t with spinlock_t and use hlist_nulls_head in quic_shash_head for lockless lookup/access (suggested by Paolo).
- Define QUIC_PN_BITS to replace a magical number in quic_get_num() (reported by Paolo)
- Rename several hash-related functions:
* quic_(listen_)sock_hash() → quic_(listen_)sock_head()
* quic_(listen_)sock_head() → quic_(listen_)sock_hash()
* quic_shash() → quic_addr_hash()
* quic_ahash() → call its code directly in quic_sock_hash().
- Include net in the hash calculations in quic_listen_sock_hash() and quic_udp_sock_head(), and include len in quic_source_conn_id_head().
--- net/quic/Makefile | 2 +- net/quic/common.c | 577 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/common.h | 205 ++++++++++++++++ net/quic/protocol.c | 7 + net/quic/socket.c | 4 + net/quic/socket.h | 21 ++ 6 files changed, 815 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 +577 -0
577 lines not on this page · raw message
net/quic/common.h new file +205 -0
205 lines not on this page · raw message
net/quic/protocol.c +7 -0
@@ -333,6 +333,10 @@ static __init int quic_init(void)if (err) goto err_percpu_counter; + err =quic_hash_tables_init (); +if (err) + goto err_hash; + err =register_pernet_subsys (&quic_net_ops);if (err) goto err_def_ops;
@@ -350,6 +354,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;
@@ -362,6 +368,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
@@ -52,6 +52,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)); +sk_sockets_allocated_dec (sk);sock_prot_inuse_add (sock_net (sk), sk->sk_prot,-1 ); }
net/quic/socket.h +21 -0
@@ -10,6 +10,8 @@#include <net/udp_tunnel.h> +#include "common.h" +#include "protocol.h" extern struct proto quic_prot;
@@ -25,6 +27,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 {
@@ -42,6 +48,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
Re: [PATCH net-next v4 03/15] quic: provide common utilities and data structures
Quoted 7 lines · Xin Long
This 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.
Quoted 5 lines
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.
Signed-off-by Xin Long <[email protected]>
PATCH v4 4/15 quic: provide family ops for address and protocol
In reply to Xin LongIntroduce QUIC address and protocol family operations to handle IPv4/IPv6 specifics consistently, similar to SCTP. The new quic_family.{c,h} provide helpers for routing, address parsing, skb transmit handling, ECN, preferred address encoding, address comparison, and MTU reporting.
This consolidates protocol-family logic and enables cleaner dual-stack support in the QUIC socket implementation.
---
v2:
- Add more checks for addrs in .get_user_addr() and .get_pref_addr().
- Consider sk_bound_dev_if in .udp_conf_init() and .flow_route() to support vrf.
v3:
- Remove quic_addr_family/proto_ops abstraction; use if statements to reduce indirect call overhead (suggested by Paolo).
- quic_v6_set_sk_addr(): add quic_v6_copy_sk_addr() helper to avoid duplicate code (noted by Paolo).
- quic_v4_flow_route(): use flowi4_dscp per latest net-next changes.
v4:
- Remove unnecessary _fl variable from flow_route() functions (noted by Paolo).
- Fix coding style of ?: operator (noted by Paolo).
--- net/quic/Makefile | 2 +- net/quic/family.c | 585 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/family.h | 41 ++++ net/quic/protocol.c | 2 +- net/quic/socket.c | 4 +- net/quic/socket.h | 1 + 6 files changed, 631 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 +585 -0
585 lines not on this page · raw message
net/quic/family.h new file +41 -0
41 lines not on this page · raw message
net/quic/protocol.c +1 -1
@@ -44,7 +44,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
@@ -116,7 +116,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); }
@@ -130,7 +130,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
@@ -11,6 +11,7 @@#include <net/udp_tunnel.h> #include "common.h" +#include "family.h" #include "protocol.h"
Signature · 1 line
2.47.1
Paolo Abeni
maintainerRe: [PATCH net-next v4 04/15] quic: provide family ops for address and protocol
Quoted 16 lines · Xin Long
net/quic/family.c new file +16
-0+static int quic_v4_flow_route (struct sock *sk,union quic_addr *da,union quic_addr *sa, +struct flowi *fl) +{ +struct flowi4 *fl4; +struct rtable *rt; + +if (__sk_dst_check (sk,0 )) +return 1 ; + +memset (fl,0x00 ,sizeof (*fl)); + fl4 = &fl->u.ip4; + fl4->saddr = sa->v4.sin_addr.s_addr; + fl4->fl4_sport = sa->v4.sin_port; + fl4->daddr = da->v4.sin_addr.s_addr; + fl4->fl4_dport = da->v4.sin_port; + fl4->flowi4_proto =IPPROTO_UDP ;
net/quic/family.c line 84
Quoted 10 lines
net/quic/family.c new file +10
-0+ fl4->flowi4_oif = sk->sk_bound_dev_if; + + fl4->flowi4_scope =ip_sock_rt_scope (sk); + fl4->flowi4_dscp =inet_sk_dscp (inet_sk (sk)); + + rt =ip_route_output_key (sock_net (sk), fl4); +if (IS_ERR (rt)) +return PTR_ERR (rt); + +if (!sa->v4.sin_family) {
The above check is strange. Any special reason to not use quic_v4_is_any_addr()?
Quoted 32 lines
net/quic/family.c new file +32
-0+ sa->v4.sin_family =AF_INET ; + sa->v4.sin_addr.s_addr = fl4->saddr; + } +sk_setup_caps (sk, &rt->dst); +return 0 ; +} + +static int quic_v6_flow_route (struct sock *sk,union quic_addr *da,union quic_addr *sa, +struct flowi *fl) +{ +struct ipv6_pinfo *np =inet6_sk (sk); +struct ip6_flowlabel *flowlabel; +struct dst_entry *dst; +struct flowi6 *fl6; + +if (__sk_dst_check (sk, np->dst_cookie)) +return 1 ; + +memset (fl,0x00 ,sizeof (*fl)); + fl6 = &fl->u.ip6; + fl6->saddr = sa->v6.sin6_addr; + fl6->fl6_sport = sa->v6.sin6_port; + fl6->daddr = da->v6.sin6_addr; + fl6->fl6_dport = da->v6.sin6_port; + fl6->flowi6_proto =IPPROTO_UDP ; + fl6->flowi6_oif = sk->sk_bound_dev_if; + +if (inet6_test_bit (SNDFLOW , sk)) { + fl6->flowlabel = (da->v6.sin6_flowinfo &IPV6_FLOWINFO_MASK ); +if (fl6->flowlabel &IPV6_FLOWLABEL_MASK ) { + flowlabel =fl6_sock_lookup (sk, fl6->flowlabel); +if (IS_ERR (flowlabel))
net/quic/family.c line 126
Quoted 10 lines
net/quic/family.c new file +10
-0+return -EINVAL ; +fl6_sock_release (flowlabel); + } + } + + dst =ip6_dst_lookup_flow (sock_net (sk), sk, fl6,NULL ); +if (IS_ERR (dst)) +return PTR_ERR (dst); + +if (!sa->v6.sin6_family) {
(similar question here)
[...]
net/quic/family.c line 213
Quoted 5 lines
net/quic/family.c new file +5
-0+static int quic_v4_get_mtu_info (struct sk_buff *skb,u32 *info) +{ +struct icmphdr *hdr; + + hdr = (struct icmphdr *)(skb_network_header (skb) -sizeof (struct icmphdr ));
Noting the above relies on headers being already pulled in the linear part. Later patch will do skb_linarize(), but that looks overkill and should hit performance badly. Instead you should use pskb_may_pull() && friends.
Quoted 35 lines
net/quic/family.c new file +35
-0+if (hdr->type ==ICMP_DEST_UNREACH && hdr->code ==ICMP_FRAG_NEEDED ) { + *info =ntohs (hdr->un.frag.mtu); +return 0 ; + } + +/* Defer other types' processing to UDP error handler. */ +return 1 ; +} + +static int quic_v6_get_mtu_info (struct sk_buff *skb,u32 *info) +{ +struct icmp6hdr *hdr; + + hdr = (struct icmp6hdr *)(skb_network_header (skb) -sizeof (struct icmp6hdr )); +if (hdr->icmp6_type ==ICMPV6_PKT_TOOBIG ) { + *info =ntohl (hdr->icmp6_mtu); +return 0 ; + } + +/* Defer other types' processing to UDP error handler. */ +return 1 ; +} + +static u8 quic_v4_get_msg_ecn (struct sk_buff *skb) +{ +return (ip_hdr (skb)->tos &INET_ECN_MASK ); +} + +static u8 quic_v6_get_msg_ecn (struct sk_buff *skb) +{ +return (ipv6_get_dsfield (ipv6_hdr (skb)) &INET_ECN_MASK ); +} + +static int quic_v4_get_user_addr (struct sock *sk,union quic_addr *a,struct sockaddr *addr, +int addr_len)
net/quic/family.c line 258
Quoted 10 lines
net/quic/family.c new file +10
-0+{ +u32 len =sizeof (struct sockaddr_in ); + +if (addr_len < len || addr->sa_family !=AF_INET ) +return 1 ; +if (ipv4_is_multicast (quic_addr (addr)->v4.sin_addr.s_addr)) +return 1 ; +memcpy (a, addr, len); +return 0 ; +}
It looks like the above function is not used in this series?!? (well it's called by quic_get_user_addr() which in turn is unsed.
Perhaps drop from here and add later as needed?
Also the name sounds possibly misleading, I read it as it should copy data to user-space and return value could possibly be an errnum.
Quoted 9 lines
net/quic/family.c new file +9
-0+static void quic_v4_get_pref_addr (struct sock *sk,union quic_addr *addr,u8 **pp,u32 *plen) +{ +u8 *p = *pp; + +memcpy (&addr->v4.sin_addr, p,QUIC_ADDR4_LEN ); + p +=QUIC_ADDR4_LEN ; +memcpy (&addr->v4.sin_port, p,QUIC_PORT_LEN ); + p +=QUIC_PORT_LEN ; + addr->v4.sin_family =AF_INET ;
net/quic/family.c line 303
Quoted 10 lines
net/quic/family.c new file +10
-0+/* Skip over IPv6 address and port, not used for AF_INET sockets. */ + p +=QUIC_ADDR6_LEN ; + p +=QUIC_PORT_LEN ; + +if (!addr->v4.sin_port ||quic_v4_is_any_addr (addr) || +ipv4_is_multicast (addr->v4.sin_addr.s_addr)) +memset (addr,0 ,sizeof (*addr)); + *plen -= (p - *pp); + *pp = p; +}
Similarly unused?
Quoted 20 lines
net/quic/family.c new file +20
-0+static bool quic_v4_cmp_sk_addr (struct sock *sk,union quic_addr *a,union quic_addr *addr) +{ +if (a->v4.sin_port != addr->v4.sin_port) +return false; +if (a->v4.sin_family != addr->v4.sin_family) +return false; +if (a->v4.sin_addr.s_addr ==htonl (INADDR_ANY ) || + addr->v4.sin_addr.s_addr ==htonl (INADDR_ANY )) +return true; +return a->v4.sin_addr.s_addr == addr->v4.sin_addr.s_addr; +} + +static bool quic_v6_cmp_sk_addr (struct sock *sk,union quic_addr *a,union quic_addr *addr) +{ +if (a->v4.sin_port != addr->v4.sin_port) +return false; + +if (a->sa.sa_family ==AF_INET && addr->sa.sa_family ==AF_INET ) { +if (a->v4.sin_addr.s_addr ==htonl (INADDR_ANY ) || + addr->v4.sin_addr.s_addr ==htonl (INADDR_ANY ))
net/quic/family.c line 387
Quoted 10 lines
net/quic/family.c new file +10
-0+return true; +return a->v4.sin_addr.s_addr == addr->v4.sin_addr.s_addr; + } + +if (a->sa.sa_family != addr->sa.sa_family) { +if (ipv6_only_sock (sk)) +return false; +if (a->sa.sa_family ==AF_INET6 &&ipv6_addr_any (&a->v6.sin6_addr)) +return true; + if (a->sa.sa_family == AF_INET && addr->sa.sa_family == AF_INET6 &&
Below this code assumes that sa_family is either AF_INET or AF_INET6. If such assumtion hold, you should use here, too. and drop the 'addr->sa.sa_family == AF_INET6' condition.
net/quic/family.c line 394
Quoted 7 lines
net/quic/family.c new file +7
-0+ ipv6_addr_v4mapped(&addr->v6.sin6_addr) && + addr->v6.sin6_addr.s6_addr32[3] == a->v4.sin_addr.s_addr) +return true; +if (addr->sa.sa_family ==AF_INET && a->sa.sa_family ==AF_INET6 && +ipv6_addr_v4mapped (&a->v6.sin6_addr) && + a->v6.sin6_addr.s6_addr32[3 ] == addr->v4.sin_addr.s_addr) +return true;
Nothing this branch does not handle the 'ipv6_addr_any(&addr->v6.sin6_addr)'
/P
Re: [PATCH net-next v4 04/15] quic: provide family ops for address and protocol
Quoted 23 lines · Paolo Abeni
Quoted 21 lines · Xin Long
net/quic/family.c new file +21
-0+static int quic_v4_flow_route (struct sock *sk,union quic_addr *da,union quic_addr *sa, +struct flowi *fl) +{ +struct flowi4 *fl4; +struct rtable *rt; + +if (__sk_dst_check (sk,0 )) +return 1 ; + +memset (fl,0x00 ,sizeof (*fl)); + fl4 = &fl->u.ip4; + fl4->saddr = sa->v4.sin_addr.s_addr; + fl4->fl4_sport = sa->v4.sin_port; + fl4->daddr = da->v4.sin_addr.s_addr; + fl4->fl4_dport = da->v4.sin_port; + fl4->flowi4_proto =IPPROTO_UDP ; + fl4->flowi4_oif = sk->sk_bound_dev_if; + + fl4->flowi4_scope =ip_sock_rt_scope (sk); + fl4->flowi4_dscp =inet_sk_dscp (inet_sk (sk)); +
Quoted 9 lines
net/quic/family.c line 84
Quoted 5 lines
net/quic/family.c new file +5
-0+ rt =ip_route_output_key (sock_net (sk), fl4); +if (IS_ERR (rt)) +return PTR_ERR (rt); + +if (!sa->v4.sin_family) {The above check is strange. Any special reason to not use
quic_v4_is_any_addr()?
quic_v4_is_any_addr() looks better, will try to replace it.
Quoted 36 lines
Quoted 36 lines
net/quic/family.c new file +36
-0+ sa->v4.sin_family =AF_INET ; + sa->v4.sin_addr.s_addr = fl4->saddr; + } +sk_setup_caps (sk, &rt->dst); +return 0 ; +} + +static int quic_v6_flow_route (struct sock *sk,union quic_addr *da,union quic_addr *sa, +struct flowi *fl) +{ +struct ipv6_pinfo *np =inet6_sk (sk); +struct ip6_flowlabel *flowlabel; +struct dst_entry *dst; +struct flowi6 *fl6; + +if (__sk_dst_check (sk, np->dst_cookie)) +return 1 ; + +memset (fl,0x00 ,sizeof (*fl)); + fl6 = &fl->u.ip6; + fl6->saddr = sa->v6.sin6_addr; + fl6->fl6_sport = sa->v6.sin6_port; + fl6->daddr = da->v6.sin6_addr; + fl6->fl6_dport = da->v6.sin6_port; + fl6->flowi6_proto =IPPROTO_UDP ; + fl6->flowi6_oif = sk->sk_bound_dev_if; + +if (inet6_test_bit (SNDFLOW , sk)) { + fl6->flowlabel = (da->v6.sin6_flowinfo &IPV6_FLOWINFO_MASK ); +if (fl6->flowlabel &IPV6_FLOWLABEL_MASK ) { + flowlabel =fl6_sock_lookup (sk, fl6->flowlabel); +if (IS_ERR (flowlabel)) +return -EINVAL ; +fl6_sock_release (flowlabel); + } + }
Quoted 9 lines
net/quic/family.c line 126
Quoted 6 lines
net/quic/family.c new file +6
-0+ + dst =ip6_dst_lookup_flow (sock_net (sk), sk, fl6,NULL ); +if (IS_ERR (dst)) +return PTR_ERR (dst); + +if (!sa->v6.sin6_family) {(similar question here)
right.
Quoted 11 lines
[...]
net/quic/family.c line 213
Quoted 5 lines
net/quic/family.c new file +5
-0+static int quic_v4_get_mtu_info (struct sk_buff *skb,u32 *info) +{ +struct icmphdr *hdr; + + hdr = (struct icmphdr *)(skb_network_header (skb) -sizeof (struct icmphdr ));Noting the above relies on headers being already pulled in the linear part. Later patch will do
skb_linarize(), but that looks overkill and should hit performance badly. Instead you should usepskb_may_pull()&& friends.
This path (ICMP error path) doesn't need to parse frames and bundled packets, so yes we can use pskb_may_pull().
However, in the normal QUIC packet receive path:
- for short header packet path, the packet format is:
Before decryption:
UDP hdr | QUIC hdr | conn_id | encrypted text
After decryption:
UDP hdr | QUIC hdr | conn_id | frame1 hdr | frame1 data | frame2 hdr
| frame2 data ...
When parsing the frames, it's hard to do it without linearizing the skb, also fields in these frame headers are always variable-length integers, making the parsing more difficult if it's not a linearized buffer.
- for long header (handshake) packet path, more complex, packets can be bundled like:
UDP hdr | QUIC hdr1 | encrypted text | QUIC hdr2 | encrypted text |
...
Quoted 50 lines
Quoted 36 lines
net/quic/family.c new file +35
-0+if (hdr->type ==ICMP_DEST_UNREACH && hdr->code ==ICMP_FRAG_NEEDED ) { + *info =ntohs (hdr->un.frag.mtu); +return 0 ; + } + +/* Defer other types' processing to UDP error handler. */ +return 1 ; +} + +static int quic_v6_get_mtu_info (struct sk_buff *skb,u32 *info) +{ +struct icmp6hdr *hdr; + + hdr = (struct icmp6hdr *)(skb_network_header (skb) -sizeof (struct icmp6hdr )); +if (hdr->icmp6_type ==ICMPV6_PKT_TOOBIG ) { + *info =ntohl (hdr->icmp6_mtu); +return 0 ; + } + +/* Defer other types' processing to UDP error handler. */ +return 1 ; +} + +static u8 quic_v4_get_msg_ecn (struct sk_buff *skb) +{ +return (ip_hdr (skb)->tos &INET_ECN_MASK ); +} + +static u8 quic_v6_get_msg_ecn (struct sk_buff *skb) +{ +return (ipv6_get_dsfield (ipv6_hdr (skb)) &INET_ECN_MASK ); +} + +static int quic_v4_get_user_addr (struct sock *sk,union quic_addr *a,struct sockaddr *addr, +int addr_len)net/quic/family.c line 258
Quoted 10 lines
net/quic/family.c new file +10
-0+{ +u32 len =sizeof (struct sockaddr_in ); + +if (addr_len < len || addr->sa_family !=AF_INET ) +return 1 ; +if (ipv4_is_multicast (quic_addr (addr)->v4.sin_addr.s_addr)) +return 1 ; +memcpy (a, addr, len); +return 0 ; +}It looks like the above function is not used in this series?!? (well it's called by
quic_get_user_addr()which in turn is unsed.
Quoted 1 line
Perhaps drop from here and add later as needed?
Sure, I will drop:
quic_seq_dump_addr()quic_get_msg_ecn()quic_get_user_addr()quic_get_pref_addr()quic_set_pref_addr()quic_set_sk_addr()quic_set_sk_ecn()
Quoted 4 lines
Also the name sounds possibly misleading, I read it as it should copy data to user-space and return value could possibly be an errnum.
Maybe quic_get_addr_from_user()? and I will return -EINVAL instead of 1 in the err path.
Quoted 48 lines
Quoted 9 lines
net/quic/family.c new file +9
-0+static void quic_v4_get_pref_addr (struct sock *sk,union quic_addr *addr,u8 **pp,u32 *plen) +{ +u8 *p = *pp; + +memcpy (&addr->v4.sin_addr, p,QUIC_ADDR4_LEN ); + p +=QUIC_ADDR4_LEN ; +memcpy (&addr->v4.sin_port, p,QUIC_PORT_LEN ); + p +=QUIC_PORT_LEN ; + addr->v4.sin_family =AF_INET ;net/quic/family.c line 303
Quoted 10 lines
net/quic/family.c new file +10
-0+/* Skip over IPv6 address and port, not used for AF_INET sockets. */ + p +=QUIC_ADDR6_LEN ; + p +=QUIC_PORT_LEN ; + +if (!addr->v4.sin_port ||quic_v4_is_any_addr (addr) || +ipv4_is_multicast (addr->v4.sin_addr.s_addr)) +memset (addr,0 ,sizeof (*addr)); + *plen -= (p - *pp); + *pp = p; +}Similarly unused?
Quoted 26 lines
net/quic/family.c new file +26
-0+static bool quic_v4_cmp_sk_addr (struct sock *sk,union quic_addr *a,union quic_addr *addr) +{ +if (a->v4.sin_port != addr->v4.sin_port) +return false; +if (a->v4.sin_family != addr->v4.sin_family) +return false; +if (a->v4.sin_addr.s_addr ==htonl (INADDR_ANY ) || + addr->v4.sin_addr.s_addr ==htonl (INADDR_ANY )) +return true; +return a->v4.sin_addr.s_addr == addr->v4.sin_addr.s_addr; +} + +static bool quic_v6_cmp_sk_addr (struct sock *sk,union quic_addr *a,union quic_addr *addr) +{ +if (a->v4.sin_port != addr->v4.sin_port) +return false; + +if (a->sa.sa_family ==AF_INET && addr->sa.sa_family ==AF_INET ) { +if (a->v4.sin_addr.s_addr ==htonl (INADDR_ANY ) || + addr->v4.sin_addr.s_addr ==htonl (INADDR_ANY )) +return true; +return a->v4.sin_addr.s_addr == addr->v4.sin_addr.s_addr; + } + +if (a->sa.sa_family != addr->sa.sa_family) { +if (ipv6_only_sock (sk))
Quoted 8 lines
net/quic/family.c line 387
Quoted 4 lines
net/quic/family.c new file +4
-0+return false; +if (a->sa.sa_family ==AF_INET6 &&ipv6_addr_any (&a->v6.sin6_addr)) +return true; + if (a->sa.sa_family == AF_INET && addr->sa.sa_family == AF_INET6 &&Below this code assumes that
sa_familyis eitherAF_INETorAF_INET6. If such assumtion hold, you should use here, too. and drop the 'addr->sa.sa_family==AF_INET6' condition.
I agree.
Quoted 11 lines
net/quic/family.c line 394
Quoted 8 lines
net/quic/family.c new file +7
-0+ ipv6_addr_v4mapped(&addr->v6.sin6_addr) && + addr->v6.sin6_addr.s6_addr32[3] == a->v4.sin_addr.s_addr) +return true; +if (addr->sa.sa_family ==AF_INET && a->sa.sa_family ==AF_INET6 && +ipv6_addr_v4mapped (&a->v6.sin6_addr) && + a->v6.sin6_addr.s6_addr32[3 ] == addr->v4.sin_addr.s_addr) +return true;Nothing this branch does not handle the '
ipv6_addr_any(&addr->v6.sin6_addr)'
Will add a helper:
static bool quic_v4_match_v6_addr(union quic_addr *a4, union quic_addr *a6)
{
if (ipv6_addr_any(&a6->v6.sin6_addr))
return true;
if (ipv6_addr_v4mapped(&a6->v6.sin6_addr) &&
a6->v6.sin6_addr.s6_addr32[3] == a4->v4.sin_addr.s_addr)
return true;
return false;
}and change this branch to:
if (a->sa.sa_family == AF_INET)
return quic_v4_match_v6_addr(a, addr);
return quic_v4_match_v6_addr(addr, a);Thanks.
PATCH v4 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
---
v2:
- Fix a kernel API description warning, found by Jakub.
- Replace uintN_t with __uN, capitalize _UAPI_LINUX_QUIC_H, and assign explicit values for QUIC_TRANSPORT_ERROR_ enum in UAPI quic.h, suggested by David Howells.
v4:
- Use MSG_QUIC_ prefix for MSG_* flags to avoid conflicts with other protocols, such as MSG_NOTIFICATION in SCTP (reported by Thomas).
- Remove QUIC_CONG_ALG_CUBIC; only NEW RENO congestion control is supported in this version.
--- include/linux/quic.h | 19 +++ include/uapi/linux/quic.h | 235 ++++++++++++++++++++++++++++++++++++++ net/quic/socket.c | 38 ++++++ net/quic/socket.h | 7 ++ 4 files changed, 299 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 +235 -0
235 lines not on this page · raw message
net/quic/socket.c +38 -0
@@ -121,6 +121,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: +* - 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 ;
@@ -135,6 +154,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: +* - 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
@@ -9,6 +9,7 @@*/ #include <net/udp_tunnel.h> +#include <linux/quic.h> #include "common.h" #include "family.h"
@@ -29,6 +30,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;
@@ -49,6 +51,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
PATCH v4 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_exceeds(): Check a stream ID would exceed local (recv) or peer (send) limits.
- quic_stream_max_streams_update(): Determines whether a MAX_STREAMS_UNI/BIDI frame should be sent to the peer.
Note stream hash table is per socket, the operations on it are always protected by the sock lock.
---
v3:
- Merge send/recv stream helpers into unified functions to reduce code:
* quic_stream_id_send/recv() → quic_stream_id_valid()
* quic_stream_id_send/recv_closed() → quic_stream_id_closed()
* quic_stream_id_send/recv_exceeds() → quic_stream_id_exceeds() (pointed out by Paolo).
- Clarify in changelog that stream hash table is always protected by sock lock (suggested by Paolo).
- quic_stream_init/free(): adjust for new hashtable type; call quic_stream_delete() in quic_stream_free() to avoid open-coded logic.
- Receiving streams: delete stream only when fully read or reset, instead of when no data was received. Prevents freeing a stream while a FIN with no data is still queued.
v4:
- Replace struct quic_shash_table with struct hlist_head for the stream hashtable. Since they are protected by the socket lock, no per-chain lock is needed.
- Initialize stream to NULL in stream creation functions to avoid warnings from Smatch (reported by Simon).
- Allocate send streams with GFP_KERNEL_ACCOUNT and receive streams with GFP_ATOMIC | __GFP_ACCOUNT for memory accounting (suggested by Paolo).
--- net/quic/Makefile | 2 +- net/quic/socket.c | 5 + net/quic/socket.h | 8 + net/quic/stream.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/stream.h | 136 ++++++++++++ 5 files changed, 664 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
@@ -41,6 +41,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 ]));
@@ -52,6 +55,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
@@ -13,6 +13,7 @@#include "common.h" #include "family.h" +#include "stream.h" #include "protocol.h"
@@ -34,6 +35,8 @@ struct quic_sock {struct quic_data ticket;struct quic_data token;struct quic_data alpn; + +struct quic_stream_table streams; };struct quic6_sock {
@@ -71,6 +74,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 +514 -0
514 lines not on this page · raw message
net/quic/stream.h new file +136 -0
136 lines not on this page · raw message
Signature · 1 line
2.47.1
Paolo Abeni
maintainerRe: [PATCH net-next v4 06/15] quic: add stream management
On 10/29/25 3:35 PM, Xin Long wrote:
+/* Create and register new streams for sending. */
Quoted 14 lines
net/quic/stream.c new file +14
-0+static struct quic_stream *quic_stream_send_create (struct quic_stream_table *streams, +s64 max_stream_id,u8 is_serv) +{ +struct quic_stream *stream =NULL ; +s64 stream_id; + + stream_id = streams->send.next_bidi_stream_id; +if (quic_stream_id_uni (max_stream_id)) + stream_id = streams->send.next_uni_stream_id; + +/* rfc9000#section-2.1: A stream ID that is used out of order results in all streams +* of that type with lower-numbered stream IDs also being opened. +*/ +while (stream_id <= max_stream_id) {
net/quic/stream.c line 102
Quoted 10 lines
net/quic/stream.c new file +10
-0+ stream =kzalloc (sizeof (*stream),GFP_KERNEL_ACCOUNT ); +if (!stream) +return NULL ; + + stream->id = stream_id; +if (quic_stream_id_uni (stream_id)) { + stream->send.max_bytes = streams->send.max_stream_data_uni; + +if (streams->send.next_uni_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->send.next_uni_stream_id = stream_id +QUIC_STREAM_ID_STEP ;
It's unclear to me the goal the above 2 statements. Dealing with id wrap-arounds? If 'streams->send.next_uni_stream_id < stream_id + QUIC_STREAM_ID_STEP' is not true the next quic_stream_send_create() will reuse the same stream_id.
I moving the above in a separate helper with some comments would help.
Quoted 67 lines
net/quic/stream.c new file +67
-0+ streams->send.streams_uni++; + +quic_stream_add (streams, stream); + stream_id +=QUIC_STREAM_ID_STEP ; +continue ; + } + +if (streams->send.next_bidi_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->send.next_bidi_stream_id = stream_id +QUIC_STREAM_ID_STEP ; + streams->send.streams_bidi++; + +if (quic_stream_id_local (stream_id, is_serv)) { + stream->send.max_bytes = streams->send.max_stream_data_bidi_remote; + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_local; + }else { + stream->send.max_bytes = streams->send.max_stream_data_bidi_local; + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_remote; + } + stream->recv.window = stream->recv.max_bytes; + +quic_stream_add (streams, stream); + stream_id +=QUIC_STREAM_ID_STEP ; + } +return stream; +} + +/* Create and register new streams for receiving. */ +static struct quic_stream *quic_stream_recv_create (struct quic_stream_table *streams, +s64 max_stream_id,u8 is_serv) +{ +struct quic_stream *stream =NULL ; +s64 stream_id; + + stream_id = streams->recv.next_bidi_stream_id; +if (quic_stream_id_uni (max_stream_id)) + stream_id = streams->recv.next_uni_stream_id; + +/* rfc9000#section-2.1: A stream ID that is used out of order results in all streams +* of that type with lower-numbered stream IDs also being opened. +*/ +while (stream_id <= max_stream_id) { + stream =kzalloc (sizeof (*stream),GFP_ATOMIC | __GFP_ACCOUNT); +if (!stream) +return NULL ; + + stream->id = stream_id; +if (quic_stream_id_uni (stream_id)) { + stream->recv.window = streams->recv.max_stream_data_uni; + stream->recv.max_bytes = stream->recv.window; + +if (streams->recv.next_uni_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->recv.next_uni_stream_id = stream_id +QUIC_STREAM_ID_STEP ; + streams->recv.streams_uni++; + +quic_stream_add (streams, stream); + stream_id +=QUIC_STREAM_ID_STEP ; +continue ; + } + +if (streams->recv.next_bidi_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->recv.next_bidi_stream_id = stream_id +QUIC_STREAM_ID_STEP ; + streams->recv.streams_bidi++; + +if (quic_stream_id_local (stream_id, is_serv)) { + stream->send.max_bytes = streams->send.max_stream_data_bidi_remote; + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_local; + }else {
net/quic/stream.c line 179
Quoted 10 lines
net/quic/stream.c new file +10
-0+ stream->send.max_bytes = streams->send.max_stream_data_bidi_local; + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_remote; + } + stream->recv.window = stream->recv.max_bytes; + +quic_stream_add (streams, stream); + stream_id +=QUIC_STREAM_ID_STEP ; + } +return stream; +}
The above 2 functions has a lot of code in common. I think you could deduplicate it by:
- defining a named type for quic_stream_table.{send,recv}
- define a generic quic_stream_create() helper using an additonal argument for the relevant table.{send,recv}
- replace the above 2 functions with a single invocation to such helper.
It looks like there are more de-dup opportunity below.
Quoted 41 lines
net/quic/stream.c new file +41
-0+ +/* Check if a send or receive stream ID is already closed. */ +static bool quic_stream_id_closed (struct quic_stream_table *streams,s64 stream_id,bool send) +{ +if (quic_stream_id_uni (stream_id)) { +if (send) +return stream_id < streams->send.next_uni_stream_id; +return stream_id < streams->recv.next_uni_stream_id; + } +if (send) +return stream_id < streams->send.next_bidi_stream_id; +return stream_id < streams->recv.next_bidi_stream_id; +} + +/* Check if a stream ID would exceed local (recv) or peer (send) limits. */ +bool quic_stream_id_exceeds (struct quic_stream_table *streams,s64 stream_id,bool send) +{ +u64 nstreams; + +if (!send) { +if (quic_stream_id_uni (stream_id)) +return stream_id > streams->recv.max_uni_stream_id; +return stream_id > streams->recv.max_bidi_stream_id; + } + +if (quic_stream_id_uni (stream_id)) { +if (stream_id > streams->send.max_uni_stream_id) +return true; + stream_id -= streams->send.next_uni_stream_id; + nstreams =quic_stream_id_to_streams (stream_id); +return nstreams + streams->send.streams_uni > streams->send.max_streams_uni; + } + +if (stream_id > streams->send.max_bidi_stream_id) +return true; + stream_id -= streams->send.next_bidi_stream_id; + nstreams =quic_stream_id_to_streams (stream_id); +return nstreams + streams->send.streams_bidi > streams->send.max_streams_bidi; +} + +/* Get or create a send stream by ID. */
net/quic/stream.c line 230
Quoted 10 lines
net/quic/stream.c new file +10
-0+struct quic_stream *quic_stream_send_get (struct quic_stream_table *streams,s64 stream_id, +u32 flags,bool is_serv) +{ +struct quic_stream *stream; + +if (!quic_stream_id_valid (stream_id, is_serv, true)) +return ERR_PTR (-EINVAL ); + + stream =quic_stream_find (streams, stream_id); +if (stream) {
You should add some comments and possibly lockdep annotation/static check about the expected locking for the whole stream lifecycle.
/P
Re: [PATCH net-next v4 06/15] quic: add stream management
Quoted 33 lines · Paolo Abeni
On 10/29/25 3:35 PM, Xin Long wrote:
+/* Create and register new streams for sending. */Quoted 14 lines
net/quic/stream.c new file +14
-0+static struct quic_stream *quic_stream_send_create (struct quic_stream_table *streams, +s64 max_stream_id,u8 is_serv) +{ +struct quic_stream *stream =NULL ; +s64 stream_id; + + stream_id = streams->send.next_bidi_stream_id; +if (quic_stream_id_uni (max_stream_id)) + stream_id = streams->send.next_uni_stream_id; + +/* rfc9000#section-2.1: A stream ID that is used out of order results in all streams +* of that type with lower-numbered stream IDs also being opened. +*/ +while (stream_id <= max_stream_id) {net/quic/stream.c line 102
Quoted 10 lines
net/quic/stream.c new file +10
-0+ stream =kzalloc (sizeof (*stream),GFP_KERNEL_ACCOUNT ); +if (!stream) +return NULL ; + + stream->id = stream_id; +if (quic_stream_id_uni (stream_id)) { + stream->send.max_bytes = streams->send.max_stream_data_uni; + +if (streams->send.next_uni_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->send.next_uni_stream_id = stream_id +QUIC_STREAM_ID_STEP ;It's unclear to me the goal the above 2 statements. Dealing with id wrap-arounds? If '
streams->send.next_uni_stream_id<stream_id+QUIC_STREAM_ID_STEP' is not true the nextquic_stream_send_create()will reuse the samestream_id.
Quoted 2 lines
I moving the above in a separate helper with some comments would help.
I will add a macro for this:
#define quic_stream_id_next_update(limits, type, id) \
do { \
if ((limits)->next_##type##_stream_id < (id) +
QUIC_STREAM_ID_STEP) \
(limits)->next_##type##_stream_id = (id) +
QUIC_STREAM_ID_STEP; \
(limits)->streams_##type++;
\
} while (0)So that we can use it to update both next_uni_stream_id and next_bidi_stream_id.
Quoted 77 lines
Quoted 77 lines
net/quic/stream.c new file +76
-0+ streams->send.streams_uni++; + +quic_stream_add (streams, stream); + stream_id +=QUIC_STREAM_ID_STEP ; +continue ; + } + +if (streams->send.next_bidi_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->send.next_bidi_stream_id = stream_id +QUIC_STREAM_ID_STEP ; + streams->send.streams_bidi++; + +if (quic_stream_id_local (stream_id, is_serv)) { + stream->send.max_bytes = streams->send.max_stream_data_bidi_remote; + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_local; + }else { + stream->send.max_bytes = streams->send.max_stream_data_bidi_local; + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_remote; + } + stream->recv.window = stream->recv.max_bytes; + +quic_stream_add (streams, stream); + stream_id +=QUIC_STREAM_ID_STEP ; + } +return stream; +} + +/* Create and register new streams for receiving. */ +static struct quic_stream *quic_stream_recv_create (struct quic_stream_table *streams, +s64 max_stream_id,u8 is_serv) +{ +struct quic_stream *stream =NULL ; +s64 stream_id; + + stream_id = streams->recv.next_bidi_stream_id; +if (quic_stream_id_uni (max_stream_id)) + stream_id = streams->recv.next_uni_stream_id; + +/* rfc9000#section-2.1: A stream ID that is used out of order results in all streams +* of that type with lower-numbered stream IDs also being opened. +*/ +while (stream_id <= max_stream_id) { + stream =kzalloc (sizeof (*stream),GFP_ATOMIC | __GFP_ACCOUNT); +if (!stream) +return NULL ; + + stream->id = stream_id; +if (quic_stream_id_uni (stream_id)) { + stream->recv.window = streams->recv.max_stream_data_uni; + stream->recv.max_bytes = stream->recv.window; + +if (streams->recv.next_uni_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->recv.next_uni_stream_id = stream_id +QUIC_STREAM_ID_STEP ; + streams->recv.streams_uni++; + +quic_stream_add (streams, stream); + stream_id +=QUIC_STREAM_ID_STEP ; +continue ; + } + +if (streams->recv.next_bidi_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->recv.next_bidi_stream_id = stream_id +QUIC_STREAM_ID_STEP ; + streams->recv.streams_bidi++; + +if (quic_stream_id_local (stream_id, is_serv)) { + stream->send.max_bytes = streams->send.max_stream_data_bidi_remote; + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_local; + }else { + stream->send.max_bytes = streams->send.max_stream_data_bidi_local; + stream->recv.max_bytes = streams->recv.max_stream_data_bidi_remote; + } + stream->recv.window = stream->recv.max_bytes; + +quic_stream_add (streams, stream); + stream_id +=QUIC_STREAM_ID_STEP ; + } +return stream;
Quoted 8 lines
Quoted 1 line
+}
The above 2 functions has a lot of code in common. I think you could deduplicate it by:
- defining a named type for quic_stream_table.{send,recv}
- define a generic /() helper using an additonal
argument for the relevant table.{send,recv}
- replace the above 2 functions with a single invocation to such helper.
This is a very smart idea!
It will dedup not only quic_stream_recv_create(), but also quic_stream_get_param() and quic_stream_set_param().
I will define a type named 'struct quic_stream_limits'. Note that, since we must pass 'bool send' to quic_stream_create() for setting the fields in a single 'stream' .
if (quic_stream_id_uni(stream_id)) {
if (send) {
stream->send.max_bytes = limits->max_stream_data_uni;
} else {
stream->recv.max_bytes = limits->max_stream_data_uni;
stream->recv.window = stream->recv.max_bytes;
}I'm planning not to pass additional argument of table.{send,recv}, but do this in quic_stream_create():
struct quic_stream_limits *limits = &streams->send;
gfp_t gfp = GFP_KERNEL_ACCOUNT;
if (!send) {
limits = &streams->recv;
gfp = GFP_ATOMIC | __GFP_ACCOUNT;
}Quoted 3 lines
It looks like there are more de-dup opportunity below.
Yes, the difference is only the variable name _uni_ and _bidi_. I'm planning to de-dup them with macros like:
#define quic_stream_id_below_next(streams, type, id, send) \
((send) ? ((id) < (streams)->send.next_##type##_stream_id) : \
((id) < (streams)->recv.next_##type##_stream_id))/* Check if a send or receive stream ID is already closed. */ static bool quic_stream_id_closed(struct quic_stream_table *streams, s64 stream_id, bool send)
{
if (quic_stream_id_uni(stream_id))
return quic_stream_id_below_next(streams, uni, stream_id, send);
return quic_stream_id_below_next(streams, bidi, stream_id, send);
}#define quic_stream_id_above_max(streams, type, id) \
(((id) > (streams)->send.max_##type##_stream_id) ? true : \
(quic_stream_id_to_streams((id) -
(streams)->send.next_##type##_stream_id) + \
(streams)->send.streams_##type >(streams)->send.max_streams_##type))
/* Check if a stream ID would exceed local (recv) or peer (send) limits. */ bool quic_stream_id_exceeds(struct quic_stream_table *streams, s64 stream_id, bool send)
{
if (!send) {
if (quic_stream_id_uni(stream_id))
return stream_id > streams->recv.max_uni_stream_id;
return stream_id > streams->recv.max_bidi_stream_id;
}
if (quic_stream_id_uni(stream_id))
return quic_stream_id_above_max(streams, uni, stream_id);
return quic_stream_id_above_max(streams, bidi, stream_id);
}Do you think it's worth it?
Quoted 46 lines
Quoted 46 lines
net/quic/stream.c new file +46
-0+ +/* Check if a send or receive stream ID is already closed. */ +static bool quic_stream_id_closed (struct quic_stream_table *streams,s64 stream_id,bool send) +{ +if (quic_stream_id_uni (stream_id)) { +if (send) +return stream_id < streams->send.next_uni_stream_id; +return stream_id < streams->recv.next_uni_stream_id; + } +if (send) +return stream_id < streams->send.next_bidi_stream_id; +return stream_id < streams->recv.next_bidi_stream_id; +} + +/* Check if a stream ID would exceed local (recv) or peer (send) limits. */ +bool quic_stream_id_exceeds (struct quic_stream_table *streams,s64 stream_id,bool send) +{ +u64 nstreams; + +if (!send) { +if (quic_stream_id_uni (stream_id)) +return stream_id > streams->recv.max_uni_stream_id; +return stream_id > streams->recv.max_bidi_stream_id; + } + +if (quic_stream_id_uni (stream_id)) { +if (stream_id > streams->send.max_uni_stream_id) +return true; + stream_id -= streams->send.next_uni_stream_id; + nstreams =quic_stream_id_to_streams (stream_id); +return nstreams + streams->send.streams_uni > streams->send.max_streams_uni; + } + +if (stream_id > streams->send.max_bidi_stream_id) +return true; + stream_id -= streams->send.next_bidi_stream_id; + nstreams =quic_stream_id_to_streams (stream_id); +return nstreams + streams->send.streams_bidi > streams->send.max_streams_bidi; +} + +/* Get or create a send stream by ID. */ +struct quic_stream *quic_stream_send_get (struct quic_stream_table *streams,s64 stream_id, +u32 flags,bool is_serv) +{ +struct quic_stream *stream; +
Quoted 9 lines
net/quic/stream.c line 230
Quoted 5 lines
net/quic/stream.c new file +5
-0+if (!quic_stream_id_valid (stream_id, is_serv, true)) +return ERR_PTR (-EINVAL ); + + stream =quic_stream_find (streams, stream_id); +if (stream) {You should add some comments and possibly lockdep annotation/static check about the expected locking for the whole stream lifecycle.
sk is not seen in this file, so I will add some comments to describe this will also be called under the sock lock.
Thanks.
Paolo Abeni
maintainerRe: [PATCH net-next v4 06/15] quic: add stream management
Quoted 48 lines · Xin Long
Quoted 33 lines · Paolo Abeni
On 10/29/25 3:35 PM, Xin Long wrote:
+/* Create and register new streams for sending. */Quoted 14 lines
net/quic/stream.c new file +14
-0+static struct quic_stream *quic_stream_send_create (struct quic_stream_table *streams, +s64 max_stream_id,u8 is_serv) +{ +struct quic_stream *stream =NULL ; +s64 stream_id; + + stream_id = streams->send.next_bidi_stream_id; +if (quic_stream_id_uni (max_stream_id)) + stream_id = streams->send.next_uni_stream_id; + +/* rfc9000#section-2.1: A stream ID that is used out of order results in all streams +* of that type with lower-numbered stream IDs also being opened. +*/ +while (stream_id <= max_stream_id) {net/quic/stream.c line 102
Quoted 10 lines
net/quic/stream.c new file +10
-0+ stream =kzalloc (sizeof (*stream),GFP_KERNEL_ACCOUNT ); +if (!stream) +return NULL ; + + stream->id = stream_id; +if (quic_stream_id_uni (stream_id)) { + stream->send.max_bytes = streams->send.max_stream_data_uni; + +if (streams->send.next_uni_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->send.next_uni_stream_id = stream_id +QUIC_STREAM_ID_STEP ;It's unclear to me the goal the above 2 statements. Dealing with id wrap-arounds? If '
streams->send.next_uni_stream_id<stream_id+QUIC_STREAM_ID_STEP' is not true the nextquic_stream_send_create()will reuse the samestream_id.Quoted 2 lines
I moving the above in a separate helper with some comments would help.
I will add a macro for this:
#define
quic_stream_id_next_update(limits, type, id)\do { \ if ((limits)->next_##type##_stream_id < (id) + QUIC_STREAM_ID_STEP) \ (limits)->next_##type##_stream_id = (id) + QUIC_STREAM_ID_STEP; \ (limits)->streams_##type++; \ } while (0)
Quoted 1 line
So that we can use it to update both
next_uni_stream_idandnext_bidi_stream_id.
A function would be better tacking the next_id value as an argument. More importantly please document the goal here which is still unclear to me.
Quoted 52 lines
Quoted 6 lines
The above 2 functions has a lot of code in common. I think you could deduplicate it by:
- defining a named type for quic_stream_table.{send,recv}
- define a generic /() helper using an additonal
argument for the relevant table.{send,recv}
- replace the above 2 functions with a single invocation to such helper.This is a very smart idea!
It will dedup not only
quic_stream_recv_create(), but alsoquic_stream_get_param()andquic_stream_set_param().I will define a type named 'struct
quic_stream_limits'. Note that, since we must pass 'bool send' toquic_stream_create()for setting the fields in a single 'stream' .if (quic_stream_id_uni(stream_id)) { if (send) { stream->send.max_bytes = limits->max_stream_data_uni; } else { stream->recv.max_bytes = limits->max_stream_data_uni; stream->recv.window = stream->recv.max_bytes; }I'm planning not to pass additional argument of table.{send,recv}, but do this in
quic_stream_create():struct quic_stream_limits *limits = &streams->send; gfp_t gfp = GFP_KERNEL_ACCOUNT; if (!send) { limits = &streams->recv; gfp = GFP_ATOMIC | __GFP_ACCOUNT; }Quoted 3 lines
It looks like there are more de-dup opportunity below.
Yes, the difference is only the variable name _uni_ and _bidi_. I'm planning to de-dup them with macros like:
#define quic_stream_id_below_next(streams, type, id, send) \ ((send) ? ((id) < (streams)->send.next_##type##_stream_id) : \ ((id) < (streams)->recv.next_##type##_stream_id))/* Check if a send or receive stream ID is already closed. */ static bool
quic_stream_id_closed(struct quic_stream_table *streams, s64 stream_id, bool send){ if (quic_stream_id_uni(stream_id)) return quic_stream_id_below_next(streams, uni, stream_id, send); return quic_stream_id_below_next(streams, bidi, stream_id, send); }
Quoted 6 lines
#define quic_stream_id_above_max(streams, type, id) \ (((id) > (streams)->send.max_##type##_stream_id) ? true : \ (quic_stream_id_to_streams((id) - (streams)->send.next_##type##_stream_id) + \ (streams)->send.streams_##type >(streams)->send.max_streams_##type))
Uhmm... with "more de-dup opportunity below" I intended quic_stream_get_param() and quic_stream_set_param(). I would refrain from adding macros. I think the above idea ('struct quic_stream_limits') would not need that?!?
/P
Re: [PATCH net-next v4 06/15] quic: add stream management
Quoted 50 lines · Paolo Abeni
Quoted 48 lines · Xin Long
Quoted 33 lines · Paolo Abeni
On 10/29/25 3:35 PM, Xin Long wrote:
+/* Create and register new streams for sending. */Quoted 14 lines
net/quic/stream.c new file +14
-0+static struct quic_stream *quic_stream_send_create (struct quic_stream_table *streams, +s64 max_stream_id,u8 is_serv) +{ +struct quic_stream *stream =NULL ; +s64 stream_id; + + stream_id = streams->send.next_bidi_stream_id; +if (quic_stream_id_uni (max_stream_id)) + stream_id = streams->send.next_uni_stream_id; + +/* rfc9000#section-2.1: A stream ID that is used out of order results in all streams +* of that type with lower-numbered stream IDs also being opened. +*/ +while (stream_id <= max_stream_id) {net/quic/stream.c line 102
Quoted 10 lines
net/quic/stream.c new file +10
-0+ stream =kzalloc (sizeof (*stream),GFP_KERNEL_ACCOUNT ); +if (!stream) +return NULL ; + + stream->id = stream_id; +if (quic_stream_id_uni (stream_id)) { + stream->send.max_bytes = streams->send.max_stream_data_uni; + +if (streams->send.next_uni_stream_id < stream_id +QUIC_STREAM_ID_STEP ) + streams->send.next_uni_stream_id = stream_id +QUIC_STREAM_ID_STEP ;It's unclear to me the goal the above 2 statements. Dealing with id wrap-arounds? If '
streams->send.next_uni_stream_id<stream_id+QUIC_STREAM_ID_STEP' is not true the nextquic_stream_send_create()will reuse the samestream_id.Quoted 2 lines
I moving the above in a separate helper with some comments would help.
I will add a macro for this:
#define
quic_stream_id_next_update(limits, type, id)\do { \ if ((limits)->next_##type##_stream_id < (id) + QUIC_STREAM_ID_STEP) \ (limits)->next_##type##_stream_id = (id) + QUIC_STREAM_ID_STEP; \ (limits)->streams_##type++; \ } while (0)
Quoted 5 lines
Quoted 1 line
So that we can use it to update both
next_uni_stream_idandnext_bidi_stream_id.A function would be better tacking the
next_idvalue as an argument. More importantly please document the goal here which is still unclear to me.
The if check may not be needed, I will double confirm:
if (limits->next_uni_stream_id < stream_id + QUIC_STREAM_ID_STEP)
If it's just one line below, maybe I just add a comment like in here?
/* Streams must be opened sequentially. Update the next stream ID so the
* correct starting point is known if an out-of-order open is requested. */limits->next_uni_stream_id = stream_id + QUIC_STREAM_ID_STEP;
Quoted 59 lines
Quoted 52 lines
Quoted 6 lines
The above 2 functions has a lot of code in common. I think you could deduplicate it by:
- defining a named type for quic_stream_table.{send,recv}
- define a generic /() helper using an additonal
argument for the relevant table.{send,recv}
- replace the above 2 functions with a single invocation to such helper.This is a very smart idea!
It will dedup not only
quic_stream_recv_create(), but alsoquic_stream_get_param()andquic_stream_set_param().I will define a type named 'struct
quic_stream_limits'. Note that, since we must pass 'bool send' toquic_stream_create()for setting the fields in a single 'stream' .if (quic_stream_id_uni(stream_id)) { if (send) { stream->send.max_bytes = limits->max_stream_data_uni; } else { stream->recv.max_bytes = limits->max_stream_data_uni; stream->recv.window = stream->recv.max_bytes; }I'm planning not to pass additional argument of table.{send,recv}, but do this in
quic_stream_create():struct quic_stream_limits *limits = &streams->send; gfp_t gfp = GFP_KERNEL_ACCOUNT; if (!send) { limits = &streams->recv; gfp = GFP_ATOMIC | __GFP_ACCOUNT; }Quoted 3 lines
It looks like there are more de-dup opportunity below.
Yes, the difference is only the variable name _uni_ and _bidi_. I'm planning to de-dup them with macros like:
#define quic_stream_id_below_next(streams, type, id, send) \ ((send) ? ((id) < (streams)->send.next_##type##_stream_id) : \ ((id) < (streams)->recv.next_##type##_stream_id))/* Check if a send or receive stream ID is already closed. */ static bool
quic_stream_id_closed(struct quic_stream_table *streams, s64 stream_id, bool send){ if (quic_stream_id_uni(stream_id)) return quic_stream_id_below_next(streams, uni, stream_id, send); return quic_stream_id_below_next(streams, bidi, stream_id, send); }Quoted 6 lines
#define quic_stream_id_above_max(streams, type, id) \ (((id) > (streams)->send.max_##type##_stream_id) ? true : \ (quic_stream_id_to_streams((id) - (streams)->send.next_##type##_stream_id) + \ (streams)->send.streams_##type >(streams)->send.max_streams_##type))
Quoted 5 lines
Uhmm... with "more de-dup opportunity below" I intended
quic_stream_get_param()andquic_stream_set_param(). I would refrain from adding macros. I think the above idea ('structquic_stream_limits') would not need that?!?
Ah okay, that makes sense now, I don't like such macros either. The above idea won't involve any new macros.
Thanks.
PATCH v4 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).
Note source/dest conn_id set is per socket, the operations on it are always pretected by the sock lock.
---
v3:
- Clarify in changelog that conn_id set is always protected by sock lock (suggested by Paolo).
- Adjust global source conn_id hashtable operations for the new hashtable type.
v4:
- Replace struct hlist_node with hlist_nulls_node for the node in struct quic_source_conn_id to support lockless lookup.
--- net/quic/Makefile | 2 +- net/quic/connid.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/connid.h | 162 +++++++++++++++++++++++++++++++++ net/quic/socket.c | 6 ++ net/quic/socket.h | 13 +++ 5 files changed, 404 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 +222 -0
222 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
@@ -41,6 +41,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 ;
@@ -55,6 +58,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
@@ -14,6 +14,7 @@#include "common.h" #include "family.h" #include "stream.h" +#include "connid.h" #include "protocol.h"
@@ -37,6 +38,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 {
@@ -79,6 +82,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 v4 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.
---
v3:
- Fix annotation in quic_udp_sock_lookup() (noted by Paolo).
- Use inet_sk_get_local_port_range() instead of inet_get_local_port_range() (suggested by Paolo).
- Adjust global UDP tunnel socket hashtable operations for the new hashtable type.
- Delete quic_workqueue; use system_wq for UDP tunnel socket destroy.
v4:
- Cache UDP tunnel socket pointer and its source address in struct quic_path for RCU-protected lookup/access.
- Return -EAGAIN instead of -EINVAL in quic_path_bind() when UDP socket is being released in workqueue.
- Move udp_tunnel_sock_release() out of the mutex_lock to avoid a warning of lockdep in quic_udp_sock_put_work().
- Introduce quic_wq for UDP socket release work, so all pending works can be flushed before destroying the hashtable in quic_exit().
--- net/quic/Makefile | 2 +- net/quic/path.c | 534 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/path.h | 173 ++++++++++++++ net/quic/protocol.c | 11 + net/quic/socket.c | 3 + net/quic/socket.h | 21 +- 6 files changed, 741 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 +534 -0
534 lines not on this page · raw message
net/quic/path.h new file +173 -0
173 lines not on this page · raw message
net/quic/protocol.c +11 -0
@@ -21,6 +21,7 @@static unsigned int quic_net_id__read_mostly ;struct percpu_counter quic_sockets_allocated; +struct workqueue_struct *quic_wq;long sysctl_quic_mem[3 ];int sysctl_quic_rmem[3 ];
@@ -337,6 +338,12 @@ static __init int quic_init(void)if (err) goto err_hash; + quic_wq =create_workqueue ("quic_workqueue" ); +if (!quic_wq) { + err = -ENOMEM ; + goto err_wq; + } + err =register_pernet_subsys (&quic_net_ops);if (err) goto err_def_ops;
@@ -354,6 +361,8 @@ static __init int quic_init(void) err_protosw:unregister_pernet_subsys (&quic_net_ops); err_def_ops: +destroy_workqueue (quic_wq); +err_wq:quic_hash_tables_destroy (); err_hash:percpu_counter_destroy (&quic_sockets_allocated);
@@ -368,6 +377,8 @@ static __exit void quic_exit(void)#endif quic_protosw_exit ();unregister_pernet_subsys (&quic_net_ops); +flush_workqueue (quic_wq); +destroy_workqueue (quic_wq);quic_hash_tables_destroy ();percpu_counter_destroy (&quic_sockets_allocated);pr_info ("quic: exit\n" );
net/quic/socket.c +3 -0
@@ -58,6 +58,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
@@ -15,6 +15,7 @@#include "family.h" #include "stream.h" #include "connid.h" +#include "path.h" #include "protocol.h"
@@ -40,6 +41,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 {
@@ -92,6 +94,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 ;
@@ -115,14 +127,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 ); + }inet_sk_set_state (sk, state); sk->sk_state_change (sk);
Signature · 1 line
2.47.1
Paolo Abeni
maintainerRe: [PATCH net-next v4 08/15] quic: add path management
Quoted 9 lines · Xin Long
This patch introduces '
quic_path_group' for managing paths, represented by 'structquic_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.
Quoted 8 lines
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.
I find the above name slightly misleading, as I expect such function to free the path argument. Possibly quic_path_unbind?
/{
Re: [PATCH net-next v4 08/15] quic: add path management
Quoted 15 lines · Paolo Abeni
Quoted 13 lines · Xin Long
This patch introduces '
quic_path_group' for managing paths, represented by 'structquic_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.
Quoted 8 lines
Quoted 4 lines
-
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.I find the above name slightly misleading, as I expect such function to free the path argument. Possibly
quic_path_unbind?
Makes sense.
Thanks.
PATCH v4 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 currently introduces the New Reno algorithm only.
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.
---
v4:
- Remove the CUBIC congestion algorithm support for this version (suggested by Paolo).
--- net/quic/Makefile | 3 +- net/quic/cong.c | 307 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/cong.h | 120 ++++++++++++++++++ net/quic/socket.c | 1 + net/quic/socket.h | 7 ++ 5 files changed, 437 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 +307 -0
307 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
@@ -43,6 +43,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
@@ -16,6 +16,7 @@#include "stream.h" #include "connid.h" #include "path.h" +#include "cong.h" #include "protocol.h"
@@ -42,6 +43,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 {
@@ -104,6 +106,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
Paolo Abeni
maintainerRe: [PATCH net-next v4 09/15] quic: add congestion control
net/quic/cong.c line 201
Quoted 13 lines · Xin Long
net/quic/cong.c new file +13
-0+/* Compute and update the pacing rate based on congestion window and smoothed RTT. */ +static void quic_cong_pace_update (struct quic_cong *cong,u32 bytes,u32 max_rate) +{ +u64 rate; + +/* rate = N * congestion_window / smoothed_rtt */ + rate = (u64 )cong->window *USEC_PER_SEC *2 ; +if (likely (cong->smoothed_rtt)) + rate =div64_ul (rate, cong->smoothed_rtt); + +WRITE_ONCE (cong->pacing_rate,min_t (u64, rate, max_rate)); +pr_debug ("%s: update pacing rate: %u, max rate: %u, srtt: %u\n" , + __func__, cong->pacing_rate, max_rate, cong->smoothed_rtt);
I think you should skip entirely the pacing_rate update when smoothed_rtt == 0
[...]> +/* rfc9002#section-5: Estimating the Round-Trip Time */
Quoted 34 lines
net/quic/cong.c new file +34
-0+void quic_cong_rtt_update (struct quic_cong *cong,u32 time,u32 ack_delay) +{ +u32 adjusted_rtt, rttvar_sample; + +/* Ignore RTT sample if ACK delay is suspiciously large. */ +if (ack_delay > cong->max_ack_delay *2 ) +return ; + +/* rfc9002#section-5.1: latest_rtt = ack_time - send_time_of_largest_acked */ + cong->latest_rtt = cong->time - time; + +/* rfc9002#section-5.2: Estimating min_rtt */ +if (!cong->min_rtt_valid) { + cong->min_rtt = cong->latest_rtt; + cong->min_rtt_valid =1 ; + } +if (cong->min_rtt > cong->latest_rtt) + cong->min_rtt = cong->latest_rtt; + +if (!cong->is_rtt_set) { +/* rfc9002#section-5.3: +* smoothed_rtt = latest_rtt +* rttvar = latest_rtt / 2 +*/ + cong->smoothed_rtt = cong->latest_rtt; + cong->rttvar = cong->smoothed_rtt /2 ; +quic_cong_pto_update (cong); + cong->is_rtt_set =1 ; +return ; + } + +/* rfc9002#section-5.3: +* adjusted_rtt = latest_rtt +* if (latest_rtt >= min_rtt + ack_delay):
net/quic/cong.c line 266
Quoted 10 lines
net/quic/cong.c new file +10
-0+* adjusted_rtt = latest_rtt - ack_delay +* smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt +* rttvar_sample = abs(smoothed_rtt - adjusted_rtt) +* rttvar = 3/4 * rttvar + 1/4 * rttvar_sample +*/ + adjusted_rtt = cong->latest_rtt; +if (cong->latest_rtt >= cong->min_rtt + ack_delay) + adjusted_rtt = cong->latest_rtt - ack_delay; + + cong->smoothed_rtt = (cong->smoothed_rtt *7 + adjusted_rtt) /8 ;
Out of sheer curiosity, is the compiler smart enough to use a 'srl 3' for the above?
/P
Re: [PATCH net-next v4 09/15] quic: add congestion control
Quoted 19 lines · Paolo Abeni
net/quic/cong.c line 201
Quoted 13 lines · Xin Long
net/quic/cong.c new file +13
-0+/* Compute and update the pacing rate based on congestion window and smoothed RTT. */ +static void quic_cong_pace_update (struct quic_cong *cong,u32 bytes,u32 max_rate) +{ +u64 rate; + +/* rate = N * congestion_window / smoothed_rtt */ + rate = (u64 )cong->window *USEC_PER_SEC *2 ; +if (likely (cong->smoothed_rtt)) + rate =div64_ul (rate, cong->smoothed_rtt); + +WRITE_ONCE (cong->pacing_rate,min_t (u64, rate, max_rate)); +pr_debug ("%s: update pacing rate: %u, max rate: %u, srtt: %u\n" , + __func__, cong->pacing_rate, max_rate, cong->smoothed_rtt);I think you should skip entirely the
pacing_rateupdate whensmoothed_rtt == 0
will update it.
Quoted 40 lines
[...]> +/* rfc9002#section-5: Estimating the Round-Trip Time */
Quoted 39 lines
net/quic/cong.c new file +39
-0+void quic_cong_rtt_update (struct quic_cong *cong,u32 time,u32 ack_delay) +{ +u32 adjusted_rtt, rttvar_sample; + +/* Ignore RTT sample if ACK delay is suspiciously large. */ +if (ack_delay > cong->max_ack_delay *2 ) +return ; + +/* rfc9002#section-5.1: latest_rtt = ack_time - send_time_of_largest_acked */ + cong->latest_rtt = cong->time - time; + +/* rfc9002#section-5.2: Estimating min_rtt */ +if (!cong->min_rtt_valid) { + cong->min_rtt = cong->latest_rtt; + cong->min_rtt_valid =1 ; + } +if (cong->min_rtt > cong->latest_rtt) + cong->min_rtt = cong->latest_rtt; + +if (!cong->is_rtt_set) { +/* rfc9002#section-5.3: +* smoothed_rtt = latest_rtt +* rttvar = latest_rtt / 2 +*/ + cong->smoothed_rtt = cong->latest_rtt; + cong->rttvar = cong->smoothed_rtt /2 ; +quic_cong_pto_update (cong); + cong->is_rtt_set =1 ; +return ; + } + +/* rfc9002#section-5.3: +* adjusted_rtt = latest_rtt +* if (latest_rtt >= min_rtt + ack_delay): +* adjusted_rtt = latest_rtt - ack_delay +* smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt +* rttvar_sample = abs(smoothed_rtt - adjusted_rtt) +* rttvar = 3/4 * rttvar + 1/4 * rttvar_sample +*/
Quoted 9 lines
net/quic/cong.c line 266
Quoted 5 lines
net/quic/cong.c new file +5
-0+ adjusted_rtt = cong->latest_rtt; +if (cong->latest_rtt >= cong->min_rtt + ack_delay) + adjusted_rtt = cong->latest_rtt - ack_delay; + + cong->smoothed_rtt = (cong->smoothed_rtt *7 + adjusted_rtt) /8 ;Out of sheer curiosity, is the compiler smart enough to use a 'srl 3' for the above?
Yes.
266 cong->smoothed_rtt = (cong->smoothed_rtt * 7 + adjusted_rtt) / 8;
0x593d <+77>: mov (%rbx),%ecx ; ecx = cong->smoothed_rtt 0x593f <+79>: lea (%rax,%rcx,8),%edx ; edx = adjusted_rtt + (ecx * 8) 0x5942 <+82>: sub %ecx,%edx ; edx = adjusted_rtt + (8*ecx) - ecx = ecx*7 + adjusted_rtt 0x5946 <+86>: shr $0x3,%edx ; edx >>= 3 → divide by 8 0x594d <+93>: mov %edx,(%rbx) ; store result back to
cong->smoothed_rtt
Thanks.
PATCH v4 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 | 149 ++++++++++++++++++++++++++++++ net/quic/socket.c | 12 +++ net/quic/socket.h | 7 ++ 5 files changed, 393 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 +149 -0
149 lines not on this page · raw message
net/quic/socket.c +12 -0
@@ -37,6 +37,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 );
@@ -48,6 +50,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 ]));
@@ -59,6 +66,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
@@ -11,6 +11,7 @@#include <net/udp_tunnel.h> #include <linux/quic.h> +#include "pnspace.h" #include "common.h" #include "family.h" #include "stream.h"
@@ -44,6 +45,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 {
@@ -111,6 +113,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
Re: [PATCH net-next v4 10/15] quic: add packet number space
Quoted 6 lines · Xin Long
net/quic/pnspace.h new file +6
-0+struct quic_pnspace { +/* ECN counters indexed by direction (TX/RX) and ECN codepoint (ECT1, ECT0, CE) */ +u64 ecn_count[QUIC_ECN_DIR_MAX ][QUIC_ECN_MAX ]; +unsigned long *pn_map;/* Bit map tracking received packet numbers for ACK generation */ +u16 pn_map_len;/* Length of the packet number bit map (in bits) */ +u8 need_sack:1 ;/* Flag indicating a SACK frame should be sent for this space */
net/quic/pnspace.h line 76
Quoted 10 lines
net/quic/pnspace.h new file +10
-0+ u8 sack_path:1; /* Path used for sending the SACK frame */ + +s64 last_max_pn_seen;/* Highest packet number seen before pn_map advanced */ +u32 last_max_pn_time;/* Timestamp when last_max_pn_seen was received */ +u32 max_time_limit;/* Time threshold to trigger pn_map advancement on packet receipt */ +s64 min_pn_seen;/* Smallest packet number received in this space */ +s64 max_pn_seen;/* Largest packet number received in this space */ +u32 max_pn_time;/* Time at which max_pn_seen was received */ +s64 base_pn;/* Packet number corresponding to the start of the pn_map */ +u32 time;/* Cached current time, or time accept a socket (listen socket) */
There are a few 32 bits holes above you could avoid reordering the fields.
Otherwise LGTM,
Re: [PATCH net-next v4 10/15] quic: add packet number space
Quoted 12 lines · Paolo Abeni
Quoted 10 lines · Xin Long
net/quic/pnspace.h new file +10
-0+struct quic_pnspace { +/* ECN counters indexed by direction (TX/RX) and ECN codepoint (ECT1, ECT0, CE) */ +u64 ecn_count[QUIC_ECN_DIR_MAX ][QUIC_ECN_MAX ]; +unsigned long *pn_map;/* Bit map tracking received packet numbers for ACK generation */ +u16 pn_map_len;/* Length of the packet number bit map (in bits) */ +u8 need_sack:1 ;/* Flag indicating a SACK frame should be sent for this space */ +u8 sack_path:1 ;/* Path used for sending the SACK frame */ + +s64 last_max_pn_seen;/* Highest packet number seen before pn_map advanced */ +u32 last_max_pn_time;/* Timestamp when last_max_pn_seen was received */
Quoted 8 lines
net/quic/pnspace.h line 76
Quoted 6 lines
net/quic/pnspace.h new file +6
-0+u32 max_time_limit;/* Time threshold to trigger pn_map advancement on packet receipt */ +s64 min_pn_seen;/* Smallest packet number received in this space */ +s64 max_pn_seen;/* Largest packet number received in this space */ +u32 max_pn_time;/* Time at which max_pn_seen was received */ +s64 base_pn;/* Packet number corresponding to the start of the pn_map */ +u32 time;/* Cached current time, or time accept a socket (listen socket) */ There are a few 32 bits holes above you could avoid reordering the fields.
I will switch base_pn and time.
For the hole after sack_path, It can't be avoided in this struct, I will leave it there.
Thanks.
Quoted 5 lines
Otherwise LGTM,
Acked-by Paolo Abeni <[email protected]>
PATCH v4 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.
---
v3:
- Remove lock from quic_net, since Initial packet decryption for ALPN will be handled serially in a workqueue when ALPN demux is enabled.
v4:
- Use kfree_sensitive() and memzero_explicit() for sensitive data.
- Use local cipher pointer in quic_crypto_set_secret() to avoid a warning from Smatch.
--- net/quic/Makefile | 2 +- net/quic/crypto.c | 560 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/crypto.h | 73 ++++++ net/quic/protocol.c | 11 + net/quic/protocol.h | 1 + net/quic/socket.c | 2 + net/quic/socket.h | 7 + 7 files changed, 655 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 +560 -0
560 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 +11 -0
@@ -265,9 +265,17 @@ 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; + } +#ifdef CONFIG_PROC_FS err =quic_net_proc_init (net);if (err) { +quic_crypto_free (&qn->crypto);free_percpu (qn->stat); qn->stat =NULL ; }
@@ -282,6 +290,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 ; }
@@ -330,6 +339,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 +1 -0
@@ -48,6 +48,7 @@ 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 */ };struct quic_net *quic_net (struct net *net);
net/quic/socket.c +2 -0
@@ -70,6 +70,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
@@ -16,6 +16,7 @@#include "family.h" #include "stream.h" #include "connid.h" +#include "crypto.h" #include "path.h" #include "cong.h"
@@ -46,6 +47,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 {
@@ -118,6 +120,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 v4 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.
---
v3:
- quic_crypto_decrypt(): return -EKEYREVOKED to defer key updates to the workqueue when the packet is not marked backlog, since quic_crypto_key_update()/crypto_aead_setkey() must run in process context.
- Only perform header decryption if !cb->number_len to avoid double decryption when a key-update packet (with flipped key_phase) re-enters quic_crypto_decrypt() from the workqueue.
v4:
- skb_dst_force() is already called in in quic_udp_rcv() on recv path, so remove its call from quic_crypto_payload_decrypt(), which may be called without RCU protection.
- Use kfree_sensitive() and memzero_explicit() for sensitive data.
- Remove the incorrect (void *) cast to quic_crypto_done.
--- net/quic/crypto.c | 673 ++++++++++++++++++++++++++++++++++++++++++++++ net/quic/crypto.h | 10 + 2 files changed, 683 insertions(+)
net/quic/crypto.c +673 -0
@@ -207,6 +207,345 @@ static int quic_crypto_rx_keys_derive_and_install(struct quic_crypto *crypto)return err; } +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_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_PN_MAX_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_sensitive (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_PN_MAX_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_PN_MAX_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_PN_MAX_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_sensitive (mask); +return err; +} + +static void *quic_crypto_aead_mem_alloc (struct crypto_aead *tfm,u32 ctx_size, +u8 **iv,struct aead_request **req, +struct scatterlist **sg,u32 nsg) +{ +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_sensitive (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 , 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; +memzero_explicit (nonce,sizeof (nonce)); +return err; + } + +err: +kfree_sensitive (ctx); +memzero_explicit (nonce,sizeof (nonce)); +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; + +/* 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 , quic_crypto_done, skb); + + err =crypto_aead_decrypt (req); +if (err == -EINPROGRESS ) { + skb->destructor = quic_crypto_destruct_skb; +skb_shinfo (skb)->destructor_arg = ctx; +memzero_explicit (nonce,sizeof (nonce)); +return err; + } +err: +kfree_sensitive (ctx); +memzero_explicit (nonce,sizeof (nonce)); +return err; +} +#define QUIC_CIPHER_MIN TLS_CIPHER_AES_GCM_128#define QUIC_CIPHER_MAX TLS_CIPHER_CHACHA20_POLY1305
@@ -231,6 +570,135 @@ 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; + } +if (!cb->number_len) {/* Packet header not yet decrypted. */ + 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 ; +if (!cb->backlog)/* Key update must be done in process context. */ +return -EKEYREVOKED ; + 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;
@@ -516,6 +984,211 @@ int quic_crypto_initial_keys_install(struct quic_crypto *crypto, struct quic_conreturn err; } +#define QUIC_RETRY_KEY_V1 "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e" +#define QUIC_RETRY_KEY_V2 "\x8f\xb4\xb0\x1b\x56\xac\x48\xe2\x60\xfb\xcb\xce\xad\x7c\xcc\x92" + +#define QUIC_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb" +#define QUIC_RETRY_NONCE_V2 "\xd8\x69\x69\xbc\x2d\x7c\x6d\x99\x90\xef\xb0\x4a" + +/* Retry Packet Integrity. */ +int quic_crypto_get_retry_tag (struct quic_crypto *crypto,struct sk_buff *skb, +struct quic_conn_id *odcid,u32 version,u8 *tag) +{ +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_sensitive (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 ]; +struct crypto_aead *tfm = crypto->tag_tfm; +u32 ts =jiffies_to_usecs (jiffies), len; +u8 *retry_token =NULL , *tx_iv, *p; +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) + goto out; + err =crypto_aead_setauthsize (tfm,QUIC_TAG_LEN ); +if (err) + goto out; + err =crypto_aead_setkey (tfm, key,TLS_CIPHER_AES_GCM_128_KEY_SIZE ); +if (err) + goto out; + 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) { + err = -ENOMEM ; + goto out; + } + + 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) + goto out; +memcpy (token, retry_token, len); + *tlen = len +1 ; +out: +kfree_sensitive (retry_token); +memzero_explicit (key,sizeof (key)); +memzero_explicit (iv,sizeof (iv)); +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 =NULL , *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; + +if (len <sizeof (flag) + addrlen +sizeof (ts) +QUIC_TAG_LEN ) +return -EINVAL ; +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) + goto out; + err =crypto_aead_setauthsize (tfm,QUIC_TAG_LEN ); +if (err) + goto out; + err =crypto_aead_setkey (tfm, key,TLS_CIPHER_AES_GCM_128_KEY_SIZE ); +if (err) + goto out; + len--; + token++; + retry_token =quic_crypto_aead_mem_alloc (tfm, len, &rx_iv, &req, &sg,1 ); +if (!retry_token) { + err = -ENOMEM ; + goto out; + } + +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_sensitive (retry_token); +memzero_explicit (key,sizeof (key)); +memzero_explicit (iv,sizeof (iv)); +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 v4 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
@@ -47,6 +47,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 ;
@@ -68,6 +70,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++)
@@ -204,6 +208,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
@@ -21,6 +21,7 @@#include "cong.h" #include "protocol.h" +#include "timer.h" extern struct proto quic_prot;extern struct proto quicv6_prot;
@@ -32,6 +33,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;
@@ -48,6 +74,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 {
@@ -125,6 +153,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
Re: [PATCH net-next v4 13/15] quic: add timer management
net/quic/timer.c line 174
Quoted 8 lines · Xin Long
net/quic/timer.c new file +8
-0+void quic_timer_stop (struct sock *sk,u8 type) +{ +if (type ==QUIC_TIMER_PACE ) { +if (hrtimer_try_to_cancel (quic_timer (sk, type)) ==1 ) +sock_put (sk); +return ; + } +if (timer_delete (quic_timer (sk, type)))
timer_shutdown()
Other than that:
Re: [PATCH net-next v4 13/15] quic: add timer management
Quoted 12 lines · Paolo Abeni
net/quic/timer.c line 174
Quoted 8 lines · Xin Long
net/quic/timer.c new file +8
-0+void quic_timer_stop (struct sock *sk,u8 type) +{ +if (type ==QUIC_TIMER_PACE ) { +if (hrtimer_try_to_cancel (quic_timer (sk, type)) ==1 ) +sock_put (sk); +return ; + } +if (timer_delete (quic_timer (sk, type)))
timer_shutdown()
Will update. Thanks.
Quoted 5 lines
Other than that:
Acked-by Paolo Abeni <[email protected]>
Re: [PATCH net-next v4 13/15] quic: add timer management
Quoted 14 lines · Xin Long
Quoted 12 lines · Paolo Abeni
net/quic/timer.c line 174
Quoted 8 lines · Xin Long
net/quic/timer.c new file +8
-0+void quic_timer_stop (struct sock *sk,u8 type) +{ +if (type ==QUIC_TIMER_PACE ) { +if (hrtimer_try_to_cancel (quic_timer (sk, type)) ==1 ) +sock_put (sk); +return ; + } +if (timer_delete (quic_timer (sk, type)))
timer_shutdown()
Quoted 1 line
Will update. Thanks.
timer_shutdown() sets timer->function to NULL, and it causes mod_timer() to return 0 without enqueuing the timer. This breaks the code:
if (!mod_timer(t, jiffies + usecs_to_jiffies(timeout)))
sock_hold(sk);
in quic_timer_start(), and cause sk leak.
So I will keep timer_delete() here.
Thanks.
Quoted 6 lines
Quoted 6 lines
Other than that:
Acked-by Paolo Abeni <[email protected]>
PATCH v4 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 | 195 ++++++++++++++++ net/quic/protocol.c | 9 + net/quic/protocol.h | 1 + net/quic/socket.h | 2 + 6 files changed, 766 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 +195 -0
195 lines not on this page · raw message
net/quic/protocol.c +9 -0
@@ -20,6 +20,7 @@static unsigned int quic_net_id__read_mostly ; +struct kmem_cache *quic_frame_cachep__read_mostly ;struct percpu_counter quic_sockets_allocated;struct workqueue_struct *quic_wq;
@@ -341,6 +342,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;
@@ -378,6 +384,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; }
@@ -392,6 +400,7 @@ static __exit void quic_exit(void)destroy_workqueue (quic_wq);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
@@ -20,6 +20,8 @@#include "path.h" #include "cong.h" +#include "frame.h" +#include "protocol.h" #include "timer.h"
Signature · 1 line
2.47.1
Paolo Abeni
maintainerRe: [PATCH net-next v4 14/15] quic: add frame encoder and decoder base
net/quic/frame.c line 528
Quoted 5 lines · Xin Long
net/quic/frame.c new file +5
-0+static void quic_frame_free (struct quic_frame *frame) +{ +struct quic_frame_frag *frag, *next; + +if (!frame->type && frame->skb) {/* RX path frame with skb. */
Are RX path frame with !skb expected/possible? such frames will be 'misinterpreted' as TX ones, specifically will do kfree(frame->data) which in turn could be a bad thing.
Possibly add a WARN on such scenario?
/P
Re: [PATCH net-next v4 14/15] quic: add frame encoder and decoder base
Quoted 12 lines · Paolo Abeni
net/quic/frame.c line 528
Quoted 5 lines · Xin Long
net/quic/frame.c new file +5
-0+static void quic_frame_free (struct quic_frame *frame) +{ +struct quic_frame_frag *frag, *next; + +if (!frame->type && frame->skb) {/* RX path frame with skb. */ Are RX path frame with !skb expected/possible? such frames will be 'misinterpreted' as TX ones, specifically will do
kfree(frame->data)which in turn could be a bad thing.
Yes, when generating and delivering an event to userspace, it keeps the frame content into frame->data, instead of frame->skb.
There's no need check !frame->type for RX path, and I will change it to:
if (frame->skb) { /* For stream/crypto/dgram frames on RX. */
kfree_skb(frame->skb);
goto out;
}if skb is set, it will go kfree_skb(frame->skb), instead of kfree(frame->data). Because if frame->skb is set, it only needs to do kfree_skb(frame->skb).
Thanks.
Quoted 4 lines
Possibly add a WARN on such scenario?
/P
Re: [PATCH net-next v4 14/15] quic: add frame encoder and decoder base
Quoted 19 lines · Xin Long
Quoted 12 lines · Paolo Abeni
net/quic/frame.c line 528
Quoted 5 lines · Xin Long
net/quic/frame.c new file +5
-0+static void quic_frame_free (struct quic_frame *frame) +{ +struct quic_frame_frag *frag, *next; + +if (!frame->type && frame->skb) {/* RX path frame with skb. */ Are RX path frame with !skb expected/possible? such frames will be 'misinterpreted' as TX ones, specifically will do
kfree(frame->data)which in turn could be a bad thing.Yes, when generating and delivering an event to userspace, it keeps the frame content into
frame->data, instead offrame->skb.There's no need check !frame->type for RX path, and I will change it to:
Quoted 8 lines
if (frame->skb) { /* For stream/crypto/dgram frames on RX. */ kfree_skb(frame->skb); goto out; }if skb is set, it will go
kfree_skb(frame->skb), instead ofkfree(frame->data). Because ifframe->skbis set, it only needs to dokfree_skb(frame->skb).
Just realized that frame->skb shares a union with frame->flist, which is used only on TX. So instead of removing the check !frame->type, I will put a comment here:
/* Handle RX stream/crypto/dgram frames. Use !frame->type to detect RX,
* since frame->skb shares a union with frame->flist, used only on TX.
*/
if (!frame->type && frame->skb) {Thanks.
PATCH v4 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().
---
v3:
- Adjust global connection and listen socket hashtable operations based on the new hashtable type.
- Introduce quic_packet_backlog_schedule() to enqueue Initial packets to quic_net.backlog_list and defer their decryption for ALPN demux to quic_packet_backlog_work() on quic_net.work, since quic_crypto_initial_keys_install()/crypto_aead_setkey() must run in process context.
v4:
- Update quic_(listen_)sock_lookup() to support lockless socket lookup using hlist_nulls_node APIs.
- Use quic_wq for QUIC packet backlog processing work.
--- net/quic/Makefile | 2 +- net/quic/packet.c | 956 ++++++++++++++++++++++++++++++++++++++++++++ net/quic/packet.h | 130 ++++++ net/quic/protocol.c | 6 + net/quic/protocol.h | 4 + net/quic/socket.c | 135 +++++++ net/quic/socket.h | 12 + 7 files changed, 1244 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 +956 -0
956 lines not on this page · raw message
net/quic/packet.h new file +130 -0
130 lines not on this page · raw message
net/quic/protocol.c +6 -0
@@ -273,6 +273,9 @@ static int __net_init quic_net_init(struct net *net)return err; } +INIT_WORK (&qn->work, quic_packet_backlog_work); +skb_queue_head_init (&qn->backlog_list); +#ifdef CONFIG_PROC_FS err =quic_net_proc_init (net);if (err) {
@@ -291,6 +294,8 @@ static void __net_exit quic_net_exit(struct net *net)#ifdef CONFIG_PROC_FS quic_net_proc_exit (net);#endif +skb_queue_purge (&qn->backlog_list); +cancel_work_sync (&qn->work);quic_crypto_free (&qn->crypto);free_percpu (qn->stat); qn->stat =NULL ;
@@ -340,6 +345,7 @@ static __init int quic_init(void) sysctl_quic_wmem[1 ] =16 *1024 ; sysctl_quic_wmem[2 ] =max (64 *1024 , max_share); +quic_path_init (quic_packet_rcv);quic_crypto_init (); quic_frame_cachep = kmem_cache_create("quic_frame", sizeof(struct quic_frame),
net/quic/protocol.h +4 -0
@@ -50,6 +50,10 @@ struct quic_net {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 */ + +/* Queue of packets deferred for processing in process context */ +struct sk_buff_head backlog_list; +struct work_struct work;/* Work scheduled to drain and process backlog_list */ };struct quic_net *quic_net (struct net *net);
net/quic/socket.c +135 -0
165 lines not on this page · raw message
net/quic/socket.h +12 -0
@@ -20,6 +20,7 @@#include "path.h" #include "cong.h" +#include "packet.h" #include "frame.h" #include "protocol.h"
@@ -77,6 +78,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 ]; };
@@ -155,6 +157,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];
@@ -200,3 +207,8 @@ static inline void quic_set_state(struct sock *sk, int state)inet_sk_set_state (sk, state); sk->sk_state_change (sk); } + +struct sock *quic_listen_sock_lookup (struct sk_buff *skb,union quic_addr *sa,union quic_addr *da, +struct 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);
Signature · 1 line
2.47.1
Paolo Abeni
maintainerRe: [PATCH net-next v4 15/15] quic: add packet builder and parser base
Quoted 42 lines · Xin Long
net/quic/packet.c new file +42
-0+/* Process PMTU reduction event on a QUIC socket. */ +void quic_packet_rcv_err_pmtu (struct sock *sk) +{ +struct quic_path_group *paths =quic_paths (sk); +struct quic_packet *packet =quic_packet (sk); +struct quic_config *c =quic_config (sk); +u32 pathmtu, info, taglen; +struct dst_entry *dst; +bool reset_timer; + +if (!ip_sk_accept_pmtu (sk)) +return ; + + info =clamp (paths->mtu_info,QUIC_PATH_MIN_PMTU ,QUIC_PATH_MAX_PMTU ); +/* If PLPMTUD is not enabled, update MSS using the route and ICMP info. */ +if (!c->plpmtud_probe_interval) { +if (quic_packet_route (sk) <0 ) +return ; + + dst =__sk_dst_get (sk); + dst->ops->update_pmtu (dst, sk,NULL , info, true); +quic_packet_mss_update (sk, info - packet->hlen); +return ; + } +/* PLPMTUD is enabled: adjust to smaller PMTU, subtract headers and AEAD tag. Also +* notify the QUIC path layer for possible state changes and probing. +*/ + taglen =quic_packet_taglen (packet); + info = info - packet->hlen - taglen; + pathmtu =quic_path_pl_toobig (paths, info, &reset_timer); +if (reset_timer) +quic_timer_reset (sk,QUIC_TIMER_PMTU , c->plpmtud_probe_interval); +if (pathmtu) +quic_packet_mss_update (sk, pathmtu + taglen); +} + +/* Handle ICMP Toobig packet and update QUIC socket path MTU. */ +static int quic_packet_rcv_err (struct sk_buff *skb) +{ +union quic_addr daddr, saddr; +struct sock *sk =NULL ; +int ret =0 ;
net/quic/packet.c line 221
Quoted 10 lines
net/quic/packet.c new file +10
-0+u32 info; + +/* All we can do is lookup the matching QUIC socket by addresses. */ +quic_get_msg_addrs (skb, &saddr, &daddr); + sk =quic_sock_lookup (skb, &daddr, &saddr,NULL ); +if (!sk) +return -ENOENT ; + +bh_lock_sock (sk); +if (quic_is_listen (sk))
The above looks race-prone. You should check the status only when holding the sk socket lock, i.e. if !sock_owned_by_user(sk)
net/quic/packet.c line 225
Quoted 4 lines
net/quic/packet.c new file +4
-0+ goto out; + +if (quic_get_mtu_info (skb, &info)) + goto out;
This can be moved outside the lock.
Quoted 19 lines
net/quic/packet.c new file +19
-0+ + ret =1 ;/* Success: update socket path MTU info. */ +quic_paths (sk)->mtu_info = info; +if (sock_owned_by_user (sk)) { +/* Socket is in use by userspace context. Defer MTU processing to later via +* tasklet. Ensure the socket is not dropped before deferral. +*/ +if (!test_and_set_bit (QUIC_MTU_REDUCED_DEFERRED , &sk->sk_tsq_flags)) +sock_hold (sk); + goto out; + } +/* Otherwise, process the MTU reduction now. */ +quic_packet_rcv_err_pmtu (sk); +out: +bh_unlock_sock (sk); +sock_put (sk); +return ret; +} +
net/quic/packet.c line 254
Quoted 10 lines
net/quic/packet.c new file +10
-0+#define QUIC_PACKET_BACKLOG_MAX 4096 + +/* Queue a packet for later processing when sleeping is allowed. */ +static int quic_packet_backlog_schedule (struct net *net,struct sk_buff *skb) +{ +struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +struct quic_net *qn =quic_net (net); + +if (cb->backlog) +return 0 ;
The above test is present also in the only caller of this function. It should be removed from there.
[...]> +/* Work function to process packets in the backlog queue. */
net/quic/packet.c line 651
Quoted 13 lines
net/quic/packet.c new file +13
-0+void quic_packet_backlog_work (struct work_struct *work) +{ + struct quic_net *qn = container_of(work, struct quic_net, work); +struct sk_buff *skb; +struct sock *sk; + + skb =skb_dequeue (&qn->backlog_list); +while (skb) { + sk =quic_packet_get_listen_sock (skb); +if (!sk) +continue ; + +lock_sock (sk);
Possibly lock_sock_fast(sk);
net/quic/packet.c line 658
Quoted 7 lines
net/quic/packet.c new file +7
-0+quic_packet_process (sk, skb); +release_sock (sk); +sock_put (sk); + + skb =skb_dequeue (&qn->backlog_list); + } +}
[...]> +/* Create and transmit a new QUIC packet. */
Quoted 1 line
+int
quic_packet_create(struct sock *sk)
Possibly rename the function accordingly to its actual action, i.e. quic_packet_create_xmit()
[...]> @@ -291,6 +294,8 @@ static void __net_exit quic_net_exit(struct net *net)
net/quic/protocol.c static void __net_exit quic_net_exit(struct net *net) · line 298
Quoted 5 lines
#ifdef
CONFIG_PROC_FSnet/quic/protocol.c +2
-0quic_net_proc_exit (net);#endif +skb_queue_purge (&qn->backlog_list); +cancel_work_sync (&qn->work);
Likely: disable_work_sync()
net/quic/protocol.c static void __net_exit quic_net_exit(struct net *net) · line 301
Quoted 3 lines
net/quic/protocol.c +0
-0quic_crypto_free (&qn->crypto);free_percpu (qn->stat); qn->stat =NULL ;
EPATCHISTOOBIG, very hard to process. Please split this one it at least 2 (i.e. rx and tx part), even if the series will grow above 15
/P
Re: [PATCH net-next v4 15/15] quic: add packet builder and parser base
Quoted 49 lines · Paolo Abeni
Quoted 47 lines · Xin Long
net/quic/packet.c new file +47
-0+/* Process PMTU reduction event on a QUIC socket. */ +void quic_packet_rcv_err_pmtu (struct sock *sk) +{ +struct quic_path_group *paths =quic_paths (sk); +struct quic_packet *packet =quic_packet (sk); +struct quic_config *c =quic_config (sk); +u32 pathmtu, info, taglen; +struct dst_entry *dst; +bool reset_timer; + +if (!ip_sk_accept_pmtu (sk)) +return ; + + info =clamp (paths->mtu_info,QUIC_PATH_MIN_PMTU ,QUIC_PATH_MAX_PMTU ); +/* If PLPMTUD is not enabled, update MSS using the route and ICMP info. */ +if (!c->plpmtud_probe_interval) { +if (quic_packet_route (sk) <0 ) +return ; + + dst =__sk_dst_get (sk); + dst->ops->update_pmtu (dst, sk,NULL , info, true); +quic_packet_mss_update (sk, info - packet->hlen); +return ; + } +/* PLPMTUD is enabled: adjust to smaller PMTU, subtract headers and AEAD tag. Also +* notify the QUIC path layer for possible state changes and probing. +*/ + taglen =quic_packet_taglen (packet); + info = info - packet->hlen - taglen; + pathmtu =quic_path_pl_toobig (paths, info, &reset_timer); +if (reset_timer) +quic_timer_reset (sk,QUIC_TIMER_PMTU , c->plpmtud_probe_interval); +if (pathmtu) +quic_packet_mss_update (sk, pathmtu + taglen); +} + +/* Handle ICMP Toobig packet and update QUIC socket path MTU. */ +static int quic_packet_rcv_err (struct sk_buff *skb) +{ +union quic_addr daddr, saddr; +struct sock *sk =NULL ; +int ret =0 ; +u32 info; + +/* All we can do is lookup the matching QUIC socket by addresses. */ +quic_get_msg_addrs (skb, &saddr, &daddr); + sk =quic_sock_lookup (skb, &daddr, &saddr,NULL );
Quoted 8 lines
net/quic/packet.c line 221
Quoted 5 lines
net/quic/packet.c new file +5
-0+if (!sk) +return -ENOENT ; + +bh_lock_sock (sk); +if (quic_is_listen (sk))The above looks race-prone. You should check the status only when holding the sk socket lock, i.e. if !sock_owned_by_user(sk)
This check can be deleted now, as quic_sock_lookup() has only returned regular socket since I moved listen socket to another hashtable.
Quoted 8 lines
net/quic/packet.c line 225
Quoted 5 lines
net/quic/packet.c new file +4
-0+ goto out; + +if (quic_get_mtu_info (skb, &info)) + goto out;This can be moved outside the lock.
Right.
Quoted 24 lines
Quoted 24 lines
net/quic/packet.c new file +24
-0+ + ret =1 ;/* Success: update socket path MTU info. */ +quic_paths (sk)->mtu_info = info; +if (sock_owned_by_user (sk)) { +/* Socket is in use by userspace context. Defer MTU processing to later via +* tasklet. Ensure the socket is not dropped before deferral. +*/ +if (!test_and_set_bit (QUIC_MTU_REDUCED_DEFERRED , &sk->sk_tsq_flags)) +sock_hold (sk); + goto out; + } +/* Otherwise, process the MTU reduction now. */ +quic_packet_rcv_err_pmtu (sk); +out: +bh_unlock_sock (sk); +sock_put (sk); +return ret; +} + +#define QUIC_PACKET_BACKLOG_MAX 4096 + +/* Queue a packet for later processing when sleeping is allowed. */ +static int quic_packet_backlog_schedule (struct net *net,struct sk_buff *skb) +{
Quoted 8 lines
net/quic/packet.c line 254
Quoted 5 lines
net/quic/packet.c new file +5
-0+struct quic_skb_cb *cb =QUIC_SKB_CB (skb); +struct quic_net *qn =quic_net (net); + +if (cb->backlog) +return 0 ;The above test is present also in the only caller of this function. It should be removed from there.
I may delete the one from the caller, as there will be other callers in the 2nd patchset need it to be checked in here.
Quoted 17 lines
[...]> +/* Work function to process packets in the backlog queue. */
net/quic/packet.c line 651
Quoted 13 lines
net/quic/packet.c new file +13
-0+void quic_packet_backlog_work (struct work_struct *work) +{ + struct quic_net *qn = container_of(work, struct quic_net, work); +struct sk_buff *skb; +struct sock *sk; + + skb =skb_dequeue (&qn->backlog_list); +while (skb) { + sk =quic_packet_get_listen_sock (skb); +if (!sk) +continue ; + +lock_sock (sk);Possibly
lock_sock_fast(sk);
These are some control QUIC packets (not on the main data path) for which we need to generate keys from header fields in order to process them.
However, the kernel crypto API cannot install a key into a tfm in atomic context, so we must use a workqueue to handle key generation, installation, and then decryption.
lock_sock_fast() can not be used here, otherwise this path runs in atomic context again.
Quoted 10 lines
net/quic/packet.c line 658
Quoted 8 lines
net/quic/packet.c new file +7
-0+quic_packet_process (sk, skb); +release_sock (sk); +sock_put (sk); + + skb =skb_dequeue (&qn->backlog_list); + } +}[...]> +/* Create and transmit a new QUIC packet. */
Quoted 3 lines
Quoted 1 line
+int
quic_packet_create(struct sock *sk)Possibly rename the function accordingly to its actual action, i.e.
quic_packet_create_xmit()
I will use quic_packet_create_and_xmit().
Quoted 11 lines
[...]> @@ -291,6 +294,8 @@ static void
__net_exitquic_net_exit(struct net *net)net/quic/protocol.c static void __net_exit quic_net_exit(struct net *net) · line 298
Quoted 5 lines
#ifdef CONFIG_PROC_FS quic_net_proc_exit(net);#endif
net/quic/protocol.c +2
-0+skb_queue_purge (&qn->backlog_list); +cancel_work_sync (&qn->work);Likely:
disable_work_sync()
Will update it.
Quoted 7 lines
Quoted 3 lines
quic_crypto_free(&qn->crypto); free_percpu(qn->stat); qn->stat = NULL;EPATCHISTOOBIG, very hard to process. Please split this one it at least 2 (i.e. rx and tx part), even if the series will grow above 15
Sure, I will do it.
BTW, about the MAINTAINERS entry Jakub mentioned, should I create a new patch for it, or append it in one of these patches, like into:
[PATCH net-next 02/15] net: build socket infrastructure for QUIC protocol
Thanks.
Jakub Kicinski
maintainerQuoted 2 lines · On Wed, 29 Oct 2025 10:35:42 -0400 Xin Long wrote:
net/Kconfig | 1 + net/Makefile | 1 +
Haven't gotten to reviewing yet myself, hopefully Paolo will find time tomorrow. But you're definitely missing a MAINTAINERS entry...
Quoted 7 lines · Jakub Kicinski
Quoted 2 lines · On Wed, 29 Oct 2025 10:35:42 -0400 Xin Long wrote:
net/Kconfig | 1 + net/Makefile | 1 +Haven't gotten to reviewing yet myself, hopefully Paolo will find time tomorrow. But you're definitely missing a
MAINTAINERSentry...
I was planning to add the MAINTAINERS entry in the second patchset, since that patch will introduce Documentation/networking/quic.rst, which should be listed in the entry as:
F: Documentation/networking/quic.rst
I guess it should be fine to add the MAINTAINERS entry now without this F: line in the current patchset, and then update it in the second patchset once the file exists.
Thanks.