use super::super::constants::*;
use super::super::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IcmpExtension {
version: Field<u8>,
reserved: Field<u16>,
checksum: Field<u16>,
}
impl IcmpExtension {
pub fn new() -> Self {
Self {
version: Field::defaulted(2),
reserved: Field::defaulted(0),
checksum: Field::unset(),
}
}
pub fn version(mut self, version: u8) -> Self {
self.version.set_user(version);
self
}
pub fn reserved(mut self, reserved: u16) -> Self {
self.reserved.set_user(reserved);
self
}
pub fn checksum(mut self, checksum: u16) -> Self {
self.checksum.set_user(checksum);
self
}
pub fn version_value(&self) -> u8 {
value_or_copy(&self.version, 2)
}
pub fn reserved_value(&self) -> u16 {
value_or_copy(&self.reserved, 0)
}
pub fn checksum_value(&self) -> Option<u16> {
self.checksum.value().copied()
}
fn validate(&self) -> Result<()> {
if self.version_value() > 0x0f {
return Err(CrafterError::invalid_field_value(
"icmp_extension.version",
"version must fit in four bits",
));
}
if self.reserved_value() > 0x0fff {
return Err(CrafterError::invalid_field_value(
"icmp_extension.reserved",
"reserved field must fit in 12 bits",
));
}
Ok(())
}
}
impl Default for IcmpExtension {
fn default() -> Self {
Self::new()
}
}
impl Layer for IcmpExtension {
fn name(&self) -> &'static str {
"IcmpExtension"
}
fn summary(&self) -> String {
format!("IcmpExtension(version={})", self.version_value())
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("version", self.version_value().to_string()),
("reserved", format!("0x{:03x}", self.reserved_value())),
(
"checksum",
self.checksum_value()
.map(|value| format!("0x{value:04x}"))
.unwrap_or_else(|| "auto".to_string()),
),
]
}
fn encoded_len(&self) -> usize {
ICMP_EXTENSION_HEADER_LEN
}
fn encoded_len_with_context(&self, ctx: &LayerContext<'_>) -> usize {
ICMP_EXTENSION_HEADER_LEN + extension_original_datagram_padding(*ctx)
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
self.validate()?;
let padding = extension_original_datagram_padding(*ctx);
out.resize(out.len() + padding, 0);
let mut header = Vec::with_capacity(ICMP_EXTENSION_HEADER_LEN);
let version_reserved =
((self.version_value() as u16) << 12) | (self.reserved_value() & 0x0fff);
header.extend_from_slice(&version_reserved.to_be_bytes());
header.extend_from_slice(&0u16.to_be_bytes());
let payload = payload_bytes_after(*ctx)?;
let checksum = self.checksum.value().copied().unwrap_or_else(|| {
let mut bytes = Vec::with_capacity(header.len() + payload.len());
bytes.extend_from_slice(&header);
bytes.extend_from_slice(&payload);
internet_checksum(&bytes)
});
header[2..4].copy_from_slice(&checksum.to_be_bytes());
out.extend_from_slice(&header);
Ok(())
}
impl_layer_object!(IcmpExtension);
}
impl_layer_div!(IcmpExtension);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IcmpExtensionObject {
length: Field<u16>,
class_num: Field<u8>,
c_type: Field<u8>,
}
impl IcmpExtensionObject {
pub fn new() -> Self {
Self {
length: Field::unset(),
class_num: Field::defaulted(0),
c_type: Field::defaulted(0),
}
}
pub fn length(mut self, length: u16) -> Self {
self.length.set_user(length);
self
}
pub fn class_num(mut self, class_num: u8) -> Self {
self.class_num.set_user(class_num);
self
}
pub fn c_type(mut self, c_type: u8) -> Self {
self.c_type.set_user(c_type);
self
}
pub fn length_value(&self) -> Option<u16> {
self.length.value().copied()
}
pub fn class_num_value(&self) -> u8 {
value_or_copy(&self.class_num, 0)
}
pub fn c_type_value(&self) -> u8 {
value_or_copy(&self.c_type, 0)
}
fn effective_length(&self, ctx: LayerContext<'_>) -> Result<u16> {
if let Some(length) = self.length.value().copied() {
return Ok(length);
}
u16::try_from(ICMP_EXTENSION_OBJECT_LEN + extension_object_payload_len(ctx)).map_err(|_| {
CrafterError::invalid_field_value(
"icmp_extension_object.length",
"extension object length exceeds 65535 bytes",
)
})
}
fn effective_class_num(&self, next: Option<&dyn Layer>) -> u8 {
if self.class_num.is_user_set() {
return self.class_num_value();
}
if next
.map(|layer| layer.as_any().is::<IcmpExtensionMpls>())
.unwrap_or(false)
{
ICMP_EXTENSION_CLASS_MPLS
} else if next
.map(|layer| layer.as_any().is::<IcmpExtensionInterfaceInfo>())
.unwrap_or(false)
{
ICMP_EXTENSION_CLASS_INTERFACE_INFO
} else if next
.map(|layer| layer.as_any().is::<IcmpExtensionInterfaceId>())
.unwrap_or(false)
{
ICMP_EXTENSION_CLASS_INTERFACE_ID
} else {
self.class_num_value()
}
}
fn effective_c_type(&self, next: Option<&dyn Layer>) -> u8 {
if self.c_type.is_user_set() {
return self.c_type_value();
}
if next
.map(|layer| layer.as_any().is::<IcmpExtensionMpls>())
.unwrap_or(false)
{
ICMP_EXTENSION_CTYPE_MPLS_INCOMING
} else if let Some(info) =
next.and_then(|layer| layer.as_any().downcast_ref::<IcmpExtensionInterfaceInfo>())
{
info.c_type_byte()
} else if let Some(id) =
next.and_then(|layer| layer.as_any().downcast_ref::<IcmpExtensionInterfaceId>())
{
id.c_type()
} else {
self.c_type_value()
}
}
}
impl Default for IcmpExtensionObject {
fn default() -> Self {
Self::new()
}
}
impl Layer for IcmpExtensionObject {
fn name(&self) -> &'static str {
"IcmpExtensionObject"
}
fn summary(&self) -> String {
format!(
"IcmpExtensionObject(class={}, ctype={})",
self.class_num_value(),
self.c_type_value()
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
(
"length",
self.length_value()
.map(|value| value.to_string())
.unwrap_or_else(|| "auto".to_string()),
),
("class_num", self.class_num_value().to_string()),
("c_type", self.c_type_value().to_string()),
]
}
fn encoded_len(&self) -> usize {
ICMP_EXTENSION_OBJECT_LEN
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
out.extend_from_slice(&self.effective_length(*ctx)?.to_be_bytes());
out.push(self.effective_class_num(ctx.next()));
out.push(self.effective_c_type(ctx.next()));
Ok(())
}
impl_layer_object!(IcmpExtensionObject);
}
impl_layer_div!(IcmpExtensionObject);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IcmpExtensionMpls {
label: Field<u32>,
experimental: Field<u8>,
bottom_of_stack: Field<bool>,
ttl: Field<u8>,
}
impl IcmpExtensionMpls {
pub fn new() -> Self {
Self {
label: Field::defaulted(0),
experimental: Field::defaulted(0),
bottom_of_stack: Field::unset(),
ttl: Field::defaulted(0),
}
}
pub fn label(mut self, label: u32) -> Self {
self.label.set_user(label);
self
}
pub fn experimental(mut self, experimental: u8) -> Self {
self.experimental.set_user(experimental);
self
}
pub fn exp(self, experimental: u8) -> Self {
self.experimental(experimental)
}
pub fn bottom_of_stack(mut self, bottom_of_stack: bool) -> Self {
self.bottom_of_stack.set_user(bottom_of_stack);
self
}
pub fn ttl(mut self, ttl: u8) -> Self {
self.ttl.set_user(ttl);
self
}
pub fn label_value(&self) -> u32 {
value_or_copy(&self.label, 0)
}
pub fn experimental_value(&self) -> u8 {
value_or_copy(&self.experimental, 0)
}
pub fn bottom_of_stack_value(&self) -> Option<bool> {
self.bottom_of_stack.value().copied()
}
pub fn ttl_value(&self) -> u8 {
value_or_copy(&self.ttl, 0)
}
fn effective_bottom_of_stack(&self, next: Option<&dyn Layer>) -> bool {
self.bottom_of_stack.value().copied().unwrap_or_else(|| {
!next
.map(|layer| layer.as_any().is::<IcmpExtensionMpls>())
.unwrap_or(false)
})
}
fn validate(&self) -> Result<()> {
if self.label_value() > MPLS_MAX_LABEL {
return Err(CrafterError::invalid_field_value(
"icmp_extension_mpls.label",
"MPLS label must fit in 20 bits",
));
}
if self.experimental_value() > MPLS_MAX_EXP {
return Err(CrafterError::invalid_field_value(
"icmp_extension_mpls.experimental",
"MPLS experimental field must fit in three bits",
));
}
Ok(())
}
}
impl Default for IcmpExtensionMpls {
fn default() -> Self {
Self::new()
}
}
impl Layer for IcmpExtensionMpls {
fn name(&self) -> &'static str {
"IcmpExtensionMpls"
}
fn summary(&self) -> String {
format!(
"IcmpExtensionMpls(label={}, ttl={})",
self.label_value(),
self.ttl_value()
)
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("label", self.label_value().to_string()),
("experimental", self.experimental_value().to_string()),
(
"bottom_of_stack",
self.bottom_of_stack_value()
.map(|value| value.to_string())
.unwrap_or_else(|| "auto".to_string()),
),
("ttl", self.ttl_value().to_string()),
]
}
fn encoded_len(&self) -> usize {
ICMP_EXTENSION_MPLS_LEN
}
fn compile(&self, ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
self.validate()?;
let word = (self.label_value() << 12)
| ((self.experimental_value() as u32) << 9)
| ((self.effective_bottom_of_stack(ctx.next()) as u32) << 8)
| self.ttl_value() as u32;
out.extend_from_slice(&word.to_be_bytes());
Ok(())
}
impl_layer_object!(IcmpExtensionMpls);
}
impl_layer_div!(IcmpExtensionMpls);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IcmpInterfaceIpAddress {
afi: Field<u16>,
reserved: Field<u16>,
address: Vec<u8>,
}
impl IcmpInterfaceIpAddress {
pub fn ipv4(address: Ipv4Addr) -> Self {
Self {
afi: Field::user(ICMP_INTERFACE_AFI_IPV4),
reserved: Field::defaulted(0),
address: address.octets().to_vec(),
}
}
pub fn ipv6(address: core::net::Ipv6Addr) -> Self {
Self {
afi: Field::user(ICMP_INTERFACE_AFI_IPV6),
reserved: Field::defaulted(0),
address: address.octets().to_vec(),
}
}
pub fn raw(afi: u16, address: impl Into<Vec<u8>>) -> Self {
Self {
afi: Field::user(afi),
reserved: Field::defaulted(0),
address: address.into(),
}
}
pub fn reserved(mut self, reserved: u16) -> Self {
self.reserved.set_user(reserved);
self
}
pub fn afi_value(&self) -> u16 {
value_or_copy(&self.afi, 0)
}
pub fn reserved_value(&self) -> u16 {
value_or_copy(&self.reserved, 0)
}
pub fn address_bytes(&self) -> &[u8] {
&self.address
}
pub fn ipv4_value(&self) -> Option<Ipv4Addr> {
if self.afi_value() == ICMP_INTERFACE_AFI_IPV4 && self.address.len() == 4 {
Some(Ipv4Addr::new(
self.address[0],
self.address[1],
self.address[2],
self.address[3],
))
} else {
None
}
}
pub fn ipv6_value(&self) -> Option<core::net::Ipv6Addr> {
if self.afi_value() == ICMP_INTERFACE_AFI_IPV6 && self.address.len() == 16 {
let mut octets = [0u8; 16];
octets.copy_from_slice(&self.address);
Some(core::net::Ipv6Addr::from(octets))
} else {
None
}
}
fn encoded_len(&self) -> usize {
ICMP_INTERFACE_IP_ADDRESS_PREFIX_LEN + self.address.len()
}
fn compile(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.afi_value().to_be_bytes());
out.extend_from_slice(&self.reserved_value().to_be_bytes());
out.extend_from_slice(&self.address);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IcmpExtensionInterfaceInfo {
role: Field<u8>,
reserved: Field<u8>,
if_index: Option<u32>,
ip_address: Option<IcmpInterfaceIpAddress>,
name: Option<Vec<u8>>,
mtu: Option<u32>,
}
impl IcmpExtensionInterfaceInfo {
pub fn new() -> Self {
Self {
role: Field::defaulted(ICMP_INTERFACE_ROLE_INCOMING),
reserved: Field::defaulted(0),
if_index: None,
ip_address: None,
name: None,
mtu: None,
}
}
pub fn role(mut self, role: u8) -> Self {
self.role.set_user(role);
self
}
pub fn reserved(mut self, reserved: u8) -> Self {
self.reserved.set_user(reserved);
self
}
pub fn if_index(mut self, if_index: u32) -> Self {
self.if_index = Some(if_index);
self
}
pub fn ip_address(mut self, ip_address: IcmpInterfaceIpAddress) -> Self {
self.ip_address = Some(ip_address);
self
}
pub fn name_bytes(mut self, name: impl Into<Vec<u8>>) -> Self {
self.name = Some(name.into());
self
}
pub fn name(self, name: &str) -> Self {
self.name_bytes(name.as_bytes().to_vec())
}
pub fn mtu(mut self, mtu: u32) -> Self {
self.mtu = Some(mtu);
self
}
pub fn role_value(&self) -> u8 {
value_or_copy(&self.role, ICMP_INTERFACE_ROLE_INCOMING)
}
pub fn reserved_value(&self) -> u8 {
value_or_copy(&self.reserved, 0)
}
pub fn if_index_value(&self) -> Option<u32> {
self.if_index
}
pub fn ip_address_value(&self) -> Option<&IcmpInterfaceIpAddress> {
self.ip_address.as_ref()
}
pub fn name_value(&self) -> Option<&[u8]> {
self.name.as_deref()
}
pub fn mtu_value(&self) -> Option<u32> {
self.mtu
}
pub fn c_type_byte(&self) -> u8 {
let mut byte = ((self.role_value() & 0x03) << 6) | ((self.reserved_value() & 0x03) << 4);
if self.if_index.is_some() {
byte |= ICMP_INTERFACE_CTYPE_IFINDEX;
}
if self.ip_address.is_some() {
byte |= ICMP_INTERFACE_CTYPE_IP_ADDRESS;
}
if self.name.is_some() {
byte |= ICMP_INTERFACE_CTYPE_NAME;
}
if self.mtu.is_some() {
byte |= ICMP_INTERFACE_CTYPE_MTU;
}
byte
}
fn name_encoded_len(&self) -> usize {
self.name
.as_ref()
.map(|name| {
let raw = 1 + name.len();
raw.div_ceil(4) * 4
})
.unwrap_or(0)
}
fn validate(&self) -> Result<()> {
if self.role_value() > 0x03 {
return Err(CrafterError::invalid_field_value(
"icmp_interface_info.role",
"interface role must fit in two bits",
));
}
if self.reserved_value() > 0x03 {
return Err(CrafterError::invalid_field_value(
"icmp_interface_info.reserved",
"interface reserved field must fit in two bits",
));
}
if let Some(name) = &self.name {
if name.len() > ICMP_INTERFACE_NAME_MAX {
return Err(CrafterError::invalid_field_value(
"icmp_interface_info.name",
"interface name must not exceed 63 octets",
));
}
}
Ok(())
}
}
impl Default for IcmpExtensionInterfaceInfo {
fn default() -> Self {
Self::new()
}
}
impl Layer for IcmpExtensionInterfaceInfo {
fn name(&self) -> &'static str {
"IcmpExtensionInterfaceInfo"
}
fn summary(&self) -> String {
let mut parts = vec![format!(
"role={}",
interface_role_summary(self.role_value())
)];
if let Some(if_index) = self.if_index {
parts.push(format!("ifindex={if_index}"));
}
if let Some(ip) = &self.ip_address {
let rendered = ip
.ipv4_value()
.map(|addr| addr.to_string())
.or_else(|| ip.ipv6_value().map(|addr| addr.to_string()))
.unwrap_or_else(|| {
format!("afi={} {}", ip.afi_value(), hex_bytes(ip.address_bytes()))
});
parts.push(format!("ip={rendered}"));
}
if let Some(name) = &self.name {
parts.push(format!("name={}", String::from_utf8_lossy(name)));
}
if let Some(mtu) = self.mtu {
parts.push(format!("mtu={mtu}"));
}
format!("IcmpExtensionInterfaceInfo({})", parts.join(", "))
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("role", interface_role_summary(self.role_value())),
("reserved", self.reserved_value().to_string()),
("c_type", format!("0x{:02x}", self.c_type_byte())),
(
"if_index",
self.if_index
.map(|value| value.to_string())
.unwrap_or_else(|| "-".to_string()),
),
(
"ip_address",
self.ip_address
.as_ref()
.map(|ip| {
ip.ipv4_value()
.map(|addr| addr.to_string())
.or_else(|| ip.ipv6_value().map(|addr| addr.to_string()))
.unwrap_or_else(|| {
format!("afi={} {}", ip.afi_value(), hex_bytes(ip.address_bytes()))
})
})
.unwrap_or_else(|| "-".to_string()),
),
(
"name",
self.name
.as_ref()
.map(|name| String::from_utf8_lossy(name).into_owned())
.unwrap_or_else(|| "-".to_string()),
),
(
"mtu",
self.mtu
.map(|value| value.to_string())
.unwrap_or_else(|| "-".to_string()),
),
]
}
fn encoded_len(&self) -> usize {
let mut len = 0;
if self.if_index.is_some() {
len += ICMP_INTERFACE_IFINDEX_LEN;
}
if let Some(ip) = &self.ip_address {
len += ip.encoded_len();
}
len += self.name_encoded_len();
if self.mtu.is_some() {
len += ICMP_INTERFACE_MTU_LEN;
}
len
}
fn compile(&self, _ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
self.validate()?;
if let Some(if_index) = self.if_index {
out.extend_from_slice(&if_index.to_be_bytes());
}
if let Some(ip) = &self.ip_address {
ip.compile(out);
}
if let Some(name) = &self.name {
let padded = self.name_encoded_len();
out.push((1 + name.len()) as u8);
out.extend_from_slice(name);
out.resize(out.len() + (padded - 1 - name.len()), 0);
}
if let Some(mtu) = self.mtu {
out.extend_from_slice(&mtu.to_be_bytes());
}
Ok(())
}
impl_layer_object!(IcmpExtensionInterfaceInfo);
}
impl_layer_div!(IcmpExtensionInterfaceInfo);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IcmpExtensionInterfaceId {
body: InterfaceIdBody,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum InterfaceIdBody {
Name(Vec<u8>),
Index(u32),
Address {
afi: u16,
address_length: u8,
reserved: u8,
address: Vec<u8>,
},
Raw { c_type: u8, bytes: Vec<u8> },
}
impl IcmpExtensionInterfaceId {
pub fn by_name(name: &str) -> Self {
Self::by_name_bytes(name.as_bytes().to_vec())
}
pub fn by_name_bytes(name: impl Into<Vec<u8>>) -> Self {
Self {
body: InterfaceIdBody::Name(name.into()),
}
}
pub fn by_index(if_index: u32) -> Self {
Self {
body: InterfaceIdBody::Index(if_index),
}
}
pub fn by_ipv4(address: Ipv4Addr) -> Self {
Self::by_address(ICMP_INTERFACE_AFI_IPV4, address.octets())
}
pub fn by_ipv6(address: core::net::Ipv6Addr) -> Self {
Self::by_address(ICMP_INTERFACE_AFI_IPV6, address.octets())
}
pub fn by_address(afi: u16, address: impl Into<Vec<u8>>) -> Self {
let address = address.into();
let address_length = address.len() as u8;
Self {
body: InterfaceIdBody::Address {
afi,
address_length,
reserved: 0,
address,
},
}
}
pub fn raw(c_type: u8, bytes: impl Into<Vec<u8>>) -> Self {
Self {
body: InterfaceIdBody::Raw {
c_type,
bytes: bytes.into(),
},
}
}
pub fn address_length(mut self, address_length: u8) -> Self {
if let InterfaceIdBody::Address {
address_length: slot,
..
} = &mut self.body
{
*slot = address_length;
}
self
}
pub fn reserved(mut self, reserved: u8) -> Self {
if let InterfaceIdBody::Address { reserved: slot, .. } = &mut self.body {
*slot = reserved;
}
self
}
pub fn c_type(&self) -> u8 {
match &self.body {
InterfaceIdBody::Name(_) => ICMP_INTERFACE_ID_CTYPE_NAME,
InterfaceIdBody::Index(_) => ICMP_INTERFACE_ID_CTYPE_INDEX,
InterfaceIdBody::Address { .. } => ICMP_INTERFACE_ID_CTYPE_ADDRESS,
InterfaceIdBody::Raw { c_type, .. } => *c_type,
}
}
pub fn name_value(&self) -> Option<&[u8]> {
match &self.body {
InterfaceIdBody::Name(name) => Some(name),
_ => None,
}
}
pub fn index_value(&self) -> Option<u32> {
match &self.body {
InterfaceIdBody::Index(index) => Some(*index),
_ => None,
}
}
pub fn afi_value(&self) -> Option<u16> {
match &self.body {
InterfaceIdBody::Address { afi, .. } => Some(*afi),
_ => None,
}
}
pub fn address_bytes(&self) -> Option<&[u8]> {
match &self.body {
InterfaceIdBody::Address { address, .. } => Some(address),
_ => None,
}
}
pub fn address_length_value(&self) -> Option<u8> {
match &self.body {
InterfaceIdBody::Address { address_length, .. } => Some(*address_length),
_ => None,
}
}
pub fn ipv4_value(&self) -> Option<Ipv4Addr> {
match &self.body {
InterfaceIdBody::Address { afi, address, .. }
if *afi == ICMP_INTERFACE_AFI_IPV4 && address.len() == 4 =>
{
Some(Ipv4Addr::new(
address[0], address[1], address[2], address[3],
))
}
_ => None,
}
}
pub fn ipv6_value(&self) -> Option<core::net::Ipv6Addr> {
match &self.body {
InterfaceIdBody::Address { afi, address, .. }
if *afi == ICMP_INTERFACE_AFI_IPV6 && address.len() == 16 =>
{
let mut octets = [0u8; 16];
octets.copy_from_slice(address);
Some(core::net::Ipv6Addr::from(octets))
}
_ => None,
}
}
pub fn raw_bytes(&self) -> Option<&[u8]> {
match &self.body {
InterfaceIdBody::Raw { bytes, .. } => Some(bytes),
_ => None,
}
}
}
impl Layer for IcmpExtensionInterfaceId {
fn name(&self) -> &'static str {
"IcmpExtensionInterfaceId"
}
fn summary(&self) -> String {
let detail = match &self.body {
InterfaceIdBody::Name(name) => {
format!("name={}", String::from_utf8_lossy(name))
}
InterfaceIdBody::Index(index) => format!("ifindex={index}"),
InterfaceIdBody::Address { afi, address, .. } => {
if let Some(addr) = self.ipv4_value() {
format!("address={addr}")
} else if let Some(addr) = self.ipv6_value() {
format!("address={addr}")
} else {
format!("address=afi={afi} {}", hex_bytes(address))
}
}
InterfaceIdBody::Raw { c_type, bytes } => {
format!("ctype={c_type} raw={}", hex_bytes(bytes))
}
};
format!("IcmpExtensionInterfaceId({detail})")
}
fn inspection_fields(&self) -> Vec<(&'static str, String)> {
vec![
("c_type", self.c_type().to_string()),
(
"name",
self.name_value()
.map(|name| String::from_utf8_lossy(name).into_owned())
.unwrap_or_else(|| "-".to_string()),
),
(
"index",
self.index_value()
.map(|value| value.to_string())
.unwrap_or_else(|| "-".to_string()),
),
(
"afi",
self.afi_value()
.map(|value| value.to_string())
.unwrap_or_else(|| "-".to_string()),
),
(
"address",
self.address_bytes()
.map(hex_bytes)
.unwrap_or_else(|| "-".to_string()),
),
]
}
fn encoded_len(&self) -> usize {
match &self.body {
InterfaceIdBody::Name(name) => name.len().div_ceil(4) * 4,
InterfaceIdBody::Index(_) => ICMP_INTERFACE_ID_INDEX_LEN,
InterfaceIdBody::Address { address, .. } => {
ICMP_INTERFACE_ID_ADDRESS_PREFIX_LEN + address.len().div_ceil(4) * 4
}
InterfaceIdBody::Raw { bytes, .. } => bytes.len(),
}
}
fn compile(&self, _ctx: &LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
match &self.body {
InterfaceIdBody::Name(name) => {
let padded = name.len().div_ceil(4) * 4;
out.extend_from_slice(name);
out.resize(out.len() + (padded - name.len()), 0);
}
InterfaceIdBody::Index(index) => {
out.extend_from_slice(&index.to_be_bytes());
}
InterfaceIdBody::Address {
afi,
address_length,
reserved,
address,
} => {
out.extend_from_slice(&afi.to_be_bytes());
out.push(*address_length);
out.push(*reserved);
let padded = address.len().div_ceil(4) * 4;
out.extend_from_slice(address);
out.resize(out.len() + (padded - address.len()), 0);
}
InterfaceIdBody::Raw { bytes, .. } => {
out.extend_from_slice(bytes);
}
}
Ok(())
}
impl_layer_object!(IcmpExtensionInterfaceId);
}
impl_layer_div!(IcmpExtensionInterfaceId);
pub(crate) fn decode_mpls_entry(chunk: &[u8]) -> IcmpExtensionMpls {
let word = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
let label = word >> 12;
let experimental = ((word >> 9) & 0x07) as u8;
let bottom_of_stack = (word >> 8) & 0x01 == 1;
let ttl = (word & 0xff) as u8;
IcmpExtensionMpls {
label: Field::user(label),
experimental: Field::user(experimental),
bottom_of_stack: Field::user(bottom_of_stack),
ttl: Field::user(ttl),
}
}
pub(crate) fn decode_interface_info(
c_type: u8,
mut body: &[u8],
) -> Option<IcmpExtensionInterfaceInfo> {
let role = (c_type >> 6) & 0x03;
let reserved = (c_type >> 4) & 0x03;
let mut info = IcmpExtensionInterfaceInfo {
role: Field::user(role),
reserved: Field::user(reserved),
if_index: None,
ip_address: None,
name: None,
mtu: None,
};
if c_type & ICMP_INTERFACE_CTYPE_IFINDEX != 0 {
if body.len() < ICMP_INTERFACE_IFINDEX_LEN {
return None;
}
info.if_index = Some(u32::from_be_bytes(copy_array_4(
&body[..ICMP_INTERFACE_IFINDEX_LEN],
)));
body = &body[ICMP_INTERFACE_IFINDEX_LEN..];
}
if c_type & ICMP_INTERFACE_CTYPE_IP_ADDRESS != 0 {
if body.len() < ICMP_INTERFACE_IP_ADDRESS_PREFIX_LEN {
return None;
}
let afi = u16::from_be_bytes([body[0], body[1]]);
let reserved16 = u16::from_be_bytes([body[2], body[3]]);
let addr_len = match afi {
ICMP_INTERFACE_AFI_IPV4 => 4,
ICMP_INTERFACE_AFI_IPV6 => 16,
_ => return None,
};
let total = ICMP_INTERFACE_IP_ADDRESS_PREFIX_LEN + addr_len;
if body.len() < total {
return None;
}
info.ip_address = Some(IcmpInterfaceIpAddress {
afi: Field::user(afi),
reserved: Field::user(reserved16),
address: body[ICMP_INTERFACE_IP_ADDRESS_PREFIX_LEN..total].to_vec(),
});
body = &body[total..];
}
if c_type & ICMP_INTERFACE_CTYPE_NAME != 0 {
if body.is_empty() {
return None;
}
let length = body[0] as usize;
if length < 1 || length > body.len() {
return None;
}
let name_len = length - 1;
if name_len > ICMP_INTERFACE_NAME_MAX {
return None;
}
let padded = length.div_ceil(4) * 4;
if padded > body.len() {
return None;
}
if body[length..padded].iter().any(|&byte| byte != 0) {
return None;
}
info.name = Some(body[1..length].to_vec());
body = &body[padded..];
}
if c_type & ICMP_INTERFACE_CTYPE_MTU != 0 {
if body.len() < ICMP_INTERFACE_MTU_LEN {
return None;
}
info.mtu = Some(u32::from_be_bytes(copy_array_4(
&body[..ICMP_INTERFACE_MTU_LEN],
)));
body = &body[ICMP_INTERFACE_MTU_LEN..];
}
if !body.is_empty() {
return None;
}
Some(info)
}
pub(crate) fn decode_interface_id(c_type: u8, body: &[u8]) -> Option<IcmpExtensionInterfaceId> {
match c_type {
ICMP_INTERFACE_ID_CTYPE_NAME => {
if body.is_empty() || body.len() % 4 != 0 {
return None;
}
let name_len = body
.iter()
.position(|&byte| byte == 0)
.unwrap_or(body.len());
if body[name_len..].iter().any(|&byte| byte != 0) {
return None;
}
if name_len.div_ceil(4) * 4 != body.len() {
return None;
}
Some(IcmpExtensionInterfaceId::by_name_bytes(
body[..name_len].to_vec(),
))
}
ICMP_INTERFACE_ID_CTYPE_INDEX => {
if body.len() != ICMP_INTERFACE_ID_INDEX_LEN {
return None;
}
Some(IcmpExtensionInterfaceId::by_index(u32::from_be_bytes(
copy_array_4(body),
)))
}
ICMP_INTERFACE_ID_CTYPE_ADDRESS => {
if body.len() < ICMP_INTERFACE_ID_ADDRESS_PREFIX_LEN {
return None;
}
let afi = u16::from_be_bytes([body[0], body[1]]);
let address_length = body[2];
let reserved = body[3];
let address = &body[ICMP_INTERFACE_ID_ADDRESS_PREFIX_LEN..];
let significant = address_length as usize;
if significant > address.len() || significant.div_ceil(4) * 4 != address.len() {
return None;
}
if address[significant..].iter().any(|&byte| byte != 0) {
return None;
}
Some(
IcmpExtensionInterfaceId::by_address(afi, address[..significant].to_vec())
.reserved(reserved),
)
}
_ => None,
}
}
#[cfg(test)]
mod icmpv4_rfc4884_extensions {
use super::{
IcmpExtension, IcmpExtensionMpls, IcmpExtensionObject, Icmpv4, Icmpv4QuotedIp,
ICMP_EXTENSION_HEADER_LEN, ICMP_PARAMETER_PROBLEM, ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM,
};
use crate::checksum::verify_internet_checksum;
use crate::{Ipv4, Ipv4Protocol, NetworkLayer, Packet, Raw, Udp};
use core::net::Ipv4Addr;
fn src() -> Ipv4Addr {
Ipv4Addr::new(192, 0, 2, 10)
}
fn dst() -> Ipv4Addr {
Ipv4Addr::new(198, 51, 100, 20)
}
fn quoted_udp() -> Packet {
Ipv4::new()
.src(Ipv4Addr::new(192, 0, 2, 1))
.dst(Ipv4Addr::new(198, 51, 100, 1))
.ipv4_protocol(Ipv4Protocol::Udp)
/ Udp::new().sport(40000).dport(53)
/ Raw::from("query")
}
const ICMP_BODY_START: usize = 28;
const RFC4884_LENGTH_BYTE: usize = 25;
#[test]
fn icmpv4_rfc4884_extensions_autofills_length_field() {
let bytes = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(1234).ttl(64))
.compile()
.unwrap();
assert_eq!(bytes.as_bytes()[RFC4884_LENGTH_BYTE], 32);
}
#[test]
fn icmpv4_rfc4884_extensions_length_is_zero_without_extensions() {
let bytes = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp()))
.compile()
.unwrap();
assert_eq!(bytes.as_bytes()[RFC4884_LENGTH_BYTE], 0);
}
#[test]
fn icmpv4_rfc4884_extensions_pads_minimum_original_datagram() {
let quote_len = quoted_udp().encoded_len();
assert!(quote_len < ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM);
let short = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::destination_unreachable()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new())
.compile()
.unwrap();
let ext_start = ICMP_BODY_START + ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM;
assert!(short.as_bytes()[ICMP_BODY_START + quote_len..ext_start]
.iter()
.all(|&byte| byte == 0));
assert_eq!(short.as_bytes()[ext_start] >> 4, 2);
assert_eq!(short.as_bytes()[RFC4884_LENGTH_BYTE], 32);
let long_quote = Ipv4::new()
.src(Ipv4Addr::new(192, 0, 2, 1))
.dst(Ipv4Addr::new(198, 51, 100, 1))
.ipv4_protocol(Ipv4Protocol::Udp)
/ Udp::new().sport(40000).dport(53)
/ Raw::from_bytes(vec![0xab; 105]); let long = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(long_quote)
/ IcmpExtension::new())
.compile()
.unwrap();
assert_eq!(long.as_bytes()[RFC4884_LENGTH_BYTE], 34);
let long_ext_start = ICMP_BODY_START + 136;
assert!(long.as_bytes()[ICMP_BODY_START + 133..long_ext_start]
.iter()
.all(|&byte| byte == 0));
assert_eq!(long.as_bytes()[long_ext_start] >> 4, 2);
}
#[test]
fn icmpv4_rfc4884_extensions_autofills_extension_checksum() {
let bytes = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(99).ttl(10))
.compile()
.unwrap();
let ext_start = ICMP_BODY_START + ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM;
assert!(verify_internet_checksum(&bytes.as_bytes()[ext_start..]));
assert_ne!(&bytes.as_bytes()[ext_start + 2..ext_start + 4], &[0, 0]);
}
#[test]
fn icmpv4_rfc4884_extensions_explicit_malformed_checksum_is_preserved() {
let bytes = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new().checksum(0xdead)
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(7).ttl(5))
.compile()
.unwrap();
let ext_start = ICMP_BODY_START + ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM;
assert_eq!(
&bytes.as_bytes()[ext_start + 2..ext_start + 4],
&0xdeadu16.to_be_bytes()
);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes.as_bytes()).unwrap();
assert!(decoded.layer::<IcmpExtension>().is_none());
assert!(decoded.layer::<Raw>().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes.as_bytes());
}
#[test]
fn icmpv4_rfc4884_extensions_generic_unknown_object_roundtrip() {
let payload = [0x11u8, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88];
let packet = Ipv4::new().src(src()).dst(dst())
/ Icmpv4::new().icmp_type(ICMP_PARAMETER_PROBLEM)
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new().class_num(200).c_type(7)
/ Raw::from_bytes(payload);
let compiled = packet.compile().unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let object = decoded.layer::<IcmpExtensionObject>().unwrap();
assert_eq!(object.length_value(), Some(12));
assert_eq!(object.class_num_value(), 200);
assert_eq!(object.c_type_value(), 7);
let raw = decoded.layer::<Raw>().unwrap();
assert_eq!(raw.as_bytes(), &payload);
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc4884_extensions_decode_splits_typed_layers() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(1234).ttl(100)
/ IcmpExtensionMpls::new().label(2345).ttl(50))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let icmp = decoded.layer::<Icmpv4>().unwrap();
assert_eq!(icmp.length_value(), Some(32));
let quoted = decoded.layer::<Icmpv4QuotedIp>().unwrap();
assert_eq!(
quoted.quoted_layer::<Ipv4>().unwrap().source(),
Ipv4Addr::new(192, 0, 2, 1)
);
let extension = decoded.layer::<IcmpExtension>().unwrap();
assert_eq!(extension.version_value(), 2);
assert_eq!(extension.checksum_value().map(|_| true), Some(true));
let object = decoded.layer::<IcmpExtensionObject>().unwrap();
assert_eq!(object.length_value(), Some(12));
assert_eq!(object.class_num_value(), 1);
assert_eq!(object.c_type_value(), 1);
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc4884_extensions_ambiguous_data_stays_raw() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(1).ttl(1))
.compile()
.unwrap();
let mut corrupt = compiled.as_bytes().to_vec();
let ext_start = ICMP_BODY_START + ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM;
corrupt[ext_start] = 0xf0 | (corrupt[ext_start] & 0x0f);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, &corrupt).unwrap();
assert!(decoded.layer::<IcmpExtension>().is_none());
assert!(decoded.layer::<IcmpExtensionObject>().is_none());
assert!(decoded.layer::<Icmpv4QuotedIp>().is_some());
let raw = decoded.layer::<Raw>().unwrap();
assert!(raw.as_bytes().len() >= ICMP_EXTENSION_HEADER_LEN);
assert_eq!(decoded.compile().unwrap().as_bytes(), &corrupt[..]);
}
}
#[cfg(test)]
mod icmpv4_rfc4950_mpls {
use super::{
IcmpExtension, IcmpExtensionMpls, IcmpExtensionObject, Icmpv4, Icmpv4QuotedIp,
ICMP_EXTENSION_CLASS_MPLS, ICMP_EXTENSION_CTYPE_MPLS_INCOMING,
ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM,
};
use crate::{Ipv4, Ipv4Protocol, NetworkLayer, Packet, Raw, Udp};
use core::net::Ipv4Addr;
fn src() -> Ipv4Addr {
Ipv4Addr::new(192, 0, 2, 10)
}
fn dst() -> Ipv4Addr {
Ipv4Addr::new(198, 51, 100, 20)
}
fn quoted_udp() -> Packet {
Ipv4::new()
.src(Ipv4Addr::new(192, 0, 2, 1))
.dst(Ipv4Addr::new(198, 51, 100, 1))
.ipv4_protocol(Ipv4Protocol::Udp)
/ Udp::new().sport(40000).dport(53)
/ Raw::from("query")
}
const EXT_HEADER_START: usize = 28 + ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM;
const OBJECT_HEADER_START: usize = EXT_HEADER_START + 4;
const FIRST_ENTRY_START: usize = OBJECT_HEADER_START + 4;
#[test]
fn icmpv4_rfc4950_mpls_single_label_encode_decode() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(0xabcde).exp(5).ttl(64))
.compile()
.unwrap();
assert_eq!(
&compiled.as_bytes()[OBJECT_HEADER_START..OBJECT_HEADER_START + 4],
&[
0x00,
0x08,
ICMP_EXTENSION_CLASS_MPLS,
ICMP_EXTENSION_CTYPE_MPLS_INCOMING
]
);
let expected = (0xabcdeu32 << 12) | (5u32 << 9) | (1u32 << 8) | 64u32;
assert_eq!(
&compiled.as_bytes()[FIRST_ENTRY_START..FIRST_ENTRY_START + 4],
&expected.to_be_bytes()
);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let mpls = decoded.layer::<IcmpExtensionMpls>().unwrap();
assert_eq!(mpls.label_value(), 0xabcde);
assert_eq!(mpls.experimental_value(), 5);
assert_eq!(mpls.ttl_value(), 64);
assert_eq!(mpls.bottom_of_stack_value(), Some(true));
let object = decoded.layer::<IcmpExtensionObject>().unwrap();
assert_eq!(object.length_value(), Some(8));
assert_eq!(object.class_num_value(), ICMP_EXTENSION_CLASS_MPLS);
assert_eq!(object.c_type_value(), ICMP_EXTENSION_CTYPE_MPLS_INCOMING);
}
#[test]
fn icmpv4_rfc4950_mpls_multi_label_encode_decode() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(100).exp(1).ttl(10)
/ IcmpExtensionMpls::new().label(200).exp(2).ttl(20)
/ IcmpExtensionMpls::new().label(300).exp(3).ttl(30))
.compile()
.unwrap();
let object = {
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
decoded
.layer::<IcmpExtensionObject>()
.unwrap()
.length_value()
};
assert_eq!(object, Some(16));
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let entries: Vec<&IcmpExtensionMpls> = decoded.layers::<IcmpExtensionMpls>().collect();
assert_eq!(entries.len(), 3);
assert_eq!(entries[0].label_value(), 100);
assert_eq!(entries[0].experimental_value(), 1);
assert_eq!(entries[0].ttl_value(), 10);
assert_eq!(entries[1].label_value(), 200);
assert_eq!(entries[2].label_value(), 300);
assert_eq!(entries[0].bottom_of_stack_value(), Some(false));
assert_eq!(entries[1].bottom_of_stack_value(), Some(false));
assert_eq!(entries[2].bottom_of_stack_value(), Some(true));
}
#[test]
fn icmpv4_rfc4950_mpls_bottom_of_stack_autofill() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(1).ttl(1)
/ IcmpExtensionMpls::new().label(2).ttl(2))
.compile()
.unwrap();
assert_eq!(compiled.as_bytes()[FIRST_ENTRY_START + 2] & 0x01, 0);
assert_eq!(compiled.as_bytes()[FIRST_ENTRY_START + 6] & 0x01, 1);
}
#[test]
fn icmpv4_rfc4950_mpls_explicit_bottom_of_stack_override() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new()
.label(1)
.ttl(1)
.bottom_of_stack(true)
/ IcmpExtensionMpls::new()
.label(2)
.ttl(2)
.bottom_of_stack(false))
.compile()
.unwrap();
assert_eq!(compiled.as_bytes()[FIRST_ENTRY_START + 2] & 0x01, 1);
assert_eq!(compiled.as_bytes()[FIRST_ENTRY_START + 6] & 0x01, 0);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let entries: Vec<&IcmpExtensionMpls> = decoded.layers::<IcmpExtensionMpls>().collect();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].bottom_of_stack_value(), Some(true));
assert_eq!(entries[1].bottom_of_stack_value(), Some(false));
}
#[test]
fn icmpv4_rfc4950_mpls_invalid_field_bounds() {
let too_large_label = Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(0x10_0000).ttl(1);
assert!(too_large_label.compile().is_err());
let too_large_exp = Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(1).exp(8).ttl(1);
assert!(too_large_exp.compile().is_err());
}
#[test]
fn icmpv4_rfc4950_mpls_byte_for_byte_roundtrip() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::destination_unreachable()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(0xfffff).exp(7).ttl(255)
/ IcmpExtensionMpls::new().label(0).exp(0).ttl(0))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
assert_eq!(decoded.layers::<IcmpExtensionMpls>().count(), 2);
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc4950_mpls_partial_entry_stays_raw() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionMpls::new().label(7).ttl(7)
/ IcmpExtensionMpls::new().label(8).ttl(8))
.compile()
.unwrap();
let mut bytes = compiled.as_bytes().to_vec();
bytes.truncate(bytes.len() - 2);
bytes[OBJECT_HEADER_START] = 0x00;
bytes[OBJECT_HEADER_START + 1] = 0x0a;
let total = u16::from_be_bytes([bytes[2], bytes[3]]) - 2;
bytes[2..4].copy_from_slice(&total.to_be_bytes());
bytes[10] = 0;
bytes[11] = 0;
let mut sum = 0u32;
for pair in bytes[0..20].chunks(2) {
sum += u16::from_be_bytes([pair[0], pair[1]]) as u32;
}
while sum >> 16 != 0 {
sum = (sum & 0xffff) + (sum >> 16);
}
let csum = !(sum as u16);
bytes[10..12].copy_from_slice(&csum.to_be_bytes());
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, &bytes).unwrap();
assert_eq!(decoded.layers::<IcmpExtensionMpls>().count(), 0);
assert!(decoded.layer::<Raw>().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), &bytes[..]);
}
}
#[cfg(test)]
mod icmpv4_rfc5837_interface_info {
use super::{
IcmpExtension, IcmpExtensionInterfaceInfo, IcmpExtensionObject, IcmpInterfaceIpAddress,
Icmpv4, Icmpv4QuotedIp, ICMP_EXTENSION_CLASS_INTERFACE_INFO, ICMP_INTERFACE_CTYPE_IFINDEX,
ICMP_INTERFACE_CTYPE_IP_ADDRESS, ICMP_INTERFACE_CTYPE_MTU, ICMP_INTERFACE_CTYPE_NAME,
ICMP_INTERFACE_ROLE_NEXT_HOP, ICMP_INTERFACE_ROLE_OUTGOING,
ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM,
};
use crate::checksum::verify_internet_checksum;
use crate::{Ipv4, Ipv4Protocol, NetworkLayer, Packet, Raw, Udp};
use core::net::{Ipv4Addr, Ipv6Addr};
fn src() -> Ipv4Addr {
Ipv4Addr::new(192, 0, 2, 10)
}
fn dst() -> Ipv4Addr {
Ipv4Addr::new(198, 51, 100, 20)
}
fn quoted_udp() -> Packet {
Ipv4::new()
.src(Ipv4Addr::new(192, 0, 2, 1))
.dst(Ipv4Addr::new(198, 51, 100, 1))
.ipv4_protocol(Ipv4Protocol::Udp)
/ Udp::new().sport(40000).dport(53)
/ Raw::from("query")
}
const EXT_HEADER_START: usize = 28 + ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM;
const OBJECT_HEADER_START: usize = EXT_HEADER_START + 4;
const OBJECT_BODY_START: usize = OBJECT_HEADER_START + 4;
#[test]
fn icmpv4_rfc5837_interface_info_all_subobjects_encode_decode() {
let info = IcmpExtensionInterfaceInfo::new()
.role(ICMP_INTERFACE_ROLE_OUTGOING)
.if_index(7)
.ip_address(IcmpInterfaceIpAddress::ipv4(Ipv4Addr::new(192, 0, 2, 99)))
.name("eth0")
.mtu(1500);
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ info)
.compile()
.unwrap();
let expected_ctype = (ICMP_INTERFACE_ROLE_OUTGOING << 6)
| ICMP_INTERFACE_CTYPE_IFINDEX
| ICMP_INTERFACE_CTYPE_IP_ADDRESS
| ICMP_INTERFACE_CTYPE_NAME
| ICMP_INTERFACE_CTYPE_MTU;
assert_eq!(
compiled.as_bytes()[OBJECT_HEADER_START + 2],
ICMP_EXTENSION_CLASS_INTERFACE_INFO
);
assert_eq!(compiled.as_bytes()[OBJECT_HEADER_START + 3], expected_ctype);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let info = decoded.layer::<IcmpExtensionInterfaceInfo>().unwrap();
assert_eq!(info.role_value(), ICMP_INTERFACE_ROLE_OUTGOING);
assert_eq!(info.if_index_value(), Some(7));
assert_eq!(
info.ip_address_value().unwrap().ipv4_value(),
Some(Ipv4Addr::new(192, 0, 2, 99))
);
assert_eq!(info.name_value(), Some(&b"eth0"[..]));
assert_eq!(info.mtu_value(), Some(1500));
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc5837_interface_info_ifindex_only() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceInfo::new().if_index(0xdead_beef))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let info = decoded.layer::<IcmpExtensionInterfaceInfo>().unwrap();
assert_eq!(info.if_index_value(), Some(0xdead_beef));
assert_eq!(info.ip_address_value(), None);
assert_eq!(info.name_value(), None);
assert_eq!(info.mtu_value(), None);
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc5837_interface_info_ipv6_address_subobject() {
let addr = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 0x1234);
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceInfo::new()
.role(ICMP_INTERFACE_ROLE_NEXT_HOP)
.ip_address(IcmpInterfaceIpAddress::ipv6(addr)))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let info = decoded.layer::<IcmpExtensionInterfaceInfo>().unwrap();
assert_eq!(info.role_value(), ICMP_INTERFACE_ROLE_NEXT_HOP);
assert_eq!(info.ip_address_value().unwrap().ipv6_value(), Some(addr));
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc5837_interface_info_name_padding() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceInfo::new().name("eth0"))
.compile()
.unwrap();
assert_eq!(compiled.as_bytes()[OBJECT_BODY_START], 5);
assert_eq!(
&compiled.as_bytes()[OBJECT_BODY_START + 1..OBJECT_BODY_START + 5],
b"eth0"
);
assert_eq!(
&compiled.as_bytes()[OBJECT_BODY_START + 5..OBJECT_BODY_START + 8],
&[0, 0, 0]
);
let object = compiled.as_bytes()[OBJECT_HEADER_START + 1];
assert_eq!(object, 12);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let info = decoded.layer::<IcmpExtensionInterfaceInfo>().unwrap();
assert_eq!(info.name_value(), Some(&b"eth0"[..]));
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc5837_interface_info_length_autofill() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceInfo::new().if_index(1).mtu(9000))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let object = decoded.layer::<IcmpExtensionObject>().unwrap();
assert_eq!(object.length_value(), Some(12));
assert_eq!(
object.class_num_value(),
ICMP_EXTENSION_CLASS_INTERFACE_INFO
);
}
#[test]
fn icmpv4_rfc5837_interface_info_explicit_length_override() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new().length(99)
/ IcmpExtensionInterfaceInfo::new().if_index(1))
.compile()
.unwrap();
assert_eq!(
u16::from_be_bytes([
compiled.as_bytes()[OBJECT_HEADER_START],
compiled.as_bytes()[OBJECT_HEADER_START + 1],
]),
99
);
}
#[test]
fn icmpv4_rfc5837_interface_info_extension_checksum() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceInfo::new()
.if_index(5)
.name("wlan0")
.mtu(1280))
.compile()
.unwrap();
assert!(verify_internet_checksum(
&compiled.as_bytes()[EXT_HEADER_START..]
));
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let extension = decoded.layer::<IcmpExtension>().unwrap();
assert!(extension.checksum_value().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc5837_interface_info_unknown_class_stays_raw() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new().class_num(200).c_type(0x0f)
/ Raw::from_bytes([1, 2, 3, 4]))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
assert!(decoded.layer::<IcmpExtensionInterfaceInfo>().is_none());
let object = decoded.layer::<IcmpExtensionObject>().unwrap();
assert_eq!(object.class_num_value(), 200);
assert!(decoded.layer::<Raw>().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc5837_interface_info_truncated_subobject_stays_raw() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::time_exceeded()
/ Icmpv4QuotedIp::new(quoted_udp())
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
.class_num(ICMP_EXTENSION_CLASS_INTERFACE_INFO)
.c_type(ICMP_INTERFACE_CTYPE_MTU)
/ Raw::from_bytes([0xaa, 0xbb]))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
assert!(decoded.layer::<IcmpExtensionInterfaceInfo>().is_none());
assert!(decoded.layer::<Raw>().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc5837_interface_info_summary() {
let info = IcmpExtensionInterfaceInfo::new()
.role(ICMP_INTERFACE_ROLE_OUTGOING)
.if_index(42)
.ip_address(IcmpInterfaceIpAddress::ipv4(Ipv4Addr::new(192, 0, 2, 1)))
.name("eth1")
.mtu(1500);
let summary = crate::packet::Layer::summary(&info);
assert!(summary.contains("outgoing"), "summary was {summary}");
assert!(summary.contains("ifindex=42"), "summary was {summary}");
assert!(summary.contains("192.0.2.1"), "summary was {summary}");
assert!(summary.contains("name=eth1"), "summary was {summary}");
assert!(summary.contains("mtu=1500"), "summary was {summary}");
}
}
#[cfg(test)]
mod icmpv4_rfc8335_extended_echo {
use super::{
IcmpExtension, IcmpExtensionInterfaceId, IcmpExtensionObject, Icmpv4,
ICMP_CODE_EXTENDED_ECHO_REPLY_MALFORMED_QUERY,
ICMP_CODE_EXTENDED_ECHO_REPLY_MULTIPLE_INTERFACES, ICMP_CODE_EXTENDED_ECHO_REPLY_NO_ERROR,
ICMP_CODE_EXTENDED_ECHO_REPLY_NO_SUCH_INTERFACE,
ICMP_CODE_EXTENDED_ECHO_REPLY_NO_SUCH_TABLE_ENTRY, ICMP_EXTENDED_ECHO_REPLY,
ICMP_EXTENDED_ECHO_REQUEST, ICMP_EXTENSION_CLASS_INTERFACE_ID,
ICMP_INTERFACE_ID_CTYPE_ADDRESS, ICMP_INTERFACE_ID_CTYPE_INDEX,
ICMP_INTERFACE_ID_CTYPE_NAME,
};
use crate::checksum::verify_internet_checksum;
use crate::{Ipv4, NetworkLayer, Packet, Raw};
use core::net::{Ipv4Addr, Ipv6Addr};
fn src() -> Ipv4Addr {
Ipv4Addr::new(192, 0, 2, 10)
}
fn dst() -> Ipv4Addr {
Ipv4Addr::new(198, 51, 100, 20)
}
#[test]
fn icmpv4_rfc8335_extended_echo_request_compile_decode() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request().id(0x1234).seq(7)
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_index(42))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes[20], ICMP_EXTENDED_ECHO_REQUEST);
assert_eq!(bytes[21], 0);
assert_eq!(&bytes[24..26], &0x1234u16.to_be_bytes());
assert_eq!(bytes[26], 7);
assert_eq!(bytes[27], 0);
assert_eq!(bytes[28] >> 4, 2);
assert_eq!(bytes[34], ICMP_EXTENSION_CLASS_INTERFACE_ID);
assert_eq!(bytes[35], ICMP_INTERFACE_ID_CTYPE_INDEX);
assert_eq!(u16::from_be_bytes([bytes[32], bytes[33]]), 8);
assert_eq!(
u32::from_be_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]),
42
);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let icmp = decoded.layer::<Icmpv4>().unwrap();
assert_eq!(icmp.icmp_type_value(), ICMP_EXTENDED_ECHO_REQUEST);
assert_eq!(icmp.identifier_value(), Some(0x1234));
assert_eq!(icmp.sequence_number_value(), Some(7));
assert_eq!(icmp.extended_l_bit_value(), Some(false));
assert!(decoded.layer::<IcmpExtension>().is_some());
let id = decoded.layer::<IcmpExtensionInterfaceId>().unwrap();
assert_eq!(id.index_value(), Some(42));
assert_eq!(id.c_type(), ICMP_INTERFACE_ID_CTYPE_INDEX);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn icmpv4_rfc8335_extended_echo_request_by_name() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request().id(1).seq(1)
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_name("eth0"))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes[35], ICMP_INTERFACE_ID_CTYPE_NAME);
assert_eq!(u16::from_be_bytes([bytes[32], bytes[33]]), 8);
assert_eq!(&bytes[36..40], b"eth0");
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let id = decoded.layer::<IcmpExtensionInterfaceId>().unwrap();
assert_eq!(id.name_value(), Some(&b"eth0"[..]));
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn icmpv4_rfc8335_extended_echo_request_by_name_padding() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request()
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_name("e0"))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(u16::from_be_bytes([bytes[32], bytes[33]]), 8);
assert_eq!(&bytes[36..38], b"e0");
assert_eq!(&bytes[38..40], &[0, 0]);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let id = decoded.layer::<IcmpExtensionInterfaceId>().unwrap();
assert_eq!(id.name_value(), Some(&b"e0"[..]));
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn icmpv4_rfc8335_extended_echo_request_by_ipv4_address() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request()
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_ipv4(Ipv4Addr::new(192, 0, 2, 99)))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes[35], ICMP_INTERFACE_ID_CTYPE_ADDRESS);
assert_eq!(u16::from_be_bytes([bytes[36], bytes[37]]), 1);
assert_eq!(bytes[38], 4);
assert_eq!(bytes[39], 0);
assert_eq!(&bytes[40..44], &[192, 0, 2, 99]);
assert_eq!(u16::from_be_bytes([bytes[32], bytes[33]]), 12);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let id = decoded.layer::<IcmpExtensionInterfaceId>().unwrap();
assert_eq!(id.ipv4_value(), Some(Ipv4Addr::new(192, 0, 2, 99)));
assert_eq!(id.address_length_value(), Some(4));
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn icmpv4_rfc8335_extended_echo_request_by_ipv6_address() {
let addr = Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 0x99);
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request()
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_ipv6(addr))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(u16::from_be_bytes([bytes[36], bytes[37]]), 2);
assert_eq!(bytes[38], 16);
assert_eq!(u16::from_be_bytes([bytes[32], bytes[33]]), 24);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let id = decoded.layer::<IcmpExtensionInterfaceId>().unwrap();
assert_eq!(id.ipv6_value(), Some(addr));
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn icmpv4_rfc8335_extended_echo_request_l_bit() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request()
.id(9)
.seq(3)
.extended_l_bit(true)
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_index(1))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes[27], 0x01);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let icmp = decoded.layer::<Icmpv4>().unwrap();
assert_eq!(icmp.extended_l_bit_value(), Some(true));
assert_eq!(icmp.extended_state_value(), None);
assert_eq!(icmp.extended_active_value(), None);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn icmpv4_rfc8335_extended_echo_reply_all_codes() {
let codes = [
(ICMP_CODE_EXTENDED_ECHO_REPLY_NO_ERROR, "no-error"),
(
ICMP_CODE_EXTENDED_ECHO_REPLY_MALFORMED_QUERY,
"malformed-query",
),
(
ICMP_CODE_EXTENDED_ECHO_REPLY_NO_SUCH_INTERFACE,
"no-such-interface",
),
(
ICMP_CODE_EXTENDED_ECHO_REPLY_NO_SUCH_TABLE_ENTRY,
"no-such-table-entry",
),
(
ICMP_CODE_EXTENDED_ECHO_REPLY_MULTIPLE_INTERFACES,
"multiple-interfaces",
),
];
for (code, name) in codes {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_reply().id(0xabcd).seq(5).code(code))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes[20], ICMP_EXTENDED_ECHO_REPLY);
assert_eq!(bytes[21], code);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let icmp = decoded.layer::<Icmpv4>().unwrap();
assert_eq!(icmp.code_value(), code);
assert_eq!(icmp.identifier_value(), Some(0xabcd));
assert_eq!(icmp.sequence_number_value(), Some(5));
assert!(
crate::packet::Layer::summary(icmp).contains(name),
"summary missing {name}"
);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
}
#[test]
fn icmpv4_rfc8335_extended_echo_reply_flags() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_reply()
.code(ICMP_CODE_EXTENDED_ECHO_REPLY_NO_ERROR)
.extended_state(super::ICMP_EXTENDED_ECHO_REPLY_STATE_REACHABLE)
.extended_active(true)
.extended_ipv4(true)
.extended_ipv6(false))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert_eq!(bytes[27], 0x40 | 0x04 | 0x02);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let icmp = decoded.layer::<Icmpv4>().unwrap();
assert_eq!(
icmp.extended_state_value(),
Some(super::ICMP_EXTENDED_ECHO_REPLY_STATE_REACHABLE)
);
assert_eq!(icmp.extended_active_value(), Some(true));
assert_eq!(icmp.extended_ipv4_value(), Some(true));
assert_eq!(icmp.extended_ipv6_value(), Some(false));
assert_eq!(icmp.extended_l_bit_value(), None);
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn icmpv4_rfc8335_extended_echo_reply_trailing_payload_stays_raw() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_reply().code(ICMP_CODE_EXTENDED_ECHO_REPLY_NO_ERROR)
/ Raw::from_bytes([0xde, 0xad, 0xbe, 0xef]))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let raw = decoded.layer::<Raw>().unwrap();
assert_eq!(raw.as_bytes(), &[0xde, 0xad, 0xbe, 0xef]);
assert!(decoded.layer::<IcmpExtension>().is_none());
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc8335_extended_echo_exactly_one_object_default() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request()
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_index(3))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let object_count = decoded
.iter()
.filter(|layer| layer.as_any().is::<IcmpExtensionObject>())
.count();
assert_eq!(object_count, 1);
let id_count = decoded
.iter()
.filter(|layer| layer.as_any().is::<IcmpExtensionInterfaceId>())
.count();
assert_eq!(id_count, 1);
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc8335_extended_echo_malformed_object_count_preserved() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request()
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_index(1)
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_index(2))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let ids: Vec<_> = decoded
.iter()
.filter_map(|layer| layer.as_any().downcast_ref::<IcmpExtensionInterfaceId>())
.map(|id| id.index_value())
.collect();
assert_eq!(ids, vec![Some(1), Some(2)]);
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc8335_extended_echo_unknown_object_falls_back_to_raw() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request()
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
.class_num(ICMP_EXTENSION_CLASS_INTERFACE_ID)
.c_type(0x7f)
/ Raw::from_bytes([1, 2, 3, 4]))
.compile()
.unwrap();
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
assert!(decoded.layer::<IcmpExtensionInterfaceId>().is_none());
let object = decoded.layer::<IcmpExtensionObject>().unwrap();
assert_eq!(object.class_num_value(), ICMP_EXTENSION_CLASS_INTERFACE_ID);
assert_eq!(object.c_type_value(), 0x7f);
assert!(decoded.layer::<Raw>().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
#[test]
fn icmpv4_rfc8335_extended_echo_checksum_roundtrip() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request().id(0x55aa).seq(2)
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_name("wlan0"))
.compile()
.unwrap();
let bytes = compiled.as_bytes();
assert!(verify_internet_checksum(&bytes[28..]));
assert!(verify_internet_checksum(&bytes[20..]));
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, bytes).unwrap();
let extension = decoded.layer::<IcmpExtension>().unwrap();
assert!(extension.checksum_value().is_some());
assert_eq!(decoded.compile().unwrap().as_bytes(), bytes);
}
#[test]
fn icmpv4_rfc8335_extended_echo_explicit_flag_byte_preserved() {
let compiled = (Ipv4::new().src(src()).dst(dst())
/ Icmpv4::extended_echo_request()
.id(1)
.seq(1)
.extended_flags(0xfe)
/ IcmpExtension::new()
/ IcmpExtensionObject::new()
/ IcmpExtensionInterfaceId::by_index(1))
.compile()
.unwrap();
assert_eq!(compiled.as_bytes()[27], 0xfe);
let decoded = Packet::decode_from_l3(NetworkLayer::Ipv4, compiled.as_bytes()).unwrap();
let icmp = decoded.layer::<Icmpv4>().unwrap();
assert_eq!(icmp.extended_flags_value(), Some(0xfe));
assert_eq!(decoded.compile().unwrap().as_bytes(), compiled.as_bytes());
}
}