Skip to content

Commit

Permalink
Merge pull request #827 from thvdveld/ipv6-hop-by-hop
Browse files Browse the repository at this point in the history
refactor: move IPv6 HBH in own module
  • Loading branch information
thvdveld authored Aug 8, 2023
2 parents ce303ec + 53bdf43 commit d3c491f
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 13 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ reassembly-buffer-count-8 = []
reassembly-buffer-count-16 = []
reassembly-buffer-count-32 = []

ipv6-hbh-max-options-1 = [] # Default
ipv6-hbh-max-options-2 = []
ipv6-hbh-max-options-3 = []
ipv6-hbh-max-options-4 = []
ipv6-hbh-max-options-8 = []
ipv6-hbh-max-options-16 = []
ipv6-hbh-max-options-32 = []

dns-max-result-count-1 = [] # Default
dns-max-result-count-2 = []
dns-max-result-count-3 = []
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,9 @@ Maximum amount of DNS servers that can be configured in one DNS socket. Default:

Maximum length of DNS names that can be queried. Default: 255.

### IPV6_HBH_MAX_OPTIONS

The maximum amount of parsed options the IPv6 Hop-by-Hop header can hold. Default: 1.

## Hosted usage examples

Expand Down
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ static CONFIGS: &[(&str, usize)] = &[
("ASSEMBLER_MAX_SEGMENT_COUNT", 4),
("REASSEMBLY_BUFFER_SIZE", 1500),
("REASSEMBLY_BUFFER_COUNT", 1),
("IPV6_HBH_MAX_OPTIONS", 1),
("DNS_MAX_RESULT_COUNT", 1),
("DNS_MAX_SERVER_COUNT", 1),
("DNS_MAX_NAME_SIZE", 255),
Expand Down
1 change: 1 addition & 0 deletions gen_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def feature(name, default, min, max, pow2=None):
feature("assembler_max_segment_count", default=4, min=1, max=32, pow2=4)
feature("reassembly_buffer_size", default=1500, min=256, max=65536, pow2=True)
feature("reassembly_buffer_count", default=1, min=1, max=32, pow2=4)
feature("ipv6_hbh_max_options", default=1, min=1, max=32, pow2=4)
feature("dns_max_result_count", default=1, min=1, max=32, pow2=4)
feature("dns_max_server_count", default=1, min=1, max=32, pow2=4)
feature("dns_max_name_size", default=255, min=64, max=255, pow2=True)
Expand Down
14 changes: 7 additions & 7 deletions src/iface/interface/ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -250,19 +250,19 @@ impl InterfaceInner {
handled_by_raw_socket: bool,
ip_payload: &'frame [u8],
) -> Option<IpPacket<'frame>> {
let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ip_payload));
let ext_hdr = check!(Ipv6ExtHeader::new_checked(ip_payload));
let ext_repr = check!(Ipv6ExtHeaderRepr::parse(&ext_hdr));
let hbh_hdr = check!(Ipv6HopByHopHeader::new_checked(ext_repr.data));
let hbh_repr = check!(Ipv6HopByHopRepr::parse(&hbh_hdr));

