├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── benchmark.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | sudo: required 3 | addons: 4 | apt: 5 | packages: 6 | - libssl-dev 7 | cache: cargo 8 | 9 | rust: 10 | - stable 11 | - beta 12 | - nightly 13 | 14 | matrix: 15 | allow_failures: 16 | - rust: nightly 17 | 18 | before_cache: | 19 | if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then 20 | cargo install cargo-tarpaulin 21 | fi 22 | 23 | script: 24 | - cargo clean 25 | - cargo build 26 | - cargo test 27 | 28 | after_success: | 29 | if [[ "$TRAVIS_RUST_VERSION" == stable ]]; then 30 | cargo tarpaulin --run-types Tests Doctests --ciserver travis-ci --coveralls $TRAVIS_JOB_ID 31 | fi 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Version 0.2.0 2 | * Update `ip_network` crate to 0.4.0 3 | 4 | ## Version 0.1.2 5 | 6 | * Used [fork](https://github.com/JakubOnderka/treebitmap) of treebitmap, that allows to implement new 7 | methods and fixes bugs. 8 | * Added new methods for `IpNetworkTable`: 9 | * `matches` 10 | * `matches_ipv4` 11 | * `matches_ipv6` 12 | * `matches_mut` 13 | * `matches_mut_ipv4` 14 | * `matches_mut_ipv6` 15 | * `longest_match_mut` 16 | * `exact_match_mut` 17 | * `retain` 18 | * `iter_ivp4` 19 | * `iter_ivp6` 20 | * `iter_mut` 21 | * `is_empty` 22 | * `IpNetworkTable` now implements [`std::default::Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait 23 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ip_network_table" 3 | version = "0.2.0" 4 | authors = ["Jakub Onderka "] 5 | license = "BSD-2-Clause" 6 | keywords = ["ip", "ipv4", "ipv6", "network", "address"] 7 | categories = ["network-programming", "data-structures"] 8 | readme = "README.md" 9 | repository = "https://github.com/JakubOnderka/ip_network_table" 10 | homepage = "https://github.com/JakubOnderka/ip_network_table" 11 | documentation = "https://docs.rs/ip_network_table" 12 | description = """ 13 | IPv4 and IPv6 network fast lookup table. 14 | """ 15 | edition = "2018" 16 | 17 | [badges] 18 | maintenance = { status = "actively-developed" } 19 | 20 | [dependencies] 21 | ip_network = "0.4.0" 22 | ip_network_table-deps-treebitmap = "0.5.0" 23 | 24 | [dev-dependencies] 25 | criterion = "0.3.0" 26 | 27 | [[bench]] 28 | name = "benchmark" 29 | harness = false 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Jakub Onderka 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this 7 | list of conditions and the following disclaimer. 8 | 2. Redistributions in binary form must reproduce the above copyright notice, 9 | this list of conditions and the following disclaimer in the documentation 10 | and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 14 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 15 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 16 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 21 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ip_network_table 2 | ======== 3 | 4 | IPv4 and IPv6 network fast lookup table. 5 | 6 | [![Documentation](https://docs.rs/ip_network_table/badge.svg)](https://docs.rs/ip_network_table) 7 | [![Build Status](https://travis-ci.com/JakubOnderka/ip_network_table.svg?branch=master)](https://travis-ci.com/JakubOnderka/ip_network_table) 8 | [![Coverage Status](https://coveralls.io/repos/github/JakubOnderka/ip_network_table/badge.svg?branch=master)](https://coveralls.io/github/JakubOnderka/ip_network_table?branch=master) 9 | [![Crates.io](https://img.shields.io/crates/v/ip_network_table.svg)](https://crates.io/crates/ip_network_table) 10 | 11 | ## Description 12 | 13 | This crate provides storage and retrieval of IPv4 and IPv6 network prefixes. 14 | 15 | Currently, it uses [`ip_network`](https://github.com/JakubOnderka/ip_network) crate, that provides IP network data structure and fork of 16 | [`treebitmap`](https://github.com/hroi/treebitmap) ([fork](https://github.com/JakubOnderka/treebitmap)) as backend, that provides fast lookup times, and a small memory footprint. Backend can be changed in future releases. 17 | 18 | ## Usage 19 | 20 | Add this to your `Cargo.toml`: 21 | 22 | ```toml 23 | [dependencies] 24 | ip_network = "0.4" 25 | ip_network_table = "0.2" 26 | ``` 27 | 28 | this to your crate root (not necessary when your project is Rust 2018 edition): 29 | 30 | ```rust 31 | extern crate ip_network; 32 | extern crate ip_network_table; 33 | ``` 34 | 35 | and then you can use it like this: 36 | 37 | ```rust 38 | use std::net::{IpAddr, Ipv6Addr}; 39 | use ip_network::{IpNetwork, Ipv6Network}; 40 | use ip_network_table::IpNetworkTable; 41 | 42 | let mut table = IpNetworkTable::new(); 43 | let network = IpNetwork::new(Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0), 64).unwrap(); 44 | let ip_address = Ipv6Addr::new(0x2001, 0xdb8, 0xdead, 0xbeef, 0, 0, 0, 0x1); 45 | 46 | assert_eq!(table.insert(network.clone(), "foo"), None); 47 | // Get value for network from table 48 | assert_eq!(table.longest_match(ip_address), Some((network, &"foo"))); 49 | ``` 50 | 51 | Minimal required version of Rust compiler is 1.31 (because of `ip_network` crate). 52 | -------------------------------------------------------------------------------- /benches/benchmark.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use ip_network::{Ipv4Network, Ipv6Network}; 3 | use ip_network_table::IpNetworkTable; 4 | use std::net::{Ipv4Addr, Ipv6Addr}; 5 | 6 | fn parse(c: &mut Criterion) { 7 | c.bench_function("insert ipv4", |b| { 8 | b.iter(|| { 9 | let mut table = IpNetworkTable::new(); 10 | table.insert( 11 | Ipv4Network::new(Ipv4Addr::new(black_box(127), 0, 0, 1), 32).unwrap(), 12 | true, 13 | ); 14 | }) 15 | }); 16 | c.bench_function("insert ipv6", |b| { 17 | b.iter(|| { 18 | let mut table = IpNetworkTable::new(); 19 | table.insert( 20 | Ipv6Network::new(Ipv6Addr::new(black_box(1), 2, 3, 4, 5, 6, 7, 8), 128).unwrap(), 21 | true, 22 | ); 23 | }) 24 | }); 25 | } 26 | 27 | criterion_group!(benches, parse); 28 | criterion_main!(benches); 29 | -------------------------------------------------------------------------------- /src/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 | 25 | use ip_network::{IpNetwork, Ipv4Network, Ipv6Network}; 26 | use ip_network_table_deps_treebitmap::IpLookupTable; 27 | use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; // forked from `treebitmap` 28 | 29 | /// Table holding IPv4 and IPv6 network prefixes with value. 30 | #[derive(Default)] 31 | pub struct IpNetworkTable { 32 | ipv4: IpLookupTable, 33 | ipv6: IpLookupTable, 34 | } 35 | 36 | impl IpNetworkTable { 37 | /// Constructs a new, empty `IpNetworkTable`. 38 | pub fn new() -> Self { 39 | Self::with_capacity(0, 0) 40 | } 41 | 42 | /// Constructs a new, empty `IpNetworkTable` 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>(&mut self, network: N, data: T) -> Option { 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>(&mut self, network: N) -> Option { 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>(&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>(&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>(&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>(&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>( 289 | &self, 290 | ip: I, 291 | ) -> Box + '_> { 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 { 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 { 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>( 337 | &mut self, 338 | ip: I, 339 | ) -> Box + '_> { 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 { 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 { 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 { 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 { 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 { 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 { 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(&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)] 491 | mod 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 | } 542 | --------------------------------------------------------------------------------