1use {
4 agave_xdp::{
5 ecn_codepoint::EcnCodepoint as XdpEcnCodepoint,
6 transmitter::{BytesTxPacket, XdpSender},
7 },
8 bytes::Bytes,
9 crossbeam_channel::TrySendError,
10 quinn::{
11 AsyncUdpSocket, Runtime, TokioRuntime, UdpPoller,
12 udp::{EcnCodepoint as QuinnEcnCodepoint, RecvMeta, Transmit},
13 },
14 std::{
15 fmt::{self, Debug},
16 io::{self, IoSliceMut},
17 net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4},
18 pin::Pin,
19 sync::Arc,
20 task::{Context, Poll},
21 },
22};
23
24#[derive(Debug)]
27pub enum QuicSocket {
28 Xdp(QuicXdpSocketParts),
30 Kernel(std::net::UdpSocket),
32}
33
34impl From<std::net::UdpSocket> for QuicSocket {
35 fn from(socket: std::net::UdpSocket) -> Self {
36 QuicSocket::Kernel(socket)
37 }
38}
39
40impl QuicSocket {
41 pub fn with_xdp(
42 socket: std::net::UdpSocket,
43 fallback_src_ip: Ipv4Addr,
44 xdp_sender: XdpSender,
45 ) -> Self {
46 Self::Xdp(QuicXdpSocketParts {
47 socket,
48 fallback_src_ip,
49 xdp_sender,
50 })
51 }
52
53 #[cfg(feature = "dev-context-only-utils")]
54 pub fn local_addr(&self) -> io::Result<SocketAddr> {
55 match self {
56 QuicSocket::Xdp(parts) => parts.socket.local_addr(),
57 QuicSocket::Kernel(socket) => socket.local_addr(),
58 }
59 }
60}
61
62pub struct QuicXdpSocketParts {
69 pub socket: std::net::UdpSocket,
70 pub fallback_src_ip: Ipv4Addr,
71 pub xdp_sender: XdpSender,
72}
73
74impl Debug for QuicXdpSocketParts {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 f.debug_struct("QuicXdpSocketParts")
77 .field("socket", &self.socket)
78 .finish()
79 }
80}
81
82pub(crate) struct QuicXdpTxSocket {
88 udp_socket: Arc<dyn AsyncUdpSocket>,
89 xdp_sender: QuicXdpSender,
90 local_ips: Vec<Ipv4Addr>,
91}
92
93impl QuicXdpTxSocket {
94 pub(crate) fn new(
95 socket: std::net::UdpSocket,
96 fallback_src_ip: Ipv4Addr,
97 xdp_sender: XdpSender,
98 ) -> io::Result<Self> {
99 let src_addr = socket.local_addr()?;
100 let SocketAddr::V4(src_addr) = src_addr else {
101 return Err(io::Error::new(
102 io::ErrorKind::InvalidInput,
103 "Only IPv4 addresses are supported",
104 ));
105 };
106 let src_addr = if src_addr.ip().is_unspecified() {
108 SocketAddrV4::new(fallback_src_ip, src_addr.port())
109 } else {
110 src_addr
111 };
112
113 let local_ips = collect_local_ipv4_ips()?;
117
118 Ok(Self {
119 udp_socket: TokioRuntime.wrap_udp_socket(socket)?,
120 xdp_sender: QuicXdpSender::new(xdp_sender, src_addr),
121 local_ips,
122 })
123 }
124
125 fn should_use_kernel_udp(&self, dst: SocketAddr) -> bool {
126 dst.ip().is_loopback() || matches!(dst.ip(), IpAddr::V4(ip) if self.local_ips.contains(&ip))
127 }
128}
129
130impl fmt::Debug for QuicXdpTxSocket {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 f.debug_struct("QuicXdpTxSocket")
133 .field("local_addr", &self.udp_socket.local_addr())
134 .finish_non_exhaustive()
135 }
136}
137
138impl AsyncUdpSocket for QuicXdpTxSocket {
139 fn create_io_poller(self: Arc<Self>) -> Pin<Box<dyn UdpPoller>> {
140 self.udp_socket.clone().create_io_poller()
152 }
153
154 fn try_send(&self, t: &Transmit<'_>) -> io::Result<()> {
164 if self.should_use_kernel_udp(t.destination) {
165 return self.udp_socket.try_send(t);
166 }
167 if t.destination.is_ipv6() {
168 return Err(io::Error::new(
169 io::ErrorKind::InvalidInput,
170 "IPv6 destination addresses are not supported for AF_XDP sends",
171 ));
172 }
173 let src_ip = match t.src_ip {
174 Some(IpAddr::V4(ip)) => Some(ip),
175 Some(IpAddr::V6(_)) => {
176 return Err(io::Error::new(
177 io::ErrorKind::InvalidInput,
178 "IPv6 source addresses are not supported",
179 ));
180 }
181 None => None,
182 };
183
184 debug_assert!(
185 t.segment_size.is_none(),
186 "GSO segmentation is disabled for AF_XDP sends, but segment_size is {:?}",
187 t.segment_size
188 );
189
190 let payload = Bytes::copy_from_slice(t.contents);
191 match self
192 .xdp_sender
193 .try_send(src_ip, t.destination, t.ecn, payload)
194 {
195 Ok(()) => Ok(()),
196 Err(TrySendError::Full(_)) => Err(io::ErrorKind::WouldBlock.into()),
197 Err(TrySendError::Disconnected(_)) => Err(io::ErrorKind::BrokenPipe.into()),
198 }
199 }
200
201 fn poll_recv(
202 &self,
203 cx: &mut Context,
204 bufs: &mut [IoSliceMut<'_>],
205 meta: &mut [RecvMeta],
206 ) -> Poll<io::Result<usize>> {
207 self.udp_socket.poll_recv(cx, bufs, meta)
208 }
209
210 fn local_addr(&self) -> io::Result<SocketAddr> {
211 self.udp_socket.local_addr()
212 }
213
214 fn max_transmit_segments(&self) -> usize {
215 1
217 }
218
219 fn max_receive_segments(&self) -> usize {
220 self.udp_socket.max_receive_segments()
221 }
222
223 fn may_fragment(&self) -> bool {
224 false
225 }
226}
227
228struct QuicXdpSender {
233 xdp_sender: XdpSender,
234 src_addr: SocketAddrV4,
235}
236
237impl QuicXdpSender {
238 fn new(xdp_sender: XdpSender, src_addr: SocketAddrV4) -> Self {
239 Self {
240 xdp_sender,
241 src_addr,
242 }
243 }
244
245 fn try_send(
246 &self,
247 src_ip: Option<Ipv4Addr>,
248 destination: SocketAddr,
249 ecn: Option<QuinnEcnCodepoint>,
250 payload: Bytes,
251 ) -> Result<(), TrySendError<BytesTxPacket>> {
252 let sender_key = if self.xdp_sender.len() == 1 {
255 0
256 } else {
257 fold_xor(destination_ip_key(&destination)) as usize
258 };
259
260 let src_ip = src_ip.unwrap_or(*self.src_addr.ip());
261 let src_addr = SocketAddrV4::new(src_ip, self.src_addr.port());
264 let ecn = ecn.map(quinn_ecn_to_xdp);
265
266 let mut packet = BytesTxPacket::new(src_addr, destination, ecn, payload);
267 packet.set_allow_mtu_overflow(true);
268 self.xdp_sender.try_send(sender_key, packet)
269 }
270}
271
272#[cfg(target_os = "linux")]
274fn collect_local_ipv4_ips() -> io::Result<Vec<Ipv4Addr>> {
275 use nix::ifaddrs::getifaddrs;
276
277 let mut ips = Vec::new();
278 for ifa in getifaddrs().map_err(io::Error::other)? {
279 let Some(addr) = ifa.address else { continue };
280 if let Some(v4) = addr.as_sockaddr_in() {
281 let ip = v4.ip();
282 if !ips.contains(&ip) {
283 ips.push(ip);
284 }
285 }
286 }
287 Ok(ips)
288}
289
290#[cfg(not(target_os = "linux"))]
291fn collect_local_ipv4_ips() -> io::Result<Vec<Ipv4Addr>> {
292 Ok(Vec::new())
293}
294
295#[inline]
296const fn quinn_ecn_to_xdp(ecn: QuinnEcnCodepoint) -> XdpEcnCodepoint {
297 match ecn {
298 QuinnEcnCodepoint::Ect0 => XdpEcnCodepoint::Ect0,
299 QuinnEcnCodepoint::Ect1 => XdpEcnCodepoint::Ect1,
300 QuinnEcnCodepoint::Ce => XdpEcnCodepoint::Ce,
301 }
302}
303
304#[inline]
305fn fold_xor(mut x: u32) -> u32 {
306 x ^= x >> 16;
307 x ^= x >> 8;
308 x
309}
310
311#[inline]
312fn destination_ip_key(destination: &SocketAddr) -> u32 {
313 match destination {
314 SocketAddr::V4(destination) => u32::from(*destination.ip()),
315 SocketAddr::V6(_) => unreachable!("IPv6 destinations are rejected before AF_XDP send"),
316 }
317}