Skip to main content

tokio_quiche/quic/
dscp.rs

1// Copyright (C) 2026, 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 met:
6//
7//     * Redistributions of source code must retain the above copyright notice,
8//       this list of conditions and the following disclaimer.
9//     * Redistributions in binary form must reproduce the above copyright
10//       notice, this list of conditions and the following disclaimer in the
11//       documentation and/or other materials provided with the distribution.
12//
13// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
17// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
23// POSSIBILITY OF SUCH DAMAGE.
24
25use std::sync::atomic::AtomicU8;
26use std::sync::atomic::Ordering;
27use std::sync::Arc;
28
29const UNMARKED: u8 = 64;
30
31/// A six-bit Differentiated Services Code Point for outgoing UDP packets.
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub struct Dscp(u8);
34
35impl Dscp {
36    /// Creates a DSCP value, rejecting values outside the six-bit range.
37    pub const fn new(value: u8) -> Option<Self> {
38        if value < UNMARKED {
39            Some(Self(value))
40        } else {
41            None
42        }
43    }
44
45    /// Returns the six-bit DSCP value.
46    pub const fn value(self) -> u8 {
47        self.0
48    }
49
50    #[cfg(any(all(target_os = "linux", not(feature = "fuzzing")), test))]
51    pub(crate) const fn tos(self) -> u8 {
52        self.0 << 2
53    }
54}
55
56/// Per-connection DSCP shared with the QUIC I/O worker.
57///
58/// Clone this handle before starting the handshake and call [`Self::set`] once
59/// the connection's final marking is known. `Dscp::new(0)` explicitly
60/// clears an earlier marking even when the shared socket has a nonzero default;
61/// `None` omits the per-packet control message.
62#[derive(Clone, Debug)]
63pub struct DscpHandle(Arc<AtomicU8>);
64
65impl DscpHandle {
66    pub(crate) fn new() -> Self {
67        Self(Arc::new(AtomicU8::new(UNMARKED)))
68    }
69
70    /// Changes the DSCP used for subsequent packets on this connection.
71    pub fn set(&self, dscp: Option<Dscp>) {
72        // Relaxed ordering is enough; the byte synchronizes no other data.
73        self.0
74            .store(dscp.map_or(UNMARKED, Dscp::value), Ordering::Relaxed);
75    }
76
77    /// Returns the currently selected marking, or `None` if disabled.
78    pub fn get(&self) -> Option<Dscp> {
79        Dscp::new(self.0.load(Ordering::Relaxed))
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn dscp_is_six_bits() {
89        assert_eq!(Dscp::new(0).map(Dscp::tos), Some(0));
90        assert_eq!(Dscp::new(34).map(Dscp::tos), Some(136));
91        assert_eq!(Dscp::new(63).map(Dscp::tos), Some(252));
92        assert_eq!(Dscp::new(64), None);
93        assert_eq!(Dscp::new(255), None);
94    }
95
96    #[test]
97    fn handles_share_only_their_own_connection() {
98        let first = DscpHandle::new();
99        let cloned = first.clone();
100        let second = DscpHandle::new();
101
102        assert_eq!(first.get(), None);
103        assert_eq!(second.get(), None);
104        second.set(Dscp::new(1));
105
106        cloned.set(Dscp::new(28));
107        assert_eq!(first.get(), Dscp::new(28));
108        assert_eq!(second.get(), Dscp::new(1));
109
110        cloned.set(Dscp::new(0));
111        assert_eq!(first.get(), Dscp::new(0));
112        cloned.set(None);
113        assert_eq!(first.get(), None);
114        assert_eq!(second.get(), Dscp::new(1));
115    }
116}