let hbh_options = Ipv6OptionsIterator::new(hbh_repr.data);
for opt_repr in hbh_options {
let opt_repr = check!(opt_repr);
for opt_repr in &hbh_repr.options {
match opt_repr {
Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (),
#[cfg(feature = "proto-rpl")]
Ipv6OptionRepr::Rpl(_) => {}

Ipv6OptionRepr::Unknown { type_, .. } => {
match Ipv6OptionFailureType::from(type_) {
match Ipv6OptionFailureType::from(*type_) {
Ipv6OptionFailureType::Skip => (),
Ipv6OptionFailureType::Discard => {
return None;
Expand All @@ -280,9 +280,9 @@ impl InterfaceInner {
sockets,
meta,
ipv6_repr,
hbh_repr.next_header,
ext_repr.next_header,
handled_by_raw_socket,
&ip_payload[hbh_repr.header_len() + hbh_repr.data.len()..],
&ip_payload[ext_repr.header_len() + ext_repr.data.len()..],
)
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ mod config {
pub const REASSEMBLY_BUFFER_SIZE: usize = 1500;
pub const RPL_RELATIONS_BUFFER_COUNT: usize = 16;
pub const RPL_PARENTS_BUFFER_COUNT: usize = 8;
pub const IPV6_HBH_MAX_OPTIONS: usize = 2;
}

#[cfg(not(test))]
Expand Down
176 changes: 176 additions & 0 deletions src/wire/ipv6hbh.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use super::{Error, Ipv6Option, Ipv6OptionRepr, Ipv6OptionsIterator, Result};

use heapless::Vec;

/// A read/write wrapper around an IPv6 Hop-by-Hop Header buffer.
pub struct Header<T: AsRef<[u8]>> {
buffer: T,
}

impl<T: AsRef<[u8]>> Header<T> {
/// Create a raw octet buffer with an IPv6 Hop-by-Hop Header structure.
pub const fn new_unchecked(buffer: T) -> Self {
Header { buffer }
}

/// Shorthand for a combination of [new_unchecked] and [check_len].
///
/// [new_unchecked]: #method.new_unchecked
/// [check_len]: #method.check_len
pub fn new_checked(buffer: T) -> Result<Self> {
let header = Self::new_unchecked(buffer);
header.check_len()?;
Ok(header)
}

/// Ensure that no accessor method will panic if called.
/// Returns `Err(Error)` if the buffer is too short.
///
/// The result of this check is invalidated by calling [set_header_len].
///
/// [set_header_len]: #method.set_header_len
pub fn check_len(&self) -> Result<()> {
if self.buffer.as_ref().is_empty() {
return Err(Error);
}

Ok(())
}

/// Consume the header, returning the underlying buffer.
pub fn into_inner(self) -> T {
self.buffer
}
}

impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> {
/// Return the options of the IPv6 Hop-by-Hop header.
pub fn options(&self) -> &'a [u8] {
self.buffer.as_ref()
}
}

impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Header<&'a mut T> {
/// Return a mutable pointer to the options of the IPv6 Hop-by-Hop header.
pub fn options_mut(&mut self) -> &mut [u8] {
self.buffer.as_mut()
}
}

/// A high-level representation of an IPv6 Hop-by-Hop Header.
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr<'a> {
pub options: heapless::Vec<Ipv6OptionRepr<'a>, { crate::config::IPV6_HBH_MAX_OPTIONS }>,
}

impl<'a> Repr<'a> {
/// Parse an IPv6 Hop-by-Hop Header and return a high-level representation.
pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
let mut options = Vec::new();

let iter = Ipv6OptionsIterator::new(header.options());

for option in iter {
let option = option?;

if let Err(e) = options.push(option) {
net_trace!("eror when parsing hop-by-hop options: {}", e);
break;
}
}

Ok(Self { options })
}

/// Return the length, in bytes, of a header that will be emitted from this high-level
/// representation.
pub fn buffer_len(&self) -> usize {
self.options.iter().map(|o| o.buffer_len()).sum()
}

/// Emit a high-level representation into an IPv6 Hop-by-Hop Header.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
let mut buffer = header.options_mut();

for opt in &self.options {
opt.emit(&mut Ipv6Option::new_unchecked(
&mut buffer[..opt.buffer_len()],
));
buffer = &mut buffer[opt.buffer_len()..];
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::wire::Error;

// A Hop-by-Hop Option header with a PadN option of option data length 4.
static REPR_PACKET_PAD4: [u8; 6] = [0x1, 0x4, 0x0, 0x0, 0x0, 0x0];

// A Hop-by-Hop Option header with a PadN option of option data length 12.
static REPR_PACKET_PAD12: [u8; 14] = [
0x1, 0x0C, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];

#[test]
fn test_check_len() {
// zero byte buffer
assert_eq!(
Err(Error),
Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len()
);
// valid
assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
// valid
assert_eq!(
Ok(()),
Header::new_unchecked(&REPR_PACKET_PAD12).check_len()
);
}

#[test]
fn test_repr_parse_valid() {
let header = Header::new_unchecked(&REPR_PACKET_PAD4);
let repr = Repr::parse(&header).unwrap();

let mut options = Vec::new();
options.push(Ipv6OptionRepr::PadN(4)).unwrap();
assert_eq!(repr, Repr { options });

let header = Header::new_unchecked(&REPR_PACKET_PAD12);
let repr = Repr::parse(&header).unwrap();

let mut options = Vec::new();
options.push(Ipv6OptionRepr::PadN(12)).unwrap();
assert_eq!(repr, Repr { options });
}

#[test]
fn test_repr_emit() {
let mut options = Vec::new();
options.push(Ipv6OptionRepr::PadN(4)).unwrap();
let repr = Repr { options };

let mut bytes = [0u8; 6];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);

assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]);

let mut options = Vec::new();
options.push(Ipv6OptionRepr::PadN(12)).unwrap();
let repr = Repr { options };

let mut bytes = [0u8; 14];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);

assert_eq!(header.into_inner(), &REPR_PACKET_PAD12[..]);
}
}
10 changes: 4 additions & 6 deletions src/wire/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ mod ipv6ext_header;
#[cfg(feature = "proto-ipv6")]
mod ipv6fragment;
#[cfg(feature = "proto-ipv6")]
mod ipv6hbh;
#[cfg(feature = "proto-ipv6")]
mod ipv6option;
#[cfg(feature = "proto-ipv6")]
mod ipv6routing;
Expand Down Expand Up @@ -198,14 +200,10 @@ pub use self::ipv6option::{
pub use self::ipv6ext_header::{Header as Ipv6ExtHeader, Repr as Ipv6ExtHeaderRepr};

#[cfg(feature = "proto-ipv6")]
/// A read/write wrapper around an IPv6 Hop-By-Hop header.
pub type Ipv6HopByHopHeader<T> = Ipv6ExtHeader<T>;
#[cfg(feature = "proto-ipv6")]
/// A high-level representation of an IPv6 Hop-By-Hop heade.
pub type Ipv6HopByHopRepr<'a> = Ipv6ExtHeaderRepr<'a>;
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr};

#[cfg(feature = "proto-ipv6")]
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr};
pub use self::ipv6hbh::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr};

#[cfg(feature = "proto-ipv6")]
pub use self::ipv6routing::{
Expand Down

0 comments on commit d3c491f

Please sign in to comment.