Skip to main content

tokio_quiche/quic/router/
mod.rs

1// Copyright (C) 2025, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27pub(crate) mod acceptor;
28pub(crate) mod connector;
29
30use super::connection::ConnectionMap;
31use super::connection::HandshakeInfo;
32use super::connection::Incoming;
33use super::connection::InitialQuicConnection;
34use super::connection::QuicConnectionParams;
35use super::io::worker::WriterConfig;
36use super::QuicheConnection;
37use crate::metrics::labels;
38use crate::metrics::quic_expensive_metrics_ip_reduce;
39use crate::metrics::Metrics;
40use crate::quic::connection::SharedConnectionIdGenerator;
41use crate::settings::Config;
42use datagram_socket::DatagramSocketRecv;
43use datagram_socket::DatagramSocketSend;
44use foundations::telemetry::log;
45use quiche::ConnectionId;
46use quiche::Header;
47use quiche::MAX_CONN_ID_LEN;
48use std::default::Default;
49use std::future::Future;
50use std::io;
51use std::net::SocketAddr;
52use std::pin::Pin;
53use std::sync::Arc;
54use std::task::ready;
55use std::task::Context;
56use std::task::Poll;
57use std::time::Instant;
58use std::time::SystemTime;
59use task_killswitch::spawn_with_killswitch;
60use tokio::sync::mpsc;
61
62#[cfg(target_os = "linux")]
63use foundations::telemetry::metrics::Counter;
64#[cfg(target_os = "linux")]
65use foundations::telemetry::metrics::TimeHistogram;
66#[cfg(target_os = "linux")]
67use libc::sockaddr_in;
68#[cfg(target_os = "linux")]
69use libc::sockaddr_in6;
70
71type ConnStream<Tx, M> = mpsc::Receiver<io::Result<InitialQuicConnection<Tx, M>>>;
72
73/// How many incoming packets (GRO batches) to process before checking the
74/// `ConnectionMapCommand` queue again. 30 means "check the command queue once
75/// every 30 packets".
76const PACKET_RX_YIELD_AFTER: usize = 30;
77/// `ConnectionMapCommand` processing batch size to amortize receive operations.
78const CONN_MAP_CMD_BATCH_SIZE: usize = 128;
79
80#[cfg(feature = "perf-quic-listener-metrics")]
81mod listener_stage_timer {
82    use foundations::telemetry::metrics::TimeHistogram;
83    use std::time::Instant;
84
85    pub(super) struct ListenerStageTimer {
86        start: Instant,
87        time_hist: TimeHistogram,
88    }
89
90    impl ListenerStageTimer {
91        pub(super) fn new(
92            start: Instant, time_hist: TimeHistogram,
93        ) -> ListenerStageTimer {
94            ListenerStageTimer { start, time_hist }
95        }
96    }
97
98    impl Drop for ListenerStageTimer {
99        fn drop(&mut self) {
100            self.time_hist
101                .observe((Instant::now() - self.start).as_nanos() as u64);
102        }
103    }
104}
105
106#[derive(Debug)]
107struct PollRecvData {
108    buf: Vec<u8>,
109    // The packet's source, e.g., the peer's address
110    src_addr: SocketAddr,
111    // The packet's original destination. If the original destination is
112    // different from the local listening address, this will be `None`.
113    dst_addr_override: Option<SocketAddr>,
114    rx_time: Option<SystemTime>,
115    gro: Option<i32>,
116    #[cfg(target_os = "linux")]
117    so_mark_data: Option<[u8; 4]>,
118}
119
120/// A message to the listener notifiying a mapping for a connection should be
121/// removed.
122pub enum ConnectionMapCommand {
123    MapCid {
124        existing_cid: ConnectionId<'static>,
125        new_cid: ConnectionId<'static>,
126    },
127    UnmapCid(ConnectionId<'static>),
128}
129
130/// An `InboundPacketRouter` maintains a map of quic connections and routes
131/// [`Incoming`] packets from the [recv half][rh] of a datagram socket to those
132/// connections or some quic initials handler. There is only 1
133/// `InboundPacketRouter` per socket.
134///
135/// [rh]: datagram_socket::DatagramSocketRecv
136///
137/// When a packet (or batch of packets) is received, the router will either
138/// route those packets to an established
139/// [`QuicConnection`](super::QuicConnection) or have a them handled by a
140/// `InitialPacketHandler` which either acts as a quic listener or
141/// quic connector, a server or client respectively.
142///
143/// If you only have a single connection, or if you need more control over the
144/// socket, use `QuicConnection` directly instead.
145pub struct InboundPacketRouter<Tx, Rx, M, I>
146where
147    Tx: DatagramSocketSend + Send + 'static,
148    M: Metrics,
149{
150    socket_tx: Arc<Tx>,
151    socket_rx: Rx,
152    local_addr: SocketAddr,
153    config: Config,
154    conns: ConnectionMap,
155    incoming_packet_handler: I,
156    shutdown_tx: Option<mpsc::Sender<()>>,
157    shutdown_rx: mpsc::Receiver<()>,
158    conn_map_cmd_tx: mpsc::UnboundedSender<ConnectionMapCommand>,
159    conn_map_cmd_rx: mpsc::UnboundedReceiver<ConnectionMapCommand>,
160    /// Reusable buffer to receive a batch of `ConnectionMapCommand`s in
161    /// `poll_conn_map_commands`. Always fully drained after use, so its length
162    /// should be 0 outside of `poll_conn_map_commands`.
163    conn_map_cmd_buf: Vec<ConnectionMapCommand>,
164    accept_sink: mpsc::Sender<io::Result<InitialQuicConnection<Tx, M>>>,
165    metrics: M,
166    #[cfg(target_os = "linux")]
167    udp_drop_count: u32,
168
169    #[cfg(target_os = "linux")]
170    reusable_cmsg_space: Vec<u8>,
171
172    #[cfg(target_os = "linux")]
173    buf: Vec<u8>,
174
175    // We keep the metrics in here, to avoid cloning them each packet
176    #[cfg(target_os = "linux")]
177    metrics_handshake_time_seconds: TimeHistogram,
178    #[cfg(target_os = "linux")]
179    metrics_udp_drop_count: Counter,
180}
181
182impl<Tx, Rx, M, I> InboundPacketRouter<Tx, Rx, M, I>
183where
184    Tx: DatagramSocketSend + Send + 'static,
185    Rx: DatagramSocketRecv,
186    M: Metrics,
187    I: InitialPacketHandler,
188{
189    pub(crate) fn new(
190        config: Config, socket_tx: Arc<Tx>, socket_rx: Rx,
191        local_addr: SocketAddr, incoming_packet_handler: I, metrics: M,
192    ) -> (Self, ConnStream<Tx, M>) {
193        let (shutdown_tx, shutdown_rx) = mpsc::channel(1);
194        let (accept_sink, accept_stream) = mpsc::channel(config.listen_backlog);
195        let (conn_map_cmd_tx, conn_map_cmd_rx) = mpsc::unbounded_channel();
196
197        (
198            InboundPacketRouter {
199                local_addr,
200                socket_tx,
201                socket_rx,
202                conns: ConnectionMap::default(),
203                incoming_packet_handler,
204                shutdown_tx: Some(shutdown_tx),
205                shutdown_rx,
206                conn_map_cmd_tx,
207                conn_map_cmd_rx,
208                conn_map_cmd_buf: Vec::with_capacity(4),
209                accept_sink,
210                #[cfg(target_os = "linux")]
211                udp_drop_count: 0,
212                #[cfg(target_os = "linux")]
213                // Specify CMSG space. Even if they're not all currently used, the cmsg buffer may
214                // have been configured by a previous version of Tokio-Quiche with the socket
215                // re-used on graceful restart. As such, this vector should _only grow_, and care
216                // should be taken when adding new cmsgs.
217                reusable_cmsg_space: nix::cmsg_space!(
218                    u32, // GRO
219                    nix::sys::time::TimeSpec, // timestamp
220                    u16, // drop count
221                    sockaddr_in, // IP_RECVORIGDSTADDR
222                    sockaddr_in6, // IPV6_RECVORIGDSTADDR
223                    u32 // SO_MARK
224                ),
225
226                config,
227
228                #[cfg(target_os = "linux")]
229                buf: Vec::new(),
230                #[cfg(target_os = "linux")]
231                metrics_handshake_time_seconds: metrics.handshake_time_seconds(labels::QuicHandshakeStage::QueueWaiting),
232                #[cfg(target_os = "linux")]
233                metrics_udp_drop_count: metrics.udp_drop_count(),
234
235                metrics,
236
237            },
238            accept_stream,
239        )
240    }
241
242    fn on_incoming(&mut self, mut incoming: Incoming) -> io::Result<()> {
243        #[cfg(feature = "perf-quic-listener-metrics")]
244        let start = std::time::Instant::now();
245
246        if let Some(dcid) = short_dcid(&incoming.buf) {
247            if let Some(ev_sender) = self.conns.get(&dcid) {
248                let _ = ev_sender.try_send(incoming);
249                return Ok(());
250            }
251        }
252
253        let hdr = Header::from_slice(&mut incoming.buf, MAX_CONN_ID_LEN)
254            .map_err(|e| match e {
255                quiche::Error::BufferTooShort | quiche::Error::InvalidPacket =>
256                    labels::QuicInvalidInitialPacketError::FailedToParse.into(),
257                e => io::Error::other(e),
258            })?;
259
260        if let Some(ev_sender) = self.conns.get(&hdr.dcid) {
261            let _ = ev_sender.try_send(incoming);
262            return Ok(());
263        }
264
265        #[cfg(feature = "perf-quic-listener-metrics")]
266        let _timer = listener_stage_timer::ListenerStageTimer::new(
267            start,
268            self.metrics.handshake_time_seconds(
269                labels::QuicHandshakeStage::HandshakeProtocol,
270            ),
271        );
272
273        if self.shutdown_tx.is_none() {
274            return Ok(());
275        }
276
277        let local_addr = incoming.local_addr;
278        let peer_addr = incoming.peer_addr;
279
280        #[cfg(feature = "perf-quic-listener-metrics")]
281        let init_rx_time = incoming.rx_time;
282
283        let new_connection = self.incoming_packet_handler.handle_initials(
284            incoming,
285            hdr,
286            self.config.as_mut(),
287        )?;
288
289        match new_connection {
290            Some(new_connection) => self.spawn_new_connection(
291                new_connection,
292                local_addr,
293                peer_addr,
294                #[cfg(feature = "perf-quic-listener-metrics")]
295                init_rx_time,
296            ),
297            None => Ok(()),
298        }
299    }
300
301    /// Creates a new [`QuicConnection`](super::QuicConnection) and spawns an
302    /// associated io worker.
303    fn spawn_new_connection(
304        &mut self, new_connection: NewConnection, local_addr: SocketAddr,
305        peer_addr: SocketAddr,
306        #[cfg(feature = "perf-quic-listener-metrics")] init_rx_time: Option<
307            SystemTime,
308        >,
309    ) -> io::Result<()> {
310        let NewConnection {
311            conn,
312            pending_cid,
313            cid_generator,
314            handshake_start_time,
315            initial_pkt,
316        } = new_connection;
317
318        let Some(ref shutdown_tx) = self.shutdown_tx else {
319            // don't create new connections if we're shutting down.
320            return Ok(());
321        };
322        let Ok(send_permit) = self.accept_sink.try_reserve() else {
323            // drop the connection if the backlog is full. the client will retry.
324            return Err(
325                labels::QuicInvalidInitialPacketError::AcceptQueueOverflow.into(),
326            );
327        };
328
329        let scid = conn.source_id().into_owned();
330        let writer_cfg = WriterConfig {
331            peer_addr,
332            local_addr,
333            pending_cid: pending_cid.clone(),
334            with_gso: self.config.has_gso,
335            pacing_offload: self.config.pacing_offload,
336            with_pktinfo: if self.local_addr.is_ipv4() {
337                self.config.has_ippktinfo
338            } else {
339                self.config.has_ipv6pktinfo
340            },
341            pool_send_buffer: self.config.pool_send_buffer,
342        };
343
344        let handshake_info = HandshakeInfo::new(
345            handshake_start_time,
346            self.config.handshake_timeout,
347        );
348
349        let conn = InitialQuicConnection::new(QuicConnectionParams {
350            writer_cfg,
351            initial_pkt,
352            shutdown_tx: shutdown_tx.clone(),
353            conn_map_cmd_tx: self.conn_map_cmd_tx.clone(),
354            scid: scid.clone(),
355            cid_generator,
356            metrics: self.metrics.clone(),
357            #[cfg(feature = "perf-quic-listener-metrics")]
358            init_rx_time,
359            handshake_info,
360            quiche_conn: conn,
361            socket: Arc::clone(&self.socket_tx),
362            local_addr,
363            peer_addr,
364        });
365
366        conn.audit_log_stats
367            .set_transport_handshake_start(instant_to_system(
368                handshake_start_time,
369            ));
370
371        self.conns.insert(&scid, &conn);
372
373        // Add the client-generated "pending" connection ID to the map as well.
374        // This is only required for QUIC servers, because clients can send
375        // Initial packets with arbitrary DCIDs to servers.
376        if let Some(pending_cid) = pending_cid {
377            self.conns.map_cid(&scid, &pending_cid);
378        }
379
380        self.metrics.accepted_initial_packet_count().inc();
381        if self.config.enable_expensive_packet_count_metrics {
382            if let Some(peer_ip) =
383                quic_expensive_metrics_ip_reduce(conn.peer_addr().ip())
384            {
385                self.metrics
386                    .expensive_accepted_initial_packet_count(peer_ip)
387                    .inc();
388            }
389        }
390
391        send_permit.send(Ok(conn));
392        Ok(())
393    }
394}
395
396impl<Tx, Rx, M, I> InboundPacketRouter<Tx, Rx, M, I>
397where
398    Tx: DatagramSocketSend + Send + Sync + 'static,
399    Rx: DatagramSocketRecv,
400    M: Metrics,
401    I: InitialPacketHandler,
402{
403    /// [`InboundPacketRouter::poll_recv_from`] should be used if the underlying
404    /// system or socket does not support rx_time nor GRO.
405    fn poll_recv_from(
406        &mut self, cx: &mut Context<'_>,
407    ) -> Poll<io::Result<PollRecvData>> {
408        let mut buf = Vec::with_capacity(datagram_socket::MAX_DATAGRAM_SIZE);
409        // We use ReadBuf's ability to write to uninitialized memory to avoid
410        // the cost of having to initialize the Vec.
411        let mut read_buf = tokio::io::ReadBuf::uninit(buf.spare_capacity_mut());
412        let addr = ready!(self.socket_rx.poll_recv_from(cx, &mut read_buf))?;
413        let n = read_buf.filled().len();
414        unsafe {
415            // Safety: ReadBuf has guaranteed that `n` initialized bytes have
416            // been written to the buffer, so we can set the vec's length
417            // accordingly
418            buf.set_len(n);
419        }
420        Poll::Ready(Ok(PollRecvData {
421            buf,
422            src_addr: addr,
423            rx_time: None,
424            gro: None,
425            dst_addr_override: None,
426            #[cfg(target_os = "linux")]
427            so_mark_data: None,
428        }))
429    }
430
431    fn poll_recv_and_rx_time(
432        &mut self, cx: &mut Context<'_>,
433    ) -> Poll<io::Result<PollRecvData>> {
434        #[cfg(not(target_os = "linux"))]
435        {
436            self.poll_recv_from(cx)
437        }
438
439        #[cfg(target_os = "linux")]
440        {
441            use libc::SOL_SOCKET;
442            use libc::SO_MARK;
443            use nix::errno::Errno;
444            use nix::sys::socket::*;
445            use std::net::SocketAddrV4;
446            use std::net::SocketAddrV6;
447            use std::os::fd::AsRawFd;
448            use tokio::io::Interest;
449
450            use crate::buf_factory::BufFactory;
451
452            let Some(udp_socket) = self.socket_rx.as_udp_socket() else {
453                // the given socket is not a UDP socket, fall back to the
454                // simple poll_recv_from.
455                return self.poll_recv_from(cx);
456            };
457
458            // Note, the resize will be a no-op after the first call since
459            // we never truncate the `self.buf`
460            self.buf.resize(BufFactory::MAX_BUF_SIZE, 0u8);
461            loop {
462                let iov_s = &mut [io::IoSliceMut::new(&mut self.buf)];
463                match udp_socket.try_io(Interest::READABLE, || {
464                    recvmsg::<SockaddrStorage>(
465                        udp_socket.as_raw_fd(),
466                        iov_s,
467                        Some(&mut self.reusable_cmsg_space),
468                        MsgFlags::empty(),
469                    )
470                    .map_err(|x| x.into())
471                }) {
472                    Ok(r) => {
473                        let filled_buf =
474                            r.iovs().next().map(Vec::from).unwrap_or_default();
475                        // The slices returend by `nix::socket::recvmsg`'s result
476                        // add up to `r.bytes`. This assert is just to make sure
477                        // the code handles the result correctly.
478                        debug_assert_eq!(r.bytes, filled_buf.len());
479
480                        let address = match r.address {
481                            Some(inner) => inner,
482                            _ => return Poll::Ready(Err(Errno::EINVAL.into())),
483                        };
484
485                        let peer_addr = match address.family() {
486                            Some(AddressFamily::Inet) => SocketAddrV4::from(
487                                *address.as_sockaddr_in().unwrap(),
488                            )
489                            .into(),
490                            Some(AddressFamily::Inet6) => SocketAddrV6::from(
491                                *address.as_sockaddr_in6().unwrap(),
492                            )
493                            .into(),
494                            _ => {
495                                return Poll::Ready(Err(Errno::EINVAL.into()));
496                            },
497                        };
498
499                        let mut rx_time = None;
500                        let mut gro = None;
501                        let mut dst_addr_override = None;
502                        let mut mark_bytes: Option<[u8; 4]> = None;
503
504                        let Ok(cmsgs) = r.cmsgs() else {
505                            // Best-effort if we can't read cmsgs.
506                            return Poll::Ready(Ok(PollRecvData {
507                                buf: filled_buf,
508                                src_addr: peer_addr,
509                                dst_addr_override,
510                                rx_time,
511                                gro,
512                                so_mark_data: mark_bytes,
513                            }));
514                        };
515
516                        for cmsg in cmsgs {
517                            match cmsg {
518                                ControlMessageOwned::RxqOvfl(c) => {
519                                    if c != self.udp_drop_count {
520                                        self.metrics_udp_drop_count.inc_by(
521                                            (c - self.udp_drop_count) as u64,
522                                        );
523                                        self.udp_drop_count = c;
524                                    }
525                                },
526                                ControlMessageOwned::ScmTimestampns(val) => {
527                                    rx_time = SystemTime::UNIX_EPOCH
528                                        .checked_add(val.into());
529                                    if let Some(delta) =
530                                        rx_time.and_then(|rx_time| {
531                                            rx_time.elapsed().ok()
532                                        })
533                                    {
534                                        self.metrics_handshake_time_seconds
535                                            .observe(delta.as_nanos() as u64);
536                                    }
537                                },
538                                ControlMessageOwned::UdpGroSegments(val) =>
539                                    gro = Some(val),
540                                ControlMessageOwned::Ipv4OrigDstAddr(val) => {
541                                    let source_addr = std::net::Ipv4Addr::from(
542                                        u32::to_be(val.sin_addr.s_addr),
543                                    );
544                                    let source_port = u16::to_be(val.sin_port);
545
546                                    let parsed_addr =
547                                        SocketAddr::V4(SocketAddrV4::new(
548                                            source_addr,
549                                            source_port,
550                                        ));
551
552                                    dst_addr_override = resolve_dst_addr(
553                                        &self.local_addr,
554                                        &parsed_addr,
555                                    );
556                                },
557                                ControlMessageOwned::Ipv6OrigDstAddr(val) => {
558                                    // Don't have to flip IPv6 bytes since it's a
559                                    // byte array, not a
560                                    // series of bytes parsed as a u32 as in the
561                                    // IPv4 case
562                                    let source_addr = std::net::Ipv6Addr::from(
563                                        val.sin6_addr.s6_addr,
564                                    );
565                                    let source_port = u16::to_be(val.sin6_port);
566                                    let source_flowinfo =
567                                        u32::to_be(val.sin6_flowinfo);
568                                    let source_scope =
569                                        u32::to_be(val.sin6_scope_id);
570
571                                    let parsed_addr =
572                                        SocketAddr::V6(SocketAddrV6::new(
573                                            source_addr,
574                                            source_port,
575                                            source_flowinfo,
576                                            source_scope,
577                                        ));
578
579                                    dst_addr_override = resolve_dst_addr(
580                                        &self.local_addr,
581                                        &parsed_addr,
582                                    );
583                                },
584                                ControlMessageOwned::Ipv4PacketInfo(_) |
585                                ControlMessageOwned::Ipv6PacketInfo(_) => {
586                                    // We only want the destination address from
587                                    // IP_RECVORIGDSTADDR, but we'll get these
588                                    // messages because we set IP_PKTINFO on the
589                                    // socket.
590                                },
591                                ControlMessageOwned::Unknown(raw_cmsg) => {
592                                    let UnknownCmsg {
593                                        cmsg_header,
594                                        data_bytes,
595                                    } = raw_cmsg;
596
597                                    if cmsg_header.cmsg_level == SOL_SOCKET &&
598                                        cmsg_header.cmsg_type == SO_MARK
599                                    {
600                                        let Ok(arr) =
601                                            <[u8; 4]>::try_from(data_bytes)
602                                        else {
603                                            // Should be unreachable as SO_MARK is
604                                            // a u32: https://elixir.bootlin.com/linux/v6.17/source/include/net/sock.h#L487
605                                            continue;
606                                        };
607
608                                        let _ = mark_bytes.insert(arr);
609                                    }
610                                },
611                                _ => {
612                                    // Unrecognized cmsg received, just ignore
613                                    // it.
614                                },
615                            };
616                        }
617
618                        return Poll::Ready(Ok(PollRecvData {
619                            buf: filled_buf,
620                            src_addr: peer_addr,
621                            dst_addr_override,
622                            rx_time,
623                            gro,
624                            so_mark_data: mark_bytes,
625                        }));
626                    },
627                    Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
628                        // NOTE: we manually poll the socket here to register
629                        // interest in the socket to become
630                        // writable for the given `cx`. Under the hood, tokio's
631                        // implementation just checks for
632                        // EWOULDBLOCK and if socket is busy registers provided
633                        // waker to be invoked when the
634                        // socket is free and consequently drive the event loop.
635                        ready!(udp_socket.poll_recv_ready(cx))?
636                    },
637                    Err(e) => return Poll::Ready(Err(e)),
638                }
639            }
640        }
641    }
642
643    fn poll_process_packet(&mut self, cx: &mut Context) -> Poll<()> {
644        let pkt_data = match ready!(self.poll_recv_and_rx_time(cx)) {
645            Ok(v) => v,
646            Err(e) => {
647                log::error!("Incoming packet router encountered recvmsg error"; "error" => e);
648                return Poll::Ready(());
649            },
650        };
651
652        let PollRecvData {
653            buf,
654            src_addr: peer_addr,
655            dst_addr_override,
656            rx_time,
657            gro,
658            #[cfg(target_os = "linux")]
659            so_mark_data,
660        } = pkt_data;
661
662        let send_from = if let Some(dst_addr) = dst_addr_override {
663            log::trace!("overriding local address"; "actual_local" => dst_addr, "configured_local" => self.local_addr);
664            dst_addr
665        } else {
666            self.local_addr
667        };
668
669        let res = self.on_incoming(Incoming {
670            peer_addr,
671            local_addr: send_from,
672            buf,
673            rx_time,
674            gro,
675            #[cfg(target_os = "linux")]
676            so_mark_data,
677        });
678
679        // Only error handling below - if `on_incoming` was successful,
680        // we return here
681        let Err(e) = res else {
682            return Poll::Ready(());
683        };
684
685        let err_type = initial_packet_error_type(&e);
686        self.metrics
687            .rejected_initial_packet_count(err_type.clone())
688            .inc();
689
690        if self.config.enable_expensive_packet_count_metrics {
691            if let Some(peer_ip) =
692                quic_expensive_metrics_ip_reduce(peer_addr.ip())
693            {
694                self.metrics
695                    .expensive_rejected_initial_packet_count(
696                        err_type.clone(),
697                        peer_ip,
698                    )
699                    .inc();
700            }
701        }
702
703        if matches!(err_type, labels::QuicInvalidInitialPacketError::Unexpected) {
704            // don't block packet routing on errors
705            let _ = self.accept_sink.try_send(Err(e));
706        }
707
708        Poll::Ready(())
709    }
710
711    fn poll_conn_map_commands(&mut self, cx: &mut Context) -> Poll<()> {
712        let cmd_rx = &mut self.conn_map_cmd_rx;
713        let buf = &mut self.conn_map_cmd_buf;
714        debug_assert!(buf.is_empty());
715
716        while ready!(cmd_rx.poll_recv_many(cx, buf, CONN_MAP_CMD_BATCH_SIZE)) > 0
717        {
718            for cmd in buf.drain(..) {
719                match cmd {
720                    ConnectionMapCommand::MapCid {
721                        existing_cid,
722                        new_cid,
723                    } => self.conns.map_cid(&existing_cid, &new_cid),
724                    ConnectionMapCommand::UnmapCid(cid) =>
725                        self.conns.unmap_cid(&cid),
726                }
727            }
728        }
729
730        Poll::Ready(())
731    }
732}
733
734// Quickly extract the connection id of a short quic packet without allocating
735fn short_dcid(buf: &[u8]) -> Option<ConnectionId<'_>> {
736    let is_short_dcid = buf.first()? >> 7 == 0;
737
738    if is_short_dcid {
739        buf.get(1..1 + MAX_CONN_ID_LEN).map(ConnectionId::from_ref)
740    } else {
741        None
742    }
743}
744
745/// Converts an [`Instant`] to a [`SystemTime`], based on the current delta
746/// between both clocks.
747fn instant_to_system(ts: Instant) -> SystemTime {
748    let now = Instant::now();
749    let system_now = SystemTime::now();
750    if let Some(delta) = now.checked_duration_since(ts) {
751        return system_now - delta;
752    }
753
754    let delta = ts.checked_duration_since(now).expect("now < ts");
755    system_now + delta
756}
757
758/// Determine if we should store the destination address for a packet, based on
759/// an address parsed from a
760/// [`ControlMessageOwned`](nix::sys::socket::ControlMessageOwned).
761///
762/// This is to prevent overriding the destination address if the packet was
763/// originally addressed to `local`, as that would cause us to incorrectly
764/// address packets when sending.
765///
766/// Returns the parsed address if it should be stored.
767#[cfg(target_os = "linux")]
768fn resolve_dst_addr(
769    local: &SocketAddr, parsed: &SocketAddr,
770) -> Option<SocketAddr> {
771    if local != parsed {
772        return Some(*parsed);
773    }
774
775    None
776}
777
778impl<Tx, Rx, M, I> Future for InboundPacketRouter<Tx, Rx, M, I>
779where
780    Tx: DatagramSocketSend + Send + Sync + 'static,
781    Rx: DatagramSocketRecv + Unpin,
782    M: Metrics,
783    I: InitialPacketHandler + Unpin,
784{
785    type Output = io::Result<()>;
786
787    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<io::Result<()>> {
788        loop {
789            // First, check whether the app stopped accepting connections.
790            if self.shutdown_tx.is_some() && self.accept_sink.is_closed() {
791                self.shutdown_tx = None;
792            }
793
794            // Second, check if all connections have shut down and we can exit.
795            if self.shutdown_tx.is_none() &&
796                self.shutdown_rx.poll_recv(cx).is_ready()
797            {
798                return Poll::Ready(Ok(()));
799            }
800
801            // Third, run the generic `InitialPacketHandler` update.
802            if let Err(error) = self.incoming_packet_handler.update(cx) {
803                // An error here is so rare that it's easier to spawn a separate
804                // task
805                let sender = self.accept_sink.clone();
806                spawn_with_killswitch(async move {
807                    let _ = sender.send(Err(error)).await;
808                });
809            }
810
811            // Fourth, update ConnectionMap before receiving packets. This ensures
812            // our SCID destinations are up-to-date (as of this moment).
813            // If this returns pending, we have processed all available commands
814            // and are registered for a wakeup on the next command.
815            let _ = self.poll_conn_map_commands(cx);
816
817            // Finally, process up to `PACKET_RX_YIELD_AFTER` packet (batches) at
818            // once. If no more packets are available, we wait to be woken again.
819            for _ in 0..PACKET_RX_YIELD_AFTER {
820                ready!(self.poll_process_packet(cx));
821            }
822        }
823    }
824}
825
826/// Categorizes errors that are returned when handling packets which are not
827/// associated with an established connection. The purpose is to suppress
828/// logging of 'expected' errors (e.g. junk data sent to the UDP socket) to
829/// prevent DoS.
830fn initial_packet_error_type(
831    e: &io::Error,
832) -> labels::QuicInvalidInitialPacketError {
833    Some(e)
834        .filter(|e| e.kind() == io::ErrorKind::Other)
835        .and_then(io::Error::get_ref)
836        .and_then(|e| e.downcast_ref())
837        .map_or(
838            labels::QuicInvalidInitialPacketError::Unexpected,
839            Clone::clone,
840        )
841}
842
843/// An [`InitialPacketHandler`] handles unknown quic initials and processes
844/// them; generally accepting new connections (acting as a server), or
845/// establishing a connection to a server (acting as a client). An
846/// [`InboundPacketRouter`] holds an instance of this trait and routes
847/// [`Incoming`] packets to it when it receives initials.
848///
849/// The handler produces [`quiche::Connection`]s which are then turned into
850/// [`QuicConnection`](super::QuicConnection), IoWorker pair.
851pub trait InitialPacketHandler {
852    fn update(&mut self, _ctx: &mut Context<'_>) -> io::Result<()> {
853        Ok(())
854    }
855
856    fn handle_initials(
857        &mut self, incoming: Incoming, hdr: Header<'static>,
858        quiche_config: &mut quiche::Config,
859    ) -> io::Result<Option<NewConnection>>;
860}
861
862/// A [`NewConnection`] describes a new [`quiche::Connection`] that can be
863/// driven by an io worker.
864pub struct NewConnection {
865    /// See [`QuicConnectionParams::quiche_conn`].
866    conn: Box<QuicheConnection>,
867    pending_cid: Option<ConnectionId<'static>>,
868    initial_pkt: Option<Incoming>,
869    cid_generator: Option<SharedConnectionIdGenerator>,
870    /// When the handshake started. Should be called before [`quiche::accept`]
871    /// or [`quiche::connect`].
872    handshake_start_time: Instant,
873}
874
875// TODO: the router module is private so we can't move these to /tests
876// TODO: Rewrite tests to be Windows compatible
877#[cfg(all(test, unix))]
878mod tests {
879    use super::acceptor::ConnectionAcceptor;
880    use super::acceptor::ConnectionAcceptorConfig;
881    use super::*;
882
883    use crate::http3::settings::Http3Settings;
884    use crate::metrics::DefaultMetrics;
885    use crate::quic::connection::SimpleConnectionIdGenerator;
886    use crate::settings::Config;
887    use crate::settings::Hooks;
888    use crate::settings::QuicSettings;
889    use crate::settings::TlsCertificatePaths;
890    use crate::socket::SocketCapabilities;
891    use crate::ConnectionIdGenerator as _;
892    use crate::ConnectionParams;
893    use crate::ServerH3Driver;
894
895    use datagram_socket::MAX_DATAGRAM_SIZE;
896    use futures::FutureExt as _;
897    use h3i::actions::h3::Action;
898    use std::net::Ipv4Addr;
899    use std::sync::Arc;
900    use std::time::Duration;
901    use tokio::net::UdpSocket;
902    use tokio::time;
903
904    const TEST_CERT_FILE: &str = concat!(
905        env!("CARGO_MANIFEST_DIR"),
906        "/",
907        "../quiche/examples/cert.crt"
908    );
909    const TEST_KEY_FILE: &str = concat!(
910        env!("CARGO_MANIFEST_DIR"),
911        "/",
912        "../quiche/examples/cert.key"
913    );
914
915    fn test_connect(host_port: String) {
916        let h3i_config = h3i::config::Config::new()
917            .with_host_port("test.com".to_string())
918            .with_idle_timeout(2000)
919            .with_connect_to(host_port)
920            .verify_peer(false)
921            .build()
922            .unwrap();
923
924        let conn_close = h3i::quiche::ConnectionError {
925            is_app: true,
926            error_code: h3i::quiche::WireErrorCode::NoError as _,
927            reason: Vec::new(),
928        };
929        let actions = vec![Action::ConnectionClose { error: conn_close }];
930
931        let _ = h3i::client::sync_client::connect(h3i_config, actions, None);
932    }
933
934    #[tokio::test]
935    async fn test_timeout() {
936        // Configure a short idle timeout to speed up connection reclamation as
937        // quiche doesn't support time mocking
938        let quic_settings = QuicSettings {
939            max_idle_timeout: Some(Duration::from_millis(1)),
940            max_recv_udp_payload_size: MAX_DATAGRAM_SIZE,
941            max_send_udp_payload_size: MAX_DATAGRAM_SIZE,
942            ..Default::default()
943        };
944
945        let tls_cert_settings = TlsCertificatePaths {
946            cert: TEST_CERT_FILE,
947            private_key: TEST_KEY_FILE,
948            kind: crate::settings::CertificateKind::X509,
949        };
950
951        let params = ConnectionParams::new_server(
952            quic_settings,
953            tls_cert_settings,
954            Hooks::default(),
955        );
956        let config = Config::new(&params, SocketCapabilities::default()).unwrap();
957
958        let socket = UdpSocket::bind("127.0.0.1:0").await.unwrap();
959        let local_addr = socket.local_addr().unwrap();
960        let host_port = local_addr.to_string();
961        let socket_tx = Arc::new(socket);
962        let socket_rx = Arc::clone(&socket_tx);
963
964        let acceptor = ConnectionAcceptor::new(
965            ConnectionAcceptorConfig {
966                disable_client_ip_validation: config.disable_client_ip_validation,
967                qlog_dir: config.qlog_dir.clone(),
968                qlog_compression: config.qlog_compression,
969                keylog_file: config
970                    .keylog_file
971                    .as_ref()
972                    .and_then(|f| f.try_clone().ok()),
973                #[cfg(target_os = "linux")]
974                with_pktinfo: false,
975            },
976            Arc::clone(&socket_tx),
977            Default::default(),
978            Arc::new(SimpleConnectionIdGenerator),
979            DefaultMetrics,
980        );
981
982        let (socket_driver, mut incoming) = InboundPacketRouter::new(
983            config,
984            socket_tx,
985            socket_rx,
986            local_addr,
987            acceptor,
988            DefaultMetrics,
989        );
990        tokio::spawn(socket_driver);
991
992        // Start a request and drop it after connection establishment
993        std::thread::spawn(move || test_connect(host_port));
994
995        // Wait for a new connection
996        time::pause();
997
998        let (h3_driver, _) = ServerH3Driver::new(Http3Settings::default());
999        let conn = incoming.recv().await.unwrap().unwrap();
1000        let drop_check = conn.incoming_ev_sender.clone();
1001        let _conn = conn.start(h3_driver);
1002
1003        // Poll the incoming until the connection is dropped
1004        time::advance(Duration::new(30, 0)).await;
1005        time::resume();
1006
1007        // NOTE: this is a smoke test - in case of issues `notified()` future will
1008        // never resolve hanging the test.
1009        drop_check.closed().await;
1010    }
1011
1012    struct NoopDatagramSender;
1013    impl DatagramSocketSend for NoopDatagramSender {
1014        fn poll_send(
1015            &self, _cx: &mut Context, buf: &[u8],
1016        ) -> Poll<io::Result<usize>> {
1017            Poll::Ready(Ok(buf.len()))
1018        }
1019
1020        fn poll_send_to(
1021            &self, _cx: &mut Context, buf: &[u8], _addr: SocketAddr,
1022        ) -> Poll<io::Result<usize>> {
1023            Poll::Ready(Ok(buf.len()))
1024        }
1025    }
1026
1027    struct AlwaysReadyReceiver;
1028    impl DatagramSocketRecv for AlwaysReadyReceiver {
1029        fn poll_recv(
1030            &mut self, _cx: &mut Context, buf: &mut tokio::io::ReadBuf,
1031        ) -> Poll<io::Result<()>> {
1032            // Short header packet:
1033            // 1 byte descriptor + 20 byte DCID + 1 byte packet number + payload
1034            const DUMMY_QUIC_PACKET: &[u8] =
1035                b"\x40THIS_20_BYTE_CONN_ID\x06payload_payload_payload";
1036            buf.put_slice(DUMMY_QUIC_PACKET);
1037            Poll::Ready(Ok(()))
1038        }
1039    }
1040
1041    struct NoopInitialHandler;
1042    impl InitialPacketHandler for NoopInitialHandler {
1043        fn handle_initials(
1044            &mut self, _incoming: Incoming, _hdr: Header<'static>,
1045            _quiche_config: &mut quiche::Config,
1046        ) -> io::Result<Option<NewConnection>> {
1047            Ok(None)
1048        }
1049    }
1050
1051    #[test]
1052    fn test_poll_packet_always_ready() {
1053        let tls_cert_settings = TlsCertificatePaths {
1054            cert: TEST_CERT_FILE,
1055            private_key: TEST_KEY_FILE,
1056            kind: crate::settings::CertificateKind::X509,
1057        };
1058        let params = ConnectionParams::new_server(
1059            QuicSettings::default(),
1060            tls_cert_settings,
1061            Hooks::default(),
1062        );
1063
1064        let config = Config::new(&params, SocketCapabilities::default()).unwrap();
1065        let local_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 0);
1066
1067        let (mut ipr, accept_stream) = InboundPacketRouter::new(
1068            config,
1069            Arc::new(NoopDatagramSender),
1070            AlwaysReadyReceiver,
1071            local_addr,
1072            NoopInitialHandler,
1073            DefaultMetrics,
1074        );
1075        let conn_map_cmd_tx = ipr.conn_map_cmd_tx.clone();
1076
1077        // Keep polling the IPR in a busy loop until it resolves
1078        let (ipr_notifier, ipr_done) = std::sync::mpsc::sync_channel::<()>(0);
1079        let ipr = std::thread::spawn(move || {
1080            let mut cx = Context::from_waker(std::task::Waker::noop());
1081            while ipr.poll_unpin(&mut cx).is_pending() {
1082                std::thread::sleep(Duration::from_millis(10));
1083            }
1084            drop(ipr_notifier);
1085            ipr
1086        });
1087
1088        // Fill the `conn_map_cmd` channel with some messages to process
1089        for _ in 0..20 {
1090            let random_cid = SimpleConnectionIdGenerator.new_connection_id();
1091            conn_map_cmd_tx
1092                .send(ConnectionMapCommand::UnmapCid(random_cid))
1093                .unwrap();
1094        }
1095        // Give the IPR some time to process the ConnectionMapCommands
1096        std::thread::sleep(Duration::from_secs(1));
1097
1098        // Shut the IPR down by dropping the accept_stream receiver. We wait for
1099        // up to 10 seconds for IPR::poll to resolve. If it doesn't, it's not
1100        // checking the shutdown condition regularly.
1101        drop(accept_stream);
1102        let ipr_done_res = ipr_done.recv_timeout(Duration::from_secs(10));
1103        assert_eq!(
1104            ipr_done_res,
1105            Err(std::sync::mpsc::RecvTimeoutError::Disconnected)
1106        );
1107
1108        // Check that the ConnectionMapCommands we added above were actually
1109        // processed
1110        let ipr = ipr.join().unwrap();
1111        assert!(ipr.conn_map_cmd_rx.is_empty());
1112    }
1113}