smoltcp/wire/
ip.rs

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