ip_network_table/lib.rs
1//! This crate provides storage and retrieval of IPv4 and IPv6 network prefixes.
2//!
3//! Currently, it uses `ip_network` crate, that provides IP network data structure and
4//! `treebitmap` as backend, that provides fast lookup times, and a small memory footprint.
5//! Backend can be changed in future releases.
6//!
7//! ## Examples
8//!
9//! ```rust
10//! use std::net::{IpAddr, Ipv6Addr};
11//! use ip_network::{IpNetwork, Ipv6Network};
12//! use ip_network_table::IpNetworkTable;
13//!
14//! let mut table = IpNetworkTable::new();
15//! let network = IpNetwork::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
16//! let ip_address = Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0x1);
17//!
18//! assert_eq!(table.insert(network, "foo"), None);
19//! // Get value for network from table
20//! assert_eq!(table.longest_match(ip_address), Some((network, &"foo")));
21//! ```
22
23#![warn(rust_2018_idioms)]
24
25use ip_network::{IpNetwork, Ipv4Network, Ipv6Network};
26use ip_network_table_deps_treebitmap::IpLookupTable;
27use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; // forked from `treebitmap`
28
29/// Table holding IPv4 and IPv6 network prefixes with value.
30#[derive(Default)]
31pub struct IpNetworkTable<T> {
32 ipv4: IpLookupTable<Ipv4Addr, T>,
33 ipv6: IpLookupTable<Ipv6Addr, T>,
34}
35
36impl<T> IpNetworkTable<T> {
37 /// Constructs a new, empty `IpNetworkTable<T>`.
38 pub fn new() -> Self {
39 Self::with_capacity(0, 0)
40 }
41
42 /// Constructs a new, empty `IpNetworkTable<T>` with the specified capacity.
43 pub fn with_capacity(ipv4_size: usize, ipv6_size: usize) -> Self {
44 Self {
45 ipv4: IpLookupTable::with_capacity(ipv4_size),
46 ipv6: IpLookupTable::with_capacity(ipv6_size),
47 }
48 }
49
50 /// Returns the number of elements in the table. First value is number of IPv4 networks and second is number of IPv6 networks.
51 pub fn len(&self) -> (usize, usize) {
52 (self.ipv4.len(), self.ipv6.len())
53 }
54
55 /// Returns `true` if table is empty.
56 pub fn is_empty(&self) -> bool {
57 self.ipv4.is_empty() && self.ipv6.is_empty()
58 }
59
60 /// Insert a value for the `IpNetwork`. If prefix existed previously, the old value is returned.
61 ///
62 /// # Examples
63 ///
64 /// ```
65 /// use ip_network_table::IpNetworkTable;
66 /// use ip_network::Ipv6Network;
67 /// use std::net::Ipv6Addr;
68 ///
69 /// let mut table = IpNetworkTable::new();
70 /// let network = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
71 ///
72 /// assert_eq!(table.insert(network, "foo"), None);
73 /// // Insert duplicate
74 /// assert_eq!(table.insert(network, "bar"), Some("foo"));
75 /// // Value is replaced
76 /// assert_eq!(table.insert(network, "null"), Some("bar"));
77 /// ```
78 pub fn insert<N: Into<IpNetwork>>(&mut self, network: N, data: T) -> Option<T> {
79 match network.into() {
80 IpNetwork::V4(ipv4_network) => self.ipv4.insert(
81 ipv4_network.network_address(),
82 ipv4_network.netmask() as u32,
83 data,
84 ),
85 IpNetwork::V6(ipv6_network) => self.ipv6.insert(
86 ipv6_network.network_address(),
87 ipv6_network.netmask() as u32,
88 data,
89 ),
90 }
91 }
92
93 /// Remove a `IpNetwork` from table. If prefix existed, the value is returned.
94 ///
95 /// # Examples
96 ///
97 /// ```
98 /// use ip_network_table::IpNetworkTable;
99 /// use ip_network::Ipv6Network;
100 /// use std::net::Ipv6Addr;
101 ///
102 /// let mut table = IpNetworkTable::new();
103 /// let network = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
104 ///
105 /// assert_eq!(table.insert(network, "foo"), None);
106 /// // Remove network from table
107 /// assert_eq!(table.remove(network), Some("foo"));
108 /// // Network is removed
109 /// assert_eq!(table.exact_match(network), None);
110 /// ```
111 pub fn remove<N: Into<IpNetwork>>(&mut self, network: N) -> Option<T> {
112 match network.into() {
113 IpNetwork::V4(ipv4_network) => self.ipv4.remove(
114 ipv4_network.network_address(),
115 ipv4_network.netmask() as u32,
116 ),
117 IpNetwork::V6(ipv6_network) => self.ipv6.remove(
118 ipv6_network.network_address(),
119 ipv6_network.netmask() as u32,
120 ),
121 }
122 }
123
124 /// Get pointer to value from table based on exact network match.
125 /// If network is not in table, `None` is returned.
126 ///
127 /// # Examples
128 ///
129 /// ```
130 /// use ip_network_table::IpNetworkTable;
131 /// use ip_network::Ipv6Network;
132 /// use std::net::Ipv6Addr;
133 ///
134 /// let mut table = IpNetworkTable::new();
135 /// let network_a = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
136 /// let network_b = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 128).unwrap();
137 ///
138 /// assert_eq!(table.insert(network_a, "foo"), None);
139 /// // Get value for network from table
140 /// assert_eq!(table.exact_match(network_a), Some(&"foo"));
141 /// // Network B doesnt exists in table
142 /// assert_eq!(table.exact_match(network_b), None);
143 /// ```
144 pub fn exact_match<N: Into<IpNetwork>>(&self, network: N) -> Option<&T> {
145 match network.into() {
146 IpNetwork::V4(ipv4_network) => self.ipv4.exact_match(
147 ipv4_network.network_address(),
148 ipv4_network.netmask() as u32,
149 ),
150 IpNetwork::V6(ipv6_network) => self.ipv6.exact_match(
151 ipv6_network.network_address(),
152 ipv6_network.netmask() as u32,
153 ),
154 }
155 }
156
157 /// Get mutable pointer to value from table based on exact network match.
158 /// If network is not in table, `None` is returned.
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// use ip_network_table::IpNetworkTable;
164 /// use ip_network::Ipv6Network;
165 /// use std::net::Ipv6Addr;
166 ///
167 /// let mut table = IpNetworkTable::new();
168 /// let network_a = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
169 /// let network_b = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 128).unwrap();
170 ///
171 /// assert_eq!(table.insert(network_a, "foo"), None);
172 /// // Get value for network from table
173 /// assert_eq!(table.exact_match_mut(network_a), Some(&mut "foo"));
174 /// // Network B doesnt exists in table
175 /// assert_eq!(table.exact_match(network_b), None);
176 /// ```
177 pub fn exact_match_mut<N: Into<IpNetwork>>(&mut self, network: N) -> Option<&mut T> {
178 match network.into() {
179 IpNetwork::V4(ipv4_network) => self.ipv4.exact_match_mut(
180 ipv4_network.network_address(),
181 ipv4_network.netmask() as u32,
182 ),
183 IpNetwork::V6(ipv6_network) => self.ipv6.exact_match_mut(
184 ipv6_network.network_address(),
185 ipv6_network.netmask() as u32,
186 ),
187 }
188 }
189
190 /// Find most specific IP network in table that contains given IP address. If no network in table contains
191 /// given IP address, `None` is returned.
192 ///
193 /// # Examples
194 ///
195 /// ```
196 /// use ip_network_table::IpNetworkTable;
197 /// use ip_network::{IpNetwork, Ipv6Network};
198 /// use std::net::{IpAddr, Ipv6Addr};
199 ///
200 /// let mut table = IpNetworkTable::new();
201 /// let network = IpNetwork::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
202 /// let ip_address = Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0x1);
203 ///
204 /// assert_eq!(table.insert(network, "foo"), None);
205 /// // Get value for network from table
206 /// assert_eq!(table.longest_match(ip_address), Some((network, &"foo")));
207 /// ```
208 pub fn longest_match<I: Into<IpAddr>>(&self, ip: I) -> Option<(IpNetwork, &T)> {
209 match ip.into() {
210 IpAddr::V4(ipv4) => self
211 .longest_match_ipv4(ipv4)
212 .map(|(n, t)| (IpNetwork::V4(n), t)),
213 IpAddr::V6(ipv6) => self
214 .longest_match_ipv6(ipv6)
215 .map(|(n, t)| (IpNetwork::V6(n), t)),
216 }
217 }
218
219 /// Find most specific IP network in table that contains given IP address. If no network in table contains
220 /// given IP address, `None` is returned.
221 ///
222 /// # Examples
223 ///
224 /// ```
225 /// use ip_network_table::IpNetworkTable;
226 /// use ip_network::{IpNetwork, Ipv6Network};
227 /// use std::net::Ipv6Addr;
228 ///
229 /// let mut table = IpNetworkTable::new();
230 /// let network = IpNetwork::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
231 /// let ip_address = Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0x1);
232 ///
233 /// assert_eq!(table.insert(network, "foo"), None);
234 /// // Get value for network from table
235 /// assert_eq!(table.longest_match_mut(ip_address), Some((network, &mut "foo")));
236 /// ```
237 pub fn longest_match_mut<I: Into<IpAddr>>(&mut self, ip: I) -> Option<(IpNetwork, &mut T)> {
238 match ip.into() {
239 IpAddr::V4(ipv4) => self.ipv4.longest_match_mut(ipv4).map(|(addr, mask, data)| {
240 (
241 IpNetwork::V4(Ipv4Network::new(addr, mask as u8).unwrap()),
242 data,
243 )
244 }),
245 IpAddr::V6(ipv6) => self.ipv6.longest_match_mut(ipv6).map(|(addr, mask, data)| {
246 (
247 IpNetwork::V6(Ipv6Network::new(addr, mask as u8).unwrap()),
248 data,
249 )
250 }),
251 }
252 }
253
254 /// Specific version of `longest_match` for IPv4 address.
255 #[inline]
256 pub fn longest_match_ipv4(&self, ip: Ipv4Addr) -> Option<(Ipv4Network, &T)> {
257 self.ipv4
258 .longest_match(ip)
259 .map(|(addr, mask, data)| (Ipv4Network::new(addr, mask as u8).unwrap(), data))
260 }
261
262 /// Specific version of `longest_match` for IPv6 address.
263 #[inline]
264 pub fn longest_match_ipv6(&self, ip: Ipv6Addr) -> Option<(Ipv6Network, &T)> {
265 self.ipv6
266 .longest_match(ip)
267 .map(|(addr, mask, data)| (Ipv6Network::new(addr, mask as u8).unwrap(), data))
268 }
269
270 /// Find all IP networks in table that contains given IP address.
271 /// Returns iterator of `IpNetwork` and reference to value.
272 ///
273 /// # Examples
274 ///
275 /// ```
276 /// use ip_network_table::IpNetworkTable;
277 /// use ip_network::{IpNetwork, Ipv6Network};
278 /// use std::net::Ipv6Addr;
279 ///
280 /// let mut table = IpNetworkTable::new();
281 /// let network = IpNetwork::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
282 /// let ip_address = Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0x1);
283 ///
284 /// assert_eq!(table.insert(network, "foo"), None);
285 /// // Get value for network from table
286 /// assert_eq!(table.matches(ip_address).count(), 1);
287 /// ```
288 pub fn matches<I: Into<IpAddr>>(
289 &self,
290 ip: I,
291 ) -> Box<dyn Iterator<Item = (IpNetwork, &T)> + '_> {
292 match ip.into() {
293 IpAddr::V4(ipv4) => Box::new(
294 self.matches_ipv4(ipv4)
295 .map(|(network, data)| (IpNetwork::V4(network), data)),
296 ),
297 IpAddr::V6(ipv6) => Box::new(
298 self.matches_ipv6(ipv6)
299 .map(|(network, data)| (IpNetwork::V6(network), data)),
300 ),
301 }
302 }
303
304 /// Specific version of `matches` for IPv4 address.
305 pub fn matches_ipv4(&self, ip: Ipv4Addr) -> impl Iterator<Item = (Ipv4Network, &T)> {
306 self.ipv4
307 .matches(ip)
308 .map(|(addr, mask, data)| (Ipv4Network::new(addr, mask as u8).unwrap(), data))
309 }
310
311 /// Specific version of `matches` for IPv6 address.
312 pub fn matches_ipv6(&self, ip: Ipv6Addr) -> impl Iterator<Item = (Ipv6Network, &T)> {
313 self.ipv6
314 .matches(ip)
315 .map(|(addr, mask, data)| (Ipv6Network::new(addr, mask as u8).unwrap(), data))
316 }
317
318 /// Find all IP networks in table that contains given IP address.
319 /// Returns iterator of `IpNetwork` and mutable reference to value.
320 ///
321 /// # Examples
322 ///
323 /// ```
324 /// use ip_network_table::IpNetworkTable;
325 /// use ip_network::{IpNetwork, Ipv6Network};
326 /// use std::net::{IpAddr, Ipv6Addr};
327 ///
328 /// let mut table = IpNetworkTable::new();
329 /// let network = IpNetwork::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
330 /// let ip_address = Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0x1);
331 ///
332 /// assert_eq!(table.insert(network, "foo"), None);
333 /// // Get value for network from table
334 /// assert_eq!(table.matches_mut(ip_address).count(), 1);
335 /// ```
336 pub fn matches_mut<I: Into<IpAddr>>(
337 &mut self,
338 ip: I,
339 ) -> Box<dyn Iterator<Item = (IpNetwork, &mut T)> + '_> {
340 match ip.into() {
341 IpAddr::V4(ipv4) => Box::new(
342 self.matches_mut_ipv4(ipv4)
343 .map(|(network, data)| (IpNetwork::V4(network), data)),
344 ),
345 IpAddr::V6(ipv6) => Box::new(
346 self.matches_mut_ipv6(ipv6)
347 .map(|(network, data)| (IpNetwork::V6(network), data)),
348 ),
349 }
350 }
351
352 /// Specific version of `matches_mut` for IPv4 address.
353 #[inline]
354 pub fn matches_mut_ipv4(
355 &mut self,
356 ip: Ipv4Addr,
357 ) -> impl Iterator<Item = (Ipv4Network, &mut T)> {
358 self.ipv4
359 .matches_mut(ip)
360 .map(|(addr, mask, data)| (Ipv4Network::new(addr, mask as u8).unwrap(), data))
361 }
362
363 /// Specific version of `matches_mut` for IPv6 address.
364 #[inline]
365 pub fn matches_mut_ipv6(
366 &mut self,
367 ip: Ipv6Addr,
368 ) -> impl Iterator<Item = (Ipv6Network, &mut T)> {
369 self.ipv6
370 .matches_mut(ip)
371 .map(|(addr, mask, data)| (Ipv6Network::new(addr, mask as u8).unwrap(), data))
372 }
373
374 /// Iterator for all networks in table, first are iterated IPv4 and then IPv6 networks. Order is not guaranteed.
375 ///
376 /// # Examples
377 ///
378 /// ```
379 /// use ip_network_table::IpNetworkTable;
380 /// use ip_network::{IpNetwork, Ipv4Network, Ipv6Network};
381 /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
382 ///
383 /// let mut table: IpNetworkTable<&str> = IpNetworkTable::new();
384 /// let network_a = Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 0), 24).unwrap();
385 /// assert_eq!(table.insert(network_a, "foo"), None);
386 /// let network_b = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
387 /// assert_eq!(table.insert(network_b, "foo"), None);
388 ///
389 /// let mut iterator = table.iter();
390 /// assert_eq!(iterator.next(), Some((IpNetwork::V4(network_a), &"foo")));
391 /// assert_eq!(iterator.next(), Some((IpNetwork::V6(network_b), &"foo")));
392 /// assert_eq!(iterator.next(), None);
393 /// ```
394 pub fn iter(&self) -> impl Iterator<Item = (IpNetwork, &T)> {
395 self.iter_ipv4()
396 .map(|(network, data)| (IpNetwork::V4(network), data))
397 .chain(
398 self.iter_ipv6()
399 .map(|(network, data)| (IpNetwork::V6(network), data)),
400 )
401 }
402
403 /// Mutable iterator for all networks in table, first are iterated IPv4 and then IPv6 networks. Order is not guaranteed.
404 ///
405 /// # Examples
406 ///
407 /// ```
408 /// use ip_network_table::IpNetworkTable;
409 /// use ip_network::{IpNetwork, Ipv4Network, Ipv6Network};
410 /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
411 ///
412 /// let mut table: IpNetworkTable<&str> = IpNetworkTable::new();
413 /// let network_a = Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 0), 24).unwrap();
414 /// assert_eq!(table.insert(network_a, "foo"), None);
415 /// let network_b = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
416 /// assert_eq!(table.insert(network_b, "foo"), None);
417 ///
418 /// let mut iterator = table.iter_mut();
419 /// for (network, value) in iterator {
420 /// *value = "bar";
421 /// }
422 ///
423 /// assert_eq!(table.exact_match(network_a), Some(&"bar"));
424 /// assert_eq!(table.exact_match(network_b), Some(&"bar"));
425 /// ```
426 pub fn iter_mut(&mut self) -> impl Iterator<Item = (IpNetwork, &mut T)> {
427 self.ipv4
428 .iter_mut()
429 .map(|(addr, mask, data)| (IpNetwork::new(addr, mask as u8).unwrap(), data))
430 .chain(
431 self.ipv6
432 .iter_mut()
433 .map(|(addr, mask, data)| (IpNetwork::new(addr, mask as u8).unwrap(), data)),
434 )
435 }
436
437 /// Iterator for all IPv4 networks in table. Order is not guaranteed.
438 pub fn iter_ipv4(&self) -> impl Iterator<Item = (Ipv4Network, &T)> {
439 self.ipv4
440 .iter()
441 .map(|(addr, mask, data)| (Ipv4Network::new(addr, mask as u8).unwrap(), data))
442 }
443
444 /// Iterator for all IPv6 networks in table. Order is not guaranteed.
445 pub fn iter_ipv6(&self) -> impl Iterator<Item = (Ipv6Network, &T)> {
446 self.ipv6
447 .iter()
448 .map(|(addr, mask, data)| (Ipv6Network::new(addr, mask as u8).unwrap(), data))
449 }
450
451 /// Retains only the elements specified by the predicate.
452 ///
453 /// In other words, remove all pairs `(k, v)` such that `f(ip_network, &mut v)` returns `false`.
454 ///
455 /// # Examples
456 ///
457 /// ```
458 /// use ip_network_table::IpNetworkTable;
459 /// use ip_network::{IpNetwork, Ipv4Network, Ipv6Network};
460 /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
461 ///
462 /// let mut table: IpNetworkTable<&str> = IpNetworkTable::new();
463 /// let network_a = Ipv4Network::new(Ipv4Addr::new(192, 168, 0, 0), 24).unwrap();
464 /// assert_eq!(table.insert(network_a, "foo"), None);
465 /// let network_b = Ipv6Network::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap();
466 /// assert_eq!(table.insert(network_b, "foo"), None);
467 ///
468 /// // Keep just IPv4 networks
469 /// table.retain(|network, _| network.is_ipv4());
470 ///
471 /// assert_eq!(table.exact_match(network_a), Some(&"foo"));
472 /// assert_eq!(table.exact_match(network_b), None);
473 /// ```
474 pub fn retain<F>(&mut self, mut f: F)
475 where
476 F: FnMut(IpNetwork, &mut T) -> bool,
477 {
478 let mut to_delete = vec![];
479 for (network, data) in self.iter_mut() {
480 if !f(network, data) {
481 to_delete.push(network);
482 }
483 }
484 for network in to_delete {
485 self.remove(network);
486 }
487 }
488}
489
490#[cfg(test)]
491mod tests {
492 use crate::IpNetworkTable;
493 use ip_network::{Ipv4Network, Ipv6Network};
494 use std::net::{Ipv4Addr, Ipv6Addr};
495
496 #[test]
497 fn insert_ipv4_ipv6() {
498 let mut table = IpNetworkTable::new();
499 table.insert(
500 Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 0), 16).unwrap(),
501 1,
502 );
503 table.insert(
504 Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128).unwrap(),
505 1,
506 );
507 }
508
509 #[test]
510 fn exact_match_ipv4() {
511 let mut table = IpNetworkTable::new();
512 table.insert(
513 Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 0), 16).unwrap(),
514 1,
515 );
516 let m = table.exact_match(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 0), 16).unwrap());
517 assert_eq!(m, Some(&1));
518 let m = table.exact_match(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 0), 17).unwrap());
519 assert_eq!(m, None);
520 let m = table.exact_match(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 0), 15).unwrap());
521 assert_eq!(m, None);
522 }
523
524 #[test]
525 fn exact_match_ipv6() {
526 let mut table = IpNetworkTable::new();
527 table.insert(
528 Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 127).unwrap(),
529 1,
530 );
531 let m = table
532 .exact_match(Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 127).unwrap());
533 assert_eq!(m, Some(&1));
534 let m = table
535 .exact_match(Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128).unwrap());
536 assert_eq!(m, None);
537 let m = table
538 .exact_match(Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 126).unwrap());
539 assert_eq!(m, None);
540 }
541}