Skip to content

Commit 0d91f19

Browse files
committed
add QuinnReuseportIdGenerator
1 parent 212557a commit 0d91f19

7 files changed

Lines changed: 104 additions & 9 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ rustls-pki-types = { version = "1", default-features = false }
168168
rustls = { version = "0.23.40", default-features = false, features = ["std", "tls12", "brotli"] }
169169
tokio-rustls = { version = "0.26", default-features = false, features = ["tls12"] }
170170
quinn = { version = "0.11", default-features = false, features = ["runtime-tokio"] }
171+
quinn-proto = { version = "0.11", default-features = false }
171172
quinn-udp = { version = "0.5.9", default-features = false, features = ["fast-apple-datapath"] }
172173
#
173174
openssl = { package = "variant-ssl", version = "0.17.30" }

lib/vey-daemon/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,5 @@ mimalloc = ["dep:vey-mimalloc"]
6363
ebpf = ["dep:vey-reuseport"]
6464
event-log = ["dep:vey-fluentd"]
6565
register = ["vey-yaml/http", "dep:http", "dep:serde_json", "dep:vey-http"]
66-
quic = ["dep:quinn"]
66+
quic = ["dep:quinn", "vey-types/quinn"]
6767
openssl-async-job = ["vey-runtime/openssl-async-job"]

lib/vey-daemon/src/listen/quic.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use vey_reuseport::quic::{QuicSocketSelectGuard, QuicSocketSelector};
2121
use vey_socket::RawSocket;
2222
use vey_std_ext::net::SocketAddrExt;
2323
use vey_types::acl::{AclAction, AclNetworkRule};
24+
#[cfg(feature = "ebpf")]
25+
use vey_types::net::QuinnReuseportIdGenerator;
2426
use vey_types::net::UdpListenConfig;
2527

2628
use crate::listen::{ListenAliveGuard, ListenStats};
@@ -114,13 +116,6 @@ where
114116

115117
for i in 0..instance_count {
116118
let socket = vey_socket::udp::new_std_bind_listen(&self.listen_config)?;
117-
#[cfg(feature = "ebpf")]
118-
let guard = if let Some(selector) = &mut self.socket_selector {
119-
let guard = selector.add_socket(RawSocket::from(&socket))?;
120-
Some(guard)
121-
} else {
122-
None
123-
};
124119
let listen_addr = socket.local_addr()?;
125120

126121
let mut endpoint_config = EndpointConfig::default();
@@ -130,6 +125,17 @@ where
130125
warn!("ignored UDP payload size {payload_max_size}: {e}");
131126
}
132127

