etherparse/net/
ipv4_options.rs

1use core::borrow::{Borrow, BorrowMut};
2
3/// Options present in an [`crate::Ipv4Header`].
4///
5/// IPv4 header options can only have a length that
6/// is a multiple of 4 bytes (meaning 4, 8, 12, ...) and
7/// a maximum length of 40 bytes (40 bytes the maximum length is
8/// limited by maximum value of the "intra header length" field
9/// in the IPv4 header).
10///
11/// # Examples
12///
13/// ```
14/// use etherparse::Ipv4Options;
15///
16/// {
17///     // static sized arrays of size 4,8,... 40 can directly be converted
18///     let options: Ipv4Options = [1,2,3,4].into();
19///     assert_eq!(&options[..], &[1,2,3,4]);
20/// }
21///
22/// {
23///     // slices can also be "try_from" converted
24///     let some_data = vec![1,2,3,4,5,6,7,8];
25///     let options: Ipv4Options = (&some_data[..]).try_into().unwrap();
26///     assert_eq!(options.as_slice(), &[1,2,3,4,5,6,7,8]);
27/// }
28/// {
29///     // only slices with a length that is multiple of 4 and a maximum value of 40
30///     // can be converted, otherwise you will get an error
31///     use etherparse::err::ipv4::BadOptionsLen;
32///
33///     let result = Ipv4Options::try_from(&[1,2,3][..]);
34///     assert_eq!(result, Err(BadOptionsLen { bad_len: 3 }));
35/// }
36/// ```
37#[derive(Clone)]
38pub struct Ipv4Options {
39    pub(crate) len: u8,
40    pub(crate) buf: [u8; 40],
41}
42
43impl Ipv4Options {
44    /// Maximum length of the IPv4 options in bytes.
45    pub const MAX_LEN: u8 = 40;
46
47    /// Setup an empty options array.
48    #[inline]
49    pub fn new() -> Ipv4Options {
50        Ipv4Options {
51            len: 0,
52            buf: [0; 40],
53        }
54    }
55
56    /// Returns the slice containing the data of the options.
57    #[inline]
58    pub fn as_slice(&self) -> &[u8] {
59        unsafe { core::slice::from_raw_parts(self.buf.as_ptr(), self.len.into()) }
60    }
61
62    /// Returns a mutable slice containing the data of the options.
63    #[inline]
64    pub fn as_mut_slice(&mut self) -> &mut [u8] {
65        unsafe { core::slice::from_raw_parts_mut(self.buf.as_mut_ptr(), self.len.into()) }
66    }
67
68    /// Returns the length of the options in bytes.
69    #[inline]
70    pub fn len(&self) -> usize {
71        usize::from(self.len)
72    }
73
74    /// Returns the length of the options in bytes.
75    #[inline]
76    pub fn len_u8(&self) -> u8 {
77        self.len
78    }
79
80    /// Returns if the length of the options is zero.
81    #[inline]
82    pub fn is_empty(&self) -> bool {
83        self.len == 0
84    }
85}
86
87impl TryFrom<&[u8]> for Ipv4Options {
88    type Error = crate::err::ipv4::BadOptionsLen;
89
90    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
91        if value.len() <= 40 && value.len() % 4 == 0 {
92            let mut result = Ipv4Options {
93                len: value.len() as u8,
94                buf: [0; 40],
95            };
96            unsafe {
97                // SAFETY: Safe as value.len() <= 40 and the result buffer size is 40.
98                core::ptr::copy_nonoverlapping(
99                    value.as_ptr(),
100                    result.buf.as_mut_ptr(),
101                    value.len(),
102                );
103            }
104            Ok(result)
105        } else {
106            Err(Self::Error {
107                bad_len: value.len(),
108            })
109        }
110    }
111}
112
113impl Default for Ipv4Options {
114    #[inline]
115    fn default() -> Self {
116        Self {
117            len: 0,
118            buf: [0; 40],
119        }
120    }
121}
122
123impl core::fmt::Debug for Ipv4Options {
124    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
125        self.as_slice().fmt(f)
126    }
127}
128
129impl PartialEq for Ipv4Options {
130    fn eq(&self, other: &Self) -> bool {
131        self.as_slice() == other.as_slice()
132    }
133}
134impl Eq for Ipv4Options {}
135
136impl core::hash::Hash for Ipv4Options {
137    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
138        self.as_slice().hash(state);
139    }
140}
141
142impl core::cmp::PartialOrd for Ipv4Options {
143    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
144        Some(self.as_slice().cmp(other.as_slice()))
145    }
146}
147
148impl core::cmp::Ord for Ipv4Options {
149    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
150        self.as_slice().cmp(other.as_slice())
151    }
152}
153
154impl From<[u8; 0]> for Ipv4Options {
155    #[inline]
156    fn from(_: [u8; 0]) -> Self {
157        Ipv4Options {
158            len: 0,
159            buf: [0; 40],
160        }
161    }
162}
163
164macro_rules! from_static_array {
165    ($x:expr) => {
166        impl From<[u8; $x]> for Ipv4Options {
167            #[inline]
168            fn from(values: [u8; $x]) -> Self {
169                let mut result = Ipv4Options {
170                    len: $x,
171                    buf: [0; 40],
172                };
173                let r = result.buf.as_mut_ptr() as *mut [u8; $x];
174                unsafe {
175                    *r = values;
176                }
177                result
178            }
179        }
180    };
181}
182
183from_static_array!(4);
184from_static_array!(8);
185from_static_array!(12);
186from_static_array!(16);
187from_static_array!(20);
188from_static_array!(24);
189from_static_array!(28);
190from_static_array!(32);
191from_static_array!(36);
192
193impl From<[u8; 40]> for Ipv4Options {
194    fn from(values: [u8; 40]) -> Self {
195        Ipv4Options {
196            len: 40,
197            buf: values,
198        }
199    }
200}
201
202impl AsRef<Ipv4Options> for Ipv4Options {
203    fn as_ref(&self) -> &Ipv4Options {
204        self
205    }
206}
207
208impl AsRef<[u8]> for Ipv4Options {
209    fn as_ref(&self) -> &[u8] {
210        self.as_slice()
211    }
212}
213
214impl AsMut<Ipv4Options> for Ipv4Options {
215    fn as_mut(&mut self) -> &mut Ipv4Options {
216        self
217    }
218}
219
220impl AsMut<[u8]> for Ipv4Options {
221    fn as_mut(&mut self) -> &mut [u8] {
222        self.as_mut_slice()
223    }
224}
225
226impl Borrow<[u8]> for Ipv4Options {
227    fn borrow(&self) -> &[u8] {
228        self.as_slice()
229    }
230}
231
232impl BorrowMut<[u8]> for Ipv4Options {
233    fn borrow_mut(&mut self) -> &mut [u8] {
234        self.as_mut_slice()
235    }
236}
237
238impl core::ops::Deref for Ipv4Options {
239    type Target = [u8];
240
241    #[inline]
242    fn deref(&self) -> &[u8] {
243        self.as_slice()
244    }
245}
246
247impl core::ops::DerefMut for Ipv4Options {
248    #[inline]
249    fn deref_mut(&mut self) -> &mut [u8] {
250        self.as_mut_slice()
251    }
252}
253
254#[cfg(test)]
255mod tests {
256    use super::*;
257    use crate::test_gens::*;
258    use proptest::prelude::*;
259    use std::format;
260
261    #[test]
262    fn new() {
263        let actual = Ipv4Options::new();
264        assert_eq!(actual.len, 0);
265        assert_eq!(actual.buf, [0; 40]);
266    }
267
268    #[test]
269    fn is_empty() {
270        {
271            let actual = Ipv4Options::new();
272            assert!(actual.is_empty());
273        }
274        {
275            let actual: Ipv4Options = [1, 2, 3, 4].into();
276            assert_eq!(false, actual.is_empty());
277        }
278    }
279
280    #[test]
281    fn try_from() {
282        const DATA: [u8; 48] = [
283            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
284            25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
285            47, 48,
286        ];
287
288        // ok cases
289        for len_div_4 in 0usize..=10 {
290            let mut actual = Ipv4Options::try_from(&DATA[..len_div_4 * 4]).unwrap();
291            assert_eq!(actual.as_slice(), &DATA[..len_div_4 * 4]);
292            assert_eq!(actual.as_mut_slice(), &DATA[..len_div_4 * 4]);
293            assert_eq!(actual.len_u8(), (len_div_4 * 4) as u8);
294            assert_eq!(actual.len(), len_div_4 * 4);
295        }
296
297        // error cases
298        use crate::err::ipv4::BadOptionsLen;
299        for len in 0usize..48 {
300            if (len % 4 != 0) || len > 40 {
301                assert_eq!(
302                    Err(BadOptionsLen { bad_len: len }),
303                    Ipv4Options::try_from(&DATA[..len])
304                )
305            }
306        }
307    }
308
309    #[test]
310    fn default() {
311        let actual: Ipv4Options = Default::default();
312        assert_eq!(actual.len, 0);
313        assert_eq!(actual.buf, [0; 40]);
314    }
315
316    proptest! {
317        #[test]
318        fn clone_dbg(options in ipv4_options_any()) {
319            assert_eq!(
320                format!("{:?}", options),
321                format!("{:?}", options.clone().as_slice())
322            );
323        }
324    }
325
326    proptest! {
327        #[test]
328        fn eq_partial_eq(
329            a in ipv4_options_any(),
330            b in ipv4_options_any()
331        ) {
332            assert_eq!(a.eq(&b), a.as_slice().eq(b.as_slice()));
333            assert_eq!(a == b, a.as_slice() == b.as_slice());
334        }
335    }
336
337    proptest! {
338        #[test]
339        fn hash(
340            options in ipv4_options_any()
341        ) {
342            use std::collections::hash_map::DefaultHasher;
343            use core::hash::{Hash, Hasher};
344            let a = {
345                let mut hasher = DefaultHasher::new();
346                options.hash(&mut hasher);
347                hasher.finish()
348            };
349            let b = {
350                let mut hasher = DefaultHasher::new();
351                options.hash(&mut hasher);
352                hasher.finish()
353            };
354            assert_eq!(a, b);
355        }
356    }
357
358    proptest! {
359        #[test]
360        fn ord_partial_ord(
361            a in ipv4_options_any(),
362            b in ipv4_options_any()
363        ) {
364            assert_eq!(a.cmp(&b), a.as_slice().cmp(&b.as_slice()));
365            assert_eq!(a.partial_cmp(&b), a.as_slice().partial_cmp(&b.as_slice()));
366        }
367    }
368
369    #[test]
370    fn from_0_byte_array() {
371        let options: Ipv4Options = [].into();
372        assert_eq!(&options[..], &[]);
373    }
374
375    macro_rules! from_static_array_test {
376        ($func_name:ident, $x:expr) => {
377            #[test]
378            fn $func_name() {
379                {
380                    let options: Ipv4Options = [$x; $x].into();
381                    assert_eq!(&options[..], &[$x; $x]);
382                }
383                assert_eq!(&Ipv4Options::from([$x; $x])[..], &[$x; $x]);
384            }
385        };
386    }
387
388    from_static_array_test!(from_arr_4, 4);
389    from_static_array_test!(from_arr_8, 8);
390    from_static_array_test!(from_arr_12, 12);
391    from_static_array_test!(from_arr_16, 16);
392    from_static_array_test!(from_arr_20, 20);
393    from_static_array_test!(from_arr_24, 24);
394    from_static_array_test!(from_arr_28, 28);
395    from_static_array_test!(from_arr_32, 32);
396    from_static_array_test!(from_arr_36, 36);
397    from_static_array_test!(from_arr_40, 40);
398
399    proptest! {
400        #[test]
401        fn as_ref(options in ipv4_options_any()) {
402            // as object reference
403            {
404                let r: &Ipv4Options = options.as_ref();
405                assert_eq!(r, &options);
406            }
407            // as slice reference
408            {
409                let r: &[u8] = options.as_ref();
410                assert_eq!(r, options.as_slice());
411            }
412        }
413    }
414
415    proptest! {
416        #[test]
417        fn as_mut(options in ipv4_options_any()) {
418            // as object reference
419            {
420                let mut o = options.clone();
421                let r: &mut Ipv4Options = o.as_mut();
422                if r.len() > 0 {
423                    r[0] = 123;
424                    assert_eq!(123, o.as_slice()[0]);
425                }
426            }
427            // as slice reference
428            {
429                let mut o = options.clone();
430                let r: &mut [u8] = o.as_mut();
431                if r.len() > 0 {
432                    r[0] = 123;
433                    assert_eq!(123, o.as_slice()[0]);
434                }
435            }
436        }
437    }
438
439    proptest! {
440        #[test]
441        fn borrow(options in ipv4_options_any()) {
442            // as object reference
443            {
444                let r: &Ipv4Options = options.borrow();
445                assert_eq!(r, &options);
446            }
447            // as slice reference
448            {
449                let r: &[u8] = options.borrow();
450                assert_eq!(r, options.as_slice());
451            }
452        }
453    }
454
455    proptest! {
456        #[test]
457        fn borrow_mut(options in ipv4_options_any()) {
458            // as object reference
459            {
460                let mut o = options.clone();
461                let r: &mut Ipv4Options = o.borrow_mut();
462                if r.len() > 0 {
463                    r[0] = 123;
464                    assert_eq!(123, o.as_slice()[0]);
465                }
466            }
467            // as slice reference
468            {
469                let mut o = options.clone();
470                let r: &mut [u8] = o.borrow_mut();
471                if r.len() > 0 {
472                    r[0] = 123;
473                    assert_eq!(123, o.as_slice()[0]);
474                }
475            }
476        }
477    }
478
479    #[test]
480    fn deref() {
481        let options: Ipv4Options = [1, 2, 3, 4].into();
482        let s: &[u8] = &options;
483        assert_eq!(s, &[1, 2, 3, 4]);
484        assert_eq!(&options[..], &[1, 2, 3, 4]);
485    }
486
487    #[test]
488    fn deref_mut() {
489        let mut options: Ipv4Options = [1, 2, 3, 4].into();
490        let s: &mut [u8] = &mut options;
491        assert_eq!(s, &[1, 2, 3, 4]);
492    }
493}