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`](Repr) 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(data: &[u8]) -> u16 {
774        // We calculate the sum in native-endian before converting to big-endian at the end
775        // see RFC 1071 section 2.(B) for details
776        let mut accum: u32 = 0;
777
778        // We manually unroll this hot loop.
779        // When optimizing for size (as is common for microcontrollers) the compiler will not unroll
780        // this. Manually unrolling allows us to do more work per loop tax (compare and branch).
781        // It does not seem to affect the auto-vectorization on bigger machines.
782        let (chunks, mut rem) = data.as_chunks::<4>();
783        for chunk in chunks {
784            let val_0 = u16::from_ne_bytes(chunk[..2].try_into().unwrap());
785            let val_1 = u16::from_ne_bytes(chunk[2..4].try_into().unwrap());
786            accum += val_0 as u32;
787            accum += val_1 as u32;
788        }
789
790        // Handle 2 bytes of tail, if present.
791        if rem.len() >= 2 {
792            let val = u16::from_ne_bytes(rem[..2].try_into().unwrap());
793            accum += val as u32;
794            rem = &rem[2..];
795        }
796
797        // Add the last remaining odd byte, if any.
798        if let Some(&value) = rem.first() {
799            accum += u16::from_ne_bytes([value, 0]) as u32;
800        }
801
802        let collapsed = propagate_carries(accum);
803        u16::to_be(collapsed)
804    }
805
806    /// Combine several RFC 1071 compliant checksums.
807    pub fn combine(checksums: &[u16]) -> u16 {
808        let mut accum: u32 = 0;
809        for &word in checksums {
810            accum += word as u32;
811        }
812        propagate_carries(accum)
813    }
814
815    #[cfg(feature = "proto-ipv4")]
816    pub fn pseudo_header_v4(
817        src_addr: &Ipv4Address,
818        dst_addr: &Ipv4Address,
819        next_header: Protocol,
820        length: u32,
821    ) -> u16 {
822        let mut proto_len = [0u8; 4];
823        proto_len[1] = next_header.into();
824        NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
825
826        combine(&[
827            data(&src_addr.octets()),
828            data(&dst_addr.octets()),
829            data(&proto_len[..]),
830        ])
831    }
832
833    #[cfg(feature = "proto-ipv6")]
834    pub fn pseudo_header_v6(
835        src_addr: &Ipv6Address,
836        dst_addr: &Ipv6Address,
837        next_header: Protocol,
838        length: u32,
839    ) -> u16 {
840        let mut proto_len = [0u8; 4];
841        proto_len[1] = next_header.into();
842        NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
843
844        combine(&[
845            data(&src_addr.octets()),
846            data(&dst_addr.octets()),
847            data(&proto_len[..]),
848        ])
849    }
850
851    pub fn pseudo_header(
852        src_addr: &Address,
853        dst_addr: &Address,
854        next_header: Protocol,
855        length: u32,
856    ) -> u16 {
857        match (src_addr, dst_addr) {
858            #[cfg(feature = "proto-ipv4")]
859            (Address::Ipv4(src_addr), Address::Ipv4(dst_addr)) => {
860                pseudo_header_v4(src_addr, dst_addr, next_header, length)
861            }
862            #[cfg(feature = "proto-ipv6")]
863            (Address::Ipv6(src_addr), Address::Ipv6(dst_addr)) => {
864                pseudo_header_v6(src_addr, dst_addr, next_header, length)
865            }
866            #[allow(unreachable_patterns)]
867            _ => unreachable!(),
868        }
869    }
870
871    // We use this in pretty printer implementations.
872    pub(crate) fn format_checksum(
873        f: &mut fmt::Formatter,
874        correct: bool,
875        partially_correct: bool,
876    ) -> fmt::Result {
877        if !correct {
878            if partially_correct {
879                write!(f, " (partial checksum correct)")
880            } else {
881                write!(f, " (checksum incorrect)")
882            }
883        } else {
884            Ok(())
885        }
886    }
887}
888
889use crate::wire::pretty_print::PrettyIndent;
890
891pub fn pretty_print_ip_payload<T: Into<Repr>>(
892    f: &mut fmt::Formatter,
893    indent: &mut PrettyIndent,
894    ip_repr: T,
895    payload: &[u8],
896) -> fmt::Result {
897    #[cfg(feature = "proto-ipv4")]
898    use super::pretty_print::PrettyPrint;
899    #[cfg(feature = "proto-ipv4")]
900    use crate::wire::Icmpv4Packet;
901    use crate::wire::ip::checksum::format_checksum;
902    use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
903
904    let checksum_caps = ChecksumCapabilities::ignored();
905    let repr = ip_repr.into();
906    match repr.next_header() {
907        #[cfg(feature = "proto-ipv4")]
908        Protocol::Icmp => {
909            indent.increase(f)?;
910            Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
911        }
912        Protocol::Udp => {
913            indent.increase(f)?;
914            match UdpPacket::<&[u8]>::new_checked(payload) {
915                Err(err) => write!(f, "{indent}({err})"),
916                Ok(udp_packet) => {
917                    match UdpRepr::parse(
918                        &udp_packet,
919                        &repr.src_addr(),
920                        &repr.dst_addr(),
921                        &checksum_caps,
922                    ) {
923                        Err(err) => write!(f, "{indent}{udp_packet} ({err})"),
924                        Ok(udp_repr) => {
925                            write!(
926                                f,
927                                "{}{} len={}",
928                                indent,
929                                udp_repr,
930                                udp_packet.payload().len()
931                            )?;
932                            let valid =
933                                udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
934                            let partially_valid = udp_packet
935                                .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr());
936
937                            format_checksum(f, valid, partially_valid)
938                        }
939                    }
940                }
941            }
942        }
943        Protocol::Tcp => {
944            indent.increase(f)?;
945            match TcpPacket::<&[u8]>::new_checked(payload) {
946                Err(err) => write!(f, "{indent}({err})"),
947                Ok(tcp_packet) => {
948                    match TcpRepr::parse(
949                        &tcp_packet,
950                        &repr.src_addr(),
951                        &repr.dst_addr(),
952                        &checksum_caps,
953                    ) {
954                        Err(err) => write!(f, "{indent}{tcp_packet} ({err})"),
955                        Ok(tcp_repr) => {
956                            write!(f, "{indent}{tcp_repr}")?;
957                            let valid =
958                                tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
959                            let partially_valid = tcp_packet
960                                .verify_partial_checksum(&repr.src_addr(), &repr.dst_addr());
961
962                            format_checksum(f, valid, partially_valid)
963                        }
964                    }
965                }
966            }
967        }
968        _ => Ok(()),
969    }
970}
971
972#[cfg(test)]
973pub(crate) mod test {
974    #![allow(unused)]
975
976    use super::*;
977    use crate::wire::{IpAddress, IpCidr, IpProtocol, IpRepr};
978    #[cfg(feature = "proto-ipv4")]
979    use crate::wire::{Ipv4Address, Ipv4Repr};
980
981    #[test]
982    #[cfg(feature = "proto-ipv4")]
983    fn to_prefix_len_ipv4() {
984        fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
985            assert_eq!(Some(prefix_len), mask.into().prefix_len());
986        }
987
988        test_eq(0, Ipv4Address::new(0, 0, 0, 0));
989        test_eq(1, Ipv4Address::new(128, 0, 0, 0));
990        test_eq(2, Ipv4Address::new(192, 0, 0, 0));
991        test_eq(3, Ipv4Address::new(224, 0, 0, 0));
992        test_eq(4, Ipv4Address::new(240, 0, 0, 0));
993        test_eq(5, Ipv4Address::new(248, 0, 0, 0));
994        test_eq(6, Ipv4Address::new(252, 0, 0, 0));
995        test_eq(7, Ipv4Address::new(254, 0, 0, 0));
996        test_eq(8, Ipv4Address::new(255, 0, 0, 0));
997        test_eq(9, Ipv4Address::new(255, 128, 0, 0));
998        test_eq(10, Ipv4Address::new(255, 192, 0, 0));
999        test_eq(11, Ipv4Address::new(255, 224, 0, 0));
1000        test_eq(12, Ipv4Address::new(255, 240, 0, 0));
1001        test_eq(13, Ipv4Address::new(255, 248, 0, 0));
1002        test_eq(14, Ipv4Address::new(255, 252, 0, 0));
1003        test_eq(15, Ipv4Address::new(255, 254, 0, 0));
1004        test_eq(16, Ipv4Address::new(255, 255, 0, 0));
1005        test_eq(17, Ipv4Address::new(255, 255, 128, 0));
1006        test_eq(18, Ipv4Address::new(255, 255, 192, 0));
1007        test_eq(19, Ipv4Address::new(255, 255, 224, 0));
1008        test_eq(20, Ipv4Address::new(255, 255, 240, 0));
1009        test_eq(21, Ipv4Address::new(255, 255, 248, 0));
1010        test_eq(22, Ipv4Address::new(255, 255, 252, 0));
1011        test_eq(23, Ipv4Address::new(255, 255, 254, 0));
1012        test_eq(24, Ipv4Address::new(255, 255, 255, 0));
1013        test_eq(25, Ipv4Address::new(255, 255, 255, 128));
1014        test_eq(26, Ipv4Address::new(255, 255, 255, 192));
1015        test_eq(27, Ipv4Address::new(255, 255, 255, 224));
1016        test_eq(28, Ipv4Address::new(255, 255, 255, 240));
1017        test_eq(29, Ipv4Address::new(255, 255, 255, 248));
1018        test_eq(30, Ipv4Address::new(255, 255, 255, 252));
1019        test_eq(31, Ipv4Address::new(255, 255, 255, 254));
1020        test_eq(32, Ipv4Address::new(255, 255, 255, 255));
1021    }
1022
1023    #[test]
1024    #[cfg(feature = "proto-ipv4")]
1025    fn to_prefix_len_ipv4_error() {
1026        assert_eq!(
1027            None,
1028            IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
1029        );
1030    }
1031
1032    #[test]
1033    #[cfg(feature = "proto-ipv6")]
1034    fn to_prefix_len_ipv6() {
1035        fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1036            assert_eq!(Some(prefix_len), mask.into().prefix_len());
1037        }
1038
1039        test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
1040        test_eq(
1041            128,
1042            Ipv6Address::new(
1043                0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1044            ),
1045        );
1046    }
1047
1048    #[test]
1049    #[cfg(feature = "proto-ipv6")]
1050    fn to_prefix_len_ipv6_error() {
1051        assert_eq!(
1052            None,
1053            IpAddress::from(Ipv6Address::new(
1054                0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
1055            ))
1056            .prefix_len()
1057        );
1058    }
1059
1060    #[test]
1061    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1062    fn parse_ipv4_packet() {
1063        let ipv4_packet_bytes: [u8; 20] = [
1064            0x45, 0x00, 0x00, 0x14, 0x00, 0x01, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x7c, 0x11, 0x12,
1065            0x13, 0x14, 0x21, 0x22, 0x23, 0x24,
1066        ];
1067
1068        let expected = Ipv4Repr {
1069            src_addr: crate::wire::ipv4::Address::new(0x11, 0x12, 0x13, 0x14),
1070            dst_addr: crate::wire::ipv4::Address::new(0x21, 0x22, 0x23, 0x24),
1071            next_header: Protocol::Icmp,
1072            payload_len: 0,
1073            hop_limit: 64,
1074        };
1075
1076        let packet = Packet::new_unchecked(&ipv4_packet_bytes[..]);
1077        let ip_repr = IpRepr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1078        assert_eq!(ip_repr.version(), Version::Ipv4);
1079        let IpRepr::Ipv4(ipv4_repr) = ip_repr else {
1080            panic!("expected Ipv4Repr");
1081        };
1082        assert_eq!(ipv4_repr, expected);
1083        assert_eq!(packet.into_inner(), ipv4_packet_bytes);
1084    }
1085
1086    #[test]
1087    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1088    fn parse_ipv4_packet_error() {
1089        let ipv4_packet_bytes: [u8; 29] = [
1090            0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12,
1091            0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1092            0x00,
1093        ];
1094
1095        let packet = Packet::new_unchecked(&ipv4_packet_bytes[..]);
1096        let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1097        assert!(ip_repr_result.is_err());
1098    }
1099
1100    #[test]
1101    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1102    fn parse_ipv6_packet() {
1103        let ipv6_packet_bytes: [u8; 52] = [
1104            0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
1105            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00,
1106            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1107            0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
1108        ];
1109
1110        let expected = Ipv6Repr {
1111            src_addr: crate::wire::ipv6::Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
1112            dst_addr: crate::wire::ipv6::Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
1113            next_header: Protocol::Udp,
1114            payload_len: 12,
1115            hop_limit: 64,
1116        };
1117
1118        let packet = Packet::new_unchecked(&ipv6_packet_bytes[..]);
1119        let ip_repr = IpRepr::parse(&packet, &ChecksumCapabilities::default()).unwrap();
1120        assert_eq!(ip_repr.version(), Version::Ipv6);
1121        let IpRepr::Ipv6(ipv6_repr) = ip_repr else {
1122            panic!("expected Ipv6Repr");
1123        };
1124        assert_eq!(ipv6_repr, expected);
1125        assert_eq!(packet.into_inner(), ipv6_packet_bytes);
1126    }
1127
1128    #[test]
1129    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1130    fn parse_ipv6_packet_error() {
1131        let ipv6_packet_bytes: [u8; 51] = [
1132            0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
1133            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00,
1134            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
1135            0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff,
1136        ];
1137
1138        let packet = Packet::new_unchecked(&ipv6_packet_bytes[..]);
1139        let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1140        assert!(ip_repr_result.is_err());
1141    }
1142
1143    #[test]
1144    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1145    fn parse_packet_too_short() {
1146        // Test empty packet where no version can be parsed
1147        let ip_packet = [0u8; 0];
1148        let packet_result = Packet::new_checked(&ip_packet[..]);
1149        assert!(packet_result.is_err());
1150    }
1151
1152    #[test]
1153    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1154    fn parse_packet_invalid_version() {
1155        let packet_bytes: [u8; 1] = [0xFF];
1156        let packet = Packet::new_unchecked(&packet_bytes[..]);
1157        let ip_repr_result = IpRepr::parse(&packet, &ChecksumCapabilities::default());
1158        assert!(ip_repr_result.is_err());
1159    }
1160}