Skip to main content

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