use crate::phy::DeviceCapabilities;
use crate::wire::*;
#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(feature = "medium-ethernet")]
pub(crate) enum EthernetPacket<'a> {
#[cfg(feature = "proto-ipv4")]
Arp(ArpRepr),
Ip(Packet<'a>),
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum Packet<'p> {
#[cfg(feature = "proto-ipv4")]
Ipv4(PacketV4<'p>),
#[cfg(feature = "proto-ipv6")]
Ipv6(PacketV6<'p>),
}
impl<'p> Packet<'p> {
pub(crate) fn new(ip_repr: IpRepr, payload: IpPayload<'p>) -> Self {
match ip_repr {
#[cfg(feature = "proto-ipv4")]
IpRepr::Ipv4(header) => Self::new_ipv4(header, payload),
#[cfg(feature = "proto-ipv6")]
IpRepr::Ipv6(header) => Self::new_ipv6(header, payload),
}
}
#[cfg(feature = "proto-ipv4")]
pub(crate) fn new_ipv4(ip_repr: Ipv4Repr, payload: IpPayload<'p>) -> Self {
Self::Ipv4(PacketV4 {
header: ip_repr,
payload,
})
}
#[cfg(feature = "proto-ipv6")]
pub(crate) fn new_ipv6(ip_repr: Ipv6Repr, payload: IpPayload<'p>) -> Self {
Self::Ipv6(PacketV6 {
header: ip_repr,
#[cfg(feature = "proto-ipv6-hbh")]
hop_by_hop: None,
#[cfg(feature = "proto-ipv6-fragmentation")]
fragment: None,
#[cfg(feature = "proto-ipv6-routing")]
routing: None,
payload,
})
}
pub(crate) fn ip_repr(&self) -> IpRepr {
match self {
#[cfg(feature = "proto-ipv4")]
Packet::Ipv4(p) => IpRepr::Ipv4(p.header),
#[cfg(feature = "proto-ipv6")]
Packet::Ipv6(p) => IpRepr::Ipv6(p.header),
}
}
pub(crate) fn payload(&self) -> &IpPayload<'p> {
match self {
#[cfg(feature = "proto-ipv4")]
Packet::Ipv4(p) => &p.payload,
#[cfg(feature = "proto-ipv6")]
Packet::Ipv6(p) => &p.payload,
}
}
pub(crate) fn emit_payload(
&self,
_ip_repr: &IpRepr,
payload: &mut [u8],
caps: &DeviceCapabilities,
) {
match self.payload() {
#[cfg(feature = "proto-ipv4")]
IpPayload::Icmpv4(icmpv4_repr) => {
icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum)
}
#[cfg(feature = "proto-igmp")]
IpPayload::Igmp(igmp_repr) => igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)),
#[cfg(feature = "proto-ipv6")]
IpPayload::Icmpv6(icmpv6_repr) => icmpv6_repr.emit(
&_ip_repr.src_addr(),
&_ip_repr.dst_addr(),
&mut Icmpv6Packet::new_unchecked(payload),
&caps.checksum,
),
#[cfg(feature = "socket-raw")]
IpPayload::Raw(raw_packet) => payload.copy_from_slice(raw_packet),
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
IpPayload::Udp(udp_repr, inner_payload) => udp_repr.emit(
&mut UdpPacket::new_unchecked(payload),
&_ip_repr.src_addr(),
&_ip_repr.dst_addr(),
inner_payload.len(),
|buf| buf.copy_from_slice(inner_payload),
&caps.checksum,
),
#[cfg(feature = "socket-tcp")]
IpPayload::Tcp(mut tcp_repr) => {
if let Some(max_burst_size) = caps.max_burst_size {
let mut max_segment_size = caps.max_transmission_unit;
max_segment_size -= _ip_repr.header_len();
max_segment_size -= tcp_repr.header_len();
let max_window_size = max_burst_size * max_segment_size;
if tcp_repr.window_len as usize > max_window_size {
tcp_repr.window_len = max_window_size as u16;
}
}
tcp_repr.emit(
&mut TcpPacket::new_unchecked(payload),
&_ip_repr.src_addr(),
&_ip_repr.dst_addr(),
&caps.checksum,
);
}
#[cfg(feature = "socket-dhcpv4")]
IpPayload::Dhcpv4(udp_repr, dhcp_repr) => udp_repr.emit(
&mut UdpPacket::new_unchecked(payload),
&_ip_repr.src_addr(),
&_ip_repr.dst_addr(),
dhcp_repr.buffer_len(),
|buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
&caps.checksum,
),
}
}
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(feature = "proto-ipv4")]
pub(crate) struct PacketV4<'p> {
header: Ipv4Repr,
payload: IpPayload<'p>,
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(feature = "proto-ipv6")]
pub(crate) struct PacketV6<'p> {
pub(crate) header: Ipv6Repr,
#[cfg(feature = "proto-ipv6-hbh")]
pub(crate) hop_by_hop: Option<Ipv6HopByHopRepr<'p>>,
#[cfg(feature = "proto-ipv6-fragmentation")]
pub(crate) fragment: Option<Ipv6FragmentRepr>,
#[cfg(feature = "proto-ipv6-routing")]
pub(crate) routing: Option<Ipv6RoutingRepr<'p>>,
pub(crate) payload: IpPayload<'p>,
}
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum IpPayload<'p> {
#[cfg(feature = "proto-ipv4")]
Icmpv4(Icmpv4Repr<'p>),
#[cfg(feature = "proto-igmp")]
Igmp(IgmpRepr),
#[cfg(feature = "proto-ipv6")]
Icmpv6(Icmpv6Repr<'p>),
#[cfg(feature = "socket-raw")]
Raw(&'p [u8]),
#[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
Udp(UdpRepr, &'p [u8]),
#[cfg(feature = "socket-tcp")]
Tcp(TcpRepr<'p>),
#[cfg(feature = "socket-dhcpv4")]
Dhcpv4(UdpRepr, DhcpRepr<'p>),
}
impl<'p> IpPayload<'p> {
#[cfg(feature = "proto-sixlowpan")]
pub(crate) fn as_sixlowpan_next_header(&self) -> SixlowpanNextHeader {
match self {
#[cfg(feature = "proto-ipv4")]
Self::Icmpv4(_) => unreachable!(),
#[cfg(feature = "socket-dhcpv4")]
Self::Dhcpv4(..) => unreachable!(),
#[cfg(feature = "proto-ipv6")]
Self::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
#[cfg(feature = "proto-igmp")]
Self::Igmp(_) => unreachable!(),
#[cfg(feature = "socket-tcp")]
Self::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
#[cfg(feature = "socket-udp")]
Self::Udp(..) => SixlowpanNextHeader::Compressed,
#[cfg(feature = "socket-raw")]
Self::Raw(_) => todo!(),
}
}
}
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
pub(crate) fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
len.min(mtu - header_len * 2 - 8)
}
#[cfg(feature = "proto-igmp")]
pub(crate) enum IgmpReportState {
Inactive,
ToGeneralQuery {
version: IgmpVersion,
timeout: crate::time::Instant,
interval: crate::time::Duration,
next_index: usize,
},
ToSpecificQuery {
version: IgmpVersion,
timeout: crate::time::Instant,
group: Ipv4Address,
},
}