1use super::{Error, Ipv6Option, Ipv6OptionRepr, Ipv6OptionsIterator, Result};
2use crate::config;
3use crate::wire::ipv6option::RouterAlert;
4use heapless::Vec;
5
6pub struct Header<T: AsRef<[u8]>> {
8 buffer: T,
9}
10
11impl<T: AsRef<[u8]>> Header<T> {
12 pub const fn new_unchecked(buffer: T) -> Self {
14 Header { buffer }
15 }
16
17 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 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 pub fn into_inner(self) -> T {
43 self.buffer
44 }
45}
46
47impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> {
48 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 pub fn options_mut(&mut self) -> &mut [u8] {
57 self.buffer.as_mut()
58 }
59}
60
61#[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 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 pub fn buffer_len(&self) -> usize {
95 self.options.iter().map(|o| o.buffer_len()).sum()
96 }
97
98 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 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 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 static REPR_PACKET_PAD4: [u8; 6] = [0x1, 0x4, 0x0, 0x0, 0x0, 0x0];
134
135 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 assert_eq!(
144 Err(Error),
145 Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len()
146 );
147 assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
149 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}