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}