From 1f07bfefd5f96bc81ad3ce85a983e2c476eb11cf Mon Sep 17 00:00:00 2001 From: Yilin Chen Date: Fri, 16 Jan 2026 23:03:37 +0800 Subject: [PATCH] Support ipnetwork Signed-off-by: Yilin Chen --- .github/workflows/ci.yml | 18 +- .gitignore | 3 +- Cargo.lock | 169 +++++ Cargo.toml | 6 +- src/ipnet.rs | 1167 ++++++++++++++++++++++++++++++++++ src/ipnetwork.rs | 1192 +++++++++++++++++++++++++++++++++++ src/lib.rs | 1278 ++------------------------------------ 7 files changed, 2597 insertions(+), 1236 deletions(-) create mode 100644 Cargo.lock create mode 100644 src/ipnet.rs create mode 100644 src/ipnetwork.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61ae361..6a43b95 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,16 +10,18 @@ env: CARGO_TERM_COLOR: always jobs: - build: - + stable: runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - run: cargo build --verbose --all-features + - run: cargo test --verbose --all-features + msrv: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: 1.31 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@1.48.0 - run: cargo build --verbose - - run: cargo build --verbose --all-features - run: cargo test --verbose - - run: cargo test --verbose --all-features diff --git a/.gitignore b/.gitignore index 3f57546..ebfc0cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ /target/ **/*.rs.bk -Cargo.lock .vscode .idea -.DS_Store \ No newline at end of file +.DS_Store diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9cb48da --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,169 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "ipnetwork" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf370abdafd54d13e54a620e8c3e1145f28e46cc9d704bc6d94414559df41763" + +[[package]] +name = "iprange" +version = "0.6.7" +dependencies = [ + "bincode", + "ipnet", + "ipnetwork", + "rand 0.3.23", + "serde", +] + +[[package]] +name = "libc" +version = "0.2.135" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c" + +[[package]] +name = "proc-macro2" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2415488199887523e74fd9a5f7be804dfd42d868ae0eca382e3917094d210e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 3e5e018..2d15d2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,9 +10,13 @@ categories = ["network-programming"] license = "MIT" [dependencies] -ipnet = "2.0.0" +ipnet = { version = "2", optional = true } +ipnetwork = { version = "0.21", optional = true } serde = { version = "1", features = ["derive"], optional = true } +[features] +default = ["ipnet"] + [dev-dependencies] rand = "0.3.17" bincode = "1" diff --git a/src/ipnet.rs b/src/ipnet.rs new file mode 100644 index 0000000..4652471 --- /dev/null +++ b/src/ipnet.rs @@ -0,0 +1,1167 @@ +use super::{IpNet, IpTrieNode, ToNetwork, TraverseState, MSO_U128, MSO_U32}; +use ipnet::{Ipv4Net, Ipv6Net}; +use std::net::{Ipv4Addr, Ipv6Addr}; + +impl IpNet for Ipv4Net { + type S = Ipv4TraverseState; + type I = Ipv4PrefixBitIterator; + + #[inline] + fn prefix_bits(&self) -> Self::I { + let prefix: u32 = self.addr().into(); + Ipv4PrefixBitIterator { + prefix, + prefix_len: self.prefix_len(), + } + } + + #[inline] + fn prefix_len(&self) -> u8 { + self.prefix_len() + } + + #[inline] + fn with_new_prefix(&self, len: u8) -> Self { + Ipv4Net::new(self.addr(), len).unwrap().trunc() + } +} + +impl ToNetwork for Ipv4Net { + #[inline] + fn to_network(&self) -> Ipv4Net { + self.trunc() + } +} + +impl ToNetwork for Ipv4Addr { + #[inline] + fn to_network(&self) -> Ipv4Net { + Ipv4Net::new(*self, 32).unwrap() + } +} + +impl ToNetwork for u32 { + #[inline] + fn to_network(&self) -> Ipv4Net { + Ipv4Net::new((*self).into(), 32).unwrap() + } +} + +impl ToNetwork for [u8; 4] { + #[inline] + fn to_network(&self) -> Ipv4Net { + Ipv4Net::new((*self).into(), 32).unwrap() + } +} + +#[doc(hidden)] +pub struct Ipv4TraverseState { + node: *const IpTrieNode, + prefix: u32, + prefix_len: u8, +} + +impl TraverseState for Ipv4TraverseState { + type Net = Ipv4Net; + + #[inline] + fn node(&self) -> *const IpTrieNode { + self.node + } + + #[inline] + fn init(root: &IpTrieNode) -> Self { + Ipv4TraverseState { + node: root, + prefix: 0, + prefix_len: 0, + } + } + + #[inline] + fn transit(&self, next_node: &IpTrieNode, current_bit: bool) -> Self { + let mask = if current_bit { + MSO_U32 >> self.prefix_len + } else { + 0 + }; + Ipv4TraverseState { + node: next_node, + prefix: self.prefix | mask, + prefix_len: self.prefix_len + 1, + } + } + + #[inline] + fn build(&self) -> Self::Net { + Ipv4Net::new(self.prefix.into(), self.prefix_len as u8).unwrap() + } +} + +#[doc(hidden)] +pub struct Ipv4PrefixBitIterator { + prefix: u32, + prefix_len: u8, +} + +impl Iterator for Ipv4PrefixBitIterator { + type Item = bool; + + #[inline] + fn next(&mut self) -> Option { + if self.prefix_len > 0 { + let prefix = self.prefix; + self.prefix <<= 1; + self.prefix_len -= 1; + Some(prefix & MSO_U32 != 0) + } else { + None + } + } +} + +impl IpNet for Ipv6Net { + type S = Ipv6TraverseState; + type I = Ipv6PrefixBitIterator; + + #[inline] + fn prefix_bits(&self) -> Self::I { + Ipv6PrefixBitIterator { + prefix: self.addr().into(), + prefix_len: self.prefix_len(), + } + } + + #[inline] + fn prefix_len(&self) -> u8 { + self.prefix_len() + } + + #[inline] + fn with_new_prefix(&self, len: u8) -> Self { + Ipv6Net::new(self.addr(), len).unwrap().trunc() + } +} + +impl ToNetwork for Ipv6Net { + #[inline] + fn to_network(&self) -> Ipv6Net { + self.trunc() + } +} + +impl ToNetwork for Ipv6Addr { + #[inline] + fn to_network(&self) -> Ipv6Net { + Ipv6Net::new(*self, 128).unwrap() + } +} + +impl ToNetwork for u128 { + #[inline] + fn to_network(&self) -> Ipv6Net { + Ipv6Net::new((*self).into(), 128).unwrap() + } +} + +impl ToNetwork for [u8; 16] { + #[inline] + fn to_network(&self) -> Ipv6Net { + Ipv6Net::new((*self).into(), 128).unwrap() + } +} + +impl ToNetwork for [u16; 8] { + #[inline] + fn to_network(&self) -> Ipv6Net { + Ipv6Net::new((*self).into(), 128).unwrap() + } +} + +#[doc(hidden)] +pub struct Ipv6TraverseState { + node: *const IpTrieNode, + prefix: u128, + prefix_len: u8, +} + +impl TraverseState for Ipv6TraverseState { + type Net = Ipv6Net; + + #[inline] + fn node(&self) -> *const IpTrieNode { + self.node + } + + #[inline] + fn init(root: &IpTrieNode) -> Self { + Ipv6TraverseState { + node: root, + prefix: 0, + prefix_len: 0, + } + } + + #[inline] + fn transit(&self, next_node: &IpTrieNode, current_bit: bool) -> Self { + let mask = if current_bit { + MSO_U128 >> self.prefix_len + } else { + 0 + }; + Ipv6TraverseState { + node: next_node, + prefix: self.prefix | mask, + prefix_len: self.prefix_len + 1, + } + } + + #[inline] + fn build(&self) -> Self::Net { + Ipv6Net::new(self.prefix.into(), self.prefix_len as u8).unwrap() + } +} + +#[doc(hidden)] +pub struct Ipv6PrefixBitIterator { + prefix: u128, + prefix_len: u8, +} + +impl Iterator for Ipv6PrefixBitIterator { + type Item = bool; + + #[inline] + fn next(&mut self) -> Option { + if self.prefix_len > 0 { + let prefix = self.prefix; + self.prefix <<= 1; + self.prefix_len -= 1; + Some(prefix & MSO_U128 != 0) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::IpRange; + + use super::*; + + #[test] + fn parse_invalid_networks() { + assert!("192.168.256.130/5".parse::().is_err()); + assert!("192.168.5.130/-1".parse::().is_err()); + assert!("192.168.5.130/33".parse::().is_err()); + assert!("192.168.5.33".parse::().is_err()); + assert!("192.168.5.130/0.0.0".parse::().is_err()); + assert!("192.168.5.130/0.0.0.256".parse::().is_err()); + } + + impl IpRange { + fn get_network(&self, prefix_size: usize, prefix: &str) -> Option { + self.trie + .search(format!("{}/{}", prefix, prefix_size).parse().unwrap()) + } + } + + #[test] + fn add_single_network() { + let mut ip_range = IpRange::new(); + let network = "192.168.5.0/24".parse().unwrap(); + ip_range.add(network); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network), ip_range.get_network(24, "192.168.5.0")); + } + + #[test] + fn add_multiple_networks_disjoint() { + let mut ip_range = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range + .add(network1) + .add(network2) + .add(network3) + .add(network4) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 4); + assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); + assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); + } + + #[test] + fn simplify() { + let mut ip_range = IpRange::new(); + ip_range + .add("192.168.0.0/20".parse().unwrap()) + .add("192.168.16.0/22".parse().unwrap()) + .add("192.168.20.0/24".parse().unwrap()) + .add("192.168.21.0/24".parse().unwrap()) + .add("192.168.22.0/24".parse().unwrap()) + .add("192.168.23.0/24".parse().unwrap()) + .add("192.168.24.0/21".parse().unwrap()) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!( + "192.168.0.0/19".parse().ok(), + ip_range.get_network(19, "192.168.0.0") + ); + } + + #[test] + fn add_multiple_networks_joint1() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn add_multiple_networks_joint2() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn add_multiple_networks_joint3() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range.add(network2).add(network1).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn add_multiple_networks_joint4() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.5.0/24".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(24, "172.16.5.0")); + } + + #[test] + fn add_multiple_networks_joint5() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + } + + #[test] + fn add_multiple_networks_joint6() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "0.0.0.0/0".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(0, "0.0.0.0")); + } + + #[test] + fn remove_networks_no_split() { + let mut ip_range = IpRange::new(); + let network1 = "192.168.0.0/24".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + ip_range.remove(network1); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + } + + #[test] + fn remove_networks_split1() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/22".parse().unwrap()); + ip_range + .remove("192.168.2.0/23".parse().unwrap()) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!( + Some("192.168.0.0/23".parse().unwrap()), + ip_range.get_network(23, "192.168.0.0") + ); + } + + #[test] + fn remove_networks_split2() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/22".parse().unwrap()); + ip_range + .remove("192.168.0.0/23".parse().unwrap()) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!( + Some("192.168.2.0/23".parse().unwrap()), + ip_range.get_network(23, "192.168.2.0") + ); + } + + #[test] + fn remove_networks_split3() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/22".parse().unwrap()); + ip_range + .remove("192.168.2.0/25".parse().unwrap()) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 3); + assert_eq!( + Some("192.168.0.0/23".parse().unwrap()), + ip_range.get_network(23, "192.168.0.0") + ); + assert_eq!( + Some("192.168.2.128/25".parse().unwrap()), + ip_range.get_network(25, "192.168.2.128") + ); + assert_eq!( + Some("192.168.3.0/24".parse().unwrap()), + ip_range.get_network(24, "192.168.3.0") + ); + } + + impl IpRange { + fn contains_ip(&self, ip: &str) -> bool { + self.contains(&ip.parse::().unwrap()) + } + + fn find_network_by_ip(&self, ip: &str) -> Option { + self.supernet(&ip.parse::().unwrap()) + } + + fn contains_network(&self, network: &str) -> bool { + self.contains(&network.parse::().unwrap()) + } + + fn super_network_by_network(&self, network: &str) -> Option { + self.supernet(&network.parse::().unwrap()) + } + } + + #[test] + fn contains_ip_with_one_network() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/24".parse().unwrap()); + + assert!(ip_range.contains_ip("192.168.0.0")); + assert!(ip_range.contains_ip("192.168.0.128")); + assert!(ip_range.contains_ip("192.168.0.255")); + assert!(!ip_range.contains_ip("192.167.255.255")); + assert!(!ip_range.contains_ip("192.168.1.0")); + } + + #[test] + fn contains_ip_with_many_networks() { + let mut ip_range = IpRange::new(); + ip_range + .add("192.168.0.0/24".parse().unwrap()) + .add("172.16.0.0/16".parse().unwrap()) + .add("10.0.0.0/8".parse().unwrap()) + .simplify(); + + assert!(ip_range.contains_ip("192.168.0.128")); + assert!(ip_range.contains_ip("172.16.32.1")); + assert!(ip_range.contains_ip("10.10.10.10")); + assert!(!ip_range.contains_ip("0.0.0.0")); + assert!(!ip_range.contains_ip("8.8.8.8")); + assert!(!ip_range.contains_ip("11.0.0.0")); + assert!(!ip_range.contains_ip("192.167.255.255")); + assert!(!ip_range.contains_ip("255.255.255.255")); + } + + #[test] + fn contains_ip_boundary1() { + let mut ip_range = IpRange::new(); + ip_range.add("0.0.0.0/0".parse().unwrap()); + + assert!(ip_range.contains_ip("0.0.0.0")); + assert!(ip_range.contains_ip("8.8.8.8")); + assert!(ip_range.contains_ip("192.168.0.0")); + assert!(ip_range.contains_ip("192.168.1.1")); + } + + #[test] + fn contains_ip_boundary2() { + let mut ip_range = IpRange::new(); + ip_range.add("254.254.254.254/32".parse().unwrap()); + + assert!(!ip_range.contains_ip("0.0.0.0")); + assert!(!ip_range.contains_ip("8.8.8.8")); + assert!(!ip_range.contains_ip("192.168.0.0")); + assert!(ip_range.contains_ip("254.254.254.254")); + } + + #[test] + fn find_network_with_one_network() { + let mut ip_range = IpRange::new(); + let network = "192.168.0.0/24".parse().unwrap(); + ip_range.add(network); + + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.0")); + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.128")); + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.255")); + assert_eq!(None, ip_range.find_network_by_ip("192.167.255.255")); + assert_eq!(None, ip_range.find_network_by_ip("192.168.1.0")); + } + + #[test] + fn find_network_with_many_networks() { + let mut ip_range = IpRange::new(); + let network1 = "192.168.0.0/24".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "10.0.0.0/8".parse().unwrap(); + ip_range + .add(network1) + .add(network2) + .add(network3) + .simplify(); + + assert_eq!(Some(network1), ip_range.find_network_by_ip("192.168.0.128")); + assert_eq!(Some(network2), ip_range.find_network_by_ip("172.16.32.1")); + assert_eq!(Some(network3), ip_range.find_network_by_ip("10.10.10.10")); + assert_eq!(None, ip_range.find_network_by_ip("0.0.0.0")); + assert_eq!(None, ip_range.find_network_by_ip("8.8.8.8")); + assert_eq!(None, ip_range.find_network_by_ip("11.0.0.0")); + assert_eq!(None, ip_range.find_network_by_ip("192.167.255.255")); + assert_eq!(None, ip_range.find_network_by_ip("255.255.255.255")); + } + + #[test] + fn find_network_boundary1() { + let mut ip_range = IpRange::new(); + let network = "0.0.0.0/0".parse().unwrap(); + ip_range.add(network); + + assert_eq!(Some(network), ip_range.find_network_by_ip("0.0.0.0")); + assert_eq!(Some(network), ip_range.find_network_by_ip("8.8.8.8")); + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.0")); + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.1.1")); + } + + #[test] + fn find_network_boundary2() { + let mut ip_range = IpRange::new(); + let network = "254.254.254.254/32".parse().unwrap(); + ip_range.add(network); + + assert_eq!(None, ip_range.find_network_by_ip("0.0.0.0")); + assert_eq!(None, ip_range.find_network_by_ip("8.8.8.8")); + assert_eq!(None, ip_range.find_network_by_ip("192.168.0.0")); + assert_eq!( + Some(network), + ip_range.find_network_by_ip("254.254.254.254") + ); + } + + #[test] + fn contains_network_with_one_network() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/24".parse().unwrap()); + + assert!(ip_range.contains_network("192.168.0.0/24")); + assert!(ip_range.contains_network("192.168.0.128/25")); + assert!(!ip_range.contains_network("192.168.0.0/23")); + assert!(!ip_range.contains_network("192.168.1.0/24")); + assert!(!ip_range.contains_network("192.167.0.0/24")); + } + + #[test] + fn contains_network_with_many_networks() { + let mut ip_range = IpRange::new(); + ip_range + .add("192.168.0.0/24".parse().unwrap()) + .add("172.16.0.0/16".parse().unwrap()) + .add("10.0.0.0/8".parse().unwrap()) + .simplify(); + + assert!(ip_range.contains_network("192.168.0.128/25")); + assert!(ip_range.contains_network("172.16.32.0/20")); + assert!(ip_range.contains_network("10.10.0.0/16")); + assert!(!ip_range.contains_network("0.0.0.0/0")); + assert!(!ip_range.contains_network("8.0.0.0/6")); + assert!(!ip_range.contains_network("8.0.0.0/7")); + assert!(!ip_range.contains_network("11.0.0.0/9")); + assert!(!ip_range.contains_network("192.167.255.255/32")); + assert!(!ip_range.contains_network("255.0.0.0/8")); + } + + #[test] + fn contains_network_boundary1() { + let mut ip_range = IpRange::new(); + ip_range.add("0.0.0.0/0".parse().unwrap()); + + assert!(ip_range.contains_network("0.0.0.0/0")); + assert!(ip_range.contains_network("8.0.0.0/6")); + assert!(ip_range.contains_network("11.0.0.0/9")); + assert!(ip_range.contains_network("192.168.0.128/25")); + assert!(ip_range.contains_network("255.255.255.255/32")); + } + + #[test] + fn contains_network_boundary2() { + let mut ip_range = IpRange::new(); + ip_range.add("254.254.254.254/32".parse().unwrap()); + + assert!(!ip_range.contains_network("0.0.0.0/0")); + assert!(!ip_range.contains_network("8.0.0.0/6")); + assert!(!ip_range.contains_network("254.254.0.0/16")); + assert!(ip_range.contains_network("254.254.254.254/32")); + assert!(!ip_range.contains_network("255.255.255.255/32")); + } + + #[test] + fn super_network_with_one_network() { + let mut ip_range = IpRange::new(); + let network = "192.168.0.0/24".parse().unwrap(); + ip_range.add(network); + + assert_eq!( + Some(network), + ip_range.super_network_by_network("192.168.0.0/24") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("192.168.0.128/25") + ); + assert_eq!(None, ip_range.super_network_by_network("192.168.0.0/23")); + assert_eq!(None, ip_range.super_network_by_network("192.168.1.0/24")); + assert_eq!(None, ip_range.super_network_by_network("192.167.0.0/24")); + } + + #[test] + fn super_network_with_many_networks() { + let mut ip_range = IpRange::new(); + let network1 = "192.168.0.0/24".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "10.0.0.0/8".parse().unwrap(); + ip_range + .add(network1) + .add(network2) + .add(network3) + .simplify(); + + assert_eq!( + Some(network1), + ip_range.super_network_by_network("192.168.0.128/25") + ); + assert_eq!( + Some(network2), + ip_range.super_network_by_network("172.16.32.0/20") + ); + assert_eq!( + Some(network3), + ip_range.super_network_by_network("10.10.0.0/16") + ); + assert_eq!(None, ip_range.super_network_by_network("0.0.0.0/0")); + assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/6")); + assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/7")); + assert_eq!(None, ip_range.super_network_by_network("11.0.0.0/9")); + assert_eq!( + None, + ip_range.super_network_by_network("192.167.255.255/32") + ); + assert_eq!(None, ip_range.super_network_by_network("255.0.0.0/8")); + } + + #[test] + fn super_network_boundary1() { + let mut ip_range = IpRange::new(); + let network = "0.0.0.0/0".parse().unwrap(); + ip_range.add(network); + + assert_eq!( + Some(network), + ip_range.super_network_by_network("0.0.0.0/0") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("8.0.0.0/6") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("11.0.0.0/9") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("192.168.0.128/25") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("255.255.255.255/32") + ); + } + + #[test] + fn super_network_boundary2() { + let mut ip_range = IpRange::new(); + let network = "254.254.254.254/32".parse().unwrap(); + ip_range.add(network); + + assert_eq!(None, ip_range.super_network_by_network("0.0.0.0/0")); + assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/6")); + assert_eq!(None, ip_range.super_network_by_network("254.254.0.0/16")); + assert_eq!( + Some(network), + ip_range.super_network_by_network("254.254.254.254/32") + ); + assert_eq!( + None, + ip_range.super_network_by_network("255.255.255.255/32") + ); + } + + #[test] + fn merge_empty1() { + let ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range2 + .add(network1) + .add(network2) + .add(network3) + .add(network4) + .simplify(); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 4); + assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); + assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); + } + + #[test] + fn merge_empty2() { + let mut ip_range1 = IpRange::new(); + let ip_range2 = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range1 + .add(network1) + .add(network2) + .add(network3) + .add(network4) + .simplify(); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 4); + assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); + assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); + } + + #[test] + fn merge_disjoint() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 4); + assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); + assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); + } + + #[test] + fn merge_joint1() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn merge_joint2() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn merge_sequent1() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.5.0/24".parse().unwrap(); + let network3 = "172.16.6.0/24".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + ip_range2.add(network3); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 2); + assert_eq!( + "172.16.4.0/23".parse().ok(), + ip_range.get_network(23, "172.16.4.0") + ); + assert_eq!( + "172.16.6.0/24".parse().ok(), + ip_range.get_network(24, "172.16.6.0") + ); + } + + #[test] + fn merge_sequent2() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let mut ip_range3 = IpRange::new(); + ip_range1 + .add("192.168.0.0/20".parse().unwrap()) + .add("192.168.24.0/21".parse().unwrap()); + ip_range2 + .add("192.168.16.0/22".parse().unwrap()) + .add("192.168.23.0/24".parse().unwrap()); + ip_range3 + .add("192.168.20.0/24".parse().unwrap()) + .add("192.168.21.0/24".parse().unwrap()) + .add("192.168.22.0/24".parse().unwrap()); + + let ip_range = ip_range1.merge(&ip_range2); + let ip_range = ip_range.merge(&ip_range3); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!( + "192.168.0.0/19".parse().ok(), + ip_range.get_network(19, "192.168.0.0") + ); + } + + #[test] + fn intersect_disjoint() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Net = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 0); + } + + #[test] + fn intersect_joint1() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network1), ip_range.get_network(24, "172.16.4.0")); + } + + #[test] + fn intersect_joint2() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network1), ip_range.get_network(24, "172.16.5.0")); + } + + #[test] + fn intersect_joint3() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.5.0/24".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network1), ip_range.get_network(24, "172.16.5.0")); + } + + #[test] + fn intersect_joint4() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "192.168.0.0/24".parse().unwrap(); + let network3 = "10.10.0.0/16".parse().unwrap(); + let network4 = "10.254.0.0/17".parse().unwrap(); + let network5 = "192.168.0.0/16".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4).add(network5); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 3); + assert_eq!(Some(network3), ip_range.get_network(16, "10.10.0.0")); + assert_eq!(Some(network4), ip_range.get_network(17, "10.254.0.0")); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.0.0")); + } + + #[test] + fn exclude_disjoint() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Net = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range1, ip_range); + } + + #[test] + fn exclude_larger() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Net = "172.16.4.0/24".parse().unwrap(); + let network2 = "192.168.1.0/24".parse().unwrap(); + let network3 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); + } + + #[test] + fn exclude_identical() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Net = "172.16.5.0/24".parse().unwrap(); + let network2 = "192.168.1.0/24".parse().unwrap(); + let network3 = "172.16.4.0/22".parse().unwrap(); + let network4 = "10.0.0.0/8".parse().unwrap(); + + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); + } + + #[test] + fn exclude_split1() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Net = "172.16.4.0/22".parse().unwrap(); + let network2 = "192.168.1.0/24".parse().unwrap(); + let network3 = "172.16.5.0/24".parse().unwrap(); + let network4 = "10.0.0.0/8".parse().unwrap(); + + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 3); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); + assert_eq!( + "172.16.4.0/24".parse().ok(), + ip_range.get_network(24, "172.16.4.0") + ); + assert_eq!( + "172.16.6.0/23".parse().ok(), + ip_range.get_network(23, "172.16.6.0") + ); + } + + #[test] + fn exclude_split2() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Net = "172.16.4.0/22".parse().unwrap(); + let network2 = "192.168.1.0/24".parse().unwrap(); + let network3 = "172.16.4.0/24".parse().unwrap(); + let network4 = "10.0.0.0/8".parse().unwrap(); + + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 3); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); + assert_eq!( + "172.16.5.0/24".parse().ok(), + ip_range.get_network(24, "172.16.5.0") + ); + assert_eq!( + "172.16.6.0/23".parse().ok(), + ip_range.get_network(23, "172.16.6.0") + ); + } + + #[test] + fn iter_ipv4() { + let mut data = vec!["1.0.1.0/24", "1.0.2.0/23", "1.0.8.0/21"]; + let ip_range: IpRange = data.iter().map(|net| net.parse().unwrap()).collect(); + let mut nets: Vec = ip_range.iter().map(|net| format!("{}", net)).collect(); + data.sort_unstable(); + nets.sort_unstable(); + assert_eq!(nets, data); + } + + #[test] + fn iter_ipv6() { + let mut data = vec![ + "2400:9a40::/32", + "2400:9dc0::/32", + "2400:9e00::/32", + "2400:a040::/32", + ]; + let ip_range: IpRange = data.iter().map(|net| net.parse().unwrap()).collect(); + let mut nets: Vec = ip_range.iter().map(|net| format!("{}", net)).collect(); + data.sort_unstable(); + nets.sort_unstable(); + assert_eq!(nets, data); + } + + #[test] + fn debug_fmt() { + let ip_range: IpRange = IpRange::default(); + assert_eq!(format!("{:?}", ip_range), "IpRange []"); + + let ip_range: IpRange = ["1.0.1.0/24", "1.0.2.0/23", "1.0.8.0/21"] + .iter() + .map(|net| net.parse().unwrap()) + .collect(); + assert_eq!( + format!("{:?}", ip_range), + "IpRange [1.0.8.0/21, 1.0.2.0/23, 1.0.1.0/24]" + ); + + let ip_range: IpRange = [ + "192.168.0.0/16", + "1.0.2.0/23", + "1.0.8.0/21", + "127.0.0.0/8", + "172.16.0.0/12", + ] + .iter() + .map(|net| net.parse().unwrap()) + .collect(); + assert_eq!( + format!("{:?}", ip_range), + "IpRange [127.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ...]" + ); + + let ip_range: IpRange = [ + "2001:4438::/32", + "2001:4510::/29", + "2400:1040::/32", + "2400:12c0::/32", + "2400:1340::/32", + "2400:1380::/32", + "2400:15c0::/32", + ] + .iter() + .map(|net| net.parse().unwrap()) + .collect(); + assert_eq!( + format!("{:?}", ip_range), + "IpRange [2001:4510::/29, 2001:4438::/32, 2400:1040::/32, ...]" + ); + } + + #[test] + fn remove_all() { + let mut ip_range = IpRange::new(); + let network: Ipv4Net = "1.0.1.0/24".parse().unwrap(); + ip_range.add(network); + ip_range.remove(network); + assert!(ip_range.iter().next().is_none()); + } + + #[test] + fn add_to_all_zeros() { + let mut ip_range: IpRange = IpRange::new(); + ip_range.add("0.0.0.0/0".parse().unwrap()); + ip_range.add("127.0.0.1/32".parse().unwrap()); + assert!(ip_range.contains_network("0.0.0.0/0")); + } + + #[test] + #[cfg(feature = "serde")] + fn serialize_ipv4_as_binary() { + let mut ip_range: IpRange = IpRange::new(); + ip_range.add("0.0.0.0/0".parse().unwrap()); + ip_range.add("127.0.0.1/32".parse().unwrap()); + ip_range.add("254.254.254.254/32".parse().unwrap()); + let encoded: Vec = bincode::serialize(&ip_range).unwrap(); + let decoded_ip_range: IpRange = bincode::deserialize(&encoded[..]).unwrap(); + assert_eq!(ip_range, decoded_ip_range); + } + + #[test] + #[cfg(feature = "serde")] + fn serialize_ipv6_as_binary() { + let mut ip_range: IpRange = IpRange::new(); + ip_range.add("2001:4438::/32".parse().unwrap()); + ip_range.add("2400:1040::/32".parse().unwrap()); + ip_range.add("2400:1340::/32".parse().unwrap()); + let encoded: Vec = bincode::serialize(&ip_range).unwrap(); + let decoded_ip_range: IpRange = bincode::deserialize(&encoded[..]).unwrap(); + assert_eq!(ip_range, decoded_ip_range); + } +} diff --git a/src/ipnetwork.rs b/src/ipnetwork.rs new file mode 100644 index 0000000..4920613 --- /dev/null +++ b/src/ipnetwork.rs @@ -0,0 +1,1192 @@ +use super::{IpNet, IpTrieNode, ToNetwork, TraverseState, MSO_U128, MSO_U32}; +use ipnetwork::{Ipv4Network, Ipv6Network}; +use std::net::{Ipv4Addr, Ipv6Addr}; + +fn trunc_ipv4(addr: Ipv4Addr, prefix: u8) -> Ipv4Addr { + let v: u32 = addr.into(); + let mask = if prefix == 0 { + 0u32 + } else { + (!0u32) << (32 - prefix as u32) + }; + (v & mask).into() +} + +fn trunc_ipv6(addr: Ipv6Addr, prefix: u8) -> Ipv6Addr { + let v: u128 = addr.into(); + let mask = if prefix == 0 { + 0u128 + } else { + (!0u128) << (128 - prefix as u32) + }; + (v & mask).into() +} + +impl IpNet for Ipv4Network { + type S = Ipv4TraverseState; + type I = Ipv4PrefixBitIterator; + + #[inline] + fn prefix_bits(&self) -> Self::I { + let prefix: u32 = self.ip().into(); + Ipv4PrefixBitIterator { + prefix, + prefix_len: self.prefix(), + } + } + + #[inline] + fn prefix_len(&self) -> u8 { + self.prefix() + } + + #[inline] + fn with_new_prefix(&self, len: u8) -> Self { + let ip = trunc_ipv4(self.ip(), len); + Ipv4Network::new(ip, len).unwrap() + } +} + +impl ToNetwork for Ipv4Network { + #[inline] + fn to_network(&self) -> Ipv4Network { + let len = self.prefix(); + Ipv4Network::new(trunc_ipv4(self.ip(), len), len).unwrap() + } +} + +impl ToNetwork for Ipv4Addr { + #[inline] + fn to_network(&self) -> Ipv4Network { + Ipv4Network::new(*self, 32).unwrap() + } +} + +impl ToNetwork for u32 { + #[inline] + fn to_network(&self) -> Ipv4Network { + Ipv4Network::new((*self).into(), 32).unwrap() + } +} + +impl ToNetwork for [u8; 4] { + #[inline] + fn to_network(&self) -> Ipv4Network { + Ipv4Network::new((*self).into(), 32).unwrap() + } +} + +#[doc(hidden)] +pub struct Ipv4TraverseState { + node: *const IpTrieNode, + prefix: u32, + prefix_len: u8, +} + +impl TraverseState for Ipv4TraverseState { + type Net = Ipv4Network; + + #[inline] + fn node(&self) -> *const IpTrieNode { + self.node + } + + #[inline] + fn init(root: &IpTrieNode) -> Self { + Ipv4TraverseState { + node: root, + prefix: 0, + prefix_len: 0, + } + } + + #[inline] + fn transit(&self, next_node: &IpTrieNode, current_bit: bool) -> Self { + let mask = if current_bit { + MSO_U32 >> self.prefix_len + } else { + 0 + }; + Ipv4TraverseState { + node: next_node, + prefix: self.prefix | mask, + prefix_len: self.prefix_len + 1, + } + } + + #[inline] + fn build(&self) -> Self::Net { + Ipv4Network::new(self.prefix.into(), self.prefix_len).unwrap() + } +} + +#[doc(hidden)] +pub struct Ipv4PrefixBitIterator { + prefix: u32, + prefix_len: u8, +} + +impl Iterator for Ipv4PrefixBitIterator { + type Item = bool; + + #[inline] + fn next(&mut self) -> Option { + if self.prefix_len > 0 { + let prefix = self.prefix; + self.prefix <<= 1; + self.prefix_len -= 1; + Some(prefix & MSO_U32 != 0) + } else { + None + } + } +} + +impl IpNet for Ipv6Network { + type S = Ipv6TraverseState; + type I = Ipv6PrefixBitIterator; + + #[inline] + fn prefix_bits(&self) -> Self::I { + Ipv6PrefixBitIterator { + prefix: self.ip().into(), + prefix_len: self.prefix(), + } + } + + #[inline] + fn prefix_len(&self) -> u8 { + self.prefix() + } + + #[inline] + fn with_new_prefix(&self, len: u8) -> Self { + let ip = trunc_ipv6(self.ip(), len); + Ipv6Network::new(ip, len).unwrap() + } +} + +impl ToNetwork for Ipv6Network { + #[inline] + fn to_network(&self) -> Ipv6Network { + let len = self.prefix(); + Ipv6Network::new(trunc_ipv6(self.ip(), len), len).unwrap() + } +} + +impl ToNetwork for Ipv6Addr { + #[inline] + fn to_network(&self) -> Ipv6Network { + Ipv6Network::new(*self, 128).unwrap() + } +} + +impl ToNetwork for u128 { + #[inline] + fn to_network(&self) -> Ipv6Network { + Ipv6Network::new((*self).into(), 128).unwrap() + } +} + +impl ToNetwork for [u8; 16] { + #[inline] + fn to_network(&self) -> Ipv6Network { + Ipv6Network::new((*self).into(), 128).unwrap() + } +} + +impl ToNetwork for [u16; 8] { + #[inline] + fn to_network(&self) -> Ipv6Network { + Ipv6Network::new((*self).into(), 128).unwrap() + } +} + +#[doc(hidden)] +pub struct Ipv6TraverseState { + node: *const IpTrieNode, + prefix: u128, + prefix_len: u8, +} + +impl TraverseState for Ipv6TraverseState { + type Net = Ipv6Network; + + #[inline] + fn node(&self) -> *const IpTrieNode { + self.node + } + + #[inline] + fn init(root: &IpTrieNode) -> Self { + Ipv6TraverseState { + node: root, + prefix: 0, + prefix_len: 0, + } + } + + #[inline] + fn transit(&self, next_node: &IpTrieNode, current_bit: bool) -> Self { + let mask = if current_bit { + MSO_U128 >> self.prefix_len + } else { + 0 + }; + Ipv6TraverseState { + node: next_node, + prefix: self.prefix | mask, + prefix_len: self.prefix_len + 1, + } + } + + #[inline] + fn build(&self) -> Self::Net { + Ipv6Network::new(self.prefix.into(), self.prefix_len).unwrap() + } +} + +#[doc(hidden)] +pub struct Ipv6PrefixBitIterator { + prefix: u128, + prefix_len: u8, +} + +impl Iterator for Ipv6PrefixBitIterator { + type Item = bool; + + #[inline] + fn next(&mut self) -> Option { + if self.prefix_len > 0 { + let prefix = self.prefix; + self.prefix <<= 1; + self.prefix_len -= 1; + Some(prefix & MSO_U128 != 0) + } else { + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::IpRange; + + use super::*; + + #[test] + fn parse_invalid_networks() { + assert!("192.168.256.130/5".parse::().is_err()); + assert!("192.168.5.130/-1".parse::().is_err()); + assert!("192.168.5.130/33".parse::().is_err()); + // Note: ipnetwork allows parsing IP without prefix (defaults to /32) + // assert!("192.168.5.33".parse::().is_err()); + assert!("192.168.5.130/0.0.0".parse::().is_err()); + assert!("192.168.5.130/0.0.0.256".parse::().is_err()); + } + + impl IpRange { + fn get_network(&self, prefix_size: usize, prefix: &str) -> Option { + self.trie + .search(format!("{}/{}", prefix, prefix_size).parse().unwrap()) + } + } + + #[test] + fn add_single_network() { + let mut ip_range = IpRange::new(); + let network = "192.168.5.0/24".parse().unwrap(); + ip_range.add(network); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network), ip_range.get_network(24, "192.168.5.0")); + } + + #[test] + fn add_multiple_networks_disjoint() { + let mut ip_range = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range + .add(network1) + .add(network2) + .add(network3) + .add(network4) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 4); + assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); + assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); + } + + #[test] + fn simplify() { + let mut ip_range = IpRange::new(); + ip_range + .add("192.168.0.0/20".parse().unwrap()) + .add("192.168.16.0/22".parse().unwrap()) + .add("192.168.20.0/24".parse().unwrap()) + .add("192.168.21.0/24".parse().unwrap()) + .add("192.168.22.0/24".parse().unwrap()) + .add("192.168.23.0/24".parse().unwrap()) + .add("192.168.24.0/21".parse().unwrap()) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!( + "192.168.0.0/19".parse().ok(), + ip_range.get_network(19, "192.168.0.0") + ); + } + + #[test] + fn add_multiple_networks_joint1() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn add_multiple_networks_joint2() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn add_multiple_networks_joint3() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range.add(network2).add(network1).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn add_multiple_networks_joint4() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.5.0/24".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(24, "172.16.5.0")); + } + + #[test] + fn add_multiple_networks_joint5() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + } + + #[test] + fn add_multiple_networks_joint6() { + let mut ip_range = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "0.0.0.0/0".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(0, "0.0.0.0")); + } + + #[test] + fn remove_networks_no_split() { + let mut ip_range = IpRange::new(); + let network1 = "192.168.0.0/24".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + ip_range.add(network1).add(network2).simplify(); + + ip_range.remove(network1); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + } + + #[test] + fn remove_networks_split1() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/22".parse().unwrap()); + ip_range + .remove("192.168.2.0/23".parse().unwrap()) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!( + Some("192.168.0.0/23".parse().unwrap()), + ip_range.get_network(23, "192.168.0.0") + ); + } + + #[test] + fn remove_networks_split2() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/22".parse().unwrap()); + ip_range + .remove("192.168.0.0/23".parse().unwrap()) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!( + Some("192.168.2.0/23".parse().unwrap()), + ip_range.get_network(23, "192.168.2.0") + ); + } + + #[test] + fn remove_networks_split3() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/22".parse().unwrap()); + ip_range + .remove("192.168.2.0/25".parse().unwrap()) + .simplify(); + + assert_eq!(ip_range.into_iter().count(), 3); + assert_eq!( + Some("192.168.0.0/23".parse().unwrap()), + ip_range.get_network(23, "192.168.0.0") + ); + assert_eq!( + Some("192.168.2.128/25".parse().unwrap()), + ip_range.get_network(25, "192.168.2.128") + ); + assert_eq!( + Some("192.168.3.0/24".parse().unwrap()), + ip_range.get_network(24, "192.168.3.0") + ); + } + + impl IpRange { + fn contains_ip(&self, ip: &str) -> bool { + self.contains(&ip.parse::().unwrap()) + } + + fn find_network_by_ip(&self, ip: &str) -> Option { + self.supernet(&ip.parse::().unwrap()) + } + + fn contains_network(&self, network: &str) -> bool { + self.contains(&network.parse::().unwrap()) + } + + fn super_network_by_network(&self, network: &str) -> Option { + self.supernet(&network.parse::().unwrap()) + } + } + + #[test] + fn contains_ip_with_one_network() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/24".parse().unwrap()); + + assert!(ip_range.contains_ip("192.168.0.0")); + assert!(ip_range.contains_ip("192.168.0.128")); + assert!(ip_range.contains_ip("192.168.0.255")); + assert!(!ip_range.contains_ip("192.167.255.255")); + assert!(!ip_range.contains_ip("192.168.1.0")); + } + + #[test] + fn contains_ip_with_many_networks() { + let mut ip_range = IpRange::new(); + ip_range + .add("192.168.0.0/24".parse().unwrap()) + .add("172.16.0.0/16".parse().unwrap()) + .add("10.0.0.0/8".parse().unwrap()) + .simplify(); + + assert!(ip_range.contains_ip("192.168.0.128")); + assert!(ip_range.contains_ip("172.16.32.1")); + assert!(ip_range.contains_ip("10.10.10.10")); + assert!(!ip_range.contains_ip("0.0.0.0")); + assert!(!ip_range.contains_ip("8.8.8.8")); + assert!(!ip_range.contains_ip("11.0.0.0")); + assert!(!ip_range.contains_ip("192.167.255.255")); + assert!(!ip_range.contains_ip("255.255.255.255")); + } + + #[test] + fn contains_ip_boundary1() { + let mut ip_range = IpRange::new(); + ip_range.add("0.0.0.0/0".parse().unwrap()); + + assert!(ip_range.contains_ip("0.0.0.0")); + assert!(ip_range.contains_ip("8.8.8.8")); + assert!(ip_range.contains_ip("192.168.0.0")); + assert!(ip_range.contains_ip("192.168.1.1")); + } + + #[test] + fn contains_ip_boundary2() { + let mut ip_range = IpRange::new(); + ip_range.add("254.254.254.254/32".parse().unwrap()); + + assert!(!ip_range.contains_ip("0.0.0.0")); + assert!(!ip_range.contains_ip("8.8.8.8")); + assert!(!ip_range.contains_ip("192.168.0.0")); + assert!(ip_range.contains_ip("254.254.254.254")); + } + + #[test] + fn find_network_with_one_network() { + let mut ip_range = IpRange::new(); + let network = "192.168.0.0/24".parse().unwrap(); + ip_range.add(network); + + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.0")); + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.128")); + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.255")); + assert_eq!(None, ip_range.find_network_by_ip("192.167.255.255")); + assert_eq!(None, ip_range.find_network_by_ip("192.168.1.0")); + } + + #[test] + fn find_network_with_many_networks() { + let mut ip_range = IpRange::new(); + let network1 = "192.168.0.0/24".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "10.0.0.0/8".parse().unwrap(); + ip_range + .add(network1) + .add(network2) + .add(network3) + .simplify(); + + assert_eq!(Some(network1), ip_range.find_network_by_ip("192.168.0.128")); + assert_eq!(Some(network2), ip_range.find_network_by_ip("172.16.32.1")); + assert_eq!(Some(network3), ip_range.find_network_by_ip("10.10.10.10")); + assert_eq!(None, ip_range.find_network_by_ip("0.0.0.0")); + assert_eq!(None, ip_range.find_network_by_ip("8.8.8.8")); + assert_eq!(None, ip_range.find_network_by_ip("11.0.0.0")); + assert_eq!(None, ip_range.find_network_by_ip("192.167.255.255")); + assert_eq!(None, ip_range.find_network_by_ip("255.255.255.255")); + } + + #[test] + fn find_network_boundary1() { + let mut ip_range = IpRange::new(); + let network = "0.0.0.0/0".parse().unwrap(); + ip_range.add(network); + + assert_eq!(Some(network), ip_range.find_network_by_ip("0.0.0.0")); + assert_eq!(Some(network), ip_range.find_network_by_ip("8.8.8.8")); + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.0")); + assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.1.1")); + } + + #[test] + fn find_network_boundary2() { + let mut ip_range = IpRange::new(); + let network = "254.254.254.254/32".parse().unwrap(); + ip_range.add(network); + + assert_eq!(None, ip_range.find_network_by_ip("0.0.0.0")); + assert_eq!(None, ip_range.find_network_by_ip("8.8.8.8")); + assert_eq!(None, ip_range.find_network_by_ip("192.168.0.0")); + assert_eq!( + Some(network), + ip_range.find_network_by_ip("254.254.254.254") + ); + } + + #[test] + fn contains_network_with_one_network() { + let mut ip_range = IpRange::new(); + ip_range.add("192.168.0.0/24".parse().unwrap()); + + assert!(ip_range.contains_network("192.168.0.0/24")); + assert!(ip_range.contains_network("192.168.0.128/25")); + assert!(!ip_range.contains_network("192.168.0.0/23")); + assert!(!ip_range.contains_network("192.168.1.0/24")); + assert!(!ip_range.contains_network("192.167.0.0/24")); + } + + #[test] + fn contains_network_with_many_networks() { + let mut ip_range = IpRange::new(); + ip_range + .add("192.168.0.0/24".parse().unwrap()) + .add("172.16.0.0/16".parse().unwrap()) + .add("10.0.0.0/8".parse().unwrap()) + .simplify(); + + assert!(ip_range.contains_network("192.168.0.128/25")); + assert!(ip_range.contains_network("172.16.32.0/20")); + assert!(ip_range.contains_network("10.10.0.0/16")); + assert!(!ip_range.contains_network("0.0.0.0/0")); + assert!(!ip_range.contains_network("8.0.0.0/6")); + assert!(!ip_range.contains_network("8.0.0.0/7")); + assert!(!ip_range.contains_network("11.0.0.0/9")); + assert!(!ip_range.contains_network("192.167.255.255/32")); + assert!(!ip_range.contains_network("255.0.0.0/8")); + } + + #[test] + fn contains_network_boundary1() { + let mut ip_range = IpRange::new(); + ip_range.add("0.0.0.0/0".parse().unwrap()); + + assert!(ip_range.contains_network("0.0.0.0/0")); + assert!(ip_range.contains_network("8.0.0.0/6")); + assert!(ip_range.contains_network("11.0.0.0/9")); + assert!(ip_range.contains_network("192.168.0.128/25")); + assert!(ip_range.contains_network("255.255.255.255/32")); + } + + #[test] + fn contains_network_boundary2() { + let mut ip_range = IpRange::new(); + ip_range.add("254.254.254.254/32".parse().unwrap()); + + assert!(!ip_range.contains_network("0.0.0.0/0")); + assert!(!ip_range.contains_network("8.0.0.0/6")); + assert!(!ip_range.contains_network("254.254.0.0/16")); + assert!(ip_range.contains_network("254.254.254.254/32")); + assert!(!ip_range.contains_network("255.255.255.255/32")); + } + + #[test] + fn super_network_with_one_network() { + let mut ip_range = IpRange::new(); + let network = "192.168.0.0/24".parse().unwrap(); + ip_range.add(network); + + assert_eq!( + Some(network), + ip_range.super_network_by_network("192.168.0.0/24") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("192.168.0.128/25") + ); + assert_eq!(None, ip_range.super_network_by_network("192.168.0.0/23")); + assert_eq!(None, ip_range.super_network_by_network("192.168.1.0/24")); + assert_eq!(None, ip_range.super_network_by_network("192.167.0.0/24")); + } + + #[test] + fn super_network_with_many_networks() { + let mut ip_range = IpRange::new(); + let network1 = "192.168.0.0/24".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "10.0.0.0/8".parse().unwrap(); + ip_range + .add(network1) + .add(network2) + .add(network3) + .simplify(); + + assert_eq!( + Some(network1), + ip_range.super_network_by_network("192.168.0.128/25") + ); + assert_eq!( + Some(network2), + ip_range.super_network_by_network("172.16.32.0/20") + ); + assert_eq!( + Some(network3), + ip_range.super_network_by_network("10.10.0.0/16") + ); + assert_eq!(None, ip_range.super_network_by_network("0.0.0.0/0")); + assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/6")); + assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/7")); + assert_eq!(None, ip_range.super_network_by_network("11.0.0.0/9")); + assert_eq!( + None, + ip_range.super_network_by_network("192.167.255.255/32") + ); + assert_eq!(None, ip_range.super_network_by_network("255.0.0.0/8")); + } + + #[test] + fn super_network_boundary1() { + let mut ip_range = IpRange::new(); + let network = "0.0.0.0/0".parse().unwrap(); + ip_range.add(network); + + assert_eq!( + Some(network), + ip_range.super_network_by_network("0.0.0.0/0") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("8.0.0.0/6") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("11.0.0.0/9") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("192.168.0.128/25") + ); + assert_eq!( + Some(network), + ip_range.super_network_by_network("255.255.255.255/32") + ); + } + + #[test] + fn super_network_boundary2() { + let mut ip_range = IpRange::new(); + let network = "254.254.254.254/32".parse().unwrap(); + ip_range.add(network); + + assert_eq!(None, ip_range.super_network_by_network("0.0.0.0/0")); + assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/6")); + assert_eq!(None, ip_range.super_network_by_network("254.254.0.0/16")); + assert_eq!( + Some(network), + ip_range.super_network_by_network("254.254.254.254/32") + ); + assert_eq!( + None, + ip_range.super_network_by_network("255.255.255.255/32") + ); + } + + #[test] + fn merge_empty1() { + let ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range2 + .add(network1) + .add(network2) + .add(network3) + .add(network4) + .simplify(); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 4); + assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); + assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); + } + + #[test] + fn merge_empty2() { + let mut ip_range1 = IpRange::new(); + let ip_range2 = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range1 + .add(network1) + .add(network2) + .add(network3) + .add(network4) + .simplify(); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 4); + assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); + assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); + } + + #[test] + fn merge_disjoint() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 4); + assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); + assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); + assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); + assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); + } + + #[test] + fn merge_joint1() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn merge_joint2() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); + } + + #[test] + fn merge_sequent1() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.5.0/24".parse().unwrap(); + let network3 = "172.16.6.0/24".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + ip_range2.add(network3); + + let ip_range = ip_range1.merge(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 2); + assert_eq!( + "172.16.4.0/23".parse().ok(), + ip_range.get_network(23, "172.16.4.0") + ); + assert_eq!( + "172.16.6.0/24".parse().ok(), + ip_range.get_network(24, "172.16.6.0") + ); + } + + #[test] + fn merge_sequent2() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let mut ip_range3 = IpRange::new(); + ip_range1 + .add("192.168.0.0/20".parse().unwrap()) + .add("192.168.24.0/21".parse().unwrap()); + ip_range2 + .add("192.168.16.0/22".parse().unwrap()) + .add("192.168.23.0/24".parse().unwrap()); + ip_range3 + .add("192.168.20.0/24".parse().unwrap()) + .add("192.168.21.0/24".parse().unwrap()) + .add("192.168.22.0/24".parse().unwrap()); + + let ip_range = ip_range1.merge(&ip_range2); + let ip_range = ip_range.merge(&ip_range3); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!( + "192.168.0.0/19".parse().ok(), + ip_range.get_network(19, "192.168.0.0") + ); + } + + #[test] + fn intersect_disjoint() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Network = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 0); + } + + #[test] + fn intersect_joint1() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.4.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network1), ip_range.get_network(24, "172.16.4.0")); + } + + #[test] + fn intersect_joint2() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network1), ip_range.get_network(24, "172.16.5.0")); + } + + #[test] + fn intersect_joint3() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "172.16.5.0/24".parse().unwrap(); + let network2 = "172.16.5.0/24".parse().unwrap(); + ip_range1.add(network1); + ip_range2.add(network2); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network1), ip_range.get_network(24, "172.16.5.0")); + } + + #[test] + fn intersect_joint4() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1 = "10.0.0.0/8".parse().unwrap(); + let network2 = "192.168.0.0/24".parse().unwrap(); + let network3 = "10.10.0.0/16".parse().unwrap(); + let network4 = "10.254.0.0/17".parse().unwrap(); + let network5 = "192.168.0.0/16".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4).add(network5); + + let ip_range = ip_range1.intersect(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 3); + assert_eq!(Some(network3), ip_range.get_network(16, "10.10.0.0")); + assert_eq!(Some(network4), ip_range.get_network(17, "10.254.0.0")); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.0.0")); + } + + #[test] + fn exclude_disjoint() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Network = "10.0.0.0/8".parse().unwrap(); + let network2 = "172.16.0.0/16".parse().unwrap(); + let network3 = "192.168.1.0/24".parse().unwrap(); + let network4 = "254.254.254.254/32".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range1, ip_range); + } + + #[test] + fn exclude_larger() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Network = "172.16.4.0/24".parse().unwrap(); + let network2 = "192.168.1.0/24".parse().unwrap(); + let network3 = "172.16.4.0/22".parse().unwrap(); + ip_range1.add(network1).add(network2); + ip_range2.add(network3); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); + } + + #[test] + fn exclude_identical() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Network = "172.16.5.0/24".parse().unwrap(); + let network2 = "192.168.1.0/24".parse().unwrap(); + let network3 = "172.16.4.0/22".parse().unwrap(); + let network4 = "10.0.0.0/8".parse().unwrap(); + + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 1); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); + } + + #[test] + fn exclude_split1() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Network = "172.16.4.0/22".parse().unwrap(); + let network2 = "192.168.1.0/24".parse().unwrap(); + let network3 = "172.16.5.0/24".parse().unwrap(); + let network4 = "10.0.0.0/8".parse().unwrap(); + + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 3); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); + assert_eq!( + "172.16.4.0/24".parse().ok(), + ip_range.get_network(24, "172.16.4.0") + ); + assert_eq!( + "172.16.6.0/23".parse().ok(), + ip_range.get_network(23, "172.16.6.0") + ); + } + + #[test] + fn exclude_split2() { + let mut ip_range1 = IpRange::new(); + let mut ip_range2 = IpRange::new(); + let network1: Ipv4Network = "172.16.4.0/22".parse().unwrap(); + let network2 = "192.168.1.0/24".parse().unwrap(); + let network3 = "172.16.4.0/24".parse().unwrap(); + let network4 = "10.0.0.0/8".parse().unwrap(); + + ip_range1.add(network1).add(network2); + ip_range2.add(network3).add(network4); + + let ip_range = ip_range1.exclude(&ip_range2); + assert_eq!(ip_range.into_iter().count(), 3); + assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); + assert_eq!( + "172.16.5.0/24".parse().ok(), + ip_range.get_network(24, "172.16.5.0") + ); + assert_eq!( + "172.16.6.0/23".parse().ok(), + ip_range.get_network(23, "172.16.6.0") + ); + } + + #[test] + fn iter_ipv4() { + let mut data = vec!["1.0.1.0/24", "1.0.2.0/23", "1.0.8.0/21"]; + let ip_range: IpRange = data.iter().map(|net| net.parse().unwrap()).collect(); + let mut nets: Vec = ip_range.iter().map(|net| format!("{}", net)).collect(); + data.sort_unstable(); + nets.sort_unstable(); + assert_eq!(nets, data); + } + + #[test] + fn iter_ipv6() { + let mut data = vec![ + "2400:9a40::/32", + "2400:9dc0::/32", + "2400:9e00::/32", + "2400:a040::/32", + ]; + let ip_range: IpRange = data.iter().map(|net| net.parse().unwrap()).collect(); + let mut nets: Vec = ip_range.iter().map(|net| format!("{}", net)).collect(); + data.sort_unstable(); + nets.sort_unstable(); + assert_eq!(nets, data); + } + + #[test] + fn debug_fmt() { + let ip_range: IpRange = IpRange::default(); + assert_eq!(format!("{:?}", ip_range), "IpRange []"); + + // Note: ipnetwork uses struct Debug format instead of Display format + let ip_range: IpRange = ["1.0.1.0/24", "1.0.2.0/23", "1.0.8.0/21"] + .iter() + .map(|net| net.parse().unwrap()) + .collect(); + let debug_str = format!("{:?}", ip_range); + assert!(debug_str.starts_with("IpRange [")); + assert!(debug_str.contains("1.0.8.0")); + assert!(debug_str.contains("1.0.2.0")); + assert!(debug_str.contains("1.0.1.0")); + + let ip_range: IpRange = [ + "192.168.0.0/16", + "1.0.2.0/23", + "1.0.8.0/21", + "127.0.0.0/8", + "172.16.0.0/12", + ] + .iter() + .map(|net| net.parse().unwrap()) + .collect(); + let debug_str = format!("{:?}", ip_range); + assert!(debug_str.starts_with("IpRange [")); + assert!(debug_str.ends_with("...]")); + + let ip_range: IpRange = [ + "2001:4438::/32", + "2001:4510::/29", + "2400:1040::/32", + "2400:12c0::/32", + "2400:1340::/32", + "2400:1380::/32", + "2400:15c0::/32", + ] + .iter() + .map(|net| net.parse().unwrap()) + .collect(); + let debug_str = format!("{:?}", ip_range); + assert!(debug_str.starts_with("IpRange [")); + assert!(debug_str.ends_with("...]")); + } + + #[test] + fn remove_all() { + let mut ip_range = IpRange::new(); + let network: Ipv4Network = "1.0.1.0/24".parse().unwrap(); + ip_range.add(network); + ip_range.remove(network); + assert!(ip_range.iter().next().is_none()); + } + + #[test] + fn add_to_all_zeros() { + let mut ip_range: IpRange = IpRange::new(); + ip_range.add("0.0.0.0/0".parse().unwrap()); + ip_range.add("127.0.0.1/32".parse().unwrap()); + assert!(ip_range.contains_network("0.0.0.0/0")); + } + + #[test] + #[cfg(feature = "serde")] + fn serialize_ipv4_as_binary() { + let mut ip_range: IpRange = IpRange::new(); + ip_range.add("0.0.0.0/0".parse().unwrap()); + ip_range.add("127.0.0.1/32".parse().unwrap()); + ip_range.add("254.254.254.254/32".parse().unwrap()); + let encoded: Vec = bincode::serialize(&ip_range).unwrap(); + let decoded_ip_range: IpRange = bincode::deserialize(&encoded[..]).unwrap(); + assert_eq!(ip_range, decoded_ip_range); + } + + #[test] + #[cfg(feature = "serde")] + fn serialize_ipv6_as_binary() { + let mut ip_range: IpRange = IpRange::new(); + ip_range.add("2001:4438::/32".parse().unwrap()); + ip_range.add("2400:1040::/32".parse().unwrap()); + ip_range.add("2400:1340::/32".parse().unwrap()); + let encoded: Vec = bincode::serialize(&ip_range).unwrap(); + let decoded_ip_range: IpRange = bincode::deserialize(&encoded[..]).unwrap(); + assert_eq!(ip_range, decoded_ip_range); + } +} diff --git a/src/lib.rs b/src/lib.rs index 95d4623..42dbc1b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,22 +15,18 @@ //! Here is a simple example: //! //! ``` -//! extern crate iprange; -//! extern crate ipnet; -//! +//! # extern crate ipnet; //! use std::net::Ipv4Addr; //! use iprange::IpRange; //! use ipnet::Ipv4Net; //! -//! fn main() { -//! let ip_range: IpRange = ["10.0.0.0/8", "172.16.0.0/16", "192.168.1.0/24"] -//! .iter() -//! .map(|s| s.parse().unwrap()) -//! .collect(); +//! let ip_range: IpRange = ["10.0.0.0/8", "172.16.0.0/16", "192.168.1.0/24"] +//! .iter() +//! .map(|s| s.parse().unwrap()) +//! .collect(); //! -//! assert!(ip_range.contains(&"172.16.32.1".parse::().unwrap())); -//! assert!(ip_range.contains(&"192.168.1.1".parse::().unwrap())); -//! } +//! assert!(ip_range.contains(&"172.16.32.1".parse::().unwrap())); +//! assert!(ip_range.contains(&"192.168.1.1".parse::().unwrap())); //! ``` //! //! [`IpRange`]: struct.IpRange.html @@ -40,17 +36,25 @@ //! [`intersect`]: struct.IpRange.html#method.intersect //! [`exclude`]: struct.IpRange.html#method.exclude +#[cfg(feature = "ipnet")] extern crate ipnet; +#[cfg(feature = "ipnetwork")] +extern crate ipnetwork; #[cfg(feature = "serde")] #[macro_use] extern crate serde; -use ipnet::{Ipv4Net, Ipv6Net}; use std::collections::VecDeque; use std::fmt; use std::iter::FromIterator; use std::marker::PhantomData; -use std::net::{Ipv4Addr, Ipv6Addr}; + +#[cfg(feature = "ipnet")] +#[path = "ipnet.rs"] +mod ipnet_impl; +#[cfg(feature = "ipnetwork")] +#[path = "ipnetwork.rs"] +mod ipnetwork_impl; /// A set of networks that supports various operations: /// @@ -65,21 +69,17 @@ use std::net::{Ipv4Addr, Ipv6Addr}; /// to iterate over the networks in an `IpRange`: /// /// ``` -/// extern crate ipnet; -/// extern crate iprange; -/// +/// # extern crate ipnet; /// use iprange::IpRange; /// use ipnet::Ipv4Net; /// -/// fn main() { -/// let ip_range: IpRange = ["172.16.0.0/16", "192.168.1.0/24"] -/// .iter() -/// .map(|s| s.parse().unwrap()) -/// .collect(); +/// let ip_range: IpRange = ["172.16.0.0/16", "192.168.1.0/24"] +/// .iter() +/// .map(|s| s.parse().unwrap()) +/// .collect(); /// -/// for network in &ip_range { -/// println!("{:?}", network); -/// } +/// for network in &ip_range { +/// println!("{:?}", network); /// } /// ``` /// @@ -114,21 +114,17 @@ impl IpRange { /// explicitly. For example: /// /// ``` - /// extern crate iprange; - /// extern crate ipnet; - /// + /// # extern crate ipnet; /// use iprange::IpRange; /// use ipnet::Ipv4Net; /// - /// fn main() { - /// let mut ip_range: IpRange = IpRange::new(); - /// ip_range.add("192.168.0.0/24".parse().unwrap()) - /// .add("192.168.1.0/24".parse().unwrap()); - /// assert_eq!(ip_range.into_iter().count(), 2); + /// let mut ip_range: IpRange = IpRange::new(); + /// ip_range.add("192.168.0.0/24".parse().unwrap()) + /// .add("192.168.1.0/24".parse().unwrap()); + /// assert_eq!(ip_range.into_iter().count(), 2); /// - /// ip_range.simplify(); - /// assert_eq!(ip_range.into_iter().count(), 1); - /// } + /// ip_range.simplify(); + /// assert_eq!(ip_range.into_iter().count(), 1); /// ``` /// /// [`simplify`]: struct.IpRange.html#method.simplify @@ -147,18 +143,14 @@ impl IpRange { /// For example: /// /// ``` - /// extern crate iprange; - /// extern crate ipnet; - /// + /// # extern crate ipnet; /// use iprange::IpRange; /// use ipnet::Ipv4Net; /// - /// fn main() { - /// let mut ip_range: IpRange = IpRange::new(); - /// ip_range.add("192.168.0.0/23".parse().unwrap()) - /// .remove("192.168.0.0/24".parse().unwrap()); - /// // Now, ip_range has only one network: "192.168.1.0/24". - /// } + /// let mut ip_range: IpRange = IpRange::new(); + /// ip_range.add("192.168.0.0/23".parse().unwrap()) + /// .remove("192.168.0.0/24".parse().unwrap()); + /// // Now, ip_range has only one network: "192.168.1.0/24". /// ``` pub fn remove(&mut self, network: N) -> &mut Self { self.trie.remove(network); @@ -170,9 +162,9 @@ impl IpRange { /// # Examples /// ``` /// # extern crate ipnet; - /// # - /// # use iprange::IpRange; - /// # use ipnet::Ipv4Net; + /// use iprange::IpRange; + /// use ipnet::Ipv4Net; + /// /// let mut ip_range = IpRange::new(); /// let network: Ipv4Net = "1.0.1.0/24".parse().unwrap(); /// ip_range.add(network.clone()); @@ -186,25 +178,21 @@ impl IpRange { /// Simplify `self` by combining networks. For example: /// /// ``` - /// extern crate iprange; - /// extern crate ipnet; - /// + /// # extern crate ipnet; /// use iprange::IpRange; /// use ipnet::Ipv4Net; /// - /// fn main() { - /// let mut ip_range: IpRange = IpRange::new(); - /// ip_range - /// .add("192.168.0.0/20".parse().unwrap()) - /// .add("192.168.16.0/22".parse().unwrap()) - /// .add("192.168.20.0/24".parse().unwrap()) - /// .add("192.168.21.0/24".parse().unwrap()) - /// .add("192.168.22.0/24".parse().unwrap()) - /// .add("192.168.23.0/24".parse().unwrap()) - /// .add("192.168.24.0/21".parse().unwrap()) - /// .simplify(); - /// // Now, ip_range has only one network: "192.168.0.0/19". - /// } + /// let mut ip_range: IpRange = IpRange::new(); + /// ip_range + /// .add("192.168.0.0/20".parse().unwrap()) + /// .add("192.168.16.0/22".parse().unwrap()) + /// .add("192.168.20.0/24".parse().unwrap()) + /// .add("192.168.21.0/24".parse().unwrap()) + /// .add("192.168.22.0/24".parse().unwrap()) + /// .add("192.168.23.0/24".parse().unwrap()) + /// .add("192.168.24.0/21".parse().unwrap()) + /// .simplify(); + /// // Now, ip_range has only one network: "192.168.0.0/19". /// ``` pub fn simplify(&mut self) { self.trie.simplify(); @@ -656,1167 +644,7 @@ impl IpTrieNode { } } +#[cfg(any(feature = "ipnet", feature = "ipnetwork"))] const MSO_U128: u128 = 1 << 127; // Most significant one for u128 +#[cfg(any(feature = "ipnet", feature = "ipnetwork"))] const MSO_U32: u32 = 1 << 31; // Most significant one for u32 - -impl IpNet for Ipv4Net { - type S = Ipv4TraverseState; - type I = Ipv4PrefixBitIterator; - - #[inline] - fn prefix_bits(&self) -> Self::I { - let prefix: u32 = self.addr().into(); - Ipv4PrefixBitIterator { - prefix, - prefix_len: self.prefix_len(), - } - } - - #[inline] - fn prefix_len(&self) -> u8 { - self.prefix_len() - } - - #[inline] - fn with_new_prefix(&self, len: u8) -> Self { - Ipv4Net::new(self.addr(), len).unwrap().trunc() - } -} - -impl ToNetwork for Ipv4Net { - #[inline] - fn to_network(&self) -> Ipv4Net { - self.trunc() - } -} - -impl ToNetwork for Ipv4Addr { - #[inline] - fn to_network(&self) -> Ipv4Net { - Ipv4Net::new(*self, 32).unwrap() - } -} - -impl ToNetwork for u32 { - #[inline] - fn to_network(&self) -> Ipv4Net { - Ipv4Net::new((*self).into(), 32).unwrap() - } -} - -impl ToNetwork for [u8; 4] { - #[inline] - fn to_network(&self) -> Ipv4Net { - Ipv4Net::new((*self).into(), 32).unwrap() - } -} - -#[doc(hidden)] -pub struct Ipv4TraverseState { - node: *const IpTrieNode, - prefix: u32, - prefix_len: u8, -} - -impl TraverseState for Ipv4TraverseState { - type Net = Ipv4Net; - - #[inline] - fn node(&self) -> *const IpTrieNode { - self.node - } - - #[inline] - fn init(root: &IpTrieNode) -> Self { - Ipv4TraverseState { - node: root, - prefix: 0, - prefix_len: 0, - } - } - - #[inline] - fn transit(&self, next_node: &IpTrieNode, current_bit: bool) -> Self { - let mask = if current_bit { - MSO_U32 >> self.prefix_len - } else { - 0 - }; - Ipv4TraverseState { - node: next_node, - prefix: self.prefix | mask, - prefix_len: self.prefix_len + 1, - } - } - - #[inline] - fn build(&self) -> Self::Net { - Ipv4Net::new(self.prefix.into(), self.prefix_len as u8).unwrap() - } -} - -#[doc(hidden)] -pub struct Ipv4PrefixBitIterator { - prefix: u32, - prefix_len: u8, -} - -impl Iterator for Ipv4PrefixBitIterator { - type Item = bool; - - #[inline] - fn next(&mut self) -> Option { - if self.prefix_len > 0 { - let prefix = self.prefix; - self.prefix <<= 1; - self.prefix_len -= 1; - Some(prefix & MSO_U32 != 0) - } else { - None - } - } -} - -impl IpNet for Ipv6Net { - type S = Ipv6TraverseState; - type I = Ipv6PrefixBitIterator; - - #[inline] - fn prefix_bits(&self) -> Self::I { - Ipv6PrefixBitIterator { - prefix: self.addr().into(), - prefix_len: self.prefix_len(), - } - } - - #[inline] - fn prefix_len(&self) -> u8 { - self.prefix_len() - } - - #[inline] - fn with_new_prefix(&self, len: u8) -> Self { - Ipv6Net::new(self.addr(), len).unwrap().trunc() - } -} - -impl ToNetwork for Ipv6Net { - #[inline] - fn to_network(&self) -> Ipv6Net { - self.trunc() - } -} - -impl ToNetwork for Ipv6Addr { - #[inline] - fn to_network(&self) -> Ipv6Net { - Ipv6Net::new(*self, 128).unwrap() - } -} - -impl ToNetwork for u128 { - #[inline] - fn to_network(&self) -> Ipv6Net { - Ipv6Net::new((*self).into(), 128).unwrap() - } -} - -impl ToNetwork for [u8; 16] { - #[inline] - fn to_network(&self) -> Ipv6Net { - Ipv6Net::new((*self).into(), 128).unwrap() - } -} - -impl ToNetwork for [u16; 8] { - #[inline] - fn to_network(&self) -> Ipv6Net { - Ipv6Net::new((*self).into(), 128).unwrap() - } -} - -#[doc(hidden)] -pub struct Ipv6TraverseState { - node: *const IpTrieNode, - prefix: u128, - prefix_len: u8, -} - -impl TraverseState for Ipv6TraverseState { - type Net = Ipv6Net; - - #[inline] - fn node(&self) -> *const IpTrieNode { - self.node - } - - #[inline] - fn init(root: &IpTrieNode) -> Self { - Ipv6TraverseState { - node: root, - prefix: 0, - prefix_len: 0, - } - } - - #[inline] - fn transit(&self, next_node: &IpTrieNode, current_bit: bool) -> Self { - let mask = if current_bit { - MSO_U128 >> self.prefix_len - } else { - 0 - }; - Ipv6TraverseState { - node: next_node, - prefix: self.prefix | mask, - prefix_len: self.prefix_len + 1, - } - } - - #[inline] - fn build(&self) -> Self::Net { - Ipv6Net::new(self.prefix.into(), self.prefix_len as u8).unwrap() - } -} - -#[doc(hidden)] -pub struct Ipv6PrefixBitIterator { - prefix: u128, - prefix_len: u8, -} - -impl Iterator for Ipv6PrefixBitIterator { - type Item = bool; - - #[inline] - fn next(&mut self) -> Option { - if self.prefix_len > 0 { - let prefix = self.prefix; - self.prefix <<= 1; - self.prefix_len -= 1; - Some(prefix & MSO_U128 != 0) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_invalid_networks() { - assert!("192.168.256.130/5".parse::().is_err()); - assert!("192.168.5.130/-1".parse::().is_err()); - assert!("192.168.5.130/33".parse::().is_err()); - assert!("192.168.5.33".parse::().is_err()); - assert!("192.168.5.130/0.0.0".parse::().is_err()); - assert!("192.168.5.130/0.0.0.256".parse::().is_err()); - } - - impl IpRange { - fn get_network(&self, prefix_size: usize, prefix: &str) -> Option { - self.trie - .search(format!("{}/{}", prefix, prefix_size).parse().unwrap()) - } - } - - #[test] - fn add_single_network() { - let mut ip_range = IpRange::new(); - let network = "192.168.5.0/24".parse().unwrap(); - ip_range.add(network); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network), ip_range.get_network(24, "192.168.5.0")); - } - - #[test] - fn add_multiple_networks_disjoint() { - let mut ip_range = IpRange::new(); - let network1 = "10.0.0.0/8".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - let network3 = "192.168.1.0/24".parse().unwrap(); - let network4 = "254.254.254.254/32".parse().unwrap(); - ip_range - .add(network1) - .add(network2) - .add(network3) - .add(network4) - .simplify(); - - assert_eq!(ip_range.into_iter().count(), 4); - assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); - assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); - assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); - assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); - } - - #[test] - fn simplify() { - let mut ip_range = IpRange::new(); - ip_range - .add("192.168.0.0/20".parse().unwrap()) - .add("192.168.16.0/22".parse().unwrap()) - .add("192.168.20.0/24".parse().unwrap()) - .add("192.168.21.0/24".parse().unwrap()) - .add("192.168.22.0/24".parse().unwrap()) - .add("192.168.23.0/24".parse().unwrap()) - .add("192.168.24.0/21".parse().unwrap()) - .simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!( - "192.168.0.0/19".parse().ok(), - ip_range.get_network(19, "192.168.0.0") - ); - } - - #[test] - fn add_multiple_networks_joint1() { - let mut ip_range = IpRange::new(); - let network1 = "172.16.4.0/24".parse().unwrap(); - let network2 = "172.16.4.0/22".parse().unwrap(); - ip_range.add(network1).add(network2).simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); - } - - #[test] - fn add_multiple_networks_joint2() { - let mut ip_range = IpRange::new(); - let network1 = "172.16.5.0/24".parse().unwrap(); - let network2 = "172.16.4.0/22".parse().unwrap(); - ip_range.add(network1).add(network2).simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); - } - - #[test] - fn add_multiple_networks_joint3() { - let mut ip_range = IpRange::new(); - let network1 = "172.16.4.0/24".parse().unwrap(); - let network2 = "172.16.4.0/22".parse().unwrap(); - ip_range.add(network2).add(network1).simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); - } - - #[test] - fn add_multiple_networks_joint4() { - let mut ip_range = IpRange::new(); - let network1 = "172.16.5.0/24".parse().unwrap(); - let network2 = "172.16.5.0/24".parse().unwrap(); - ip_range.add(network1).add(network2).simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(24, "172.16.5.0")); - } - - #[test] - fn add_multiple_networks_joint5() { - let mut ip_range = IpRange::new(); - let network1 = "172.16.5.0/24".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - ip_range.add(network1).add(network2).simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); - } - - #[test] - fn add_multiple_networks_joint6() { - let mut ip_range = IpRange::new(); - let network1 = "172.16.5.0/24".parse().unwrap(); - let network2 = "0.0.0.0/0".parse().unwrap(); - ip_range.add(network1).add(network2).simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(0, "0.0.0.0")); - } - - #[test] - fn remove_networks_no_split() { - let mut ip_range = IpRange::new(); - let network1 = "192.168.0.0/24".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - ip_range.add(network1).add(network2).simplify(); - - ip_range.remove(network1); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); - } - - #[test] - fn remove_networks_split1() { - let mut ip_range = IpRange::new(); - ip_range.add("192.168.0.0/22".parse().unwrap()); - ip_range - .remove("192.168.2.0/23".parse().unwrap()) - .simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!( - Some("192.168.0.0/23".parse().unwrap()), - ip_range.get_network(23, "192.168.0.0") - ); - } - - #[test] - fn remove_networks_split2() { - let mut ip_range = IpRange::new(); - ip_range.add("192.168.0.0/22".parse().unwrap()); - ip_range - .remove("192.168.0.0/23".parse().unwrap()) - .simplify(); - - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!( - Some("192.168.2.0/23".parse().unwrap()), - ip_range.get_network(23, "192.168.2.0") - ); - } - - #[test] - fn remove_networks_split3() { - let mut ip_range = IpRange::new(); - ip_range.add("192.168.0.0/22".parse().unwrap()); - ip_range - .remove("192.168.2.0/25".parse().unwrap()) - .simplify(); - - assert_eq!(ip_range.into_iter().count(), 3); - assert_eq!( - Some("192.168.0.0/23".parse().unwrap()), - ip_range.get_network(23, "192.168.0.0") - ); - assert_eq!( - Some("192.168.2.128/25".parse().unwrap()), - ip_range.get_network(25, "192.168.2.128") - ); - assert_eq!( - Some("192.168.3.0/24".parse().unwrap()), - ip_range.get_network(24, "192.168.3.0") - ); - } - - impl IpRange { - fn contains_ip(&self, ip: &str) -> bool { - self.contains(&ip.parse::().unwrap()) - } - - fn find_network_by_ip(&self, ip: &str) -> Option { - self.supernet(&ip.parse::().unwrap()) - } - - fn contains_network(&self, network: &str) -> bool { - self.contains(&network.parse::().unwrap()) - } - - fn super_network_by_network(&self, network: &str) -> Option { - self.supernet(&network.parse::().unwrap()) - } - } - - #[test] - fn contains_ip_with_one_network() { - let mut ip_range = IpRange::new(); - ip_range.add("192.168.0.0/24".parse().unwrap()); - - assert!(ip_range.contains_ip("192.168.0.0")); - assert!(ip_range.contains_ip("192.168.0.128")); - assert!(ip_range.contains_ip("192.168.0.255")); - assert!(!ip_range.contains_ip("192.167.255.255")); - assert!(!ip_range.contains_ip("192.168.1.0")); - } - - #[test] - fn contains_ip_with_many_networks() { - let mut ip_range = IpRange::new(); - ip_range - .add("192.168.0.0/24".parse().unwrap()) - .add("172.16.0.0/16".parse().unwrap()) - .add("10.0.0.0/8".parse().unwrap()) - .simplify(); - - assert!(ip_range.contains_ip("192.168.0.128")); - assert!(ip_range.contains_ip("172.16.32.1")); - assert!(ip_range.contains_ip("10.10.10.10")); - assert!(!ip_range.contains_ip("0.0.0.0")); - assert!(!ip_range.contains_ip("8.8.8.8")); - assert!(!ip_range.contains_ip("11.0.0.0")); - assert!(!ip_range.contains_ip("192.167.255.255")); - assert!(!ip_range.contains_ip("255.255.255.255")); - } - - #[test] - fn contains_ip_boundary1() { - let mut ip_range = IpRange::new(); - ip_range.add("0.0.0.0/0".parse().unwrap()); - - assert!(ip_range.contains_ip("0.0.0.0")); - assert!(ip_range.contains_ip("8.8.8.8")); - assert!(ip_range.contains_ip("192.168.0.0")); - assert!(ip_range.contains_ip("192.168.1.1")); - } - - #[test] - fn contains_ip_boundary2() { - let mut ip_range = IpRange::new(); - ip_range.add("254.254.254.254/32".parse().unwrap()); - - assert!(!ip_range.contains_ip("0.0.0.0")); - assert!(!ip_range.contains_ip("8.8.8.8")); - assert!(!ip_range.contains_ip("192.168.0.0")); - assert!(ip_range.contains_ip("254.254.254.254")); - } - - #[test] - fn find_network_with_one_network() { - let mut ip_range = IpRange::new(); - let network = "192.168.0.0/24".parse().unwrap(); - ip_range.add(network); - - assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.0")); - assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.128")); - assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.255")); - assert_eq!(None, ip_range.find_network_by_ip("192.167.255.255")); - assert_eq!(None, ip_range.find_network_by_ip("192.168.1.0")); - } - - #[test] - fn find_network_with_many_networks() { - let mut ip_range = IpRange::new(); - let network1 = "192.168.0.0/24".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - let network3 = "10.0.0.0/8".parse().unwrap(); - ip_range - .add(network1) - .add(network2) - .add(network3) - .simplify(); - - assert_eq!(Some(network1), ip_range.find_network_by_ip("192.168.0.128")); - assert_eq!(Some(network2), ip_range.find_network_by_ip("172.16.32.1")); - assert_eq!(Some(network3), ip_range.find_network_by_ip("10.10.10.10")); - assert_eq!(None, ip_range.find_network_by_ip("0.0.0.0")); - assert_eq!(None, ip_range.find_network_by_ip("8.8.8.8")); - assert_eq!(None, ip_range.find_network_by_ip("11.0.0.0")); - assert_eq!(None, ip_range.find_network_by_ip("192.167.255.255")); - assert_eq!(None, ip_range.find_network_by_ip("255.255.255.255")); - } - - #[test] - fn find_network_boundary1() { - let mut ip_range = IpRange::new(); - let network = "0.0.0.0/0".parse().unwrap(); - ip_range.add(network); - - assert_eq!(Some(network), ip_range.find_network_by_ip("0.0.0.0")); - assert_eq!(Some(network), ip_range.find_network_by_ip("8.8.8.8")); - assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.0.0")); - assert_eq!(Some(network), ip_range.find_network_by_ip("192.168.1.1")); - } - - #[test] - fn find_network_boundary2() { - let mut ip_range = IpRange::new(); - let network = "254.254.254.254/32".parse().unwrap(); - ip_range.add(network); - - assert_eq!(None, ip_range.find_network_by_ip("0.0.0.0")); - assert_eq!(None, ip_range.find_network_by_ip("8.8.8.8")); - assert_eq!(None, ip_range.find_network_by_ip("192.168.0.0")); - assert_eq!( - Some(network), - ip_range.find_network_by_ip("254.254.254.254") - ); - } - - #[test] - fn contains_network_with_one_network() { - let mut ip_range = IpRange::new(); - ip_range.add("192.168.0.0/24".parse().unwrap()); - - assert!(ip_range.contains_network("192.168.0.0/24")); - assert!(ip_range.contains_network("192.168.0.128/25")); - assert!(!ip_range.contains_network("192.168.0.0/23")); - assert!(!ip_range.contains_network("192.168.1.0/24")); - assert!(!ip_range.contains_network("192.167.0.0/24")); - } - - #[test] - fn contains_network_with_many_networks() { - let mut ip_range = IpRange::new(); - ip_range - .add("192.168.0.0/24".parse().unwrap()) - .add("172.16.0.0/16".parse().unwrap()) - .add("10.0.0.0/8".parse().unwrap()) - .simplify(); - - assert!(ip_range.contains_network("192.168.0.128/25")); - assert!(ip_range.contains_network("172.16.32.0/20")); - assert!(ip_range.contains_network("10.10.0.0/16")); - assert!(!ip_range.contains_network("0.0.0.0/0")); - assert!(!ip_range.contains_network("8.0.0.0/6")); - assert!(!ip_range.contains_network("8.0.0.0/7")); - assert!(!ip_range.contains_network("11.0.0.0/9")); - assert!(!ip_range.contains_network("192.167.255.255/32")); - assert!(!ip_range.contains_network("255.0.0.0/8")); - } - - #[test] - fn contains_network_boundary1() { - let mut ip_range = IpRange::new(); - ip_range.add("0.0.0.0/0".parse().unwrap()); - - assert!(ip_range.contains_network("0.0.0.0/0")); - assert!(ip_range.contains_network("8.0.0.0/6")); - assert!(ip_range.contains_network("11.0.0.0/9")); - assert!(ip_range.contains_network("192.168.0.128/25")); - assert!(ip_range.contains_network("255.255.255.255/32")); - } - - #[test] - fn contains_network_boundary2() { - let mut ip_range = IpRange::new(); - ip_range.add("254.254.254.254/32".parse().unwrap()); - - assert!(!ip_range.contains_network("0.0.0.0/0")); - assert!(!ip_range.contains_network("8.0.0.0/6")); - assert!(!ip_range.contains_network("254.254.0.0/16")); - assert!(ip_range.contains_network("254.254.254.254/32")); - assert!(!ip_range.contains_network("255.255.255.255/32")); - } - - #[test] - fn super_network_with_one_network() { - let mut ip_range = IpRange::new(); - let network = "192.168.0.0/24".parse().unwrap(); - ip_range.add(network); - - assert_eq!( - Some(network), - ip_range.super_network_by_network("192.168.0.0/24") - ); - assert_eq!( - Some(network), - ip_range.super_network_by_network("192.168.0.128/25") - ); - assert_eq!(None, ip_range.super_network_by_network("192.168.0.0/23")); - assert_eq!(None, ip_range.super_network_by_network("192.168.1.0/24")); - assert_eq!(None, ip_range.super_network_by_network("192.167.0.0/24")); - } - - #[test] - fn super_network_with_many_networks() { - let mut ip_range = IpRange::new(); - let network1 = "192.168.0.0/24".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - let network3 = "10.0.0.0/8".parse().unwrap(); - ip_range - .add(network1) - .add(network2) - .add(network3) - .simplify(); - - assert_eq!( - Some(network1), - ip_range.super_network_by_network("192.168.0.128/25") - ); - assert_eq!( - Some(network2), - ip_range.super_network_by_network("172.16.32.0/20") - ); - assert_eq!( - Some(network3), - ip_range.super_network_by_network("10.10.0.0/16") - ); - assert_eq!(None, ip_range.super_network_by_network("0.0.0.0/0")); - assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/6")); - assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/7")); - assert_eq!(None, ip_range.super_network_by_network("11.0.0.0/9")); - assert_eq!( - None, - ip_range.super_network_by_network("192.167.255.255/32") - ); - assert_eq!(None, ip_range.super_network_by_network("255.0.0.0/8")); - } - - #[test] - fn super_network_boundary1() { - let mut ip_range = IpRange::new(); - let network = "0.0.0.0/0".parse().unwrap(); - ip_range.add(network); - - assert_eq!( - Some(network), - ip_range.super_network_by_network("0.0.0.0/0") - ); - assert_eq!( - Some(network), - ip_range.super_network_by_network("8.0.0.0/6") - ); - assert_eq!( - Some(network), - ip_range.super_network_by_network("11.0.0.0/9") - ); - assert_eq!( - Some(network), - ip_range.super_network_by_network("192.168.0.128/25") - ); - assert_eq!( - Some(network), - ip_range.super_network_by_network("255.255.255.255/32") - ); - } - - #[test] - fn super_network_boundary2() { - let mut ip_range = IpRange::new(); - let network = "254.254.254.254/32".parse().unwrap(); - ip_range.add(network); - - assert_eq!(None, ip_range.super_network_by_network("0.0.0.0/0")); - assert_eq!(None, ip_range.super_network_by_network("8.0.0.0/6")); - assert_eq!(None, ip_range.super_network_by_network("254.254.0.0/16")); - assert_eq!( - Some(network), - ip_range.super_network_by_network("254.254.254.254/32") - ); - assert_eq!( - None, - ip_range.super_network_by_network("255.255.255.255/32") - ); - } - - #[test] - fn merge_empty1() { - let ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "10.0.0.0/8".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - let network3 = "192.168.1.0/24".parse().unwrap(); - let network4 = "254.254.254.254/32".parse().unwrap(); - ip_range2 - .add(network1) - .add(network2) - .add(network3) - .add(network4) - .simplify(); - - let ip_range = ip_range1.merge(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 4); - assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); - assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); - assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); - assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); - } - - #[test] - fn merge_empty2() { - let mut ip_range1 = IpRange::new(); - let ip_range2 = IpRange::new(); - let network1 = "10.0.0.0/8".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - let network3 = "192.168.1.0/24".parse().unwrap(); - let network4 = "254.254.254.254/32".parse().unwrap(); - ip_range1 - .add(network1) - .add(network2) - .add(network3) - .add(network4) - .simplify(); - - let ip_range = ip_range1.merge(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 4); - assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); - assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); - assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); - assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); - } - - #[test] - fn merge_disjoint() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "10.0.0.0/8".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - let network3 = "192.168.1.0/24".parse().unwrap(); - let network4 = "254.254.254.254/32".parse().unwrap(); - ip_range1.add(network1).add(network2); - ip_range2.add(network3).add(network4); - - let ip_range = ip_range1.merge(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 4); - assert_eq!(Some(network1), ip_range.get_network(8, "10.0.0.0")); - assert_eq!(Some(network2), ip_range.get_network(16, "172.16.0.0")); - assert_eq!(Some(network3), ip_range.get_network(24, "192.168.1.0")); - assert_eq!(Some(network4), ip_range.get_network(32, "254.254.254.254")); - } - - #[test] - fn merge_joint1() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "172.16.4.0/24".parse().unwrap(); - let network2 = "172.16.4.0/22".parse().unwrap(); - ip_range1.add(network1); - ip_range2.add(network2); - - let ip_range = ip_range1.merge(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); - } - - #[test] - fn merge_joint2() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "172.16.5.0/24".parse().unwrap(); - let network2 = "172.16.4.0/22".parse().unwrap(); - ip_range1.add(network1); - ip_range2.add(network2); - - let ip_range = ip_range1.merge(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(22, "172.16.4.0")); - } - - #[test] - fn merge_sequent1() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "172.16.4.0/24".parse().unwrap(); - let network2 = "172.16.5.0/24".parse().unwrap(); - let network3 = "172.16.6.0/24".parse().unwrap(); - ip_range1.add(network1); - ip_range2.add(network2); - ip_range2.add(network3); - - let ip_range = ip_range1.merge(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 2); - assert_eq!( - "172.16.4.0/23".parse().ok(), - ip_range.get_network(23, "172.16.4.0") - ); - assert_eq!( - "172.16.6.0/24".parse().ok(), - ip_range.get_network(24, "172.16.6.0") - ); - } - - #[test] - fn merge_sequent2() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let mut ip_range3 = IpRange::new(); - ip_range1 - .add("192.168.0.0/20".parse().unwrap()) - .add("192.168.24.0/21".parse().unwrap()); - ip_range2 - .add("192.168.16.0/22".parse().unwrap()) - .add("192.168.23.0/24".parse().unwrap()); - ip_range3 - .add("192.168.20.0/24".parse().unwrap()) - .add("192.168.21.0/24".parse().unwrap()) - .add("192.168.22.0/24".parse().unwrap()); - - let ip_range = ip_range1.merge(&ip_range2); - let ip_range = ip_range.merge(&ip_range3); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!( - "192.168.0.0/19".parse().ok(), - ip_range.get_network(19, "192.168.0.0") - ); - } - - #[test] - fn intersect_disjoint() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1: Ipv4Net = "10.0.0.0/8".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - let network3 = "192.168.1.0/24".parse().unwrap(); - let network4 = "254.254.254.254/32".parse().unwrap(); - ip_range1.add(network1).add(network2); - ip_range2.add(network3).add(network4); - - let ip_range = ip_range1.intersect(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 0); - } - - #[test] - fn intersect_joint1() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "172.16.4.0/24".parse().unwrap(); - let network2 = "172.16.4.0/22".parse().unwrap(); - ip_range1.add(network1); - ip_range2.add(network2); - - let ip_range = ip_range1.intersect(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network1), ip_range.get_network(24, "172.16.4.0")); - } - - #[test] - fn intersect_joint2() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "172.16.5.0/24".parse().unwrap(); - let network2 = "172.16.4.0/22".parse().unwrap(); - ip_range1.add(network1); - ip_range2.add(network2); - - let ip_range = ip_range1.intersect(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network1), ip_range.get_network(24, "172.16.5.0")); - } - - #[test] - fn intersect_joint3() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "172.16.5.0/24".parse().unwrap(); - let network2 = "172.16.5.0/24".parse().unwrap(); - ip_range1.add(network1); - ip_range2.add(network2); - - let ip_range = ip_range1.intersect(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network1), ip_range.get_network(24, "172.16.5.0")); - } - - #[test] - fn intersect_joint4() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1 = "10.0.0.0/8".parse().unwrap(); - let network2 = "192.168.0.0/24".parse().unwrap(); - let network3 = "10.10.0.0/16".parse().unwrap(); - let network4 = "10.254.0.0/17".parse().unwrap(); - let network5 = "192.168.0.0/16".parse().unwrap(); - ip_range1.add(network1).add(network2); - ip_range2.add(network3).add(network4).add(network5); - - let ip_range = ip_range1.intersect(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 3); - assert_eq!(Some(network3), ip_range.get_network(16, "10.10.0.0")); - assert_eq!(Some(network4), ip_range.get_network(17, "10.254.0.0")); - assert_eq!(Some(network2), ip_range.get_network(24, "192.168.0.0")); - } - - #[test] - fn exclude_disjoint() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1: Ipv4Net = "10.0.0.0/8".parse().unwrap(); - let network2 = "172.16.0.0/16".parse().unwrap(); - let network3 = "192.168.1.0/24".parse().unwrap(); - let network4 = "254.254.254.254/32".parse().unwrap(); - ip_range1.add(network1).add(network2); - ip_range2.add(network3).add(network4); - - let ip_range = ip_range1.exclude(&ip_range2); - assert_eq!(ip_range1, ip_range); - } - - #[test] - fn exclude_larger() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1: Ipv4Net = "172.16.4.0/24".parse().unwrap(); - let network2 = "192.168.1.0/24".parse().unwrap(); - let network3 = "172.16.4.0/22".parse().unwrap(); - ip_range1.add(network1).add(network2); - ip_range2.add(network3); - - let ip_range = ip_range1.exclude(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); - } - - #[test] - fn exclude_identical() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1: Ipv4Net = "172.16.5.0/24".parse().unwrap(); - let network2 = "192.168.1.0/24".parse().unwrap(); - let network3 = "172.16.4.0/22".parse().unwrap(); - let network4 = "10.0.0.0/8".parse().unwrap(); - - ip_range1.add(network1).add(network2); - ip_range2.add(network3).add(network4); - - let ip_range = ip_range1.exclude(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 1); - assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); - } - - #[test] - fn exclude_split1() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1: Ipv4Net = "172.16.4.0/22".parse().unwrap(); - let network2 = "192.168.1.0/24".parse().unwrap(); - let network3 = "172.16.5.0/24".parse().unwrap(); - let network4 = "10.0.0.0/8".parse().unwrap(); - - ip_range1.add(network1).add(network2); - ip_range2.add(network3).add(network4); - - let ip_range = ip_range1.exclude(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 3); - assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); - assert_eq!( - "172.16.4.0/24".parse().ok(), - ip_range.get_network(24, "172.16.4.0") - ); - assert_eq!( - "172.16.6.0/23".parse().ok(), - ip_range.get_network(23, "172.16.6.0") - ); - } - - #[test] - fn exclude_split2() { - let mut ip_range1 = IpRange::new(); - let mut ip_range2 = IpRange::new(); - let network1: Ipv4Net = "172.16.4.0/22".parse().unwrap(); - let network2 = "192.168.1.0/24".parse().unwrap(); - let network3 = "172.16.4.0/24".parse().unwrap(); - let network4 = "10.0.0.0/8".parse().unwrap(); - - ip_range1.add(network1).add(network2); - ip_range2.add(network3).add(network4); - - let ip_range = ip_range1.exclude(&ip_range2); - assert_eq!(ip_range.into_iter().count(), 3); - assert_eq!(Some(network2), ip_range.get_network(24, "192.168.1.0")); - assert_eq!( - "172.16.5.0/24".parse().ok(), - ip_range.get_network(24, "172.16.5.0") - ); - assert_eq!( - "172.16.6.0/23".parse().ok(), - ip_range.get_network(23, "172.16.6.0") - ); - } - - #[test] - fn iter_ipv4() { - let mut data = vec!["1.0.1.0/24", "1.0.2.0/23", "1.0.8.0/21"]; - let ip_range: IpRange = data.iter().map(|net| net.parse().unwrap()).collect(); - let mut nets: Vec = ip_range.iter().map(|net| format!("{}", net)).collect(); - data.sort_unstable(); - nets.sort_unstable(); - assert_eq!(nets, data); - } - - #[test] - fn iter_ipv6() { - let mut data = vec![ - "2400:9a40::/32", - "2400:9dc0::/32", - "2400:9e00::/32", - "2400:a040::/32", - ]; - let ip_range: IpRange = data.iter().map(|net| net.parse().unwrap()).collect(); - let mut nets: Vec = ip_range.iter().map(|net| format!("{}", net)).collect(); - data.sort_unstable(); - nets.sort_unstable(); - assert_eq!(nets, data); - } - - #[test] - fn debug_fmt() { - let ip_range: IpRange = IpRange::default(); - assert_eq!(format!("{:?}", ip_range), "IpRange []"); - - let ip_range: IpRange = ["1.0.1.0/24", "1.0.2.0/23", "1.0.8.0/21"] - .iter() - .map(|net| net.parse().unwrap()) - .collect(); - assert_eq!( - format!("{:?}", ip_range), - "IpRange [1.0.8.0/21, 1.0.2.0/23, 1.0.1.0/24]" - ); - - let ip_range: IpRange = [ - "192.168.0.0/16", - "1.0.2.0/23", - "1.0.8.0/21", - "127.0.0.0/8", - "172.16.0.0/12", - ] - .iter() - .map(|net| net.parse().unwrap()) - .collect(); - assert_eq!( - format!("{:?}", ip_range), - "IpRange [127.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, ...]" - ); - - let ip_range: IpRange = [ - "2001:4438::/32", - "2001:4510::/29", - "2400:1040::/32", - "2400:12c0::/32", - "2400:1340::/32", - "2400:1380::/32", - "2400:15c0::/32", - ] - .iter() - .map(|net| net.parse().unwrap()) - .collect(); - assert_eq!( - format!("{:?}", ip_range), - "IpRange [2001:4510::/29, 2001:4438::/32, 2400:1040::/32, ...]" - ); - } - - #[test] - fn remove_all() { - let mut ip_range = IpRange::new(); - let network: Ipv4Net = "1.0.1.0/24".parse().unwrap(); - ip_range.add(network); - ip_range.remove(network); - assert!(ip_range.iter().next().is_none()); - } - - #[test] - fn add_to_all_zeros() { - let mut ip_range: IpRange = IpRange::new(); - ip_range.add("0.0.0.0/0".parse().unwrap()); - ip_range.add("127.0.0.1/32".parse().unwrap()); - assert!(ip_range.contains_network("0.0.0.0/0")); - } - - #[test] - #[cfg(feature = "serde")] - fn serialize_ipv4_as_binary() { - let mut ip_range: IpRange = IpRange::new(); - ip_range.add("0.0.0.0/0".parse().unwrap()); - ip_range.add("127.0.0.1/32".parse().unwrap()); - ip_range.add("254.254.254.254/32".parse().unwrap()); - let encoded: Vec = bincode::serialize(&ip_range).unwrap(); - let decoded_ip_range: IpRange = bincode::deserialize(&encoded[..]).unwrap(); - assert_eq!(ip_range, decoded_ip_range); - } - - #[test] - #[cfg(feature = "serde")] - fn serialize_ipv6_as_binary() { - let mut ip_range: IpRange = IpRange::new(); - ip_range.add("2001:4438::/32".parse().unwrap()); - ip_range.add("2400:1040::/32".parse().unwrap()); - ip_range.add("2400:1340::/32".parse().unwrap()); - let encoded: Vec = bincode::serialize(&ip_range).unwrap(); - let decoded_ip_range: IpRange = bincode::deserialize(&encoded[..]).unwrap(); - assert_eq!(ip_range, decoded_ip_range); - } -}