Skip to main content

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 const 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 From<Endpoint> for ::core::net::SocketAddr {
398    fn from(x: Endpoint) -> ::core::net::SocketAddr {
399        ::core::net::SocketAddr::new(x.addr.into(), x.port)
400    }
401}
402
403impl fmt::Display for Endpoint {
404    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
405        write!(f, "{}:{}", self.addr, self.port)
406    }
407}
408
409#[cfg(feature = "defmt")]
410impl defmt::Format for Endpoint {
411    fn format(&self, f: defmt::Formatter) {
412        defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
413    }
414}
415
416impl<T: Into<Address>> From<(T, u16)> for Endpoint {
417    fn from((addr, port): (T, u16)) -> Endpoint {
418        Endpoint {
419            addr: addr.into(),
420            port,
421        }
422    }
423}
424
425/// An internet endpoint address for listening.
426///
427/// In contrast with [`Endpoint`], `ListenEndpoint` allows not specifying the address,
428/// in order to listen on a given port at all our addresses.
429///
430/// An endpoint can be constructed from a port, in which case the address is unspecified.
431#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
432pub struct ListenEndpoint {
433    pub addr: Option<Address>,
434    pub port: u16,
435}
436
437impl ListenEndpoint {
438    /// Query whether the endpoint has a specified address and port.
439    pub const fn is_specified(&self) -> bool {
440        self.addr.is_some() && self.port != 0
441    }
442}
443
444#[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
445impl From<::core::net::SocketAddr> for ListenEndpoint {
446    fn from(x: ::core::net::SocketAddr) -> ListenEndpoint {
447        ListenEndpoint {
448            addr: Some(x.ip().into()),
449            port: x.port(),
450        }
451    }
452}
453
454#[cfg(feature = "proto-ipv4")]
455impl From<::core::net::SocketAddrV4> for ListenEndpoint {
456    fn from(x: ::core::net::SocketAddrV4) -> ListenEndpoint {
457        ListenEndpoint {
458            addr: Some((*x.ip()).into()),
459            port: x.port(),
460        }
461    }
462}
463
464#[cfg(feature = "proto-ipv6")]
465impl From<::core::net::SocketAddrV6> for ListenEndpoint {
466    fn from(x: ::core::net::SocketAddrV6) -> ListenEndpoint {
467        ListenEndpoint {
468            addr: Some((*x.ip()).into()),
469            port: x.port(),
470        }
471    }
472}
473
474impl fmt::Display for ListenEndpoint {
475    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
476        if let Some(addr) = self.addr {
477            write!(f, "{}:{}", addr, self.port)
478        } else {
479            write!(f, "*:{}", self.port)
480        }
481    }
482}
483
484#[cfg(feature = "defmt")]
485impl defmt::Format for ListenEndpoint {
486    fn format(&self, f: defmt::Formatter) {
487        defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
488    }
489}
490
491impl From<u16> for ListenEndpoint {
492    fn from(port: u16) -> ListenEndpoint {
493        ListenEndpoint { addr: None, port }
494    }
495}
496
497impl From<Endpoint> for ListenEndpoint {
498    fn from(endpoint: Endpoint) -> ListenEndpoint {
499        ListenEndpoint {
500            addr: Some(endpoint.addr),
501            port: endpoint.port,
502        }
503    }
504}
505
506impl<T: Into<Address>> From<(T, u16)> for ListenEndpoint {
507    fn from((addr, port): (T, u16)) -> ListenEndpoint {
508        ListenEndpoint {
509            addr: Some(addr.into()),
510            port,
511        }
512    }
513}
514
515/// An IP packet representation.
516///
517/// This enum abstracts the various versions of IP packets. It either contains an IPv4
518/// or IPv6 concrete high-level representation.
519#[derive(Debug, Clone, PartialEq, Eq)]
520#[cfg_attr(feature = "defmt", derive(defmt::Format))]
521pub enum Repr {
522    #[cfg(feature = "proto-ipv4")]
523    Ipv4(Ipv4Repr),
524    #[cfg(feature = "proto-ipv6")]
525    Ipv6(Ipv6Repr),
526}
527
528#[cfg(feature = "proto-ipv4")]
529impl From<Ipv4Repr> for Repr {
530    fn from(repr: Ipv4Repr) -> Repr {
531        Repr::Ipv4(repr)
532    }
533}
534
535#[cfg(feature = "proto-ipv6")]
536impl From<Ipv6Repr> for Repr {
537    fn from(repr: Ipv6Repr) -> Repr {
538        Repr::Ipv6(repr)
539    }
540}
541
542/// A read/write wrapper around a generic Internet Protocol packet buffer.
543#[derive(Debug, PartialEq, Eq, Clone)]
544#[cfg_attr(feature = "defmt", derive(defmt::Format))]
545pub struct Packet<T: AsRef<[u8]>> {
546    buffer: T,
547}
548
549mod field {
550    use crate::wire::field::*;
551    // 4-bit version number
552    pub const VER: Field = 0..1;
553}
554
555impl<T: AsRef<[u8]>> Packet<T> {
556    /// Create a raw octet buffer with an IP packet structure. This packet structure can be either
557    /// IPv4 or Ipv6
558    pub const fn new_unchecked(buffer: T) -> Packet<T> {
559        Packet { buffer }
560    }
561
562    /// Shorthand for a combination of [new_unchecked] and [check_len].
563    ///
564    /// [new_unchecked]: #method.new_unchecked
565    /// [check_len]: #method.check_len
566    pub fn new_checked(buffer: T) -> Result<Packet<T>> {
567        let packet = Self::new_unchecked(buffer);
568        packet.check_len()?;
569        Ok(packet)
570    }
571
572    /// Ensure that reading the version field of the buffer will not panic if called.
573    /// Returns `Err(Error)` if the buffer is too short.
574    pub fn check_len(&self) -> Result<()> {
575        // Both IPv4 and IPv6 headers contain Internet Protocol version in the upper nibble of the
576        // first packet byte
577        if self.buffer.as_ref().len() < field::VER.end {
578            Err(Error)
579        } else {
580            Ok(())
581        }
582    }
583
584    /// Consume the packet, returning the underlying buffer
585    pub fn into_inner(self) -> T {
586        self.buffer
587    }
588
589    /// Returns the version field.
590    pub fn version(&self) -> u8 {
591        let data = self.buffer.as_ref();
592        data[field::VER.start] >> 4
593    }
594}
595
596impl Repr {
597    /// Create a new IpRepr, choosing the right IP version for the src/dst addrs.
598    ///
599    /// # Panics
600    ///
601    /// Panics if `src_addr` and `dst_addr` are different IP version.
602    pub fn new(
603        src_addr: Address,
604        dst_addr: Address,
605        next_header: Protocol,
606        payload_len: usize,
607        hop_limit: u8,
608    ) -> Self {
609        match (src_addr, dst_addr) {
610            #[cfg(feature = "proto-ipv4")]
611            (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => Self::Ipv4(Ipv4Repr {
612                src_addr,
613                dst_addr,
614                next_header,
615                payload_len,
616                hop_limit,
617            }),
618            #[cfg(feature = "proto-ipv6")]
619            (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => Self::Ipv6(Ipv6Repr {
620                src_addr,
621                dst_addr,
622                next_header,
623                payload_len,
624                hop_limit,
625            }),
626            #[allow(unreachable_patterns)]
627            _ => panic!("IP version mismatch: src={src_addr:?} dst={dst_addr:?}"),
628        }
629    }
630
631    /// Parse an Internet Protocol packet and return an [IpRepr] containing either an Internet
632    /// Protocol version 4 or Internet Protocol version 6 packet. Delegates the parsing to the
633    /// specific Internet Protocol parsing function. Includes [ChecksumCapabilities] to handle
634    /// Internet Protocol version 4 parsing.
635    /// Returns `Err(Error)` if the packet does not include a valid IPv4 or IPv6 packet, or if the
636    /// specific Internet Protocol version feature is not enabled for the supplied packet
637    pub fn parse<T: AsRef<[u8]> + ?Sized>(
638        packet: &Packet<&T>,
639        checksum_caps: &ChecksumCapabilities,
640    ) -> Result<Repr> {
641        packet.check_len()?;
642        match packet.version() {
643            #[cfg(feature = "proto-ipv4")]
644            4 => {
645                let packet = Ipv4Packet::new_checked(packet.buffer)?;
646                let ipv4_repr = Ipv4Repr::parse(&packet, checksum_caps)?;
647                Ok(Repr::Ipv4(ipv4_repr))
648            }
649            #[cfg(feature = "proto-ipv6")]
650            6 => {
651                let packet = Ipv6Packet::new_checked(packet.buffer)?;
652                let ipv6_repr = Ipv6Repr::parse(&packet)?;
653                Ok(Repr::Ipv6(ipv6_repr))
654            }
655            _ => Err(Error),
656        }
657    }
658
659    /// Return the protocol version.
660    pub const fn version(&self) -> Version {
661        match *self {
662            #[cfg(feature = "proto-ipv4")]
663            Repr::Ipv4(_) => Version::Ipv4,
664            #[cfg(feature = "proto-ipv6")]
665            Repr::Ipv6(_) => Version::Ipv6,
666        }
667    }
668
669    /// Return the source address.
670    pub const fn src_addr(&self) -> Address {
671        match *self {
672            #[cfg(feature = "proto-ipv4")]
673            Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
674            #[cfg(feature = "proto-ipv6")]
675            Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
676        }
677    }
678
679    /// Return the destination address.
680    pub const fn dst_addr(&self) -> Address {
681        match *self {
682            #[cfg(feature = "proto-ipv4")]
683            Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
684            #[cfg(feature = "proto-ipv6")]
685            Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
686        }
687    }
688
689    /// Return the next header (protocol).
690    pub const fn next_header(&self) -> Protocol {
691        match *self {
692            #[cfg(feature = "proto-ipv4")]
693            Repr::Ipv4(repr) => repr.next_header,
694            #[cfg(feature = "proto-ipv6")]
695            Repr::Ipv6(repr) => repr.next_header,
696        }
697    }
698
699    /// Return the payload length.
700    pub const fn payload_len(&self) -> usize {
701        match *self {
702            #[cfg(feature = "proto-ipv4")]
703            Repr::Ipv4(repr) => repr.payload_len,
704            #[cfg(feature = "proto-ipv6")]
705            Repr::Ipv6(repr) => repr.payload_len,
706        }
707    }
708
709    /// Set the payload length.
710    pub fn set_payload_len(&mut self, length: usize) {
711        match self {
712            #[cfg(feature = "proto-ipv4")]
713            Repr::Ipv4(Ipv4Repr { payload_len, .. }) => *payload_len = length,
714            #[cfg(feature = "proto-ipv6")]
715            Repr::Ipv6(Ipv6Repr { payload_len, .. }) => *payload_len = length,
716        }
717    }
718
719    /// Return the TTL value.
720    pub const fn hop_limit(&self) -> u8 {
721        match *self {
722            #[cfg(feature = "proto-ipv4")]
723            Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
724            #[cfg(feature = "proto-ipv6")]
725            Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
726        }
727    }
728
729    /// Return the length of a header that will be emitted from this high-level representation.
730    pub const fn header_len(&self) -> usize {
731        match *self {
732            #[cfg(feature = "proto-ipv4")]
733            Repr::Ipv4(repr) => repr.buffer_len(),
734            #[cfg(feature = "proto-ipv6")]
735            Repr::Ipv6(repr) => repr.buffer_len(),
736        }
737    }
738
739    /// Emit this high-level representation into a buffer.
740    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
741        &self,
742        buffer: T,
743        _checksum_caps: &ChecksumCapabilities,
744    ) {
745        match *self {
746            #[cfg(feature = "proto-ipv4")]
747            Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
748            #[cfg(feature = "proto-ipv6")]
749            Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
750        }
751    }
752
753    /// Return the total length of a packet that will be emitted from this
754    /// high-level representation.
755    ///
756    /// This is the same as `repr.buffer_len() + repr.payload_len()`.
757    pub const fn buffer_len(&self) -> usize {
758        self.header_len() + self.payload_len()
759    }
760}
761
762pub mod checksum {
763    use byteorder::{ByteOrder, NetworkEndian};
764
765    use super::*;
766
767    const fn propagate_carries(word: u32) -> u16 {
768        let sum = (word >> 16) + (word & 0xffff);
769        ((sum >> 16) as u16) + (sum as u16)
770    }
771
772    /// Compute an RFC 1071 compliant checksum (without the final complement).
773    pub fn data(mut data: &[u8]) -> u16 {
774        let mut accum = 0;
775
776        // For each 32-byte chunk...
777        const CHUNK_SIZE: usize = 32;
778        while data.len() >= CHUNK_SIZE {
779            let mut d = &data[..CHUNK_SIZE];
780            // ... take by 2 bytes and sum them.
781            while d.len() >= 2 {
782                accum += NetworkEndian::read_u16(d) as u32;
783                d = &d[2..];
784            }
785
786            data = &data[CHUNK_SIZE..];
787        }
788
789        // Sum the rest that does not fit the last 32-byte chunk,
790        // taking by 2 bytes.
791        while data.len() >= 2 {
792            accum += NetworkEndian::read_u16(data) as u32;
793            data = &data[2..];
794        }
795
796        // Add the last remaining odd byte, if any.
797        if let Some(&value) = data.first() {
798            accum += (value as u32) << 8;
799        }
800
801        propagate_carries(accum)
802    }
803
804    /// Combine several RFC 1071 compliant checksums.
805    pub fn combine(checksums: &[u16]) -> u16 {
806        let mut accum: u32 = 0;
807        for &word in checksums {
808            accum += word as u32;
809        }
810        propagate_carries(accum)
811    }
812
813    #[cfg(feature = "proto-ipv4")]
814    pub fn pseudo_header_v4(
815        src_addr: &Ipv4Address,
816        dst_addr: &Ipv4Address,
817        next_header: Protocol,
818        length: u32,
819    ) -> u16 {
820        let mut proto_len = [0u8; 4];
821        proto_len[1] = next_header.into();
822        NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
823
824        combine(&[
825            data(&src_addr.octets()),
826            data(&dst_addr.octets()),
827            data(&proto_len[..]),
828        ])
829    }
830
831    #[cfg(feature = "proto-ipv6")]
832    pub fn pseudo_header_v6(
833        src_addr: &Ipv6Address,
834        dst_addr: &Ipv6Address,
835        next_header: Protocol,
836        length: u32,
837    ) -> u16 {
838        let mut proto_len = [0u8; 4];
839        proto_len[1] = next_header.into();
840        NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
841
842        combine(&[
843            data(&src_addr.octets()),
844            data(&dst_addr.octets()),
845            data(&proto_len[..]),
846        ])
847    }
848
849    pub fn pseudo_header(
850        src_addr: &Address,
851        dst_addr: &Address,
852        next_header: Protocol,
853        length: u32,
854    ) -> u16 {
855        match (src_addr, dst_addr) {
856            #[cfg(feature = "proto-ipv4")]
857            (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => {
858                pseudo_header_v4(src_addr, dst_addr, next_header, length)
859            }
860            #[cfg(feature = "proto-ipv6")]
861            (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => {
862                pseudo_header_v6(src_addr, dst_addr, next_header, length)
863            }
864            #[allow(unreachable_patterns)]
865            _ => unreachable!(),
866        }
867    }
868
869    // We use this in pretty printer implementations.
870    pub(crate) fn format_checksum(
871        f: &mut fmt::Formatter,
872        correct: bool,
873        partially_correct: bool,
874    ) -> fmt::Result {
875        if !correct {
876            if partially_correct {
877                write!(f, " (partial checksum correct)")
878            } else {
879                write!(f, " (checksum incorrect)")
880            }
881        } else {
882            Ok(())
883        }
884    }
885}
886
887use crate::wire::pretty_print::PrettyIndent;
888
889pub fn pretty_print_ip_payload<T: Into<Repr>>(
890    f: &mut fmt::Formatter,
891    indent: &mut PrettyIndent,
892    ip_repr: T,
893    payload: &[u8],
894) -> fmt::Result {
895    #[cfg(feature = "proto-ipv4")]
896    use super::pretty_print::PrettyPrint;
897    #[cfg(feature = "proto-ipv4")]
898    use crate::wire::Icmpv4Packet;
899    use crate::wire::ip::checksum::format_checksum;
900    use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
901
902    let checksum_caps = ChecksumCapabilities::ignored();
903    let repr = ip_repr.into();
904    match repr.next_header() {
905        #[cfg(feature = "proto-ipv4")]
906        Protocol::Icmp => {
907            indent.increase(f)?;
908            Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
909        }
910        Protocol::Udp => {
911            indent.increase(f)?;
912            match UdpPacket::<&[u8]>::new_checked(payload) {
913                Err(err) => write!(f, "{indent}({err})"),
914                Ok(udp_packet) => {
915                    match UdpRepr::parse(
916                        &udp_packet,
917                        &repr.src_addr(),
918                        &repr.dst_addr(),
919                        &checksum_caps,
920                    ) {
921                        Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
922                        Ok(udp_repr) => {
923                            write!(
924                                f,
925                                "{}{} len={}",
926                                indent,
927                                udp_repr,
928                                udp_packet.payload().len()
929                            )?;
930                            let valid =
931                                udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
932                            let partially_valid = udp_packet
933                                .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr());
934
935                            format_checksum(f, valid, partially_valid)
936                        }
937                    }
938                }
939            }
940        }
941        Protocol::Tcp => {
942            indent.increase(f)?;
943            match TcpPacket::<&[u8]>::new_checked(payload) {
944                Err(err) => write!(f, "{indent}({err})"),
945                Ok(tcp_packet) => {
946                    match TcpRepr::parse(
947                        &tcp_packet,
948                        &repr.src_addr(),
949                        &repr.dst_addr(),
950                        &checksum_caps,
951                    ) {
952                        Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
953                        Ok(tcp_repr) => {
954                            write!(f, "{indent}{tcp_repr}")?;
955                            let valid =
956                                tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
957                            let partially_valid = tcp_packet
958                                .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr());
959
960                            format_checksum(f, valid, partially_valid)
961                        }
962                    }
963                }
964            }
965        }
966        _ => Ok(()),
967    }
968}
969
970#[cfg(test)]
971pub(crate) mod test {
972    #![allow(unused)]
973
974    use super::*;
975    use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr};
976    #[cfg(feature = "proto-ipv4")]
977    use crate::wire::{Ipv4Address, Ipv4Repr};
978
979    #[test]
980    #[cfg(feature = "proto-ipv4")]
981    fn to_prefix_len_ipv4() {
982        fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
983            assert_eq!(Some(prefix_len), mask.into().prefix_len());
984        }
985
986        test_eq(0, Ipv4Address::new(0, 0, 0, 0));
987        test_eq(1, Ipv4Address::new(128, 0, 0, 0));
988        test_eq(2, Ipv4Address::new(192, 0, 0, 0));
989        test_eq(3, Ipv4Address::new(224, 0, 0, 0));
990        test_eq(4, Ipv4Address::new(240, 0, 0, 0));
991        test_eq(5, Ipv4Address::new(248, 0, 0, 0));
992        test_eq(6, Ipv4Address::new(252, 0, 0, 0));
993        test_eq(7, Ipv4Address::new(254, 0, 0, 0));
994        test_eq(8, Ipv4Address::new(255, 0, 0, 0));
995        test_eq(9, Ipv4Address::new(255, 128, 0, 0));
996        test_eq(10, Ipv4Address::new(255, 192, 0, 0));
997        test_eq(11, Ipv4Address::new(255, 224, 0, 0));
998        test_eq(12, Ipv4Address::new(255, 240, 0, 0));
999        test_eq(13, Ipv4Address::new(255, 248, 0, 0));
1000        test_eq(14, Ipv4Address::new(255, 252, 0, 0));
1001        test_eq(15, Ipv4Address::new(255, 254, 0, 0));
1002        test_eq(16, Ipv4Address::new(255, 255, 0, 0));
1003        test_eq(17, Ipv4Address::new(255, 255, 128, 0));
1004        test_eq(18, Ipv4Address::new(255, 255, 192, 0));
1005        test_eq(19, Ipv4Address::new(255, 255, 224, 0));
1006        test_eq(20, Ipv4Address::new(255, 255, 240, 0));
1007        test_eq(21, Ipv4Address::new(255, 255, 248, 0));
1008        test_eq(22, Ipv4Address::new(255, 255, 252, 0));
1009        test_eq(23, Ipv4Address::new(255, 255, 254, 0));
1010        test_eq(24, Ipv4Address::new(255, 255, 255, 0));
1011        test_eq(25, Ipv4Address::new(255, 255, 255, 128));
1012        test_eq(26, Ipv4Address::new(255, 255, 255, 192));
1013        test_eq(27, Ipv4Address::new(255, 255, 255, 224));
1014        test_eq(28, Ipv4Address::new(255, 255, 255, 240));
1015        test_eq(29, Ipv4Address::new(255, 255, 255, 248));
1016        test_eq(30, Ipv4Address::new(255, 255, 255, 252));
1017        test_eq(31, Ipv4Address::new(255, 255, 255, 254));
1018        test_eq(32, Ipv4Address::new(255, 255, 255, 255));
1019    }
1020
1021    #[test]
1022    #[cfg(feature = "proto-ipv4")]
1023    fn to_prefix_len_ipv4_error() {
1024        assert_eq!(
1025            None,
1026            IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
1027        );
1028    }
1029
1030    #[test]
1031    #[cfg(feature = "proto-ipv6")]
1032    fn to_prefix_len_ipv6() {
1033        fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1034            assert_eq!(Some(prefix_len), mask.into().prefix_len());
1035        }
1036
1037        test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
1038        test_eq(
1039            128,
1040            Ipv6Address::new(
1041                0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1042            ),
1043        );
1044    }
1045
1046    #[test]
1047    #[cfg(feature = "proto-ipv6")]
1048    fn to_prefix_len_ipv6_error() {
1049        assert_eq!(
1050            None,
1051            IpAddress::from(Ipv6Address::new(
1052                0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
1053            ))
1054            .prefix_len()
1055        );
1056    }
1057
1058    #[test]
1059    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1060    fn parse_ipv4_packet() {
1061        let ipv4_packet_bytes: [u8; 20] = [
1062            0x45, 0x00, 0x00, 0x14, 0x00, 0x01, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x7c, 0x11, 0x12,
1063            0x13, 0x14, 0x21, 0x22, 0x23, 0x24,
1064        ];
1065
1066        let expected = Ipv4Repr {
1067            src_addr: crate::wire::ipv4::Address::new(0x11, 0x12, 0x13, 0x14),
1068            dst_addr: crate::wire::ipv4::Address::new(0x21, 0x22, 0x23, 0x24),
1069            next_header: Protocol::Icmp,
1070            payload_len: 0,
1071            hop_limit: 64,
1072        };
1073
1074        let packet = Packet::new_unchecked(&ipv4_packet_bytes[..]);
1075        let ip_repr = IpRepr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1076        assert_eq!(ip_repr.version(), Version::Ipv4);
1077        let IpRepr::Ipv4(ipv4_repr) = ip_repr else {
1078            panic!("expected Ipv4Repr");
1079        };
1080        assert_eq!(ipv4_repr, expected);
1081        assert_eq!(packet.into_inner(), ipv4_packet_bytes);
1082    }
1083
1084    #[test]
1085    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1086    fn parse_ipv4_packet_error() {
1087        let ipv4_packet_bytes: [u8; 29] = [
1088            0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12,
1089            0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1090            0x00,
1091        ];
1092
1093        let packet = Packet::new_unchecked(&ipv4_packet_bytes[..]);
1094        let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1095        assert!(ip_repr_result.is_err());
1096    }
1097
1098    #[test]
1099    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1100    fn parse_ipv6_packet() {
1101        let ipv6_packet_bytes: [u8; 52] = [
1102            0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
1103            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00,
1104            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1105            0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
1106        ];
1107
1108        let expected = Ipv6Repr {
1109            src_addr: crate::wire::ipv6::Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
1110            dst_addr: crate::wire::ipv6::Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
1111            next_header: Protocol::Udp,
1112            payload_len: 12,
1113            hop_limit: 64,
1114        };
1115
1116        let packet = Packet::new_unchecked(&ipv6_packet_bytes[..]);
1117        let ip_repr = IpRepr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1118        assert_eq!(ip_repr.version(), Version::Ipv6);
1119        let IpRepr::Ipv6(ipv6_repr) = ip_repr else {
1120            panic!("expected Ipv6Repr");
1121        };
1122        assert_eq!(ipv6_repr, expected);
1123        assert_eq!(packet.into_inner(), ipv6_packet_bytes);
1124    }
1125
1126    #[test]
1127    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1128    fn parse_ipv6_packet_error() {
1129        let ipv6_packet_bytes: [u8; 51] = [
1130            0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
1131            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00,
1132            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1133            0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff,
1134        ];
1135
1136        let packet = Packet::new_unchecked(&ipv6_packet_bytes[..]);
1137        let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1138        assert!(ip_repr_result.is_err());
1139    }
1140
1141    #[test]
1142    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1143    fn parse_packet_too_short() {
1144        // Test empty packet where no version can be parsed
1145        let ip_packet = [0u8; 0];
1146        let packet_result = Packet::new_checked(&ip_packet[..]);
1147        assert!(packet_result.is_err());
1148    }
1149
1150    #[test]
1151    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1152    fn parse_packet_invalid_version() {
1153        let packet_bytes: [u8; 1] = [0xFF];
1154        let packet = Packet::new_unchecked(&packet_bytes[..]);
1155        let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1156        assert!(ip_repr_result.is_err());
1157    }
1158}