Skip to content

Commit

Permalink
Merge pull request #99 from RabadanDotDev/add-linux-sll-support
Browse files Browse the repository at this point in the history
Add linux sll support
  • Loading branch information
JulianSchmid authored May 2, 2024
2 parents 15c4677 + 1e76496 commit b53002c
Show file tree
Hide file tree
Showing 56 changed files with 4,145 additions and 141 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ This is the faster option if your code is not interested in all fields of all th
Depending from which point downward you want to slice a package check out the functions:

* [`SlicedPacket::from_ethernet`](https://docs.rs/etherparse/~0/etherparse/struct.SlicedPacket.html#method.from_ethernet) for parsing from an Ethernet II header downwards
* [`SlicedPacket::from_linux_sll`](https://docs.rs/etherparse/~0/etherparse/struct.SlicedPacket.html#method.from_linux_sll) for parsing from a Linux Cooked Capture v1 (SLL) downwards
* [`SlicedPacket::from_ether_type`](https://docs.rs/etherparse/~0/etherparse/struct.SlicedPacket.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header
* [`SlicedPacket::from_ip`](https://docs.rs/etherparse/~0/etherparse/struct.SlicedPacket.html#method.from_ip) for parsing from an IPv4 or IPv6 downwards

Expand Down Expand Up @@ -98,6 +99,7 @@ In case you want to parse cut off packets (e.g. packets returned in in ICMP mess
It is also possible to only slice one packet layer:

* [`Ethernet2Slice::from_slice_without_fcs`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Slice.html#method.from_slice_without_fcs) & [`Ethernet2Slice::from_slice_with_crc32_fcs`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Slice.html#method.from_slice_with_crc32_fcs)
* [`LinuxSllSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllSlice.html#method.from_slice)
* [`SingleVlanSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanSlice.html#method.from_slice) & [`DoubleVlanSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanSlice.html#method.from_slice)
* [`IpSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/enum.IpSlice.html#method.from_slice) & [`LaxIpSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/enum.LaxIpSlice.html#method.from_slice)
* [`Ipv4Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Slice.html#method.from_slice) & [`LaxIpv4Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LaxIpv4Slice.html#method.from_slice)
Expand All @@ -118,6 +120,7 @@ It is also possible just to parse headers. Have a look at the documentation for
following \[NAME\]HeaderSlice.from_slice methods, if you want to just slice the header:

* [`Ethernet2HeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2HeaderSlice.html#method.from_slice)
* [`LinuxSllHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeaderSlice.html#method.from_slice)
* [`SingleVlanHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeaderSlice.html#method.from_slice)
* [`DoubleVlanHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeaderSlice.html#method.from_slice)
* [`Ipv4HeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4HeaderSlice.html#method.from_slice)
Expand All @@ -133,6 +136,7 @@ following \[NAME\]HeaderSlice.from_slice methods, if you want to just slice the
And for deserialization into the corresponding header structs have a look at:

* [`Ethernet2Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.read) & [`Ethernet2Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.from_slice)
* [`LinuxSllHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeader.html#method.read) & [`LinuxSllHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeader.html#method.from_slice)
* [`SingleVlanHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.read) & [`SingleVlanHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.from_slice)
* [`DoubleVlanHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.read) & [`DoubleVlanHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.from_slice)
* [`IpHeaders::read`](https://docs.rs/etherparse/~0/etherparse/enum.IpHeaders.html#method.read) & [`IpHeaders::from_slice`](https://docs.rs/etherparse/~0/etherparse/enum.IpHeaders.html#method.from_slice)
Expand Down Expand Up @@ -189,6 +193,7 @@ Alternatively it is possible to manually build a packet ([example](etherparse/ex
Read the documentations of the different methods for a more details:

* [`Ethernet2Header::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.to_bytes) & [`Ethernet2Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.write)
* [`LinuxSllHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeader.html#method.to_bytes) & [`LinuxSllHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.LinuxSllHeader.html#method.write)
* [`SingleVlanHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.to_bytes) & [`SingleVlanHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.write)
* [`DoubleVlanHeader::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.to_bytes) & [`DoubleVlanHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.write)
* [`Ipv4Header::to_bytes`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.to_bytes) & [`Ipv4Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.write) & [`Ipv4Header::write_raw`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.write_raw)
Expand Down Expand Up @@ -228,6 +233,11 @@ Read the documentations of the different methods for a more details:
* [Internet Control Message Protocol version 6 (ICMPv6) Parameters](https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml)
* Multicast Listener Discovery (MLD) for IPv6 [RFC 2710](https://datatracker.ietf.org/doc/html/rfc2710)
* Neighbor Discovery for IP version 6 (IPv6) [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861)
* [LINKTYPE_LINUX_SLL](https://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html) on tcpdump
* LINUX_SLL [header definition](https://github.com/the-tcpdump-group/libpcap/blob/a932566fa1f6df16176ac702b1762ea1cd9ed9a3/pcap/sll.h) on libpcap
* [Linux packet types definitions](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_packet.h?id=e33c4963bf536900f917fb65a687724d5539bc21) on the Linux kernel
* Address Resolution Protocol (ARP) Parameters [Harware Types](https://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml#arp-parameters-2)
* [Arp hardware identifiers definitions](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/include/uapi/linux/if_arp.h?id=e33c4963bf536900f917fb65a687724d5539bc21) on the Linux kernel

## License
Licensed under either of Apache License, Version 2.0 or MIT license at your option. The corresponding license texts can be found in the LICENSE-APACHE file and the LICENSE-MIT file.
Expand Down
11 changes: 11 additions & 0 deletions etherparse/examples/read_by_slicing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,20 @@ fn main() {
value.source(),
value.destination()
),
Some(LinuxSll(value)) => println!(
" LinuxSll (packet type: {:?}, source address: {:?})",
value.packet_type(),
value.sender_address(),
),
Some(EtherPayload(payload)) => {
println!(" EtherPayload (ether type {:?})", payload.ether_type)
}
Some(LinuxSllPayload(payload)) => {
println!(
" LinuxSllPayload (protocol type {:?})",
payload.protocol_type
)
}
None => {}
}

Expand Down
7 changes: 7 additions & 0 deletions etherparse/proptest-regressions/sliced_packet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc b196b71d1fd361e0dc02078dcedaa8e67bb496f2cb76ceddd1fa51ce90c70f6d # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0x0000 }, ref linux_sll = LinuxSllHeader { packet_type: 0 (Sent to us), arp_hrd_type: 824 (Netlink header), sender_address_valid_length: 0, sender_address: [0, 0, 0, 0, 0, 0, 0, 0], protocol_type: NetlinkProtocolType(0) }, ref vlan_outer = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref vlan_inner = SingleVlanHeader { pcp: VlanPcp(0), drop_eligible_indicator: false, vlan_id: VlanId(0), ether_type: 0x0000 }, ref ipv4 = Ipv4Header { dscp: Ipv4Dscp(0), ecn: Ipv4Ecn(0), total_len: 60781, identification: 0, dont_fragment: false, more_fragments: false, fragment_offset: IpFragOffset(0), time_to_live: 0, protocol: 253 (Use for experimentation and testing), header_checksum: 128, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [] }, ref udp = UdpHeader { source_port: 24402, destination_port: 23948, length: 49051, checksum: 43062 }
30 changes: 19 additions & 11 deletions etherparse/src/compositions_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use proptest::prelude::*;

#[derive(Clone, Debug, Eq, PartialEq)]
struct ComponentTest {
link: Option<Ethernet2Header>,
link: Option<LinkHeader>,
vlan: Option<VlanHeader>,
ip: Option<IpHeaders>,
transport: Option<TransportHeader>,
Expand Down Expand Up @@ -142,26 +142,32 @@ impl ComponentTest {

// PacketHeaders::from_ether_type
ether_down.assert_headers(
PacketHeaders::from_ether_type(test.link.as_ref().unwrap().ether_type, &buffer[..])
.unwrap(),
PacketHeaders::from_ether_type(
test.link.clone().unwrap().ethernet2().unwrap().ether_type,
&buffer[..],
)
.unwrap(),
);

// SlicedPacket::from_ether_type
ether_down.assert_sliced_packet(
SlicedPacket::from_ether_type(test.link.as_ref().unwrap().ether_type, &buffer[..])
.unwrap(),
SlicedPacket::from_ether_type(
test.link.clone().unwrap().ethernet2().unwrap().ether_type,
&buffer[..],
)
.unwrap(),
);

// create unexpected end of slice errors for the different headers
for len in ether_down.invalid_ser_lengths() {
if let Some(len) = len {
assert!(PacketHeaders::from_ether_type(
test.link.as_ref().unwrap().ether_type,
test.link.clone().unwrap().ethernet2().unwrap().ether_type,
&buffer[..len]
)
.is_err());
assert!(SlicedPacket::from_ether_type(
test.link.as_ref().unwrap().ether_type,
test.link.clone().unwrap().ethernet2().unwrap().ether_type,
&buffer[..len]
)
.is_err());
Expand Down Expand Up @@ -290,8 +296,10 @@ impl ComponentTest {
self.link,
match result.link.as_ref() {
Some(l) => match l {
LinkSlice::Ethernet2(e) => Some(e.to_header()),
LinkSlice::Ethernet2(e) => Some(LinkHeader::Ethernet2(e.to_header())),
LinkSlice::LinuxSll(e) => Some(LinkHeader::LinuxSll(e.to_header())),
LinkSlice::EtherPayload(_) => None,
LinkSlice::LinuxSllPayload(_) => None,
},
None => None,
}
Expand Down Expand Up @@ -608,7 +616,7 @@ proptest! {
link: Some({
let mut result = eth.clone();
result.ether_type = ether_type;
result
LinkHeader::Ethernet2(result)
}),
vlan: None,
ip: None,
Expand Down Expand Up @@ -639,11 +647,11 @@ fn test_packet_slicing_panics() {
transport: None,
};
ComponentTest {
link: Some(Ethernet2Header {
link: Some(LinkHeader::Ethernet2(Ethernet2Header {
source: [0; 6],
destination: [0; 6],
ether_type: 0.into(),
}),
})),
vlan: None,
ip: None,
transport: None,
Expand Down
5 changes: 1 addition & 4 deletions etherparse/src/err/double_vlan/header_slice_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ impl std::error::Error for HeaderSliceError {
#[cfg(test)]
mod tests {
use super::{HeaderSliceError::*, *};
use crate::{
err::{Layer, LenError},
LenSource,
};
use crate::{err::Layer, LenSource};
use alloc::format;
use std::{
collections::hash_map::DefaultHasher,
Expand Down
76 changes: 74 additions & 2 deletions etherparse/src/err/from_slice_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ pub enum FromSliceError {
/// not enough data being available).
Len(LenError),

/// Error when decoding an Linux SLL header.
LinuxSll(linux_sll::HeaderError),

/// Error while parsing a double vlan header.
DoubleVlan(double_vlan::HeaderError),

Expand Down Expand Up @@ -38,6 +41,12 @@ impl FromSliceError {
_ => None,
}
}
pub fn linux_sll(&self) -> Option<&linux_sll::HeaderError> {
match self {
FromSliceError::LinuxSll(err) => Some(err),
_ => None,
}
}
pub fn double_vlan(&self) -> Option<&double_vlan::HeaderError> {
match self {
FromSliceError::DoubleVlan(err) => Some(err),
Expand Down Expand Up @@ -87,6 +96,7 @@ impl core::fmt::Display for FromSliceError {
use FromSliceError::*;
match self {
Len(err) => err.fmt(f),
LinuxSll(err) => err.fmt(f),
DoubleVlan(err) => err.fmt(f),
Ip(err) => err.fmt(f),
IpAuth(err) => err.fmt(f),
Expand All @@ -104,6 +114,7 @@ impl std::error::Error for FromSliceError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
FromSliceError::Len(err) => Some(err),
FromSliceError::LinuxSll(err) => Some(err),
FromSliceError::DoubleVlan(err) => Some(err),
FromSliceError::Ip(err) => Some(err),
FromSliceError::IpAuth(err) => Some(err),
Expand All @@ -123,6 +134,24 @@ impl From<LenError> for FromSliceError {
}
}

// linux sll conversions

impl From<linux_sll::HeaderError> for FromSliceError {
fn from(value: linux_sll::HeaderError) -> Self {
FromSliceError::LinuxSll(value)
}
}

impl From<linux_sll::HeaderSliceError> for FromSliceError {
fn from(value: linux_sll::HeaderSliceError) -> Self {
use linux_sll::HeaderSliceError::*;
match value {
Len(err) => FromSliceError::Len(err),
Content(err) => FromSliceError::LinuxSll(err),
}
}
}

// double vlan error conversions

impl From<double_vlan::HeaderError> for FromSliceError {
Expand Down Expand Up @@ -280,6 +309,7 @@ impl From<packet::SliceError> for FromSliceError {
use packet::SliceError::*;
match value {
Len(err) => FromSliceError::Len(err),
LinuxSll(err) => FromSliceError::LinuxSll(err),
Ip(err) => FromSliceError::Ip(err),
Ipv4(err) => FromSliceError::Ipv4(err),
Ipv6(err) => FromSliceError::Ipv6(err),
Expand Down Expand Up @@ -310,7 +340,7 @@ impl From<tcp::HeaderSliceError> for FromSliceError {

#[cfg(test)]
mod tests {
use crate::{EtherType, LenSource};
use crate::{ArpHardwareId, EtherType, LenSource};

use super::{FromSliceError::*, *};
use core::hash::{Hash, Hasher};
Expand Down Expand Up @@ -395,14 +425,17 @@ mod tests {

#[test]
fn display_source() {
let test_values: [FromSliceError; 8] = [
let test_values: [FromSliceError; 9] = [
Len(LenError {
required_len: 0,
len: 0,
len_source: LenSource::Slice,
layer: Layer::Icmpv4,
layer_start_offset: 0,
}),
LinuxSll(linux_sll::HeaderError::UnsupportedArpHardwareId {
arp_hardware_type: ArpHardwareId::ETHER,
}),
DoubleVlan(double_vlan::HeaderError::NonVlanEtherType {
unexpected_ether_type: EtherType(123),
}),
Expand Down Expand Up @@ -431,6 +464,9 @@ mod tests {
layer: Layer::Icmpv4,
layer_start_offset: 0,
};
let linux_sll_error = || linux_sll::HeaderError::UnsupportedArpHardwareId {
arp_hardware_type: ArpHardwareId::ETHER,
};
let double_vlan_error = || double_vlan::HeaderError::NonVlanEtherType {
unexpected_ether_type: EtherType(1),
};
Expand All @@ -445,6 +481,13 @@ mod tests {
assert_eq!(Len(len_error()).len(), Some(&len_error()));
assert_eq!(Ipv4(ipv4_error()).len(), None);

// linux_sll
assert_eq!(
LinuxSll(linux_sll_error()).linux_sll(),
Some(&linux_sll_error())
);
assert_eq!(Ipv4(ipv4_error()).linux_sll(), None);

// double_vlan
assert_eq!(
DoubleVlan(double_vlan_error()).double_vlan(),
Expand Down Expand Up @@ -498,6 +541,35 @@ mod tests {
FromSliceError::from(len_error()).len().unwrap()
);

// linux sll
{
let header_error = || linux_sll::HeaderError::UnsupportedArpHardwareId {
arp_hardware_type: ArpHardwareId::ETHER,
};
assert_eq!(
&header_error(),
FromSliceError::from(header_error()).linux_sll().unwrap()
);
assert_eq!(
&header_error(),
FromSliceError::from(linux_sll::HeaderSliceError::Content(header_error()))
.linux_sll()
.unwrap()
);
assert_eq!(
&len_error(),
FromSliceError::from(linux_sll::HeaderSliceError::Len(len_error()))
.len()
.unwrap()
);
assert_eq!(
&header_error(),
FromSliceError::from(linux_sll::HeaderSliceError::Content(header_error()))
.linux_sll()
.unwrap()
);
}

// double vlan errors
{
let header_error = || double_vlan::HeaderError::NonVlanEtherType {
Expand Down
5 changes: 1 addition & 4 deletions etherparse/src/err/ip/headers_read_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,7 @@ impl std::error::Error for HeaderReadError {
#[cfg(all(test, feature = "std"))]
mod test {
use super::{super::HeaderError::*, super::HeadersError::*, HeaderReadError::*, *};
use crate::{
err::{Layer, LenError},
LenSource,
};
use crate::{err::Layer, LenSource};
use alloc::format;

#[test]
Expand Down
5 changes: 1 addition & 4 deletions etherparse/src/err/ip/headers_slice_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ mod tests {
HeadersSliceError::*,
*,
};
use crate::{
err::{Layer, LenError},
LenSource,
};
use crate::{err::Layer, LenSource};
use alloc::format;
use std::{
collections::hash_map::DefaultHasher,
Expand Down
5 changes: 1 addition & 4 deletions etherparse/src/err/ip/lax_header_slice_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ impl std::error::Error for LaxHeaderSliceError {
#[cfg(test)]
mod tests {
use super::{super::HeaderError::*, LaxHeaderSliceError::*, *};
use crate::{
err::{Layer, LenError},
LenSource,
};
use crate::{err::Layer, LenSource};
use alloc::format;
use std::{
collections::hash_map::DefaultHasher,
Expand Down
5 changes: 1 addition & 4 deletions etherparse/src/err/ip_auth/header_slice_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,7 @@ impl std::error::Error for HeaderSliceError {
#[cfg(test)]
mod tests {
use super::{HeaderSliceError::*, *};
use crate::{
err::{Layer, LenError},
LenSource,
};
use crate::{err::Layer, LenSource};
use alloc::format;
use std::{
collections::hash_map::DefaultHasher,
Expand Down
2 changes: 1 addition & 1 deletion etherparse/src/err/ip_exts/headers_slice_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl std::error::Error for HeadersSliceError {
mod tests {
use super::{HeadersSliceError::*, *};
use crate::{
err::{ipv6_exts::HeaderError::*, Layer, LenError},
err::{ipv6_exts::HeaderError::*, Layer},
LenSource,
};
use alloc::format;
Expand Down
Loading

0 comments on commit b53002c

Please sign in to comment.