Skip to content

Commit

Permalink
ipv6route: compress routes in SRH
Browse files Browse the repository at this point in the history
  • Loading branch information
thvdveld committed Dec 8, 2023
1 parent 469facf commit 7d1bb39
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 83 deletions.
5 changes: 5 additions & 0 deletions src/iface/interface/ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,11 @@ impl InterfaceInner {
pad,
addresses,
} => {
for addr in addresses.iter_mut() {
addr.0[..*cmpr_e as usize]
.copy_from_slice(&ipv6_repr.src_addr.as_bytes()[..*cmpr_e as usize]);
}

// Calculate the number of addresses left to visit.
let n = (((ext_hdr.header_len() as usize * 8)
- *pad as usize
Expand Down
220 changes: 137 additions & 83 deletions src/wire/ipv6routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,20 +381,29 @@ impl Repr {
Type::Rpl => {
let mut addresses = heapless::Vec::new();

let addresses_bytes = header.addresses();
let mut buffer = [0u8; 16];
let cmpr_e = header.cmpr_e();
let cmp_i = header.cmpr_i();
let pad = header.pad();

let mut addr_iterator = header.addresses()
[..header.addresses().len() - pad as usize]
.chunks_exact(16 - cmpr_e as usize);

for addr_raw in addr_iterator.by_ref() {
let mut buffer = [0u8; 16];
buffer[cmpr_e as usize..].copy_from_slice(addr_raw);
addresses.push(Address::from_bytes(&buffer)).unwrap();
}

for (i, b) in addresses_bytes.iter().enumerate() {
let j = i % 16;
buffer[j] = *b;
let last_addr = addr_iterator.remainder();

if i % 16 == 0 && i != 0 {
addresses.push(Address::from_bytes(&buffer)).unwrap();
}
if !last_addr.is_empty() {
let mut buffer = [0u8; 16];
buffer[cmp_i as usize..]
.copy_from_slice(&last_addr[..last_addr.len() - pad as usize]);
addresses.push(Address::from_bytes(&buffer)).unwrap();
}

addresses.push(Address::from_bytes(&buffer)).unwrap();

Ok(Repr::Rpl {
segments_left: header.segments_left(),
cmpr_i: header.cmpr_i(),
Expand All @@ -414,7 +423,30 @@ impl Repr {
match self {
// 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() * 16,
Repr::Rpl { addresses, .. } => {
// Compute the length of the common prefix for every address on the route.
let mut common_prefix = 0;

if addresses.len() > 1 {
'outer: for i in 0..16 {
for addr in addresses.iter() {
if addr.as_bytes()[i] != addresses[0].as_bytes()[i] {
break 'outer;
}
}
common_prefix += 1;
}
}

let mut len = 2 + 4 + addresses.len() * 16 - common_prefix * addresses.len();

// Add the padding:
if (len + 2) % 8 != 0 {
len += 8 - ((len + 2) % 8);
}

len
}
}
}

Expand All @@ -432,23 +464,45 @@ impl Repr {
}
Repr::Rpl {
segments_left,
cmpr_i,
cmpr_e,
pad,
ref addresses,
..
} => {
header.set_routing_type(Type::Rpl);
header.set_segments_left(segments_left);
header.set_cmpr_i(cmpr_i);
header.set_cmpr_e(cmpr_e);
header.set_pad(pad);
header.clear_reserved();

// Compute the length of the common prefix for every address on the route.
let mut common_prefix = 0;

if addresses.len() > 1 {
'outer: for i in 0..16 {
for addr in addresses.iter() {
if addr.as_bytes()[i] != addresses[0].as_bytes()[i] {
break 'outer;
}
}
common_prefix += 1;
}
}

// Calculate the padding for the last address:
let len = 2 + 4 + addresses.len() * 16 - common_prefix * addresses.len();
let pad = if (len + 2) % 8 != 0 {
8 - (len + 2) % 8
} else {
0
};

header.set_cmpr_i(common_prefix as u8);
header.set_cmpr_e(common_prefix as u8);
header.set_pad(pad as u8);

let mut addrs_buf = header.addresses_mut();

for addr in addresses {
addrs_buf[..addr.as_bytes().len()].copy_from_slice(addr.as_bytes());
addrs_buf = &mut addrs_buf[addr.as_bytes().len()..];
addrs_buf[..16 - common_prefix]
.copy_from_slice(&addr.as_bytes()[common_prefix..]);
addrs_buf = &mut addrs_buf[16 - common_prefix..];
}
}
}
Expand Down Expand Up @@ -507,45 +561,51 @@ mod test {
home_address: Address::LOOPBACK,
};

// A Source Routing Header with full IPv6 addresses in bytes
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 Source Routing Header with elided IPv6 addresses in bytes
static BYTES_SRH_ELIDED: [u8; 54] = [
0x03, 0x06, 0x99, 0x60, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x04, 0x00,
0x04, 0x00, 0x04, 0x00, 0x04, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x06, 0x00, 0x06,
0x00, 0x06, 0x00, 0x06, 0x07, 0x00, 0x07, 0x00, 0x07, 0x00, 0x07, 0x08, 0x00, 0x08, 0x00,
0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];

// A representation of a Source Routing Header with full IPv6 addresses
fn repr_srh_full() -> Repr {
// A representation of a Source Routing Header with elided IPv6 addresses
fn repr_srh_elided() -> Repr {
Repr::Rpl {
segments_left: 2,
cmpr_i: 0,
cmpr_e: 0,
pad: 0,
segments_left: 6,
cmpr_i: 9,
cmpr_e: 9,
pad: 6,
addresses: heapless::Vec::from_slice(&[
crate::wire::Ipv6Address::from_bytes(&[
0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2,
]),
crate::wire::Ipv6Address::from_bytes(&[
0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1,
]),
Address::new(0, 0, 0, 0, 3, 3, 3, 3),
Address::new(0, 0, 0, 0, 4, 4, 4, 4),
Address::new(0, 0, 0, 0, 5, 5, 5, 5),
Address::new(0, 0, 0, 0, 6, 6, 6, 6),
Address::new(0, 0, 0, 0, 7, 7, 7, 7),
Address::new(0, 0, 0, 0, 8, 8, 8, 8),
])
.unwrap(),
}
}

//// A Source Routing Header with elided IPv6 addresses in bytes
//static BYTES_SRH_ELIDED: [u8; 14] = [
//0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
//];
static BYTES_SRH_VERY_ELIDED: [u8; 14] = [
0x03, 0x02, 0xff, 0x60, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];

//// A representation of a Source Routing Header with elided IPv6 addresses
//static REPR_SRH_ELIDED: Repr = Repr::Rpl {
//segments_left: 2,
//cmpr_i: 15,
//cmpr_e: 14,
//pad: 5,
//addresses: &[0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0],
//};
// A representation of a Source Routing Header with elided IPv6 addresses
fn repr_srh_very_elided() -> Repr {
Repr::Rpl {
segments_left: 2,
cmpr_i: 15,
cmpr_e: 15,
pad: 6,
addresses: heapless::Vec::from_slice(&[
Address::new(0, 0, 0, 0, 0, 0, 0, 3),
Address::new(0, 0, 0, 0, 0, 0, 0, 4),
])
.unwrap(),
}
}

#[test]
fn test_check_len() {
Expand All @@ -556,22 +616,16 @@ mod test {
);
assert_eq!(
Err(Error),
Header::new_unchecked(&BYTES_SRH_FULL[..3]).check_len()
Header::new_unchecked(&BYTES_SRH_ELIDED[..3]).check_len()
);
//assert_eq!(
//Err(Error),
//Header::new_unchecked(&BYTES_SRH_ELIDED[..3]).check_len()
//);
// valid
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()
//);
assert!(Header::new_unchecked(&BYTES_TYPE2[..]).check_len().is_ok());
assert!(Header::new_unchecked(&BYTES_SRH_ELIDED[..])
.check_len()
.is_ok());
assert!(Header::new_unchecked(&BYTES_SRH_VERY_ELIDED[..])
.check_len()
.is_ok());
}

#[test]
Expand All @@ -581,15 +635,15 @@ mod test {
assert_eq!(header.segments_left(), 1);
assert_eq!(header.home_address(), Address::LOOPBACK);

let header = Header::new_unchecked(&BYTES_SRH_FULL[..]);
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_FULL[6..]);
assert_eq!(header.segments_left(), 6);
assert_eq!(header.addresses(), &BYTES_SRH_ELIDED[6..]);

//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[6..]);
let header = Header::new_unchecked(&BYTES_SRH_VERY_ELIDED[..]);
assert_eq!(header.routing_type(), Type::Rpl);
assert_eq!(header.segments_left(), 2);
assert_eq!(header.addresses(), &BYTES_SRH_VERY_ELIDED[6..]);
}

#[test]
Expand All @@ -598,13 +652,13 @@ mod test {
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr, REPR_TYPE2);

let header = Header::new_checked(&BYTES_SRH_FULL[..]).unwrap();
let header = Header::new_checked(&BYTES_SRH_ELIDED[..]).unwrap();
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr, repr_srh_full());
assert_eq!(repr, repr_srh_elided());

//let header = Header::new_checked(&BYTES_SRH_ELIDED[..]).unwrap();
//let repr = Repr::parse(&header).unwrap();
//assert_eq!(repr, REPR_SRH_ELIDED);
let header = Header::new_checked(&BYTES_SRH_VERY_ELIDED[..]).unwrap();
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr, repr_srh_very_elided());
}

#[test]
Expand All @@ -614,21 +668,21 @@ mod test {
REPR_TYPE2.emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_TYPE2[..]);

let mut bytes = [0u8; 38];
let mut bytes = [0u8; 54];
let mut header = Header::new_unchecked(&mut bytes[..]);
repr_srh_full().emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_SRH_FULL[..]);
repr_srh_elided().emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_SRH_ELIDED[..]);

//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[..]);
let mut bytes = [0u8; 14];
let mut header = Header::new_unchecked(&mut bytes[..]);
repr_srh_very_elided().emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_SRH_VERY_ELIDED[..]);
}

#[test]
fn test_buffer_len() {
assert_eq!(REPR_TYPE2.buffer_len(), 22);
assert_eq!(repr_srh_full().buffer_len(), 38);
//assert_eq!(REPR_SRH_ELIDED.buffer_len(), 14);
assert_eq!(repr_srh_elided().buffer_len(), 54);
assert_eq!(repr_srh_very_elided().buffer_len(), 14);
}
}

0 comments on commit 7d1bb39

Please sign in to comment.