#![doc(
html_logo_url = "https://raw.githubusercontent.com/bgpkit/assets/main/logos/icon-transparent.png",
html_favicon_url = "https://raw.githubusercontent.com/bgpkit/assets/main/logos/favicon.ico"
)]
#[cfg(feature = "export")]
mod export;
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
use prefix_trie::PrefixMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct IpCount {
pub ipv4: u64,
pub ipv6: Option<u128>,
}
#[derive(Default)]
pub struct IpnetTrie<T> {
ipv4: PrefixMap<Ipv4Net, T>,
ipv6: PrefixMap<Ipv6Net, T>,
}
impl<T> Clone for IpnetTrie<T>
where
T: Clone,
{
fn clone(&self) -> Self {
Self {
ipv4: self.ipv4.clone(),
ipv6: self.ipv6.clone(),
}
}
}
pub fn exclude_prefix(source: IpNet, target: IpNet) -> Vec<IpNet> {
let new_prefixes = match source.contains(&target) {
true => {
source
.subnets(target.prefix_len())
.unwrap()
.into_iter()
.filter(|p| *p != target)
.collect()
}
false => {
vec![source]
}
};
IpNet::aggregate(&new_prefixes)
}
impl<T> IpnetTrie<T> {
pub fn new() -> Self {
Self {
ipv4: PrefixMap::new(),
ipv6: PrefixMap::new(),
}
}
pub fn len(&self) -> (usize, usize) {
(self.ipv4.iter().count(), self.ipv6.iter().count())
}
pub fn is_empty(&self) -> bool {
self.ipv4.iter().next().is_none() && self.ipv6.iter().next().is_none()
}
pub fn insert<N: Into<IpNet>>(&mut self, network: N, data: T) -> Option<T> {
match network.into() {
IpNet::V4(ipv4_network) => self.ipv4.insert(ipv4_network, data),
IpNet::V6(ipv6_network) => self.ipv6.insert(ipv6_network, data),
}
}
pub fn remove<N: Into<IpNet>>(&mut self, network: N) -> Option<T> {
match network.into() {
IpNet::V4(ipv4_network) => self.ipv4.remove(&ipv4_network),
IpNet::V6(ipv6_network) => self.ipv6.remove(&ipv6_network),
}
}
pub fn exact_match<N: Into<IpNet>>(&self, network: N) -> Option<&T> {
match network.into() {
IpNet::V4(ipv4_network) => self.ipv4.get(&ipv4_network),
IpNet::V6(ipv6_network) => self.ipv6.get(&ipv6_network),
}
}
pub fn exact_match_mut<N: Into<IpNet>>(&mut self, network: N) -> Option<&mut T> {
match network.into() {
IpNet::V4(ipv4_network) => self.ipv4.get_mut(&ipv4_network),
IpNet::V6(ipv6_network) => self.ipv6.get_mut(&ipv6_network),
}
}
pub fn longest_match(&self, ipnet: &IpNet) -> Option<(IpNet, &T)> {
match ipnet {
IpNet::V4(net) => self
.longest_match_ipv4(net)
.map(|(net, data)| (IpNet::V4(*net), data)),
IpNet::V6(net) => self
.longest_match_ipv6(net)
.map(|(net, data)| (IpNet::V6(*net), data)),
}
}
pub fn longest_match_mut(&mut self, ipnet: &IpNet) -> Option<(IpNet, &mut T)> {
match ipnet {
IpNet::V4(net) => self
.longest_match_ipv4_mut(net)
.map(|(net, data)| (IpNet::V4(*net), data)),
IpNet::V6(net) => self
.longest_match_ipv6_mut(net)
.map(|(net, data)| (IpNet::V6(*net), data)),
}
}
#[inline]
pub fn longest_match_ipv4(&self, net: &Ipv4Net) -> Option<(&Ipv4Net, &T)> {
self.ipv4.get_lpm(net)
}
#[inline]
pub fn longest_match_ipv6(&self, net: &Ipv6Net) -> Option<(&Ipv6Net, &T)> {
self.ipv6.get_lpm(net)
}
#[inline]
pub fn longest_match_ipv4_mut(&mut self, net: &Ipv4Net) -> Option<(&Ipv4Net, &mut T)> {
self.ipv4.get_lpm_mut(net)
}
#[inline]
pub fn longest_match_ipv6_mut(&mut self, net: &Ipv6Net) -> Option<(&Ipv6Net, &mut T)> {
self.ipv6.get_lpm_mut(net)
}
pub fn matches(&self, ipnet: &IpNet) -> Vec<(IpNet, &T)> {
match ipnet {
IpNet::V4(net) => self
.matches_ipv4(net)
.into_iter()
.map(|(net, data)| (IpNet::V4(*net), data))
.collect(),
IpNet::V6(net) => self
.matches_ipv6(net)
.into_iter()
.map(|(net, data)| (IpNet::V6(*net), data))
.collect(),
}
}
pub fn matches_ipv4(&self, net: &Ipv4Net) -> Vec<(&Ipv4Net, &T)> {
match self.ipv4.get_spm(net) {
None => vec![],
Some((shortest, _)) => self.ipv4.children(*shortest).collect(),
}
}
pub fn matches_ipv6(&self, net: &Ipv6Net) -> Vec<(&Ipv6Net, &T)> {
match self.ipv6.get_spm(net) {
None => vec![],
Some((shortest, _)) => self.ipv6.children(*shortest).collect(),
}
}
pub fn iter(&self) -> impl Iterator<Item = (IpNet, &T)> {
self.iter_ipv4()
.map(|(network, data)| (IpNet::V4(*network), data))
.chain(
self.iter_ipv6()
.map(|(network, data)| (IpNet::V6(*network), data)),
)
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (IpNet, &mut T)> {
self.ipv4
.iter_mut()
.map(|(net, data)| (IpNet::from(*net), data))
.chain(
self.ipv6
.iter_mut()
.map(|(net, data)| (IpNet::from(*net), data)),
)
}
pub fn iter_ipv4(&self) -> impl Iterator<Item = (&Ipv4Net, &T)> {
self.ipv4.iter()
}
pub fn iter_ipv6(&self) -> impl Iterator<Item = (&Ipv6Net, &T)> {
self.ipv6.iter()
}
pub fn retain<F>(&mut self, mut f: F)
where
F: FnMut(IpNet, &mut T) -> bool,
{
let mut to_delete = vec![];
for (network, data) in self.iter_mut() {
if !f(network, data) {
to_delete.push(network);
}
}
for network in to_delete {
self.remove(network);
}
}
pub fn ip_count(&self) -> IpCount {
let (root_ipv4_prefixes, root_ipv6_prefixes) = self.get_aggregated_prefixes();
let mut ipv4_space: u64 = 0;
for prefix in root_ipv4_prefixes {
ipv4_space += 1u64 << (32 - prefix.prefix_len() as u32);
}
let mut ipv6_space: u128 = 0;
let mut ipv6_full = false;
for prefix in root_ipv6_prefixes {
let host_bits = 128 - prefix.prefix_len() as u32;
match 1u128
.checked_shl(host_bits)
.and_then(|c| ipv6_space.checked_add(c))
{
Some(v) => ipv6_space = v,
None => {
ipv6_full = true;
break;
}
}
}
IpCount {
ipv4: ipv4_space,
ipv6: if ipv6_full { None } else { Some(ipv6_space) },
}
}
pub fn get_aggregated_prefixes(&self) -> (Vec<Ipv4Net>, Vec<Ipv6Net>) {
let mut all_prefixes = self
.ipv4
.iter()
.map(|(net, _data)| IpNet::from(*net))
.collect::<Vec<IpNet>>();
all_prefixes.extend(self.ipv6.iter().map(|(net, _data)| IpNet::from(*net)));
let aggregated_prefixes = IpNet::aggregate(&all_prefixes);
let mut ipv4_prefixes = Vec::new();
let mut ipv6_prefixes = Vec::new();
for prefix in aggregated_prefixes {
match prefix {
IpNet::V4(net) => ipv4_prefixes.push(net),
IpNet::V6(net) => ipv6_prefixes.push(net),
}
}
(ipv4_prefixes, ipv6_prefixes)
}
pub fn diff(&self, other: &Self) -> (Vec<IpNet>, Vec<IpNet>) {
let mut added = IpnetTrie::<bool>::new();
let mut removed = IpnetTrie::<bool>::new();
let (self_ipv4_prefixes, self_ipv6_prefixes) = self.get_aggregated_prefixes();
let (other_ipv4_prefixes, other_ipv6_prefixes) = other.get_aggregated_prefixes();
let mut self_ipv4_map: PrefixMap<Ipv4Net, bool> = PrefixMap::new();
for prefix in &self_ipv4_prefixes {
self_ipv4_map.insert(*prefix, true);
}
let mut other_ipv4_map: PrefixMap<Ipv4Net, bool> = PrefixMap::new();
for prefix in &other_ipv4_prefixes {
other_ipv4_map.insert(*prefix, true);
}
for v4_prefix in &other_ipv4_prefixes {
if self_ipv4_map.get_lpm(v4_prefix).is_some() {
continue;
}
let sub_prefixes = IpNet::aggregate(
&self_ipv4_map
.children(*v4_prefix)
.map(|(p, _)| IpNet::from(*p))
.collect::<Vec<IpNet>>(),
);
if sub_prefixes.is_empty() {
added.insert(*v4_prefix, true);
} else {
let mut target_prefixes: Vec<IpNet> = vec![(*v4_prefix).into()];
for sub_prefix in sub_prefixes {
let mut new_prefixes = vec![];
for target_prefix in target_prefixes {
new_prefixes.extend(exclude_prefix(target_prefix, sub_prefix));
}
target_prefixes = IpNet::aggregate(&new_prefixes);
}
for target_prefix in target_prefixes {
added.insert(target_prefix, true);
}
}
}
for v4_prefix in &self_ipv4_prefixes {
if other_ipv4_map.get_lpm(v4_prefix).is_some() {
continue;
}
let sub_prefixes = IpNet::aggregate(
&other_ipv4_map
.children(*v4_prefix)
.map(|(p, _)| IpNet::from(*p))
.collect::<Vec<IpNet>>(),
);
if sub_prefixes.is_empty() {
removed.insert(*v4_prefix, true);
} else {
let mut target_prefixes: Vec<IpNet> = vec![(*v4_prefix).into()];
for sub_prefix in sub_prefixes {
let mut new_prefixes = vec![];
for target_prefix in target_prefixes {
new_prefixes.extend(exclude_prefix(target_prefix, sub_prefix));
}
target_prefixes = IpNet::aggregate(&new_prefixes);
}
for target_prefix in target_prefixes {
removed.insert(target_prefix, true);
}
}
}
let mut self_ipv6_map: PrefixMap<Ipv6Net, bool> = PrefixMap::new();
for prefix in &self_ipv6_prefixes {
self_ipv6_map.insert(*prefix, true);
}
let mut other_ipv6_map: PrefixMap<Ipv6Net, bool> = PrefixMap::new();
for prefix in &other_ipv6_prefixes {
other_ipv6_map.insert(*prefix, true);
}
for v6_prefix in &other_ipv6_prefixes {
if self_ipv6_map.get_lpm(v6_prefix).is_some() {
continue;
}
let sub_prefixes = IpNet::aggregate(
&self_ipv6_map
.children(*v6_prefix)
.map(|(p, _)| IpNet::from(*p))
.collect::<Vec<IpNet>>(),
);
if sub_prefixes.is_empty() {
added.insert(*v6_prefix, true);
} else {
let mut target_prefixes: Vec<IpNet> = vec![(*v6_prefix).into()];
for sub_prefix in sub_prefixes {
let mut new_prefixes = vec![];
for target_prefix in target_prefixes {
new_prefixes.extend(exclude_prefix(target_prefix, sub_prefix));
}
target_prefixes = IpNet::aggregate(&new_prefixes);
}
for target_prefix in target_prefixes {
added.insert(target_prefix, true);
}
}
}
for v6_prefix in &self_ipv6_prefixes {
if other_ipv6_map.get_lpm(v6_prefix).is_some() {
continue;
}
let sub_prefixes = IpNet::aggregate(
&other_ipv6_map
.children(*v6_prefix)
.map(|(p, _)| IpNet::from(*p))
.collect::<Vec<IpNet>>(),
);
if sub_prefixes.is_empty() {
removed.insert(*v6_prefix, true);
} else {
let mut target_prefixes: Vec<IpNet> = vec![(*v6_prefix).into()];
for sub_prefix in sub_prefixes {
let mut new_prefixes = vec![];
for target_prefix in target_prefixes {
new_prefixes.extend(exclude_prefix(target_prefix, sub_prefix));
}
target_prefixes = IpNet::aggregate(&new_prefixes);
}
for target_prefix in target_prefixes {
removed.insert(target_prefix, true);
}
}
}
(
added.iter().map(|(p, _)| p).collect(),
removed.iter().map(|(p, _)| p).collect(),
)
}
}
#[cfg(test)]
mod tests {
use crate::IpnetTrie;
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
#[test]
fn insert_ipv4_ipv6() {
let mut table = IpnetTrie::new();
table.insert(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 0), 16).unwrap(), 1);
table.insert(
Ipv6Net::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128).unwrap(),
1,
);
}
#[test]
fn exact_match_ipv4() {
let mut table = IpnetTrie::new();
table.insert(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 0), 16).unwrap(), 1);
let m = table.exact_match(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 0), 16).unwrap());
assert_eq!(m, Some(&1));
let m = table.exact_match(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 0), 17).unwrap());
assert_eq!(m, None);
let m = table.exact_match(Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 0), 15).unwrap());
assert_eq!(m, None);
}
#[test]
fn exact_match_ipv6() {
let mut table = IpnetTrie::new();
table.insert(
Ipv6Net::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 127).unwrap(),
1,
);
let m =
table.exact_match(Ipv6Net::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 127).unwrap());
assert_eq!(m, Some(&1));
let m =
table.exact_match(Ipv6Net::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 128).unwrap());
assert_eq!(m, None);
let m =
table.exact_match(Ipv6Net::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 126).unwrap());
assert_eq!(m, None);
}
#[test]
fn test_ip_count() {
use crate::IpCount;
let mut table = IpnetTrie::new();
table.insert(Ipv4Net::from_str("192.0.2.129/25").unwrap(), 1);
table.insert(Ipv4Net::from_str("192.0.2.0/24").unwrap(), 1);
table.insert(Ipv4Net::from_str("192.0.2.0/24").unwrap(), 1);
table.insert(Ipv4Net::from_str("192.0.2.0/24").unwrap(), 1);
assert_eq!(
table.ip_count(),
IpCount {
ipv4: 256,
ipv6: Some(0)
}
);
table.insert(Ipv4Net::from_str("198.51.100.0/25").unwrap(), 1);
table.insert(Ipv4Net::from_str("198.51.100.64/26").unwrap(), 1);
assert_eq!(
table.ip_count(),
IpCount {
ipv4: 384,
ipv6: Some(0)
}
);
table.insert(Ipv4Net::from_str("198.51.100.65/26").unwrap(), 1);
assert_eq!(
table.ip_count(),
IpCount {
ipv4: 384,
ipv6: Some(0)
}
);
table.insert(Ipv6Net::from_str("2001:DB80::/48").unwrap(), 1);
assert_eq!(
table.ip_count(),
IpCount {
ipv4: 384,
ipv6: Some(2_u128.pow(80))
}
);
table.insert(Ipv6Net::from_str("2001:DB80::/49").unwrap(), 1);
assert_eq!(
table.ip_count(),
IpCount {
ipv4: 384,
ipv6: Some(2_u128.pow(80))
}
);
}
#[test]
fn test_ip_count_full_space() {
use crate::IpCount;
let mut v4_full = IpnetTrie::new();
v4_full.insert(Ipv4Net::from_str("0.0.0.0/0").unwrap(), 1);
assert_eq!(
v4_full.ip_count(),
IpCount {
ipv4: 1u64 << 32,
ipv6: Some(0)
}
);
let mut v6_full = IpnetTrie::new();
v6_full.insert(Ipv6Net::from_str("::/0").unwrap(), 1);
assert_eq!(
v6_full.ip_count(),
IpCount {
ipv4: 0,
ipv6: None
}
);
let mut v6_two_halves = IpnetTrie::new();
v6_two_halves.insert(Ipv6Net::from_str("::/1").unwrap(), 1);
v6_two_halves.insert(Ipv6Net::from_str("8000::/1").unwrap(), 1);
assert_eq!(
v6_two_halves.ip_count(),
IpCount {
ipv4: 0,
ipv6: None
}
);
let mut both = IpnetTrie::new();
both.insert(Ipv4Net::from_str("0.0.0.0/0").unwrap(), 1);
both.insert(Ipv6Net::from_str("::/0").unwrap(), 1);
assert_eq!(
both.ip_count(),
IpCount {
ipv4: 1u64 << 32,
ipv6: None
}
);
}
#[test]
fn test_comparison() {
let mut trie_1 = IpnetTrie::new();
trie_1.insert(Ipv4Net::from_str("192.168.0.0/23").unwrap(), 1);
trie_1.insert(Ipv4Net::from_str("192.168.2.0/24").unwrap(), 1);
let mut trie_2 = IpnetTrie::new();
trie_2.insert(Ipv4Net::from_str("192.168.2.0/24").unwrap(), 1);
let (_added, removed) = trie_1.diff(&trie_2);
assert_eq!(removed.len(), 1);
assert_eq!(
removed[0],
IpNet::V4(Ipv4Net::from_str("192.168.0.0/23").unwrap())
);
trie_2.insert(Ipv4Net::from_str("192.168.0.0/24").unwrap(), 1);
let (_added, removed) = trie_1.diff(&trie_2);
assert_eq!(removed.len(), 1);
assert_eq!(
removed[0],
IpNet::from(Ipv4Net::from_str("192.168.1.0/24").unwrap())
);
trie_2.insert(Ipv4Net::from_str("192.168.3.0/24").unwrap(), 1);
let (added, removed) = trie_1.diff(&trie_2);
assert_eq!(removed.len(), 1);
assert_eq!(
removed[0],
IpNet::from(Ipv4Net::from_str("192.168.1.0/24").unwrap())
);
assert_eq!(added.len(), 1);
assert_eq!(
added[0],
IpNet::from(Ipv4Net::from_str("192.168.3.0/24").unwrap())
);
trie_2.insert(Ipv6Net::from_str("2001:DB80::/48").unwrap(), 1);
let (added, removed) = trie_1.diff(&trie_2);
assert_eq!(removed.len(), 1);
assert_eq!(
removed[0],
IpNet::from(Ipv4Net::from_str("192.168.1.0/24").unwrap())
);
assert_eq!(added.len(), 2);
assert_eq!(
added[0],
IpNet::from(Ipv4Net::from_str("192.168.3.0/24").unwrap())
);
assert_eq!(
added[1],
IpNet::from(Ipv6Net::from_str("2001:DB80::/48").unwrap())
);
}
}