ip_network/ipv6_network.rs
1use std::fmt;
2use std::net::Ipv6Addr;
3use std::str::FromStr;
4use std::hash::{Hash, Hasher};
5use crate::{IpNetworkError, IpNetworkParseError};
6use crate::helpers;
7use crate::iterator;
8use std::collections::HashMap;
9use std::collections::hash_map::Entry;
10
11/// IPv6 Multicast Address Scopes.
12#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug)]
13pub enum Ipv6MulticastScope {
14 InterfaceLocal,
15 LinkLocal,
16 RealmLocal,
17 AdminLocal,
18 SiteLocal,
19 OrganizationLocal,
20 Global,
21}
22
23/// IPv6 Network.
24#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord)]
25pub struct Ipv6Network {
26 pub(crate) network_address: Ipv6Addr,
27 pub(crate) netmask: u8,
28}
29
30impl Ipv6Network {
31 /// IPv6 address length in bits.
32 pub const LENGTH: u8 = 128;
33
34 /// Default route that contains all IP addresses, IP network ::/0
35 pub const DEFAULT_ROUTE: Self = Self {
36 network_address: Ipv6Addr::UNSPECIFIED,
37 netmask: 0,
38 };
39
40 /// Constructs new `Ipv6Network` based on [`Ipv6Addr`] and `netmask`.
41 ///
42 /// Returns error if netmask is bigger than 128 or if host bits are set in `network_address`.
43 ///
44 /// [`Ipv6Addr`]: https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html
45 ///
46 /// # Examples
47 ///
48 /// ```
49 /// use std::net::Ipv6Addr;
50 /// use ip_network::Ipv6Network;
51 ///
52 /// let ip = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
53 /// let ip_network = Ipv6Network::new(ip, 32)?;
54 /// assert_eq!(ip_network.network_address(), ip);
55 /// assert_eq!(ip_network.netmask(), 32);
56 /// # Ok::<(), ip_network::IpNetworkError>(())
57 /// ```
58 #[allow(clippy::new_ret_no_self)]
59 pub fn new(network_address: Ipv6Addr, netmask: u8) -> Result<Self, IpNetworkError> {
60 if netmask > Self::LENGTH {
61 return Err(IpNetworkError::NetmaskError(netmask));
62 }
63
64 if u128::from(network_address).trailing_zeros() < u32::from(Self::LENGTH - netmask) {
65 return Err(IpNetworkError::HostBitsSet);
66 }
67
68 Ok(Self {
69 network_address,
70 netmask,
71 })
72 }
73
74 /// Constructs new `Ipv6Network` based on [`Ipv6Addr`] and `netmask` with truncating host bits
75 /// from given `network_address`.
76 ///
77 /// Returns error if netmask is bigger than 128.
78 ///
79 /// [`Ipv6Addr`]: https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html
80 ///
81 /// # Examples
82 ///
83 /// ```
84 /// use std::net::Ipv6Addr;
85 /// use ip_network::Ipv6Network;
86 ///
87 /// let ip = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 1, 0, 0);
88 /// let ip_network = Ipv6Network::new_truncate(ip, 32)?;
89 /// assert_eq!(ip_network.network_address(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
90 /// assert_eq!(ip_network.netmask(), 32);
91 /// # Ok::<(), ip_network::IpNetworkError>(())
92 /// ```
93 pub fn new_truncate(network_address: Ipv6Addr, netmask: u8) -> Result<Self, IpNetworkError> {
94 if netmask > Self::LENGTH {
95 return Err(IpNetworkError::NetmaskError(netmask));
96 }
97
98 let network_address_u128 = u128::from(network_address) & helpers::bite_mask_u128(netmask);
99 let network_address = Ipv6Addr::from(network_address_u128);
100
101 Ok(Self {
102 network_address,
103 netmask,
104 })
105 }
106
107 /// Returns network IP address (first address in range).
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// use std::net::Ipv6Addr;
113 /// use ip_network::Ipv6Network;
114 ///
115 /// let ip = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
116 /// let ip_network = Ipv6Network::new(ip, 32)?;
117 /// assert_eq!(ip_network.network_address(), ip);
118 /// # Ok::<(), ip_network::IpNetworkError>(())
119 /// ```
120 #[inline]
121 pub fn network_address(&self) -> Ipv6Addr {
122 self.network_address
123 }
124
125 /// Returns last IP address in range. Similar as `broadcast_address` for IPv4.
126 ///
127 /// # Examples
128 ///
129 /// ```
130 /// use std::net::Ipv6Addr;
131 /// use ip_network::Ipv6Network;
132 ///
133 /// let ip = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
134 /// let ip_network = Ipv6Network::new(ip, 32)?;
135 /// assert_eq!(ip_network.last_address(), Ipv6Addr::new(0x2001, 0xdb8, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff));
136 /// # Ok::<(), ip_network::IpNetworkError>(())
137 /// ```
138 pub fn last_address(&self) -> Ipv6Addr {
139 Ipv6Addr::from(u128::from(self.network_address) | !helpers::bite_mask_u128(self.netmask))
140 }
141
142 /// Returns network mask.
143 ///
144 /// # Examples
145 ///
146 /// ```
147 /// use std::net::Ipv6Addr;
148 /// use ip_network::Ipv6Network;
149 ///
150 /// let ip = Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0);
151 /// let ip_network = Ipv6Network::new(ip, 32)?;
152 /// assert_eq!(ip_network.netmask(), 32);
153 /// # Ok::<(), ip_network::IpNetworkError>(())
154 /// ```
155 #[inline]
156 pub fn netmask(&self) -> u8 {
157 self.netmask
158 }
159
160 /// Returns [`true`] if given [`IPv6Addr`] is inside this network.
161 ///
162 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
163 /// [`Ipv6Addr`]: https://doc.rust-lang.org/std/net/struct.Ipv6Addr.html
164 ///
165 /// # Examples
166 ///
167 /// ```
168 /// use std::net::Ipv6Addr;
169 /// use ip_network::Ipv6Network;
170 ///
171 /// let ip_network = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 64)?;
172 /// assert!(ip_network.contains(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)));
173 /// assert!(!ip_network.contains(Ipv6Addr::new(0x2001, 0xdb9, 0, 0, 0, 0, 0, 0)));
174 /// # Ok::<(), ip_network::IpNetworkError>(())
175 /// ```
176 pub fn contains(&self, ip: Ipv6Addr) -> bool {
177 let truncated_ip = u128::from(ip) & helpers::bite_mask_u128(self.netmask);
178 truncated_ip == u128::from(self.network_address)
179 }
180
181 /// Returns network with smaller netmask by one. If netmask is already zero, `None` will be returned.
182 ///
183 /// # Examples
184 ///
185 /// ```
186 /// use std::net::Ipv6Addr;
187 /// use ip_network::Ipv6Network;
188 ///
189 /// let network = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32)?;
190 /// assert_eq!(network.supernet(), Some(Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 31)?));
191 /// # Ok::<(), ip_network::IpNetworkError>(())
192 /// ```
193 pub fn supernet(&self) -> Option<Self> {
194 if self.netmask == 0 {
195 None
196 } else {
197 Some(Self::new_truncate(self.network_address, self.netmask - 1).unwrap())
198 }
199 }
200
201 /// Returns `Ipv6NetworkIterator` over networks with netmask bigger one.
202 /// If netmask is already 128, empty iterator will be returned.
203 ///
204 /// # Examples
205 ///
206 /// ```
207 /// use std::net::Ipv6Addr;
208 /// use ip_network::Ipv6Network;
209 ///
210 /// let ip_network = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32)?;
211 /// let mut iterator = ip_network.subnets();
212 /// assert_eq!(iterator.next().unwrap(), Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 33)?);
213 /// assert_eq!(iterator.last().unwrap(), Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0x8000, 0, 0, 0, 0, 0), 33)?);
214 /// # Ok::<(), ip_network::IpNetworkError>(())
215 /// ```
216 pub fn subnets(&self) -> iterator::Ipv6NetworkIterator {
217 let new_netmask = ::std::cmp::min(self.netmask + 1, Self::LENGTH);
218 iterator::Ipv6NetworkIterator::new(*self, new_netmask)
219 }
220
221 /// Returns `Ipv6NetworkIterator` over networks with defined netmask. Because [`len()`] method
222 /// returns `usize` and number of networks can be bigger than `usize`, you can use `real_len()` method
223 /// to get exact number of networks.
224 ///
225 /// [`len()`]: https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html#method.len
226 ///
227 /// # Panics
228 ///
229 /// This method panics when prefix is bigger than 128 or when prefix is lower or equal than netmask.
230 ///
231 /// # Examples
232 ///
233 /// ```
234 /// use std::net::Ipv6Addr;
235 /// use ip_network::Ipv6Network;
236 ///
237 /// let network = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32)?;
238 /// let mut iterator = network.subnets_with_prefix(33);
239 /// assert_eq!(2, iterator.real_len());
240 /// assert_eq!(iterator.next().unwrap(), Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 33)?);
241 /// assert_eq!(iterator.last().unwrap(), Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0x8000, 0, 0, 0, 0, 0), 33)?);
242 /// # Ok::<(), ip_network::IpNetworkError>(())
243 /// ```
244 pub fn subnets_with_prefix(&self, prefix: u8) -> iterator::Ipv6NetworkIterator {
245 iterator::Ipv6NetworkIterator::new(*self, prefix)
246 }
247
248 /// Returns [`true`] for the default route network (::/0), that contains all IPv6 addresses.
249 ///
250 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
251 ///
252 /// # Examples
253 ///
254 /// ```
255 /// use std::net::Ipv6Addr;
256 /// use ip_network::Ipv6Network;
257 ///
258 /// assert!(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0)?.is_default_route());
259 /// # Ok::<(), ip_network::IpNetworkError>(())
260 /// ```
261 pub fn is_default_route(&self) -> bool {
262 self.netmask == 0
263 }
264
265 /// Returns [`true`] for the special 'unspecified' network (::/128).
266 ///
267 /// This property is defined in [IETF RFC 4291].
268 ///
269 /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
270 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
271 ///
272 /// # Examples
273 ///
274 /// ```
275 /// use std::net::Ipv6Addr;
276 /// use ip_network::Ipv6Network;
277 ///
278 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_unspecified());
279 /// assert!(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 128)?.is_unspecified());
280 /// # Ok::<(), ip_network::IpNetworkError>(())
281 /// ```
282 pub fn is_unspecified(&self) -> bool {
283 self.netmask == Self::LENGTH && self.network_address.is_unspecified()
284 }
285
286 /// Returns [`true`] if this is a loopback network (::1/128).
287 ///
288 /// This property is defined in [IETF RFC 4291].
289 ///
290 /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
291 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
292 ///
293 /// # Examples
294 ///
295 /// ```
296 /// use std::net::Ipv6Addr;
297 /// use ip_network::Ipv6Network;
298 ///
299 /// assert!(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1), 128)?.is_loopback());
300 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_loopback());
301 /// # Ok::<(), ip_network::IpNetworkError>(())
302 /// ```
303 pub fn is_loopback(&self) -> bool {
304 self.network_address.is_loopback()
305 }
306
307 /// Returns [`true`] if the address appears to be globally routable.
308 ///
309 /// The following return [`false`]:
310 ///
311 /// - the loopback network
312 /// - link-local, site-local, and unique local unicast networks
313 /// - interface-, link-, realm-, admin- and site-local multicast networks
314 ///
315 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
316 /// [`false`]: https://doc.rust-lang.org/std/primitive.bool.html
317 ///
318 /// # Examples
319 ///
320 /// ```
321 /// use std::net::Ipv6Addr;
322 /// use ip_network::Ipv6Network;
323 ///
324 /// assert!(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_global());
325 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1), 128)?.is_global());
326 /// assert!(Ipv6Network::new(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1), 128)?.is_global());
327 /// # Ok::<(), ip_network::IpNetworkError>(())
328 /// ```
329 pub fn is_global(&self) -> bool {
330 match self.multicast_scope() {
331 Some(Ipv6MulticastScope::Global) => true,
332 None => self.is_unicast_global(),
333 _ => false,
334 }
335 }
336
337 /// Returns [`true`] if this is a part of unique local network (fc00::/7).
338 ///
339 /// This property is defined in [IETF RFC 4193].
340 ///
341 /// [IETF RFC 4193]: https://tools.ietf.org/html/rfc4193
342 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
343 ///
344 /// # Examples
345 ///
346 /// ```
347 /// use std::net::Ipv6Addr;
348 /// use ip_network::Ipv6Network;
349 ///
350 /// assert!(Ipv6Network::new(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0), 16)?.is_unique_local());
351 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_unique_local());
352 /// # Ok::<(), ip_network::IpNetworkError>(())
353 /// ```
354 pub fn is_unique_local(&self) -> bool {
355 (self.network_address.segments()[0] & 0xfe00) == 0xfc00 && self.netmask >= 7
356 }
357
358 /// Returns [`true`] if the network is part of unicast and link-local (fe80::/10).
359 ///
360 /// This property is defined in [IETF RFC 4291].
361 ///
362 /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
363 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
364 ///
365 /// # Examples
366 ///
367 /// ```
368 /// use std::net::Ipv6Addr;
369 /// use ip_network::Ipv6Network;
370 ///
371 /// assert!(Ipv6Network::new(Ipv6Addr::new(0xfe8a, 0, 0, 0, 0, 0, 0, 0), 16)?.is_unicast_link_local());
372 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_unicast_link_local());
373 /// # Ok::<(), ip_network::IpNetworkError>(())
374 /// ```
375 pub fn is_unicast_link_local(&self) -> bool {
376 (self.network_address.segments()[0] & 0xffc0) == 0xfe80 && self.netmask >= 10
377 }
378
379 /// Returns [`true`] if this is a deprecated unicast site-local network (fec0::/10).
380 ///
381 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
382 ///
383 /// # Examples
384 ///
385 /// ```
386 /// use std::net::Ipv6Addr;
387 /// use ip_network::Ipv6Network;
388 ///
389 /// assert!(Ipv6Network::new(Ipv6Addr::new(0xfec2, 0, 0, 0, 0, 0, 0, 0), 16)?.is_unicast_site_local());
390 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_unicast_site_local());
391 /// # Ok::<(), ip_network::IpNetworkError>(())
392 /// ```
393 pub fn is_unicast_site_local(&self) -> bool {
394 (self.network_address.segments()[0] & 0xffc0) == 0xfec0 && self.netmask >= 10
395 }
396
397 /// Returns [`true`] if this is a part of network reserved for documentation (2001:db8::/32).
398 ///
399 /// This property is defined in [IETF RFC 3849].
400 ///
401 /// [IETF RFC 3849]: https://tools.ietf.org/html/rfc3849
402 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
403 ///
404 /// # Examples
405 ///
406 /// ```
407 /// use std::net::Ipv6Addr;
408 /// use ip_network::Ipv6Network;
409 ///
410 /// assert!(Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32)?.is_documentation());
411 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_documentation());
412 /// # Ok::<(), ip_network::IpNetworkError>(())
413 /// ```
414 pub fn is_documentation(&self) -> bool {
415 let segments = self.network_address.segments();
416 segments[0] == 0x2001 && segments[1] == 0xdb8 && self.netmask >= 32
417 }
418
419 /// Returns [`true`] if the network is a globally routable unicast network.
420 ///
421 /// The following return [`false`]:
422 ///
423 /// - the loopback network
424 /// - the link-local network
425 /// - the (deprecated) site-local network
426 /// - unique local network
427 /// - the unspecified network
428 /// - the network range reserved for documentation
429 ///
430 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
431 /// [`false`]: https://doc.rust-lang.org/std/primitive.bool.html
432 ///
433 /// # Examples
434 ///
435 /// ```
436 /// use std::net::Ipv6Addr;
437 /// use ip_network::Ipv6Network;
438 ///
439 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32)?.is_unicast_global());
440 /// assert!(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_unicast_global());
441 /// # Ok::<(), ip_network::IpNetworkError>(())
442 /// ```
443 pub fn is_unicast_global(&self) -> bool {
444 !self.is_multicast()
445 && !self.is_loopback()
446 && !self.is_unicast_link_local()
447 && !self.is_unicast_site_local()
448 && !self.is_unique_local()
449 && !self.is_unspecified()
450 && !self.is_documentation()
451 }
452
453 /// Returns [`true`] if this is a part of multicast network (ff00::/8).
454 ///
455 /// This property is defined by [IETF RFC 4291].
456 ///
457 /// [IETF RFC 4291]: https://tools.ietf.org/html/rfc4291
458 /// [`true`]: https://doc.rust-lang.org/std/primitive.bool.html
459 ///
460 /// # Examples
461 ///
462 /// ```
463 /// use std::net::Ipv6Addr;
464 /// use ip_network::Ipv6Network;
465 ///
466 /// assert!(Ipv6Network::new(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0), 8)?.is_multicast());
467 /// assert!(!Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.is_multicast());
468 /// # Ok::<(), ip_network::IpNetworkError>(())
469 /// ```
470 pub fn is_multicast(&self) -> bool {
471 self.network_address.is_multicast()
472 }
473
474 /// Returns the network's multicast scope if the network is multicast.
475 ///
476 /// These scopes are defined in [IETF RFC 7346].
477 ///
478 /// [IETF RFC 7346]: https://tools.ietf.org/html/rfc7346
479 ///
480 /// # Examples
481 ///
482 /// ```
483 /// use std::net::Ipv6Addr;
484 /// use ip_network::{Ipv6Network, Ipv6MulticastScope};
485 ///
486 /// assert_eq!(Ipv6Network::new(Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0), 32)?.multicast_scope(),
487 /// Some(Ipv6MulticastScope::Global));
488 /// assert_eq!(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff), 128)?.multicast_scope(), None);
489 /// # Ok::<(), ip_network::IpNetworkError>(())
490 /// ```
491 pub fn multicast_scope(&self) -> Option<Ipv6MulticastScope> {
492 if self.is_multicast() && self.netmask >= 16 {
493 match self.network_address.segments()[0] & 0x000f {
494 1 => Some(Ipv6MulticastScope::InterfaceLocal),
495 2 => Some(Ipv6MulticastScope::LinkLocal),
496 3 => Some(Ipv6MulticastScope::RealmLocal),
497 4 => Some(Ipv6MulticastScope::AdminLocal),
498 5 => Some(Ipv6MulticastScope::SiteLocal),
499 8 => Some(Ipv6MulticastScope::OrganizationLocal),
500 14 => Some(Ipv6MulticastScope::Global),
501 _ => None,
502 }
503 } else {
504 None
505 }
506 }
507
508 /// Converts string in format X:X::X/Y (CIDR notation) to `Ipv6Network`, but truncating host bits.
509 ///
510 /// # Examples
511 ///
512 /// ```
513 /// use std::net::Ipv6Addr;
514 /// use ip_network::Ipv6Network;
515 ///
516 /// let ip_network = Ipv6Network::from_str_truncate("2001:db8::1/32")?;
517 /// assert_eq!(ip_network.network_address(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
518 /// assert_eq!(ip_network.netmask(), 32);
519 /// # Ok::<(), ip_network::IpNetworkParseError>(())
520 /// ```
521 pub fn from_str_truncate(s: &str) -> Result<Self, IpNetworkParseError> {
522 let (ip, netmask) =
523 helpers::split_ip_netmask(s).ok_or(IpNetworkParseError::InvalidFormatError)?;
524
525 let network_address =
526 Ipv6Addr::from_str(ip).map_err(|_| IpNetworkParseError::AddrParseError)?;
527 let netmask =
528 u8::from_str(netmask).map_err(|_| IpNetworkParseError::InvalidNetmaskFormat)?;
529
530 Self::new_truncate(network_address, netmask).map_err(IpNetworkParseError::IpNetworkError)
531 }
532
533 /// Return an iterator of the collapsed Ipv6Networks.
534 ///
535 /// Implementation of this method was inspired by Python [`ipaddress.collapse_addresses`]
536 ///
537 /// [`ipaddress.collapse_addresses`]: https://docs.python.org/3/library/ipaddress.html#ipaddress.collapse_addresses
538 ///
539 /// # Examples
540 ///
541 /// ```
542 /// use std::net::Ipv6Addr;
543 /// use ip_network::Ipv6Network;
544 /// use std::str::FromStr;
545 ///
546 /// let collapsed = Ipv6Network::collapse_addresses(&[
547 /// Ipv6Network::from_str("2001::/120")?,
548 /// Ipv6Network::from_str("2001::/96")?,
549 /// ]);
550 ///
551 /// assert_eq!(Ipv6Network::from_str("2001::/96")?, collapsed[0]);
552 /// # Ok::<(), ip_network::IpNetworkParseError>(())
553 /// ```
554 pub fn collapse_addresses(addresses: &[Self]) -> Vec<Self> {
555 let mut subnets = HashMap::new();
556
557 let mut to_merge = addresses.to_vec();
558 while let Some(net) = to_merge.pop() {
559 let supernet = net.supernet().unwrap_or(Ipv6Network::DEFAULT_ROUTE);
560 match subnets.entry(supernet) {
561 Entry::Vacant(vacant) => {
562 vacant.insert(net);
563 }
564 Entry::Occupied(occupied) => {
565 if *occupied.get() != net {
566 occupied.remove();
567 to_merge.push(supernet);
568 }
569 }
570 }
571 }
572
573 let mut output: Vec<Ipv6Network> = vec![];
574 let mut values = subnets.values().collect::<Vec<_>>();
575 values.sort_unstable();
576
577 for net in values {
578 if let Some(last) = output.last() {
579 // Since they are sorted, last.network_address <= net.network_address is a given.
580 if last.last_address() >= net.last_address() {
581 continue;
582 }
583 }
584 output.push(*net);
585 }
586 output
587 }
588}
589
590impl fmt::Display for Ipv6Network {
591 /// Converts `Ipv6Network` to string in format X:X::X/Y (CIDR notation).
592 ///
593 /// # Examples
594 ///
595 /// ```
596 /// use std::net::Ipv6Addr;
597 /// use ip_network::Ipv6Network;
598 ///
599 /// let ip_network = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32)?;
600 /// assert_eq!(ip_network.to_string(), "2001:db8::/32");
601 /// # Ok::<(), ip_network::IpNetworkError>(())
602 /// ```
603 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
604 write!(f, "{}/{}", self.network_address, self.netmask)
605 }
606}
607
608impl FromStr for Ipv6Network {
609 type Err = IpNetworkParseError;
610
611 /// Converts string in format X:X::X/Y (CIDR notation) to `Ipv6Network`.
612 ///
613 /// # Examples
614 ///
615 /// ```
616 /// use std::net::Ipv6Addr;
617 /// use ip_network::Ipv6Network;
618 /// use std::str::FromStr;
619 ///
620 /// let ip_network = Ipv6Network::from_str("2001:db8::/32")?;
621 /// assert_eq!(ip_network.network_address(), Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0));
622 /// assert_eq!(ip_network.netmask(), 32);
623 /// # Ok::<(), ip_network::IpNetworkParseError>(())
624 /// ```
625 fn from_str(s: &str) -> Result<Ipv6Network, IpNetworkParseError> {
626 let (ip, netmask) =
627 helpers::split_ip_netmask(s).ok_or(IpNetworkParseError::InvalidFormatError)?;
628
629 let network_address =
630 Ipv6Addr::from_str(ip).map_err(|_| IpNetworkParseError::AddrParseError)?;
631 let netmask =
632 u8::from_str(netmask).map_err(|_| IpNetworkParseError::InvalidNetmaskFormat)?;
633
634 Self::new(network_address, netmask).map_err(IpNetworkParseError::IpNetworkError)
635 }
636}
637
638impl From<Ipv6Addr> for Ipv6Network {
639 /// Converts `Ipv6Addr` to `Ipv6Network` with netmask 128.
640 #[inline]
641 fn from(ip: Ipv6Addr) -> Self {
642 Self {
643 network_address: ip,
644 netmask: Self::LENGTH,
645 }
646 }
647}
648
649impl PartialEq for Ipv6Network {
650 #[inline]
651 fn eq(&self, other: &Ipv6Network) -> bool {
652 self.netmask == other.netmask && self.network_address == other.network_address
653 }
654}
655
656impl Hash for Ipv6Network {
657 fn hash<H: Hasher>(&self, state: &mut H) {
658 self.network_address.hash(state);
659 self.netmask.hash(state);
660 }
661}
662
663#[cfg(test)]
664mod tests {
665 use std::net::Ipv6Addr;
666 use crate::{Ipv6Network, IpNetworkError, Ipv6MulticastScope};
667 use std::str::FromStr;
668 use std::hash::{Hash, Hasher};
669 use std::collections::hash_map::DefaultHasher;
670
671 fn return_test_ipv6_network() -> Ipv6Network {
672 Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32).unwrap()
673 }
674
675 #[test]
676 fn default_route() {
677 let network = Ipv6Network::DEFAULT_ROUTE;
678 assert!(network.is_default_route());
679 }
680
681 #[test]
682 fn new() {
683 let ip = Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0);
684 let network = Ipv6Network::new(ip, 7).unwrap();
685 assert_eq!(
686 network.network_address(),
687 Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0)
688 );
689 assert_eq!(network.netmask(), 7);
690 }
691
692 #[test]
693 fn new_invalid_netmask() {
694 let ip = Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0);
695 let network = Ipv6Network::new(ip, 129);
696 assert!(network.is_err());
697 assert_eq!(IpNetworkError::NetmaskError(129), network.unwrap_err());
698 }
699
700 #[test]
701 fn new_truncate_invalid_netmask() {
702 let ip = Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0);
703 let network = Ipv6Network::new_truncate(ip, 129);
704 assert!(network.is_err());
705 assert_eq!(IpNetworkError::NetmaskError(129), network.unwrap_err());
706 }
707
708 #[test]
709 fn contains() {
710 let ip_network = return_test_ipv6_network();
711 assert!(!ip_network.contains(Ipv6Addr::new(
712 0x2001, 0x0db7, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
713 )));
714 assert!(ip_network.contains(Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)));
715 assert!(ip_network.contains(Ipv6Addr::new(
716 0x2001, 0x0db8, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff
717 )));
718 assert!(!ip_network.contains(Ipv6Addr::new(0x2001, 0x0db9, 0, 0, 0, 0, 0, 0)));
719 }
720
721 #[test]
722 fn supernet() {
723 let ip_network = return_test_ipv6_network();
724 assert_eq!(
725 ip_network.supernet(),
726 Some(Ipv6Network::new(Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 0), 31).unwrap())
727 );
728 }
729
730 #[test]
731 fn supernet_none() {
732 let ipv6_network = Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0), 0).unwrap();
733 assert_eq!(None, ipv6_network.supernet());
734 }
735
736 #[test]
737 fn subnets() {
738 let mut subnets = return_test_ipv6_network().subnets();
739 assert_eq!(subnets.len(), 2);
740 assert_eq!(
741 subnets.next().unwrap(),
742 Ipv6Network::new(Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 0), 33).unwrap()
743 );
744 assert_eq!(
745 subnets.next().unwrap(),
746 Ipv6Network::new(Ipv6Addr::new(0x2001, 0x0db8, 0x8000, 0, 0, 0, 0, 0), 33).unwrap()
747 );
748 assert!(subnets.next().is_none());
749 }
750
751 #[test]
752 fn subnets_with_prefix() {
753 let ip_network = return_test_ipv6_network();
754 let mut subnets = ip_network.subnets_with_prefix(34);
755 assert_eq!(subnets.len(), 4);
756 assert_eq!(
757 subnets.next().unwrap(),
758 Ipv6Network::new(Ipv6Addr::new(0x2001, 0x0db8, 0x0000, 0, 0, 0, 0, 0), 34).unwrap()
759 );
760 assert_eq!(
761 subnets.next().unwrap(),
762 Ipv6Network::new(Ipv6Addr::new(0x2001, 0x0db8, 0x4000, 0, 0, 0, 0, 0), 34).unwrap()
763 );
764 assert_eq!(
765 subnets.next().unwrap(),
766 Ipv6Network::new(Ipv6Addr::new(0x2001, 0x0db8, 0x8000, 0, 0, 0, 0, 0), 34).unwrap()
767 );
768 assert_eq!(
769 subnets.next().unwrap(),
770 Ipv6Network::new(Ipv6Addr::new(0x2001, 0x0db8, 0xc000, 0, 0, 0, 0, 0), 34).unwrap()
771 );
772 assert!(subnets.next().is_none());
773 }
774
775 #[test]
776 fn is_loopback() {
777 assert!(
778 Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1), 128)
779 .unwrap()
780 .is_loopback()
781 )
782 }
783
784 #[test]
785 fn is_global() {
786 assert!("2c0f:fb50:4000::/36"
787 .parse::<Ipv6Network>()
788 .unwrap()
789 .is_global());
790 assert!("2001:4860:4000::/36"
791 .parse::<Ipv6Network>()
792 .unwrap()
793 .is_global());
794 }
795
796 #[test]
797 fn multicast_scope() {
798 let multicast_scope =
799 |network: &str| network.parse::<Ipv6Network>().unwrap().multicast_scope();
800
801 assert_eq!(None, multicast_scope("ff02::/15"));
802 assert_eq!(None, multicast_scope("fff2::/15"));
803 assert_eq!(
804 Some(Ipv6MulticastScope::InterfaceLocal),
805 multicast_scope("ff01::/16")
806 );
807 assert_eq!(
808 Some(Ipv6MulticastScope::InterfaceLocal),
809 multicast_scope("fff1::/16")
810 );
811 assert_eq!(
812 Some(Ipv6MulticastScope::LinkLocal),
813 multicast_scope("ff02::/16")
814 );
815 assert_eq!(
816 Some(Ipv6MulticastScope::LinkLocal),
817 multicast_scope("fff2::/16")
818 );
819 assert_eq!(
820 Some(Ipv6MulticastScope::RealmLocal),
821 multicast_scope("ff03::/16")
822 );
823 assert_eq!(
824 Some(Ipv6MulticastScope::RealmLocal),
825 multicast_scope("fff3::/16")
826 );
827 assert_eq!(
828 Some(Ipv6MulticastScope::AdminLocal),
829 multicast_scope("ff04::/16")
830 );
831 assert_eq!(
832 Some(Ipv6MulticastScope::AdminLocal),
833 multicast_scope("fff4::/16")
834 );
835 assert_eq!(
836 Some(Ipv6MulticastScope::SiteLocal),
837 multicast_scope("ff05::/16")
838 );
839 assert_eq!(
840 Some(Ipv6MulticastScope::SiteLocal),
841 multicast_scope("fff5::/16")
842 );
843 assert_eq!(
844 Some(Ipv6MulticastScope::OrganizationLocal),
845 multicast_scope("ff08::/16")
846 );
847 assert_eq!(
848 Some(Ipv6MulticastScope::OrganizationLocal),
849 multicast_scope("fff8::/16")
850 );
851 assert_eq!(
852 Some(Ipv6MulticastScope::Global),
853 multicast_scope("ff0e::/16")
854 );
855 assert_eq!(
856 Some(Ipv6MulticastScope::Global),
857 multicast_scope("fffe::/16")
858 );
859 }
860
861 #[test]
862 fn collapse_addresses() {
863 let addresses = [
864 Ipv6Network::from_str("2001::/100").unwrap(),
865 Ipv6Network::from_str("2001::/120").unwrap(),
866 Ipv6Network::from_str("2001::/96").unwrap(),
867 ];
868 let collapsed = Ipv6Network::collapse_addresses(&addresses);
869 assert_eq!(1, collapsed.len());
870 assert_eq!(Ipv6Network::from_str("2001::/96").unwrap(), collapsed[0]);
871 }
872
873 #[test]
874 fn parse() {
875 let ip_network: Ipv6Network = "2001:db8::/32".parse().unwrap();
876 assert_eq!(ip_network, return_test_ipv6_network());
877 }
878
879 #[test]
880 fn format() {
881 let ip_network = return_test_ipv6_network();
882 assert_eq!(ip_network.to_string(), "2001:db8::/32");
883 }
884
885 #[test]
886 fn from_ipv6addr() {
887 let ip = Ipv6Addr::new(0x2001, 0x0db8, 0xc000, 0, 0, 0, 0, 0);
888 let ipv6_network = Ipv6Network::from(ip);
889 assert_eq!(ip, ipv6_network.network_address());
890 assert_eq!(128, ipv6_network.netmask());
891 }
892
893 #[test]
894 fn hash() {
895 let network1 = Ipv6Network::from_str("2001::/100").unwrap();
896 let network2 = Ipv6Network::from_str("2001::/120").unwrap();
897
898 let mut hasher1 = DefaultHasher::new();
899 network1.hash(&mut hasher1);
900
901 let mut hasher2 = DefaultHasher::new();
902 network2.hash(&mut hasher2);
903
904 assert_ne!(hasher1.finish(), hasher2.finish());
905 }
906}