Skip to main content

net_io/
addr.rs

1//! Addresses: normalization, CIDR blocks, and prefix keys.
2//!
3//! Nothing here touches the OS; it is written against `core` types so it can
4//! move to a `no_std` crate later without changes.
5
6use std::fmt;
7use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4};
8use std::str::FromStr;
9
10/// Turns an IPv4-mapped IPv6 address (`::ffff:a.b.c.d`) into the IPv4 address.
11///
12/// Nothing else is rewritten: scope ids are kept, and NAT64 (`64:ff9b::/96`)
13/// is not translated. net-io normalizes every peer at accept, so caps, gates,
14/// and logs agree on one form.
15///
16/// # Examples
17///
18/// ```
19/// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
20/// let mapped = IpAddr::V6(Ipv4Addr::new(192, 0, 2, 7).to_ipv6_mapped());
21/// assert_eq!(net_io::normalize(mapped), IpAddr::V4(Ipv4Addr::new(192, 0, 2, 7)));
22/// assert_eq!(net_io::normalize(IpAddr::V6(Ipv6Addr::LOCALHOST)), IpAddr::V6(Ipv6Addr::LOCALHOST));
23/// ```
24#[inline]
25#[must_use]
26pub fn normalize(ip: IpAddr) -> IpAddr {
27    match ip {
28        IpAddr::V6(v6) => match v6.to_ipv4_mapped() {
29            Some(v4) => IpAddr::V4(v4),
30            None => IpAddr::V6(v6),
31        },
32        v4 => v4,
33    }
34}
35
36/// [`normalize`] for a socket address; the port is kept.
37///
38/// # Examples
39///
40/// ```
41/// let addr: std::net::SocketAddr = "[::ffff:10.0.0.1]:80".parse().unwrap();
42/// assert_eq!(net_io::normalize_socket(addr).to_string(), "10.0.0.1:80");
43/// ```
44#[inline]
45#[must_use]
46pub fn normalize_socket(addr: SocketAddr) -> SocketAddr {
47    match addr {
48        SocketAddr::V6(v6) => match v6.ip().to_ipv4_mapped() {
49            Some(v4) => SocketAddr::V4(SocketAddrV4::new(v4, v6.port())),
50            None => SocketAddr::V6(v6),
51        },
52        v4 => v4,
53    }
54}
55
56/// An error parsing or building an address value.
57///
58/// # Examples
59///
60/// ```
61/// use net_io::{AddrError, Cidr};
62/// assert_eq!(Cidr::parse("10.0.0.1/8"), Err(AddrError::HostBitsSet));
63/// ```
64#[non_exhaustive]
65#[derive(Clone, Copy, Debug, PartialEq, Eq)]
66pub enum AddrError {
67    /// The text is not an address or block; `at` is the byte offset where it went wrong.
68    Syntax {
69        /// Byte offset of the problem.
70        at: usize,
71    },
72    /// The prefix length is above the family's maximum.
73    PrefixTooLong {
74        /// 32 or 128.
75        max: u8,
76    },
77    /// Bits beyond the prefix are set; use the masked constructor or fix the text.
78    HostBitsSet,
79    /// More entries than allowed.
80    TooMany {
81        /// The maximum.
82        max: usize,
83    },
84    /// The text is empty.
85    Empty,
86}
87
88impl fmt::Display for AddrError {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        match self {
91            AddrError::Syntax { at } => write!(f, "not an address or CIDR block (at byte {at})"),
92            AddrError::PrefixTooLong { max } => write!(f, "prefix length above {max}"),
93            AddrError::HostBitsSet => f.write_str(
94                "bits beyond the prefix are set; write the network address or use new_masked",
95            ),
96            AddrError::TooMany { max } => write!(f, "more than {max} entries"),
97            AddrError::Empty => f.write_str("empty address text"),
98        }
99    }
100}
101
102impl std::error::Error for AddrError {}
103
104/// Network bits of `ip` at `prefix`, as 16 big-endian bytes (IPv4 in the first 4).
105fn masked_bytes(ip: IpAddr, prefix: u8) -> [u8; 16] {
106    let mut out = [0u8; 16];
107    let (bytes, total): ([u8; 16], u8) = match ip {
108        IpAddr::V4(v4) => {
109            let mut b = [0u8; 16];
110            b[..4].copy_from_slice(&v4.octets());
111            (b, 32)
112        }
113        IpAddr::V6(v6) => (v6.octets(), 128),
114    };
115    let prefix = prefix.min(total);
116    let full = usize::from(prefix / 8);
117    out[..full].copy_from_slice(&bytes[..full]);
118    let rem = prefix % 8;
119    if rem != 0 && full < 16 {
120        out[full] = bytes[full] & (0xFFu8 << (8 - rem));
121    }
122    out
123}
124
125fn ip_from_bytes(v4: bool, bytes: &[u8; 16]) -> IpAddr {
126    if v4 {
127        IpAddr::V4(Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]))
128    } else {
129        IpAddr::V6(Ipv6Addr::from(*bytes))
130    }
131}
132
133fn max_prefix(ip: IpAddr) -> u8 {
134    if ip.is_ipv4() { 32 } else { 128 }
135}
136
137/// A network block: a normalized network address and a prefix length.
138///
139/// # Examples
140///
141/// ```
142/// use net_io::Cidr;
143/// let office: Cidr = "203.0.113.0/24".parse()?;
144/// assert!(office.contains("203.0.113.9".parse().unwrap()));
145/// assert!(!office.contains("198.51.100.1".parse().unwrap()));
146/// assert_eq!(office.to_string(), "203.0.113.0/24");
147/// assert!(Cidr::parse("10.0.0.1/8").is_err());
148/// assert_eq!(Cidr::new_masked("10.0.0.1".parse().unwrap(), 8)?.to_string(), "10.0.0.0/8");
149/// # Ok::<(), net_io::AddrError>(())
150/// ```
151#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
152pub struct Cidr {
153    network: IpAddr,
154    prefix: u8,
155}
156
157impl Cidr {
158    /// A block whose host bits must be zero.
159    ///
160    /// # Errors
161    ///
162    /// [`AddrError::PrefixTooLong`], [`AddrError::HostBitsSet`].
163    ///
164    /// ```
165    /// let block = net_io::Cidr::new("10.0.0.0".parse().unwrap(), 8)?;
166    /// assert_eq!(block.prefix_len(), 8);
167    /// # Ok::<(), net_io::AddrError>(())
168    /// ```
169    pub fn new(ip: IpAddr, prefix: u8) -> Result<Cidr, AddrError> {
170        let ip = normalize(ip);
171        let max = max_prefix(ip);
172        if prefix > max {
173            return Err(AddrError::PrefixTooLong { max });
174        }
175        let masked = masked_bytes(ip, prefix);
176        let network = ip_from_bytes(ip.is_ipv4(), &masked);
177        if network != ip {
178            return Err(AddrError::HostBitsSet);
179        }
180        Ok(Cidr { network, prefix })
181    }
182
183    /// A block that clears host bits instead of refusing them.
184    ///
185    /// # Errors
186    ///
187    /// [`AddrError::PrefixTooLong`].
188    ///
189    /// ```
190    /// let block = net_io::Cidr::new_masked("192.0.2.77".parse().unwrap(), 24)?;
191    /// assert_eq!(block.to_string(), "192.0.2.0/24");
192    /// # Ok::<(), net_io::AddrError>(())
193    /// ```
194    pub fn new_masked(ip: IpAddr, prefix: u8) -> Result<Cidr, AddrError> {
195        let ip = normalize(ip);
196        let max = max_prefix(ip);
197        if prefix > max {
198            return Err(AddrError::PrefixTooLong { max });
199        }
200        let masked = masked_bytes(ip, prefix);
201        Ok(Cidr {
202            network: ip_from_bytes(ip.is_ipv4(), &masked),
203            prefix,
204        })
205    }
206
207    /// Parses `"10.0.0.0/8"`, `"2001:db8::/32"`, or a bare address (`/32` or `/128`).
208    ///
209    /// Canonical text only: no leading zeros in IPv4 octets, no zone ids, no whitespace.
210    ///
211    /// # Errors
212    ///
213    /// [`AddrError::Empty`], [`AddrError::Syntax`], [`AddrError::PrefixTooLong`],
214    /// [`AddrError::HostBitsSet`].
215    ///
216    /// ```
217    /// let block = net_io::Cidr::parse("::1")?;
218    /// assert_eq!(block.prefix_len(), 128);
219    /// # Ok::<(), net_io::AddrError>(())
220    /// ```
221    pub fn parse(text: &str) -> Result<Cidr, AddrError> {
222        if text.is_empty() {
223            return Err(AddrError::Empty);
224        }
225        let (addr_text, prefix_text) = match text.find('/') {
226            Some(slash) => (&text[..slash], Some((slash, &text[slash + 1..]))),
227            None => (text, None),
228        };
229        let ip = IpAddr::from_str(addr_text).map_err(|_| AddrError::Syntax { at: 0 })?;
230        let prefix = match prefix_text {
231            None => max_prefix(normalize(ip)),
232            Some((slash, p)) => parse_prefix(p).ok_or(AddrError::Syntax { at: slash + 1 })?,
233        };
234        // A mapped IPv6 block keeps its meaning only if the prefix covers the ::ffff:0:0/96 part.
235        let (ip, prefix) = match ip {
236            IpAddr::V6(v6) if v6.to_ipv4_mapped().is_some() && prefix_text.is_some() => {
237                if prefix < 96 {
238                    return Err(AddrError::Syntax { at: 0 });
239                }
240                (ip, prefix - 96)
241            }
242            _ => (ip, prefix),
243        };
244        Cidr::new(ip, prefix)
245    }
246
247    /// Whether the block contains `ip` (normalized first).
248    ///
249    /// ```
250    /// let block = net_io::Cidr::parse("10.0.0.0/8")?;
251    /// assert!(block.contains("::ffff:10.1.2.3".parse().unwrap()));
252    /// # Ok::<(), net_io::AddrError>(())
253    /// ```
254    #[inline]
255    #[must_use]
256    pub fn contains(&self, ip: IpAddr) -> bool {
257        let ip = normalize(ip);
258        if ip.is_ipv4() != self.network.is_ipv4() {
259            return false;
260        }
261        ip_from_bytes(ip.is_ipv4(), &masked_bytes(ip, self.prefix)) == self.network
262    }
263
264    /// The network address.
265    ///
266    /// ```
267    /// let block = net_io::Cidr::parse("10.0.0.0/8")?;
268    /// assert_eq!(block.network().to_string(), "10.0.0.0");
269    /// # Ok::<(), net_io::AddrError>(())
270    /// ```
271    #[must_use]
272    pub fn network(&self) -> IpAddr {
273        self.network
274    }
275
276    /// The prefix length.
277    ///
278    /// ```
279    /// assert_eq!(net_io::Cidr::parse("10.0.0.0/8")?.prefix_len(), 8);
280    /// # Ok::<(), net_io::AddrError>(())
281    /// ```
282    #[must_use]
283    pub fn prefix_len(&self) -> u8 {
284        self.prefix
285    }
286
287    /// Whether this is an IPv4 block.
288    ///
289    /// ```
290    /// assert!(net_io::Cidr::parse("10.0.0.0/8")?.is_ipv4());
291    /// # Ok::<(), net_io::AddrError>(())
292    /// ```
293    #[must_use]
294    pub fn is_ipv4(&self) -> bool {
295        self.network.is_ipv4()
296    }
297}
298
299fn parse_prefix(text: &str) -> Option<u8> {
300    if text.is_empty() || text.len() > 3 || (text.len() > 1 && text.starts_with('0')) {
301        return None;
302    }
303    let mut value: u16 = 0;
304    for b in text.bytes() {
305        if !b.is_ascii_digit() {
306            return None;
307        }
308        value = value * 10 + u16::from(b - b'0');
309    }
310    u8::try_from(value).ok()
311}
312
313impl FromStr for Cidr {
314    type Err = AddrError;
315    fn from_str(s: &str) -> Result<Cidr, AddrError> {
316        Cidr::parse(s)
317    }
318}
319
320impl fmt::Display for Cidr {
321    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322        write!(f, "{}/{}", self.network, self.prefix)
323    }
324}
325
326/// The prefix lengths used to group addresses.
327///
328/// IPv6 hosts usually own a whole /64, so the default groups IPv6 at /64 and
329/// IPv4 at /32.
330///
331/// # Examples
332///
333/// ```
334/// use net_io::PrefixPolicy;
335/// assert_eq!(PrefixPolicy::DEFAULT.v6(), 64);
336/// assert!(PrefixPolicy::new(33, 64).is_err());
337/// ```
338#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
339pub struct PrefixPolicy {
340    v4: u8,
341    v6: u8,
342}
343
344impl PrefixPolicy {
345    /// Every address on its own: /32 and /128.
346    pub const HOST: PrefixPolicy = PrefixPolicy { v4: 32, v6: 128 };
347    /// /32 for IPv4 and /64 for IPv6.
348    pub const DEFAULT: PrefixPolicy = PrefixPolicy { v4: 32, v6: 64 };
349
350    /// A policy with the given lengths.
351    ///
352    /// # Errors
353    ///
354    /// [`AddrError::PrefixTooLong`] above 32 or 128.
355    ///
356    /// ```
357    /// let wide = net_io::PrefixPolicy::new(24, 48)?;
358    /// assert_eq!(wide.v4(), 24);
359    /// # Ok::<(), net_io::AddrError>(())
360    /// ```
361    pub const fn new(v4: u8, v6: u8) -> Result<PrefixPolicy, AddrError> {
362        if v4 > 32 {
363            return Err(AddrError::PrefixTooLong { max: 32 });
364        }
365        if v6 > 128 {
366            return Err(AddrError::PrefixTooLong { max: 128 });
367        }
368        Ok(PrefixPolicy { v4, v6 })
369    }
370
371    /// The IPv4 prefix length.
372    #[must_use]
373    pub const fn v4(&self) -> u8 {
374        self.v4
375    }
376
377    /// The IPv6 prefix length.
378    #[must_use]
379    pub const fn v6(&self) -> u8 {
380        self.v6
381    }
382}
383
384impl Default for PrefixPolicy {
385    fn default() -> PrefixPolicy {
386        PrefixPolicy::DEFAULT
387    }
388}
389
390/// "This address at this prefix length" as a fixed-size `Copy` hash key.
391///
392/// # Examples
393///
394/// ```
395/// use net_io::{PrefixKey, PrefixPolicy};
396/// let a = PrefixKey::of("2001:db8:1:2::10".parse().unwrap(), PrefixPolicy::DEFAULT);
397/// let b = PrefixKey::of("2001:db8:1:2:ffff::1".parse().unwrap(), PrefixPolicy::DEFAULT);
398/// assert_eq!(a, b);
399/// let wide = PrefixPolicy::new(24, 48)?;
400/// assert_eq!(PrefixKey::of("192.0.2.200".parse().unwrap(), wide).to_string(), "192.0.2.0/24");
401/// # Ok::<(), net_io::AddrError>(())
402/// ```
403#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
404pub struct PrefixKey {
405    family: u8,
406    bytes: [u8; 16],
407    len: u8,
408}
409
410impl PrefixKey {
411    /// The key of `ip` under `policy` (normalized first; scope ids ignored).
412    #[inline]
413    #[must_use]
414    pub fn of(ip: IpAddr, policy: PrefixPolicy) -> PrefixKey {
415        let ip = normalize(ip);
416        let len = if ip.is_ipv4() { policy.v4 } else { policy.v6 };
417        PrefixKey {
418            family: if ip.is_ipv4() { 4 } else { 6 },
419            bytes: masked_bytes(ip, len),
420            len,
421        }
422    }
423
424    /// The key of the single address (/32 or /128).
425    ///
426    /// ```
427    /// let key = net_io::PrefixKey::exact("10.0.0.1".parse().unwrap());
428    /// assert_eq!(key.prefix_len(), 32);
429    /// ```
430    #[must_use]
431    pub fn exact(ip: IpAddr) -> PrefixKey {
432        PrefixKey::of(ip, PrefixPolicy::HOST)
433    }
434
435    /// The network address of the key.
436    ///
437    /// ```
438    /// let key = net_io::PrefixKey::of("10.1.2.3".parse().unwrap(), net_io::PrefixPolicy::new(8, 64)?);
439    /// assert_eq!(key.network().to_string(), "10.0.0.0");
440    /// # Ok::<(), net_io::AddrError>(())
441    /// ```
442    #[must_use]
443    pub fn network(&self) -> IpAddr {
444        ip_from_bytes(self.family == 4, &self.bytes)
445    }
446
447    /// The prefix length of the key.
448    #[must_use]
449    pub fn prefix_len(&self) -> u8 {
450        self.len
451    }
452}
453
454impl fmt::Display for PrefixKey {
455    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456        write!(f, "{}/{}", self.network(), self.len)
457    }
458}
459
460#[cfg(test)]
461mod tests {
462    use super::*;
463
464    fn ip(s: &str) -> IpAddr {
465        s.parse().unwrap()
466    }
467
468    #[test]
469    fn test_normalize_mapped_v6_becomes_v4() {
470        assert_eq!(normalize(ip("::ffff:1.2.3.4")), ip("1.2.3.4"));
471    }
472
473    #[test]
474    fn test_normalize_nat64_untouched() {
475        assert_eq!(normalize(ip("64:ff9b::102:304")), ip("64:ff9b::102:304"));
476    }
477
478    #[test]
479    fn test_normalize_socket_keeps_port_and_scope() {
480        let a: SocketAddr = "[fe80::1%3]:80".parse().unwrap();
481        assert_eq!(normalize_socket(a), a);
482    }
483
484    #[test]
485    fn test_cidr_parse_valid_forms() {
486        assert_eq!(Cidr::parse("10.0.0.0/8").unwrap().to_string(), "10.0.0.0/8");
487        assert_eq!(
488            Cidr::parse("2001:db8::/32").unwrap().to_string(),
489            "2001:db8::/32"
490        );
491        assert_eq!(Cidr::parse("192.0.2.7").unwrap().prefix_len(), 32);
492        assert_eq!(Cidr::parse("0.0.0.0/0").unwrap().prefix_len(), 0);
493    }
494
495    #[test]
496    fn test_cidr_parse_rejects_bad_text() {
497        assert_eq!(Cidr::parse(""), Err(AddrError::Empty));
498        assert!(matches!(
499            Cidr::parse("10.0.0.0/"),
500            Err(AddrError::Syntax { .. })
501        ));
502        assert!(matches!(
503            Cidr::parse("10.0.0.0/08"),
504            Err(AddrError::Syntax { .. })
505        ));
506        assert!(matches!(
507            Cidr::parse("10.0.0.0/+8"),
508            Err(AddrError::Syntax { .. })
509        ));
510        assert!(matches!(
511            Cidr::parse("010.0.0.0/8"),
512            Err(AddrError::Syntax { .. })
513        ));
514        assert!(matches!(
515            Cidr::parse(" 10.0.0.0/8"),
516            Err(AddrError::Syntax { .. })
517        ));
518        assert!(matches!(
519            Cidr::parse("fe80::1%3/64"),
520            Err(AddrError::Syntax { .. })
521        ));
522        assert_eq!(
523            Cidr::parse("10.0.0.0/33"),
524            Err(AddrError::PrefixTooLong { max: 32 })
525        );
526        assert_eq!(
527            Cidr::parse("::/129"),
528            Err(AddrError::PrefixTooLong { max: 128 })
529        );
530        assert_eq!(Cidr::parse("10.0.0.1/8"), Err(AddrError::HostBitsSet));
531    }
532
533    #[test]
534    fn test_cidr_mapped_block_normalizes_to_v4() {
535        let c = Cidr::parse("::ffff:10.0.0.0/104").unwrap();
536        assert_eq!(c.to_string(), "10.0.0.0/8");
537        assert!(matches!(
538            Cidr::parse("::ffff:10.0.0.0/64"),
539            Err(AddrError::Syntax { .. })
540        ));
541    }
542
543    #[test]
544    fn test_cidr_contains_boundaries() {
545        let c = Cidr::parse("192.0.2.0/31").unwrap();
546        assert!(c.contains(ip("192.0.2.0")));
547        assert!(c.contains(ip("192.0.2.1")));
548        assert!(!c.contains(ip("192.0.2.2")));
549        assert!(!c.contains(ip("::1")));
550        let all = Cidr::parse("::/0").unwrap();
551        assert!(all.contains(ip("2001:db8::1")));
552        assert!(!all.contains(ip("1.1.1.1")));
553    }
554
555    #[test]
556    fn test_cidr_new_masked_clears_bits() {
557        let c = Cidr::new_masked(ip("2001:db8:ffff::1"), 33).unwrap();
558        assert_eq!(c.to_string(), "2001:db8:8000::/33");
559    }
560
561    #[test]
562    fn test_prefix_key_groups_by_policy() {
563        let p = PrefixPolicy::new(24, 56).unwrap();
564        assert_eq!(
565            PrefixKey::of(ip("1.2.3.4"), p),
566            PrefixKey::of(ip("1.2.3.200"), p)
567        );
568        assert_ne!(
569            PrefixKey::of(ip("1.2.3.4"), p),
570            PrefixKey::of(ip("1.2.4.4"), p)
571        );
572        assert_eq!(
573            PrefixKey::of(ip("2001:db8:0:ff12::1"), p).to_string(),
574            "2001:db8:0:ff00::/56"
575        );
576    }
577
578    #[test]
579    fn test_prefix_key_v4_and_v6_never_equal() {
580        let p = PrefixPolicy::new(0, 0).unwrap();
581        assert_ne!(PrefixKey::of(ip("0.0.0.0"), p), PrefixKey::of(ip("::"), p));
582    }
583
584    #[test]
585    fn test_prefix_key_mapped_equals_v4() {
586        assert_eq!(
587            PrefixKey::exact(ip("::ffff:9.9.9.9")),
588            PrefixKey::exact(ip("9.9.9.9"))
589        );
590    }
591
592    #[test]
593    fn test_prefix_key_size_is_eighteen_bytes() {
594        assert_eq!(std::mem::size_of::<PrefixKey>(), 18);
595    }
596
597    #[test]
598    fn test_policy_bounds() {
599        assert!(PrefixPolicy::new(32, 128).is_ok());
600        assert_eq!(
601            PrefixPolicy::new(0, 129),
602            Err(AddrError::PrefixTooLong { max: 128 })
603        );
604    }
605
606    /// Property: `Cidr::contains` and `PrefixKey` grouping agree with integer masks.
607    #[test]
608    fn prop_cidr_and_prefix_key_match_integer_masks() {
609        use crate::testing::Gen;
610        let mut g = Gen::new(0xADD2_0001);
611        for _ in 0..if cfg!(miri) { 200 } else { 20_000 } {
612            if g.chance(2) {
613                let net = g.next_u64() as u32;
614                let p = g.below(33) as u32;
615                let mask = if p == 0 { 0 } else { u32::MAX << (32 - p) };
616                // Half the probes share the prefix, so both answers are exercised.
617                let ip = if g.chance(2) {
618                    (net & mask) | (g.next_u64() as u32 & !mask)
619                } else {
620                    g.next_u64() as u32
621                };
622                let (net_ip, probe) = (
623                    IpAddr::from(std::net::Ipv4Addr::from(net)),
624                    IpAddr::from(std::net::Ipv4Addr::from(ip)),
625                );
626                let block = Cidr::new_masked(net_ip, p as u8).unwrap();
627                let inside = ip & mask == net & mask;
628                assert_eq!(block.contains(probe), inside, "{block} {probe}");
629                let policy = PrefixPolicy::new(p as u8, 64).unwrap();
630                assert_eq!(
631                    PrefixKey::of(probe, policy) == PrefixKey::of(net_ip, policy),
632                    inside
633                );
634            } else {
635                let net = (u128::from(g.next_u64()) << 64) | u128::from(g.next_u64());
636                // Keep clear of the IPv4-mapped block, which normalizes to IPv4.
637                let net = net | (1u128 << 127);
638                let p = g.below(129) as u32;
639                let mask = if p == 0 { 0 } else { u128::MAX << (128 - p) };
640                let rand =
641                    (u128::from(g.next_u64()) << 64) | u128::from(g.next_u64()) | (1u128 << 127);
642                let ip = if g.chance(2) {
643                    (net & mask) | (rand & !mask)
644                } else {
645                    rand
646                };
647                let (net_ip, probe) = (
648                    IpAddr::from(std::net::Ipv6Addr::from(net)),
649                    IpAddr::from(std::net::Ipv6Addr::from(ip)),
650                );
651                let block = Cidr::new_masked(net_ip, p as u8).unwrap();
652                let inside = ip & mask == net & mask;
653                assert_eq!(block.contains(probe), inside, "{block} {probe}");
654                let policy = PrefixPolicy::new(32, p as u8).unwrap();
655                assert_eq!(
656                    PrefixKey::of(probe, policy) == PrefixKey::of(net_ip, policy),
657                    inside
658                );
659            }
660        }
661    }
662}