blob: 63c73825633d5eaac72e7695c30ecd61c88d0089 [file] [log] [blame] [edit]
use crate::*;
/// Packet slice split into multiple slices containing the different headers & payload.
///
/// Everything that could not be parsed is stored in a slice in the field "payload".
///
/// You can use
///
/// * [`SlicedPacket::from_ethernet`]
/// * [`SlicedPacket::from_ether_type`]
/// * [`SlicedPacket::from_ip`]
///
/// depending on your starting header to slice a packet.
///
/// # Examples
///
/// Basic usage:
///
///```
/// # use etherparse::{SlicedPacket, PacketBuilder};
/// # let builder = PacketBuilder::
/// # ethernet2([1,2,3,4,5,6], //source mac
/// # [7,8,9,10,11,12]) //destination mac
/// # .ipv4([192,168,1,1], //source ip
/// # [192,168,1,2], //destination ip
/// # 20) //time to life
/// # .udp(21, //source port
/// # 1234); // destination port
/// # //payload of the udp packet
/// # let payload = [1,2,3,4,5,6,7,8];
/// # //get some memory to store the serialized data
/// # let mut packet = Vec::<u8>::with_capacity(
/// # builder.size(payload.len()));
/// # builder.write(&mut packet, &payload).unwrap();
/// match SlicedPacket::from_ethernet(&packet) {
/// Err(value) => println!("Err {:?}", value),
/// Ok(value) => {
/// println!("link: {:?}", value.link);
/// println!("vlan: {:?}", value.vlan);
/// println!("net: {:?}", value.net);
/// println!("transport: {:?}", value.transport);
/// }
/// }
/// ```
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SlicedPacket<'a> {
/// Ethernet II header if present.
pub link: Option<LinkSlice<'a>>,
/// Single or double vlan headers if present.
pub vlan: Option<VlanSlice<'a>>,
/// IPv4 or IPv6 header, IP extension headers & payload if present.
pub net: Option<NetSlice<'a>>,
/// TCP or UDP header & payload if present.
pub transport: Option<TransportSlice<'a>>,
}
impl<'a> SlicedPacket<'a> {
/// Separates a network packet slice into different slices containing the headers from the ethernet header downwards.
///
/// The result is returned as a [`SlicedPacket`] struct. This function assumes the given data starts
/// with an ethernet II header.
///
/// # Examples
///
/// Basic usage:
///
///```
/// # use etherparse::{SlicedPacket, PacketBuilder};
/// # let builder = PacketBuilder::
/// # ethernet2([1,2,3,4,5,6], //source mac
/// # [7,8,9,10,11,12]) //destination mac
/// # .ipv4([192,168,1,1], //source ip
/// # [192,168,1,2], //destination ip
/// # 20) //time to life
/// # .udp(21, //source port
/// # 1234); // destination port
/// # //payload of the udp packet
/// # let payload = [1,2,3,4,5,6,7,8];
/// # //get some memory to store the serialized data
/// # let mut packet = Vec::<u8>::with_capacity(
/// # builder.size(payload.len()));
/// # builder.write(&mut packet, &payload).unwrap();
/// match SlicedPacket::from_ethernet(&packet) {
/// Err(value) => println!("Err {:?}", value),
/// Ok(value) => {
/// println!("link: {:?}", value.link);
/// println!("vlan: {:?}", value.vlan);
/// println!("net: {:?}", value.net);
/// println!("transport: {:?}", value.transport);
/// }
/// }
/// ```
pub fn from_ethernet(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
SlicedPacketCursor::new(data).slice_ethernet2()
}
/// Separates a network packet slice into different slices containing the
/// headers from the Linux Cooked Capture v1 (SLL) header downwards.
///
/// The result is returned as a [`SlicedPacket`] struct. This function
/// assumes the given data starts with a Linux Cooked Capture v1 (SLL)
/// header.
///
/// # Examples
///
/// Basic usage:
///
///```
/// # use etherparse::{SlicedPacket, PacketBuilder, LinuxSllPacketType};
/// # let builder = PacketBuilder::
/// # linux_sll(LinuxSllPacketType::OTHERHOST, //packet type
/// # 6, //sender address valid length
/// # [1,2,3,4,5,6,0,0]) //sender address with padding
/// # .ipv4([192,168,1,1], //source ip
/// # [192,168,1,2], //destination ip
/// # 20) //time to life
/// # .udp(21, //source port
/// # 1234); //destination port
/// # //payload of the udp packet
/// # let payload = [1,2,3,4,5,6,7,8];
/// # //get some memory to store the serialized data
/// # let mut packet = Vec::<u8>::with_capacity(
/// # builder.size(payload.len()));
/// # builder.write(&mut packet, &payload).unwrap();
/// match SlicedPacket::from_linux_sll(&packet) {
/// Err(value) => println!("Err {:?}", value),
/// Ok(value) => {
/// println!("link: {:?}", value.link);
/// println!("vlan: {:?}", value.vlan);
/// println!("net: {:?}", value.net);
/// println!("transport: {:?}", value.transport);
/// }
/// }
/// ```
pub fn from_linux_sll(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
SlicedPacketCursor::new(data).slice_linux_sll()
}
/// Separates a network packet slice into different slices containing the headers using
/// the given `ether_type` number to identify the first header.
///
/// The result is returned as a [`SlicedPacket`] struct. Currently supported
/// ether type numbers are:
///
/// * `ether_type::IPV4`
/// * `ether_type::IPV6`
/// * `ether_type::VLAN_TAGGED_FRAME`
/// * `ether_type::PROVIDER_BRIDGING`
/// * `ether_type::VLAN_DOUBLE_TAGGED_FRAME`
///
/// If an unsupported ether type is given the given slice will be set as payload
/// and all other fields will be set to `None`.
///
/// # Example
///
/// Basic usage:
///
///```
/// # use etherparse::{Ethernet2Header, PacketBuilder};
/// # let builder = PacketBuilder::
/// # ethernet2([1,2,3,4,5,6], //source mac
/// # [7,8,9,10,11,12]) //destination mac
/// # .ipv4([192,168,1,1], //source ip
/// # [192,168,1,2], //destination ip
/// # 20) //time to life
/// # .udp(21, //source port
/// # 1234); // destination port
/// # // payload of the udp packet
/// # let payload = [1,2,3,4,5,6,7,8];
/// # // get some memory to store the serialized data
/// # let mut complete_packet = Vec::<u8>::with_capacity(
/// # builder.size(payload.len())
/// # );
/// # builder.write(&mut complete_packet, &payload).unwrap();
/// #
/// # // skip ethernet 2 header so we can parse from there downwards
/// # let packet = &complete_packet[Ethernet2Header::LEN..];
/// #
/// use etherparse::{ether_type, SlicedPacket};
///
/// match SlicedPacket::from_ether_type(ether_type::IPV4, packet) {
/// Err(value) => println!("Err {:?}", value),
/// Ok(value) => {
/// println!("link: {:?}", value.link);
/// println!("vlan: {:?}", value.vlan);
/// println!("net: {:?}", value.net);
/// println!("transport: {:?}", value.transport);
/// }
/// }
/// ```
pub fn from_ether_type(
ether_type: EtherType,
data: &'a [u8],
) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
use ether_type::*;
let mut cursor = SlicedPacketCursor::new(data);
cursor.result.link = Some(LinkSlice::EtherPayload(EtherPayloadSlice {
ether_type,
payload: data,
}));
match ether_type {
IPV4 => cursor.slice_ipv4(),
IPV6 => cursor.slice_ipv6(),
VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => cursor.slice_vlan(),
_ => Ok(cursor.result),
}
}
/// Separates a network packet slice into different slices containing the headers from the ip header downwards.
///
/// The result is returned as a [`SlicedPacket`] struct. This function assumes the given data starts
/// with an IPv4 or IPv6 header.
///
/// # Examples
///
/// Basic usage:
///
///```
/// # use etherparse::{SlicedPacket, PacketBuilder};
/// # let builder = PacketBuilder::
/// # ipv4([192,168,1,1], //source ip
/// # [192,168,1,2], //destination ip
/// # 20) //time to life
/// # .udp(21, //source port
/// # 1234); // destination port
/// # //payload of the udp packet
/// # let payload = [1,2,3,4,5,6,7,8];
/// # //get some memory to store the serialized data
/// # let mut packet = Vec::<u8>::with_capacity(
/// # builder.size(payload.len()));
/// # builder.write(&mut packet, &payload).unwrap();
/// match SlicedPacket::from_ip(&packet) {
/// Err(value) => println!("Err {:?}", value),
/// Ok(value) => {
/// //link & vlan fields are empty when parsing from ip downwards
/// assert_eq!(None, value.link);
/// assert_eq!(None, value.vlan);
///
/// //ip & transport (udp or tcp)
/// println!("net: {:?}", value.net);
/// println!("transport: {:?}", value.transport);
/// }
/// }
/// ```
pub fn from_ip(data: &'a [u8]) -> Result<SlicedPacket<'a>, err::packet::SliceError> {
SlicedPacketCursor::new(data).slice_ip()
}
/// If the slice in the `payload` field contains an ethernet payload
/// this method returns the ether type number describing the payload type.
///
/// The ether type number can come from an ethernet II header or a
/// VLAN header depending on which headers are present.
///
/// In case that `ip` and/or `transport` fields are the filled None
/// is returned, as the payload contents then are defined by a
/// lower layer protocol described in these fields.
pub fn payload_ether_type(&self) -> Option<EtherType> {
if self.net.is_some() || self.transport.is_some() {
None
} else if let Some(vlan) = &self.vlan {
use VlanSlice::*;
match vlan {
SingleVlan(s) => Some(s.ether_type()),
DoubleVlan(d) => Some(d.inner().ether_type()),
}
} else if let Some(link) = &self.link {
use LinkSlice::*;
match link {
Ethernet2(eth) => Some(eth.ether_type()),
LinkSlice::LinuxSll(e) => match e.protocol_type() {
LinuxSllProtocolType::EtherType(EtherType(v))
| LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(
v,
)) => Some(EtherType(v)),
_ => None,
},
EtherPayload(e) => Some(e.ether_type),
LinkSlice::LinuxSllPayload(e) => match e.protocol_type {
LinuxSllProtocolType::EtherType(EtherType(v))
| LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(
v,
)) => Some(EtherType(v)),
_ => None,
},
}
} else {
None
}
}
/// Returns the last ether payload of the packet (if one is present).
///
/// If VLAN header is present the payload after the most inner VLAN
/// header is returned and if there is no VLAN header is present in the
/// link field is returned.
pub fn ether_payload(&self) -> Option<EtherPayloadSlice<'a>> {
if let Some(vlan) = self.vlan.as_ref() {
match vlan {
VlanSlice::SingleVlan(s) => Some(s.payload()),
VlanSlice::DoubleVlan(s) => Some(s.payload()),
}
} else if let Some(link) = self.link.as_ref() {
match link {
LinkSlice::Ethernet2(e) => Some(e.payload()),
LinkSlice::LinuxSll(e) => match e.protocol_type() {
LinuxSllProtocolType::EtherType(_)
| LinuxSllProtocolType::LinuxNonstandardEtherType(_) => {
Some(EtherPayloadSlice::try_from(e.payload()).ok()?)
}
_ => None,
},
LinkSlice::EtherPayload(e) => Some(e.clone()),
LinkSlice::LinuxSllPayload(e) => match e.protocol_type {
LinuxSllProtocolType::EtherType(_)
| LinuxSllProtocolType::LinuxNonstandardEtherType(_) => {
Some(EtherPayloadSlice::try_from(e.clone()).ok()?)
}
_ => None,
},
}
} else {
None
}
}
/// Return the IP payload after the the IP header and the IP extension
/// headers (if one is present).
pub fn ip_payload(&self) -> Option<&IpPayloadSlice<'a>> {
if let Some(net) = self.net.as_ref() {
use NetSlice::*;
match net {
Ipv4(v) => Some(v.payload()),
Ipv6(v) => Some(v.payload()),
}
} else {
None
}
}
/// Returns true if `net` contains an fragmented IPv4 or IPv6 payload.
pub fn is_ip_payload_fragmented(&self) -> bool {
use NetSlice::*;
match &self.net {
Some(Ipv4(v)) => v.is_payload_fragmented(),
Some(Ipv6(v)) => v.is_payload_fragmented(),
None => false,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::err::{packet::SliceError, Layer, LenError};
use crate::test_gens::*;
use crate::test_packet::TestPacket;
use proptest::prelude::*;
const VLAN_ETHER_TYPES: [EtherType; 3] = [
ether_type::VLAN_TAGGED_FRAME,
ether_type::PROVIDER_BRIDGING,
ether_type::VLAN_DOUBLE_TAGGED_FRAME,
];
#[test]
fn clone_eq() {
let header = SlicedPacket {
link: None,
vlan: None,
net: None,
transport: None,
};
assert_eq!(header.clone(), header);
}
#[test]
fn debug() {
use alloc::format;
let header = SlicedPacket {
link: None,
vlan: None,
net: None,
transport: None,
};
assert_eq!(
format!("{:?}", header),
format!(
"SlicedPacket {{ link: {:?}, vlan: {:?}, net: {:?}, transport: {:?} }}",
header.link, header.vlan, header.net, header.transport,
)
);
}
#[test]
fn ether_payload() {
use alloc::vec::*;
// no content
assert_eq!(
SlicedPacket {
link: None,
vlan: None,
net: None,
transport: None,
}
.ether_payload(),
None
);
// only ethernet header II
{
let payload = [1, 2, 3, 4];
let mut buf = Vec::with_capacity(Ethernet2Header::LEN + 4);
buf.extend_from_slice(
&Ethernet2Header {
ether_type: EtherType::WAKE_ON_LAN,
..Default::default()
}
.to_bytes(),
);
buf.extend_from_slice(&payload);
assert_eq!(
SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(),
Some(EtherPayloadSlice {
ether_type: EtherType::WAKE_ON_LAN,
payload: &payload
})
);
}
// ether type payload
{
let payload = [1, 2, 3, 4];
assert_eq!(
SlicedPacket {
link: Some(LinkSlice::EtherPayload(EtherPayloadSlice {
ether_type: EtherType::WAKE_ON_LAN,
payload: &payload
})),
vlan: None,
net: None,
transport: None,
}
.ether_payload(),
Some(EtherPayloadSlice {
ether_type: EtherType::WAKE_ON_LAN,
payload: &payload
})
);
}
// only linux_sll payload
{
let payload = [1, 2, 3, 4];
let mut buf = Vec::with_capacity(LinuxSllHeader::LEN + 4);
buf.extend_from_slice(
&LinuxSllHeader {
packet_type: LinuxSllPacketType::HOST,
arp_hrd_type: ArpHardwareId::ETHER,
sender_address_valid_length: 6,
sender_address: [1, 2, 3, 4, 5, 6, 0, 0],
protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN),
}
.to_bytes(),
);
buf.extend_from_slice(&payload);
assert_eq!(
SlicedPacket::from_linux_sll(&buf).unwrap().ether_payload(),
Some(EtherPayloadSlice {
ether_type: EtherType::WAKE_ON_LAN,
payload: &payload
})
);
}
// ether type payload
{
let payload = [1, 2, 3, 4];
assert_eq!(
SlicedPacket {
link: Some(LinkSlice::LinuxSllPayload(LinuxSllPayloadSlice {
protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN),
payload: &payload
})),
vlan: None,
net: None,
transport: None,
}
.ether_payload(),
Some(EtherPayloadSlice {
ether_type: EtherType::WAKE_ON_LAN,
payload: &payload
})
);
}
// single vlan header
{
let payload = [1, 2, 3, 4];
let mut buf = Vec::with_capacity(Ethernet2Header::LEN + SingleVlanHeader::LEN + 4);
buf.extend_from_slice(
&Ethernet2Header {
ether_type: EtherType::VLAN_TAGGED_FRAME,
..Default::default()
}
.to_bytes(),
);
buf.extend_from_slice(
&SingleVlanHeader {
ether_type: EtherType::WAKE_ON_LAN,
..Default::default()
}
.to_bytes(),
);
buf.extend_from_slice(&payload);
assert_eq!(
SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(),
Some(EtherPayloadSlice {
ether_type: EtherType::WAKE_ON_LAN,
payload: &payload
})
);
}
// double vlan header
{
let payload = [1, 2, 3, 4];
let mut buf = Vec::with_capacity(Ethernet2Header::LEN + SingleVlanHeader::LEN * 2 + 4);
buf.extend_from_slice(
&Ethernet2Header {
ether_type: EtherType::VLAN_DOUBLE_TAGGED_FRAME,
..Default::default()
}
.to_bytes(),
);
buf.extend_from_slice(
&SingleVlanHeader {
ether_type: EtherType::VLAN_TAGGED_FRAME,
..Default::default()
}
.to_bytes(),
);
buf.extend_from_slice(
&SingleVlanHeader {
ether_type: EtherType::WAKE_ON_LAN,
..Default::default()
}
.to_bytes(),
);
buf.extend_from_slice(&payload);
assert_eq!(
SlicedPacket::from_ethernet(&buf).unwrap().ether_payload(),
Some(EtherPayloadSlice {
ether_type: EtherType::WAKE_ON_LAN,
payload: &payload
})
);
}
}
#[test]
fn ip_payload() {
use alloc::vec::*;
// no content
assert_eq!(
SlicedPacket {
link: None,
vlan: None,
net: None,
transport: None,
}
.ip_payload(),
None
);
// ipv4
{
let payload = [1, 2, 3, 4];
let mut buf = Vec::with_capacity(Ipv4Header::MIN_LEN + 4);
buf.extend_from_slice(
&Ipv4Header {
protocol: IpNumber::ARIS,
total_len: Ipv4Header::MIN_LEN_U16 + 4,
..Default::default()
}
.to_bytes(),
);
buf.extend_from_slice(&payload);
assert_eq!(
SlicedPacket::from_ip(&buf).unwrap().ip_payload(),
Some(&IpPayloadSlice {
payload: &payload,
ip_number: IpNumber::ARIS,
fragmented: false,
len_source: LenSource::Ipv4HeaderTotalLen,
})
);
}
// ipv6
{
let payload = [1, 2, 3, 4];
let mut buf = Vec::with_capacity(Ipv6Header::LEN + 4);
buf.extend_from_slice(
&Ipv6Header {
payload_length: 4,
next_header: IpNumber::ARGUS,
..Default::default()
}
.to_bytes(),
);
buf.extend_from_slice(&payload);
assert_eq!(
SlicedPacket::from_ip(&buf).unwrap().ip_payload(),
Some(&IpPayloadSlice {
payload: &payload,
ip_number: IpNumber::ARGUS,
fragmented: false,
len_source: LenSource::Ipv6HeaderPayloadLen,
})
);
}
}
#[test]
fn from_x_slice() {
// no eth
from_x_slice_vlan_variants(&TestPacket {
link: None,
vlan: None,
net: None,
transport: None,
});
// eth payload
{
let data = [1, 2, 3, 4];
let result = SlicedPacket::from_ether_type(EtherType(0x8221), &data).unwrap();
assert_eq!(
result,
SlicedPacket {
link: Some(LinkSlice::EtherPayload(EtherPayloadSlice {
ether_type: EtherType(0x8221),
payload: &data
})),
vlan: None,
net: None,
transport: None
}
);
}
// eth
{
let eth = Ethernet2Header {
source: [1, 2, 3, 4, 5, 6],
destination: [1, 2, 3, 4, 5, 6],
ether_type: 0.into(),
};
let test = TestPacket {
link: Some(LinkHeader::Ethernet2(eth.clone())),
vlan: None,
net: None,
transport: None,
};
// ok ethernet header (with unknown next)
from_x_slice_vlan_variants(&test);
// eth len error
{
let data = test.to_vec(&[]);
for len in 0..data.len() {
let err = LenError {
required_len: eth.header_len(),
len,
len_source: LenSource::Slice,
layer: Layer::Ethernet2Header,
layer_start_offset: 0,
};
from_slice_assert_err(&test, &data[..len], SliceError::Len(err.clone()));
}
}
}
// linux_sll
{
let linux_sll = LinuxSllHeader {
packet_type: LinuxSllPacketType::HOST,
arp_hrd_type: ArpHardwareId::ETHER,
sender_address_valid_length: 6,
sender_address: [1, 2, 3, 4, 5, 6, 0, 0],
protocol_type: LinuxSllProtocolType::EtherType(EtherType::WAKE_ON_LAN),
};
let test = TestPacket {
link: Some(LinkHeader::LinuxSll(linux_sll.clone())),
vlan: None,
net: None,
transport: None,
};
// eth len error
{
let data = test.to_vec(&[]);
for len in 0..data.len() {
let err = LenError {
required_len: linux_sll.header_len(),
len,
len_source: LenSource::Slice,
layer: Layer::LinuxSllHeader,
layer_start_offset: 0,
};
from_slice_assert_err(&test, &data[..len], SliceError::Len(err.clone()));
}
}
}
}
fn from_x_slice_vlan_variants(base: &TestPacket) {
// none
from_x_slice_ip_variants(base);
// single vlan header
{
let single = SingleVlanHeader {
pcp: 1.try_into().unwrap(),
drop_eligible_indicator: false,
vlan_id: 2.try_into().unwrap(),
ether_type: 3.into(),
};
for vlan_ether_type in VLAN_ETHER_TYPES {
let mut test = base.clone();
test.set_ether_type(vlan_ether_type);
test.vlan = Some(VlanHeader::Single(single.clone()));
// ok vlan header
from_x_slice_ip_variants(&test);
// len error
{
let data = test.to_vec(&[]);
for len in 0..single.header_len() {
let base_len = test.len(&[]) - single.header_len();
let err = LenError {
required_len: single.header_len(),
len,
len_source: LenSource::Slice,
layer: Layer::VlanHeader,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
SliceError::Len(err.clone()),
);
}
}
}
}
// double vlan header
for outer_vlan_ether_type in VLAN_ETHER_TYPES {
for inner_vlan_ether_type in VLAN_ETHER_TYPES {
let double = DoubleVlanHeader {
outer: SingleVlanHeader {
pcp: 1.try_into().unwrap(),
drop_eligible_indicator: false,
vlan_id: 2.try_into().unwrap(),
ether_type: inner_vlan_ether_type,
},
inner: SingleVlanHeader {
pcp: 1.try_into().unwrap(),
drop_eligible_indicator: false,
vlan_id: 2.try_into().unwrap(),
ether_type: 3.into(),
},
};
let mut test = base.clone();
test.set_ether_type(outer_vlan_ether_type);
test.vlan = Some(VlanHeader::Double(double.clone()));
// ok double vlan header
from_x_slice_ip_variants(&test);
// len error
{
let data = test.to_vec(&[]);
for len in 0..SingleVlanHeader::LEN {
let base_len = test.len(&[]) - SingleVlanHeader::LEN;
let err = LenError {
required_len: SingleVlanHeader::LEN,
len,
len_source: LenSource::Slice,
layer: Layer::VlanHeader,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
SliceError::Len(err.clone()),
);
}
}
}
}
}
fn from_x_slice_ip_variants(base: &TestPacket) {
// none
from_x_slice_transport_variants(base);
// ipv4
for fragmented in [false, true] {
let ipv4 = {
let mut ipv4 =
Ipv4Header::new(0, 1, 2.into(), [3, 4, 5, 6], [7, 8, 9, 10]).unwrap();
ipv4.more_fragments = fragmented;
ipv4
};
{
let mut test = base.clone();
test.set_ether_type(ether_type::IPV4);
test.net = Some(NetHeaders::Ipv4(ipv4.clone(), Default::default()));
test.set_payload_len(0);
// ok ipv4
from_x_slice_transport_variants(&test);
// ipv4 len error
{
let data = test.to_vec(&[]);
for len in 0..ipv4.header_len() {
let base_len = test.len(&[]) - ipv4.header_len();
let err = LenError {
required_len: ipv4.header_len(),
len,
len_source: LenSource::Slice,
layer: Layer::Ipv4Header,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
if test.link.is_some() || test.vlan.is_some() {
SliceError::Len(err.clone())
} else {
SliceError::Len({
if len < 1 {
let mut err = err.clone();
err.required_len = 1;
err.layer = Layer::IpHeader;
err
} else {
err.clone()
}
})
},
);
}
}
// ipv4 content error (ihl length too small)
{
use err::ip::HeaderError::*;
let mut data = test.to_vec(&[]);
let ipv4_offset = data.len() - ipv4.header_len();
// set the ihl to 0 to trigger a content error
data[ipv4_offset] = 0b1111_0000 & data[ipv4_offset];
from_slice_assert_err(
&test,
&data,
if test.link.is_some() || test.vlan.is_some() {
SliceError::Ipv4(
err::ipv4::HeaderError::HeaderLengthSmallerThanHeader { ihl: 0 },
)
} else {
SliceError::Ip(Ipv4HeaderLengthSmallerThanHeader { ihl: 0 })
},
);
}
// ipv4 content error (total length too small)
{
let mut data = test.to_vec(&[]);
let ipv4_offset = data.len() - ipv4.header_len();
// set the total length to 0 to trigger a content error
data[ipv4_offset + 2] = 0;
data[ipv4_offset + 3] = 0;
let err = LenError {
required_len: ipv4.header_len(),
len: 0,
len_source: LenSource::Ipv4HeaderTotalLen,
layer: Layer::Ipv4Packet,
layer_start_offset: {
test.link.as_ref().map(|h| h.header_len()).unwrap_or(0)
+ test.vlan.as_ref().map(|h| h.header_len()).unwrap_or(0)
},
};
from_slice_assert_err(&test, &data, SliceError::Len(err.clone()));
}
}
// ipv4 extension content error
{
let auth = IpAuthHeader::new(0.into(), 1, 2, &[]).unwrap();
let mut test = base.clone();
test.set_ether_type(ether_type::IPV4);
test.net = Some(NetHeaders::Ipv4(
{
let mut ipv4 = ipv4.clone();
ipv4.protocol = ip_number::AUTH;
ipv4
},
Ipv4Extensions {
auth: Some(auth.clone()),
},
));
test.set_payload_len(0);
// ok ipv4 & extension
from_x_slice_transport_variants(&test);
// ipv4 extension len error
for len in 0..auth.header_len() {
// set payload length
let mut test = test.clone();
test.set_payload_le_from_ip_on(
-1 * (auth.header_len() as isize) + (len as isize),
);
let data = test.to_vec(&[]);
let base_len = test.len(&[]) - auth.header_len();
let err = LenError {
required_len: auth.header_len(),
len,
len_source: LenSource::Ipv4HeaderTotalLen,
layer: Layer::IpAuthHeader,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
SliceError::Len(err.clone()),
);
}
// ipv4 extension content error
{
let mut data = test.to_vec(&[]);
let auth_offset = data.len() - auth.header_len();
// set the icv len too smaller then allowed
data[auth_offset + 1] = 0;
// expect an error
let err = err::ip_auth::HeaderError::ZeroPayloadLen;
from_slice_assert_err(&test, &data, SliceError::Ipv4Exts(err.clone()));
}
}
}
// ipv6
{
let ipv6 = Ipv6Header {
traffic_class: 0,
flow_label: 1.try_into().unwrap(),
payload_length: 2,
next_header: 3.into(),
hop_limit: 4,
source: [0; 16],
destination: [0; 16],
};
// ipv6 header only
{
let mut test = base.clone();
test.set_ether_type(ether_type::IPV6);
test.net = Some(NetHeaders::Ipv6(ipv6.clone(), Default::default()));
test.set_payload_len(0);
// ok ipv6
from_x_slice_transport_variants(&test);
// header len ipv6
{
let data = test.to_vec(&[]);
for len in 0..ipv6.header_len() {
let base_len = test.len(&[]) - ipv6.header_len();
let err = err::LenError {
required_len: ipv6.header_len(),
len,
len_source: LenSource::Slice,
layer: Layer::Ipv6Header,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
if test.link.is_some() || test.vlan.is_some() {
SliceError::Len(err.clone())
} else {
SliceError::Len({
if len < 1 {
let mut err = err.clone();
err.required_len = 1;
err.layer = Layer::IpHeader;
err
} else {
err.clone()
}
})
},
);
}
}
// content error ipv6
{
use err::ip::HeaderError::*;
let mut data = test.to_vec(&[]);
// inject an invalid ip version
let base_len = data.len() - ipv6.header_len();
data[base_len] = data[base_len] & 0b0000_1111;
from_slice_assert_err(
&test,
&data,
if test.link.is_some() || test.vlan.is_some() {
SliceError::Ipv6(err::ipv6::HeaderError::UnexpectedVersion {
version_number: 0,
})
} else {
SliceError::Ip(UnsupportedIpVersion { version_number: 0 })
},
);
}
}
// ipv6 + extension
for fragment in [false, true] {
let auth = IpAuthHeader::new(ip_number::GGP, 1, 2, &[]).unwrap();
let frag = Ipv6FragmentHeader {
next_header: ip_number::AUTH,
fragment_offset: 0.try_into().unwrap(),
more_fragments: fragment,
identification: 3,
};
let mut test = base.clone();
test.set_ether_type(ether_type::IPV6);
test.net = Some(NetHeaders::Ipv6(
{
let mut ipv6 = ipv6.clone();
ipv6.next_header = ip_number::IPV6_FRAG;
ipv6
},
{
let mut exts: Ipv6Extensions = Default::default();
exts.fragment = Some(frag.clone());
exts.auth = Some(auth.clone());
exts
},
));
test.set_payload_len(0);
// ok ipv6 & extensions
from_x_slice_transport_variants(&test);
// ipv6 extension len error
for len in 0..auth.header_len() {
// set payload length
let mut test = test.clone();
test.set_payload_le_from_ip_on(
-1 * (auth.header_len() as isize) + (len as isize),
);
let data = test.to_vec(&[]);
let base_len = test.len(&[]) - auth.header_len();
let err = LenError {
required_len: auth.header_len(),
len,
len_source: LenSource::Ipv6HeaderPayloadLen,
layer: Layer::IpAuthHeader,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
SliceError::Len(err.clone()),
);
}
// ipv6 extension content error (auth)
{
let mut data = test.to_vec(&[]);
let auth_offset = data.len() - auth.header_len();
// set the icv len too smaller then allowed
data[auth_offset + 1] = 0;
let err = err::ip_auth::HeaderError::ZeroPayloadLen;
from_slice_assert_err(
&test,
&data,
SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::IpAuth(err.clone())),
);
}
// ipv6 extension content error (hop by hop not at start)
{
let mut data = test.to_vec(&[]);
let auth_offset = data.len() - auth.header_len();
// set the next header to be a hop-by-hop header to trigger a "not at start error"
data[auth_offset] = 0;
from_slice_assert_err(
&test,
&data,
SliceError::Ipv6Exts(err::ipv6_exts::HeaderError::HopByHopNotAtStart),
);
}
}
}
}
fn from_x_slice_transport_variants(base: &TestPacket) {
// none
from_x_slice_assert_ok(base);
// transport can only be set if ip is present
if let Some(ip) = &base.net {
// udp
{
let udp = UdpHeader {
source_port: 1,
destination_port: 2,
length: 3,
checksum: 4,
};
let mut test = base.clone();
test.net = Some({
let mut ip = match ip {
NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
};
ip.set_next_headers(ip_number::UDP);
ip.into()
});
test.transport = Some(TransportHeader::Udp(udp.clone()));
test.set_payload_len(0);
// ok decode
from_x_slice_assert_ok(&test);
// length error
if false == test.is_ip_payload_fragmented() {
for len in 0..udp.header_len() {
// build new test packet
let mut test = test.clone();
// set payload length
test.set_payload_le_from_ip_on(len as isize);
// generate data
let data = test.to_vec(&[]);
let base_len = test.len(&[]) - udp.header_len();
let err = LenError {
required_len: udp.header_len(),
len,
len_source: match test.net.as_ref().unwrap() {
NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
},
layer: Layer::UdpHeader,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
SliceError::Len(err.clone()),
);
}
}
}
// tcp
{
let tcp = TcpHeader::new(1, 2, 3, 4);
let mut test = base.clone();
test.net = Some({
let mut ip = match ip {
NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
};
ip.set_next_headers(ip_number::TCP);
ip.into()
});
test.transport = Some(TransportHeader::Tcp(tcp.clone()));
test.set_payload_len(0);
// ok decode
from_x_slice_assert_ok(&test);
// error can only occur if ip does not fragment the packet
if false == test.is_ip_payload_fragmented() {
// length error
{
for len in 0..(tcp.header_len() as usize) {
// set payload length
let mut test = test.clone();
test.set_payload_le_from_ip_on(len as isize);
let data = test.to_vec(&[]);
let base_len = test.len(&[]) - (tcp.header_len() as usize);
let err = LenError {
required_len: tcp.header_len() as usize,
len,
len_source: match test.net.as_ref().unwrap() {
NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
},
layer: Layer::TcpHeader,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
SliceError::Len(err.clone()),
);
}
}
// content error
{
let mut data = test.to_vec(&[]);
let base_len = test.len(&[]) - (tcp.header_len() as usize);
// set data offset to 0 to trigger an error
data[base_len + 12] = data[base_len + 12] & 0b0000_1111;
let err = err::tcp::HeaderError::DataOffsetTooSmall { data_offset: 0 };
from_slice_assert_err(&test, &data, SliceError::Tcp(err.clone()));
}
}
}
// icmpv4
{
let icmpv4 =
Icmpv4Header::new(Icmpv4Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
let mut test = base.clone();
test.net = Some({
let mut ip = match ip {
NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
};
ip.set_next_headers(ip_number::ICMP);
ip.into()
});
test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone()));
test.set_payload_len(0);
// ok decode
from_x_slice_assert_ok(&test);
// length error
if false == test.is_ip_payload_fragmented() {
for len in 0..icmpv4.header_len() {
// set payload length
let mut test = test.clone();
test.set_payload_le_from_ip_on(len as isize);
let data = test.to_vec(&[]);
let base_len = test.len(&[]) - icmpv4.header_len();
let err = LenError {
required_len: icmpv4.header_len(),
len,
len_source: match test.net.as_ref().unwrap() {
NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
},
layer: Layer::Icmpv4,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
SliceError::Len(err.clone()),
);
}
}
}
// icmpv6
{
let icmpv6 =
Icmpv6Header::new(Icmpv6Type::EchoReply(IcmpEchoHeader { id: 1, seq: 2 }));
let mut test = base.clone();
test.net = Some({
let mut ip = match ip {
NetHeaders::Ipv4(h, e) => IpHeaders::Ipv4(h.clone(), e.clone()),
NetHeaders::Ipv6(h, e) => IpHeaders::Ipv6(h.clone(), e.clone()),
};
ip.set_next_headers(ip_number::IPV6_ICMP);
ip.into()
});
test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone()));
test.set_payload_len(0);
// ok decode
from_x_slice_assert_ok(&test);
// length error
if false == test.is_ip_payload_fragmented() {
for len in 0..icmpv6.header_len() {
// set payload length
let mut test = test.clone();
test.set_payload_le_from_ip_on(len as isize);
let data = test.to_vec(&[]);
let base_len = test.len(&[]) - icmpv6.header_len();
let err = LenError {
required_len: icmpv6.header_len(),
len,
len_source: match test.net.as_ref().unwrap() {
NetHeaders::Ipv4(_, _) => LenSource::Ipv4HeaderTotalLen,
NetHeaders::Ipv6(_, _) => LenSource::Ipv6HeaderPayloadLen,
},
layer: Layer::Icmpv6,
layer_start_offset: base_len,
};
from_slice_assert_err(
&test,
&data[..base_len + len],
SliceError::Len(err.clone()),
);
}
}
}
}
}
fn from_x_slice_assert_ok(test_base: &TestPacket) {
fn assert_test_result(test: &TestPacket, expected_payload: &[u8], result: &SlicedPacket) {
// check if fragmenting
let is_fragmented = test.is_ip_payload_fragmented();
// check headers
assert_eq!(
test.link,
match result.link.as_ref() {
Some(s) => match s {
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,
}
);
assert_eq!(test.vlan, result.vlan.as_ref().map(|e| e.to_header()));
assert_eq!(
test.net,
result.net.as_ref().map(|s: &NetSlice| -> NetHeaders {
match s {
NetSlice::Ipv4(ipv4) => NetHeaders::Ipv4(
ipv4.header().to_header(),
ipv4.extensions().to_header(),
),
NetSlice::Ipv6(ipv6) => NetHeaders::Ipv6(
ipv6.header().to_header(),
Ipv6Extensions::from_slice(
ipv6.header().next_header(),
ipv6.extensions().slice(),
)
.unwrap()
.0,
),
}
})
);
// check transport header & payload
if is_fragmented {
assert_eq!(result.transport, None);
} else {
use TransportHeader as H;
use TransportSlice as S;
match &result.transport {
Some(S::Icmpv4(icmpv4)) => {
assert_eq!(&test.transport, &Some(H::Icmpv4(icmpv4.header())));
assert_eq!(icmpv4.payload(), expected_payload);
}
Some(S::Icmpv6(icmpv6)) => {
assert_eq!(&test.transport, &Some(H::Icmpv6(icmpv6.header())));
assert_eq!(icmpv6.payload(), expected_payload);
}
Some(S::Udp(s)) => {
assert_eq!(&test.transport, &Some(H::Udp(s.to_header())));
}
Some(S::Tcp(s)) => {
assert_eq!(&test.transport, &Some(H::Tcp(s.to_header())));
}
None => {
assert_eq!(&test.transport, &None);
}
}
}
}
// setup payload
let payload = [1, 2, 3, 4];
// set length fields in ip headers
let test = {
let mut test = test_base.clone();
test.set_payload_len(payload.len());
test
};
// write data
let data = test.to_vec(&payload);
// from_ethernet
if test.link.is_some() {
let result = SlicedPacket::from_ethernet(&data).unwrap();
assert_test_result(&test, &payload, &result);
}
// from_ether_type (vlan at start)
if test.link.is_none() && test.vlan.is_some() {
for ether_type in VLAN_ETHER_TYPES {
let result = SlicedPacket::from_ether_type(ether_type, &data).unwrap();
assert_eq!(
result.link,
Some(LinkSlice::EtherPayload(EtherPayloadSlice {
ether_type,
payload: &data
}))
);
assert_test_result(&test, &payload, &result);
}
}
// from_ether_type (ip at start)
if test.link.is_none() && test.vlan.is_none() {
if let Some(ip) = &test.net {
let ether_type = match ip {
NetHeaders::Ipv4(_, _) => ether_type::IPV4,
NetHeaders::Ipv6(_, _) => ether_type::IPV6,
};
let result = SlicedPacket::from_ether_type(ether_type, &data).unwrap();
assert_eq!(
result.link,
Some(LinkSlice::EtherPayload(EtherPayloadSlice {
ether_type,
payload: &data
}))
);
assert_test_result(&test, &payload, &result);
}
}
// from_ip_slice
if test.link.is_none() && test.vlan.is_none() && test.net.is_some() {
let result = SlicedPacket::from_ip(&data).unwrap();
assert_test_result(&test, &payload, &result);
}
}
/// Check that the given errors get triggered if presented with the given
/// data.
fn from_slice_assert_err(test: &TestPacket, data: &[u8], err: SliceError) {
// from_ethernet_slice
if let Some(ref header) = test.link {
match header {
LinkHeader::Ethernet2(_) => {
assert_eq!(err.clone(), SlicedPacket::from_ethernet(&data).unwrap_err())
}
LinkHeader::LinuxSll(_) => assert_eq!(
err.clone(),
SlicedPacket::from_linux_sll(&data).unwrap_err()
),
}
}
// from_ether_type (vlan at start)
if test.link.is_none() && test.vlan.is_some() {
for ether_type in VLAN_ETHER_TYPES {
assert_eq!(
err.clone(),
SlicedPacket::from_ether_type(ether_type, &data).unwrap_err()
);
}
}
// from_ether_type (ip at start)
if test.link.is_none() && test.vlan.is_none() {
if let Some(ip) = &test.net {
let err = SlicedPacket::from_ether_type(
match ip {
NetHeaders::Ipv4(_, _) => ether_type::IPV4,
NetHeaders::Ipv6(_, _) => ether_type::IPV6,
},
&data,
)
.unwrap_err();
assert_eq!(err, err.clone());
}
}
// from_ip_slice
if test.link.is_none() && test.vlan.is_none() && test.net.is_some() {
assert_eq!(err, SlicedPacket::from_ip(&data).unwrap_err());
}
}
proptest! {
#[test]
fn payload_ether_type(
ref eth in ethernet_2_unknown(),
ref linux_sll in linux_sll_any(),
ref vlan_outer in vlan_single_unknown(),
ref vlan_inner in vlan_single_unknown(),
ref ipv4 in ipv4_unknown(),
ref udp in udp_any(),
) {
use IpHeaders::*;
use alloc::vec::Vec;
// empty
{
let s = SlicedPacket{
link: None,
vlan: None,
net: None,
transport: None,
};
assert_eq!(None, s.payload_ether_type());
}
// only linux sll
{
let mut serialized = Vec::with_capacity(linux_sll.header_len());
eth.write(&mut serialized).unwrap();
let ether_type = match linux_sll.protocol_type {
LinuxSllProtocolType::EtherType(EtherType(v)) | LinuxSllProtocolType::LinuxNonstandardEtherType(LinuxNonstandardEtherType(v)) => Some(EtherType(v)),
_ => None,
};
if let Ok(s) = SlicedPacket::from_linux_sll(&serialized) {
assert_eq!(
ether_type,
s.payload_ether_type()
);
}
}
// only ethernet
{
let mut serialized = Vec::with_capacity(eth.header_len());
eth.write(&mut serialized).unwrap();
assert_eq!(
Some(eth.ether_type),
SlicedPacket::from_ethernet(&serialized)
.unwrap()
.payload_ether_type()
);
}
// with single vlan
{
let mut eth_mod = eth.clone();
eth_mod.ether_type = ether_type::VLAN_TAGGED_FRAME;
let mut serialized = Vec::with_capacity(
eth_mod.header_len() +
vlan_outer.header_len()
);
eth_mod.write(&mut serialized).unwrap();
vlan_outer.write(&mut serialized).unwrap();
assert_eq!(
Some(vlan_outer.ether_type),
SlicedPacket::from_ethernet(&serialized)
.unwrap()
.payload_ether_type()
);
}
// with double vlan
{
let mut eth_mod = eth.clone();
eth_mod.ether_type = ether_type::VLAN_TAGGED_FRAME;
let mut vlan_outer_mod = vlan_outer.clone();
vlan_outer_mod.ether_type = ether_type::VLAN_TAGGED_FRAME;
let mut serialized = Vec::with_capacity(
eth_mod.header_len() +
vlan_outer_mod.header_len() +
vlan_inner.header_len()
);
eth_mod.write(&mut serialized).unwrap();
vlan_outer_mod.write(&mut serialized).unwrap();
vlan_inner.write(&mut serialized).unwrap();
assert_eq!(
Some(vlan_inner.ether_type),
SlicedPacket::from_ethernet(&serialized)
.unwrap()
.payload_ether_type()
);
}
// with ip
{
let builder = PacketBuilder::ethernet2(eth.source, eth.destination)
.ip(Ipv4(ipv4.clone(), Default::default()));
let mut serialized = Vec::with_capacity(builder.size(0));
builder.write(&mut serialized, ipv4.protocol, &[]).unwrap();
assert_eq!(
None,
SlicedPacket::from_ethernet(&serialized)
.unwrap()
.payload_ether_type()
);
}
// with transport
{
let builder = PacketBuilder::ethernet2(eth.source, eth.destination)
.ip(Ipv4(ipv4.clone(), Default::default()))
.udp(udp.source_port, udp.destination_port);
let mut serialized = Vec::with_capacity(builder.size(0));
builder.write(&mut serialized, &[]).unwrap();
assert_eq!(
None,
SlicedPacket::from_ethernet(&serialized)
.unwrap()
.payload_ether_type()
);
}
}
}
}