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