1 use core::convert::From;
2 use core::fmt;
3 
4 use super::{Error, Result};
5 use crate::phy::ChecksumCapabilities;
6 #[cfg(feature = "proto-ipv4")]
7 use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
8 #[cfg(feature = "proto-ipv6")]
9 use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
10 
11 /// Internet protocol version.
12 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
13 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
14 pub enum Version {
15     #[cfg(feature = "proto-ipv4")]
16     Ipv4,
17     #[cfg(feature = "proto-ipv6")]
18     Ipv6,
19 }
20 
21 impl Version {
22     /// Return the version of an IP packet stored in the provided buffer.
23     ///
24     /// This function never returns `Ok(IpVersion::Unspecified)`; instead,
25     /// unknown versions result in `Err(Error)`.
of_packet(data: &[u8]) -> Result<Version>26     pub const fn of_packet(data: &[u8]) -> Result<Version> {
27         match data[0] >> 4 {
28             #[cfg(feature = "proto-ipv4")]
29             4 => Ok(Version::Ipv4),
30             #[cfg(feature = "proto-ipv6")]
31             6 => Ok(Version::Ipv6),
32             _ => Err(Error),
33         }
34     }
35 }
36 
37 impl fmt::Display for Version {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result38     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39         match *self {
40             #[cfg(feature = "proto-ipv4")]
41             Version::Ipv4 => write!(f, "IPv4"),
42             #[cfg(feature = "proto-ipv6")]
43             Version::Ipv6 => write!(f, "IPv6"),
44         }
45     }
46 }
47 
48 enum_with_unknown! {
49     /// IP datagram encapsulated protocol.
50     pub enum Protocol(u8) {
51         HopByHop  = 0x00,
52         Icmp      = 0x01,
53         Igmp      = 0x02,
54         Tcp       = 0x06,
55         Udp       = 0x11,
56         Ipv6Route = 0x2b,
57         Ipv6Frag  = 0x2c,
58         Icmpv6    = 0x3a,
59         Ipv6NoNxt = 0x3b,
60         Ipv6Opts  = 0x3c
61     }
62 }
63 
64 impl fmt::Display for Protocol {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result65     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66         match *self {
67             Protocol::HopByHop => write!(f, "Hop-by-Hop"),
68             Protocol::Icmp => write!(f, "ICMP"),
69             Protocol::Igmp => write!(f, "IGMP"),
70             Protocol::Tcp => write!(f, "TCP"),
71             Protocol::Udp => write!(f, "UDP"),
72             Protocol::Ipv6Route => write!(f, "IPv6-Route"),
73             Protocol::Ipv6Frag => write!(f, "IPv6-Frag"),
74             Protocol::Icmpv6 => write!(f, "ICMPv6"),
75             Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
76             Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
77             Protocol::Unknown(id) => write!(f, "0x{id:02x}"),
78         }
79     }
80 }
81 
82 /// An internetworking address.
83 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
84 pub enum Address {
85     /// An IPv4 address.
86     #[cfg(feature = "proto-ipv4")]
87     Ipv4(Ipv4Address),
88     /// An IPv6 address.
89     #[cfg(feature = "proto-ipv6")]
90     Ipv6(Ipv6Address),
91 }
92 
93 impl Address {
94     /// Create an address wrapping an IPv4 address with the given octets.
95     #[cfg(feature = "proto-ipv4")]
v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address96     pub const fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
97         Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
98     }
99 
100     /// Create an address wrapping an IPv6 address with the given octets.
101     #[cfg(feature = "proto-ipv6")]
102     #[allow(clippy::too_many_arguments)]
v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address103     pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
104         Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
105     }
106 
107     /// Return the protocol version.
version(&self) -> Version108     pub const fn version(&self) -> Version {
109         match self {
110             #[cfg(feature = "proto-ipv4")]
111             Address::Ipv4(_) => Version::Ipv4,
112             #[cfg(feature = "proto-ipv6")]
113             Address::Ipv6(_) => Version::Ipv6,
114         }
115     }
116 
117     /// Return an address as a sequence of octets, in big-endian.
as_bytes(&self) -> &[u8]118     pub const fn as_bytes(&self) -> &[u8] {
119         match self {
120             #[cfg(feature = "proto-ipv4")]
121             Address::Ipv4(addr) => addr.as_bytes(),
122             #[cfg(feature = "proto-ipv6")]
123             Address::Ipv6(addr) => addr.as_bytes(),
124         }
125     }
126 
127     /// Query whether the address is a valid unicast address.
is_unicast(&self) -> bool128     pub fn is_unicast(&self) -> bool {
129         match self {
130             #[cfg(feature = "proto-ipv4")]
131             Address::Ipv4(addr) => addr.is_unicast(),
132             #[cfg(feature = "proto-ipv6")]
133             Address::Ipv6(addr) => addr.is_unicast(),
134         }
135     }
136 
137     /// Query whether the address is a valid multicast address.
is_multicast(&self) -> bool138     pub const fn is_multicast(&self) -> bool {
139         match self {
140             #[cfg(feature = "proto-ipv4")]
141             Address::Ipv4(addr) => addr.is_multicast(),
142             #[cfg(feature = "proto-ipv6")]
143             Address::Ipv6(addr) => addr.is_multicast(),
144         }
145     }
146 
147     /// Query whether the address is the broadcast address.
is_broadcast(&self) -> bool148     pub fn is_broadcast(&self) -> bool {
149         match self {
150             #[cfg(feature = "proto-ipv4")]
151             Address::Ipv4(addr) => addr.is_broadcast(),
152             #[cfg(feature = "proto-ipv6")]
153             Address::Ipv6(_) => false,
154         }
155     }
156 
157     /// Query whether the address falls into the "unspecified" range.
is_unspecified(&self) -> bool158     pub fn is_unspecified(&self) -> bool {
159         match self {
160             #[cfg(feature = "proto-ipv4")]
161             Address::Ipv4(addr) => addr.is_unspecified(),
162             #[cfg(feature = "proto-ipv6")]
163             Address::Ipv6(addr) => addr.is_unspecified(),
164         }
165     }
166 
167     /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`,
168     /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise.
prefix_len(&self) -> Option<u8>169     pub fn prefix_len(&self) -> Option<u8> {
170         let mut ones = true;
171         let mut prefix_len = 0;
172         for byte in self.as_bytes() {
173             let mut mask = 0x80;
174             for _ in 0..8 {
175                 let one = *byte & mask != 0;
176                 if ones {
177                     // Expect 1s until first 0
178                     if one {
179                         prefix_len += 1;
180                     } else {
181                         ones = false;
182                     }
183                 } else if one {
184                     // 1 where 0 was expected
185                     return None;
186                 }
187                 mask >>= 1;
188             }
189         }
190         Some(prefix_len)
191     }
192 }
193 
194 #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
195 impl From<::std::net::IpAddr> for Address {
from(x: ::std::net::IpAddr) -> Address196     fn from(x: ::std::net::IpAddr) -> Address {
197         match x {
198             ::std::net::IpAddr::V4(ipv4) => Address::Ipv4(ipv4.into()),
199             ::std::net::IpAddr::V6(ipv6) => Address::Ipv6(ipv6.into()),
200         }
201     }
202 }
203 
204 #[cfg(feature = "std")]
205 impl From<Address> for ::std::net::IpAddr {
from(x: Address) -> ::std::net::IpAddr206     fn from(x: Address) -> ::std::net::IpAddr {
207         match x {
208             #[cfg(feature = "proto-ipv4")]
209             Address::Ipv4(ipv4) => ::std::net::IpAddr::V4(ipv4.into()),
210             #[cfg(feature = "proto-ipv6")]
211             Address::Ipv6(ipv6) => ::std::net::IpAddr::V6(ipv6.into()),
212         }
213     }
214 }
215 
216 #[cfg(all(feature = "std", feature = "proto-ipv4"))]
217 impl From<::std::net::Ipv4Addr> for Address {
from(ipv4: ::std::net::Ipv4Addr) -> Address218     fn from(ipv4: ::std::net::Ipv4Addr) -> Address {
219         Address::Ipv4(ipv4.into())
220     }
221 }
222 
223 #[cfg(all(feature = "std", feature = "proto-ipv6"))]
224 impl From<::std::net::Ipv6Addr> for Address {
from(ipv6: ::std::net::Ipv6Addr) -> Address225     fn from(ipv6: ::std::net::Ipv6Addr) -> Address {
226         Address::Ipv6(ipv6.into())
227     }
228 }
229 
230 #[cfg(feature = "proto-ipv4")]
231 impl From<Ipv4Address> for Address {
from(addr: Ipv4Address) -> Self232     fn from(addr: Ipv4Address) -> Self {
233         Address::Ipv4(addr)
234     }
235 }
236 
237 #[cfg(feature = "proto-ipv6")]
238 impl From<Ipv6Address> for Address {
from(addr: Ipv6Address) -> Self239     fn from(addr: Ipv6Address) -> Self {
240         Address::Ipv6(addr)
241     }
242 }
243 
244 impl fmt::Display for Address {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result245     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
246         match *self {
247             #[cfg(feature = "proto-ipv4")]
248             Address::Ipv4(addr) => write!(f, "{addr}"),
249             #[cfg(feature = "proto-ipv6")]
250             Address::Ipv6(addr) => write!(f, "{addr}"),
251         }
252     }
253 }
254 
255 #[cfg(feature = "defmt")]
256 impl defmt::Format for Address {
format(&self, f: defmt::Formatter)257     fn format(&self, f: defmt::Formatter) {
258         match self {
259             #[cfg(feature = "proto-ipv4")]
260             &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr),
261             #[cfg(feature = "proto-ipv6")]
262             &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr),
263         }
264     }
265 }
266 
267 /// A specification of a CIDR block, containing an address and a variable-length
268 /// subnet masking prefix length.
269 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
270 pub enum Cidr {
271     #[cfg(feature = "proto-ipv4")]
272     Ipv4(Ipv4Cidr),
273     #[cfg(feature = "proto-ipv6")]
274     Ipv6(Ipv6Cidr),
275 }
276 
277 impl Cidr {
278     /// Create a CIDR block from the given address and prefix length.
279     ///
280     /// # Panics
281     /// This function panics if the given prefix length is invalid for the given address.
new(addr: Address, prefix_len: u8) -> Cidr282     pub fn new(addr: Address, prefix_len: u8) -> Cidr {
283         match addr {
284             #[cfg(feature = "proto-ipv4")]
285             Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
286             #[cfg(feature = "proto-ipv6")]
287             Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
288         }
289     }
290 
291     /// Return the IP address of this CIDR block.
address(&self) -> Address292     pub const fn address(&self) -> Address {
293         match *self {
294             #[cfg(feature = "proto-ipv4")]
295             Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
296             #[cfg(feature = "proto-ipv6")]
297             Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()),
298         }
299     }
300 
301     /// Return the prefix length of this CIDR block.
prefix_len(&self) -> u8302     pub const fn prefix_len(&self) -> u8 {
303         match *self {
304             #[cfg(feature = "proto-ipv4")]
305             Cidr::Ipv4(cidr) => cidr.prefix_len(),
306             #[cfg(feature = "proto-ipv6")]
307             Cidr::Ipv6(cidr) => cidr.prefix_len(),
308         }
309     }
310 
311     /// Query whether the subnetwork described by this CIDR block contains
312     /// the given address.
contains_addr(&self, addr: &Address) -> bool313     pub fn contains_addr(&self, addr: &Address) -> bool {
314         match (self, addr) {
315             #[cfg(feature = "proto-ipv4")]
316             (Cidr::Ipv4(cidr), Address::Ipv4(addr)) => cidr.contains_addr(addr),
317             #[cfg(feature = "proto-ipv6")]
318             (Cidr::Ipv6(cidr), Address::Ipv6(addr)) => cidr.contains_addr(addr),
319             #[allow(unreachable_patterns)]
320             _ => false,
321         }
322     }
323 
324     /// Query whether the subnetwork described by this CIDR block contains
325     /// the subnetwork described by the given CIDR block.
contains_subnet(&self, subnet: &Cidr) -> bool326     pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
327         match (self, subnet) {
328             #[cfg(feature = "proto-ipv4")]
329             (Cidr::Ipv4(cidr), Cidr::Ipv4(other)) => cidr.contains_subnet(other),
330             #[cfg(feature = "proto-ipv6")]
331             (Cidr::Ipv6(cidr), Cidr::Ipv6(other)) => cidr.contains_subnet(other),
332             #[allow(unreachable_patterns)]
333             _ => false,
334         }
335     }
336 }
337 
338 #[cfg(feature = "proto-ipv4")]
339 impl From<Ipv4Cidr> for Cidr {
from(addr: Ipv4Cidr) -> Self340     fn from(addr: Ipv4Cidr) -> Self {
341         Cidr::Ipv4(addr)
342     }
343 }
344 
345 #[cfg(feature = "proto-ipv6")]
346 impl From<Ipv6Cidr> for Cidr {
from(addr: Ipv6Cidr) -> Self347     fn from(addr: Ipv6Cidr) -> Self {
348         Cidr::Ipv6(addr)
349     }
350 }
351 
352 impl fmt::Display for Cidr {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result353     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354         match *self {
355             #[cfg(feature = "proto-ipv4")]
356             Cidr::Ipv4(cidr) => write!(f, "{cidr}"),
357             #[cfg(feature = "proto-ipv6")]
358             Cidr::Ipv6(cidr) => write!(f, "{cidr}"),
359         }
360     }
361 }
362 
363 #[cfg(feature = "defmt")]
364 impl defmt::Format for Cidr {
format(&self, f: defmt::Formatter)365     fn format(&self, f: defmt::Formatter) {
366         match self {
367             #[cfg(feature = "proto-ipv4")]
368             &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr),
369             #[cfg(feature = "proto-ipv6")]
370             &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr),
371         }
372     }
373 }
374 
375 /// An internet endpoint address.
376 ///
377 /// `Endpoint` always fully specifies both the address and the port.
378 ///
379 /// See also ['ListenEndpoint'], which allows not specifying the address
380 /// in order to listen on a given port on any address.
381 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
382 pub struct Endpoint {
383     pub addr: Address,
384     pub port: u16,
385 }
386 
387 impl Endpoint {
388     /// Create an endpoint address from given address and port.
new(addr: Address, port: u16) -> Endpoint389     pub const fn new(addr: Address, port: u16) -> Endpoint {
390         Endpoint { addr: addr, port }
391     }
392 }
393 
394 #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
395 impl From<::std::net::SocketAddr> for Endpoint {
from(x: ::std::net::SocketAddr) -> Endpoint396     fn from(x: ::std::net::SocketAddr) -> Endpoint {
397         Endpoint {
398             addr: x.ip().into(),
399             port: x.port(),
400         }
401     }
402 }
403 
404 #[cfg(all(feature = "std", feature = "proto-ipv4"))]
405 impl From<::std::net::SocketAddrV4> for Endpoint {
from(x: ::std::net::SocketAddrV4) -> Endpoint406     fn from(x: ::std::net::SocketAddrV4) -> Endpoint {
407         Endpoint {
408             addr: (*x.ip()).into(),
409             port: x.port(),
410         }
411     }
412 }
413 
414 #[cfg(all(feature = "std", feature = "proto-ipv6"))]
415 impl From<::std::net::SocketAddrV6> for Endpoint {
from(x: ::std::net::SocketAddrV6) -> Endpoint416     fn from(x: ::std::net::SocketAddrV6) -> Endpoint {
417         Endpoint {
418             addr: (*x.ip()).into(),
419             port: x.port(),
420         }
421     }
422 }
423 
424 impl fmt::Display for Endpoint {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result425     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
426         write!(f, "{}:{}", self.addr, self.port)
427     }
428 }
429 
430 #[cfg(feature = "defmt")]
431 impl defmt::Format for Endpoint {
format(&self, f: defmt::Formatter)432     fn format(&self, f: defmt::Formatter) {
433         defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
434     }
435 }
436 
437 impl<T: Into<Address>> From<(T, u16)> for Endpoint {
from((addr, port): (T, u16)) -> Endpoint438     fn from((addr, port): (T, u16)) -> Endpoint {
439         Endpoint {
440             addr: addr.into(),
441             port,
442         }
443     }
444 }
445 
446 /// An internet endpoint address for listening.
447 ///
448 /// In contrast with [`Endpoint`], `ListenEndpoint` allows not specifying the address,
449 /// in order to listen on a given port at all our addresses.
450 ///
451 /// An endpoint can be constructed from a port, in which case the address is unspecified.
452 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
453 pub struct ListenEndpoint {
454     pub addr: Option<Address>,
455     pub port: u16,
456 }
457 
458 impl ListenEndpoint {
459     /// Query whether the endpoint has a specified address and port.
is_specified(&self) -> bool460     pub const fn is_specified(&self) -> bool {
461         self.addr.is_some() && self.port != 0
462     }
463 }
464 
465 #[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
466 impl From<::std::net::SocketAddr> for ListenEndpoint {
from(x: ::std::net::SocketAddr) -> ListenEndpoint467     fn from(x: ::std::net::SocketAddr) -> ListenEndpoint {
468         ListenEndpoint {
469             addr: Some(x.ip().into()),
470             port: x.port(),
471         }
472     }
473 }
474 
475 #[cfg(all(feature = "std", feature = "proto-ipv4"))]
476 impl From<::std::net::SocketAddrV4> for ListenEndpoint {
from(x: ::std::net::SocketAddrV4) -> ListenEndpoint477     fn from(x: ::std::net::SocketAddrV4) -> ListenEndpoint {
478         ListenEndpoint {
479             addr: Some((*x.ip()).into()),
480             port: x.port(),
481         }
482     }
483 }
484 
485 #[cfg(all(feature = "std", feature = "proto-ipv6"))]
486 impl From<::std::net::SocketAddrV6> for ListenEndpoint {
from(x: ::std::net::SocketAddrV6) -> ListenEndpoint487     fn from(x: ::std::net::SocketAddrV6) -> ListenEndpoint {
488         ListenEndpoint {
489             addr: Some((*x.ip()).into()),
490             port: x.port(),
491         }
492     }
493 }
494 
495 impl fmt::Display for ListenEndpoint {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result496     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
497         if let Some(addr) = self.addr {
498             write!(f, "{}:{}", addr, self.port)
499         } else {
500             write!(f, "*:{}", self.port)
501         }
502     }
503 }
504 
505 #[cfg(feature = "defmt")]
506 impl defmt::Format for ListenEndpoint {
format(&self, f: defmt::Formatter)507     fn format(&self, f: defmt::Formatter) {
508         defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
509     }
510 }
511 
512 impl From<u16> for ListenEndpoint {
from(port: u16) -> ListenEndpoint513     fn from(port: u16) -> ListenEndpoint {
514         ListenEndpoint { addr: None, port }
515     }
516 }
517 
518 impl From<Endpoint> for ListenEndpoint {
from(endpoint: Endpoint) -> ListenEndpoint519     fn from(endpoint: Endpoint) -> ListenEndpoint {
520         ListenEndpoint {
521             addr: Some(endpoint.addr),
522             port: endpoint.port,
523         }
524     }
525 }
526 
527 impl<T: Into<Address>> From<(T, u16)> for ListenEndpoint {
from((addr, port): (T, u16)) -> ListenEndpoint528     fn from((addr, port): (T, u16)) -> ListenEndpoint {
529         ListenEndpoint {
530             addr: Some(addr.into()),
531             port,
532         }
533     }
534 }
535 
536 /// An IP packet representation.
537 ///
538 /// This enum abstracts the various versions of IP packets. It either contains an IPv4
539 /// or IPv6 concrete high-level representation.
540 #[derive(Debug, Clone, PartialEq, Eq)]
541 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
542 pub enum Repr {
543     #[cfg(feature = "proto-ipv4")]
544     Ipv4(Ipv4Repr),
545     #[cfg(feature = "proto-ipv6")]
546     Ipv6(Ipv6Repr),
547 }
548 
549 #[cfg(feature = "proto-ipv4")]
550 impl From<Ipv4Repr> for Repr {
from(repr: Ipv4Repr) -> Repr551     fn from(repr: Ipv4Repr) -> Repr {
552         Repr::Ipv4(repr)
553     }
554 }
555 
556 #[cfg(feature = "proto-ipv6")]
557 impl From<Ipv6Repr> for Repr {
from(repr: Ipv6Repr) -> Repr558     fn from(repr: Ipv6Repr) -> Repr {
559         Repr::Ipv6(repr)
560     }
561 }
562 
563 impl Repr {
564     /// Create a new IpRepr, choosing the right IP version for the src/dst addrs.
565     ///
566     /// # Panics
567     ///
568     /// Panics if `src_addr` and `dst_addr` are different IP version.
new( src_addr: Address, dst_addr: Address, next_header: Protocol, payload_len: usize, hop_limit: u8, ) -> Self569     pub fn new(
570         src_addr: Address,
571         dst_addr: Address,
572         next_header: Protocol,
573         payload_len: usize,
574         hop_limit: u8,
575     ) -> Self {
576         match (src_addr, dst_addr) {
577             #[cfg(feature = "proto-ipv4")]
578             (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr {
579                 src_addr,
580                 dst_addr,
581                 next_header,
582                 payload_len,
583                 hop_limit,
584             }),
585             #[cfg(feature = "proto-ipv6")]
586             (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr {
587                 src_addr,
588                 dst_addr,
589                 next_header,
590                 payload_len,
591                 hop_limit,
592             }),
593             #[allow(unreachable_patterns)]
594             _ => panic!("IP version mismatch: src={src_addr:?} dst={dst_addr:?}"),
595         }
596     }
597 
598     /// Return the protocol version.
version(&self) -> Version599     pub const fn version(&self) -> Version {
600         match *self {
601             #[cfg(feature = "proto-ipv4")]
602             Repr::Ipv4(_) => Version::Ipv4,
603             #[cfg(feature = "proto-ipv6")]
604             Repr::Ipv6(_) => Version::Ipv6,
605         }
606     }
607 
608     /// Return the source address.
src_addr(&self) -> Address609     pub const fn src_addr(&self) -> Address {
610         match *self {
611             #[cfg(feature = "proto-ipv4")]
612             Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
613             #[cfg(feature = "proto-ipv6")]
614             Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
615         }
616     }
617 
618     /// Return the destination address.
dst_addr(&self) -> Address619     pub const fn dst_addr(&self) -> Address {
620         match *self {
621             #[cfg(feature = "proto-ipv4")]
622             Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
623             #[cfg(feature = "proto-ipv6")]
624             Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
625         }
626     }
627 
628     /// Return the next header (protocol).
next_header(&self) -> Protocol629     pub const fn next_header(&self) -> Protocol {
630         match *self {
631             #[cfg(feature = "proto-ipv4")]
632             Repr::Ipv4(repr) => repr.next_header,
633             #[cfg(feature = "proto-ipv6")]
634             Repr::Ipv6(repr) => repr.next_header,
635         }
636     }
637 
638     /// Return the payload length.
payload_len(&self) -> usize639     pub const fn payload_len(&self) -> usize {
640         match *self {
641             #[cfg(feature = "proto-ipv4")]
642             Repr::Ipv4(repr) => repr.payload_len,
643             #[cfg(feature = "proto-ipv6")]
644             Repr::Ipv6(repr) => repr.payload_len,
645         }
646     }
647 
648     /// Set the payload length.
set_payload_len(&mut self, length: usize)649     pub fn set_payload_len(&mut self, length: usize) {
650         match self {
651             #[cfg(feature = "proto-ipv4")]
652             Repr::Ipv4(Ipv4Repr { payload_len, .. }) => *payload_len = length,
653             #[cfg(feature = "proto-ipv6")]
654             Repr::Ipv6(Ipv6Repr { payload_len, .. }) => *payload_len = length,
655         }
656     }
657 
658     /// Return the TTL value.
hop_limit(&self) -> u8659     pub const fn hop_limit(&self) -> u8 {
660         match *self {
661             #[cfg(feature = "proto-ipv4")]
662             Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
663             #[cfg(feature = "proto-ipv6")]
664             Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
665         }
666     }
667 
668     /// Return the length of a header that will be emitted from this high-level representation.
header_len(&self) -> usize669     pub const fn header_len(&self) -> usize {
670         match *self {
671             #[cfg(feature = "proto-ipv4")]
672             Repr::Ipv4(repr) => repr.buffer_len(),
673             #[cfg(feature = "proto-ipv6")]
674             Repr::Ipv6(repr) => repr.buffer_len(),
675         }
676     }
677 
678     /// Emit this high-level representation into a buffer.
emit<T: AsRef<[u8]> + AsMut<[u8]>>( &self, buffer: T, _checksum_caps: &ChecksumCapabilities, )679     pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
680         &self,
681         buffer: T,
682         _checksum_caps: &ChecksumCapabilities,
683     ) {
684         match *self {
685             #[cfg(feature = "proto-ipv4")]
686             Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
687             #[cfg(feature = "proto-ipv6")]
688             Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
689         }
690     }
691 
692     /// Return the total length of a packet that will be emitted from this
693     /// high-level representation.
694     ///
695     /// This is the same as `repr.buffer_len() + repr.payload_len()`.
buffer_len(&self) -> usize696     pub const fn buffer_len(&self) -> usize {
697         self.header_len() + self.payload_len()
698     }
699 }
700 
701 pub mod checksum {
702     use byteorder::{ByteOrder, NetworkEndian};
703 
704     use super::*;
705 
propagate_carries(word: u32) -> u16706     const fn propagate_carries(word: u32) -> u16 {
707         let sum = (word >> 16) + (word & 0xffff);
708         ((sum >> 16) as u16) + (sum as u16)
709     }
710 
711     /// Compute an RFC 1071 compliant checksum (without the final complement).
data(mut data: &[u8]) -> u16712     pub fn data(mut data: &[u8]) -> u16 {
713         let mut accum = 0;
714 
715         // For each 32-byte chunk...
716         const CHUNK_SIZE: usize = 32;
717         while data.len() >= CHUNK_SIZE {
718             let mut d = &data[..CHUNK_SIZE];
719             // ... take by 2 bytes and sum them.
720             while d.len() >= 2 {
721                 accum += NetworkEndian::read_u16(d) as u32;
722                 d = &d[2..];
723             }
724 
725             data = &data[CHUNK_SIZE..];
726         }
727 
728         // Sum the rest that does not fit the last 32-byte chunk,
729         // taking by 2 bytes.
730         while data.len() >= 2 {
731             accum += NetworkEndian::read_u16(data) as u32;
732             data = &data[2..];
733         }
734 
735         // Add the last remaining odd byte, if any.
736         if let Some(&value) = data.first() {
737             accum += (value as u32) << 8;
738         }
739 
740         propagate_carries(accum)
741     }
742 
743     /// Combine several RFC 1071 compliant checksums.
combine(checksums: &[u16]) -> u16744     pub fn combine(checksums: &[u16]) -> u16 {
745         let mut accum: u32 = 0;
746         for &word in checksums {
747             accum += word as u32;
748         }
749         propagate_carries(accum)
750     }
751 
752     /// Compute an IP pseudo header checksum.
pseudo_header( src_addr: &Address, dst_addr: &Address, next_header: Protocol, length: u32, ) -> u16753     pub fn pseudo_header(
754         src_addr: &Address,
755         dst_addr: &Address,
756         next_header: Protocol,
757         length: u32,
758     ) -> u16 {
759         match (src_addr, dst_addr) {
760             #[cfg(feature = "proto-ipv4")]
761             (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
762                 let mut proto_len = [0u8; 4];
763                 proto_len[1] = next_header.into();
764                 NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
765 
766                 combine(&[
767                     data(src_addr.as_bytes()),
768                     data(dst_addr.as_bytes()),
769                     data(&proto_len[..]),
770                 ])
771             }
772 
773             #[cfg(feature = "proto-ipv6")]
774             (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => {
775                 let mut proto_len = [0u8; 8];
776                 proto_len[7] = next_header.into();
777                 NetworkEndian::write_u32(&mut proto_len[0..4], length);
778                 combine(&[
779                     data(src_addr.as_bytes()),
780                     data(dst_addr.as_bytes()),
781                     data(&proto_len[..]),
782                 ])
783             }
784 
785             #[allow(unreachable_patterns)]
786             _ => panic!("Unexpected pseudo header addresses: {src_addr}, {dst_addr}"),
787         }
788     }
789 
790     // We use this in pretty printer implementations.
format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result791     pub(crate) fn format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result {
792         if !correct {
793             write!(f, " (checksum incorrect)")
794         } else {
795             Ok(())
796         }
797     }
798 }
799 
800 use crate::wire::pretty_print::PrettyIndent;
801 
pretty_print_ip_payload<T: Into<Repr>>( f: &mut fmt::Formatter, indent: &mut PrettyIndent, ip_repr: T, payload: &[u8], ) -> fmt::Result802 pub fn pretty_print_ip_payload<T: Into<Repr>>(
803     f: &mut fmt::Formatter,
804     indent: &mut PrettyIndent,
805     ip_repr: T,
806     payload: &[u8],
807 ) -> fmt::Result {
808     #[cfg(feature = "proto-ipv4")]
809     use super::pretty_print::PrettyPrint;
810     use crate::wire::ip::checksum::format_checksum;
811     #[cfg(feature = "proto-ipv4")]
812     use crate::wire::Icmpv4Packet;
813     use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
814 
815     let checksum_caps = ChecksumCapabilities::ignored();
816     let repr = ip_repr.into();
817     match repr.next_header() {
818         #[cfg(feature = "proto-ipv4")]
819         Protocol::Icmp => {
820             indent.increase(f)?;
821             Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
822         }
823         Protocol::Udp => {
824             indent.increase(f)?;
825             match UdpPacket::<&[u8]>::new_checked(payload) {
826                 Err(err) => write!(f, "{indent}({err})"),
827                 Ok(udp_packet) => {
828                     match UdpRepr::parse(
829                         &udp_packet,
830                         &repr.src_addr(),
831                         &repr.dst_addr(),
832                         &checksum_caps,
833                     ) {
834                         Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
835                         Ok(udp_repr) => {
836                             write!(
837                                 f,
838                                 "{}{} len={}",
839                                 indent,
840                                 udp_repr,
841                                 udp_packet.payload().len()
842                             )?;
843                             let valid =
844                                 udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
845                             format_checksum(f, valid)
846                         }
847                     }
848                 }
849             }
850         }
851         Protocol::Tcp => {
852             indent.increase(f)?;
853             match TcpPacket::<&[u8]>::new_checked(payload) {
854                 Err(err) => write!(f, "{indent}({err})"),
855                 Ok(tcp_packet) => {
856                     match TcpRepr::parse(
857                         &tcp_packet,
858                         &repr.src_addr(),
859                         &repr.dst_addr(),
860                         &checksum_caps,
861                     ) {
862                         Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
863                         Ok(tcp_repr) => {
864                             write!(f, "{indent}{tcp_repr}")?;
865                             let valid =
866                                 tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
867                             format_checksum(f, valid)
868                         }
869                     }
870                 }
871             }
872         }
873         _ => Ok(()),
874     }
875 }
876 
877 #[cfg(test)]
878 pub(crate) mod test {
879     #![allow(unused)]
880 
881     #[cfg(feature = "proto-ipv6")]
882     pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([
883         0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
884     ]));
885     #[cfg(feature = "proto-ipv6")]
886     pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([
887         0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
888     ]));
889     #[cfg(feature = "proto-ipv6")]
890     pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([
891         0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
892     ]));
893     #[cfg(feature = "proto-ipv6")]
894     pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([
895         0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
896     ]));
897     #[cfg(feature = "proto-ipv6")]
898     pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED);
899 
900     #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
901     pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
902     #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
903     pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 2]));
904     #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
905     pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 3]));
906     #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
907     pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 4]));
908     #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
909     pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED);
910 
911     use super::*;
912     use crate::wire::{IpAddress, IpCidr, IpProtocol};
913     #[cfg(feature = "proto-ipv4")]
914     use crate::wire::{Ipv4Address, Ipv4Repr};
915 
916     #[test]
917     #[cfg(feature = "proto-ipv4")]
to_prefix_len_ipv4()918     fn to_prefix_len_ipv4() {
919         fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
920             assert_eq!(Some(prefix_len), mask.into().prefix_len());
921         }
922 
923         test_eq(0, Ipv4Address::new(0, 0, 0, 0));
924         test_eq(1, Ipv4Address::new(128, 0, 0, 0));
925         test_eq(2, Ipv4Address::new(192, 0, 0, 0));
926         test_eq(3, Ipv4Address::new(224, 0, 0, 0));
927         test_eq(4, Ipv4Address::new(240, 0, 0, 0));
928         test_eq(5, Ipv4Address::new(248, 0, 0, 0));
929         test_eq(6, Ipv4Address::new(252, 0, 0, 0));
930         test_eq(7, Ipv4Address::new(254, 0, 0, 0));
931         test_eq(8, Ipv4Address::new(255, 0, 0, 0));
932         test_eq(9, Ipv4Address::new(255, 128, 0, 0));
933         test_eq(10, Ipv4Address::new(255, 192, 0, 0));
934         test_eq(11, Ipv4Address::new(255, 224, 0, 0));
935         test_eq(12, Ipv4Address::new(255, 240, 0, 0));
936         test_eq(13, Ipv4Address::new(255, 248, 0, 0));
937         test_eq(14, Ipv4Address::new(255, 252, 0, 0));
938         test_eq(15, Ipv4Address::new(255, 254, 0, 0));
939         test_eq(16, Ipv4Address::new(255, 255, 0, 0));
940         test_eq(17, Ipv4Address::new(255, 255, 128, 0));
941         test_eq(18, Ipv4Address::new(255, 255, 192, 0));
942         test_eq(19, Ipv4Address::new(255, 255, 224, 0));
943         test_eq(20, Ipv4Address::new(255, 255, 240, 0));
944         test_eq(21, Ipv4Address::new(255, 255, 248, 0));
945         test_eq(22, Ipv4Address::new(255, 255, 252, 0));
946         test_eq(23, Ipv4Address::new(255, 255, 254, 0));
947         test_eq(24, Ipv4Address::new(255, 255, 255, 0));
948         test_eq(25, Ipv4Address::new(255, 255, 255, 128));
949         test_eq(26, Ipv4Address::new(255, 255, 255, 192));
950         test_eq(27, Ipv4Address::new(255, 255, 255, 224));
951         test_eq(28, Ipv4Address::new(255, 255, 255, 240));
952         test_eq(29, Ipv4Address::new(255, 255, 255, 248));
953         test_eq(30, Ipv4Address::new(255, 255, 255, 252));
954         test_eq(31, Ipv4Address::new(255, 255, 255, 254));
955         test_eq(32, Ipv4Address::new(255, 255, 255, 255));
956     }
957 
958     #[test]
959     #[cfg(feature = "proto-ipv4")]
to_prefix_len_ipv4_error()960     fn to_prefix_len_ipv4_error() {
961         assert_eq!(
962             None,
963             IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
964         );
965     }
966 
967     #[test]
968     #[cfg(feature = "proto-ipv6")]
to_prefix_len_ipv6()969     fn to_prefix_len_ipv6() {
970         fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
971             assert_eq!(Some(prefix_len), mask.into().prefix_len());
972         }
973 
974         test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
975         test_eq(
976             128,
977             Ipv6Address::new(
978                 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
979             ),
980         );
981     }
982 
983     #[test]
984     #[cfg(feature = "proto-ipv6")]
to_prefix_len_ipv6_error()985     fn to_prefix_len_ipv6_error() {
986         assert_eq!(
987             None,
988             IpAddress::from(Ipv6Address::new(
989                 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
990             ))
991             .prefix_len()
992         );
993     }
994 }
995