Skip to main content

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