1 use super::*;
2 
3 #[cfg(feature = "socket-dhcpv4")]
4 use crate::socket::dhcpv4;
5 #[cfg(feature = "socket-icmp")]
6 use crate::socket::icmp;
7 use crate::socket::AnySocket;
8 
9 use crate::phy::{Medium, TxToken};
10 use crate::time::Instant;
11 use crate::wire::*;
12 
13 impl InterfaceInner {
process_ipv4<'a, T: AsRef<[u8]> + ?Sized>( &mut self, sockets: &mut SocketSet, ipv4_packet: &Ipv4Packet<&'a T>, frag: &'a mut FragmentsBuffer, ) -> Option<IpPacket<'a>>14     pub(super) fn process_ipv4<'a, T: AsRef<[u8]> + ?Sized>(
15         &mut self,
16         sockets: &mut SocketSet,
17         ipv4_packet: &Ipv4Packet<&'a T>,
18         frag: &'a mut FragmentsBuffer,
19     ) -> Option<IpPacket<'a>> {
20         let ipv4_repr = check!(Ipv4Repr::parse(ipv4_packet, &self.caps.checksum));
21         if !self.is_unicast_v4(ipv4_repr.src_addr) {
22             // Discard packets with non-unicast source addresses.
23             net_debug!("non-unicast source address");
24             return None;
25         }
26 
27         #[cfg(feature = "proto-ipv4-fragmentation")]
28         let ip_payload = {
29             if ipv4_packet.more_frags() || ipv4_packet.frag_offset() != 0 {
30                 let key = FragKey::Ipv4(ipv4_packet.get_key());
31 
32                 let f = match frag.assembler.get(&key, self.now + frag.reassembly_timeout) {
33                     Ok(f) => f,
34                     Err(_) => {
35                         net_debug!("No available packet assembler for fragmented packet");
36                         return None;
37                     }
38                 };
39 
40                 if !ipv4_packet.more_frags() {
41                     // This is the last fragment, so we know the total size
42                     check!(f.set_total_size(
43                         ipv4_packet.total_len() as usize - ipv4_packet.header_len() as usize
44                             + ipv4_packet.frag_offset() as usize,
45                     ));
46                 }
47 
48                 if let Err(e) = f.add(ipv4_packet.payload(), ipv4_packet.frag_offset() as usize) {
49                     net_debug!("fragmentation error: {:?}", e);
50                     return None;
51                 }
52 
53                 // NOTE: according to the standard, the total length needs to be
54                 // recomputed, as well as the checksum. However, we don't really use
55                 // the IPv4 header after the packet is reassembled.
56                 match f.assemble() {
57                     Some(payload) => payload,
58                     None => return None,
59                 }
60             } else {
61                 ipv4_packet.payload()
62             }
63         };
64 
65         #[cfg(not(feature = "proto-ipv4-fragmentation"))]
66         let ip_payload = ipv4_packet.payload();
67 
68         let ip_repr = IpRepr::Ipv4(ipv4_repr);
69 
70         #[cfg(feature = "socket-raw")]
71         let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload);
72         #[cfg(not(feature = "socket-raw"))]
73         let handled_by_raw_socket = false;
74 
75         #[cfg(feature = "socket-dhcpv4")]
76         {
77             if ipv4_repr.next_header == IpProtocol::Udp && self.hardware_addr.is_some() {
78                 let udp_packet = check!(UdpPacket::new_checked(ip_payload));
79                 if let Some(dhcp_socket) = sockets
80                     .items_mut()
81                     .find_map(|i| dhcpv4::Socket::downcast_mut(&mut i.socket))
82                 {
83                     // First check for source and dest ports, then do `UdpRepr::parse` if they match.
84                     // This way we avoid validating the UDP checksum twice for all non-DHCP UDP packets (one here, one in `process_udp`)
85                     if udp_packet.src_port() == dhcp_socket.server_port
86                         && udp_packet.dst_port() == dhcp_socket.client_port
87                     {
88                         let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
89                         let udp_repr = check!(UdpRepr::parse(
90                             &udp_packet,
91                             &src_addr,
92                             &dst_addr,
93                             &self.caps.checksum
94                         ));
95                         let udp_payload = udp_packet.payload();
96 
97                         dhcp_socket.process(self, &ipv4_repr, &udp_repr, udp_payload);
98                         return None;
99                     }
100                 }
101             }
102         }
103 
104         if !self.has_ip_addr(ipv4_repr.dst_addr)
105             && !self.has_multicast_group(ipv4_repr.dst_addr)
106             && !self.is_broadcast_v4(ipv4_repr.dst_addr)
107         {
108             // Ignore IP packets not directed at us, or broadcast, or any of the multicast groups.
109             // If AnyIP is enabled, also check if the packet is routed locally.
110             if !self.any_ip
111                 || !ipv4_repr.dst_addr.is_unicast()
112                 || self
113                     .routes
114                     .lookup(&IpAddress::Ipv4(ipv4_repr.dst_addr), self.now)
115                     .map_or(true, |router_addr| !self.has_ip_addr(router_addr))
116             {
117                 return None;
118             }
119         }
120 
121         match ipv4_repr.next_header {
122             IpProtocol::Icmp => self.process_icmpv4(sockets, ip_repr, ip_payload),
123 
124             #[cfg(feature = "proto-igmp")]
125             IpProtocol::Igmp => self.process_igmp(ipv4_repr, ip_payload),
126 
127             #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
128             IpProtocol::Udp => {
129                 let udp_packet = check!(UdpPacket::new_checked(ip_payload));
130                 let udp_repr = check!(UdpRepr::parse(
131                     &udp_packet,
132                     &ipv4_repr.src_addr.into(),
133                     &ipv4_repr.dst_addr.into(),
134                     &self.checksum_caps(),
135                 ));
136 
137                 self.process_udp(
138                     sockets,
139                     ip_repr,
140                     udp_repr,
141                     handled_by_raw_socket,
142                     udp_packet.payload(),
143                     ip_payload,
144                 )
145             }
146 
147             #[cfg(feature = "socket-tcp")]
148             IpProtocol::Tcp => self.process_tcp(sockets, ip_repr, ip_payload),
149 
150             _ if handled_by_raw_socket => None,
151 
152             _ => {
153                 // Send back as much of the original payload as we can.
154                 let payload_len =
155                     icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len());
156                 let icmp_reply_repr = Icmpv4Repr::DstUnreachable {
157                     reason: Icmpv4DstUnreachable::ProtoUnreachable,
158                     header: ipv4_repr,
159                     data: &ip_payload[0..payload_len],
160                 };
161                 self.icmpv4_reply(ipv4_repr, icmp_reply_repr)
162             }
163         }
164     }
165 
166     #[cfg(feature = "medium-ethernet")]
process_arp<'frame, T: AsRef<[u8]>>( &mut self, timestamp: Instant, eth_frame: &EthernetFrame<&'frame T>, ) -> Option<EthernetPacket<'frame>>167     pub(super) fn process_arp<'frame, T: AsRef<[u8]>>(
168         &mut self,
169         timestamp: Instant,
170         eth_frame: &EthernetFrame<&'frame T>,
171     ) -> Option<EthernetPacket<'frame>> {
172         let arp_packet = check!(ArpPacket::new_checked(eth_frame.payload()));
173         let arp_repr = check!(ArpRepr::parse(&arp_packet));
174 
175         match arp_repr {
176             ArpRepr::EthernetIpv4 {
177                 operation,
178                 source_hardware_addr,
179                 source_protocol_addr,
180                 target_protocol_addr,
181                 ..
182             } => {
183                 // Only process ARP packets for us.
184                 if !self.has_ip_addr(target_protocol_addr) {
185                     return None;
186                 }
187 
188                 // Only process REQUEST and RESPONSE.
189                 if let ArpOperation::Unknown(_) = operation {
190                     net_debug!("arp: unknown operation code");
191                     return None;
192                 }
193 
194                 // Discard packets with non-unicast source addresses.
195                 if !source_protocol_addr.is_unicast() || !source_hardware_addr.is_unicast() {
196                     net_debug!("arp: non-unicast source address");
197                     return None;
198                 }
199 
200                 if !self.in_same_network(&IpAddress::Ipv4(source_protocol_addr)) {
201                     net_debug!("arp: source IP address not in same network as us");
202                     return None;
203                 }
204 
205                 // Fill the ARP cache from any ARP packet aimed at us (both request or response).
206                 // We fill from requests too because if someone is requesting our address they
207                 // are probably going to talk to us, so we avoid having to request their address
208                 // when we later reply to them.
209                 self.neighbor_cache.as_mut().unwrap().fill(
210                     source_protocol_addr.into(),
211                     source_hardware_addr.into(),
212                     timestamp,
213                 );
214 
215                 if operation == ArpOperation::Request {
216                     let src_hardware_addr = match self.hardware_addr {
217                         Some(HardwareAddress::Ethernet(addr)) => addr,
218                         _ => unreachable!(),
219                     };
220 
221                     Some(EthernetPacket::Arp(ArpRepr::EthernetIpv4 {
222                         operation: ArpOperation::Reply,
223                         source_hardware_addr: src_hardware_addr,
224                         source_protocol_addr: target_protocol_addr,
225                         target_hardware_addr: source_hardware_addr,
226                         target_protocol_addr: source_protocol_addr,
227                     }))
228                 } else {
229                     None
230                 }
231             }
232         }
233     }
234 
process_icmpv4<'frame>( &mut self, _sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], ) -> Option<IpPacket<'frame>>235     pub(super) fn process_icmpv4<'frame>(
236         &mut self,
237         _sockets: &mut SocketSet,
238         ip_repr: IpRepr,
239         ip_payload: &'frame [u8],
240     ) -> Option<IpPacket<'frame>> {
241         let icmp_packet = check!(Icmpv4Packet::new_checked(ip_payload));
242         let icmp_repr = check!(Icmpv4Repr::parse(&icmp_packet, &self.caps.checksum));
243 
244         #[cfg(feature = "socket-icmp")]
245         let mut handled_by_icmp_socket = false;
246 
247         #[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
248         for icmp_socket in _sockets
249             .items_mut()
250             .filter_map(|i| icmp::Socket::downcast_mut(&mut i.socket))
251         {
252             if icmp_socket.accepts(self, &ip_repr, &icmp_repr.into()) {
253                 icmp_socket.process(self, &ip_repr, &icmp_repr.into());
254                 handled_by_icmp_socket = true;
255             }
256         }
257 
258         match icmp_repr {
259             // Respond to echo requests.
260             #[cfg(feature = "proto-ipv4")]
261             Icmpv4Repr::EchoRequest {
262                 ident,
263                 seq_no,
264                 data,
265             } => {
266                 let icmp_reply_repr = Icmpv4Repr::EchoReply {
267                     ident,
268                     seq_no,
269                     data,
270                 };
271                 match ip_repr {
272                     IpRepr::Ipv4(ipv4_repr) => self.icmpv4_reply(ipv4_repr, icmp_reply_repr),
273                     #[allow(unreachable_patterns)]
274                     _ => unreachable!(),
275                 }
276             }
277 
278             // Ignore any echo replies.
279             Icmpv4Repr::EchoReply { .. } => None,
280 
281             // Don't report an error if a packet with unknown type
282             // has been handled by an ICMP socket
283             #[cfg(feature = "socket-icmp")]
284             _ if handled_by_icmp_socket => None,
285 
286             // FIXME: do something correct here?
287             _ => None,
288         }
289     }
290 
icmpv4_reply<'frame, 'icmp: 'frame>( &self, ipv4_repr: Ipv4Repr, icmp_repr: Icmpv4Repr<'icmp>, ) -> Option<IpPacket<'frame>>291     pub(super) fn icmpv4_reply<'frame, 'icmp: 'frame>(
292         &self,
293         ipv4_repr: Ipv4Repr,
294         icmp_repr: Icmpv4Repr<'icmp>,
295     ) -> Option<IpPacket<'frame>> {
296         if !self.is_unicast_v4(ipv4_repr.src_addr) {
297             // Do not send ICMP replies to non-unicast sources
298             None
299         } else if self.is_unicast_v4(ipv4_repr.dst_addr) {
300             // Reply as normal when src_addr and dst_addr are both unicast
301             let ipv4_reply_repr = Ipv4Repr {
302                 src_addr: ipv4_repr.dst_addr,
303                 dst_addr: ipv4_repr.src_addr,
304                 next_header: IpProtocol::Icmp,
305                 payload_len: icmp_repr.buffer_len(),
306                 hop_limit: 64,
307             };
308             Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr)))
309         } else if self.is_broadcast_v4(ipv4_repr.dst_addr) {
310             // Only reply to broadcasts for echo replies and not other ICMP messages
311             match icmp_repr {
312                 Icmpv4Repr::EchoReply { .. } => match self.ipv4_addr() {
313                     Some(src_addr) => {
314                         let ipv4_reply_repr = Ipv4Repr {
315                             src_addr,
316                             dst_addr: ipv4_repr.src_addr,
317                             next_header: IpProtocol::Icmp,
318                             payload_len: icmp_repr.buffer_len(),
319                             hop_limit: 64,
320                         };
321                         Some(IpPacket::Icmpv4((ipv4_reply_repr, icmp_repr)))
322                     }
323                     None => None,
324                 },
325                 _ => None,
326             }
327         } else {
328             None
329         }
330     }
331 
332     #[cfg(feature = "proto-ipv4-fragmentation")]
dispatch_ipv4_frag<Tx: TxToken>(&mut self, tx_token: Tx, frag: &mut Fragmenter)333     pub(super) fn dispatch_ipv4_frag<Tx: TxToken>(&mut self, tx_token: Tx, frag: &mut Fragmenter) {
334         let caps = self.caps.clone();
335 
336         let mtu_max = self.ip_mtu();
337         let ip_len = (frag.packet_len - frag.sent_bytes + frag.ipv4.repr.buffer_len()).min(mtu_max);
338         let payload_len = ip_len - frag.ipv4.repr.buffer_len();
339 
340         let more_frags = (frag.packet_len - frag.sent_bytes) != payload_len;
341         frag.ipv4.repr.payload_len = payload_len;
342         frag.sent_bytes += payload_len;
343 
344         let mut tx_len = ip_len;
345         #[cfg(feature = "medium-ethernet")]
346         if matches!(caps.medium, Medium::Ethernet) {
347             tx_len += EthernetFrame::<&[u8]>::header_len();
348         }
349 
350         // Emit function for the Ethernet header.
351         #[cfg(feature = "medium-ethernet")]
352         let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| {
353             let mut frame = EthernetFrame::new_unchecked(tx_buffer);
354 
355             let src_addr = self.hardware_addr.unwrap().ethernet_or_panic();
356             frame.set_src_addr(src_addr);
357             frame.set_dst_addr(frag.ipv4.dst_hardware_addr);
358 
359             match repr.version() {
360                 #[cfg(feature = "proto-ipv4")]
361                 IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4),
362                 #[cfg(feature = "proto-ipv6")]
363                 IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6),
364             }
365         };
366 
367         tx_token.consume(tx_len, |mut tx_buffer| {
368             #[cfg(feature = "medium-ethernet")]
369             if matches!(self.caps.medium, Medium::Ethernet) {
370                 emit_ethernet(&IpRepr::Ipv4(frag.ipv4.repr), tx_buffer);
371                 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
372             }
373 
374             let mut packet =
375                 Ipv4Packet::new_unchecked(&mut tx_buffer[..frag.ipv4.repr.buffer_len()]);
376             frag.ipv4.repr.emit(&mut packet, &caps.checksum);
377             packet.set_ident(frag.ipv4.ident);
378             packet.set_more_frags(more_frags);
379             packet.set_dont_frag(false);
380             packet.set_frag_offset(frag.ipv4.frag_offset);
381 
382             if caps.checksum.ipv4.tx() {
383                 packet.fill_checksum();
384             }
385 
386             tx_buffer[frag.ipv4.repr.buffer_len()..][..payload_len].copy_from_slice(
387                 &frag.buffer[frag.ipv4.frag_offset as usize + frag.ipv4.repr.buffer_len()..]
388                     [..payload_len],
389             );
390 
391             // Update the frag offset for the next fragment.
392             frag.ipv4.frag_offset += payload_len as u16;
393         })
394     }
395 
396     #[cfg(feature = "proto-igmp")]
igmp_report_packet<'any>( &self, version: IgmpVersion, group_addr: Ipv4Address, ) -> Option<IpPacket<'any>>397     pub(super) fn igmp_report_packet<'any>(
398         &self,
399         version: IgmpVersion,
400         group_addr: Ipv4Address,
401     ) -> Option<IpPacket<'any>> {
402         let iface_addr = self.ipv4_addr()?;
403         let igmp_repr = IgmpRepr::MembershipReport {
404             group_addr,
405             version,
406         };
407         let pkt = IpPacket::Igmp((
408             Ipv4Repr {
409                 src_addr: iface_addr,
410                 // Send to the group being reported
411                 dst_addr: group_addr,
412                 next_header: IpProtocol::Igmp,
413                 payload_len: igmp_repr.buffer_len(),
414                 hop_limit: 1,
415                 // [#183](https://github.com/m-labs/smoltcp/issues/183).
416             },
417             igmp_repr,
418         ));
419         Some(pkt)
420     }
421 
422     #[cfg(feature = "proto-igmp")]
igmp_leave_packet<'any>( &self, group_addr: Ipv4Address, ) -> Option<IpPacket<'any>>423     pub(super) fn igmp_leave_packet<'any>(
424         &self,
425         group_addr: Ipv4Address,
426     ) -> Option<IpPacket<'any>> {
427         self.ipv4_addr().map(|iface_addr| {
428             let igmp_repr = IgmpRepr::LeaveGroup { group_addr };
429             IpPacket::Igmp((
430                 Ipv4Repr {
431                     src_addr: iface_addr,
432                     dst_addr: Ipv4Address::MULTICAST_ALL_ROUTERS,
433                     next_header: IpProtocol::Igmp,
434                     payload_len: igmp_repr.buffer_len(),
435                     hop_limit: 1,
436                 },
437                 igmp_repr,
438             ))
439         })
440     }
441 }
442