Skip to content

Commit

Permalink
Add function
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianSchmid committed Jul 17, 2023
1 parent 4b6e0b6 commit 65ceec6
Showing 1 changed file with 234 additions and 0 deletions.
234 changes: 234 additions & 0 deletions etherparse/src/internet/ip_header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,240 @@ impl IpHeader {
}
}

/// Reads an `IpHeader` & seperates the payload from the given slice with
/// less strict length checks (usefull for cut off packet or for packets with
/// unset length fields).
///
/// This function can be used when:
///
/// * The packet is incomplete (e.g. a cut off packet returned via ICMP).
/// * `total_len` (for IPv4) `payload_length` (for IPv6) have not yet been set.
///
/// # Differences to `from_slice`:
///
/// The main differences is that the function ignores inconsistent
/// `total_len` (in IPv4 headers) and `payload_length` (in IPv6 headers)
/// values. When these length values in the IP header are inconsistant the
/// lenght of the given slice is used as a substitute.
///
/// You can check if the slice length was used as a substitude by checking
/// if the `len_source` value in the returned [`IpPayload`] is set to
/// [`LenSource::Slice`]. If a substitution was not needed `len_source`
/// is set to [`LenSource::Ipv4HeaderTotalLen`] or
/// [`LenSource::Ipv6HeaderPayloadLen`].
///
/// # When is the slice length used as a fallback?
///
/// For IPv4 packets the slice length is used as a fallback/substitude
/// if the `total_length` field in the IPv4 header is:
///
/// * Bigger then the given slice (payload cannot fully be seperated).
/// * Too small to contain at least the IPv4 header.
///
/// For IPv6 packet the slice length is used as a fallback/substitude
/// if the `payload_length` is
///
/// * Bigger then the given slice (payload cannot fully be seperated).
/// * The value `0`.
pub fn from_slice_lax(
slice: &[u8],
) -> Result<(IpHeader, IpPayload<'_>), err::ip::HeaderSliceError> {
use err::ip::{HeaderError::*, HeaderSliceError::*};

if slice.is_empty() {
Err(Len(err::LenError {
required_len: 1,
len: slice.len(),
len_source: err::LenSource::Slice,
layer: err::Layer::IpHeader,
layer_start_offset: 0,
}))

Check warning on line 295 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L283-L295

Added lines #L283 - L295 were not covered by tests
} else {
match slice[0] >> 4 {

Check warning on line 297 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L297

Added line #L297 was not covered by tests
4 => {
// check length
if slice.len() < Ipv4Header::MIN_LEN {
return Err(Len(err::LenError {
required_len: Ipv4Header::MIN_LEN,
len: slice.len(),
len_source: err::LenSource::Slice,
layer: err::Layer::Ipv4Header,
layer_start_offset: 0,
}));
}

// read ihl
//
// SAFETY:
// Safe as the slice length is checked to be at least
// Ipv4Header::MIN_LEN (20) at the start.
let ihl = unsafe { slice.get_unchecked(0) } & 0xf;

//check that the ihl is correct
if ihl < 5 {
return Err(Content(Ipv4HeaderLengthSmallerThanHeader { ihl }));
}

// check that the slice contains enough data for the entire header + options
let header_len = usize::from(ihl) * 4;
if slice.len() < header_len {
return Err(Len(LenError {
required_len: header_len,
len: slice.len(),
len_source: LenSource::Slice,
layer: Layer::Ipv4Header,
layer_start_offset: 0,
}));
}

let header = unsafe {
// SAFETY: Safe as the IHL & slice len has been validated
Ipv4HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
slice.as_ptr(),
header_len,
))
.to_header()
};

// check that the total len is at least containing the header len
let total_len: usize = header.total_len.into();

Check warning on line 344 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L300-L344

Added lines #L300 - L344 were not covered by tests

// restrict the rest of the slice based on the total len
let (len_source, rest) = if slice.len() < total_len {

Check warning on line 347 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L347

Added line #L347 was not covered by tests
// fallback to slice len
(
LenSource::Slice,
unsafe {
core::slice::from_raw_parts(
// SAFETY: Safe as the slice length was validated to be at least header_length
slice.as_ptr().add(header_len),
// SAFETY: Safe as slice length has been validated to be at least header_len long
slice.len() - header_len,
)
}
)

Check warning on line 359 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L349-L359

Added lines #L349 - L359 were not covered by tests
} else {
(
LenSource::Ipv4HeaderTotalLen,
unsafe {
core::slice::from_raw_parts(
// SAFETY: Safe as the slice length was validated to be at least header_length
slice.as_ptr().add(header_len),
// SAFETY: Safe as slice length has been validated to be at least total_length_usize long
total_len - header_len,
)
}
)

Check warning on line 371 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L361-L371

Added lines #L361 - L371 were not covered by tests
};

let (exts, next_protocol, rest) =
Ipv4Extensions::from_slice(header.protocol, rest).map_err(|err| {
use err::ip_auth::HeaderSliceError as I;
match err {
I::Len(mut err) => {
err.layer_start_offset += header_len;
err.len_source = len_source;
Len(err)

Check warning on line 381 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L374-L381

Added lines #L374 - L381 were not covered by tests
}
I::Content(err) => Content(Ipv4Ext(err)),

Check warning on line 383 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L383

Added line #L383 was not covered by tests
}
})?;

Check warning on line 385 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L385

Added line #L385 was not covered by tests

let fragmented = header.is_fragmenting_payload();
Ok((
IpHeader::Version4(header, exts),
IpPayload {
ip_number: next_protocol,
fragmented,
len_source,
payload: rest,
},
))

Check warning on line 396 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L387-L396

Added lines #L387 - L396 were not covered by tests
}
6 => {
if slice.len() < Ipv6Header::LEN {
return Err(Len(err::LenError {
required_len: Ipv6Header::LEN,
len: slice.len(),
len_source: err::LenSource::Slice,
layer: err::Layer::Ipv6Header,
layer_start_offset: 0,
}));
}
let header = {
// SAFETY:
// This is safe as the slice length is checked to be
// at least Ipv6Header::LEN (40) befpre this code block.
unsafe {
Ipv6HeaderSlice::from_slice_unchecked(core::slice::from_raw_parts(
slice.as_ptr(),
Ipv6Header::LEN,
))
.to_header()
}
};

// restrict slice by the length specified in the header
let payload_len = usize::from(header.payload_length);
let (header_payload, len_source) =
if header.payload_length == 0 || (slice.len() - Ipv6Header::LEN) < payload_len {

Check warning on line 424 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L399-L424

Added lines #L399 - L424 were not covered by tests
// TODO: Add payload length parsing from the jumbogram
unsafe {
(
core::slice::from_raw_parts(
// SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above.
slice.as_ptr().add(Ipv6Header::LEN),
// SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above.
slice.len() - Ipv6Header::LEN,
),
LenSource::Slice,
)

Check warning on line 435 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L427-L435

Added lines #L427 - L435 were not covered by tests
}
} else {
unsafe {
(
core::slice::from_raw_parts(
// SAFTEY: Safe as we verify what `slice.len() >= Ipv6Header::LEN` above.
slice.as_ptr().add(Ipv6Header::LEN),
// SAFTEY: Safe as we verify that `(slice.len() - Ipv6Header::LEN) >= payload_len` above.
payload_len,
),
LenSource::Ipv6HeaderPayloadLen,
)

Check warning on line 447 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L439-L447

Added lines #L439 - L447 were not covered by tests
}
};

let (exts, next_header, rest) =
Ipv6Extensions::from_slice(header.next_header, header_payload).map_err(
|err| {
use err::ipv6_exts::HeaderSliceError as I;
match err {
I::Len(mut err) => {
err.layer_start_offset += Ipv6Header::LEN;
err.len_source = len_source;
Len(err)

Check warning on line 459 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L451-L459

Added lines #L451 - L459 were not covered by tests
}
I::Content(err) => Content(Ipv6Ext(err)),

Check warning on line 461 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L461

Added line #L461 was not covered by tests
}
},
)?;

Check warning on line 464 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L463-L464

Added lines #L463 - L464 were not covered by tests

let fragmented = exts.is_fragmenting_payload();
Ok((
IpHeader::Version6(header, exts),
IpPayload {
ip_number: next_header,
fragmented,
len_source,
payload: rest,
},
))

Check warning on line 475 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L466-L475

Added lines #L466 - L475 were not covered by tests
}
version_number => Err(Content(UnsupportedIpVersion { version_number })),

Check warning on line 477 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L477

Added line #L477 was not covered by tests
}
}
}

Check warning on line 480 in etherparse/src/internet/ip_header.rs

View check run for this annotation

Codecov / codecov/patch

etherparse/src/internet/ip_header.rs#L480

Added line #L480 was not covered by tests

/// Read an IPv4 header & extension headers from a slice and return the slice containing the payload
/// according to the total_length field in the IPv4 header.
pub fn ipv4_from_slice(
Expand Down

0 comments on commit 65ceec6

Please sign in to comment.