etherparse/net/
ipv6_ext_slice_iter.rs

1use crate::*;
2use core::slice::from_raw_parts;
3
4/// Allows iterating over the IPv6 extension headers present in an [Ipv6ExtensionsSlice].
5#[derive(Clone, Debug, Eq, PartialEq)]
6pub struct Ipv6ExtensionSliceIter<'a> {
7    pub(crate) next_header: IpNumber,
8    pub(crate) rest: &'a [u8],
9}
10
11impl Default for Ipv6ExtensionSliceIter<'_> {
12    fn default() -> Self {
13        Ipv6ExtensionSliceIter {
14            // don't use 0 as this is the reserved value
15            // for the hop by hop header
16            next_header: IpNumber::IPV6_NO_NEXT_HEADER,
17            rest: &[],
18        }
19    }
20}
21
22impl<'a> Iterator for Ipv6ExtensionSliceIter<'a> {
23    type Item = Ipv6ExtensionSlice<'a>;
24
25    fn next(&mut self) -> Option<Ipv6ExtensionSlice<'a>> {
26        use ip_number::*;
27        use Ipv6ExtensionSlice::*;
28
29        match self.next_header {
30            // Note on the unsafe calls:
31            //
32            // As the slice contents & length were previously checked by
33            // Ipv6ExtensionsSlice::from_slice the content does not have to be
34            // rechecked.
35            IPV6_HOP_BY_HOP => unsafe {
36                let slice = Ipv6RawExtHeaderSlice::from_slice_unchecked(self.rest);
37                let len = slice.slice().len();
38                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
39                self.next_header = slice.next_header();
40                Some(HopByHop(slice))
41            },
42            IPV6_ROUTE => unsafe {
43                let slice = Ipv6RawExtHeaderSlice::from_slice_unchecked(self.rest);
44                let len = slice.slice().len();
45                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
46                self.next_header = slice.next_header();
47                Some(Routing(slice))
48            },
49            IPV6_DEST_OPTIONS => unsafe {
50                let slice = Ipv6RawExtHeaderSlice::from_slice_unchecked(self.rest);
51                let len = slice.slice().len();
52                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
53                self.next_header = slice.next_header();
54                Some(DestinationOptions(slice))
55            },
56            IPV6_FRAG => unsafe {
57                let slice = Ipv6FragmentHeaderSlice::from_slice_unchecked(self.rest);
58                let len = slice.slice().len();
59                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
60                self.next_header = slice.next_header();
61
62                Some(Fragment(slice))
63            },
64            AUTH => unsafe {
65                let slice = IpAuthHeaderSlice::from_slice_unchecked(self.rest);
66                let len = slice.slice().len();
67                self.rest = from_raw_parts(self.rest.as_ptr().add(len), self.rest.len() - len);
68                self.next_header = slice.next_header();
69                Some(Authentication(slice))
70            },
71            // done parsing, the next header is not a known/supported header extension
72            _ => None,
73        }
74    }
75}
76
77#[cfg(test)]
78mod test {
79    use super::ipv6_exts_test_helpers::*;
80    use super::*;
81    use crate::ip_number::*;
82    use crate::test_gens::*;
83    use alloc::borrow::ToOwned;
84    use proptest::prelude::*;
85
86    #[test]
87    fn into_iter() {
88        let a: Ipv6ExtensionsSlice = Default::default();
89        let mut iter = a.into_iter();
90        assert_eq!(None, iter.next());
91    }
92
93    proptest! {
94        #[test]
95        fn next(
96            header_size in any::<u8>(),
97            post_header in ip_number_any()
98                .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(),
99                    |v| !EXTENSION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x)
100                )
101        ) {
102            /// Run a test with the given ip numbers
103            fn run_test(ip_numbers: &[IpNumber], header_sizes: &[u8]) {
104                // setup test payload
105                let e = ExtensionTestPayload::new(
106                    ip_numbers,
107                    header_sizes
108                );
109
110                // a hop by hop header that is not at the start triggers an error
111                if false == e.ip_numbers[1..].iter().any(|&x| x == IPV6_HOP_BY_HOP) {
112                    // normal read
113                    let (header, _, _) = Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap();
114                    let mut iter = header.into_iter();
115                    let mut slice = e.slice();
116
117                    // go through all expected headers
118                    for i in 0..e.ip_numbers.len() - 1 {
119                        use Ipv6ExtensionSlice::*;
120
121                        // iterate and check all results
122                        let next = iter.next().unwrap();
123                        match e.ip_numbers[i] {
124                            IPV6_HOP_BY_HOP => {
125                                let header = Ipv6RawExtHeaderSlice::from_slice(slice).unwrap();
126                                assert_eq!(next, HopByHop(header.clone()));
127                                slice = &slice[header.slice().len()..];
128                            },
129                            IPV6_ROUTE => {
130                                let header = Ipv6RawExtHeaderSlice::from_slice(slice).unwrap();
131                                assert_eq!(next, Routing(header.clone()));
132                                slice = &slice[header.slice().len()..];
133                            },
134                            IPV6_DEST_OPTIONS => {
135                                let header = Ipv6RawExtHeaderSlice::from_slice(slice).unwrap();
136                                assert_eq!(next, DestinationOptions(header.clone()));
137                                slice = &slice[header.slice().len()..];
138                            }
139                            IPV6_FRAG => {
140                                let header = Ipv6FragmentHeaderSlice::from_slice(slice).unwrap();
141                                assert_eq!(next, Fragment(header.clone()));
142                                slice = &slice[header.slice().len()..];
143                            },
144                            AUTH => {
145                                let header = IpAuthHeaderSlice::from_slice(slice).unwrap();
146                                assert_eq!(next, Authentication(header.clone()));
147                                slice = &slice[header.slice().len()..];
148                            },
149                            _ => unreachable!()
150                        }
151                    }
152
153                    // expect that all headers have been visited
154                    assert_eq!(None, iter.next());
155                }
156            }
157
158            // test the parsing of different extension header combinations
159            for first_header in &EXTENSION_KNOWN_IP_NUMBERS {
160
161                // single header parsing
162                run_test(
163                    &[*first_header, post_header],
164                    &[header_size],
165                );
166
167                for second_header in &EXTENSION_KNOWN_IP_NUMBERS {
168
169                    // double header parsing
170                    run_test(
171                        &[*first_header, *second_header, post_header],
172                        &[header_size],
173                    );
174
175                    for third_header in &EXTENSION_KNOWN_IP_NUMBERS {
176                        // tripple header parsing
177                        run_test(
178                            &[*first_header, *second_header, *third_header, post_header],
179                            &[header_size],
180                        );
181                    }
182                }
183            }
184        }
185    }
186
187    #[test]
188    fn debug() {
189        use alloc::format;
190
191        let a: Ipv6ExtensionSliceIter = Default::default();
192        assert_eq!(
193            format!(
194                "Ipv6ExtensionSliceIter {{ next_header: {:?}, rest: [] }}",
195                IpNumber(59)
196            ),
197            format!("{:?}", a)
198        );
199    }
200
201    #[test]
202    fn clone_eq() {
203        let a: Ipv6ExtensionSliceIter = Default::default();
204        assert_eq!(a.clone(), a);
205    }
206
207    #[test]
208    fn default() {
209        let mut a: Ipv6ExtensionSliceIter = Default::default();
210        assert_eq!(None, a.next());
211    }
212}