|
@@ -1,7 +1,6 @@
|
|
|
use super::{Error, Result};
|
|
|
use core::fmt;
|
|
|
|
|
|
-use crate::wire::IpProtocol as Protocol;
|
|
|
use crate::wire::Ipv6Address as Address;
|
|
|
|
|
|
enum_with_unknown! {
|
|
@@ -70,31 +69,20 @@ pub struct Header<T: AsRef<[u8]>> {
|
|
|
//
|
|
|
//
|
|
|
// See https://tools.ietf.org/html/rfc8200#section-4.4 for details.
|
|
|
+//
|
|
|
+// **NOTE**: The fields start counting after the header length field.
|
|
|
mod field {
|
|
|
#![allow(non_snake_case)]
|
|
|
|
|
|
use crate::wire::field::*;
|
|
|
|
|
|
// Minimum size of the header.
|
|
|
- pub const MIN_HEADER_SIZE: usize = 4;
|
|
|
+ pub const MIN_HEADER_SIZE: usize = 2;
|
|
|
|
|
|
- // 8-bit identifier of the header immediately following this header.
|
|
|
- pub const NXT_HDR: usize = 0;
|
|
|
- // 8-bit unsigned integer. Length of the DATA field in 8-octet units,
|
|
|
- // not including the first 8 octets.
|
|
|
- pub const LENGTH: usize = 1;
|
|
|
// 8-bit identifier of a particular Routing header variant.
|
|
|
- pub const TYPE: usize = 2;
|
|
|
+ pub const TYPE: usize = 0;
|
|
|
// 8-bit unsigned integer. The number of route segments remaining.
|
|
|
- pub const SEG_LEFT: usize = 3;
|
|
|
- // Variable-length field. Routing-Type-specific data.
|
|
|
- //
|
|
|
- // Length of the header is in 8-octet units, not including the first 8 octets. The first four
|
|
|
- // octets are the next header type, the header length, routing type and segments left.
|
|
|
- pub const fn DATA(length_field: u8) -> Field {
|
|
|
- let bytes = length_field as usize * 8 + 8;
|
|
|
- 4..bytes
|
|
|
- }
|
|
|
+ pub const SEG_LEFT: usize = 1;
|
|
|
|
|
|
// The Type 2 Routing Header has the following format:
|
|
|
//
|
|
@@ -113,7 +101,7 @@ mod field {
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
|
// 16-byte field containing the home address of the destination mobile node.
|
|
|
- pub const HOME_ADDRESS: Field = 8..24;
|
|
|
+ pub const HOME_ADDRESS: Field = 6..22;
|
|
|
|
|
|
// The RPL Source Routing Header has the following format:
|
|
|
//
|
|
@@ -130,20 +118,17 @@ mod field {
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
|
|
|
// 8-bit field containing the CmprI and CmprE values.
|
|
|
- pub const CMPR: usize = 4;
|
|
|
+ pub const CMPR: usize = 2;
|
|
|
// 8-bit field containing the Pad value.
|
|
|
- pub const PAD: usize = 5;
|
|
|
+ pub const PAD: usize = 3;
|
|
|
// Variable length field containing addresses
|
|
|
- pub const fn ADDRESSES(length_field: u8) -> Field {
|
|
|
- let data = DATA(length_field);
|
|
|
- 8..data.end
|
|
|
- }
|
|
|
+ pub const ADDRESSES: usize = 6;
|
|
|
}
|
|
|
|
|
|
/// Core getter methods relevant to any routing type.
|
|
|
impl<T: AsRef<[u8]>> Header<T> {
|
|
|
/// Create a raw octet buffer with an IPv6 Routing Header structure.
|
|
|
- pub const fn new(buffer: T) -> Header<T> {
|
|
|
+ pub const fn new_unchecked(buffer: T) -> Header<T> {
|
|
|
Header { buffer }
|
|
|
}
|
|
|
|
|
@@ -152,7 +137,7 @@ impl<T: AsRef<[u8]>> Header<T> {
|
|
|
/// [new_unchecked]: #method.new_unchecked
|
|
|
/// [check_len]: #method.check_len
|
|
|
pub fn new_checked(buffer: T) -> Result<Header<T>> {
|
|
|
- let header = Self::new(buffer);
|
|
|
+ let header = Self::new_unchecked(buffer);
|
|
|
header.check_len()?;
|
|
|
Ok(header)
|
|
|
}
|
|
@@ -169,15 +154,10 @@ impl<T: AsRef<[u8]>> Header<T> {
|
|
|
return Err(Error);
|
|
|
}
|
|
|
|
|
|
- if len < field::DATA(self.header_len()).end {
|
|
|
- return Err(Error);
|
|
|
- }
|
|
|
-
|
|
|
- // The header lenght field could be wrong and thus we need to check this as well:
|
|
|
- if matches!(self.routing_type(), Type::Type2)
|
|
|
- && field::DATA(self.header_len()).end != field::HOME_ADDRESS.end
|
|
|
- {
|
|
|
- return Err(Error);
|
|
|
+ match self.routing_type() {
|
|
|
+ Type::Type2 if len < field::HOME_ADDRESS.end => return Err(Error),
|
|
|
+ Type::Rpl if len < field::ADDRESSES => return Err(Error),
|
|
|
+ _ => (),
|
|
|
}
|
|
|
|
|
|
Ok(())
|
|
@@ -188,21 +168,6 @@ impl<T: AsRef<[u8]>> Header<T> {
|
|
|
self.buffer
|
|
|
}
|
|
|
|
|
|
- /// Return the next header field.
|
|
|
- #[inline]
|
|
|
- pub fn next_header(&self) -> Protocol {
|
|
|
- let data = self.buffer.as_ref();
|
|
|
- Protocol::from(data[field::NXT_HDR])
|
|
|
- }
|
|
|
-
|
|
|
- /// Return the header length field. Length of the Routing header in 8-octet units,
|
|
|
- /// not including the first 8 octets.
|
|
|
- #[inline]
|
|
|
- pub fn header_len(&self) -> u8 {
|
|
|
- let data = self.buffer.as_ref();
|
|
|
- data[field::LENGTH]
|
|
|
- }
|
|
|
-
|
|
|
/// Return the routing type field.
|
|
|
#[inline]
|
|
|
pub fn routing_type(&self) -> Type {
|
|
@@ -265,26 +230,12 @@ impl<T: AsRef<[u8]>> Header<T> {
|
|
|
/// This function may panic if this header is not the RPL Source Routing Header routing type.
|
|
|
pub fn addresses(&self) -> &[u8] {
|
|
|
let data = self.buffer.as_ref();
|
|
|
- &data[field::ADDRESSES(data[field::LENGTH])]
|
|
|
+ &data[field::ADDRESSES..]
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Core setter methods relevant to any routing type.
|
|
|
impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
|
|
|
- /// Set the next header field.
|
|
|
- #[inline]
|
|
|
- pub fn set_next_header(&mut self, value: Protocol) {
|
|
|
- let data = self.buffer.as_mut();
|
|
|
- data[field::NXT_HDR] = value.into();
|
|
|
- }
|
|
|
-
|
|
|
- /// Set the option data length. Length of the Routing header in 8-octet units.
|
|
|
- #[inline]
|
|
|
- pub fn set_header_len(&mut self, value: u8) {
|
|
|
- let data = self.buffer.as_mut();
|
|
|
- data[field::LENGTH] = value;
|
|
|
- }
|
|
|
-
|
|
|
/// Set the routing type.
|
|
|
#[inline]
|
|
|
pub fn set_routing_type(&mut self, value: Type) {
|
|
@@ -376,8 +327,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
|
|
|
/// This function may panic if this header is not the RPL Source Routing Header routing type.
|
|
|
pub fn set_addresses(&mut self, value: &[u8]) {
|
|
|
let data = self.buffer.as_mut();
|
|
|
- let len = data[field::LENGTH];
|
|
|
- let addresses = &mut data[field::ADDRESSES(len)];
|
|
|
+ let addresses = &mut data[field::ADDRESSES..];
|
|
|
addresses.copy_from_slice(value);
|
|
|
}
|
|
|
}
|
|
@@ -400,20 +350,12 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
|
|
|
#[non_exhaustive]
|
|
|
pub enum Repr<'a> {
|
|
|
Type2 {
|
|
|
- /// The type of header immediately following the Routing header.
|
|
|
- next_header: Protocol,
|
|
|
- /// Length of the Routing header in 8-octet units, not including the first 8 octets.
|
|
|
- length: u8,
|
|
|
/// Number of route segments remaining.
|
|
|
segments_left: u8,
|
|
|
/// The home address of the destination mobile node.
|
|
|
home_address: Address,
|
|
|
},
|
|
|
Rpl {
|
|
|
- /// The type of header immediately following the Routing header.
|
|
|
- next_header: Protocol,
|
|
|
- /// Length of the Routing header in 8-octet units, not including the first 8 octets.
|
|
|
- length: u8,
|
|
|
/// Number of route segments remaining.
|
|
|
segments_left: u8,
|
|
|
/// Number of prefix octets from each segment, except the last segment, that are elided.
|
|
@@ -436,14 +378,10 @@ impl<'a> Repr<'a> {
|
|
|
{
|
|
|
match header.routing_type() {
|
|
|
Type::Type2 => Ok(Repr::Type2 {
|
|
|
- next_header: header.next_header(),
|
|
|
- length: header.header_len(),
|
|
|
segments_left: header.segments_left(),
|
|
|
home_address: header.home_address(),
|
|
|
}),
|
|
|
Type::Rpl => Ok(Repr::Rpl {
|
|
|
- next_header: header.next_header(),
|
|
|
- length: header.header_len(),
|
|
|
segments_left: header.segments_left(),
|
|
|
cmpr_i: header.cmpr_i(),
|
|
|
cmpr_e: header.cmpr_e(),
|
|
@@ -459,7 +397,9 @@ impl<'a> Repr<'a> {
|
|
|
/// representation.
|
|
|
pub const fn buffer_len(&self) -> usize {
|
|
|
match self {
|
|
|
- &Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => field::DATA(length).end,
|
|
|
+ // Routing Type + Segments Left + Reserved + Home Address
|
|
|
+ Repr::Type2 { home_address, .. } => 2 + 4 + home_address.as_bytes().len(),
|
|
|
+ Repr::Rpl { addresses, .. } => 2 + 4 + addresses.len(),
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -467,29 +407,21 @@ impl<'a> Repr<'a> {
|
|
|
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
|
|
|
match *self {
|
|
|
Repr::Type2 {
|
|
|
- next_header,
|
|
|
- length,
|
|
|
segments_left,
|
|
|
home_address,
|
|
|
} => {
|
|
|
- header.set_next_header(next_header);
|
|
|
- header.set_header_len(length);
|
|
|
header.set_routing_type(Type::Type2);
|
|
|
header.set_segments_left(segments_left);
|
|
|
header.clear_reserved();
|
|
|
header.set_home_address(home_address);
|
|
|
}
|
|
|
Repr::Rpl {
|
|
|
- next_header,
|
|
|
- length,
|
|
|
segments_left,
|
|
|
cmpr_i,
|
|
|
cmpr_e,
|
|
|
pad,
|
|
|
addresses,
|
|
|
} => {
|
|
|
- header.set_next_header(next_header);
|
|
|
- header.set_header_len(length);
|
|
|
header.set_routing_type(Type::Rpl);
|
|
|
header.set_segments_left(segments_left);
|
|
|
header.set_cmpr_i(cmpr_i);
|
|
@@ -506,32 +438,33 @@ impl<'a> fmt::Display for Repr<'a> {
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
match *self {
|
|
|
Repr::Type2 {
|
|
|
- next_header,
|
|
|
- length,
|
|
|
segments_left,
|
|
|
home_address,
|
|
|
} => {
|
|
|
write!(
|
|
|
f,
|
|
|
- "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}",
|
|
|
- next_header,
|
|
|
- length,
|
|
|
+ "IPv6 Routing type={} seg_left={} home_address={}",
|
|
|
Type::Type2,
|
|
|
segments_left,
|
|
|
home_address
|
|
|
)
|
|
|
}
|
|
|
Repr::Rpl {
|
|
|
- next_header,
|
|
|
- length,
|
|
|
segments_left,
|
|
|
cmpr_i,
|
|
|
cmpr_e,
|
|
|
pad,
|
|
|
..
|
|
|
} => {
|
|
|
- write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}",
|
|
|
- next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad)
|
|
|
+ write!(
|
|
|
+ f,
|
|
|
+ "IPv6 Routing type={} seg_left={} cmpr_i={} cmpr_e={} pad={}",
|
|
|
+ Type::Rpl,
|
|
|
+ segments_left,
|
|
|
+ cmpr_i,
|
|
|
+ cmpr_e,
|
|
|
+ pad
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -542,30 +475,26 @@ mod test {
|
|
|
use super::*;
|
|
|
|
|
|
// A Type 2 Routing Header
|
|
|
- static BYTES_TYPE2: [u8; 24] = [
|
|
|
- 0x6, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
|
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
|
|
|
+ static BYTES_TYPE2: [u8; 22] = [
|
|
|
+ 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
|
+ 0x0, 0x0, 0x0, 0x1,
|
|
|
];
|
|
|
|
|
|
// A representation of a Type 2 Routing header
|
|
|
static REPR_TYPE2: Repr = Repr::Type2 {
|
|
|
- next_header: Protocol::Tcp,
|
|
|
- length: 2,
|
|
|
segments_left: 1,
|
|
|
home_address: Address::LOOPBACK,
|
|
|
};
|
|
|
|
|
|
// A Source Routing Header with full IPv6 addresses in bytes
|
|
|
- static BYTES_SRH_FULL: [u8; 40] = [
|
|
|
- 0x6, 0x4, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
|
- 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
|
- 0x0, 0x0, 0x3, 0x1,
|
|
|
+ static BYTES_SRH_FULL: [u8; 38] = [
|
|
|
+ 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
|
+ 0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
|
+ 0x3, 0x1,
|
|
|
];
|
|
|
|
|
|
// A representation of a Source Routing Header with full IPv6 addresses
|
|
|
static REPR_SRH_FULL: Repr = Repr::Rpl {
|
|
|
- next_header: Protocol::Tcp,
|
|
|
- length: 4,
|
|
|
segments_left: 2,
|
|
|
cmpr_i: 0,
|
|
|
cmpr_e: 0,
|
|
@@ -577,14 +506,12 @@ mod test {
|
|
|
};
|
|
|
|
|
|
// A Source Routing Header with elided IPv6 addresses in bytes
|
|
|
- static BYTES_SRH_ELIDED: [u8; 16] = [
|
|
|
- 0x6, 0x1, 0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
|
+ static BYTES_SRH_ELIDED: [u8; 14] = [
|
|
|
+ 0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
|
|
|
];
|
|
|
|
|
|
// A representation of a Source Routing Header with elided IPv6 addresses
|
|
|
static REPR_SRH_ELIDED: Repr = Repr::Rpl {
|
|
|
- next_header: Protocol::Tcp,
|
|
|
- length: 1,
|
|
|
segments_left: 2,
|
|
|
cmpr_i: 15,
|
|
|
cmpr_e: 14,
|
|
@@ -595,41 +522,46 @@ mod test {
|
|
|
#[test]
|
|
|
fn test_check_len() {
|
|
|
// less than min header size
|
|
|
- assert_eq!(Err(Error), Header::new(&BYTES_TYPE2[..3]).check_len());
|
|
|
- assert_eq!(Err(Error), Header::new(&BYTES_SRH_FULL[..3]).check_len());
|
|
|
- assert_eq!(Err(Error), Header::new(&BYTES_SRH_ELIDED[..3]).check_len());
|
|
|
- // less than specified length field
|
|
|
- assert_eq!(Err(Error), Header::new(&BYTES_TYPE2[..23]).check_len());
|
|
|
- assert_eq!(Err(Error), Header::new(&BYTES_SRH_FULL[..39]).check_len());
|
|
|
- assert_eq!(Err(Error), Header::new(&BYTES_SRH_ELIDED[..11]).check_len());
|
|
|
+ assert_eq!(
|
|
|
+ Err(Error),
|
|
|
+ Header::new_unchecked(&BYTES_TYPE2[..3]).check_len()
|
|
|
+ );
|
|
|
+ assert_eq!(
|
|
|
+ Err(Error),
|
|
|
+ Header::new_unchecked(&BYTES_SRH_FULL[..3]).check_len()
|
|
|
+ );
|
|
|
+ assert_eq!(
|
|
|
+ Err(Error),
|
|
|
+ Header::new_unchecked(&BYTES_SRH_ELIDED[..3]).check_len()
|
|
|
+ );
|
|
|
// valid
|
|
|
- assert_eq!(Ok(()), Header::new(&BYTES_TYPE2[..]).check_len());
|
|
|
- assert_eq!(Ok(()), Header::new(&BYTES_SRH_FULL[..]).check_len());
|
|
|
- assert_eq!(Ok(()), Header::new(&BYTES_SRH_ELIDED[..]).check_len());
|
|
|
+ assert_eq!(Ok(()), Header::new_unchecked(&BYTES_TYPE2[..]).check_len());
|
|
|
+ assert_eq!(
|
|
|
+ Ok(()),
|
|
|
+ Header::new_unchecked(&BYTES_SRH_FULL[..]).check_len()
|
|
|
+ );
|
|
|
+ assert_eq!(
|
|
|
+ Ok(()),
|
|
|
+ Header::new_unchecked(&BYTES_SRH_ELIDED[..]).check_len()
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn test_header_deconstruct() {
|
|
|
- let header = Header::new(&BYTES_TYPE2[..]);
|
|
|
- assert_eq!(header.next_header(), Protocol::Tcp);
|
|
|
- assert_eq!(header.header_len(), 2);
|
|
|
+ let header = Header::new_unchecked(&BYTES_TYPE2[..]);
|
|
|
assert_eq!(header.routing_type(), Type::Type2);
|
|
|
assert_eq!(header.segments_left(), 1);
|
|
|
assert_eq!(header.home_address(), Address::LOOPBACK);
|
|
|
|
|
|
- let header = Header::new(&BYTES_SRH_FULL[..]);
|
|
|
- assert_eq!(header.next_header(), Protocol::Tcp);
|
|
|
- assert_eq!(header.header_len(), 4);
|
|
|
+ let header = Header::new_unchecked(&BYTES_SRH_FULL[..]);
|
|
|
assert_eq!(header.routing_type(), Type::Rpl);
|
|
|
assert_eq!(header.segments_left(), 2);
|
|
|
- assert_eq!(header.addresses(), &BYTES_SRH_FULL[8..]);
|
|
|
+ assert_eq!(header.addresses(), &BYTES_SRH_FULL[6..]);
|
|
|
|
|
|
- let header = Header::new(&BYTES_SRH_ELIDED[..]);
|
|
|
- assert_eq!(header.next_header(), Protocol::Tcp);
|
|
|
- assert_eq!(header.header_len(), 1);
|
|
|
+ let header = Header::new_unchecked(&BYTES_SRH_ELIDED[..]);
|
|
|
assert_eq!(header.routing_type(), Type::Rpl);
|
|
|
assert_eq!(header.segments_left(), 2);
|
|
|
- assert_eq!(header.addresses(), &BYTES_SRH_ELIDED[8..]);
|
|
|
+ assert_eq!(header.addresses(), &BYTES_SRH_ELIDED[6..]);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
@@ -649,26 +581,26 @@ mod test {
|
|
|
|
|
|
#[test]
|
|
|
fn test_repr_emit() {
|
|
|
- let mut bytes = [0u8; 24];
|
|
|
- let mut header = Header::new(&mut bytes[..]);
|
|
|
+ let mut bytes = [0u8; 22];
|
|
|
+ let mut header = Header::new_unchecked(&mut bytes[..]);
|
|
|
REPR_TYPE2.emit(&mut header);
|
|
|
assert_eq!(header.into_inner(), &BYTES_TYPE2[..]);
|
|
|
|
|
|
- let mut bytes = [0u8; 40];
|
|
|
- let mut header = Header::new(&mut bytes[..]);
|
|
|
+ let mut bytes = [0u8; 38];
|
|
|
+ let mut header = Header::new_unchecked(&mut bytes[..]);
|
|
|
REPR_SRH_FULL.emit(&mut header);
|
|
|
assert_eq!(header.into_inner(), &BYTES_SRH_FULL[..]);
|
|
|
|
|
|
- let mut bytes = [0u8; 16];
|
|
|
- let mut header = Header::new(&mut bytes[..]);
|
|
|
+ let mut bytes = [0u8; 14];
|
|
|
+ let mut header = Header::new_unchecked(&mut bytes[..]);
|
|
|
REPR_SRH_ELIDED.emit(&mut header);
|
|
|
assert_eq!(header.into_inner(), &BYTES_SRH_ELIDED[..]);
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
fn test_buffer_len() {
|
|
|
- assert_eq!(REPR_TYPE2.buffer_len(), 24);
|
|
|
- assert_eq!(REPR_SRH_FULL.buffer_len(), 40);
|
|
|
- assert_eq!(REPR_SRH_ELIDED.buffer_len(), 16);
|
|
|
+ assert_eq!(REPR_TYPE2.buffer_len(), 22);
|
|
|
+ assert_eq!(REPR_SRH_FULL.buffer_len(), 38);
|
|
|
+ assert_eq!(REPR_SRH_ELIDED.buffer_len(), 14);
|
|
|
}
|
|
|
}
|