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}