Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move IPv6 HBH in own module #827

Merged
merged 1 commit into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -276,7 +276,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