1 // Heads up! Before working on this file you should read the parts
2 // of RFC 1122 that discuss Ethernet, ARP and IP for any IPv4 work
3 // and RFCs 8200 and 4861 for any IPv6 and NDISC work.
4
5 #[cfg(test)]
6 mod tests;
7
8 #[cfg(feature = "medium-ethernet")]
9 mod ethernet;
10 #[cfg(feature = "proto-sixlowpan")]
11 mod sixlowpan;
12
13 #[cfg(feature = "proto-ipv4")]
14 mod ipv4;
15 #[cfg(feature = "proto-ipv6")]
16 mod ipv6;
17
18 #[cfg(feature = "proto-igmp")]
19 mod igmp;
20
21 #[cfg(feature = "proto-igmp")]
22 pub use igmp::MulticastError;
23
24 use core::cmp;
25 use core::result::Result;
26 use heapless::{LinearMap, Vec};
27
28 #[cfg(any(feature = "proto-ipv4", feature = "proto-sixlowpan"))]
29 use super::fragmentation::PacketAssemblerSet;
30 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
31 use super::neighbor::{Answer as NeighborAnswer, Cache as NeighborCache};
32 use super::socket_set::SocketSet;
33 use crate::config::{
34 FRAGMENTATION_BUFFER_SIZE, IFACE_MAX_ADDR_COUNT, IFACE_MAX_MULTICAST_GROUP_COUNT,
35 IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT,
36 };
37 use crate::iface::Routes;
38 use crate::phy::{ChecksumCapabilities, Device, DeviceCapabilities, Medium, RxToken, TxToken};
39 use crate::rand::Rand;
40 #[cfg(feature = "socket-dns")]
41 use crate::socket::dns;
42 use crate::socket::*;
43 use crate::time::{Duration, Instant};
44 use crate::wire::*;
45
46 #[cfg(feature = "_proto-fragmentation")]
47 #[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
48 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
49 pub(crate) enum FragKey {
50 #[cfg(feature = "proto-ipv4-fragmentation")]
51 Ipv4(Ipv4FragKey),
52 #[cfg(feature = "proto-sixlowpan-fragmentation")]
53 Sixlowpan(SixlowpanFragKey),
54 }
55
56 pub(crate) struct FragmentsBuffer {
57 #[cfg(feature = "proto-sixlowpan")]
58 decompress_buf: [u8; sixlowpan::MAX_DECOMPRESSED_LEN],
59
60 #[cfg(feature = "_proto-fragmentation")]
61 pub(crate) assembler: PacketAssemblerSet<FragKey>,
62
63 #[cfg(feature = "_proto-fragmentation")]
64 reassembly_timeout: Duration,
65 }
66
67 #[cfg(not(feature = "_proto-fragmentation"))]
68 pub(crate) struct Fragmenter {}
69
70 #[cfg(not(feature = "_proto-fragmentation"))]
71 impl Fragmenter {
new() -> Self72 pub(crate) fn new() -> Self {
73 Self {}
74 }
75 }
76
77 #[cfg(feature = "_proto-fragmentation")]
78 pub(crate) struct Fragmenter {
79 /// The buffer that holds the unfragmented 6LoWPAN packet.
80 buffer: [u8; FRAGMENTATION_BUFFER_SIZE],
81 /// The size of the packet without the IEEE802.15.4 header and the fragmentation headers.
82 packet_len: usize,
83 /// The amount of bytes that already have been transmitted.
84 sent_bytes: usize,
85
86 #[cfg(feature = "proto-ipv4-fragmentation")]
87 ipv4: Ipv4Fragmenter,
88 #[cfg(feature = "proto-sixlowpan-fragmentation")]
89 sixlowpan: SixlowpanFragmenter,
90 }
91
92 #[cfg(feature = "proto-ipv4-fragmentation")]
93 pub(crate) struct Ipv4Fragmenter {
94 /// The IPv4 representation.
95 repr: Ipv4Repr,
96 /// The destination hardware address.
97 #[cfg(feature = "medium-ethernet")]
98 dst_hardware_addr: EthernetAddress,
99 /// The offset of the next fragment.
100 frag_offset: u16,
101 /// The identifier of the stream.
102 ident: u16,
103 }
104
105 #[cfg(feature = "proto-sixlowpan-fragmentation")]
106 pub(crate) struct SixlowpanFragmenter {
107 /// The datagram size that is used for the fragmentation headers.
108 datagram_size: u16,
109 /// The datagram tag that is used for the fragmentation headers.
110 datagram_tag: u16,
111 datagram_offset: usize,
112
113 /// The size of the FRAG_N packets.
114 fragn_size: usize,
115
116 /// The link layer IEEE802.15.4 source address.
117 ll_dst_addr: Ieee802154Address,
118 /// The link layer IEEE802.15.4 source address.
119 ll_src_addr: Ieee802154Address,
120 }
121
122 #[cfg(feature = "_proto-fragmentation")]
123 impl Fragmenter {
new() -> Self124 pub(crate) fn new() -> Self {
125 Self {
126 buffer: [0u8; FRAGMENTATION_BUFFER_SIZE],
127 packet_len: 0,
128 sent_bytes: 0,
129
130 #[cfg(feature = "proto-ipv4-fragmentation")]
131 ipv4: Ipv4Fragmenter {
132 repr: Ipv4Repr {
133 src_addr: Ipv4Address::default(),
134 dst_addr: Ipv4Address::default(),
135 next_header: IpProtocol::Unknown(0),
136 payload_len: 0,
137 hop_limit: 0,
138 },
139 #[cfg(feature = "medium-ethernet")]
140 dst_hardware_addr: EthernetAddress::default(),
141 frag_offset: 0,
142 ident: 0,
143 },
144
145 #[cfg(feature = "proto-sixlowpan-fragmentation")]
146 sixlowpan: SixlowpanFragmenter {
147 datagram_size: 0,
148 datagram_tag: 0,
149 datagram_offset: 0,
150 fragn_size: 0,
151 ll_dst_addr: Ieee802154Address::Absent,
152 ll_src_addr: Ieee802154Address::Absent,
153 },
154 }
155 }
156
157 /// Return `true` when everything is transmitted.
158 #[inline]
finished(&self) -> bool159 fn finished(&self) -> bool {
160 self.packet_len == self.sent_bytes
161 }
162
163 /// Returns `true` when there is nothing to transmit.
164 #[inline]
is_empty(&self) -> bool165 fn is_empty(&self) -> bool {
166 self.packet_len == 0
167 }
168
169 // Reset the buffer.
reset(&mut self)170 fn reset(&mut self) {
171 self.packet_len = 0;
172 self.sent_bytes = 0;
173
174 #[cfg(feature = "proto-ipv4-fragmentation")]
175 {
176 self.ipv4.repr = Ipv4Repr {
177 src_addr: Ipv4Address::default(),
178 dst_addr: Ipv4Address::default(),
179 next_header: IpProtocol::Unknown(0),
180 payload_len: 0,
181 hop_limit: 0,
182 };
183 #[cfg(feature = "medium-ethernet")]
184 {
185 self.ipv4.dst_hardware_addr = EthernetAddress::default();
186 }
187 }
188
189 #[cfg(feature = "proto-sixlowpan-fragmentation")]
190 {
191 self.sixlowpan.datagram_size = 0;
192 self.sixlowpan.datagram_tag = 0;
193 self.sixlowpan.fragn_size = 0;
194 self.sixlowpan.ll_dst_addr = Ieee802154Address::Absent;
195 self.sixlowpan.ll_src_addr = Ieee802154Address::Absent;
196 }
197 }
198 }
199
200 macro_rules! check {
201 ($e:expr) => {
202 match $e {
203 Ok(x) => x,
204 Err(_) => {
205 // concat!/stringify! doesn't work with defmt macros
206 #[cfg(not(feature = "defmt"))]
207 net_trace!(concat!("iface: malformed ", stringify!($e)));
208 #[cfg(feature = "defmt")]
209 net_trace!("iface: malformed");
210 return Default::default();
211 }
212 }
213 };
214 }
215 use check;
216
217 /// A network interface.
218 ///
219 /// The network interface logically owns a number of other data structures; to avoid
220 /// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be
221 /// a `&mut [T]`, or `Vec<T>` if a heap is available.
222 pub struct Interface {
223 inner: InterfaceInner,
224 fragments: FragmentsBuffer,
225 fragmenter: Fragmenter,
226 }
227
228 /// The device independent part of an Ethernet network interface.
229 ///
230 /// Separating the device from the data required for processing and dispatching makes
231 /// it possible to borrow them independently. For example, the tx and rx tokens borrow
232 /// the `device` mutably until they're used, which makes it impossible to call other
233 /// methods on the `Interface` in this time (since its `device` field is borrowed
234 /// exclusively). However, it is still possible to call methods on its `inner` field.
235 pub struct InterfaceInner {
236 caps: DeviceCapabilities,
237 now: Instant,
238 rand: Rand,
239
240 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
241 neighbor_cache: Option<NeighborCache>,
242 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
243 hardware_addr: Option<HardwareAddress>,
244 #[cfg(feature = "medium-ieee802154")]
245 sequence_no: u8,
246 #[cfg(feature = "medium-ieee802154")]
247 pan_id: Option<Ieee802154Pan>,
248 #[cfg(feature = "proto-ipv4-fragmentation")]
249 ipv4_id: u16,
250 #[cfg(feature = "proto-sixlowpan")]
251 sixlowpan_address_context:
252 Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>,
253 #[cfg(feature = "proto-sixlowpan-fragmentation")]
254 tag: u16,
255 ip_addrs: Vec<IpCidr, IFACE_MAX_ADDR_COUNT>,
256 #[cfg(feature = "proto-ipv4")]
257 any_ip: bool,
258 routes: Routes,
259 #[cfg(feature = "proto-igmp")]
260 ipv4_multicast_groups: LinearMap<Ipv4Address, (), IFACE_MAX_MULTICAST_GROUP_COUNT>,
261 /// When to report for (all or) the next multicast group membership via IGMP
262 #[cfg(feature = "proto-igmp")]
263 igmp_report_state: IgmpReportState,
264 }
265
266 /// Configuration structure used for creating a network interface.
267 #[non_exhaustive]
268 pub struct Config {
269 /// Random seed.
270 ///
271 /// It is strongly recommended that the random seed is different on each boot,
272 /// to avoid problems with TCP port/sequence collisions.
273 ///
274 /// The seed doesn't have to be cryptographically secure.
275 pub random_seed: u64,
276
277 /// Set the Hardware address the interface will use.
278 ///
279 /// # Panics
280 /// Creating the interface panics if the address is not unicast.
281 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
282 pub hardware_addr: Option<HardwareAddress>,
283
284 /// Set the IEEE802.15.4 PAN ID the interface will use.
285 ///
286 /// **NOTE**: we use the same PAN ID for destination and source.
287 #[cfg(feature = "medium-ieee802154")]
288 pub pan_id: Option<Ieee802154Pan>,
289 }
290
291 impl Config {
new() -> Self292 pub fn new() -> Self {
293 Config {
294 random_seed: 0,
295 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
296 hardware_addr: None,
297 #[cfg(feature = "medium-ieee802154")]
298 pan_id: None,
299 }
300 }
301 }
302
303 impl Default for Config {
default() -> Self304 fn default() -> Self {
305 Self::new()
306 }
307 }
308
309 #[derive(Debug, PartialEq)]
310 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
311 #[cfg(feature = "medium-ethernet")]
312 enum EthernetPacket<'a> {
313 #[cfg(feature = "proto-ipv4")]
314 Arp(ArpRepr),
315 Ip(IpPacket<'a>),
316 }
317
318 #[derive(Debug, PartialEq)]
319 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
320 pub(crate) enum IpPacket<'a> {
321 #[cfg(feature = "proto-ipv4")]
322 Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)),
323 #[cfg(feature = "proto-igmp")]
324 Igmp((Ipv4Repr, IgmpRepr)),
325 #[cfg(feature = "proto-ipv6")]
326 Icmpv6((Ipv6Repr, Icmpv6Repr<'a>)),
327 #[cfg(feature = "socket-raw")]
328 Raw((IpRepr, &'a [u8])),
329 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
330 Udp((IpRepr, UdpRepr, &'a [u8])),
331 #[cfg(feature = "socket-tcp")]
332 Tcp((IpRepr, TcpRepr<'a>)),
333 #[cfg(feature = "socket-dhcpv4")]
334 Dhcpv4((Ipv4Repr, UdpRepr, DhcpRepr<'a>)),
335 }
336
337 impl<'a> IpPacket<'a> {
ip_repr(&self) -> IpRepr338 pub(crate) fn ip_repr(&self) -> IpRepr {
339 match self {
340 #[cfg(feature = "proto-ipv4")]
341 IpPacket::Icmpv4((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr),
342 #[cfg(feature = "proto-igmp")]
343 IpPacket::Igmp((ipv4_repr, _)) => IpRepr::Ipv4(*ipv4_repr),
344 #[cfg(feature = "proto-ipv6")]
345 IpPacket::Icmpv6((ipv6_repr, _)) => IpRepr::Ipv6(*ipv6_repr),
346 #[cfg(feature = "socket-raw")]
347 IpPacket::Raw((ip_repr, _)) => ip_repr.clone(),
348 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
349 IpPacket::Udp((ip_repr, _, _)) => ip_repr.clone(),
350 #[cfg(feature = "socket-tcp")]
351 IpPacket::Tcp((ip_repr, _)) => ip_repr.clone(),
352 #[cfg(feature = "socket-dhcpv4")]
353 IpPacket::Dhcpv4((ipv4_repr, _, _)) => IpRepr::Ipv4(*ipv4_repr),
354 }
355 }
356
emit_payload( &self, _ip_repr: &IpRepr, payload: &mut [u8], caps: &DeviceCapabilities, )357 pub(crate) fn emit_payload(
358 &self,
359 _ip_repr: &IpRepr,
360 payload: &mut [u8],
361 caps: &DeviceCapabilities,
362 ) {
363 match self {
364 #[cfg(feature = "proto-ipv4")]
365 IpPacket::Icmpv4((_, icmpv4_repr)) => {
366 icmpv4_repr.emit(&mut Icmpv4Packet::new_unchecked(payload), &caps.checksum)
367 }
368 #[cfg(feature = "proto-igmp")]
369 IpPacket::Igmp((_, igmp_repr)) => {
370 igmp_repr.emit(&mut IgmpPacket::new_unchecked(payload))
371 }
372 #[cfg(feature = "proto-ipv6")]
373 IpPacket::Icmpv6((_, icmpv6_repr)) => icmpv6_repr.emit(
374 &_ip_repr.src_addr(),
375 &_ip_repr.dst_addr(),
376 &mut Icmpv6Packet::new_unchecked(payload),
377 &caps.checksum,
378 ),
379 #[cfg(feature = "socket-raw")]
380 IpPacket::Raw((_, raw_packet)) => payload.copy_from_slice(raw_packet),
381 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
382 IpPacket::Udp((_, udp_repr, inner_payload)) => udp_repr.emit(
383 &mut UdpPacket::new_unchecked(payload),
384 &_ip_repr.src_addr(),
385 &_ip_repr.dst_addr(),
386 inner_payload.len(),
387 |buf| buf.copy_from_slice(inner_payload),
388 &caps.checksum,
389 ),
390 #[cfg(feature = "socket-tcp")]
391 IpPacket::Tcp((_, mut tcp_repr)) => {
392 // This is a terrible hack to make TCP performance more acceptable on systems
393 // where the TCP buffers are significantly larger than network buffers,
394 // e.g. a 64 kB TCP receive buffer (and so, when empty, a 64k window)
395 // together with four 1500 B Ethernet receive buffers. If left untreated,
396 // this would result in our peer pushing our window and sever packet loss.
397 //
398 // I'm really not happy about this "solution" but I don't know what else to do.
399 if let Some(max_burst_size) = caps.max_burst_size {
400 let mut max_segment_size = caps.max_transmission_unit;
401 max_segment_size -= _ip_repr.header_len();
402 max_segment_size -= tcp_repr.header_len();
403
404 let max_window_size = max_burst_size * max_segment_size;
405 if tcp_repr.window_len as usize > max_window_size {
406 tcp_repr.window_len = max_window_size as u16;
407 }
408 }
409
410 tcp_repr.emit(
411 &mut TcpPacket::new_unchecked(payload),
412 &_ip_repr.src_addr(),
413 &_ip_repr.dst_addr(),
414 &caps.checksum,
415 );
416 }
417 #[cfg(feature = "socket-dhcpv4")]
418 IpPacket::Dhcpv4((_, udp_repr, dhcp_repr)) => udp_repr.emit(
419 &mut UdpPacket::new_unchecked(payload),
420 &_ip_repr.src_addr(),
421 &_ip_repr.dst_addr(),
422 dhcp_repr.buffer_len(),
423 |buf| dhcp_repr.emit(&mut DhcpPacket::new_unchecked(buf)).unwrap(),
424 &caps.checksum,
425 ),
426 }
427 }
428 }
429
430 #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize431 fn icmp_reply_payload_len(len: usize, mtu: usize, header_len: usize) -> usize {
432 // Send back as much of the original payload as will fit within
433 // the minimum MTU required by IPv4. See RFC 1812 § 4.3.2.3 for
434 // more details.
435 //
436 // Since the entire network layer packet must fit within the minimum
437 // MTU supported, the payload must not exceed the following:
438 //
439 // <min mtu> - IP Header Size * 2 - ICMPv4 DstUnreachable hdr size
440 cmp::min(len, mtu - header_len * 2 - 8)
441 }
442
443 #[cfg(feature = "proto-igmp")]
444 enum IgmpReportState {
445 Inactive,
446 ToGeneralQuery {
447 version: IgmpVersion,
448 timeout: Instant,
449 interval: Duration,
450 next_index: usize,
451 },
452 ToSpecificQuery {
453 version: IgmpVersion,
454 timeout: Instant,
455 group: Ipv4Address,
456 },
457 }
458
459 impl Interface {
460 /// Create a network interface using the previously provided configuration.
461 ///
462 /// # Panics
463 /// If a required option is not provided, this function will panic. Required
464 /// options are:
465 ///
466 /// - [ethernet_addr]
467 /// - [neighbor_cache]
468 ///
469 /// [ethernet_addr]: #method.ethernet_addr
470 /// [neighbor_cache]: #method.neighbor_cache
new<D>(config: Config, device: &mut D) -> Self where D: Device + ?Sized,471 pub fn new<D>(config: Config, device: &mut D) -> Self
472 where
473 D: Device + ?Sized,
474 {
475 let caps = device.capabilities();
476
477 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
478 let hardware_addr = match caps.medium {
479 #[cfg(feature = "medium-ethernet")]
480 Medium::Ethernet => Some(
481 config
482 .hardware_addr
483 .expect("hardware_addr required option was not set"),
484 ),
485 #[cfg(feature = "medium-ip")]
486 Medium::Ip => {
487 assert!(
488 config.hardware_addr.is_none(),
489 "hardware_addr is set, but device medium is IP"
490 );
491 None
492 }
493 #[cfg(feature = "medium-ieee802154")]
494 Medium::Ieee802154 => Some(
495 config
496 .hardware_addr
497 .expect("hardware_addr required option was not set"),
498 ),
499 };
500
501 let mut rand = Rand::new(config.random_seed);
502
503 #[cfg(feature = "medium-ieee802154")]
504 let mut sequence_no;
505 #[cfg(feature = "medium-ieee802154")]
506 loop {
507 sequence_no = (rand.rand_u32() & 0xff) as u8;
508 if sequence_no != 0 {
509 break;
510 }
511 }
512
513 #[cfg(feature = "proto-sixlowpan")]
514 let mut tag;
515
516 #[cfg(feature = "proto-sixlowpan")]
517 loop {
518 tag = rand.rand_u16();
519 if tag != 0 {
520 break;
521 }
522 }
523
524 #[cfg(feature = "proto-ipv4")]
525 let mut ipv4_id;
526
527 #[cfg(feature = "proto-ipv4")]
528 loop {
529 ipv4_id = rand.rand_u16();
530 if ipv4_id != 0 {
531 break;
532 }
533 }
534
535 Interface {
536 fragments: FragmentsBuffer {
537 #[cfg(feature = "proto-sixlowpan")]
538 decompress_buf: [0u8; sixlowpan::MAX_DECOMPRESSED_LEN],
539
540 #[cfg(feature = "_proto-fragmentation")]
541 assembler: PacketAssemblerSet::new(),
542 #[cfg(feature = "_proto-fragmentation")]
543 reassembly_timeout: Duration::from_secs(60),
544 },
545 fragmenter: Fragmenter::new(),
546 inner: InterfaceInner {
547 now: Instant::from_secs(0),
548 caps,
549 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
550 hardware_addr,
551 ip_addrs: Vec::new(),
552 #[cfg(feature = "proto-ipv4")]
553 any_ip: false,
554 routes: Routes::new(),
555 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
556 neighbor_cache: Some(NeighborCache::new()),
557 #[cfg(feature = "proto-igmp")]
558 ipv4_multicast_groups: LinearMap::new(),
559 #[cfg(feature = "proto-igmp")]
560 igmp_report_state: IgmpReportState::Inactive,
561 #[cfg(feature = "medium-ieee802154")]
562 sequence_no,
563 #[cfg(feature = "medium-ieee802154")]
564 pan_id: config.pan_id,
565 #[cfg(feature = "proto-sixlowpan-fragmentation")]
566 tag,
567 #[cfg(feature = "proto-ipv4-fragmentation")]
568 ipv4_id,
569 #[cfg(feature = "proto-sixlowpan")]
570 sixlowpan_address_context: Vec::new(),
571 rand,
572 },
573 }
574 }
575
576 /// Get the socket context.
577 ///
578 /// The context is needed for some socket methods.
context(&mut self) -> &mut InterfaceInner579 pub fn context(&mut self) -> &mut InterfaceInner {
580 &mut self.inner
581 }
582
583 /// Get the HardwareAddress address of the interface.
584 ///
585 /// # Panics
586 /// This function panics if the medium is not Ethernet or Ieee802154.
587 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
hardware_addr(&self) -> HardwareAddress588 pub fn hardware_addr(&self) -> HardwareAddress {
589 #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
590 assert!(self.inner.caps.medium == Medium::Ethernet);
591 #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
592 assert!(self.inner.caps.medium == Medium::Ieee802154);
593
594 #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
595 assert!(
596 self.inner.caps.medium == Medium::Ethernet
597 || self.inner.caps.medium == Medium::Ieee802154
598 );
599
600 self.inner.hardware_addr.unwrap()
601 }
602
603 /// Set the HardwareAddress address of the interface.
604 ///
605 /// # Panics
606 /// This function panics if the address is not unicast, and if the medium is not Ethernet or
607 /// Ieee802154.
608 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
set_hardware_addr(&mut self, addr: HardwareAddress)609 pub fn set_hardware_addr(&mut self, addr: HardwareAddress) {
610 #[cfg(all(feature = "medium-ethernet", not(feature = "medium-ieee802154")))]
611 assert!(self.inner.caps.medium == Medium::Ethernet);
612 #[cfg(all(feature = "medium-ieee802154", not(feature = "medium-ethernet")))]
613 assert!(self.inner.caps.medium == Medium::Ieee802154);
614
615 #[cfg(all(feature = "medium-ieee802154", feature = "medium-ethernet"))]
616 assert!(
617 self.inner.caps.medium == Medium::Ethernet
618 || self.inner.caps.medium == Medium::Ieee802154
619 );
620
621 InterfaceInner::check_hardware_addr(&addr);
622 self.inner.hardware_addr = Some(addr);
623 }
624
625 /// Get the IP addresses of the interface.
ip_addrs(&self) -> &[IpCidr]626 pub fn ip_addrs(&self) -> &[IpCidr] {
627 self.inner.ip_addrs.as_ref()
628 }
629
630 /// Get the first IPv4 address if present.
631 #[cfg(feature = "proto-ipv4")]
ipv4_addr(&self) -> Option<Ipv4Address>632 pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
633 self.inner.ipv4_addr()
634 }
635
636 /// Get the first IPv6 address if present.
637 #[cfg(feature = "proto-ipv6")]
ipv6_addr(&self) -> Option<Ipv6Address>638 pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
639 self.inner.ipv6_addr()
640 }
641
642 /// Update the IP addresses of the interface.
643 ///
644 /// # Panics
645 /// This function panics if any of the addresses are not unicast.
update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F)646 pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr, IFACE_MAX_ADDR_COUNT>)>(&mut self, f: F) {
647 f(&mut self.inner.ip_addrs);
648 InterfaceInner::flush_cache(&mut self.inner);
649 InterfaceInner::check_ip_addrs(&self.inner.ip_addrs)
650 }
651
652 /// Check whether the interface has the given IP address assigned.
has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool653 pub fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
654 self.inner.has_ip_addr(addr)
655 }
656
routes(&self) -> &Routes657 pub fn routes(&self) -> &Routes {
658 &self.inner.routes
659 }
660
routes_mut(&mut self) -> &mut Routes661 pub fn routes_mut(&mut self) -> &mut Routes {
662 &mut self.inner.routes
663 }
664
665 /// Enable or disable the AnyIP capability.
666 ///
667 /// AnyIP allowins packets to be received
668 /// locally on IPv4 addresses other than the interface's configured [ip_addrs].
669 /// When AnyIP is enabled and a route prefix in [`routes`](Self::routes) specifies one of
670 /// the interface's [`ip_addrs`](Self::ip_addrs) as its gateway, the interface will accept
671 /// packets addressed to that prefix.
672 ///
673 /// # IPv6
674 ///
675 /// This option is not available or required for IPv6 as packets sent to
676 /// the interface are not filtered by IPv6 address.
677 #[cfg(feature = "proto-ipv4")]
set_any_ip(&mut self, any_ip: bool)678 pub fn set_any_ip(&mut self, any_ip: bool) {
679 self.inner.any_ip = any_ip;
680 }
681
682 /// Get whether AnyIP is enabled.
683 ///
684 /// See [`set_any_ip`](Self::set_any_ip) for details on AnyIP
685 #[cfg(feature = "proto-ipv4")]
any_ip(&self) -> bool686 pub fn any_ip(&self) -> bool {
687 self.inner.any_ip
688 }
689
690 /// Get the 6LoWPAN address contexts.
691 #[cfg(feature = "proto-sixlowpan")]
sixlowpan_address_context( &self, ) -> &Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>692 pub fn sixlowpan_address_context(
693 &self,
694 ) -> &Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
695 &self.inner.sixlowpan_address_context
696 }
697
698 /// Get a mutable reference to the 6LoWPAN address contexts.
699 #[cfg(feature = "proto-sixlowpan")]
sixlowpan_address_context_mut( &mut self, ) -> &mut Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT>700 pub fn sixlowpan_address_context_mut(
701 &mut self,
702 ) -> &mut Vec<SixlowpanAddressContext, IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT> {
703 &mut self.inner.sixlowpan_address_context
704 }
705
706 /// Get the packet reassembly timeout.
707 #[cfg(feature = "_proto-fragmentation")]
reassembly_timeout(&self) -> Duration708 pub fn reassembly_timeout(&self) -> Duration {
709 self.fragments.reassembly_timeout
710 }
711
712 /// Set the packet reassembly timeout.
713 #[cfg(feature = "_proto-fragmentation")]
set_reassembly_timeout(&mut self, timeout: Duration)714 pub fn set_reassembly_timeout(&mut self, timeout: Duration) {
715 if timeout > Duration::from_secs(60) {
716 net_debug!("RFC 4944 specifies that the reassembly timeout MUST be set to a maximum of 60 seconds");
717 }
718 self.fragments.reassembly_timeout = timeout;
719 }
720
721 /// Transmit packets queued in the given sockets, and receive packets queued
722 /// in the device.
723 ///
724 /// This function returns a boolean value indicating whether any packets were
725 /// processed or emitted, and thus, whether the readiness of any socket might
726 /// have changed.
poll<D>( &mut self, timestamp: Instant, device: &mut D, sockets: &mut SocketSet<'_>, ) -> bool where D: Device + ?Sized,727 pub fn poll<D>(
728 &mut self,
729 timestamp: Instant,
730 device: &mut D,
731 sockets: &mut SocketSet<'_>,
732 ) -> bool
733 where
734 D: Device + ?Sized,
735 {
736 self.inner.now = timestamp;
737
738 #[cfg(feature = "_proto-fragmentation")]
739 self.fragments.assembler.remove_expired(timestamp);
740
741 match self.inner.caps.medium {
742 #[cfg(feature = "medium-ieee802154")]
743 Medium::Ieee802154 =>
744 {
745 #[cfg(feature = "proto-sixlowpan-fragmentation")]
746 if self.sixlowpan_egress(device) {
747 return true;
748 }
749 }
750 #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
751 _ =>
752 {
753 #[cfg(feature = "proto-ipv4-fragmentation")]
754 if self.ipv4_egress(device) {
755 return true;
756 }
757 }
758 }
759
760 let mut readiness_may_have_changed = false;
761
762 loop {
763 let mut did_something = false;
764 did_something |= self.socket_ingress(device, sockets);
765 did_something |= self.socket_egress(device, sockets);
766
767 #[cfg(feature = "proto-igmp")]
768 {
769 did_something |= self.igmp_egress(device);
770 }
771
772 if did_something {
773 readiness_may_have_changed = true;
774 } else {
775 break;
776 }
777 }
778
779 readiness_may_have_changed
780 }
781
782 /// Return a _soft deadline_ for calling [poll] the next time.
783 /// The [Instant] returned is the time at which you should call [poll] next.
784 /// It is harmless (but wastes energy) to call it before the [Instant], and
785 /// potentially harmful (impacting quality of service) to call it after the
786 /// [Instant]
787 ///
788 /// [poll]: #method.poll
789 /// [Instant]: struct.Instant.html
poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Instant>790 pub fn poll_at(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Instant> {
791 self.inner.now = timestamp;
792
793 #[cfg(feature = "_proto-fragmentation")]
794 if !self.fragmenter.is_empty() {
795 return Some(Instant::from_millis(0));
796 }
797
798 let inner = &mut self.inner;
799
800 sockets
801 .items()
802 .filter_map(move |item| {
803 let socket_poll_at = item.socket.poll_at(inner);
804 match item
805 .meta
806 .poll_at(socket_poll_at, |ip_addr| inner.has_neighbor(&ip_addr))
807 {
808 PollAt::Ingress => None,
809 PollAt::Time(instant) => Some(instant),
810 PollAt::Now => Some(Instant::from_millis(0)),
811 }
812 })
813 .min()
814 }
815
816 /// Return an _advisory wait time_ for calling [poll] the next time.
817 /// The [Duration] returned is the time left to wait before calling [poll] next.
818 /// It is harmless (but wastes energy) to call it before the [Duration] has passed,
819 /// and potentially harmful (impacting quality of service) to call it after the
820 /// [Duration] has passed.
821 ///
822 /// [poll]: #method.poll
823 /// [Duration]: struct.Duration.html
poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Duration>824 pub fn poll_delay(&mut self, timestamp: Instant, sockets: &SocketSet<'_>) -> Option<Duration> {
825 match self.poll_at(timestamp, sockets) {
826 Some(poll_at) if timestamp < poll_at => Some(poll_at - timestamp),
827 Some(_) => Some(Duration::from_millis(0)),
828 _ => None,
829 }
830 }
831
socket_ingress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool where D: Device + ?Sized,832 fn socket_ingress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool
833 where
834 D: Device + ?Sized,
835 {
836 let mut processed_any = false;
837
838 while let Some((rx_token, tx_token)) = device.receive(self.inner.now) {
839 rx_token.consume(|frame| {
840 match self.inner.caps.medium {
841 #[cfg(feature = "medium-ethernet")]
842 Medium::Ethernet => {
843 if let Some(packet) =
844 self.inner
845 .process_ethernet(sockets, &frame, &mut self.fragments)
846 {
847 if let Err(err) =
848 self.inner.dispatch(tx_token, packet, &mut self.fragmenter)
849 {
850 net_debug!("Failed to send response: {:?}", err);
851 }
852 }
853 }
854 #[cfg(feature = "medium-ip")]
855 Medium::Ip => {
856 if let Some(packet) =
857 self.inner.process_ip(sockets, &frame, &mut self.fragments)
858 {
859 if let Err(err) =
860 self.inner
861 .dispatch_ip(tx_token, packet, &mut self.fragmenter)
862 {
863 net_debug!("Failed to send response: {:?}", err);
864 }
865 }
866 }
867 #[cfg(feature = "medium-ieee802154")]
868 Medium::Ieee802154 => {
869 if let Some(packet) =
870 self.inner
871 .process_ieee802154(sockets, &frame, &mut self.fragments)
872 {
873 if let Err(err) =
874 self.inner
875 .dispatch_ip(tx_token, packet, &mut self.fragmenter)
876 {
877 net_debug!("Failed to send response: {:?}", err);
878 }
879 }
880 }
881 }
882 processed_any = true;
883 });
884 }
885
886 processed_any
887 }
888
socket_egress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool where D: Device + ?Sized,889 fn socket_egress<D>(&mut self, device: &mut D, sockets: &mut SocketSet<'_>) -> bool
890 where
891 D: Device + ?Sized,
892 {
893 let _caps = device.capabilities();
894
895 enum EgressError {
896 Exhausted,
897 Dispatch(DispatchError),
898 }
899
900 let mut emitted_any = false;
901 for item in sockets.items_mut() {
902 if !item
903 .meta
904 .egress_permitted(self.inner.now, |ip_addr| self.inner.has_neighbor(&ip_addr))
905 {
906 continue;
907 }
908
909 let mut neighbor_addr = None;
910 let mut respond = |inner: &mut InterfaceInner, response: IpPacket| {
911 neighbor_addr = Some(response.ip_repr().dst_addr());
912 let t = device.transmit(inner.now).ok_or_else(|| {
913 net_debug!("failed to transmit IP: device exhausted");
914 EgressError::Exhausted
915 })?;
916
917 inner
918 .dispatch_ip(t, response, &mut self.fragmenter)
919 .map_err(EgressError::Dispatch)?;
920
921 emitted_any = true;
922
923 Ok(())
924 };
925
926 let result = match &mut item.socket {
927 #[cfg(feature = "socket-raw")]
928 Socket::Raw(socket) => socket.dispatch(&mut self.inner, |inner, response| {
929 respond(inner, IpPacket::Raw(response))
930 }),
931 #[cfg(feature = "socket-icmp")]
932 Socket::Icmp(socket) => {
933 socket.dispatch(&mut self.inner, |inner, response| match response {
934 #[cfg(feature = "proto-ipv4")]
935 (IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) => {
936 respond(inner, IpPacket::Icmpv4((ipv4_repr, icmpv4_repr)))
937 }
938 #[cfg(feature = "proto-ipv6")]
939 (IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) => {
940 respond(inner, IpPacket::Icmpv6((ipv6_repr, icmpv6_repr)))
941 }
942 #[allow(unreachable_patterns)]
943 _ => unreachable!(),
944 })
945 }
946 #[cfg(feature = "socket-udp")]
947 Socket::Udp(socket) => socket.dispatch(&mut self.inner, |inner, response| {
948 respond(inner, IpPacket::Udp(response))
949 }),
950 #[cfg(feature = "socket-tcp")]
951 Socket::Tcp(socket) => socket.dispatch(&mut self.inner, |inner, response| {
952 respond(inner, IpPacket::Tcp(response))
953 }),
954 #[cfg(feature = "socket-dhcpv4")]
955 Socket::Dhcpv4(socket) => socket.dispatch(&mut self.inner, |inner, response| {
956 respond(inner, IpPacket::Dhcpv4(response))
957 }),
958 #[cfg(feature = "socket-dns")]
959 Socket::Dns(socket) => socket.dispatch(&mut self.inner, |inner, response| {
960 respond(inner, IpPacket::Udp(response))
961 }),
962 };
963
964 match result {
965 Err(EgressError::Exhausted) => break, // Device buffer full.
966 Err(EgressError::Dispatch(_)) => {
967 // `NeighborCache` already takes care of rate limiting the neighbor discovery
968 // requests from the socket. However, without an additional rate limiting
969 // mechanism, we would spin on every socket that has yet to discover its
970 // neighbor.
971 item.meta.neighbor_missing(
972 self.inner.now,
973 neighbor_addr.expect("non-IP response packet"),
974 );
975 }
976 Ok(()) => {}
977 }
978 }
979 emitted_any
980 }
981
982 /// Process fragments that still need to be sent for IPv4 packets.
983 ///
984 /// This function returns a boolean value indicating whether any packets were
985 /// processed or emitted, and thus, whether the readiness of any socket might
986 /// have changed.
987 #[cfg(feature = "proto-ipv4-fragmentation")]
ipv4_egress<D>(&mut self, device: &mut D) -> bool where D: Device + ?Sized,988 fn ipv4_egress<D>(&mut self, device: &mut D) -> bool
989 where
990 D: Device + ?Sized,
991 {
992 // Reset the buffer when we transmitted everything.
993 if self.fragmenter.finished() {
994 self.fragmenter.reset();
995 }
996
997 if self.fragmenter.is_empty() {
998 return false;
999 }
1000
1001 let pkt = &self.fragmenter;
1002 if pkt.packet_len > pkt.sent_bytes {
1003 if let Some(tx_token) = device.transmit(self.inner.now) {
1004 self.inner
1005 .dispatch_ipv4_frag(tx_token, &mut self.fragmenter);
1006 return true;
1007 }
1008 }
1009 false
1010 }
1011
1012 /// Process fragments that still need to be sent for 6LoWPAN packets.
1013 ///
1014 /// This function returns a boolean value indicating whether any packets were
1015 /// processed or emitted, and thus, whether the readiness of any socket might
1016 /// have changed.
1017 #[cfg(feature = "proto-sixlowpan-fragmentation")]
sixlowpan_egress<D>(&mut self, device: &mut D) -> bool where D: Device + ?Sized,1018 fn sixlowpan_egress<D>(&mut self, device: &mut D) -> bool
1019 where
1020 D: Device + ?Sized,
1021 {
1022 // Reset the buffer when we transmitted everything.
1023 if self.fragmenter.finished() {
1024 self.fragmenter.reset();
1025 }
1026
1027 if self.fragmenter.is_empty() {
1028 return false;
1029 }
1030
1031 let pkt = &self.fragmenter;
1032 if pkt.packet_len > pkt.sent_bytes {
1033 if let Some(tx_token) = device.transmit(self.inner.now) {
1034 self.inner
1035 .dispatch_ieee802154_frag(tx_token, &mut self.fragmenter);
1036 return true;
1037 }
1038 }
1039 false
1040 }
1041 }
1042
1043 impl InterfaceInner {
1044 #[allow(unused)] // unused depending on which sockets are enabled
now(&self) -> Instant1045 pub(crate) fn now(&self) -> Instant {
1046 self.now
1047 }
1048
1049 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1050 #[allow(unused)] // unused depending on which sockets are enabled
hardware_addr(&self) -> Option<HardwareAddress>1051 pub(crate) fn hardware_addr(&self) -> Option<HardwareAddress> {
1052 self.hardware_addr
1053 }
1054
1055 #[allow(unused)] // unused depending on which sockets are enabled
checksum_caps(&self) -> ChecksumCapabilities1056 pub(crate) fn checksum_caps(&self) -> ChecksumCapabilities {
1057 self.caps.checksum.clone()
1058 }
1059
1060 #[allow(unused)] // unused depending on which sockets are enabled
ip_mtu(&self) -> usize1061 pub(crate) fn ip_mtu(&self) -> usize {
1062 self.caps.ip_mtu()
1063 }
1064
1065 #[allow(unused)] // unused depending on which sockets are enabled, and in tests
rand(&mut self) -> &mut Rand1066 pub(crate) fn rand(&mut self) -> &mut Rand {
1067 &mut self.rand
1068 }
1069
1070 #[allow(unused)] // unused depending on which sockets are enabled
get_source_address(&mut self, dst_addr: IpAddress) -> Option<IpAddress>1071 pub(crate) fn get_source_address(&mut self, dst_addr: IpAddress) -> Option<IpAddress> {
1072 let v = dst_addr.version();
1073 for cidr in self.ip_addrs.iter() {
1074 let addr = cidr.address();
1075 if addr.version() == v {
1076 return Some(addr);
1077 }
1078 }
1079 None
1080 }
1081
1082 #[cfg(feature = "proto-ipv4")]
1083 #[allow(unused)]
get_source_address_ipv4( &mut self, _dst_addr: Ipv4Address, ) -> Option<Ipv4Address>1084 pub(crate) fn get_source_address_ipv4(
1085 &mut self,
1086 _dst_addr: Ipv4Address,
1087 ) -> Option<Ipv4Address> {
1088 for cidr in self.ip_addrs.iter() {
1089 #[allow(irrefutable_let_patterns)] // if only ipv4 is enabled
1090 if let IpCidr::Ipv4(cidr) = cidr {
1091 return Some(cidr.address());
1092 }
1093 }
1094 None
1095 }
1096
1097 #[cfg(feature = "proto-ipv6")]
1098 #[allow(unused)]
get_source_address_ipv6( &mut self, _dst_addr: Ipv6Address, ) -> Option<Ipv6Address>1099 pub(crate) fn get_source_address_ipv6(
1100 &mut self,
1101 _dst_addr: Ipv6Address,
1102 ) -> Option<Ipv6Address> {
1103 for cidr in self.ip_addrs.iter() {
1104 #[allow(irrefutable_let_patterns)] // if only ipv6 is enabled
1105 if let IpCidr::Ipv6(cidr) = cidr {
1106 return Some(cidr.address());
1107 }
1108 }
1109 None
1110 }
1111
1112 #[cfg(test)]
mock() -> Self1113 pub(crate) fn mock() -> Self {
1114 Self {
1115 caps: DeviceCapabilities {
1116 #[cfg(feature = "medium-ethernet")]
1117 medium: crate::phy::Medium::Ethernet,
1118 #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ip"))]
1119 medium: crate::phy::Medium::Ip,
1120 #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
1121 medium: crate::phy::Medium::Ieee802154,
1122
1123 checksum: crate::phy::ChecksumCapabilities {
1124 #[cfg(feature = "proto-ipv4")]
1125 icmpv4: crate::phy::Checksum::Both,
1126 #[cfg(feature = "proto-ipv6")]
1127 icmpv6: crate::phy::Checksum::Both,
1128 ipv4: crate::phy::Checksum::Both,
1129 tcp: crate::phy::Checksum::Both,
1130 udp: crate::phy::Checksum::Both,
1131 },
1132 max_burst_size: None,
1133 #[cfg(feature = "medium-ethernet")]
1134 max_transmission_unit: 1514,
1135 #[cfg(not(feature = "medium-ethernet"))]
1136 max_transmission_unit: 1500,
1137 },
1138 now: Instant::from_millis_const(0),
1139
1140 ip_addrs: Vec::from_slice(&[
1141 #[cfg(feature = "proto-ipv4")]
1142 IpCidr::Ipv4(Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 1), 24)),
1143 #[cfg(feature = "proto-ipv6")]
1144 IpCidr::Ipv6(Ipv6Cidr::new(
1145 Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
1146 64,
1147 )),
1148 ])
1149 .unwrap(),
1150 rand: Rand::new(1234),
1151 routes: Routes::new(),
1152
1153 #[cfg(feature = "proto-ipv4")]
1154 any_ip: false,
1155
1156 #[cfg(feature = "medium-ieee802154")]
1157 pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)),
1158 #[cfg(feature = "medium-ieee802154")]
1159 sequence_no: 1,
1160
1161 #[cfg(feature = "proto-sixlowpan-fragmentation")]
1162 tag: 1,
1163
1164 #[cfg(feature = "proto-sixlowpan")]
1165 sixlowpan_address_context: Vec::new(),
1166
1167 #[cfg(feature = "proto-ipv4-fragmentation")]
1168 ipv4_id: 1,
1169
1170 #[cfg(feature = "medium-ethernet")]
1171 hardware_addr: Some(crate::wire::HardwareAddress::Ethernet(
1172 crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]),
1173 )),
1174 #[cfg(all(not(feature = "medium-ethernet"), feature = "medium-ieee802154"))]
1175 hardware_addr: Some(crate::wire::HardwareAddress::Ieee802154(
1176 crate::wire::Ieee802154Address::Extended([
1177 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x2, 0x2,
1178 ]),
1179 )),
1180
1181 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1182 neighbor_cache: None,
1183
1184 #[cfg(feature = "proto-igmp")]
1185 igmp_report_state: IgmpReportState::Inactive,
1186 #[cfg(feature = "proto-igmp")]
1187 ipv4_multicast_groups: LinearMap::new(),
1188 }
1189 }
1190
1191 #[cfg(test)]
1192 #[allow(unused)] // unused depending on which sockets are enabled
set_now(&mut self, now: Instant)1193 pub(crate) fn set_now(&mut self, now: Instant) {
1194 self.now = now
1195 }
1196
1197 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
check_hardware_addr(addr: &HardwareAddress)1198 fn check_hardware_addr(addr: &HardwareAddress) {
1199 if !addr.is_unicast() {
1200 panic!("Ethernet address {addr} is not unicast")
1201 }
1202 }
1203
check_ip_addrs(addrs: &[IpCidr])1204 fn check_ip_addrs(addrs: &[IpCidr]) {
1205 for cidr in addrs {
1206 if !cidr.address().is_unicast() && !cidr.address().is_unspecified() {
1207 panic!("IP address {} is not unicast", cidr.address())
1208 }
1209 }
1210 }
1211
1212 #[cfg(feature = "medium-ieee802154")]
get_sequence_number(&mut self) -> u81213 fn get_sequence_number(&mut self) -> u8 {
1214 let no = self.sequence_no;
1215 self.sequence_no = self.sequence_no.wrapping_add(1);
1216 no
1217 }
1218
1219 #[cfg(feature = "proto-ipv4-fragmentation")]
get_ipv4_ident(&mut self) -> u161220 fn get_ipv4_ident(&mut self) -> u16 {
1221 let ipv4_id = self.ipv4_id;
1222 self.ipv4_id = self.ipv4_id.wrapping_add(1);
1223 ipv4_id
1224 }
1225
1226 #[cfg(feature = "proto-sixlowpan-fragmentation")]
get_sixlowpan_fragment_tag(&mut self) -> u161227 fn get_sixlowpan_fragment_tag(&mut self) -> u16 {
1228 let tag = self.tag;
1229 self.tag = self.tag.wrapping_add(1);
1230 tag
1231 }
1232
1233 /// Determine if the given `Ipv6Address` is the solicited node
1234 /// multicast address for a IPv6 addresses assigned to the interface.
1235 /// See [RFC 4291 § 2.7.1] for more details.
1236 ///
1237 /// [RFC 4291 § 2.7.1]: https://tools.ietf.org/html/rfc4291#section-2.7.1
1238 #[cfg(feature = "proto-ipv6")]
has_solicited_node(&self, addr: Ipv6Address) -> bool1239 pub fn has_solicited_node(&self, addr: Ipv6Address) -> bool {
1240 self.ip_addrs.iter().any(|cidr| {
1241 match *cidr {
1242 IpCidr::Ipv6(cidr) if cidr.address() != Ipv6Address::LOOPBACK => {
1243 // Take the lower order 24 bits of the IPv6 address and
1244 // append those bits to FF02:0:0:0:0:1:FF00::/104.
1245 addr.as_bytes()[14..] == cidr.address().as_bytes()[14..]
1246 }
1247 _ => false,
1248 }
1249 })
1250 }
1251
1252 /// Check whether the interface has the given IP address assigned.
has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool1253 fn has_ip_addr<T: Into<IpAddress>>(&self, addr: T) -> bool {
1254 let addr = addr.into();
1255 self.ip_addrs.iter().any(|probe| probe.address() == addr)
1256 }
1257
1258 /// Get the first IPv4 address of the interface.
1259 #[cfg(feature = "proto-ipv4")]
ipv4_addr(&self) -> Option<Ipv4Address>1260 pub fn ipv4_addr(&self) -> Option<Ipv4Address> {
1261 self.ip_addrs.iter().find_map(|addr| match *addr {
1262 IpCidr::Ipv4(cidr) => Some(cidr.address()),
1263 #[allow(unreachable_patterns)]
1264 _ => None,
1265 })
1266 }
1267
1268 /// Get the first IPv6 address if present.
1269 #[cfg(feature = "proto-ipv6")]
ipv6_addr(&self) -> Option<Ipv6Address>1270 pub fn ipv6_addr(&self) -> Option<Ipv6Address> {
1271 self.ip_addrs.iter().find_map(|addr| match *addr {
1272 IpCidr::Ipv6(cidr) => Some(cidr.address()),
1273 #[allow(unreachable_patterns)]
1274 _ => None,
1275 })
1276 }
1277
1278 #[cfg(not(feature = "proto-igmp"))]
has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool1279 fn has_multicast_group<T: Into<IpAddress>>(&self, addr: T) -> bool {
1280 false
1281 }
1282
1283 #[cfg(feature = "medium-ip")]
process_ip<'frame, T: AsRef<[u8]>>( &mut self, sockets: &mut SocketSet, ip_payload: &'frame T, frag: &'frame mut FragmentsBuffer, ) -> Option<IpPacket<'frame>>1284 fn process_ip<'frame, T: AsRef<[u8]>>(
1285 &mut self,
1286 sockets: &mut SocketSet,
1287 ip_payload: &'frame T,
1288 frag: &'frame mut FragmentsBuffer,
1289 ) -> Option<IpPacket<'frame>> {
1290 match IpVersion::of_packet(ip_payload.as_ref()) {
1291 #[cfg(feature = "proto-ipv4")]
1292 Ok(IpVersion::Ipv4) => {
1293 let ipv4_packet = check!(Ipv4Packet::new_checked(ip_payload));
1294
1295 self.process_ipv4(sockets, &ipv4_packet, frag)
1296 }
1297 #[cfg(feature = "proto-ipv6")]
1298 Ok(IpVersion::Ipv6) => {
1299 let ipv6_packet = check!(Ipv6Packet::new_checked(ip_payload));
1300 self.process_ipv6(sockets, &ipv6_packet)
1301 }
1302 // Drop all other traffic.
1303 _ => None,
1304 }
1305 }
1306
1307 #[cfg(feature = "socket-raw")]
raw_socket_filter( &mut self, sockets: &mut SocketSet, ip_repr: &IpRepr, ip_payload: &[u8], ) -> bool1308 fn raw_socket_filter(
1309 &mut self,
1310 sockets: &mut SocketSet,
1311 ip_repr: &IpRepr,
1312 ip_payload: &[u8],
1313 ) -> bool {
1314 let mut handled_by_raw_socket = false;
1315
1316 // Pass every IP packet to all raw sockets we have registered.
1317 for raw_socket in sockets
1318 .items_mut()
1319 .filter_map(|i| raw::Socket::downcast_mut(&mut i.socket))
1320 {
1321 if raw_socket.accepts(ip_repr) {
1322 raw_socket.process(self, ip_repr, ip_payload);
1323 handled_by_raw_socket = true;
1324 }
1325 }
1326 handled_by_raw_socket
1327 }
1328
1329 /// Checks if an incoming packet has a broadcast address for the interfaces
1330 /// associated ipv4 addresses.
1331 #[cfg(feature = "proto-ipv4")]
is_subnet_broadcast(&self, address: Ipv4Address) -> bool1332 fn is_subnet_broadcast(&self, address: Ipv4Address) -> bool {
1333 self.ip_addrs
1334 .iter()
1335 .filter_map(|own_cidr| match own_cidr {
1336 IpCidr::Ipv4(own_ip) => Some(own_ip.broadcast()?),
1337 #[cfg(feature = "proto-ipv6")]
1338 IpCidr::Ipv6(_) => None,
1339 })
1340 .any(|broadcast_address| address == broadcast_address)
1341 }
1342
1343 /// Checks if an ipv4 address is broadcast, taking into account subnet broadcast addresses
1344 #[cfg(feature = "proto-ipv4")]
is_broadcast_v4(&self, address: Ipv4Address) -> bool1345 fn is_broadcast_v4(&self, address: Ipv4Address) -> bool {
1346 address.is_broadcast() || self.is_subnet_broadcast(address)
1347 }
1348
1349 /// Checks if an ipv4 address is unicast, taking into account subnet broadcast addresses
1350 #[cfg(feature = "proto-ipv4")]
is_unicast_v4(&self, address: Ipv4Address) -> bool1351 fn is_unicast_v4(&self, address: Ipv4Address) -> bool {
1352 address.is_unicast() && !self.is_subnet_broadcast(address)
1353 }
1354
1355 #[cfg(any(feature = "socket-udp", feature = "socket-dns"))]
process_udp<'frame>( &mut self, sockets: &mut SocketSet, ip_repr: IpRepr, udp_repr: UdpRepr, handled_by_raw_socket: bool, udp_payload: &'frame [u8], ip_payload: &'frame [u8], ) -> Option<IpPacket<'frame>>1356 fn process_udp<'frame>(
1357 &mut self,
1358 sockets: &mut SocketSet,
1359 ip_repr: IpRepr,
1360 udp_repr: UdpRepr,
1361 handled_by_raw_socket: bool,
1362 udp_payload: &'frame [u8],
1363 ip_payload: &'frame [u8],
1364 ) -> Option<IpPacket<'frame>> {
1365 #[cfg(feature = "socket-udp")]
1366 for udp_socket in sockets
1367 .items_mut()
1368 .filter_map(|i| udp::Socket::downcast_mut(&mut i.socket))
1369 {
1370 if udp_socket.accepts(self, &ip_repr, &udp_repr) {
1371 udp_socket.process(self, &ip_repr, &udp_repr, udp_payload);
1372 return None;
1373 }
1374 }
1375
1376 #[cfg(feature = "socket-dns")]
1377 for dns_socket in sockets
1378 .items_mut()
1379 .filter_map(|i| dns::Socket::downcast_mut(&mut i.socket))
1380 {
1381 if dns_socket.accepts(&ip_repr, &udp_repr) {
1382 dns_socket.process(self, &ip_repr, &udp_repr, udp_payload);
1383 return None;
1384 }
1385 }
1386
1387 // The packet wasn't handled by a socket, send an ICMP port unreachable packet.
1388 match ip_repr {
1389 #[cfg(feature = "proto-ipv4")]
1390 IpRepr::Ipv4(_) if handled_by_raw_socket => None,
1391 #[cfg(feature = "proto-ipv6")]
1392 IpRepr::Ipv6(_) if handled_by_raw_socket => None,
1393 #[cfg(feature = "proto-ipv4")]
1394 IpRepr::Ipv4(ipv4_repr) => {
1395 let payload_len =
1396 icmp_reply_payload_len(ip_payload.len(), IPV4_MIN_MTU, ipv4_repr.buffer_len());
1397 let icmpv4_reply_repr = Icmpv4Repr::DstUnreachable {
1398 reason: Icmpv4DstUnreachable::PortUnreachable,
1399 header: ipv4_repr,
1400 data: &ip_payload[0..payload_len],
1401 };
1402 self.icmpv4_reply(ipv4_repr, icmpv4_reply_repr)
1403 }
1404 #[cfg(feature = "proto-ipv6")]
1405 IpRepr::Ipv6(ipv6_repr) => {
1406 let payload_len =
1407 icmp_reply_payload_len(ip_payload.len(), IPV6_MIN_MTU, ipv6_repr.buffer_len());
1408 let icmpv6_reply_repr = Icmpv6Repr::DstUnreachable {
1409 reason: Icmpv6DstUnreachable::PortUnreachable,
1410 header: ipv6_repr,
1411 data: &ip_payload[0..payload_len],
1412 };
1413 self.icmpv6_reply(ipv6_repr, icmpv6_reply_repr)
1414 }
1415 }
1416 }
1417
1418 #[cfg(feature = "socket-tcp")]
process_tcp<'frame>( &mut self, sockets: &mut SocketSet, ip_repr: IpRepr, ip_payload: &'frame [u8], ) -> Option<IpPacket<'frame>>1419 pub(crate) fn process_tcp<'frame>(
1420 &mut self,
1421 sockets: &mut SocketSet,
1422 ip_repr: IpRepr,
1423 ip_payload: &'frame [u8],
1424 ) -> Option<IpPacket<'frame>> {
1425 let (src_addr, dst_addr) = (ip_repr.src_addr(), ip_repr.dst_addr());
1426 let tcp_packet = check!(TcpPacket::new_checked(ip_payload));
1427 let tcp_repr = check!(TcpRepr::parse(
1428 &tcp_packet,
1429 &src_addr,
1430 &dst_addr,
1431 &self.caps.checksum
1432 ));
1433
1434 for tcp_socket in sockets
1435 .items_mut()
1436 .filter_map(|i| tcp::Socket::downcast_mut(&mut i.socket))
1437 {
1438 if tcp_socket.accepts(self, &ip_repr, &tcp_repr) {
1439 return tcp_socket
1440 .process(self, &ip_repr, &tcp_repr)
1441 .map(IpPacket::Tcp);
1442 }
1443 }
1444
1445 if tcp_repr.control == TcpControl::Rst {
1446 // Never reply to a TCP RST packet with another TCP RST packet.
1447 None
1448 } else {
1449 // The packet wasn't handled by a socket, send a TCP RST packet.
1450 Some(IpPacket::Tcp(tcp::Socket::rst_reply(&ip_repr, &tcp_repr)))
1451 }
1452 }
1453
1454 #[cfg(feature = "medium-ethernet")]
dispatch<Tx>( &mut self, tx_token: Tx, packet: EthernetPacket, frag: &mut Fragmenter, ) -> Result<(), DispatchError> where Tx: TxToken,1455 fn dispatch<Tx>(
1456 &mut self,
1457 tx_token: Tx,
1458 packet: EthernetPacket,
1459 frag: &mut Fragmenter,
1460 ) -> Result<(), DispatchError>
1461 where
1462 Tx: TxToken,
1463 {
1464 match packet {
1465 #[cfg(feature = "proto-ipv4")]
1466 EthernetPacket::Arp(arp_repr) => {
1467 let dst_hardware_addr = match arp_repr {
1468 ArpRepr::EthernetIpv4 {
1469 target_hardware_addr,
1470 ..
1471 } => target_hardware_addr,
1472 };
1473
1474 self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
1475 frame.set_dst_addr(dst_hardware_addr);
1476 frame.set_ethertype(EthernetProtocol::Arp);
1477
1478 let mut packet = ArpPacket::new_unchecked(frame.payload_mut());
1479 arp_repr.emit(&mut packet);
1480 })
1481 }
1482 EthernetPacket::Ip(packet) => self.dispatch_ip(tx_token, packet, frag),
1483 }
1484 }
1485
in_same_network(&self, addr: &IpAddress) -> bool1486 fn in_same_network(&self, addr: &IpAddress) -> bool {
1487 self.ip_addrs.iter().any(|cidr| cidr.contains_addr(addr))
1488 }
1489
route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress>1490 fn route(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
1491 // Send directly.
1492 if self.in_same_network(addr) || addr.is_broadcast() {
1493 return Some(*addr);
1494 }
1495
1496 // Route via a router.
1497 self.routes.lookup(addr, timestamp)
1498 }
1499
has_neighbor(&self, addr: &IpAddress) -> bool1500 fn has_neighbor(&self, addr: &IpAddress) -> bool {
1501 match self.route(addr, self.now) {
1502 Some(_routed_addr) => match self.caps.medium {
1503 #[cfg(feature = "medium-ethernet")]
1504 Medium::Ethernet => self
1505 .neighbor_cache
1506 .as_ref()
1507 .unwrap()
1508 .lookup(&_routed_addr, self.now)
1509 .found(),
1510 #[cfg(feature = "medium-ieee802154")]
1511 Medium::Ieee802154 => self
1512 .neighbor_cache
1513 .as_ref()
1514 .unwrap()
1515 .lookup(&_routed_addr, self.now)
1516 .found(),
1517 #[cfg(feature = "medium-ip")]
1518 Medium::Ip => true,
1519 },
1520 None => false,
1521 }
1522 }
1523
1524 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
lookup_hardware_addr<Tx>( &mut self, tx_token: Tx, src_addr: &IpAddress, dst_addr: &IpAddress, fragmenter: &mut Fragmenter, ) -> Result<(HardwareAddress, Tx), DispatchError> where Tx: TxToken,1525 fn lookup_hardware_addr<Tx>(
1526 &mut self,
1527 tx_token: Tx,
1528 src_addr: &IpAddress,
1529 dst_addr: &IpAddress,
1530 fragmenter: &mut Fragmenter,
1531 ) -> Result<(HardwareAddress, Tx), DispatchError>
1532 where
1533 Tx: TxToken,
1534 {
1535 if dst_addr.is_broadcast() {
1536 let hardware_addr = match self.caps.medium {
1537 #[cfg(feature = "medium-ethernet")]
1538 Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::BROADCAST),
1539 #[cfg(feature = "medium-ieee802154")]
1540 Medium::Ieee802154 => HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST),
1541 #[cfg(feature = "medium-ip")]
1542 Medium::Ip => unreachable!(),
1543 };
1544
1545 return Ok((hardware_addr, tx_token));
1546 }
1547
1548 if dst_addr.is_multicast() {
1549 let b = dst_addr.as_bytes();
1550 let hardware_addr = match *dst_addr {
1551 #[cfg(feature = "proto-ipv4")]
1552 IpAddress::Ipv4(_addr) => {
1553 HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1554 0x01,
1555 0x00,
1556 0x5e,
1557 b[1] & 0x7F,
1558 b[2],
1559 b[3],
1560 ]))
1561 }
1562 #[cfg(feature = "proto-ipv6")]
1563 IpAddress::Ipv6(_addr) => match self.caps.medium {
1564 #[cfg(feature = "medium-ethernet")]
1565 Medium::Ethernet => HardwareAddress::Ethernet(EthernetAddress::from_bytes(&[
1566 0x33, 0x33, b[12], b[13], b[14], b[15],
1567 ])),
1568 #[cfg(feature = "medium-ieee802154")]
1569 Medium::Ieee802154 => {
1570 // Not sure if this is correct
1571 HardwareAddress::Ieee802154(Ieee802154Address::BROADCAST)
1572 }
1573 #[cfg(feature = "medium-ip")]
1574 Medium::Ip => unreachable!(),
1575 },
1576 };
1577
1578 return Ok((hardware_addr, tx_token));
1579 }
1580
1581 let dst_addr = self
1582 .route(dst_addr, self.now)
1583 .ok_or(DispatchError::NoRoute)?;
1584
1585 match self
1586 .neighbor_cache
1587 .as_mut()
1588 .unwrap()
1589 .lookup(&dst_addr, self.now)
1590 {
1591 NeighborAnswer::Found(hardware_addr) => return Ok((hardware_addr, tx_token)),
1592 NeighborAnswer::RateLimited => return Err(DispatchError::NeighborPending),
1593 _ => (), // XXX
1594 }
1595
1596 match (src_addr, dst_addr) {
1597 #[cfg(feature = "proto-ipv4")]
1598 (&IpAddress::Ipv4(src_addr), IpAddress::Ipv4(dst_addr)) => {
1599 net_debug!(
1600 "address {} not in neighbor cache, sending ARP request",
1601 dst_addr
1602 );
1603 let src_hardware_addr = self.hardware_addr.unwrap().ethernet_or_panic();
1604
1605 let arp_repr = ArpRepr::EthernetIpv4 {
1606 operation: ArpOperation::Request,
1607 source_hardware_addr: src_hardware_addr,
1608 source_protocol_addr: src_addr,
1609 target_hardware_addr: EthernetAddress::BROADCAST,
1610 target_protocol_addr: dst_addr,
1611 };
1612
1613 if let Err(e) =
1614 self.dispatch_ethernet(tx_token, arp_repr.buffer_len(), |mut frame| {
1615 frame.set_dst_addr(EthernetAddress::BROADCAST);
1616 frame.set_ethertype(EthernetProtocol::Arp);
1617
1618 arp_repr.emit(&mut ArpPacket::new_unchecked(frame.payload_mut()))
1619 })
1620 {
1621 net_debug!("Failed to dispatch ARP request: {:?}", e);
1622 return Err(DispatchError::NeighborPending);
1623 }
1624 }
1625
1626 #[cfg(feature = "proto-ipv6")]
1627 (&IpAddress::Ipv6(src_addr), IpAddress::Ipv6(dst_addr)) => {
1628 net_debug!(
1629 "address {} not in neighbor cache, sending Neighbor Solicitation",
1630 dst_addr
1631 );
1632
1633 let solicit = Icmpv6Repr::Ndisc(NdiscRepr::NeighborSolicit {
1634 target_addr: dst_addr,
1635 lladdr: Some(self.hardware_addr.unwrap().into()),
1636 });
1637
1638 let packet = IpPacket::Icmpv6((
1639 Ipv6Repr {
1640 src_addr,
1641 dst_addr: dst_addr.solicited_node(),
1642 next_header: IpProtocol::Icmpv6,
1643 payload_len: solicit.buffer_len(),
1644 hop_limit: 0xff,
1645 },
1646 solicit,
1647 ));
1648
1649 if let Err(e) = self.dispatch_ip(tx_token, packet, fragmenter) {
1650 net_debug!("Failed to dispatch NDISC solicit: {:?}", e);
1651 return Err(DispatchError::NeighborPending);
1652 }
1653 }
1654
1655 #[allow(unreachable_patterns)]
1656 _ => (),
1657 }
1658
1659 // The request got dispatched, limit the rate on the cache.
1660 self.neighbor_cache.as_mut().unwrap().limit_rate(self.now);
1661 Err(DispatchError::NeighborPending)
1662 }
1663
flush_cache(&mut self)1664 fn flush_cache(&mut self) {
1665 #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
1666 if let Some(cache) = self.neighbor_cache.as_mut() {
1667 cache.flush()
1668 }
1669 }
1670
dispatch_ip<Tx: TxToken>( &mut self, tx_token: Tx, packet: IpPacket, frag: &mut Fragmenter, ) -> Result<(), DispatchError>1671 fn dispatch_ip<Tx: TxToken>(
1672 &mut self,
1673 tx_token: Tx,
1674 packet: IpPacket,
1675 frag: &mut Fragmenter,
1676 ) -> Result<(), DispatchError> {
1677 let ip_repr = packet.ip_repr();
1678 assert!(!ip_repr.dst_addr().is_unspecified());
1679
1680 // Dispatch IEEE802.15.4:
1681
1682 #[cfg(feature = "medium-ieee802154")]
1683 if matches!(self.caps.medium, Medium::Ieee802154) {
1684 let (addr, tx_token) = self.lookup_hardware_addr(
1685 tx_token,
1686 &ip_repr.src_addr(),
1687 &ip_repr.dst_addr(),
1688 frag,
1689 )?;
1690 let addr = addr.ieee802154_or_panic();
1691
1692 self.dispatch_ieee802154(addr, tx_token, packet, frag);
1693 return Ok(());
1694 }
1695
1696 // Dispatch IP/Ethernet:
1697
1698 let caps = self.caps.clone();
1699
1700 #[cfg(feature = "proto-ipv4-fragmentation")]
1701 let ipv4_id = self.get_ipv4_ident();
1702
1703 // First we calculate the total length that we will have to emit.
1704 let mut total_len = ip_repr.buffer_len();
1705
1706 // Add the size of the Ethernet header if the medium is Ethernet.
1707 #[cfg(feature = "medium-ethernet")]
1708 if matches!(self.caps.medium, Medium::Ethernet) {
1709 total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
1710 }
1711
1712 // If the medium is Ethernet, then we need to retrieve the destination hardware address.
1713 #[cfg(feature = "medium-ethernet")]
1714 let (dst_hardware_addr, tx_token) = match self.caps.medium {
1715 Medium::Ethernet => {
1716 match self.lookup_hardware_addr(
1717 tx_token,
1718 &ip_repr.src_addr(),
1719 &ip_repr.dst_addr(),
1720 frag,
1721 )? {
1722 (HardwareAddress::Ethernet(addr), tx_token) => (addr, tx_token),
1723 #[cfg(feature = "medium-ieee802154")]
1724 (HardwareAddress::Ieee802154(_), _) => unreachable!(),
1725 }
1726 }
1727 _ => (EthernetAddress([0; 6]), tx_token),
1728 };
1729
1730 // Emit function for the Ethernet header.
1731 #[cfg(feature = "medium-ethernet")]
1732 let emit_ethernet = |repr: &IpRepr, tx_buffer: &mut [u8]| {
1733 let mut frame = EthernetFrame::new_unchecked(tx_buffer);
1734
1735 let src_addr = self.hardware_addr.unwrap().ethernet_or_panic();
1736 frame.set_src_addr(src_addr);
1737 frame.set_dst_addr(dst_hardware_addr);
1738
1739 match repr.version() {
1740 #[cfg(feature = "proto-ipv4")]
1741 IpVersion::Ipv4 => frame.set_ethertype(EthernetProtocol::Ipv4),
1742 #[cfg(feature = "proto-ipv6")]
1743 IpVersion::Ipv6 => frame.set_ethertype(EthernetProtocol::Ipv6),
1744 }
1745
1746 Ok(())
1747 };
1748
1749 // Emit function for the IP header and payload.
1750 let emit_ip = |repr: &IpRepr, mut tx_buffer: &mut [u8]| {
1751 repr.emit(&mut tx_buffer, &self.caps.checksum);
1752
1753 let payload = &mut tx_buffer[repr.header_len()..];
1754 packet.emit_payload(repr, payload, &caps);
1755 };
1756
1757 let total_ip_len = ip_repr.buffer_len();
1758
1759 match ip_repr {
1760 #[cfg(feature = "proto-ipv4")]
1761 IpRepr::Ipv4(mut repr) => {
1762 // If we have an IPv4 packet, then we need to check if we need to fragment it.
1763 if total_ip_len > self.caps.max_transmission_unit {
1764 #[cfg(feature = "proto-ipv4-fragmentation")]
1765 {
1766 net_debug!("start fragmentation");
1767
1768 // Calculate how much we will send now (including the Ethernet header).
1769 let tx_len = self.caps.max_transmission_unit;
1770
1771 let ip_header_len = repr.buffer_len();
1772 let first_frag_ip_len = self.caps.ip_mtu();
1773
1774 if frag.buffer.len() < first_frag_ip_len {
1775 net_debug!(
1776 "Fragmentation buffer is too small, at least {} needed. Dropping",
1777 first_frag_ip_len
1778 );
1779 return Ok(());
1780 }
1781
1782 #[cfg(feature = "medium-ethernet")]
1783 {
1784 frag.ipv4.dst_hardware_addr = dst_hardware_addr;
1785 }
1786
1787 // Save the total packet len (without the Ethernet header, but with the first
1788 // IP header).
1789 frag.packet_len = total_ip_len;
1790
1791 // Save the IP header for other fragments.
1792 frag.ipv4.repr = repr;
1793
1794 // Save how much bytes we will send now.
1795 frag.sent_bytes = first_frag_ip_len;
1796
1797 // Modify the IP header
1798 repr.payload_len = first_frag_ip_len - repr.buffer_len();
1799
1800 // Emit the IP header to the buffer.
1801 emit_ip(&ip_repr, &mut frag.buffer);
1802 let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut frag.buffer[..]);
1803 frag.ipv4.ident = ipv4_id;
1804 ipv4_packet.set_ident(ipv4_id);
1805 ipv4_packet.set_more_frags(true);
1806 ipv4_packet.set_dont_frag(false);
1807 ipv4_packet.set_frag_offset(0);
1808
1809 if caps.checksum.ipv4.tx() {
1810 ipv4_packet.fill_checksum();
1811 }
1812
1813 // Transmit the first packet.
1814 tx_token.consume(tx_len, |mut tx_buffer| {
1815 #[cfg(feature = "medium-ethernet")]
1816 if matches!(self.caps.medium, Medium::Ethernet) {
1817 emit_ethernet(&ip_repr, tx_buffer)?;
1818 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1819 }
1820
1821 // Change the offset for the next packet.
1822 frag.ipv4.frag_offset = (first_frag_ip_len - ip_header_len) as u16;
1823
1824 // Copy the IP header and the payload.
1825 tx_buffer[..first_frag_ip_len]
1826 .copy_from_slice(&frag.buffer[..first_frag_ip_len]);
1827
1828 Ok(())
1829 })
1830 }
1831
1832 #[cfg(not(feature = "proto-ipv4-fragmentation"))]
1833 {
1834 net_debug!("Enable the `proto-ipv4-fragmentation` feature for fragmentation support.");
1835 Ok(())
1836 }
1837 } else {
1838 // No fragmentation is required.
1839 tx_token.consume(total_len, |mut tx_buffer| {
1840 #[cfg(feature = "medium-ethernet")]
1841 if matches!(self.caps.medium, Medium::Ethernet) {
1842 emit_ethernet(&ip_repr, tx_buffer)?;
1843 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1844 }
1845
1846 emit_ip(&ip_repr, tx_buffer);
1847 Ok(())
1848 })
1849 }
1850 }
1851 // We don't support IPv6 fragmentation yet.
1852 #[cfg(feature = "proto-ipv6")]
1853 IpRepr::Ipv6(_) => tx_token.consume(total_len, |mut tx_buffer| {
1854 #[cfg(feature = "medium-ethernet")]
1855 if matches!(self.caps.medium, Medium::Ethernet) {
1856 emit_ethernet(&ip_repr, tx_buffer)?;
1857 tx_buffer = &mut tx_buffer[EthernetFrame::<&[u8]>::header_len()..];
1858 }
1859
1860 emit_ip(&ip_repr, tx_buffer);
1861 Ok(())
1862 }),
1863 }
1864 }
1865 }
1866
1867 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
1868 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
1869 enum DispatchError {
1870 /// No route to dispatch this packet. Retrying won't help unless
1871 /// configuration is changed.
1872 NoRoute,
1873 /// We do have a route to dispatch this packet, but we haven't discovered
1874 /// the neighbor for it yet. Discovery has been initiated, dispatch
1875 /// should be retried later.
1876 NeighborPending,
1877 }
1878