1use crate::phy::DeviceCapabilities;
2use crate::wire::*;
3
4#[allow(clippy::large_enum_variant)]
5#[derive(Debug, PartialEq)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7#[cfg(feature = "medium-ethernet")]
8pub(crate) enum EthernetPacket<'a> {
9 #[cfg(feature = "proto-ipv4")]
10 Arp(ArpRepr),
11 Ip(Packet<'a>),
12}
13
14#[derive(Debug, PartialEq)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub(crate) enum Packet<'p> {
17 #[cfg(feature = "proto-ipv4")]
18 Ipv4(PacketV4<'p>),
19 #[cfg(feature = "proto-ipv6")]
20 Ipv6(PacketV6<'p>),
21}
22
23impl<'p> Packet<'p> {
24 pub(crate) fn new(ip_repr: IpRepr, payload: IpPayload<'p>) -> Self {
25 match ip_repr {
26 #[cfg(feature = "proto-ipv4")]
27 IpRepr::Ipv4(header) => Self::new_ipv4(header, payload),
28 #[cfg(feature = "proto-ipv6")]
29 IpRepr::Ipv6(header) => Self::new_ipv6(header, payload),
30 }
31 }
32
33 #[cfg(feature = "proto-ipv4")]
34 pub(crate) fn new_ipv4(ip_repr: Ipv4Repr, payload: IpPayload<'p>) -> Self {
35 Self::Ipv4(PacketV4 {
36 header: ip_repr,
37 payload,
38 })
39 }
40
41 #[cfg(feature = "proto-ipv6")]
42 pub(crate) fn new_ipv6(ip_repr: Ipv6Repr, payload: IpPayload<'p>) -> Self {
43 Self::Ipv6(PacketV6 {
44 header: ip_repr,
45 #[cfg(feature = "proto-ipv6-hbh")]
46 hop_by_hop: None,
47 #[cfg(feature = "proto-ipv6-fragmentation")]
48 fragment: None,
49 #[cfg(feature = "proto-ipv6-routing")]
50 routing: None,
51 payload,
52 })
53 }
54
55 pub(crate) fn ip_repr(&self) -> IpRepr {
56 match self {
57 #[cfg(feature = "proto-ipv4")]
58 Packet::Ipv4(p) => IpRepr::Ipv4(p.header),
59 #[cfg(feature = "proto-ipv6")]
60 Packet::Ipv6(p) => IpRepr::Ipv6(p.header),
61 }
62 }
63
64 pub(crate) fn payload(&self) -> &IpPayload<'p> {
65 match self {
66 #[cfg(feature = "proto-ipv4")]
67 Packet::Ipv4(p) => &p.payload,
68 #[cfg(feature = "proto-ipv6")]
69 Packet::Ipv6(p) => &p.payload,
70 }
71 }
72
73 pub(crate) fn emit_payload(
74 &self,
75 _ip_repr: &IpRepr,
76 payload: &mut [u8],
77 caps: &DeviceCapabilities,
78 ) {
79 match self.payload() {
80 #[cfg(feature = "proto-ipv4")]
81 IpPayload::Icmpv4(icmpv4_repr) => {
82 icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum)
83 }
84 #[cfg(feature = "proto-igmp")]
85 IpPayload::Igmp(igmp_repr) => igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)),
86 #[cfg(feature = "proto-ipv6")]
87 IpPayload::Icmpv6(icmpv6_repr) => icmpv6_repr.emit(
88 &_ip_repr.src_addr(),
89 &_ip_repr.dst_addr(),
90 &mut Icmpv6Packet::new_unchecked(payload),
91 &caps.checksum,
92 ),
93 #[cfg(feature = "socket-raw")]
94 IpPayload::Raw(raw_packet) => payload.copy_from_slice(raw_packet),
95 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
96 IpPayload::Udp(udp_repr, inner_payload) => udp_repr.emit(
97 &mut UdpPacket::new_unchecked(payload),
98 &_ip_repr.src_addr(),
99 &_ip_repr.dst_addr(),
100 inner_payload.len(),
101 |buf| buf.copy_from_slice(inner_payload),
102 &caps.checksum,
103 ),
104 #[cfg(feature = "socket-tcp")]
105 IpPayload::Tcp(mut tcp_repr) => {
106 if let Some(max_burst_size) = caps.max_burst_size {
114 let mut max_segment_size = caps.max_transmission_unit;
115 max_segment_size -= _ip_repr.header_len();
116 max_segment_size -= tcp_repr.header_len();
117
118 let max_window_size = max_burst_size * max_segment_size;
119 if tcp_repr.window_len as usize > max_window_size {
120 tcp_repr.window_len = max_window_size as u16;
121 }
122 }
123
124 tcp_repr.emit(
125 &mut TcpPacket::new_unchecked(payload),
126 &_ip_repr.src_addr(),
127 &_ip_repr.dst_addr(),
128 &caps.checksum,
129 );
130 }
131 #[cfg(feature = "socket-dhcpv4")]
132 IpPayload::Dhcpv4(udp_repr, dhcp_repr) => udp_repr.emit(
133 &mut UdpPacket::new_unchecked(payload),
134 &_ip_repr.src_addr(),
135 &_ip_repr.dst_addr(),
136 dhcp_repr.buffer_len(),
137 |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
138 &caps.checksum,
139 ),
140 }
141 }
142}
143
144#[derive(Debug, PartialEq)]
145#[cfg_attr(feature = "defmt", derive(defmt::Format))]
146#[cfg(feature = "proto-ipv4")]
147pub(crate) struct PacketV4<'p> {
148 header: Ipv4Repr,
149 payload: IpPayload<'p>,
150}
151
152#[derive(Debug, PartialEq)]
153#[cfg_attr(feature = "defmt", derive(defmt::Format))]
154#[cfg(feature = "proto-ipv6")]
155pub(crate) struct PacketV6<'p> {
156 pub(crate) header: Ipv6Repr,
157 #[cfg(feature = "proto-ipv6-hbh")]
158 pub(crate) hop_by_hop: Option<Ipv6HopByHopRepr<'p>>,
159 #[cfg(feature = "proto-ipv6-fragmentation")]
160 pub(crate) fragment: Option<Ipv6FragmentRepr>,
161 #[cfg(feature = "proto-ipv6-routing")]
162 pub(crate) routing: Option<Ipv6RoutingRepr<'p>>,
163 pub(crate) payload: IpPayload<'p>,
164}
165
166#[derive(Debug, PartialEq)]
167#[cfg_attr(feature = "defmt", derive(defmt::Format))]
168pub(crate) enum IpPayload<'p> {
169 #[cfg(feature = "proto-ipv4")]
170 Icmpv4(Icmpv4Repr<'p>),
171 #[cfg(feature = "proto-igmp")]
172 Igmp(IgmpRepr),
173 #[cfg(feature = "proto-ipv6")]
174 Icmpv6(Icmpv6Repr<'p>),
175 #[cfg(feature = "socket-raw")]
176 Raw(&'p [u8]),
177 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
178 Udp(UdpRepr, &'p [u8]),
179 #[cfg(feature = "socket-tcp")]
180 Tcp(TcpRepr<'p>),
181 #[cfg(feature = "socket-dhcpv4")]
182 Dhcpv4(UdpRepr, DhcpRepr<'p>),
183}
184
185impl<'p> IpPayload<'p> {
186 #[cfg(feature = "proto-sixlowpan")]
187 pub(crate) fn as_sixlowpan_next_header(&self) -> SixlowpanNextHeader {
188 match self {
189 #[cfg(feature = "proto-ipv4")]
190 Self::Icmpv4(_) => unreachable!(),
191 #[cfg(feature = "socket-dhcpv4")]
192 Self::Dhcpv4(..) => unreachable!(),
193 #[cfg(feature = "proto-ipv6")]
194 Self::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
195 #[cfg(feature = "proto-igmp")]
196 Self::Igmp(_) => unreachable!(),
197 #[cfg(feature = "socket-tcp")]
198 Self::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
199 #[cfg(feature = "socket-udp")]
200 Self::Udp(..) => SixlowpanNextHeader::Compressed,
201 #[cfg(feature = "socket-raw")]
202 Self::Raw(_) => todo!(),
203 }
204 }
205}
206
207#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
208pub(crate) fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
209 len.min(mtu - header_len * 2 - 8)
218}
219
220#[cfg(feature = "proto-igmp")]
221pub(crate) enum IgmpReportState {
222 Inactive,
223 ToGeneralQuery {
224 version: IgmpVersion,
225 timeout: crate::time::Instant,
226 interval: crate::time::Duration,
227 next_index: usize,
228 },
229 ToSpecificQuery {
230 version: IgmpVersion,
231 timeout: crate::time::Instant,
232 group: Ipv4Address,
233 },
234}