smoltcp/wire/
ipv6routing.rs

1use super::{Error, Result};
2use core::fmt;
3
4use crate::wire::{Ipv6Address as Address, Ipv6AddressExt};
5
6enum_with_unknown! {
7    /// IPv6 Extension Routing Header Routing Type
8    pub enum Type(u8) {
9        /// Source Route (DEPRECATED)
10        ///
11        /// See https://tools.ietf.org/html/rfc5095 for details.
12        Type0 = 0,
13        /// Nimrod (DEPRECATED 2009-05-06)
14        Nimrod = 1,
15        /// Type 2 Routing Header for Mobile IPv6
16        ///
17        /// See https://tools.ietf.org/html/rfc6275#section-6.4 for details.
18        Type2 = 2,
19        /// RPL Source Routing Header
20        ///
21        /// See https://tools.ietf.org/html/rfc6554 for details.
22        Rpl = 3,
23        /// RFC3692-style Experiment 1
24        ///
25        /// See https://tools.ietf.org/html/rfc4727 for details.
26        Experiment1 = 253,
27        /// RFC3692-style Experiment 2
28        ///
29        /// See https://tools.ietf.org/html/rfc4727 for details.
30        Experiment2 = 254,
31        /// Reserved for future use
32        Reserved = 252
33    }
34}
35
36impl fmt::Display for Type {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        match *self {
39            Type::Type0 => write!(f, "Type0"),
40            Type::Nimrod => write!(f, "Nimrod"),
41            Type::Type2 => write!(f, "Type2"),
42            Type::Rpl => write!(f, "Rpl"),
43            Type::Experiment1 => write!(f, "Experiment1"),
44            Type::Experiment2 => write!(f, "Experiment2"),
45            Type::Reserved => write!(f, "Reserved"),
46            Type::Unknown(id) => write!(f, "{id}"),
47        }
48    }
49}
50
51/// A read/write wrapper around an IPv6 Routing Header buffer.
52#[derive(Debug, PartialEq, Eq)]
53#[cfg_attr(feature = "defmt", derive(defmt::Format))]
54pub struct Header<T: AsRef<[u8]>> {
55    buffer: T,
56}
57
58// Format of the Routing Header
59//
60// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
61// |  Next Header  |  Hdr Ext Len  |  Routing Type | Segments Left |
62// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
63// |                                                               |
64// .                                                               .
65// .                       type-specific data                      .
66// .                                                               .
67// |                                                               |
68// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
69//
70//
71// See https://tools.ietf.org/html/rfc8200#section-4.4 for details.
72//
73// **NOTE**: The fields start counting after the header length field.
74mod field {
75    #![allow(non_snake_case)]
76
77    use crate::wire::field::*;
78
79    // Minimum size of the header.
80    pub const MIN_HEADER_SIZE: usize = 2;
81
82    // 8-bit identifier of a particular Routing header variant.
83    pub const TYPE: usize = 0;
84    // 8-bit unsigned integer. The number of route segments remaining.
85    pub const SEG_LEFT: usize = 1;
86
87    // The Type 2 Routing Header has the following format:
88    //
89    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
90    // |  Next Header  | Hdr Ext Len=2 | Routing Type=2|Segments Left=1|
91    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
92    // |                            Reserved                           |
93    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
94    // |                                                               |
95    // +                                                               +
96    // |                                                               |
97    // +                         Home Address                          +
98    // |                                                               |
99    // +                                                               +
100    // |                                                               |
101    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102
103    // 16-byte field containing the home address of the destination mobile node.
104    pub const HOME_ADDRESS: Field = 6..22;
105
106    // The RPL Source Routing Header has the following format:
107    //
108    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
109    // |  Next Header  |  Hdr Ext Len  | Routing Type  | Segments Left |
110    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
111    // | CmprI | CmprE |  Pad  |               Reserved                |
112    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
113    // |                                                               |
114    // .                                                               .
115    // .                        Addresses[1..n]                        .
116    // .                                                               .
117    // |                                                               |
118    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
119
120    // 8-bit field containing the CmprI and CmprE values.
121    pub const CMPR: usize = 2;
122    // 8-bit field containing the Pad value.
123    pub const PAD: usize = 3;
124    // Variable length field containing addresses
125    pub const ADDRESSES: usize = 6;
126}
127
128/// Core getter methods relevant to any routing type.
129impl<T: AsRef<[u8]>> Header<T> {
130    /// Create a raw octet buffer with an IPv6 Routing Header structure.
131    pub const fn new_unchecked(buffer: T) -> Header<T> {
132        Header { buffer }
133    }
134
135    /// Shorthand for a combination of [new_unchecked] and [check_len].
136    ///
137    /// [new_unchecked]: #method.new_unchecked
138    /// [check_len]: #method.check_len
139    pub fn new_checked(buffer: T) -> Result<Header<T>> {
140        let header = Self::new_unchecked(buffer);
141        header.check_len()?;
142        Ok(header)
143    }
144
145    /// Ensure that no accessor method will panic if called.
146    /// Returns `Err(Error)` if the buffer is too short.
147    ///
148    /// The result of this check is invalidated by calling [set_header_len].
149    ///
150    /// [set_header_len]: #method.set_header_len
151    pub fn check_len(&self) -> Result<()> {
152        let len = self.buffer.as_ref().len();
153        if len < field::MIN_HEADER_SIZE {
154            return Err(Error);
155        }
156
157        match self.routing_type() {
158            Type::Type2 if len < field::HOME_ADDRESS.end => return Err(Error),
159            Type::Rpl if len < field::ADDRESSES => return Err(Error),
160            _ => (),
161        }
162
163        Ok(())
164    }
165
166    /// Consume the header, returning the underlying buffer.
167    pub fn into_inner(self) -> T {
168        self.buffer
169    }
170
171    /// Return the routing type field.
172    #[inline]
173    pub fn routing_type(&self) -> Type {
174        let data = self.buffer.as_ref();
175        Type::from(data[field::TYPE])
176    }
177
178    /// Return the segments left field.
179    #[inline]
180    pub fn segments_left(&self) -> u8 {
181        let data = self.buffer.as_ref();
182        data[field::SEG_LEFT]
183    }
184}
185
186/// Getter methods for the Type 2 Routing Header routing type.
187impl<T: AsRef<[u8]>> Header<T> {
188    /// Return the IPv6 Home Address
189    ///
190    /// # Panics
191    /// This function may panic if this header is not the Type2 Routing Header routing type.
192    pub fn home_address(&self) -> Address {
193        let data = self.buffer.as_ref();
194        Address::from_bytes(&data[field::HOME_ADDRESS])
195    }
196}
197
198/// Getter methods for the RPL Source Routing Header routing type.
199impl<T: AsRef<[u8]>> Header<T> {
200    /// Return the number of prefix octets elided from addresses[1..n-1].
201    ///
202    /// # Panics
203    /// This function may panic if this header is not the RPL Source Routing Header routing type.
204    pub fn cmpr_i(&self) -> u8 {
205        let data = self.buffer.as_ref();
206        data[field::CMPR] >> 4
207    }
208
209    /// Return the number of prefix octets elided from the last address (`addresses[n]`).
210    ///
211    /// # Panics
212    /// This function may panic if this header is not the RPL Source Routing Header routing type.
213    pub fn cmpr_e(&self) -> u8 {
214        let data = self.buffer.as_ref();
215        data[field::CMPR] & 0xf
216    }
217
218    /// Return the number of octets used for padding after `addresses[n]`.
219    ///
220    /// # Panics
221    /// This function may panic if this header is not the RPL Source Routing Header routing type.
222    pub fn pad(&self) -> u8 {
223        let data = self.buffer.as_ref();
224        data[field::PAD] >> 4
225    }
226
227    /// Return the address vector in bytes
228    ///
229    /// # Panics
230    /// This function may panic if this header is not the RPL Source Routing Header routing type.
231    pub fn addresses(&self) -> &[u8] {
232        let data = self.buffer.as_ref();
233        &data[field::ADDRESSES..]
234    }
235}
236
237/// Core setter methods relevant to any routing type.
238impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
239    /// Set the routing type.
240    #[inline]
241    pub fn set_routing_type(&mut self, value: Type) {
242        let data = self.buffer.as_mut();
243        data[field::TYPE] = value.into();
244    }
245
246    /// Set the segments left field.
247    #[inline]
248    pub fn set_segments_left(&mut self, value: u8) {
249        let data = self.buffer.as_mut();
250        data[field::SEG_LEFT] = value;
251    }
252
253    /// Initialize reserved fields to 0.
254    ///
255    /// # Panics
256    /// This function may panic if the routing type is not set.
257    #[inline]
258    pub fn clear_reserved(&mut self) {
259        let routing_type = self.routing_type();
260        let data = self.buffer.as_mut();
261
262        match routing_type {
263            Type::Type2 => {
264                data[2] = 0;
265                data[3] = 0;
266                data[4] = 0;
267                data[5] = 0;
268            }
269            Type::Rpl => {
270                // Retain the higher order 4 bits of the padding field
271                data[field::PAD] &= 0xF0;
272                data[4] = 0;
273                data[5] = 0;
274            }
275
276            _ => panic!("Unrecognized routing type when clearing reserved fields."),
277        }
278    }
279}
280
281/// Setter methods for the RPL Source Routing Header routing type.
282impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
283    /// Set the Ipv6 Home Address
284    ///
285    /// # Panics
286    /// This function may panic if this header is not the Type 2 Routing Header routing type.
287    pub fn set_home_address(&mut self, value: Address) {
288        let data = self.buffer.as_mut();
289        data[field::HOME_ADDRESS].copy_from_slice(&value.octets());
290    }
291}
292
293/// Setter methods for the RPL Source Routing Header routing type.
294impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
295    /// Set the number of prefix octets elided from addresses[1..n-1].
296    ///
297    /// # Panics
298    /// This function may panic if this header is not the RPL Source Routing Header routing type.
299    pub fn set_cmpr_i(&mut self, value: u8) {
300        let data = self.buffer.as_mut();
301        let raw = (value << 4) | (data[field::CMPR] & 0xF);
302        data[field::CMPR] = raw;
303    }
304
305    /// Set the number of prefix octets elided from the last address (`addresses[n]`).
306    ///
307    /// # Panics
308    /// This function may panic if this header is not the RPL Source Routing Header routing type.
309    pub fn set_cmpr_e(&mut self, value: u8) {
310        let data = self.buffer.as_mut();
311        let raw = (value & 0xF) | (data[field::CMPR] & 0xF0);
312        data[field::CMPR] = raw;
313    }
314
315    /// Set the number of octets used for padding after `addresses[n]`.
316    ///
317    /// # Panics
318    /// This function may panic if this header is not the RPL Source Routing Header routing type.
319    pub fn set_pad(&mut self, value: u8) {
320        let data = self.buffer.as_mut();
321        data[field::PAD] = value << 4;
322    }
323
324    /// Set address data
325    ///
326    /// # Panics
327    /// This function may panic if this header is not the RPL Source Routing Header routing type.
328    pub fn set_addresses(&mut self, value: &[u8]) {
329        let data = self.buffer.as_mut();
330        let addresses = &mut data[field::ADDRESSES..];
331        addresses.copy_from_slice(value);
332    }
333}
334
335impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
336    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337        match Repr::parse(self) {
338            Ok(repr) => write!(f, "{repr}"),
339            Err(err) => {
340                write!(f, "IPv6 Routing ({err})")?;
341                Ok(())
342            }
343        }
344    }
345}
346
347/// A high-level representation of an IPv6 Routing Header.
348#[derive(Debug, PartialEq, Eq, Clone, Copy)]
349#[cfg_attr(feature = "defmt", derive(defmt::Format))]
350#[non_exhaustive]
351pub enum Repr<'a> {
352    Type2 {
353        /// Number of route segments remaining.
354        segments_left: u8,
355        /// The home address of the destination mobile node.
356        home_address: Address,
357    },
358    Rpl {
359        /// Number of route segments remaining.
360        segments_left: u8,
361        /// Number of prefix octets from each segment, except the last segment, that are elided.
362        cmpr_i: u8,
363        /// Number of prefix octets from the last segment that are elided.
364        cmpr_e: u8,
365        /// Number of octets that are used for padding after `address[n]` at the end of the
366        /// RPL Source Route Header.
367        pad: u8,
368        /// Vector of addresses, numbered 1 to `n`.
369        addresses: &'a [u8],
370    },
371}
372
373impl<'a> Repr<'a> {
374    /// Parse an IPv6 Routing Header and return a high-level representation.
375    pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>>
376    where
377        T: AsRef<[u8]> + ?Sized,
378    {
379        header.check_len()?;
380        match header.routing_type() {
381            Type::Type2 => Ok(Repr::Type2 {
382                segments_left: header.segments_left(),
383                home_address: header.home_address(),
384            }),
385            Type::Rpl => Ok(Repr::Rpl {
386                segments_left: header.segments_left(),
387                cmpr_i: header.cmpr_i(),
388                cmpr_e: header.cmpr_e(),
389                pad: header.pad(),
390                addresses: header.addresses(),
391            }),
392
393            _ => Err(Error),
394        }
395    }
396
397    /// Return the length, in bytes, of a header that will be emitted from this high-level
398    /// representation.
399    pub const fn buffer_len(&self) -> usize {
400        match self {
401            // Routing Type + Segments Left + Reserved + Home Address
402            Repr::Type2 { home_address, .. } => 2 + 4 + home_address.octets().len(),
403            Repr::Rpl { addresses, .. } => 2 + 4 + addresses.len(),
404        }
405    }
406
407    /// Emit a high-level representation into an IPv6 Routing Header.
408    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
409        match *self {
410            Repr::Type2 {
411                segments_left,
412                home_address,
413            } => {
414                header.set_routing_type(Type::Type2);
415                header.set_segments_left(segments_left);
416                header.clear_reserved();
417                header.set_home_address(home_address);
418            }
419            Repr::Rpl {
420                segments_left,
421                cmpr_i,
422                cmpr_e,
423                pad,
424                addresses,
425            } => {
426                header.set_routing_type(Type::Rpl);
427                header.set_segments_left(segments_left);
428                header.set_cmpr_i(cmpr_i);
429                header.set_cmpr_e(cmpr_e);
430                header.set_pad(pad);
431                header.clear_reserved();
432                header.set_addresses(addresses);
433            }
434        }
435    }
436}
437
438impl<'a> fmt::Display for Repr<'a> {
439    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440        match *self {
441            Repr::Type2 {
442                segments_left,
443                home_address,
444            } => {
445                write!(
446                    f,
447                    "IPv6 Routing type={} seg_left={} home_address={}",
448                    Type::Type2,
449                    segments_left,
450                    home_address
451                )
452            }
453            Repr::Rpl {
454                segments_left,
455                cmpr_i,
456                cmpr_e,
457                pad,
458                ..
459            } => {
460                write!(
461                    f,
462                    "IPv6 Routing type={} seg_left={} cmpr_i={} cmpr_e={} pad={}",
463                    Type::Rpl,
464                    segments_left,
465                    cmpr_i,
466                    cmpr_e,
467                    pad
468                )
469            }
470        }
471    }
472}
473
474#[cfg(test)]
475mod test {
476    use super::*;
477
478    // A Type 2 Routing Header
479    static BYTES_TYPE2: [u8; 22] = [
480        0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
481        0x0, 0x0, 0x0, 0x1,
482    ];
483
484    // A representation of a Type 2 Routing header
485    static REPR_TYPE2: Repr = Repr::Type2 {
486        segments_left: 1,
487        home_address: Address::LOCALHOST,
488    };
489
490    // A Source Routing Header with full IPv6 addresses in bytes
491    static BYTES_SRH_FULL: [u8; 38] = [
492        0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
493        0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
494        0x3, 0x1,
495    ];
496
497    // A representation of a Source Routing Header with full IPv6 addresses
498    static REPR_SRH_FULL: Repr = Repr::Rpl {
499        segments_left: 2,
500        cmpr_i: 0,
501        cmpr_e: 0,
502        pad: 0,
503        addresses: &[
504            0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd,
505            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1,
506        ],
507    };
508
509    // A Source Routing Header with elided IPv6 addresses in bytes
510    static BYTES_SRH_ELIDED: [u8; 14] = [
511        0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
512    ];
513
514    // A representation of a Source Routing Header with elided IPv6 addresses
515    static REPR_SRH_ELIDED: Repr = Repr::Rpl {
516        segments_left: 2,
517        cmpr_i: 15,
518        cmpr_e: 14,
519        pad: 5,
520        addresses: &[0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0],
521    };
522
523    #[test]
524    fn test_check_len() {
525        // less than min header size
526        assert_eq!(
527            Err(Error),
528            Header::new_unchecked(&BYTES_TYPE2[..3]).check_len()
529        );
530        assert_eq!(
531            Err(Error),
532            Header::new_unchecked(&BYTES_SRH_FULL[..3]).check_len()
533        );
534        assert_eq!(
535            Err(Error),
536            Header::new_unchecked(&BYTES_SRH_ELIDED[..3]).check_len()
537        );
538        // valid
539        assert_eq!(Ok(()), Header::new_unchecked(&BYTES_TYPE2[..]).check_len());
540        assert_eq!(
541            Ok(()),
542            Header::new_unchecked(&BYTES_SRH_FULL[..]).check_len()
543        );
544        assert_eq!(
545            Ok(()),
546            Header::new_unchecked(&BYTES_SRH_ELIDED[..]).check_len()
547        );
548    }
549
550    #[test]
551    fn test_header_deconstruct() {
552        let header = Header::new_unchecked(&BYTES_TYPE2[..]);
553        assert_eq!(header.routing_type(), Type::Type2);
554        assert_eq!(header.segments_left(), 1);
555        assert_eq!(header.home_address(), Address::LOCALHOST);
556
557        let header = Header::new_unchecked(&BYTES_SRH_FULL[..]);
558        assert_eq!(header.routing_type(), Type::Rpl);
559        assert_eq!(header.segments_left(), 2);
560        assert_eq!(header.addresses(), &BYTES_SRH_FULL[6..]);
561
562        let header = Header::new_unchecked(&BYTES_SRH_ELIDED[..]);
563        assert_eq!(header.routing_type(), Type::Rpl);
564        assert_eq!(header.segments_left(), 2);
565        assert_eq!(header.addresses(), &BYTES_SRH_ELIDED[6..]);
566    }
567
568    #[test]
569    fn test_repr_parse_valid() {
570        let header = Header::new_checked(&BYTES_TYPE2[..]).unwrap();
571        let repr = Repr::parse(&header).unwrap();
572        assert_eq!(repr, REPR_TYPE2);
573
574        let header = Header::new_checked(&BYTES_SRH_FULL[..]).unwrap();
575        let repr = Repr::parse(&header).unwrap();
576        assert_eq!(repr, REPR_SRH_FULL);
577
578        let header = Header::new_checked(&BYTES_SRH_ELIDED[..]).unwrap();
579        let repr = Repr::parse(&header).unwrap();
580        assert_eq!(repr, REPR_SRH_ELIDED);
581    }
582
583    #[test]
584    fn test_repr_emit() {
585        let mut bytes = [0xFFu8; 22];
586        let mut header = Header::new_unchecked(&mut bytes[..]);
587        REPR_TYPE2.emit(&mut header);
588        assert_eq!(header.into_inner(), &BYTES_TYPE2[..]);
589
590        let mut bytes = [0xFFu8; 38];
591        let mut header = Header::new_unchecked(&mut bytes[..]);
592        REPR_SRH_FULL.emit(&mut header);
593        assert_eq!(header.into_inner(), &BYTES_SRH_FULL[..]);
594
595        let mut bytes = [0xFFu8; 14];
596        let mut header = Header::new_unchecked(&mut bytes[..]);
597        REPR_SRH_ELIDED.emit(&mut header);
598        assert_eq!(header.into_inner(), &BYTES_SRH_ELIDED[..]);
599    }
600
601    #[test]
602    fn test_buffer_len() {
603        assert_eq!(REPR_TYPE2.buffer_len(), 22);
604        assert_eq!(REPR_SRH_FULL.buffer_len(), 38);
605        assert_eq!(REPR_SRH_ELIDED.buffer_len(), 14);
606    }
607}