smoltcp/wire/
ipv6hbh.rs

1use super::{Error, Ipv6Option, Ipv6OptionRepr, Ipv6OptionsIterator, Result};
2use crate::config;
3use crate::wire::ipv6option::RouterAlert;
4use heapless::Vec;
5
6/// A read/write wrapper around an IPv6 Hop-by-Hop Header buffer.
7pub struct Header<T: AsRef<[u8]>> {
8    buffer: T,
9}
10
11impl<T: AsRef<[u8]>> Header<T> {
12    /// Create a raw octet buffer with an IPv6 Hop-by-Hop Header structure.
13    pub const fn new_unchecked(buffer: T) -> Self {
14        Header { buffer }
15    }
16
17    /// Shorthand for a combination of [new_unchecked] and [check_len].
18    ///
19    /// [new_unchecked]: #method.new_unchecked
20    /// [check_len]: #method.check_len
21    pub fn new_checked(buffer: T) -> Result<Self> {
22        let header = Self::new_unchecked(buffer);
23        header.check_len()?;
24        Ok(header)
25    }
26
27    /// Ensure that no accessor method will panic if called.
28    /// Returns `Err(Error)` if the buffer is too short.
29    ///
30    /// The result of this check is invalidated by calling [set_header_len].
31    ///
32    /// [set_header_len]: #method.set_header_len
33    pub fn check_len(&self) -> Result<()> {
34        if self.buffer.as_ref().is_empty() {
35            return Err(Error);
36        }
37
38        Ok(())
39    }
40
41    /// Consume the header, returning the underlying buffer.
42    pub fn into_inner(self) -> T {
43        self.buffer
44    }
45}
46
47impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> {
48    /// Return the options of the IPv6 Hop-by-Hop header.
49    pub fn options(&self) -> &'a [u8] {
50        self.buffer.as_ref()
51    }
52}
53
54impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Header<&'a mut T> {
55    /// Return a mutable pointer to the options of the IPv6 Hop-by-Hop header.
56    pub fn options_mut(&mut self) -> &mut [u8] {
57        self.buffer.as_mut()
58    }
59}
60
61/// A high-level representation of an IPv6 Hop-by-Hop Header.
62#[derive(Debug, PartialEq, Eq, Clone)]
63#[cfg_attr(feature = "defmt", derive(defmt::Format))]
64pub struct Repr<'a> {
65    pub options: Vec<Ipv6OptionRepr<'a>, { config::IPV6_HBH_MAX_OPTIONS }>,
66}
67
68impl<'a> Repr<'a> {
69    /// Parse an IPv6 Hop-by-Hop Header and return a high-level representation.
70    pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>>
71    where
72        T: AsRef<[u8]> + ?Sized,
73    {
74        header.check_len()?;
75
76        let mut options = Vec::new();
77
78        let iter = Ipv6OptionsIterator::new(header.options());
79
80        for option in iter {
81            let option = option?;
82
83            if let Err(e) = options.push(option) {
84                net_trace!("error when parsing hop-by-hop options: {}", e);
85                break;
86            }
87        }
88
89        Ok(Self { options })
90    }
91
92    /// Return the length, in bytes, of a header that will be emitted from this high-level
93    /// representation.
94    pub fn buffer_len(&self) -> usize {
95        self.options.iter().map(|o| o.buffer_len()).sum()
96    }
97
98    /// Emit a high-level representation into an IPv6 Hop-by-Hop Header.
99    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
100        let mut buffer = header.options_mut();
101
102        for opt in &self.options {
103            opt.emit(&mut Ipv6Option::new_unchecked(
104                &mut buffer[..opt.buffer_len()],
105            ));
106            buffer = &mut buffer[opt.buffer_len()..];
107        }
108    }
109
110    /// The hop-by-hop header containing a MLDv2 router alert option
111    pub fn mldv2_router_alert() -> Self {
112        let mut options = Vec::new();
113        options
114            .push(Ipv6OptionRepr::RouterAlert(
115                RouterAlert::MulticastListenerDiscovery,
116            ))
117            .unwrap();
118        Self { options }
119    }
120
121    /// Append a PadN option to the vector of hop-by-hop options
122    pub fn push_padn_option(&mut self, n: u8) {
123        self.options.push(Ipv6OptionRepr::PadN(n)).unwrap();
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use crate::wire::Error;
131
132    // A Hop-by-Hop Option header with a PadN option of option data length 4.
133    static REPR_PACKET_PAD4: [u8; 6] = [0x1, 0x4, 0x0, 0x0, 0x0, 0x0];
134
135    // A Hop-by-Hop Option header with a PadN option of option data length 12.
136    static REPR_PACKET_PAD12: [u8; 14] = [
137        0x1, 0x0C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
138    ];
139
140    #[test]
141    fn test_check_len() {
142        // zero byte buffer
143        assert_eq!(
144            Err(Error),
145            Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len()
146        );
147        // valid
148        assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
149        // valid
150        assert_eq!(
151            Ok(()),
152            Header::new_unchecked(&REPR_PACKET_PAD12).check_len()
153        );
154    }
155
156    #[test]
157    fn test_repr_parse_valid() {
158        let header = Header::new_unchecked(&REPR_PACKET_PAD4);
159        let repr = Repr::parse(&header).unwrap();
160
161        let mut options = Vec::new();
162        options.push(Ipv6OptionRepr::PadN(4)).unwrap();
163        assert_eq!(repr, Repr { options });
164
165        let header = Header::new_unchecked(&REPR_PACKET_PAD12);
166        let repr = Repr::parse(&header).unwrap();
167
168        let mut options = Vec::new();
169        options.push(Ipv6OptionRepr::PadN(12)).unwrap();
170        assert_eq!(repr, Repr { options });
171    }
172
173    #[test]
174    fn test_repr_emit() {
175        let mut options = Vec::new();
176        options.push(Ipv6OptionRepr::PadN(4)).unwrap();
177        let repr = Repr { options };
178
179        let mut bytes = [0u8; 6];
180        let mut header = Header::new_unchecked(&mut bytes);
181        repr.emit(&mut header);
182
183        assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]);
184
185        let mut options = Vec::new();
186        options.push(Ipv6OptionRepr::PadN(12)).unwrap();
187        let repr = Repr { options };
188
189        let mut bytes = [0u8; 14];
190        let mut header = Header::new_unchecked(&mut bytes);
191        repr.emit(&mut header);
192
193        assert_eq!(header.into_inner(), &REPR_PACKET_PAD12[..]);
194    }
195}