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(all(feature = "proto-ipv4", feature = "multicast"))]
85 IpPayload::Igmp(igmp_repr) => igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload)),
86 #[cfg(feature = "proto-ipv6")]
87 IpPayload::Icmpv6(icmpv6_repr) => {
88 let ipv6_repr = match _ip_repr {
89 #[cfg(feature = "proto-ipv4")]
90 IpRepr::Ipv4(_) => unreachable!(),
91 IpRepr::Ipv6(repr) => repr,
92 };
93
94 icmpv6_repr.emit(
95 &ipv6_repr.src_addr,
96 &ipv6_repr.dst_addr,
97 &mut Icmpv6Packet::new_unchecked(payload),
98 &caps.checksum,
99 )
100 }
101 #[cfg(feature = "proto-ipv6")]
102 IpPayload::HopByHopIcmpv6(hbh_repr, icmpv6_repr) => {
103 let ipv6_repr = match _ip_repr {
104 #[cfg(feature = "proto-ipv4")]
105 IpRepr::Ipv4(_) => unreachable!(),
106 IpRepr::Ipv6(repr) => repr,
107 };
108
109 let ipv6_ext_hdr = Ipv6ExtHeaderRepr {
110 next_header: IpProtocol::Icmpv6,
111 length: 0,
112 data: &[],
113 };
114 ipv6_ext_hdr.emit(&mut Ipv6ExtHeader::new_unchecked(
115 &mut payload[..ipv6_ext_hdr.header_len()],
116 ));
117
118 let hbh_start = ipv6_ext_hdr.header_len();
119 let hbh_end = hbh_start + hbh_repr.buffer_len();
120 hbh_repr.emit(&mut Ipv6HopByHopHeader::new_unchecked(
121 &mut payload[hbh_start..hbh_end],
122 ));
123
124 icmpv6_repr.emit(
125 &ipv6_repr.src_addr,
126 &ipv6_repr.dst_addr,
127 &mut Icmpv6Packet::new_unchecked(&mut payload[hbh_end..]),
128 &caps.checksum,
129 );
130 }
131
132 #[cfg(feature = "socket-raw")]
133 IpPayload::Raw(raw_packet) => payload.copy_from_slice(raw_packet),
134 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
135 IpPayload::Udp(udp_repr, inner_payload) => udp_repr.emit(
136 &mut UdpPacket::new_unchecked(payload),
137 &_ip_repr.src_addr(),
138 &_ip_repr.dst_addr(),
139 inner_payload.len(),
140 |buf| buf.copy_from_slice(inner_payload),
141 &caps.checksum,
142 ),
143 #[cfg(feature = "socket-tcp")]
144 IpPayload::Tcp(mut tcp_repr) => {
145 if let Some(max_burst_size) = caps.max_burst_size {
153 let mut max_segment_size = caps.max_transmission_unit;
154 max_segment_size -= _ip_repr.header_len();
155 max_segment_size -= tcp_repr.header_len();
156
157 let max_window_size = max_burst_size * max_segment_size;
158 if tcp_repr.window_len as usize > max_window_size {
159 tcp_repr.window_len = max_window_size as u16;
160 }
161 }
162
163 tcp_repr.emit(
164 &mut TcpPacket::new_unchecked(payload),
165 &_ip_repr.src_addr(),
166 &_ip_repr.dst_addr(),
167 &caps.checksum,
168 );
169 }
170 #[cfg(feature = "socket-dhcpv4")]
171 IpPayload::Dhcpv4(udp_repr, dhcp_repr) => udp_repr.emit(
172 &mut UdpPacket::new_unchecked(payload),
173 &_ip_repr.src_addr(),
174 &_ip_repr.dst_addr(),
175 dhcp_repr.buffer_len(),
176 |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
177 &caps.checksum,
178 ),
179 }
180 }
181}
182
183#[derive(Debug, PartialEq)]
184#[cfg_attr(feature = "defmt", derive(defmt::Format))]
185#[cfg(feature = "proto-ipv4")]
186pub(crate) struct PacketV4<'p> {
187 header: Ipv4Repr,
188 payload: IpPayload<'p>,
189}
190
191#[derive(Debug, PartialEq)]
192#[cfg_attr(feature = "defmt", derive(defmt::Format))]
193#[cfg(feature = "proto-ipv6")]
194pub(crate) struct PacketV6<'p> {
195 pub(crate) header: Ipv6Repr,
196 #[cfg(feature = "proto-ipv6-hbh")]
197 pub(crate) hop_by_hop: Option<Ipv6HopByHopRepr<'p>>,
198 #[cfg(feature = "proto-ipv6-fragmentation")]
199 pub(crate) fragment: Option<Ipv6FragmentRepr>,
200 #[cfg(feature = "proto-ipv6-routing")]
201 pub(crate) routing: Option<Ipv6RoutingRepr<'p>>,
202 pub(crate) payload: IpPayload<'p>,
203}
204
205#[derive(Debug, PartialEq)]
206#[cfg_attr(feature = "defmt", derive(defmt::Format))]
207pub(crate) enum IpPayload<'p> {
208 #[cfg(feature = "proto-ipv4")]
209 Icmpv4(Icmpv4Repr<'p>),
210 #[cfg(all(feature = "proto-ipv4", feature = "multicast"))]
211 Igmp(IgmpRepr),
212 #[cfg(feature = "proto-ipv6")]
213 Icmpv6(Icmpv6Repr<'p>),
214 #[cfg(feature = "proto-ipv6")]
215 HopByHopIcmpv6(Ipv6HopByHopRepr<'p>, Icmpv6Repr<'p>),
216 #[cfg(feature = "socket-raw")]
217 Raw(&'p [u8]),
218 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
219 Udp(UdpRepr, &'p [u8]),
220 #[cfg(feature = "socket-tcp")]
221 Tcp(TcpRepr<'p>),
222 #[cfg(feature = "socket-dhcpv4")]
223 Dhcpv4(UdpRepr, DhcpRepr<'p>),
224}
225
226impl<'p> IpPayload<'p> {
227 #[cfg(feature = "proto-sixlowpan")]
228 pub(crate) fn as_sixlowpan_next_header(&self) -> SixlowpanNextHeader {
229 match self {
230 #[cfg(feature = "proto-ipv4")]
231 Self::Icmpv4(_) => unreachable!(),
232 #[cfg(feature = "socket-dhcpv4")]
233 Self::Dhcpv4(..) => unreachable!(),
234 #[cfg(feature = "proto-ipv6")]
235 Self::Icmpv6(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Icmpv6),
236 #[cfg(feature = "proto-ipv6")]
237 Self::HopByHopIcmpv6(_, _) => unreachable!(),
238 #[cfg(all(feature = "proto-ipv4", feature = "multicast"))]
239 Self::Igmp(_) => unreachable!(),
240 #[cfg(feature = "socket-tcp")]
241 Self::Tcp(_) => SixlowpanNextHeader::Uncompressed(IpProtocol::Tcp),
242 #[cfg(feature = "socket-udp")]
243 Self::Udp(..) => SixlowpanNextHeader::Compressed,
244 #[cfg(feature = "socket-raw")]
245 Self::Raw(_) => todo!(),
246 }
247 }
248}
249
250#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
251pub(crate) fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
252 len.min(mtu - header_len * 2 - 8)
261}