128+
#[cfg(feature = "ebpf")]
129+
let guard = if let Some(selector) = &mut self.socket_selector {
130+
let guard = selector.add_socket(RawSocket::from(&socket))?;
131+
let cid_generator = QuinnReuseportIdGenerator::new(guard.cookie());
132+
// TODO set cid lifetime
133+
endpoint_config.cid_generator(move || Box::new(cid_generator));
134+
Some(guard)
135+
} else {
136+
None
137+
};
138+
133139
let runtime = ListenQuicRuntimeInstance {
134140
server: self.server.clone(),
135141
server_type: self.server.r#type(),

lib/vey-types/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ arcstr.workspace = true
2525
memchr.workspace = true
2626
constant_time_eq.workspace = true
2727
zeroize.workspace = true
28+
zerocopy.workspace = true
2829
url.workspace = true
2930
num-traits.workspace = true
3031
arc-swap.workspace = true
@@ -39,6 +40,7 @@ radix_trie = { workspace = true, optional = true }
3940
rustls = { workspace = true, optional = true }
4041
rustls-pki-types = { workspace = true, optional = true }
4142
quinn = { workspace = true, optional = true }
43+
quinn-proto = { workspace = true, optional = true }
4244
webpki-roots = { version = "1.0", optional = true }
4345
rustls-native-certs = { version = "0.8", optional = true }
4446
openssl = { workspace = true, optional = true }
@@ -61,7 +63,7 @@ quic = []
6163
auth-crypt = ["dep:openssl", "dep:blake3", "dep:hex"]
6264
auth-facts = ["dep:ip_network"]
6365
resolve = ["dep:ahash", "dep:radix_trie", "dep:fastrand"]
64-
quinn = ["dep:quinn", "quic"]
66+
quinn = ["dep:quinn", "dep:quinn-proto", "quic"]
6567
rustls = ["dep:rustls", "dep:rustls-pki-types", "dep:webpki-roots", "dep:rustls-native-certs", "dep:lru", "dep:ahash"]
6668
rustls-ring = ["rustls", "rustls/ring", "quinn?/rustls-ring"]
6769
rustls-aws-lc = ["rustls", "rustls/aws-lc-rs", "quinn?/rustls-aws-lc-rs"]
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* SPDX-FileCopyrightText: 2026 VEY-OSS Developers.
4+
*/
5+
6+
use std::hash::Hasher;
7+
use std::time::Duration;
8+
9+
use quinn_proto::{ConnectionId, ConnectionIdGenerator, InvalidCid};
10+
use rustc_hash::FxHasher;
11+
use zerocopy::{FromBytes, IntoBytes};
12+
13+
const CID_LENGTH: usize = 20;
14+
const CID_COOKIE_LENGTH: usize = 8;
15+
const CID_NONCE_LENGTH: usize = 4;
16+
17+
#[derive(Clone, Copy)]
18+
pub struct QuinnReuseportIdGenerator {
19+
key: u64,
20+
cookie: u64,
21+
cid_lifetime: Option<Duration>,
22+
}
23+
24+
impl QuinnReuseportIdGenerator {
25+
pub fn new(cookie: u64) -> Self {
26+
let key = rand::random();
27+
QuinnReuseportIdGenerator {
28+
key,
29+
cookie,
30+
cid_lifetime: None,
31+
}
32+
}
33+
}
34+
35+
impl ConnectionIdGenerator for QuinnReuseportIdGenerator {
36+
fn generate_cid(&mut self) -> ConnectionId {
37+
let mut buf = [0; CID_LENGTH];
38+
buf[..CID_COOKIE_LENGTH].copy_from_slice(&self.cookie.to_be_bytes());
39+
rand::fill(&mut buf[CID_COOKIE_LENGTH..CID_COOKIE_LENGTH + CID_NONCE_LENGTH]);
40+
41+
let mut hasher = FxHasher::default();
42+
hasher.write_u64(self.key);
43+
hasher.write(&buf[..CID_COOKIE_LENGTH + CID_NONCE_LENGTH]);
44+
let hash = hasher.finish();
45+
buf[CID_COOKIE_LENGTH + CID_NONCE_LENGTH..].copy_from_slice(&hash.as_bytes());
46+
ConnectionId::new(&buf)
47+
}
48+
49+
fn validate(&self, cid: &ConnectionId) -> Result<(), InvalidCid> {
50+
if cid.len() != CID_LENGTH {
51+
return Err(InvalidCid);
52+
}
53+
54+
let cookie = u64::from_be_bytes(cid[..CID_COOKIE_LENGTH].try_into().unwrap());
55+
if cookie != self.cookie {
56+
return Err(InvalidCid);
57+
}
58+
59+
let given_hash = u64::ref_from_bytes(&cid[CID_COOKIE_LENGTH + CID_NONCE_LENGTH..])
60+
.map_err(|_e| InvalidCid)?;
61+
62+
let mut hasher = FxHasher::default();
63+
hasher.write_u64(self.key);
64+
hasher.write(&cid[..CID_COOKIE_LENGTH + CID_NONCE_LENGTH]);
65+
let expected_hash = hasher.finish();
66+
67+
if *given_hash != expected_hash {
68+
Err(InvalidCid)
69+
} else {
70+
Ok(())
71+
}
72+
}
73+
74+
fn cid_len(&self) -> usize {
75+
20
76+
}
77+
78+
fn cid_lifetime(&self) -> Option<Duration> {
79+
self.cid_lifetime
80+
}
81+
}

lib/vey-types/src/net/quinn/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55

66
mod transport;
77
pub use transport::QuinnTransportConfigBuilder;
8+
9+
mod connection_id;
10+
pub use connection_id::QuinnReuseportIdGenerator;

0 commit comments

Comments
 (0)