// Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use std::fmt::{self, Debug, Formatter}; use enum_map::Enum; use strum::{EnumIter, FromRepr}; /// ECN (Explicit Congestion Notification) codepoints mapped to the /// lower 2 bits of the TOS field. /// #[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug, FromRepr, EnumIter)] #[repr(u8)] pub enum Ecn { #[default] /// Not-ECT, Not ECN-Capable Transport, RFC3168 NotEct = 0b00, /// ECT(1), ECN-Capable Transport(1), RFC8311 and RFC9331 Ect1 = 0b01, /// ECT(0), ECN-Capable Transport(0), RFC3168 Ect0 = 0b10, /// CE, Congestion Experienced, RFC3168 Ce = 0b11, } impl From for u8 { fn from(v: Ecn) -> Self { v as Self } } impl From for Ecn { fn from(v: u8) -> Self { Self::from_repr(v & 0b0000_0011).expect("all ECN values are covered") } } impl From for Ecn { fn from(v: Tos) -> Self { Self::from(u8::from(v)) } } impl Ecn { /// Return `true` for any marking: ECT(0), ECT(1), or CE. #[must_use] pub const fn is_marked(self) -> bool { matches!(self, Self::Ect0 | Self::Ect1 | Self::Ce) } /// Return `true` if the value is ECT(0) or ECT(1). #[must_use] pub const fn is_ect(self) -> bool { matches!(self, Self::Ect0 | Self::Ect1) } } /// Diffserv codepoints, mapped to the upper six bits of the TOS field. /// #[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug, FromRepr)] #[repr(u8)] pub enum Dscp { #[default] /// Class Selector 0, RFC2474 Cs0 = 0b0000_0000, /// Lower-Effort, RFC8622 Le = 0b0000_0100, Reserved2 = 0b0000_1000, Reserved3 = 0b0000_1100, Reserved4 = 0b0001_0000, Reserved5 = 0b0001_0100, Reserved6 = 0b0001_1000, Reserved7 = 0b0001_1100, /// Class Selector 1, RFC2474 Cs1 = 0b0010_0000, Reserved9 = 0b0010_0100, Af11 = 0b0010_1000, /// Assured Forwarding 12, RFC2597 Reserved11 = 0b0010_1100, /// Assured Forwarding 11, RFC2597 Af12 = 0b0011_0000, Reserved13 = 0b0011_0100, /// Assured Forwarding 13, RFC2597 Af13 = 0b0011_1000, Reserved15 = 0b0011_1100, /// Class Selector 2, RFC2474 Cs2 = 0b0100_0000, Reserved17 = 0b0100_0100, /// Assured Forwarding 21, RFC2597 Af21 = 0b0100_1000, Reserved19 = 0b0100_1100, /// Assured Forwarding 22, RFC2597 Af22 = 0b0101_0000, Reserved21 = 0b0101_0100, /// Assured Forwarding 23, RFC2597 Af23 = 0b0101_1000, Reserved23 = 0b0101_1100, /// Class Selector 3, RFC2474 Cs3 = 0b0110_0000, Reserved25 = 0b0110_0100, /// Assured Forwarding 31, RFC2597 Af31 = 0b0110_1000, Reserved27 = 0b0110_1100, /// Assured Forwarding 32, RFC2597 Af32 = 0b0111_0000, Reserved29 = 0b0111_0100, /// Assured Forwarding 33, RFC2597 Af33 = 0b0111_1000, Reserved31 = 0b0111_1100, /// Class Selector 4, RFC2474 Cs4 = 0b1000_0000, Reserved33 = 0b1000_0100, /// Assured Forwarding 41, RFC2597 Af41 = 0b1000_1000, Reserved35 = 0b1000_1100, /// Assured Forwarding 42, RFC2597 Af42 = 0b1001_0000, Reserved37 = 0b1001_0100, /// Assured Forwarding 43, RFC2597 Af43 = 0b1001_1000, Reserved39 = 0b1001_1100, /// Class Selector 5, RFC2474 Cs5 = 0b1010_0000, Reserved41 = 0b1010_0100, Reserved42 = 0b1010_1000, Reserved43 = 0b1010_1100, /// Capacity-Admitted Traffic, RFC5865 VoiceAdmit = 0b1011_0000, Reserved45 = 0b1011_0100, /// Expedited Forwarding, RFC3246 Ef = 0b1011_1000, Reserved47 = 0b1011_1100, /// Class Selector 6, RFC2474 Cs6 = 0b1100_0000, Reserved49 = 0b1100_0100, Reserved50 = 0b1100_1000, Reserved51 = 0b1100_1100, Reserved52 = 0b1101_0000, Reserved53 = 0b1101_0100, Reserved54 = 0b1101_1000, Reserved55 = 0b1101_1100, /// Class Selector 7, RFC2474 Cs7 = 0b1110_0000, Reserved57 = 0b1110_0100, Reserved58 = 0b1110_1000, Reserved59 = 0b1110_1100, Reserved60 = 0b1111_0000, Reserved61 = 0b1111_0100, Reserved62 = 0b1111_1000, Reserved63 = 0b1111_1100, } impl From for u8 { fn from(v: Dscp) -> Self { v as Self } } impl From for Dscp { fn from(v: u8) -> Self { Self::from_repr(v & 0b1111_1100).expect("all DCSP values are covered") } } impl From for Dscp { fn from(v: Tos) -> Self { Self::from(u8::from(v)) } } /// The type-of-service field in an IP packet. #[derive(Copy, Clone, PartialEq, Eq, Default)] pub struct Tos(u8); impl From for Tos { fn from(v: Ecn) -> Self { Self(u8::from(v)) } } impl From for Tos { fn from(v: Dscp) -> Self { Self(u8::from(v)) } } impl From<(Dscp, Ecn)> for Tos { fn from(v: (Dscp, Ecn)) -> Self { Self(u8::from(v.0) | u8::from(v.1)) } } impl From<(Ecn, Dscp)> for Tos { fn from(v: (Ecn, Dscp)) -> Self { Self(u8::from(v.0) | u8::from(v.1)) } } impl From for u8 { fn from(v: Tos) -> Self { v.0 } } impl From for Tos { fn from(v: u8) -> Self { Self(v) } } impl Debug for Tos { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_tuple("Tos") .field(&Dscp::from(*self)) .field(&Ecn::from(*self)) .finish() } } impl Tos { pub fn set_ecn(&mut self, ecn: Ecn) { self.0 = u8::from(Dscp::from(*self)) | u8::from(ecn); } pub fn set_dscp(&mut self, dscp: Dscp) { self.0 = u8::from(Ecn::from(*self)) | u8::from(dscp); } #[must_use] pub fn is_ecn_marked(self) -> bool { Ecn::from(self).is_marked() } } #[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] mod tests { use crate::{Dscp, Ecn, Tos}; #[test] fn ecn_into_u8() { assert_eq!(u8::from(Ecn::NotEct), 0b00); assert_eq!(u8::from(Ecn::Ect1), 0b01); assert_eq!(u8::from(Ecn::Ect0), 0b10); assert_eq!(u8::from(Ecn::Ce), 0b11); } #[test] fn u8_into_ecn() { assert_eq!(Ecn::from(0b00), Ecn::NotEct); assert_eq!(Ecn::from(0b01), Ecn::Ect1); assert_eq!(Ecn::from(0b10), Ecn::Ect0); assert_eq!(Ecn::from(0b11), Ecn::Ce); } #[test] fn u8_into_ecn_all() { for i in 0..=u8::MAX { _ = Ecn::from(i); } } #[test] fn dscp_into_u8() { assert_eq!(u8::from(Dscp::Cs0), 0b0000_0000); assert_eq!(u8::from(Dscp::Cs1), 0b0010_0000); assert_eq!(u8::from(Dscp::Cs2), 0b0100_0000); assert_eq!(u8::from(Dscp::Cs3), 0b0110_0000); assert_eq!(u8::from(Dscp::Cs4), 0b1000_0000); assert_eq!(u8::from(Dscp::Cs5), 0b1010_0000); assert_eq!(u8::from(Dscp::Cs6), 0b1100_0000); assert_eq!(u8::from(Dscp::Cs7), 0b1110_0000); assert_eq!(u8::from(Dscp::Af11), 0b0010_1000); assert_eq!(u8::from(Dscp::Af12), 0b0011_0000); assert_eq!(u8::from(Dscp::Af13), 0b0011_1000); assert_eq!(u8::from(Dscp::Af21), 0b0100_1000); assert_eq!(u8::from(Dscp::Af22), 0b0101_0000); assert_eq!(u8::from(Dscp::Af23), 0b0101_1000); assert_eq!(u8::from(Dscp::Af31), 0b0110_1000); assert_eq!(u8::from(Dscp::Af32), 0b0111_0000); assert_eq!(u8::from(Dscp::Af33), 0b0111_1000); assert_eq!(u8::from(Dscp::Af41), 0b1000_1000); assert_eq!(u8::from(Dscp::Af42), 0b1001_0000); assert_eq!(u8::from(Dscp::Af43), 0b1001_1000); assert_eq!(u8::from(Dscp::Ef), 0b1011_1000); assert_eq!(u8::from(Dscp::VoiceAdmit), 0b1011_0000); assert_eq!(u8::from(Dscp::Le), 0b0000_0100); } #[test] fn u8_into_dscp_all() { for i in 0..=u8::MAX { _ = Dscp::from(i); } } #[test] fn u8_into_dscp() { assert_eq!(Dscp::from(0b0000_0000), Dscp::Cs0); assert_eq!(Dscp::from(0b0010_0000), Dscp::Cs1); assert_eq!(Dscp::from(0b0100_0000), Dscp::Cs2); assert_eq!(Dscp::from(0b0110_0000), Dscp::Cs3); assert_eq!(Dscp::from(0b1000_0000), Dscp::Cs4); assert_eq!(Dscp::from(0b1010_0000), Dscp::Cs5); assert_eq!(Dscp::from(0b1100_0000), Dscp::Cs6); assert_eq!(Dscp::from(0b1110_0000), Dscp::Cs7); assert_eq!(Dscp::from(0b0010_1000), Dscp::Af11); assert_eq!(Dscp::from(0b0011_0000), Dscp::Af12); assert_eq!(Dscp::from(0b0011_1000), Dscp::Af13); assert_eq!(Dscp::from(0b0100_1000), Dscp::Af21); assert_eq!(Dscp::from(0b0101_0000), Dscp::Af22); assert_eq!(Dscp::from(0b0101_1000), Dscp::Af23); assert_eq!(Dscp::from(0b0110_1000), Dscp::Af31); assert_eq!(Dscp::from(0b0111_0000), Dscp::Af32); assert_eq!(Dscp::from(0b0111_1000), Dscp::Af33); assert_eq!(Dscp::from(0b1000_1000), Dscp::Af41); assert_eq!(Dscp::from(0b1001_0000), Dscp::Af42); assert_eq!(Dscp::from(0b1001_1000), Dscp::Af43); assert_eq!(Dscp::from(0b1011_1000), Dscp::Ef); assert_eq!(Dscp::from(0b1011_0000), Dscp::VoiceAdmit); assert_eq!(Dscp::from(0b0000_0100), Dscp::Le); } #[test] fn ecn_into_tos() { let ecn = Ecn::default(); let tos: Tos = ecn.into(); assert_eq!(u8::from(tos), ecn as u8); } #[test] fn dscp_into_tos() { let dscp = Dscp::default(); let tos_dscp: Tos = dscp.into(); assert_eq!(u8::from(tos_dscp), dscp as u8); } #[test] fn u8_to_tos() { let tos_u8 = 0x8b; let tos = Tos::from((Ecn::Ce, Dscp::Af41)); assert_eq!(tos_u8, u8::from(tos)); assert_eq!(Tos::from(tos_u8), tos); } #[test] fn tos_to_dscp() { let tos = Tos::from((Dscp::Af41, Ecn::NotEct)); let dscp = Dscp::from(tos); assert_eq!(dscp, Dscp::Af41); } #[test] fn tos_modify_ecn() { let mut tos: Tos = (Dscp::Af41, Ecn::NotEct).into(); tos.set_ecn(Ecn::Ce); assert_eq!(u8::from(tos), 0b1000_1011); } #[test] fn tos_modify_dscp() { let mut tos: Tos = (Dscp::Af41, Ecn::Ect1).into(); tos.set_dscp(Dscp::Le); assert_eq!(u8::from(tos), 0b0000_0101); } #[test] fn tos_is_ecn_marked() { assert!(Tos::from((Dscp::Af41, Ecn::Ce)).is_ecn_marked()); assert!(!Tos::from((Dscp::Af41, Ecn::NotEct)).is_ecn_marked()); } #[test] fn ecn_is_ecn_marked() { assert!(Ecn::Ce.is_marked()); assert!(!Ecn::NotEct.is_marked()); } #[test] fn ecn_is_ect() { assert!(Ecn::Ect0.is_ect()); assert!(Ecn::Ect1.is_ect()); assert!(!Ecn::Ce.is_ect()); assert!(!Ecn::NotEct.is_ect()); } #[test] fn dscp_into_tos_non_default() { let tos: Tos = Dscp::Af41.into(); assert_eq!(u8::from(tos), u8::from(Dscp::Af41)); } }