smoltcp/iface/interface/
ipv4.rs

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