ya_smoltcp/wire/
ip.rs

1use core::convert::From;
2use core::fmt;
3
4use crate::phy::ChecksumCapabilities;
5#[cfg(feature = "proto-ipv4")]
6use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
7#[cfg(feature = "proto-ipv6")]
8use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
9use crate::{Error, Result};
10
11/// Internet protocol version.
12#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14#[non_exhaustive]
15pub enum Version {
16    Unspecified,
17    #[cfg(feature = "proto-ipv4")]
18    Ipv4,
19    #[cfg(feature = "proto-ipv6")]
20    Ipv6,
21}
22
23impl Version {
24    /// Return the version of an IP packet stored in the provided buffer.
25    ///
26    /// This function never returns `Ok(IpVersion::Unspecified)`; instead,
27    /// unknown versions result in `Err(Error::Unrecognized)`.
28    pub fn of_packet(data: &[u8]) -> Result<Version> {
29        match data[0] >> 4 {
30            #[cfg(feature = "proto-ipv4")]
31            4 => Ok(Version::Ipv4),
32            #[cfg(feature = "proto-ipv6")]
33            6 => Ok(Version::Ipv6),
34            _ => Err(Error::Unrecognized),
35        }
36    }
37}
38
39impl fmt::Display for Version {
40    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41        match *self {
42            Version::Unspecified => write!(f, "IPv?"),
43            #[cfg(feature = "proto-ipv4")]
44            Version::Ipv4 => write!(f, "IPv4"),
45            #[cfg(feature = "proto-ipv6")]
46            Version::Ipv6 => write!(f, "IPv6"),
47        }
48    }
49}
50
51enum_with_unknown! {
52    /// IP datagram encapsulated protocol.
53    pub enum Protocol(u8) {
54        HopByHop  = 0x00,
55        Icmp      = 0x01,
56        Igmp      = 0x02,
57        Tcp       = 0x06,
58        Udp       = 0x11,
59        Ipv6Route = 0x2b,
60        Ipv6Frag  = 0x2c,
61        Icmpv6    = 0x3a,
62        Ipv6NoNxt = 0x3b,
63        Ipv6Opts  = 0x3c
64    }
65}
66
67impl fmt::Display for Protocol {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        match *self {
70            Protocol::HopByHop => write!(f, "Hop-by-Hop"),
71            Protocol::Icmp => write!(f, "ICMP"),
72            Protocol::Igmp => write!(f, "IGMP"),
73            Protocol::Tcp => write!(f, "TCP"),
74            Protocol::Udp => write!(f, "UDP"),
75            Protocol::Ipv6Route => write!(f, "IPv6-Route"),
76            Protocol::Ipv6Frag => write!(f, "IPv6-Frag"),
77            Protocol::Icmpv6 => write!(f, "ICMPv6"),
78            Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
79            Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
80            Protocol::Unknown(id) => write!(f, "0x{:02x}", id),
81        }
82    }
83}
84
85/// An internetworking address.
86#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
87#[non_exhaustive]
88pub enum Address {
89    /// An unspecified address.
90    /// May be used as a placeholder for storage where the address is not assigned yet.
91    Unspecified,
92    /// An IPv4 address.
93    #[cfg(feature = "proto-ipv4")]
94    Ipv4(Ipv4Address),
95    /// An IPv6 address.
96    #[cfg(feature = "proto-ipv6")]
97    Ipv6(Ipv6Address),
98}
99
100impl Address {
101    /// Create an address wrapping an IPv4 address with the given octets.
102    #[cfg(feature = "proto-ipv4")]
103    pub fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
104        Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
105    }
106
107    /// Create an address wrapping an IPv6 address with the given octets.
108    #[cfg(feature = "proto-ipv6")]
109    #[allow(clippy::too_many_arguments)]
110    pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
111        Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
112    }
113
114    /// Return an address as a sequence of octets, in big-endian.
115    pub fn as_bytes(&self) -> &[u8] {
116        match *self {
117            Address::Unspecified => &[],
118            #[cfg(feature = "proto-ipv4")]
119            Address::Ipv4(ref addr) => addr.as_bytes(),
120            #[cfg(feature = "proto-ipv6")]
121            Address::Ipv6(ref addr) => addr.as_bytes(),
122        }
123    }
124
125    /// Query whether the address is a valid unicast address.
126    pub fn is_unicast(&self) -> bool {
127        match *self {
128            Address::Unspecified => false,
129            #[cfg(feature = "proto-ipv4")]
130            Address::Ipv4(addr) => addr.is_unicast(),
131            #[cfg(feature = "proto-ipv6")]
132            Address::Ipv6(addr) => addr.is_unicast(),
133        }
134    }
135
136    /// Query whether the address is a valid multicast address.
137    pub fn is_multicast(&self) -> bool {
138        match *self {
139            Address::Unspecified => false,
140            #[cfg(feature = "proto-ipv4")]
141            Address::Ipv4(addr) => addr.is_multicast(),
142            #[cfg(feature = "proto-ipv6")]
143            Address::Ipv6(addr) => addr.is_multicast(),
144        }
145    }
146
147    /// Query whether the address is the broadcast address.
148    pub fn is_broadcast(&self) -> bool {
149        match *self {
150            Address::Unspecified => false,
151            #[cfg(feature = "proto-ipv4")]
152            Address::Ipv4(addr) => addr.is_broadcast(),
153            #[cfg(feature = "proto-ipv6")]
154            Address::Ipv6(_) => false,
155        }
156    }
157
158    /// Query whether the address falls into the "unspecified" range.
159    pub fn is_unspecified(&self) -> bool {
160        match *self {
161            Address::Unspecified => true,
162            #[cfg(feature = "proto-ipv4")]
163            Address::Ipv4(addr) => addr.is_unspecified(),
164            #[cfg(feature = "proto-ipv6")]
165            Address::Ipv6(addr) => addr.is_unspecified(),
166        }
167    }
168
169    /// Return an unspecified address that has the same IP version as `self`.
170    pub fn as_unspecified(&self) -> Address {
171        match *self {
172            Address::Unspecified => Address::Unspecified,
173            #[cfg(feature = "proto-ipv4")]
174            Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED),
175            #[cfg(feature = "proto-ipv6")]
176            Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED),
177        }
178    }
179
180    /// If `self` is a CIDR-compatible subnet mask, return `Some(prefix_len)`,
181    /// where `prefix_len` is the number of leading zeroes. Return `None` otherwise.
182    pub fn prefix_len(&self) -> Option<u8> {
183        let mut ones = true;
184        let mut prefix_len = 0;
185        for byte in self.as_bytes() {
186            let mut mask = 0x80;
187            for _ in 0..8 {
188                let one = *byte & mask != 0;
189                if ones {
190                    // Expect 1s until first 0
191                    if one {
192                        prefix_len += 1;
193                    } else {
194                        ones = false;
195                    }
196                } else if one {
197                    // 1 where 0 was expected
198                    return None;
199                }
200                mask >>= 1;
201            }
202        }
203        Some(prefix_len)
204    }
205}
206
207#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
208impl From<::std::net::IpAddr> for Address {
209    fn from(x: ::std::net::IpAddr) -> Address {
210        match x {
211            ::std::net::IpAddr::V4(ipv4) => Address::Ipv4(ipv4.into()),
212            ::std::net::IpAddr::V6(ipv6) => Address::Ipv6(ipv6.into()),
213        }
214    }
215}
216
217#[cfg(feature = "std")]
218impl From<Address> for ::std::net::IpAddr {
219    fn from(x: Address) -> ::std::net::IpAddr {
220        match x {
221            #[cfg(feature = "proto-ipv4")]
222            Address::Ipv4(ipv4) => ::std::net::IpAddr::V4(ipv4.into()),
223            #[cfg(feature = "proto-ipv6")]
224            Address::Ipv6(ipv6) => ::std::net::IpAddr::V6(ipv6.into()),
225            _ => unreachable!(),
226        }
227    }
228}
229
230#[cfg(all(feature = "std", feature = "proto-ipv4"))]
231impl From<::std::net::Ipv4Addr> for Address {
232    fn from(ipv4: ::std::net::Ipv4Addr) -> Address {
233        Address::Ipv4(ipv4.into())
234    }
235}
236
237#[cfg(all(feature = "std", feature = "proto-ipv6"))]
238impl From<::std::net::Ipv6Addr> for Address {
239    fn from(ipv6: ::std::net::Ipv6Addr) -> Address {
240        Address::Ipv6(ipv6.into())
241    }
242}
243
244impl Default for Address {
245    fn default() -> Address {
246        Address::Unspecified
247    }
248}
249
250#[cfg(feature = "proto-ipv4")]
251impl From<Ipv4Address> for Address {
252    fn from(addr: Ipv4Address) -> Self {
253        Address::Ipv4(addr)
254    }
255}
256
257#[cfg(feature = "proto-ipv6")]
258impl From<Ipv6Address> for Address {
259    fn from(addr: Ipv6Address) -> Self {
260        Address::Ipv6(addr)
261    }
262}
263
264impl fmt::Display for Address {
265    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266        match *self {
267            Address::Unspecified => write!(f, "*"),
268            #[cfg(feature = "proto-ipv4")]
269            Address::Ipv4(addr) => write!(f, "{}", addr),
270            #[cfg(feature = "proto-ipv6")]
271            Address::Ipv6(addr) => write!(f, "{}", addr),
272        }
273    }
274}
275
276#[cfg(feature = "defmt")]
277impl defmt::Format for Address {
278    fn format(&self, f: defmt::Formatter) {
279        match self {
280            &Address::Unspecified => defmt::write!(f, "{:?}", "*"),
281            #[cfg(feature = "proto-ipv4")]
282            &Address::Ipv4(addr) => defmt::write!(f, "{:?}", addr),
283            #[cfg(feature = "proto-ipv6")]
284            &Address::Ipv6(addr) => defmt::write!(f, "{:?}", addr),
285        }
286    }
287}
288
289/// A specification of a CIDR block, containing an address and a variable-length
290/// subnet masking prefix length.
291#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
292#[non_exhaustive]
293pub enum Cidr {
294    #[cfg(feature = "proto-ipv4")]
295    Ipv4(Ipv4Cidr),
296    #[cfg(feature = "proto-ipv6")]
297    Ipv6(Ipv6Cidr),
298}
299
300impl Cidr {
301    /// Create a CIDR block from the given address and prefix length.
302    ///
303    /// # Panics
304    /// This function panics if the given address is unspecified, or
305    /// the given prefix length is invalid for the given address.
306    pub fn new(addr: Address, prefix_len: u8) -> Cidr {
307        match addr {
308            #[cfg(feature = "proto-ipv4")]
309            Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
310            #[cfg(feature = "proto-ipv6")]
311            Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
312            Address::Unspecified => {
313                panic!("a CIDR block cannot be based on an unspecified address")
314            }
315        }
316    }
317
318    /// Return the IP address of this CIDR block.
319    pub fn address(&self) -> Address {
320        match *self {
321            #[cfg(feature = "proto-ipv4")]
322            Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
323            #[cfg(feature = "proto-ipv6")]
324            Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()),
325        }
326    }
327
328    /// Return the prefix length of this CIDR block.
329    pub fn prefix_len(&self) -> u8 {
330        match *self {
331            #[cfg(feature = "proto-ipv4")]
332            Cidr::Ipv4(cidr) => cidr.prefix_len(),
333            #[cfg(feature = "proto-ipv6")]
334            Cidr::Ipv6(cidr) => cidr.prefix_len(),
335        }
336    }
337
338    /// Query whether the subnetwork described by this CIDR block contains
339    /// the given address.
340    pub fn contains_addr(&self, addr: &Address) -> bool {
341        match (self, addr) {
342            #[cfg(feature = "proto-ipv4")]
343            (&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) => cidr.contains_addr(addr),
344            #[cfg(feature = "proto-ipv6")]
345            (&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) => cidr.contains_addr(addr),
346            #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
347            (&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) => false,
348            (_, &Address::Unspecified) =>
349            // a fully unspecified address covers both IPv4 and IPv6,
350            // and no CIDR block can do that.
351            {
352                false
353            }
354        }
355    }
356
357    /// Query whether the subnetwork described by this CIDR block contains
358    /// the subnetwork described by the given CIDR block.
359    pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
360        match (self, subnet) {
361            #[cfg(feature = "proto-ipv4")]
362            (&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) => cidr.contains_subnet(other),
363            #[cfg(feature = "proto-ipv6")]
364            (&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) => cidr.contains_subnet(other),
365            #[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
366            (&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) => false,
367        }
368    }
369}
370
371#[cfg(feature = "proto-ipv4")]
372impl From<Ipv4Cidr> for Cidr {
373    fn from(addr: Ipv4Cidr) -> Self {
374        Cidr::Ipv4(addr)
375    }
376}
377
378#[cfg(feature = "proto-ipv6")]
379impl From<Ipv6Cidr> for Cidr {
380    fn from(addr: Ipv6Cidr) -> Self {
381        Cidr::Ipv6(addr)
382    }
383}
384
385impl fmt::Display for Cidr {
386    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
387        match *self {
388            #[cfg(feature = "proto-ipv4")]
389            Cidr::Ipv4(cidr) => write!(f, "{}", cidr),
390            #[cfg(feature = "proto-ipv6")]
391            Cidr::Ipv6(cidr) => write!(f, "{}", cidr),
392        }
393    }
394}
395
396#[cfg(feature = "defmt")]
397impl defmt::Format for Cidr {
398    fn format(&self, f: defmt::Formatter) {
399        match self {
400            #[cfg(feature = "proto-ipv4")]
401            &Cidr::Ipv4(cidr) => defmt::write!(f, "{:?}", cidr),
402            #[cfg(feature = "proto-ipv6")]
403            &Cidr::Ipv6(cidr) => defmt::write!(f, "{:?}", cidr),
404        }
405    }
406}
407
408/// An internet endpoint address.
409///
410/// An endpoint can be constructed from a port, in which case the address is unspecified.
411#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
412pub struct Endpoint {
413    pub addr: Address,
414    pub port: u16,
415}
416
417impl Endpoint {
418    /// An endpoint with unspecified address and port.
419    pub const UNSPECIFIED: Endpoint = Endpoint {
420        addr: Address::Unspecified,
421        port: 0,
422    };
423
424    /// Create an endpoint address from given address and port.
425    pub fn new(addr: Address, port: u16) -> Endpoint {
426        Endpoint { addr, port }
427    }
428
429    /// Query whether the endpoint has a specified address and port.
430    pub fn is_specified(&self) -> bool {
431        !self.addr.is_unspecified() && self.port != 0
432    }
433}
434
435#[cfg(all(feature = "std", feature = "proto-ipv4", feature = "proto-ipv6"))]
436impl From<::std::net::SocketAddr> for Endpoint {
437    fn from(x: ::std::net::SocketAddr) -> Endpoint {
438        Endpoint {
439            addr: x.ip().into(),
440            port: x.port(),
441        }
442    }
443}
444
445#[cfg(all(feature = "std", feature = "proto-ipv4"))]
446impl From<::std::net::SocketAddrV4> for Endpoint {
447    fn from(x: ::std::net::SocketAddrV4) -> Endpoint {
448        Endpoint {
449            addr: (*x.ip()).into(),
450            port: x.port(),
451        }
452    }
453}
454
455#[cfg(all(feature = "std", feature = "proto-ipv6"))]
456impl From<::std::net::SocketAddrV6> for Endpoint {
457    fn from(x: ::std::net::SocketAddrV6) -> Endpoint {
458        Endpoint {
459            addr: (*x.ip()).into(),
460            port: x.port(),
461        }
462    }
463}
464
465impl fmt::Display for Endpoint {
466    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
467        write!(f, "{}:{}", self.addr, self.port)
468    }
469}
470
471#[cfg(feature = "defmt")]
472impl defmt::Format for Endpoint {
473    fn format(&self, f: defmt::Formatter) {
474        defmt::write!(f, "{:?}:{=u16}", self.addr, self.port);
475    }
476}
477
478impl From<u16> for Endpoint {
479    fn from(port: u16) -> Endpoint {
480        Endpoint {
481            addr: Address::Unspecified,
482            port,
483        }
484    }
485}
486
487impl<T: Into<Address>> From<(T, u16)> for Endpoint {
488    fn from((addr, port): (T, u16)) -> Endpoint {
489        Endpoint {
490            addr: addr.into(),
491            port,
492        }
493    }
494}
495
496/// An IP packet representation.
497///
498/// This enum abstracts the various versions of IP packets. It either contains a concrete
499/// high-level representation for some IP protocol version, or an unspecified representation,
500/// which permits the `IpAddress::Unspecified` addresses.
501#[derive(Debug, Clone, PartialEq, Eq)]
502#[cfg_attr(feature = "defmt", derive(defmt::Format))]
503#[non_exhaustive]
504pub enum Repr {
505    Unspecified {
506        src_addr: Address,
507        dst_addr: Address,
508        protocol: Protocol,
509        payload_len: usize,
510        hop_limit: u8,
511    },
512    #[cfg(feature = "proto-ipv4")]
513    Ipv4(Ipv4Repr),
514    #[cfg(feature = "proto-ipv6")]
515    Ipv6(Ipv6Repr),
516}
517
518#[cfg(feature = "proto-ipv4")]
519impl From<Ipv4Repr> for Repr {
520    fn from(repr: Ipv4Repr) -> Repr {
521        Repr::Ipv4(repr)
522    }
523}
524
525#[cfg(feature = "proto-ipv6")]
526impl From<Ipv6Repr> for Repr {
527    fn from(repr: Ipv6Repr) -> Repr {
528        Repr::Ipv6(repr)
529    }
530}
531
532impl Repr {
533    /// Return the protocol version.
534    pub fn version(&self) -> Version {
535        match *self {
536            Repr::Unspecified { .. } => Version::Unspecified,
537            #[cfg(feature = "proto-ipv4")]
538            Repr::Ipv4(_) => Version::Ipv4,
539            #[cfg(feature = "proto-ipv6")]
540            Repr::Ipv6(_) => Version::Ipv6,
541        }
542    }
543
544    /// Return the source address.
545    pub fn src_addr(&self) -> Address {
546        match *self {
547            Repr::Unspecified { src_addr, .. } => src_addr,
548            #[cfg(feature = "proto-ipv4")]
549            Repr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
550            #[cfg(feature = "proto-ipv6")]
551            Repr::Ipv6(repr) => Address::Ipv6(repr.src_addr),
552        }
553    }
554
555    /// Return the destination address.
556    pub fn dst_addr(&self) -> Address {
557        match *self {
558            Repr::Unspecified { dst_addr, .. } => dst_addr,
559            #[cfg(feature = "proto-ipv4")]
560            Repr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
561            #[cfg(feature = "proto-ipv6")]
562            Repr::Ipv6(repr) => Address::Ipv6(repr.dst_addr),
563        }
564    }
565
566    /// Return the protocol.
567    pub fn protocol(&self) -> Protocol {
568        match *self {
569            Repr::Unspecified { protocol, .. } => protocol,
570            #[cfg(feature = "proto-ipv4")]
571            Repr::Ipv4(repr) => repr.protocol,
572            #[cfg(feature = "proto-ipv6")]
573            Repr::Ipv6(repr) => repr.next_header,
574        }
575    }
576
577    /// Return the payload length.
578    pub fn payload_len(&self) -> usize {
579        match *self {
580            Repr::Unspecified { payload_len, .. } => payload_len,
581            #[cfg(feature = "proto-ipv4")]
582            Repr::Ipv4(repr) => repr.payload_len,
583            #[cfg(feature = "proto-ipv6")]
584            Repr::Ipv6(repr) => repr.payload_len,
585        }
586    }
587
588    /// Set the payload length.
589    pub fn set_payload_len(&mut self, length: usize) {
590        match *self {
591            Repr::Unspecified {
592                ref mut payload_len,
593                ..
594            } => *payload_len = length,
595            #[cfg(feature = "proto-ipv4")]
596            Repr::Ipv4(Ipv4Repr {
597                ref mut payload_len,
598                ..
599            }) => *payload_len = length,
600            #[cfg(feature = "proto-ipv6")]
601            Repr::Ipv6(Ipv6Repr {
602                ref mut payload_len,
603                ..
604            }) => *payload_len = length,
605        }
606    }
607
608    /// Return the TTL value.
609    pub fn hop_limit(&self) -> u8 {
610        match *self {
611            Repr::Unspecified { hop_limit, .. } => hop_limit,
612            #[cfg(feature = "proto-ipv4")]
613            Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
614            #[cfg(feature = "proto-ipv6")]
615            Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
616        }
617    }
618
619    /// Convert an unspecified representation into a concrete one, or return
620    /// `Err(Error::Unaddressable)` if not possible.
621    ///
622    /// # Panics
623    /// This function panics if source and destination addresses belong to different families,
624    /// or the destination address is unspecified, since this indicates a logic error.
625    pub fn lower(&self, fallback_src_addrs: &[Cidr]) -> Result<Repr> {
626        macro_rules! resolve_unspecified {
627            ($reprty:path, $ipty:path, $iprepr:expr, $fallbacks:expr) => {
628                if $iprepr.src_addr.is_unspecified() {
629                    for cidr in $fallbacks {
630                        match cidr.address() {
631                            $ipty(addr) => {
632                                $iprepr.src_addr = addr;
633                                return Ok($reprty($iprepr));
634                            }
635                            _ => (),
636                        }
637                    }
638                    Err(Error::Unaddressable)
639                } else {
640                    Ok($reprty($iprepr))
641                }
642            };
643        }
644
645        match self {
646            #[cfg(feature = "proto-ipv4")]
647            &Repr::Unspecified {
648                src_addr: src_addr @ Address::Unspecified,
649                dst_addr: Address::Ipv4(dst_addr),
650                protocol,
651                payload_len,
652                hop_limit,
653            }
654            | &Repr::Unspecified {
655                src_addr: src_addr @ Address::Ipv4(_),
656                dst_addr: Address::Ipv4(dst_addr),
657                protocol,
658                payload_len,
659                hop_limit,
660            } if src_addr.is_unspecified() => {
661                let mut src_addr = if let Address::Ipv4(src_ipv4_addr) = src_addr {
662                    Some(src_ipv4_addr)
663                } else {
664                    None
665                };
666                for cidr in fallback_src_addrs {
667                    if let Address::Ipv4(addr) = cidr.address() {
668                        src_addr = Some(addr);
669                        break;
670                    }
671                }
672                Ok(Repr::Ipv4(Ipv4Repr {
673                    src_addr: src_addr.ok_or(Error::Unaddressable)?,
674                    dst_addr,
675                    protocol,
676                    payload_len,
677                    hop_limit,
678                }))
679            }
680
681            #[cfg(feature = "proto-ipv6")]
682            &Repr::Unspecified {
683                src_addr: src_addr @ Address::Unspecified,
684                dst_addr: Address::Ipv6(dst_addr),
685                protocol,
686                payload_len,
687                hop_limit,
688            }
689            | &Repr::Unspecified {
690                src_addr: src_addr @ Address::Ipv6(_),
691                dst_addr: Address::Ipv6(dst_addr),
692                protocol,
693                payload_len,
694                hop_limit,
695            } if src_addr.is_unspecified() => {
696                let mut src_addr = if let Address::Ipv6(src_ipv6_addr) = src_addr {
697                    Some(src_ipv6_addr)
698                } else {
699                    None
700                };
701                for cidr in fallback_src_addrs {
702                    if let Address::Ipv6(addr) = cidr.address() {
703                        src_addr = Some(addr);
704                        break;
705                    }
706                }
707                Ok(Repr::Ipv6(Ipv6Repr {
708                    src_addr: src_addr.ok_or(Error::Unaddressable)?,
709                    next_header: protocol,
710                    dst_addr,
711                    payload_len,
712                    hop_limit,
713                }))
714            }
715
716            #[cfg(feature = "proto-ipv4")]
717            &Repr::Unspecified {
718                src_addr: Address::Ipv4(src_addr),
719                dst_addr: Address::Ipv4(dst_addr),
720                protocol,
721                payload_len,
722                hop_limit,
723            } => Ok(Repr::Ipv4(Ipv4Repr {
724                src_addr: src_addr,
725                dst_addr: dst_addr,
726                protocol: protocol,
727                payload_len: payload_len,
728                hop_limit,
729            })),
730
731            #[cfg(feature = "proto-ipv6")]
732            &Repr::Unspecified {
733                src_addr: Address::Ipv6(src_addr),
734                dst_addr: Address::Ipv6(dst_addr),
735                protocol,
736                payload_len,
737                hop_limit,
738            } => Ok(Repr::Ipv6(Ipv6Repr {
739                src_addr: src_addr,
740                dst_addr: dst_addr,
741                next_header: protocol,
742                payload_len: payload_len,
743                hop_limit: hop_limit,
744            })),
745
746            #[cfg(feature = "proto-ipv4")]
747            &Repr::Ipv4(mut repr) => {
748                resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs)
749            }
750
751            #[cfg(feature = "proto-ipv6")]
752            &Repr::Ipv6(mut repr) => {
753                resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs)
754            }
755
756            &Repr::Unspecified { .. } => {
757                panic!("source and destination IP address families do not match")
758            }
759        }
760    }
761
762    /// Return the length of a header that will be emitted from this high-level representation.
763    ///
764    /// # Panics
765    /// This function panics if invoked on an unspecified representation.
766    pub fn buffer_len(&self) -> usize {
767        match *self {
768            Repr::Unspecified { .. } => panic!("unspecified IP representation"),
769            #[cfg(feature = "proto-ipv4")]
770            Repr::Ipv4(repr) => repr.buffer_len(),
771            #[cfg(feature = "proto-ipv6")]
772            Repr::Ipv6(repr) => repr.buffer_len(),
773        }
774    }
775
776    /// Emit this high-level representation into a buffer.
777    ///
778    /// # Panics
779    /// This function panics if invoked on an unspecified representation.
780    pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
781        &self,
782        buffer: T,
783        _checksum_caps: &ChecksumCapabilities,
784    ) {
785        match *self {
786            Repr::Unspecified { .. } => panic!("unspecified IP representation"),
787            #[cfg(feature = "proto-ipv4")]
788            Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
789            #[cfg(feature = "proto-ipv6")]
790            Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
791        }
792    }
793
794    /// Return the total length of a packet that will be emitted from this
795    /// high-level representation.
796    ///
797    /// This is the same as `repr.buffer_len() + repr.payload_len()`.
798    ///
799    /// # Panics
800    /// This function panics if invoked on an unspecified representation.
801    pub fn total_len(&self) -> usize {
802        self.buffer_len() + self.payload_len()
803    }
804}
805
806pub mod checksum {
807    use byteorder::{ByteOrder, NetworkEndian};
808
809    use super::*;
810
811    fn propagate_carries(word: u32) -> u16 {
812        let sum = (word >> 16) + (word & 0xffff);
813        ((sum >> 16) as u16) + (sum as u16)
814    }
815
816    /// Compute an RFC 1071 compliant checksum (without the final complement).
817    pub fn data(mut data: &[u8]) -> u16 {
818        let mut accum = 0;
819
820        // For each 32-byte chunk...
821        const CHUNK_SIZE: usize = 32;
822        while data.len() >= CHUNK_SIZE {
823            let mut d = &data[..CHUNK_SIZE];
824            // ... take by 2 bytes and sum them.
825            while d.len() >= 2 {
826                accum += NetworkEndian::read_u16(d) as u32;
827                d = &d[2..];
828            }
829
830            data = &data[CHUNK_SIZE..];
831        }
832
833        // Sum the rest that does not fit the last 32-byte chunk,
834        // taking by 2 bytes.
835        while data.len() >= 2 {
836            accum += NetworkEndian::read_u16(data) as u32;
837            data = &data[2..];
838        }
839
840        // Add the last remaining odd byte, if any.
841        if let Some(&value) = data.first() {
842            accum += (value as u32) << 8;
843        }
844
845        propagate_carries(accum)
846    }
847
848    /// Combine several RFC 1071 compliant checksums.
849    pub fn combine(checksums: &[u16]) -> u16 {
850        let mut accum: u32 = 0;
851        for &word in checksums {
852            accum += word as u32;
853        }
854        propagate_carries(accum)
855    }
856
857    /// Compute an IP pseudo header checksum.
858    pub fn pseudo_header(
859        src_addr: &Address,
860        dst_addr: &Address,
861        protocol: Protocol,
862        length: u32,
863    ) -> u16 {
864        match (src_addr, dst_addr) {
865            #[cfg(feature = "proto-ipv4")]
866            (&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
867                let mut proto_len = [0u8; 4];
868                proto_len[1] = protocol.into();
869                NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
870
871                combine(&[
872                    data(src_addr.as_bytes()),
873                    data(dst_addr.as_bytes()),
874                    data(&proto_len[..]),
875                ])
876            }
877
878            #[cfg(feature = "proto-ipv6")]
879            (&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => {
880                let mut proto_len = [0u8; 8];
881                proto_len[7] = protocol.into();
882                NetworkEndian::write_u32(&mut proto_len[0..4], length);
883                combine(&[
884                    data(src_addr.as_bytes()),
885                    data(dst_addr.as_bytes()),
886                    data(&proto_len[..]),
887                ])
888            }
889
890            _ => panic!(
891                "Unexpected pseudo header addresses: {}, {}",
892                src_addr, dst_addr
893            ),
894        }
895    }
896
897    // We use this in pretty printer implementations.
898    pub(crate) fn format_checksum(f: &mut fmt::Formatter, correct: bool) -> fmt::Result {
899        if !correct {
900            write!(f, " (checksum incorrect)")
901        } else {
902            Ok(())
903        }
904    }
905}
906
907use crate::wire::pretty_print::PrettyIndent;
908
909pub fn pretty_print_ip_payload<T: Into<Repr>>(
910    f: &mut fmt::Formatter,
911    indent: &mut PrettyIndent,
912    ip_repr: T,
913    payload: &[u8],
914) -> fmt::Result {
915    #[cfg(feature = "proto-ipv4")]
916    use super::pretty_print::PrettyPrint;
917    use crate::wire::ip::checksum::format_checksum;
918    #[cfg(feature = "proto-ipv4")]
919    use crate::wire::Icmpv4Packet;
920    use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
921
922    let checksum_caps = ChecksumCapabilities::ignored();
923    let repr = ip_repr.into();
924    match repr.protocol() {
925        #[cfg(feature = "proto-ipv4")]
926        Protocol::Icmp => {
927            indent.increase(f)?;
928            Icmpv4Packet::<&[u8]>::pretty_print(&payload, f, indent)
929        }
930        Protocol::Udp => {
931            indent.increase(f)?;
932            match UdpPacket::<&[u8]>::new_checked(payload) {
933                Err(err) => write!(f, "{}({})", indent, err),
934                Ok(udp_packet) => {
935                    match UdpRepr::parse(
936                        &udp_packet,
937                        &repr.src_addr(),
938                        &repr.dst_addr(),
939                        &checksum_caps,
940                    ) {
941                        Err(err) => write!(f, "{}{} ({})", indent, udp_packet, err),
942                        Ok(udp_repr) => {
943                            write!(
944                                f,
945                                "{}{} len={}",
946                                indent,
947                                udp_repr,
948                                udp_packet.payload().len()
949                            )?;
950                            let valid =
951                                udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
952                            format_checksum(f, valid)
953                        }
954                    }
955                }
956            }
957        }
958        Protocol::Tcp => {
959            indent.increase(f)?;
960            match TcpPacket::<&[u8]>::new_checked(payload) {
961                Err(err) => write!(f, "{}({})", indent, err),
962                Ok(tcp_packet) => {
963                    match TcpRepr::parse(
964                        &tcp_packet,
965                        &repr.src_addr(),
966                        &repr.dst_addr(),
967                        &checksum_caps,
968                    ) {
969                        Err(err) => write!(f, "{}{} ({})", indent, tcp_packet, err),
970                        Ok(tcp_repr) => {
971                            write!(f, "{}{}", indent, tcp_repr)?;
972                            let valid =
973                                tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
974                            format_checksum(f, valid)
975                        }
976                    }
977                }
978            }
979        }
980        _ => Ok(()),
981    }
982}
983
984#[cfg(test)]
985pub(crate) mod test {
986    #![allow(unused)]
987
988    #[cfg(feature = "proto-ipv6")]
989    pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([
990        0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
991    ]));
992    #[cfg(feature = "proto-ipv6")]
993    pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([
994        0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
995    ]));
996    #[cfg(feature = "proto-ipv6")]
997    pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([
998        0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
999    ]));
1000    #[cfg(feature = "proto-ipv6")]
1001    pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([
1002        0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
1003    ]));
1004    #[cfg(feature = "proto-ipv6")]
1005    pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED);
1006
1007    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1008    pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
1009    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1010    pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 2]));
1011    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1012    pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 3]));
1013    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1014    pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 4]));
1015    #[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
1016    pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED);
1017
1018    use super::*;
1019    use crate::wire::{IpAddress, IpCidr, IpProtocol};
1020    #[cfg(feature = "proto-ipv4")]
1021    use crate::wire::{Ipv4Address, Ipv4Repr};
1022
1023    macro_rules! generate_common_tests {
1024        ($name:ident, $repr:ident, $ip_repr:path, $ip_addr:path,
1025         $addr_from:path, $nxthdr:ident, $bytes_a:expr, $bytes_b:expr,
1026         $unspecified:expr) => {
1027            mod $name {
1028                use super::*;
1029
1030                #[test]
1031                fn test_ip_repr_lower() {
1032                    let ip_addr_a = $addr_from(&$bytes_a);
1033                    let ip_addr_b = $addr_from(&$bytes_b);
1034                    let proto = IpProtocol::Icmp;
1035                    let payload_len = 10;
1036
1037                    assert_eq!(
1038                        Repr::Unspecified {
1039                            src_addr: $ip_addr(ip_addr_a),
1040                            dst_addr: $ip_addr(ip_addr_b),
1041                            protocol: proto,
1042                            hop_limit: 0x2a,
1043                            payload_len,
1044                        }
1045                        .lower(&[]),
1046                        Ok($ip_repr($repr {
1047                            src_addr: ip_addr_a,
1048                            dst_addr: ip_addr_b,
1049                            $nxthdr: proto,
1050                            hop_limit: 0x2a,
1051                            payload_len
1052                        }))
1053                    );
1054
1055                    assert_eq!(
1056                        Repr::Unspecified {
1057                            src_addr: IpAddress::Unspecified,
1058                            dst_addr: $ip_addr(ip_addr_b),
1059                            protocol: proto,
1060                            hop_limit: 64,
1061                            payload_len
1062                        }
1063                        .lower(&[]),
1064                        Err(Error::Unaddressable)
1065                    );
1066
1067                    assert_eq!(
1068                        Repr::Unspecified {
1069                            src_addr: IpAddress::Unspecified,
1070                            dst_addr: $ip_addr(ip_addr_b),
1071                            protocol: proto,
1072                            hop_limit: 64,
1073                            payload_len
1074                        }
1075                        .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
1076                        Ok($ip_repr($repr {
1077                            src_addr: ip_addr_a,
1078                            dst_addr: ip_addr_b,
1079                            $nxthdr: proto,
1080                            hop_limit: 64,
1081                            payload_len
1082                        }))
1083                    );
1084
1085                    assert_eq!(
1086                        Repr::Unspecified {
1087                            src_addr: $ip_addr($unspecified),
1088                            dst_addr: $ip_addr(ip_addr_b),
1089                            protocol: proto,
1090                            hop_limit: 64,
1091                            payload_len
1092                        }
1093                        .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
1094                        Ok($ip_repr($repr {
1095                            src_addr: ip_addr_a,
1096                            dst_addr: ip_addr_b,
1097                            $nxthdr: proto,
1098                            hop_limit: 64,
1099                            payload_len
1100                        }))
1101                    );
1102
1103                    assert_eq!(
1104                        Repr::Unspecified {
1105                            src_addr: $ip_addr($unspecified),
1106                            dst_addr: $ip_addr(ip_addr_b),
1107                            protocol: proto,
1108                            hop_limit: 64,
1109                            payload_len
1110                        }
1111                        .lower(&[]),
1112                        Ok($ip_repr($repr {
1113                            src_addr: $unspecified,
1114                            dst_addr: ip_addr_b,
1115                            $nxthdr: proto,
1116                            hop_limit: 64,
1117                            payload_len
1118                        }))
1119                    );
1120
1121                    assert_eq!(
1122                        $ip_repr($repr {
1123                            src_addr: ip_addr_a,
1124                            dst_addr: ip_addr_b,
1125                            $nxthdr: proto,
1126                            hop_limit: 255,
1127                            payload_len
1128                        })
1129                        .lower(&[]),
1130                        Ok($ip_repr($repr {
1131                            src_addr: ip_addr_a,
1132                            dst_addr: ip_addr_b,
1133                            $nxthdr: proto,
1134                            hop_limit: 255,
1135                            payload_len
1136                        }))
1137                    );
1138
1139                    assert_eq!(
1140                        $ip_repr($repr {
1141                            src_addr: $unspecified,
1142                            dst_addr: ip_addr_b,
1143                            $nxthdr: proto,
1144                            hop_limit: 255,
1145                            payload_len
1146                        })
1147                        .lower(&[]),
1148                        Err(Error::Unaddressable)
1149                    );
1150
1151                    assert_eq!(
1152                        $ip_repr($repr {
1153                            src_addr: $unspecified,
1154                            dst_addr: ip_addr_b,
1155                            $nxthdr: proto,
1156                            hop_limit: 64,
1157                            payload_len
1158                        })
1159                        .lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
1160                        Ok($ip_repr($repr {
1161                            src_addr: ip_addr_a,
1162                            dst_addr: ip_addr_b,
1163                            $nxthdr: proto,
1164                            hop_limit: 64,
1165                            payload_len
1166                        }))
1167                    );
1168                }
1169            }
1170        };
1171        (ipv4 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
1172            generate_common_tests!(
1173                ipv4,
1174                Ipv4Repr,
1175                Repr::Ipv4,
1176                IpAddress::Ipv4,
1177                Ipv4Address::from_bytes,
1178                protocol,
1179                $addr_bytes_a,
1180                $addr_bytes_b,
1181                Ipv4Address::UNSPECIFIED
1182            );
1183        };
1184        (ipv6 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
1185            generate_common_tests!(
1186                ipv6,
1187                Ipv6Repr,
1188                Repr::Ipv6,
1189                IpAddress::Ipv6,
1190                Ipv6Address::from_bytes,
1191                next_header,
1192                $addr_bytes_a,
1193                $addr_bytes_b,
1194                Ipv6Address::UNSPECIFIED
1195            );
1196        };
1197    }
1198
1199    #[cfg(feature = "proto-ipv4")]
1200    generate_common_tests!(ipv4
1201                           [1, 2, 3, 4],
1202                           [5, 6, 7, 8]);
1203
1204    #[cfg(feature = "proto-ipv6")]
1205    generate_common_tests!(ipv6
1206                           [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1207                           [0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
1208
1209    #[test]
1210    #[cfg(all(feature = "proto-ipv4", feature = "proto-ipv6"))]
1211    #[should_panic(expected = "source and destination IP address families do not match")]
1212    fn test_lower_between_families() {
1213        Repr::Unspecified {
1214            src_addr: Address::Ipv6(Ipv6Address::UNSPECIFIED),
1215            dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED),
1216            protocol: IpProtocol::Icmpv6,
1217            hop_limit: 0xff,
1218            payload_len: 0,
1219        }
1220        .lower(&[]);
1221    }
1222
1223    #[test]
1224    fn endpoint_unspecified() {
1225        assert!(!Endpoint::UNSPECIFIED.is_specified());
1226    }
1227
1228    #[test]
1229    #[cfg(feature = "proto-ipv4")]
1230    fn to_prefix_len_ipv4() {
1231        fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1232            assert_eq!(Some(prefix_len), mask.into().prefix_len());
1233        }
1234
1235        test_eq(0, Ipv4Address::new(0, 0, 0, 0));
1236        test_eq(1, Ipv4Address::new(128, 0, 0, 0));
1237        test_eq(2, Ipv4Address::new(192, 0, 0, 0));
1238        test_eq(3, Ipv4Address::new(224, 0, 0, 0));
1239        test_eq(4, Ipv4Address::new(240, 0, 0, 0));
1240        test_eq(5, Ipv4Address::new(248, 0, 0, 0));
1241        test_eq(6, Ipv4Address::new(252, 0, 0, 0));
1242        test_eq(7, Ipv4Address::new(254, 0, 0, 0));
1243        test_eq(8, Ipv4Address::new(255, 0, 0, 0));
1244        test_eq(9, Ipv4Address::new(255, 128, 0, 0));
1245        test_eq(10, Ipv4Address::new(255, 192, 0, 0));
1246        test_eq(11, Ipv4Address::new(255, 224, 0, 0));
1247        test_eq(12, Ipv4Address::new(255, 240, 0, 0));
1248        test_eq(13, Ipv4Address::new(255, 248, 0, 0));
1249        test_eq(14, Ipv4Address::new(255, 252, 0, 0));
1250        test_eq(15, Ipv4Address::new(255, 254, 0, 0));
1251        test_eq(16, Ipv4Address::new(255, 255, 0, 0));
1252        test_eq(17, Ipv4Address::new(255, 255, 128, 0));
1253        test_eq(18, Ipv4Address::new(255, 255, 192, 0));
1254        test_eq(19, Ipv4Address::new(255, 255, 224, 0));
1255        test_eq(20, Ipv4Address::new(255, 255, 240, 0));
1256        test_eq(21, Ipv4Address::new(255, 255, 248, 0));
1257        test_eq(22, Ipv4Address::new(255, 255, 252, 0));
1258        test_eq(23, Ipv4Address::new(255, 255, 254, 0));
1259        test_eq(24, Ipv4Address::new(255, 255, 255, 0));
1260        test_eq(25, Ipv4Address::new(255, 255, 255, 128));
1261        test_eq(26, Ipv4Address::new(255, 255, 255, 192));
1262        test_eq(27, Ipv4Address::new(255, 255, 255, 224));
1263        test_eq(28, Ipv4Address::new(255, 255, 255, 240));
1264        test_eq(29, Ipv4Address::new(255, 255, 255, 248));
1265        test_eq(30, Ipv4Address::new(255, 255, 255, 252));
1266        test_eq(31, Ipv4Address::new(255, 255, 255, 254));
1267        test_eq(32, Ipv4Address::new(255, 255, 255, 255));
1268    }
1269
1270    #[cfg(feature = "proto-ipv4")]
1271    fn to_prefix_len_ipv4_error() {
1272        assert_eq!(
1273            None,
1274            IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).prefix_len()
1275        );
1276    }
1277
1278    #[test]
1279    #[cfg(feature = "proto-ipv6")]
1280    fn to_prefix_len_ipv6() {
1281        fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
1282            assert_eq!(Some(prefix_len), mask.into().prefix_len());
1283        }
1284
1285        test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
1286        test_eq(
1287            128,
1288            Ipv6Address::new(
1289                0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
1290            ),
1291        );
1292    }
1293
1294    #[cfg(feature = "proto-ipv6")]
1295    fn to_prefix_len_ipv6_error() {
1296        assert_eq!(
1297            None,
1298            IpAddress::from(Ipv6Address::new(
1299                0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
1300            ))
1301            .prefix_len()
1302        );
1303    }
1304}