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}