etherparse/net/
ip_dscp.rs

1use crate::err::ValueTooBigError;
2
3/// Deprecated, use [`IpDscp`] instead.
4#[deprecated(since = "0.18.0", note = "Use `IpDscp` instead of `Ipv4Dscp`")]
5pub type Ipv4Dscp = IpDscp;
6
7/// 6 bit unsigned integer containing the "Differentiated Services
8/// Code Point" (present in the [`crate::Ipv4Header`] and in the
9/// in [`crate::Ipv6Header`] as part of `traffic_class`).
10///
11/// Established in
12/// [RFC-2472](https://datatracker.ietf.org/doc/html/rfc2474) and defined/maintained in the
13/// [IANA dscp-registry](https://www.iana.org/assignments/dscp-registry/dscp-registry.xhtml)
14#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
15pub struct IpDscp(u8);
16
17impl IpDscp {
18    /// IpDscp with value 0.
19    pub const ZERO: IpDscp = IpDscp(0);
20
21    /// Maximum value of an IPv4 header DSCP.
22    pub const MAX_U8: u8 = 0b0011_1111;
23
24    /// Maximum value of DSCP field (6 bits).
25    pub const MAX: IpDscp = IpDscp(Self::MAX_U8);
26
27    /// Class Selector 0 (Pool 1) [RFC-2474](https://datatracker.ietf.org/doc/html/rfc2474)
28    pub const CS0: IpDscp = IpDscp(0b00_0000);
29
30    /// Class Selector 1 (Pool 1) [RFC-2474](https://datatracker.ietf.org/doc/html/rfc2474)
31    pub const CS1: IpDscp = IpDscp(0b00_1000);
32
33    /// Class Selector 2 (Pool 1) [RFC-2474](https://datatracker.ietf.org/doc/html/rfc2474)
34    pub const CS2: IpDscp = IpDscp(0b01_0000);
35
36    /// Class Selector 3 (Pool 1) [RFC-2474](https://datatracker.ietf.org/doc/html/rfc2474)
37    pub const CS3: IpDscp = IpDscp(0b01_1000);
38
39    /// Class Selector 4 (Pool 1) [RFC-2474](https://datatracker.ietf.org/doc/html/rfc2474)
40    pub const CS4: IpDscp = IpDscp(0b10_0000);
41
42    /// Class Selector 5 (Pool 1) [RFC-2474](https://datatracker.ietf.org/doc/html/rfc2474)
43    pub const CS5: IpDscp = IpDscp(0b10_1000);
44
45    /// Class Selector 6 (Pool 1) [RFC-2474](https://datatracker.ietf.org/doc/html/rfc2474)
46    pub const CS6: IpDscp = IpDscp(0b11_0000);
47
48    /// Class Selector 7 (Pool 1) [RFC-2474](https://datatracker.ietf.org/doc/html/rfc2474)
49    pub const CS7: IpDscp = IpDscp(0b11_1000);
50
51    /// Assured Forwarding PHB Group 11 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
52    pub const AF11: IpDscp = IpDscp(0b00_1010);
53
54    /// Assured Forwarding PHB Group 12 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
55    pub const AF12: IpDscp = IpDscp(0b00_1100);
56
57    /// Assured Forwarding PHB Group 13 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
58    pub const AF13: IpDscp = IpDscp(0b00_1110);
59
60    /// Assured Forwarding PHB Group 21 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
61    pub const AF21: IpDscp = IpDscp(0b01_0010);
62
63    /// Assured Forwarding PHB Group 22 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
64    pub const AF22: IpDscp = IpDscp(0b01_0100);
65
66    /// Assured Forwarding PHB Group 23 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
67    pub const AF23: IpDscp = IpDscp(0b01_0110);
68
69    /// Assured Forwarding PHB Group 31 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
70    pub const AF31: IpDscp = IpDscp(0b01_1010);
71
72    /// Assured Forwarding PHB Group 32 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
73    pub const AF32: IpDscp = IpDscp(0b01_1100);
74
75    /// Assured Forwarding PHB Group 11 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
76    pub const AF33: IpDscp = IpDscp(0b01_1110);
77
78    /// Assured Forwarding PHB Group 11 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
79    pub const AF41: IpDscp = IpDscp(0b10_0010);
80
81    /// Assured Forwarding PHB Group 11 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
82    pub const AF42: IpDscp = IpDscp(0b10_0100);
83
84    /// Assured Forwarding PHB Group 11 (Pool 1) [RFC-2597](https://datatracker.ietf.org/doc/html/rfc2597)
85    pub const AF43: IpDscp = IpDscp(0b10_0110);
86
87    /// Expedited Forwarding (Pool 1) [RFC-3246](https://datatracker.ietf.org/doc/html/rfc3246)
88    pub const EF: IpDscp = IpDscp(0b10_1110);
89
90    /// Voice admit (Pool 1) [RFC-5865](https://datatracker.ietf.org/doc/html/rfc5865)
91    pub const VOICE_ADMIT: IpDscp = IpDscp(0b10_1100);
92
93    /// Lower Effort PHB (Pool 3) [RFC-8622](https://datatracker.ietf.org/doc/html/rfc8622)
94    pub const LOWER_EFFORT: IpDscp = IpDscp(0b00_0001);
95
96    /// Tries to create an [`IpDscp`] and checks that the passed value
97    /// is smaller or equal than [`IpDscp::MAX_U8`] (6 bit unsigned integer).
98    ///
99    /// In case the passed value is bigger then what can be represented in an 6 bit
100    /// integer an error is returned. Otherwise an `Ok` containing the [`IpDscp`].
101    ///
102    /// ```
103    /// use etherparse::IpDscp;
104    ///
105    /// let dscp = IpDscp::try_new(32).unwrap();
106    /// assert_eq!(dscp.value(), 32);
107    ///
108    /// // if a number that can not be represented in an 6 bit integer
109    /// // gets passed in an error is returned
110    /// use etherparse::err::{ValueTooBigError, ValueType};
111    /// assert_eq!(
112    ///     IpDscp::try_new(IpDscp::MAX_U8 + 1),
113    ///     Err(ValueTooBigError{
114    ///         actual: IpDscp::MAX_U8 + 1,
115    ///         max_allowed: IpDscp::MAX_U8,
116    ///         value_type: ValueType::IpDscp,
117    ///     })
118    /// );
119    /// ```
120    #[inline]
121    pub const fn try_new(value: u8) -> Result<IpDscp, ValueTooBigError<u8>> {
122        use crate::err::ValueType;
123        if value <= IpDscp::MAX_U8 {
124            Ok(IpDscp(value))
125        } else {
126            Err(ValueTooBigError {
127                actual: value,
128                max_allowed: IpDscp::MAX_U8,
129                value_type: ValueType::IpDscp,
130            })
131        }
132    }
133
134    /// Creates an [`IpDscp`] without checking that the value
135    /// is smaller or equal than [`IpDscp::MAX_U8`] (6 bit unsigned integer).
136    /// The caller must guarantee that `value <= IpDscp::MAX_U8`.
137    ///
138    /// # Safety
139    ///
140    /// `value` must be smaller or equal than [`IpDscp::MAX_U8`]
141    /// otherwise the behavior of functions or data structures relying
142    /// on this pre-requirement is undefined.
143    #[inline]
144    pub const unsafe fn new_unchecked(value: u8) -> IpDscp {
145        debug_assert!(value <= IpDscp::MAX_U8);
146        IpDscp(value)
147    }
148
149    /// Returns the underlying unsigned 6 bit value as an `u8` value.
150    #[inline]
151    pub const fn value(self) -> u8 {
152        self.0
153    }
154}
155
156impl core::fmt::Display for IpDscp {
157    #[inline]
158    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159        self.0.fmt(f)
160    }
161}
162
163impl From<IpDscp> for u8 {
164    #[inline]
165    fn from(value: IpDscp) -> Self {
166        value.0
167    }
168}
169
170impl TryFrom<u8> for IpDscp {
171    type Error = ValueTooBigError<u8>;
172
173    #[inline]
174    fn try_from(value: u8) -> Result<Self, Self::Error> {
175        use crate::err::ValueType;
176        if value <= IpDscp::MAX_U8 {
177            Ok(IpDscp(value))
178        } else {
179            Err(Self::Error {
180                actual: value,
181                max_allowed: IpDscp::MAX_U8,
182                value_type: ValueType::IpDscp,
183            })
184        }
185    }
186}
187
188#[cfg(test)]
189mod test {
190    use super::*;
191    use core::hash::{Hash, Hasher};
192    use proptest::prelude::*;
193    use std::format;
194
195    #[test]
196    fn derived_traits() {
197        // copy & clone
198        {
199            let a = IpDscp(32);
200            let b = a;
201            assert_eq!(a, b);
202            assert_eq!(a.clone(), a);
203        }
204
205        // default
206        {
207            let actual: IpDscp = Default::default();
208            assert_eq!(actual.value(), 0);
209        }
210
211        // debug
212        {
213            let a = IpDscp(32);
214            assert_eq!(format!("{:?}", a), format!("IpDscp(32)"));
215        }
216
217        // ord & partial ord
218        {
219            use core::cmp::Ordering;
220            let a = IpDscp(32);
221            let b = a;
222            assert_eq!(a.cmp(&b), Ordering::Equal);
223            assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
224        }
225
226        // hash
227        {
228            use std::collections::hash_map::DefaultHasher;
229            let a = {
230                let mut hasher = DefaultHasher::new();
231                IpDscp(64).hash(&mut hasher);
232                hasher.finish()
233            };
234            let b = {
235                let mut hasher = DefaultHasher::new();
236                IpDscp(64).hash(&mut hasher);
237                hasher.finish()
238            };
239            assert_eq!(a, b);
240        }
241    }
242
243    proptest! {
244        #[test]
245        fn try_new(
246            valid_value in 0..=0b0011_1111u8,
247            invalid_value in 0b0100_0000u8..=u8::MAX
248        ) {
249            use crate::err::{ValueType, ValueTooBigError};
250            assert_eq!(
251                valid_value,
252                IpDscp::try_new(valid_value).unwrap().value()
253            );
254            assert_eq!(
255                IpDscp::try_new(invalid_value).unwrap_err(),
256                ValueTooBigError{
257                    actual: invalid_value,
258                    max_allowed: 0b0011_1111,
259                    value_type:  ValueType::IpDscp
260                }
261            );
262        }
263    }
264
265    proptest! {
266        #[test]
267        fn try_from(
268            valid_value in 0..=0b0011_1111u8,
269            invalid_value in 0b0100_0000u8..=u8::MAX
270        ) {
271            use crate::err::{ValueType, ValueTooBigError};
272            // try_into
273            {
274                let actual: IpDscp = valid_value.try_into().unwrap();
275                assert_eq!(actual.value(), valid_value);
276
277                let err: Result<IpDscp, ValueTooBigError<u8>> = invalid_value.try_into();
278                assert_eq!(
279                    err.unwrap_err(),
280                    ValueTooBigError{
281                        actual: invalid_value,
282                        max_allowed: 0b0011_1111,
283                        value_type:  ValueType::IpDscp
284                    }
285                );
286            }
287            // try_from
288            {
289                assert_eq!(
290                    IpDscp::try_from(valid_value).unwrap().value(),
291                    valid_value
292                );
293
294                assert_eq!(
295                    IpDscp::try_from(invalid_value).unwrap_err(),
296                    ValueTooBigError{
297                        actual: invalid_value,
298                        max_allowed: 0b0011_1111,
299                        value_type:  ValueType::IpDscp
300                    }
301                );
302            }
303        }
304    }
305
306    proptest! {
307        #[test]
308        fn new_unchecked(valid_value in 0..=0b0011_1111u8) {
309            assert_eq!(
310                valid_value,
311                unsafe {
312                    IpDscp::new_unchecked(valid_value).value()
313                }
314            );
315        }
316    }
317
318    proptest! {
319        #[test]
320        fn fmt(valid_value in 0..=0b0011_1111u8) {
321            assert_eq!(format!("{}", IpDscp(valid_value)), format!("{}", valid_value));
322        }
323    }
324
325    proptest! {
326        #[test]
327        fn from(valid_value in 0..=0b0011_1111u8,) {
328            let dscp = IpDscp::try_new(valid_value).unwrap();
329            let actual: u8 = dscp.into();
330            assert_eq!(actual, valid_value);
331        }
332    }
333}