use core::any::Any;
use core::net::Ipv4Addr;
use core::ops::Div;
use core::str::FromStr;
use crate::checksum::internet_checksum;
use crate::error::{CrafterError, Result};
use crate::field::Field;
use crate::packet::{IntoPacket, Layer, LayerContext, Packet, Raw, TransportChecksumContext};
use crate::protocols::ipv4::IPPROTO_ICMPV6;
mod constants;
pub use self::constants::*;
const ICMP_HEADER_LEN: usize = 8;
const ICMP_TIMESTAMP_BODY_LEN: usize = 12;
const ICMP_ADDRESS_MASK_BODY_LEN: usize = 4;
const ICMP_ROUTER_ADVERTISEMENT_ENTRY_LEN: usize = 8;
const ICMP_EXTENSION_HEADER_LEN: usize = 4;
const ICMP_EXTENSION_OBJECT_LEN: usize = 4;
const ICMP_EXTENSION_MPLS_LEN: usize = 4;
const ICMP_INTERFACE_IFINDEX_LEN: usize = 4;
const ICMP_INTERFACE_IP_ADDRESS_PREFIX_LEN: usize = 4;
const ICMP_INTERFACE_MTU_LEN: usize = 4;
const ICMP_INTERFACE_NAME_MAX: usize = 63;
const ICMP_INTERFACE_ID_INDEX_LEN: usize = 4;
const ICMP_INTERFACE_ID_ADDRESS_PREFIX_LEN: usize = 4;
const ICMP_EXTENSION_VERSION: u8 = 2;
const ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM: usize = 128;
const MPLS_MAX_LABEL: u32 = 0x000f_ffff;
const MPLS_MAX_EXP: u8 = 0x07;
macro_rules! impl_layer_object {
($type:ty) => {
fn clone_layer(&self) -> Box<dyn Layer> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
};
}
macro_rules! impl_layer_div {
($type:ty) => {
impl<R> Div<R> for $type
where
R: IntoPacket,
{
type Output = Packet;
fn div(self, rhs: R) -> Self::Output {
Packet::from_layer(self).concat(rhs)
}
}
};
}
mod shared;
pub use self::shared::*;
mod v6;
pub(crate) use self::v6::append_icmpv6_packet;
pub use self::v6::Icmpv6;
pub use self::v6::{Icmpv6Body, Icmpv6ErrorBody};
pub use self::v6::{
ndp_option_type_is_known, ndp_option_type_name, ndp_rdnss_length_units, NdpOption, NdpOptions,
Pref64Plc, Prf, NDP_DNS_LIFETIME_INFINITY, NDP_DNS_RESERVED_LEN,
NDP_LINK_LAYER_ADDR_ETHERNET_LEN, NDP_MTU_OPTION_LEN, NDP_MTU_OPTION_UNITS, NDP_NONCE_MIN_LEN,
NDP_OPTION_HEADER_LEN, NDP_OPTION_LENGTH_UNIT, NDP_OPT_CAPTIVE_PORTAL, NDP_OPT_DNSSL,
NDP_OPT_MTU, NDP_OPT_NONCE, NDP_OPT_PREF64, NDP_OPT_PREFIX_INFORMATION,
NDP_OPT_RA_FLAGS_EXTENSION, NDP_OPT_RDNSS, NDP_OPT_REDIRECTED_HEADER,
NDP_OPT_ROUTE_INFORMATION, NDP_OPT_SOURCE_LINK_LAYER_ADDR, NDP_OPT_TARGET_LINK_LAYER_ADDR,
NDP_PREF64_LEN, NDP_PREF64_PLC_MASK, NDP_PREF64_PREFIX_LEN, NDP_PREF64_SCALED_LIFETIME_MAX,
NDP_PREF64_SCALED_LIFETIME_SHIFT, NDP_PREF64_UNITS, NDP_PREFIX_FLAGS_RESERVED,
NDP_PREFIX_FLAG_AUTONOMOUS, NDP_PREFIX_FLAG_ON_LINK, NDP_PREFIX_INFORMATION_LEN,
NDP_PREFIX_INFORMATION_UNITS, NDP_PREFIX_LIFETIME_INFINITY, NDP_PRF_MASK, NDP_PRF_SHIFT,
NDP_RA_FLAGS_EXTENSION_BITS_LEN, NDP_RA_FLAGS_EXTENSION_LEN, NDP_RA_FLAGS_EXTENSION_UNITS,
NDP_RDNSS_ADDRESS_LEN, NDP_REDIRECTED_HEADER_RESERVED_LEN,
NDP_ROUTE_INFORMATION_LEN_FULL_PREFIX, NDP_ROUTE_INFORMATION_LEN_HALF_PREFIX,
NDP_ROUTE_INFORMATION_LEN_NO_PREFIX, NDP_ROUTE_LIFETIME_INFINITY,
};
pub use self::v6::{
NeighborAdvertisement, NeighborSolicitation, Redirect, RouterAdvertisement, RouterSolicitation,
ICMPV6_NA_FLAGS_RESERVED, ICMPV6_NA_FLAG_OVERRIDE, ICMPV6_NA_FLAG_ROUTER,
ICMPV6_NA_FLAG_SOLICITED, ICMPV6_RA_DEFAULT_CUR_HOP_LIMIT, ICMPV6_RA_DEFAULT_ROUTER_LIFETIME,
ICMPV6_RA_FLAGS_RESERVED, ICMPV6_RA_FLAG_MANAGED, ICMPV6_RA_FLAG_OTHER,
};
pub use self::v6::{
Mldv2Query, Mldv2Report, MulticastAddressRecord, MulticastListenerMessage, MulticastRecordType,
MLDV2_QUERY_MIN_BODY_LEN, MLDV2_QUERY_QRV_MASK, MLDV2_QUERY_RESV_MASK, MLDV2_QUERY_S_FLAG,
};
pub use self::v6::{
NodeInformation, NI_NONCE_LEN, NI_QTYPE_IPV4_ADDRESSES, NI_QTYPE_NODE_ADDRESSES,
NI_QTYPE_NODE_NAME, NI_QTYPE_NOOP, NI_QUERY_CODE_SUBJECT_IPV4, NI_QUERY_CODE_SUBJECT_IPV6,
NI_QUERY_CODE_SUBJECT_NAME, NI_RESPONSE_CODE_REFUSED, NI_RESPONSE_CODE_SUCCESS,
NI_RESPONSE_CODE_UNKNOWN_QTYPE,
};
pub use self::v6::{
ICMPV6_CODE_EXTENDED_ECHO_REPLY_MALFORMED_QUERY,
ICMPV6_CODE_EXTENDED_ECHO_REPLY_MULTIPLE_INTERFACES, ICMPV6_CODE_EXTENDED_ECHO_REPLY_NO_ERROR,
ICMPV6_CODE_EXTENDED_ECHO_REPLY_NO_SUCH_INTERFACE,
ICMPV6_CODE_EXTENDED_ECHO_REPLY_NO_SUCH_TABLE_ENTRY, ICMPV6_EXTENDED_ECHO_REPLY_ACTIVE,
ICMPV6_EXTENDED_ECHO_REPLY_IPV4, ICMPV6_EXTENDED_ECHO_REPLY_IPV6,
ICMPV6_EXTENDED_ECHO_REPLY_STATE_DELAY, ICMPV6_EXTENDED_ECHO_REPLY_STATE_FAILED,
ICMPV6_EXTENDED_ECHO_REPLY_STATE_INCOMPLETE, ICMPV6_EXTENDED_ECHO_REPLY_STATE_PROBE,
ICMPV6_EXTENDED_ECHO_REPLY_STATE_REACHABLE, ICMPV6_EXTENDED_ECHO_REPLY_STATE_RESERVED,
ICMPV6_EXTENDED_ECHO_REPLY_STATE_STALE, ICMPV6_EXTENDED_ECHO_REQUEST_L_BIT,
};
mod decode;
pub(crate) use self::decode::{append_icmp_packet, append_icmp_packet_with_checksum_validation};
pub(crate) use self::decode::decode_extended_echo_extension;
mod v4;
pub use self::v4::Icmpv4;
#[deprecated(since = "2.1.0", note = "renamed to Icmpv4; use the v4-explicit name")]
pub type Icmp = Icmpv4;
pub use self::v4::{
Icmpv4AddressMask, Icmpv4QuotedIp, Icmpv4RouterAdvertisementEntry, Icmpv4Timestamp,
};
#[allow(deprecated)]
pub use self::v4::{IcmpAddressMask, IcmpQuotedIpv4, IcmpRouterAdvertisementEntry, IcmpTimestamp};
fn payload_bytes_after(ctx: LayerContext<'_>) -> Result<Vec<u8>> {
let mut payload = Vec::new();
compile_payload_after_into(ctx, &mut payload)?;
Ok(payload)
}
fn compile_payload_after_into(ctx: LayerContext<'_>, out: &mut Vec<u8>) -> Result<()> {
ctx.packet().compile_layers_after_into(ctx.index(), out)
}
fn payload_len_after(ctx: LayerContext<'_>) -> usize {
ctx.packet().encoded_len_after(ctx.index())
}
fn router_advertisement_entry_count(ctx: LayerContext<'_>) -> usize {
ctx.packet()
.iter()
.skip(ctx.index() + 1)
.take_while(|layer| layer.as_any().is::<Icmpv4RouterAdvertisementEntry>())
.count()
}
fn encoded_len_until_extension(ctx: LayerContext<'_>) -> usize {
ctx.packet()
.iter()
.skip(ctx.index() + 1)
.take_while(|layer| !layer.as_any().is::<IcmpExtension>())
.map(Layer::encoded_len)
.sum()
}
fn following_extension_present(ctx: LayerContext<'_>) -> bool {
ctx.packet()
.iter()
.skip(ctx.index() + 1)
.any(|layer| layer.as_any().is::<IcmpExtension>())
}
fn rfc4884_padded_original_len(raw_len: usize, unit: usize) -> usize {
let unit = unit.max(1);
let rounded = raw_len.div_ceil(unit) * unit;
rounded.max(ICMP_RFC4884_MIN_ORIGINAL_DATAGRAM)
}
fn extension_original_datagram_padding(ctx: LayerContext<'_>) -> usize {
let Some(target) = preceding_icmp_original_datagram_len(ctx) else {
return 0;
};
let raw_len = original_datagram_len_before_extension(ctx);
target.saturating_sub(raw_len)
}
fn original_datagram_len_before_extension(ctx: LayerContext<'_>) -> usize {
let Some(icmp_index) = preceding_icmp_index(ctx) else {
return 0;
};
((icmp_index + 1)..ctx.index())
.filter_map(|index| ctx.packet().get(index))
.map(Layer::encoded_len)
.sum()
}
fn preceding_icmp_original_datagram_len(ctx: LayerContext<'_>) -> Option<usize> {
let icmp_index = preceding_icmp_index(ctx)?;
let icmp = ctx
.packet()
.get(icmp_index)?
.as_any()
.downcast_ref::<Icmpv4>()?;
let icmp_ctx = LayerContext::new(ctx.packet(), icmp_index);
let words = icmp
.effective_extension_length(Some(icmp_ctx), ICMP_EXTENSION_OBJECT_LEN)
.ok()??;
Some(words as usize * ICMP_EXTENSION_OBJECT_LEN)
}
fn preceding_icmp_index(ctx: LayerContext<'_>) -> Option<usize> {
(0..ctx.index()).rev().find(|&index| {
ctx.packet()
.get(index)
.is_some_and(|layer| layer.as_any().is::<Icmpv4>())
})
}
fn extension_object_payload_len(ctx: LayerContext<'_>) -> usize {
ctx.packet()
.iter()
.skip(ctx.index() + 1)
.take_while(|layer| {
!layer.as_any().is::<IcmpExtensionObject>() && !layer.as_any().is::<IcmpExtension>()
})
.map(Layer::encoded_len)
.sum()
}
fn checksum_context(
ctx: LayerContext<'_>,
transport_protocol: u8,
) -> Option<TransportChecksumContext> {
(0..ctx.index()).rev().find_map(|index| {
ctx.packet()
.get(index)
.and_then(|layer| layer.transport_checksum_context(transport_protocol))
})
}
fn value_or_copy<T: Copy>(field: &Field<T>, default: T) -> T {
field.value().copied().unwrap_or(default)
}
fn value_or_u16_from_rest(field: &Field<u16>, rest: &Field<[u8; 4]>, offset: usize) -> u16 {
field.value().copied().unwrap_or_else(|| {
let rest = rest.value().copied().unwrap_or([0; 4]);
u16::from_be_bytes([rest[offset], rest[offset + 1]])
})
}
fn value_or_u8_from_rest(field: &Field<u8>, rest: &Field<[u8; 4]>, offset: usize) -> u8 {
field.value().copied().unwrap_or_else(|| {
let rest = rest.value().copied().unwrap_or([0; 4]);
rest[offset]
})
}
fn field_from_echo(
icmp_type: u8,
rest: &[u8; 4],
offset: usize,
is_echo: fn(u8) -> bool,
) -> Field<u16> {
if is_echo(icmp_type) {
Field::user(u16::from_be_bytes([rest[offset], rest[offset + 1]]))
} else {
Field::unset()
}
}
fn parse_ipv4(input: &str) -> Result<Ipv4Addr> {
Ipv4Addr::from_str(input).map_err(|_| {
CrafterError::invalid_field_value("ipv4_address", "expected dotted-quad IPv4 address")
})
}
fn hex_bytes(bytes: &[u8]) -> String {
let mut output = String::new();
for (index, byte) in bytes.iter().enumerate() {
if index > 0 {
output.push(' ');
}
output.push_str(&format!("{byte:02x}"));
}
output
}
fn copy_array_4(bytes: &[u8]) -> [u8; 4] {
let mut out = [0u8; 4];
out.copy_from_slice(&bytes[..4]);
out
}