ip/concrete/addr/
mod.rs

1use core::borrow::BorrowMut;
2use core::fmt;
3use core::str::FromStr;
4
5use super::{impl_try_from_any, AddressRange, PrefixLength};
6use crate::{
7    any, concrete,
8    error::{err, Error, Kind},
9    fmt::AddressDisplay,
10    traits::{
11        self,
12        primitive::{Address as _, Octets as _},
13        Afi,
14    },
15    Ipv4, Ipv6,
16};
17
18mod private;
19pub use self::private::Address;
20
21mod convert;
22mod ops;
23
24mod ipv4;
25mod ipv6;
26pub use self::ipv6::MulticastScope as Ipv6MulticastScope;
27
28mod range;
29pub use self::range::Range;
30
31impl<A: Afi> Address<A> {
32    /// The `localhost` address for address family `A`.
33    pub const LOCALHOST: Self = Self::new(A::Primitive::LOCALHOST);
34
35    /// The "unspecified" address for address family `A`.
36    pub const UNSPECIFIED: Self = Self::new(A::Primitive::UNSPECIFIED);
37
38    /// The "all-zeros" address for address family `A`.
39    pub const ZEROS: Self = Self::new(A::Primitive::ZERO);
40
41    /// Construct a new [`Address<A>`] from a big-endian byte-array.
42    ///
43    /// # Examples
44    ///
45    /// ``` rust
46    /// use ip::{Address, Ipv4, Ipv6};
47    ///
48    /// assert_eq!(
49    ///     Address::<Ipv4>::from_octets([10, 0, 0, 1]),
50    ///     "10.0.0.1".parse::<Address<Ipv4>>()?,
51    /// );
52    ///
53    /// assert_eq!(
54    ///     Address::<Ipv6>::from_octets([0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,]),
55    ///     "2001:db8::1".parse::<Address<Ipv6>>()?,
56    /// );
57    /// # Ok::<(), ip::Error>(())
58    /// ```
59    pub fn from_octets(octets: A::Octets) -> Self {
60        Self::new(A::Primitive::from_be_bytes(octets))
61    }
62
63    /// Try to construct a new [`Address<A>`] from a big-endian slice of octets.
64    ///
65    /// If `slice` is shorter than [`A::Octets`][Afi::Octets] then the missing
66    /// low-order octets will be zero-filled.
67    ///
68    /// # Errors
69    ///
70    /// An error of kind [`OctetSliceOverrun`][Kind::OctetSliceOverrun] will be
71    /// returned if the length of slice is greater than the length of
72    /// [`A::Octets`][Afi::Octets].
73    ///
74    /// # Examples
75    ///
76    /// ``` rust
77    /// use ip::{Address, Ipv4, Ipv6};
78    ///
79    /// assert_eq!(
80    ///     Address::<Ipv6>::from_slice(&[0x20, 0x01, 0x0d, 0xb8])?,
81    ///     "2001:db8::".parse::<Address<Ipv6>>()?,
82    /// );
83    ///
84    /// assert!(matches!(
85    ///     Address::<Ipv4>::from_slice(&[1, 2, 3, 4, 5]),
86    ///     Err(_),
87    /// ));
88    /// # Ok::<(), ip::Error>(())
89    /// ```
90    pub fn from_slice(slice: &[u8]) -> Result<Self, Error> {
91        let mut octets = A::Octets::ZEROS;
92        let len = slice.len();
93        if len > A::Octets::LENGTH {
94            return Err(err!(Kind::OctetSliceOverrun));
95        }
96        octets.borrow_mut()[..len].copy_from_slice(slice);
97        Ok(Self::from_octets(octets))
98    }
99
100    /// Returns a big-endian byte-array representing the value of `self`.
101    ///
102    /// # Examples
103    ///
104    /// ``` rust
105    /// use ip::{Address, Ipv4, Ipv6};
106    ///
107    /// assert_eq!("10.0.0.1".parse::<Address<Ipv4>>()?.octets(), [10, 0, 0, 1],);
108    ///
109    /// assert_eq!(
110    ///     "2001:db8::1".parse::<Address<Ipv6>>()?.octets(),
111    ///     [0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01],
112    /// );
113    /// # Ok::<(), ip::Error>(())
114    /// ```
115    pub fn octets(&self) -> A::Octets {
116        self.into_primitive().to_be_bytes()
117    }
118
119    #[allow(clippy::missing_panics_doc)]
120    /// Compute the common length of `self` and another [`Address<A>`].
121    ///
122    /// See also [`common_length()`].
123    pub fn common_length(self, other: Self) -> PrefixLength<A> {
124        // ok to unwrap here as long as primitive width invariants hold
125        PrefixLength::<A>::from_primitive((self ^ other).leading_zeros()).unwrap()
126    }
127}
128/// Compute the length, as a [`PrefixLength<A>`], for the common prefixes of
129/// two [`Address<A>`].
130///
131/// # Examples
132///
133/// ``` rust
134/// use ip::{concrete::common_length, Address, Ipv4, Ipv6, PrefixLength};
135///
136/// assert_eq!(
137///     common_length(
138///         "192.168.1.255".parse::<Address<Ipv4>>()?,
139///         "192.168.128.1".parse::<Address<Ipv4>>()?,
140///     ),
141///     PrefixLength::<Ipv4>::from_primitive(16)?,
142/// );
143///
144/// assert_eq!(
145///     common_length(
146///         "2001:db8:dead::1".parse::<Address<Ipv6>>()?,
147///         "2001:db8:beef::1".parse::<Address<Ipv6>>()?,
148///     ),
149///     PrefixLength::<Ipv6>::from_primitive(33)?,
150/// );
151/// # Ok::<(), ip::Error>(())
152/// ```
153pub fn common_length<A: Afi>(lhs: Address<A>, rhs: Address<A>) -> PrefixLength<A> {
154    lhs.common_length(rhs)
155}
156
157impl<A: Afi> traits::Address for Address<A> {
158    fn afi(&self) -> concrete::Afi {
159        A::as_afi()
160    }
161
162    #[allow(clippy::option_if_let_else)]
163    fn is_broadcast(&self) -> bool {
164        if let Some(broadcast) = A::Primitive::BROADCAST {
165            self.into_primitive() == broadcast
166        } else {
167            false
168        }
169    }
170
171    fn is_link_local(&self) -> bool {
172        AddressRange::from(&A::Primitive::LINK_LOCAL_RANGE).contains(self)
173    }
174
175    #[allow(clippy::option_if_let_else)]
176    fn is_private(&self) -> bool {
177        if let Some(ranges) = A::Primitive::PRIVATE_RANGES {
178            ranges
179                .iter()
180                .any(|range| AddressRange::from(range).contains(self))
181        } else {
182            false
183        }
184    }
185
186    #[allow(clippy::option_if_let_else)]
187    fn is_reserved(&self) -> bool {
188        if let Some(range) = A::Primitive::RESERVED_RANGE {
189            // TODO: this should compare to `Self::BROADCAST`, but that is
190            // currently defined only for `Address<Ipv4>`.
191            AddressRange::from(&range).contains(self) && self.into_primitive() != A::Primitive::ONES
192        } else {
193            false
194        }
195    }
196
197    #[allow(clippy::option_if_let_else)]
198    fn is_shared(&self) -> bool {
199        if let Some(range) = A::Primitive::SHARED_RANGE {
200            AddressRange::from(&range).contains(self)
201        } else {
202            false
203        }
204    }
205
206    #[allow(clippy::option_if_let_else)]
207    fn is_thisnet(&self) -> bool {
208        if let Some(range) = A::Primitive::THISNET_RANGE {
209            AddressRange::from(&range).contains(self)
210        } else {
211            false
212        }
213    }
214
215    fn is_benchmarking(&self) -> bool {
216        AddressRange::from(&A::Primitive::BENCHMARK_RANGE).contains(self)
217    }
218
219    fn is_documentation(&self) -> bool {
220        A::Primitive::DOCUMENTATION_RANGES
221            .iter()
222            .any(|range| AddressRange::from(range).contains(self))
223    }
224
225    fn is_global(&self) -> bool {
226        self.into_primitive().is_global()
227    }
228
229    fn is_loopback(&self) -> bool {
230        AddressRange::from(&A::Primitive::LOOPBACK_RANGE).contains(self)
231    }
232
233    fn is_multicast(&self) -> bool {
234        AddressRange::from(&A::Primitive::MULTICAST_RANGE).contains(self)
235    }
236
237    fn is_unspecified(&self) -> bool {
238        self == &Self::UNSPECIFIED
239    }
240
241    #[allow(clippy::option_if_let_else)]
242    fn is_unique_local(&self) -> bool {
243        if let Some(range) = A::Primitive::ULA_RANGE {
244            AddressRange::from(&range).contains(self)
245        } else {
246            false
247        }
248    }
249}
250
251impl<A: Afi> FromStr for Address<A> {
252    type Err = Error;
253
254    fn from_str(s: &str) -> Result<Self, Self::Err> {
255        A::Primitive::parse_addr(s).map(Self::new)
256    }
257}
258
259impl_try_from_any! {
260    any::Address {
261        any::Address::Ipv4 => Address<Ipv4>,
262        any::Address::Ipv6 => Address<Ipv6>,
263    }
264}
265
266impl<A: Afi> fmt::Display for Address<A> {
267    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268        self.into_primitive().fmt_addr(f)
269    }
270}
271
272impl<A: Afi> fmt::Debug for Address<A> {
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        write!(f, "Address<{:?}>({})", A::as_afi(), self)
275    }
276}
277
278#[cfg(any(test, feature = "arbitrary"))]
279use proptest::{
280    arbitrary::{any_with, Arbitrary, ParamsFor, StrategyFor},
281    strategy::{BoxedStrategy, Strategy},
282};
283
284#[cfg(any(test, feature = "arbitrary"))]
285impl<A> Arbitrary for Address<A>
286where
287    A: Afi + 'static,
288    A::Primitive: Arbitrary + 'static,
289    StrategyFor<A::Primitive>: 'static,
290{
291    type Parameters = ParamsFor<A::Primitive>;
292    type Strategy = BoxedStrategy<Self>;
293
294    fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
295        any_with::<A::Primitive>(params).prop_map(Self::new).boxed()
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302    use crate::traits::Address as _;
303
304    #[test]
305    fn ipv4_broadcast_is_broadcast() {
306        assert!("255.255.255.255"
307            .parse::<Address<Ipv4>>()
308            .unwrap()
309            .is_broadcast());
310    }
311
312    #[test]
313    fn ipv4_unicast_is_not_broadcast() {
314        assert!(!"203.0.113.1"
315            .parse::<Address<Ipv4>>()
316            .unwrap()
317            .is_broadcast());
318    }
319
320    #[test]
321    fn ipv6_all_ones_is_not_broadcast() {
322        assert!(!"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
323            .parse::<Address<Ipv6>>()
324            .unwrap()
325            .is_broadcast());
326    }
327
328    #[test]
329    fn ipv4_link_local_is_link_local() {
330        assert!("169.254.254.1"
331            .parse::<Address<Ipv4>>()
332            .unwrap()
333            .is_link_local());
334    }
335
336    #[test]
337    fn ipv6_link_local_is_link_local() {
338        assert!("fe80::1".parse::<Address<Ipv6>>().unwrap().is_link_local());
339    }
340
341    #[test]
342    fn ipv4_unicast_is_not_link_local() {
343        assert!(!"203.0.113.1"
344            .parse::<Address<Ipv4>>()
345            .unwrap()
346            .is_link_local());
347    }
348
349    #[test]
350    fn ipv6_localhost_is_not_link_local() {
351        assert!(!Address::<Ipv6>::LOCALHOST.is_link_local());
352    }
353
354    #[test]
355    fn ipv4_private_is_private() {
356        assert!("172.18.0.1".parse::<Address<Ipv4>>().unwrap().is_private());
357    }
358
359    #[test]
360    fn ipv4_unicast_is_not_private() {
361        assert!(!"203.0.113.1".parse::<Address<Ipv4>>().unwrap().is_private());
362    }
363
364    #[test]
365    fn ipv6_ula_is_not_private() {
366        assert!(!"fc01::1".parse::<Address<Ipv6>>().unwrap().is_private());
367    }
368
369    #[test]
370    fn ipv4_reserved_is_reserved() {
371        assert!("240.0.0.1".parse::<Address<Ipv4>>().unwrap().is_reserved());
372    }
373
374    #[test]
375    fn ipv4_broadcast_is_not_reserved() {
376        assert!(!Address::<Ipv4>::BROADCAST.is_reserved());
377    }
378
379    #[test]
380    fn ipv6_unassigned_is_not_reserved() {
381        assert!(!"4::1".parse::<Address<Ipv6>>().unwrap().is_reserved());
382    }
383
384    #[test]
385    fn ipv4_shared_is_shared() {
386        assert!("100.72.1.1".parse::<Address<Ipv4>>().unwrap().is_shared());
387    }
388
389    #[test]
390    fn ipv4_unicast_is_not_shared() {
391        assert!(!"192.0.2.1".parse::<Address<Ipv4>>().unwrap().is_shared());
392    }
393
394    #[test]
395    fn ipv6_ula_is_not_shared() {
396        assert!(!"fc00::1".parse::<Address<Ipv6>>().unwrap().is_shared());
397    }
398
399    #[test]
400    fn ipv4_thisnet_is_thisnet() {
401        assert!("0.255.255.255"
402            .parse::<Address<Ipv4>>()
403            .unwrap()
404            .is_thisnet());
405    }
406
407    #[test]
408    fn ipv6_unspecified_is_not_thisnet() {
409        assert!(!Address::<Ipv6>::UNSPECIFIED.is_thisnet());
410    }
411
412    #[test]
413    fn ipv4_benchmarking_is_benmarking() {
414        assert!("198.19.0.1"
415            .parse::<Address<Ipv4>>()
416            .unwrap()
417            .is_benchmarking());
418    }
419
420    #[test]
421    fn ipv6_benchmarking_is_benmarking() {
422        assert!("2001:2::1"
423            .parse::<Address<Ipv6>>()
424            .unwrap()
425            .is_benchmarking());
426    }
427
428    #[test]
429    fn ipv4_test_net_2_is_documentation() {
430        assert!("198.51.100.1"
431            .parse::<Address<Ipv4>>()
432            .unwrap()
433            .is_documentation());
434    }
435
436    #[test]
437    fn ipv6_documentation_is_documentation() {
438        assert!("2001:db8::1"
439            .parse::<Address<Ipv6>>()
440            .unwrap()
441            .is_documentation());
442    }
443
444    #[test]
445    fn ipv4_private_1_is_not_global() {
446        assert!(!"10.254.0.0".parse::<Address<Ipv4>>().unwrap().is_global());
447    }
448
449    #[test]
450    fn ipv4_private_2_is_not_global() {
451        assert!(!"192.168.10.65"
452            .parse::<Address<Ipv4>>()
453            .unwrap()
454            .is_global());
455    }
456
457    #[test]
458    fn ipv4_private_3_is_not_global() {
459        assert!(!"172.16.10.65".parse::<Address<Ipv4>>().unwrap().is_global());
460    }
461
462    #[test]
463    fn ipv6_ula_is_not_global() {
464        assert!(!"fc00::1".parse::<Address<Ipv6>>().unwrap().is_global());
465    }
466
467    #[test]
468    fn ipv4_thisnet_is_not_global() {
469        assert!(!"0.1.2.3".parse::<Address<Ipv4>>().unwrap().is_global());
470    }
471
472    #[test]
473    fn ipv4_unspecified_is_not_global() {
474        assert!(!Address::<Ipv4>::UNSPECIFIED.is_global());
475    }
476
477    #[test]
478    fn ipv6_unspecified_is_not_global() {
479        assert!(!Address::<Ipv6>::UNSPECIFIED.is_global());
480    }
481
482    #[test]
483    fn ipv4_localhost_is_not_global() {
484        assert!(!Address::<Ipv4>::LOCALHOST.is_global());
485    }
486
487    #[test]
488    fn ipv6_localhost_is_not_global() {
489        assert!(!Address::<Ipv6>::LOCALHOST.is_global());
490    }
491
492    #[test]
493    fn ipv4_link_local_is_not_global() {
494        assert!(!"169.254.45.1".parse::<Address<Ipv4>>().unwrap().is_global());
495    }
496
497    #[test]
498    fn ipv6_link_local_is_not_global() {
499        assert!(!"fe80::1".parse::<Address<Ipv6>>().unwrap().is_global());
500    }
501
502    #[test]
503    fn ipv4_broadcast_is_not_global() {
504        assert!(!Address::<Ipv4>::BROADCAST.is_global());
505    }
506
507    #[test]
508    fn ipv4_doc_1_is_not_global() {
509        assert!(!"192.0.2.255".parse::<Address<Ipv4>>().unwrap().is_global());
510    }
511
512    #[test]
513    fn ipv4_doc_2_is_not_global() {
514        assert!(!"198.51.100.65"
515            .parse::<Address<Ipv4>>()
516            .unwrap()
517            .is_global());
518    }
519
520    #[test]
521    fn ipv4_doc_3_is_not_global() {
522        assert!(!"203.0.113.6".parse::<Address<Ipv4>>().unwrap().is_global());
523    }
524
525    #[test]
526    fn ipv6_doc_is_not_global() {
527        assert!(!"2001:db8::1".parse::<Address<Ipv6>>().unwrap().is_global());
528    }
529
530    #[test]
531    fn ipv4_shared_is_not_global() {
532        assert!(!"100.100.0.0".parse::<Address<Ipv4>>().unwrap().is_global());
533    }
534
535    #[test]
536    fn ipv4_proto_specific_1_is_not_global() {
537        assert!(!"192.0.0.0".parse::<Address<Ipv4>>().unwrap().is_global());
538    }
539
540    #[test]
541    fn ipv4_proto_specific_2_is_not_global() {
542        assert!(!"192.0.0.255".parse::<Address<Ipv4>>().unwrap().is_global());
543    }
544
545    #[test]
546    fn ipv6_proto_specific_is_not_global() {
547        assert!(!"2001:100::1".parse::<Address<Ipv6>>().unwrap().is_global());
548    }
549
550    #[test]
551    fn ipv4_reserved_is_not_global() {
552        assert!(!"250.10.20.30".parse::<Address<Ipv4>>().unwrap().is_global());
553    }
554
555    #[test]
556    fn ipv4_benchmarking_is_not_global() {
557        assert!(!"198.18.0.0".parse::<Address<Ipv4>>().unwrap().is_global());
558    }
559
560    #[test]
561    fn ipv6_benchmarking_is_not_global() {
562        assert!(!"2001:2::1".parse::<Address<Ipv6>>().unwrap().is_global());
563    }
564
565    #[test]
566    fn ipv4_local_multicast_is_not_global() {
567        assert!(!"224.0.0.1".parse::<Address<Ipv4>>().unwrap().is_global());
568    }
569
570    #[test]
571    fn ipv4_domain_multicast_is_not_global() {
572        assert!(!"239.0.0.1".parse::<Address<Ipv4>>().unwrap().is_global());
573    }
574
575    #[test]
576    fn ipv6_non_global_multicast_is_not_global() {
577        assert!(!"ff08::1".parse::<Address<Ipv6>>().unwrap().is_global());
578    }
579
580    #[test]
581    fn ipv4_pcp_anycast_is_global() {
582        assert!("192.0.0.9".parse::<Address<Ipv4>>().unwrap().is_global());
583    }
584
585    #[test]
586    fn ipv6_orchidv2_is_global() {
587        assert!("2001:20::1".parse::<Address<Ipv6>>().unwrap().is_global());
588    }
589
590    #[test]
591    fn ipv4_global_multicast_is_global() {
592        assert!("224.0.1.1".parse::<Address<Ipv4>>().unwrap().is_global());
593    }
594
595    #[test]
596    fn ipv6_global_multicast_is_global() {
597        assert!("ff0e::1".parse::<Address<Ipv6>>().unwrap().is_global());
598    }
599
600    #[test]
601    fn ipv4_global_unicast_is_global() {
602        assert!("1.1.1.1".parse::<Address<Ipv4>>().unwrap().is_global());
603    }
604
605    #[test]
606    fn ipv6_global_unicast_is_global() {
607        assert!("2606:4700:4700::1111"
608            .parse::<Address<Ipv6>>()
609            .unwrap()
610            .is_global());
611    }
612
613    #[test]
614    fn ipv4_loopback_is_loopback() {
615        assert!("127.0.0.53".parse::<Address<Ipv4>>().unwrap().is_loopback());
616    }
617
618    #[test]
619    fn ipv6_loopback_is_loopback() {
620        assert!("::1".parse::<Address<Ipv6>>().unwrap().is_loopback());
621    }
622
623    #[test]
624    fn ipv4_multicast_is_multicast() {
625        assert!("224.254.0.0"
626            .parse::<Address<Ipv4>>()
627            .unwrap()
628            .is_multicast());
629    }
630
631    #[test]
632    fn ipv4_unicast_is_not_multicast() {
633        assert!(!"172.16.10.65"
634            .parse::<Address<Ipv4>>()
635            .unwrap()
636            .is_multicast());
637    }
638
639    #[test]
640    fn ipv6_multicast_is_multicast() {
641        assert!("ff01::1".parse::<Address<Ipv6>>().unwrap().is_multicast());
642    }
643
644    #[test]
645    fn ipv4_unspecified_is_unspecified() {
646        assert!("0.0.0.0".parse::<Address<Ipv4>>().unwrap().is_unspecified());
647    }
648
649    #[test]
650    fn ipv6_unspecified_is_unspecified() {
651        assert!("::".parse::<Address<Ipv6>>().unwrap().is_unspecified());
652    }
653
654    #[test]
655    fn ipv6_ula_is_unique_local() {
656        assert!("fc01::1"
657            .parse::<Address<Ipv6>>()
658            .unwrap()
659            .is_unique_local());
660    }
661
662    #[test]
663    fn ipv6_doc_is_not_unique_local() {
664        assert!(!"2001:db8::1"
665            .parse::<Address<Ipv6>>()
666            .unwrap()
667            .is_unique_local());
668    }
669
670    #[test]
671    fn ipv4_private_is_not_unique_local() {
672        assert!(!"192.168.1.1"
673            .parse::<Address<Ipv4>>()
674            .unwrap()
675            .is_unique_local());
676    }
677
678    #[test]
679    fn ipv6_unicast_is_unicast() {
680        assert!("2001:db8::1".parse::<Address<Ipv6>>().unwrap().is_unicast());
681    }
682    #[test]
683    fn ipv4_unicast_is_unicast() {
684        assert!("192.168.1.1".parse::<Address<Ipv4>>().unwrap().is_unicast());
685    }
686    #[test]
687    fn ipv6_multicast_is_not_unicast() {
688        assert!(!"ffaa::1".parse::<Address<Ipv6>>().unwrap().is_unicast());
689    }
690    #[test]
691    fn ipv4_multicast_is_not_unicast() {
692        assert!(!"239.0.0.1".parse::<Address<Ipv4>>().unwrap().is_unicast());
693    }
694    #[test]
695    fn ipv4_broadcast_is_not_unicast() {
696        assert!(!"255.255.255.255"
697            .parse::<Address<Ipv4>>()
698            .unwrap()
699            .is_unicast());
700    }
701
702    #[test]
703    fn ipv4_unicast_global_is_unicast_global() {
704        assert!("1.1.1.1"
705            .parse::<Address<Ipv4>>()
706            .unwrap()
707            .is_unicast_global());
708    }
709    #[test]
710    fn ipv6_unicast_global_is_unicast_global() {
711        assert!("2606:4700:4700::1111"
712            .parse::<Address<Ipv6>>()
713            .unwrap()
714            .is_unicast_global());
715    }
716    #[test]
717    fn ipv4_unicast_private_is_not_unicast_global() {
718        assert!(!"192.168.1.1"
719            .parse::<Address<Ipv4>>()
720            .unwrap()
721            .is_unicast_global());
722    }
723    #[test]
724    fn ipv4_multicast_global_is_not_unicast_global() {
725        assert!(!"225.0.0.1"
726            .parse::<Address<Ipv4>>()
727            .unwrap()
728            .is_unicast_global());
729    }
730    #[test]
731    fn ipv6_unicast_documentation_is_not_unicast_global() {
732        assert!(!"2001:db8::1"
733            .parse::<Address<Ipv6>>()
734            .unwrap()
735            .is_unicast_global());
736    }
737    #[test]
738    fn ipv6_multicast_global_is_not_unicast_global() {
739        assert!(!"ff0e::1"
740            .parse::<Address<Ipv6>>()
741            .unwrap()
742            .is_unicast_global());
743    }
744}