smoltcp/wire/sixlowpan/
mod.rs

1//! Implementation of [RFC 6282] which specifies a compression format for IPv6 datagrams over
2//! IEEE802.154-based networks.
3//!
4//! [RFC 6282]: https://datatracker.ietf.org/doc/html/rfc6282
5
6use super::{Error, Result};
7use crate::wire::ieee802154::Address as LlAddress;
8use crate::wire::ipv6;
9use crate::wire::ipv6::AddressExt;
10use crate::wire::IpProtocol;
11
12pub mod frag;
13pub mod iphc;
14pub mod nhc;
15
16const ADDRESS_CONTEXT_LENGTH: usize = 8;
17
18#[derive(Debug, PartialEq, Eq, Clone, Copy)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub struct AddressContext(pub [u8; ADDRESS_CONTEXT_LENGTH]);
21
22/// The representation of an unresolved address. 6LoWPAN compression of IPv6 addresses can be with
23/// and without context information. The decompression with context information is not yet
24/// implemented.
25#[derive(Debug, PartialEq, Eq, Clone, Copy)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27pub enum UnresolvedAddress<'a> {
28    WithoutContext(AddressMode<'a>),
29    WithContext((usize, AddressMode<'a>)),
30    Reserved,
31}
32
33#[derive(Debug, PartialEq, Eq, Clone, Copy)]
34#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35pub enum AddressMode<'a> {
36    /// The full address is carried in-line.
37    FullInline(&'a [u8]),
38    /// The first 64-bits of the address are elided. The value of those bits
39    /// is the link-local prefix padded with zeros. The remaining 64 bits are
40    /// carried in-line.
41    InLine64bits(&'a [u8]),
42    /// The first 112 bits of the address are elided. The value of the first
43    /// 64 bits is the link-local prefix padded with zeros. The following 64 bits
44    /// are 0000:00ff:fe00:XXXX, where XXXX are the 16 bits carried in-line.
45    InLine16bits(&'a [u8]),
46    /// The address is fully elided. The first 64 bits of the address are
47    /// the link-local prefix padded with zeros. The remaining 64 bits are
48    /// computed from the encapsulating header (e.g., 802.15.4 or IPv6 source address)
49    /// as specified in Section 3.2.2.
50    FullyElided,
51    /// The address takes the form ffXX::00XX:XXXX:XXXX
52    Multicast48bits(&'a [u8]),
53    /// The address takes the form ffXX::00XX:XXXX.
54    Multicast32bits(&'a [u8]),
55    /// The address takes the form ff02::00XX.
56    Multicast8bits(&'a [u8]),
57    /// The unspecified address.
58    Unspecified,
59    NotSupported,
60}
61
62const LINK_LOCAL_PREFIX: [u8; 2] = [0xfe, 0x80];
63const EUI64_MIDDLE_VALUE: [u8; 2] = [0xff, 0xfe];
64
65impl<'a> UnresolvedAddress<'a> {
66    pub fn resolve(
67        self,
68        ll_address: Option<LlAddress>,
69        addr_context: &[AddressContext],
70    ) -> Result<ipv6::Address> {
71        let mut bytes = [0; 16];
72
73        let copy_context = |index: usize, bytes: &mut [u8]| -> Result<()> {
74            if index >= addr_context.len() {
75                return Err(Error);
76            }
77
78            let context = addr_context[index];
79            bytes[..ADDRESS_CONTEXT_LENGTH].copy_from_slice(&context.0);
80
81            Ok(())
82        };
83
84        match self {
85            UnresolvedAddress::WithoutContext(mode) => match mode {
86                AddressMode::FullInline(addr) => Ok(ipv6::Address::from_bytes(addr)),
87                AddressMode::InLine64bits(inline) => {
88                    bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]);
89                    bytes[8..].copy_from_slice(inline);
90                    Ok(ipv6::Address::from_bytes(&bytes[..]))
91                }
92                AddressMode::InLine16bits(inline) => {
93                    bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]);
94                    bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]);
95                    bytes[14..].copy_from_slice(inline);
96                    Ok(ipv6::Address::from_bytes(&bytes[..]))
97                }
98                AddressMode::FullyElided => {
99                    bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]);
100                    match ll_address {
101                        Some(LlAddress::Short(ll)) => {
102                            bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]);
103                            bytes[14..].copy_from_slice(&ll);
104                        }
105                        Some(addr @ LlAddress::Extended(_)) => match addr.as_eui_64() {
106                            Some(addr) => bytes[8..].copy_from_slice(&addr),
107                            None => return Err(Error),
108                        },
109                        Some(LlAddress::Absent) => return Err(Error),
110                        None => return Err(Error),
111                    }
112                    Ok(ipv6::Address::from_bytes(&bytes[..]))
113                }
114                AddressMode::Multicast48bits(inline) => {
115                    bytes[0] = 0xff;
116                    bytes[1] = inline[0];
117                    bytes[11..].copy_from_slice(&inline[1..][..5]);
118                    Ok(ipv6::Address::from_bytes(&bytes[..]))
119                }
120                AddressMode::Multicast32bits(inline) => {
121                    bytes[0] = 0xff;
122                    bytes[1] = inline[0];
123                    bytes[13..].copy_from_slice(&inline[1..][..3]);
124                    Ok(ipv6::Address::from_bytes(&bytes[..]))
125                }
126                AddressMode::Multicast8bits(inline) => {
127                    bytes[0] = 0xff;
128                    bytes[1] = 0x02;
129                    bytes[15] = inline[0];
130                    Ok(ipv6::Address::from_bytes(&bytes[..]))
131                }
132                _ => Err(Error),
133            },
134            UnresolvedAddress::WithContext(mode) => match mode {
135                (_, AddressMode::Unspecified) => Ok(ipv6::Address::UNSPECIFIED),
136                (index, AddressMode::InLine64bits(inline)) => {
137                    copy_context(index, &mut bytes[..])?;
138                    bytes[16 - inline.len()..].copy_from_slice(inline);
139                    Ok(ipv6::Address::from_bytes(&bytes[..]))
140                }
141                (index, AddressMode::InLine16bits(inline)) => {
142                    copy_context(index, &mut bytes[..])?;
143                    bytes[16 - inline.len()..].copy_from_slice(inline);
144                    Ok(ipv6::Address::from_bytes(&bytes[..]))
145                }
146                (index, AddressMode::FullyElided) => {
147                    match ll_address {
148                        Some(LlAddress::Short(ll)) => {
149                            bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]);
150                            bytes[14..].copy_from_slice(&ll);
151                        }
152                        Some(addr @ LlAddress::Extended(_)) => match addr.as_eui_64() {
153                            Some(addr) => bytes[8..].copy_from_slice(&addr),
154                            None => return Err(Error),
155                        },
156                        Some(LlAddress::Absent) => return Err(Error),
157                        None => return Err(Error),
158                    }
159
160                    copy_context(index, &mut bytes[..])?;
161
162                    Ok(ipv6::Address::from_bytes(&bytes[..]))
163                }
164                _ => Err(Error),
165            },
166            UnresolvedAddress::Reserved => Err(Error),
167        }
168    }
169}
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172#[cfg_attr(feature = "defmt", derive(defmt::Format))]
173pub enum SixlowpanPacket {
174    FragmentHeader,
175    IphcHeader,
176}
177
178const DISPATCH_FIRST_FRAGMENT_HEADER: u8 = 0b11000;
179const DISPATCH_FRAGMENT_HEADER: u8 = 0b11100;
180const DISPATCH_IPHC_HEADER: u8 = 0b011;
181const DISPATCH_UDP_HEADER: u8 = 0b11110;
182const DISPATCH_EXT_HEADER: u8 = 0b1110;
183
184impl SixlowpanPacket {
185    /// Returns the type of the 6LoWPAN header.
186    /// This can either be a fragment header or an IPHC header.
187    ///
188    /// # Errors
189    /// Returns `[Error::Unrecognized]` when neither the Fragment Header dispatch or the IPHC
190    /// dispatch is recognized.
191    pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result<Self> {
192        let raw = buffer.as_ref();
193
194        if raw.is_empty() {
195            return Err(Error);
196        }
197
198        if raw[0] >> 3 == DISPATCH_FIRST_FRAGMENT_HEADER || raw[0] >> 3 == DISPATCH_FRAGMENT_HEADER
199        {
200            Ok(Self::FragmentHeader)
201        } else if raw[0] >> 5 == DISPATCH_IPHC_HEADER {
202            Ok(Self::IphcHeader)
203        } else {
204            Err(Error)
205        }
206    }
207}
208
209#[derive(Debug, PartialEq, Eq, Clone, Copy)]
210pub enum NextHeader {
211    Compressed,
212    Uncompressed(IpProtocol),
213}
214
215impl core::fmt::Display for NextHeader {
216    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
217        match self {
218            NextHeader::Compressed => write!(f, "compressed"),
219            NextHeader::Uncompressed(protocol) => write!(f, "{protocol}"),
220        }
221    }
222}
223
224#[cfg(feature = "defmt")]
225impl defmt::Format for NextHeader {
226    fn format(&self, fmt: defmt::Formatter) {
227        match self {
228            NextHeader::Compressed => defmt::write!(fmt, "compressed"),
229            NextHeader::Uncompressed(protocol) => defmt::write!(fmt, "{}", protocol),
230        }
231    }
232}
233
234#[cfg(test)]
235mod test {
236    use super::*;
237
238    #[test]
239    fn sixlowpan_fragment_emit() {
240        let repr = frag::Repr::FirstFragment {
241            size: 0xff,
242            tag: 0xabcd,
243        };
244        let buffer = [0u8; 4];
245        let mut packet = frag::Packet::new_unchecked(buffer);
246
247        assert_eq!(repr.buffer_len(), 4);
248        repr.emit(&mut packet);
249
250        assert_eq!(packet.datagram_size(), 0xff);
251        assert_eq!(packet.datagram_tag(), 0xabcd);
252        assert_eq!(packet.into_inner(), [0xc0, 0xff, 0xab, 0xcd]);
253
254        let repr = frag::Repr::Fragment {
255            size: 0xff,
256            tag: 0xabcd,
257            offset: 0xcc,
258        };
259        let buffer = [0u8; 5];
260        let mut packet = frag::Packet::new_unchecked(buffer);
261
262        assert_eq!(repr.buffer_len(), 5);
263        repr.emit(&mut packet);
264
265        assert_eq!(packet.datagram_size(), 0xff);
266        assert_eq!(packet.datagram_tag(), 0xabcd);
267        assert_eq!(packet.into_inner(), [0xe0, 0xff, 0xab, 0xcd, 0xcc]);
268    }
269
270    #[test]
271    fn sixlowpan_three_fragments() {
272        use crate::wire::ieee802154::Frame as Ieee802154Frame;
273        use crate::wire::ieee802154::Repr as Ieee802154Repr;
274        use crate::wire::Ieee802154Address;
275
276        let key = frag::Key {
277            ll_src_addr: Ieee802154Address::Extended([50, 147, 130, 47, 40, 8, 62, 217]),
278            ll_dst_addr: Ieee802154Address::Extended([26, 11, 66, 66, 66, 66, 66, 66]),
279            datagram_size: 307,
280            datagram_tag: 63,
281        };
282
283        let frame1: &[u8] = &[
284            0x41, 0xcc, 0x92, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9,
285            0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xc1, 0x33, 0x00, 0x3f, 0x6e, 0x33, 0x02,
286            0x35, 0x3d, 0xf0, 0xd2, 0x5f, 0x1b, 0x39, 0xb4, 0x6b, 0x4c, 0x6f, 0x72, 0x65, 0x6d,
287            0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73,
288            0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65,
289            0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63,
290            0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74, 0x2e, 0x20, 0x41, 0x6c, 0x69, 0x71,
291            0x75, 0x61, 0x6d, 0x20, 0x64, 0x75, 0x69, 0x20, 0x6f, 0x64, 0x69, 0x6f, 0x2c, 0x20,
292            0x69, 0x61, 0x63, 0x75, 0x6c, 0x69, 0x73, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x72,
293        ];
294
295        let ieee802154_frame = Ieee802154Frame::new_checked(frame1).unwrap();
296        let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap();
297
298        let sixlowpan_frame =
299            SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap();
300
301        let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame {
302            frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap()
303        } else {
304            unreachable!()
305        };
306
307        assert_eq!(frag.datagram_size(), 307);
308        assert_eq!(frag.datagram_tag(), 0x003f);
309        assert_eq!(frag.datagram_offset(), 0);
310
311        assert_eq!(frag.get_key(&ieee802154_repr), key);
312
313        let frame2: &[u8] = &[
314            0x41, 0xcc, 0x93, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9,
315            0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xe1, 0x33, 0x00, 0x3f, 0x11, 0x75, 0x74,
316            0x72, 0x75, 0x6d, 0x20, 0x61, 0x74, 0x2c, 0x20, 0x74, 0x72, 0x69, 0x73, 0x74, 0x69,
317            0x71, 0x75, 0x65, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75, 0x6e, 0x63, 0x20, 0x65,
318            0x72, 0x61, 0x74, 0x20, 0x63, 0x75, 0x72, 0x61, 0x65, 0x2e, 0x20, 0x4c, 0x6f, 0x72,
319            0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72,
320            0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e,
321            0x73, 0x65, 0x63, 0x74, 0x65, 0x74, 0x75, 0x72, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69,
322            0x73, 0x63, 0x69, 0x6e, 0x67, 0x20, 0x65, 0x6c, 0x69, 0x74,
323        ];
324
325        let ieee802154_frame = Ieee802154Frame::new_checked(frame2).unwrap();
326        let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap();
327
328        let sixlowpan_frame =
329            SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap();
330
331        let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame {
332            frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap()
333        } else {
334            unreachable!()
335        };
336
337        assert_eq!(frag.datagram_size(), 307);
338        assert_eq!(frag.datagram_tag(), 0x003f);
339        assert_eq!(frag.datagram_offset(), 136 / 8);
340
341        assert_eq!(frag.get_key(&ieee802154_repr), key);
342
343        let frame3: &[u8] = &[
344            0x41, 0xcc, 0x94, 0xef, 0xbe, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x0b, 0x1a, 0xd9,
345            0x3e, 0x08, 0x28, 0x2f, 0x82, 0x93, 0x32, 0xe1, 0x33, 0x00, 0x3f, 0x1d, 0x2e, 0x20,
346            0x41, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x64, 0x75, 0x69, 0x20, 0x6f, 0x64,
347            0x69, 0x6f, 0x2c, 0x20, 0x69, 0x61, 0x63, 0x75, 0x6c, 0x69, 0x73, 0x20, 0x76, 0x65,
348            0x6c, 0x20, 0x72, 0x75, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x61, 0x74, 0x2c, 0x20, 0x74,
349            0x72, 0x69, 0x73, 0x74, 0x69, 0x71, 0x75, 0x65, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e,
350            0x75, 0x6e, 0x63, 0x20, 0x65, 0x72, 0x61, 0x74, 0x20, 0x63, 0x75, 0x72, 0x61, 0x65,
351            0x2e, 0x20, 0x0a,
352        ];
353
354        let ieee802154_frame = Ieee802154Frame::new_checked(frame3).unwrap();
355        let ieee802154_repr = Ieee802154Repr::parse(&ieee802154_frame).unwrap();
356
357        let sixlowpan_frame =
358            SixlowpanPacket::dispatch(ieee802154_frame.payload().unwrap()).unwrap();
359
360        let frag = if let SixlowpanPacket::FragmentHeader = sixlowpan_frame {
361            frag::Packet::new_checked(ieee802154_frame.payload().unwrap()).unwrap()
362        } else {
363            unreachable!()
364        };
365
366        assert_eq!(frag.datagram_size(), 307);
367        assert_eq!(frag.datagram_tag(), 0x003f);
368        assert_eq!(frag.datagram_offset(), 232 / 8);
369
370        assert_eq!(frag.get_key(&ieee802154_repr), key);
371    }
372}