ip/concrete/addr/ipv6.rs
1use super::Address;
2use crate::{
3 any,
4 concrete::{Ipv4, Ipv6},
5 traits::{primitive::IntoIpv6Segments as _, Address as _, Afi},
6};
7
8// TODO: make methods `const fn`
9impl Address<Ipv6> {
10 /// Returns [`true`] if the address is unicast link local.
11 ///
12 /// This method is provided for compatibility with [`std::net::Ipv6Addr`],
13 /// and is just a wrapper around
14 /// [`Address::is_link_local()`][crate::traits::Address::is_link_local()].
15 #[must_use]
16 pub fn is_unicast_link_local(&self) -> bool {
17 self.is_link_local()
18 }
19
20 /// Returns the [`Ipv6MulticastScope`][MulticastScope] variant of the
21 /// address if the address is a multicast address, or [`None`]
22 /// otherwise.
23 ///
24 /// # Examples
25 ///
26 /// ``` rust
27 /// use ip::{concrete::Ipv6MulticastScope, Address, Ipv6};
28 ///
29 /// let ipv6_site_local_multicast = "ff05::1".parse::<Address<Ipv6>>()?;
30 /// assert_eq!(
31 /// ipv6_site_local_multicast.multicast_scope(),
32 /// Some(Ipv6MulticastScope::SiteLocal),
33 /// );
34 ///
35 /// let ipv6_global_multicast = "ff0e::1".parse::<Address<Ipv6>>()?;
36 /// assert_eq!(
37 /// ipv6_global_multicast.multicast_scope(),
38 /// Some(Ipv6MulticastScope::Global),
39 /// );
40 ///
41 /// let ipv6_unicast = "2001:db8::1".parse::<Address<Ipv6>>()?;
42 /// assert_eq!(ipv6_unicast.multicast_scope(), None,);
43 /// # Ok::<(), ip::Error>(())
44 /// ```
45 #[allow(clippy::match_same_arms)]
46 #[must_use]
47 pub fn multicast_scope(&self) -> Option<MulticastScope> {
48 if self.is_multicast() {
49 match self.octets()[1] & 0x0f {
50 0x0 => Some(MulticastScope::Reserved),
51 0x1 => Some(MulticastScope::InterfaceLocal),
52 0x2 => Some(MulticastScope::LinkLocal),
53 0x3 => Some(MulticastScope::RealmLocal),
54 0x4 => Some(MulticastScope::AdminLocal),
55 0x5 => Some(MulticastScope::SiteLocal),
56 0x6..=0x7 => Some(MulticastScope::Unassigned),
57 0x8 => Some(MulticastScope::OrganizationLocal),
58 0x9..=0xd => Some(MulticastScope::Unassigned),
59 0xe => Some(MulticastScope::Global),
60 0xf => Some(MulticastScope::Reserved),
61 _ => unreachable!(),
62 }
63 } else {
64 None
65 }
66 }
67
68 /// Returns a big-endian [`[u16; 8]`] representing the segments of the
69 /// address.
70 ///
71 /// # Examples
72 ///
73 /// ``` rust
74 /// use ip::{Address, Ipv6};
75 ///
76 /// assert_eq!(
77 /// "2001:db8:f00::1".parse::<Address<Ipv6>>()?.segments(),
78 /// [0x2001, 0xdb8, 0xf00, 0x0, 0x0, 0x0, 0x0, 0x1],
79 /// );
80 /// # Ok::<(), ip::Error>(())
81 /// ```
82 #[must_use]
83 pub fn segments(&self) -> [u16; 8] {
84 self.into_primitive().into_segments()
85 }
86
87 // TODO: move to `traits::Address`
88 /// Convert the address to its canonical representation as an
89 /// [`any::Address`], by converting an IPv4-mapped address to a
90 /// [`any::Address::Ipv4`], and returning an [`any::Address::Ipv6`]
91 /// otherwise.
92 ///
93 /// # Examples
94 ///
95 /// ``` rust
96 /// use ip::{Address, Any, Ipv6};
97 ///
98 /// let ipv4_mapped = "::ffff:192.168.1.1".parse::<Address<Ipv6>>()?;
99 /// assert_eq!(
100 /// ipv4_mapped.to_canonical(),
101 /// "192.168.1.1".parse::<Address<Any>>()?,
102 /// );
103 ///
104 /// let ipv6_unicast = "2001:db8::1".parse::<Address<Ipv6>>()?;
105 /// assert_eq!(
106 /// ipv6_unicast.to_canonical(),
107 /// Address::<Any>::Ipv6(ipv6_unicast),
108 /// );
109 /// # Ok::<(), ip::Error>(())
110 /// ```
111 #[allow(clippy::wrong_self_convention)]
112 #[allow(clippy::option_if_let_else)]
113 #[must_use]
114 pub fn to_canonical(&self) -> any::Address {
115 if let Some(ipv4_addr) = self.to_ipv4_mapped() {
116 any::Address::Ipv4(ipv4_addr)
117 } else {
118 any::Address::Ipv6(*self)
119 }
120 }
121
122 /// Returns the embedded [`Address<Ipv4>`] in an IPv4-compatible or
123 /// IPv4-mapped [`Address<Ipv6>`], or [`None`] otherwise.
124 ///
125 /// # Examples
126 ///
127 /// ``` rust
128 /// use ip::{Address, Ipv6};
129 ///
130 /// assert_eq!(
131 /// "::192.168.1.1"
132 /// .parse::<Address<Ipv6>>()?
133 /// .to_ipv4()
134 /// .map(|ipv4| ipv4.octets()),
135 /// Some([192, 168, 1, 1]),
136 /// );
137 ///
138 /// assert_eq!("2001:db8::1".parse::<Address<Ipv6>>()?.to_ipv4(), None,);
139 /// # Ok::<(), ip::Error>(())
140 /// ```
141 #[allow(clippy::wrong_self_convention)]
142 #[must_use]
143 pub fn to_ipv4(&self) -> Option<Address<Ipv4>> {
144 self.to_ipv4_mapped().or_else(|| match self.octets() {
145 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, octets @ ..] => Some(Address::new(
146 <Ipv4 as Afi>::Primitive::from_be_bytes(octets),
147 )),
148 _ => None,
149 })
150 }
151
152 /// Returns the embedded [`Address<Ipv4>`] in an IPv4-mapped
153 /// [`Address<Ipv6>`], or [`None`] otherwise.
154 ///
155 /// # Examples
156 ///
157 /// ``` rust
158 /// use ip::{Address, Ipv6};
159 ///
160 /// assert_eq!(
161 /// "::ffff:172.16.1.1"
162 /// .parse::<Address<Ipv6>>()?
163 /// .to_ipv4_mapped()
164 /// .map(|ipv4| ipv4.octets()),
165 /// Some([172, 16, 1, 1]),
166 /// );
167 ///
168 /// assert_eq!(
169 /// "::192.168.1.1".parse::<Address<Ipv6>>()?.to_ipv4_mapped(),
170 /// None,
171 /// );
172 ///
173 /// assert_eq!(
174 /// "2001:db8::1".parse::<Address<Ipv6>>()?.to_ipv4_mapped(),
175 /// None,
176 /// );
177 /// # Ok::<(), ip::Error>(())
178 /// ```
179 #[allow(clippy::wrong_self_convention)]
180 #[must_use]
181 pub fn to_ipv4_mapped(&self) -> Option<Address<Ipv4>> {
182 match self.octets() {
183 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, octets @ ..] => Some(Address::new(
184 <Ipv4 as Afi>::Primitive::from_be_bytes(octets),
185 )),
186 _ => None,
187 }
188 }
189}
190
191// TODO: document omission of `non_exhaustive`
192/// IPv6 multicast address scopes, as defined in [RFC 4291].
193///
194/// See also [`Address::multicast_scope()`].
195///
196/// [RFC 4291]: https://tools.ietf.org/html/rfc4291
197#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
198pub enum MulticastScope {
199 /// Reserved.
200 Reserved,
201 /// Scope values not yet assigned to a multicast scope.
202 Unassigned,
203 /// Interface local scope.
204 InterfaceLocal,
205 /// Link local scope.
206 LinkLocal,
207 /// Realm local scope.
208 RealmLocal,
209 /// Locally administered scope.
210 AdminLocal,
211 /// Site local scope.
212 SiteLocal,
213 /// Organization local scope.
214 OrganizationLocal,
215 /// Global scope.
216 Global,
217}