mod ip_collection;
use crate::bogon::ip_collection::IpCollection;
use std::net::IpAddr;
use std::sync::LazyLock;
pub(crate) fn is_bogon(ip: IpAddr) -> Option<&'static str> {
for bogon in BOGONS.iter() {
if bogon.range.contains(&ip) {
return Some(bogon.description);
}
}
None
}
struct Bogon {
range: IpCollection,
description: &'static str,
}
static BOGONS: LazyLock<Vec<&'static Bogon>> = LazyLock::new(|| {
vec![
&THIS_NETWORK,
&PRIVATE_USE,
&CARRIER_GRADE,
&LOOPBACK,
&LINK_LOCAL,
&IETF_PROTOCOL,
&TEST_NET_1,
&NETWORK_INTERCONNECT,
&TEST_NET_2,
&TEST_NET_3,
&MULTICAST,
&FUTURE_USE,
&NODE_SCOPE_UNSPECIFIED,
&NODE_SCOPE_LOOPBACK,
&IPV4_MAPPED,
&IPV4_COMPATIBLE,
&REMOTELY_TRIGGERED,
&ORCHID,
&DOCUMENTATION_PREFIX,
&ULA,
&LINK_LOCAL_UNICAST,
&SITE_LOCAL_UNICAST,
&MULTICAST_V6,
]
});
static THIS_NETWORK: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("0.0.0.0-0.255.255.255").unwrap(),
description: "\"this\" network",
});
static PRIVATE_USE: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new(
"10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255",
)
.unwrap(),
description: "private-use",
});
static CARRIER_GRADE: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("100.64.0.0-100.127.255.255").unwrap(),
description: "carrier-grade NAT",
});
static LOOPBACK: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("127.0.0.0-127.255.255.255").unwrap(),
description: "loopback",
});
static LINK_LOCAL: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("169.254.0.0-169.254.255.255").unwrap(),
description: "link local",
});
static IETF_PROTOCOL: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("192.0.0.0-192.0.0.255").unwrap(),
description: "IETF protocol assignments",
});
static TEST_NET_1: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("192.0.2.0-192.0.2.255").unwrap(),
description: "TEST-NET-1",
});
static NETWORK_INTERCONNECT: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("198.18.0.0-198.19.255.255").unwrap(),
description: "network interconnect device benchmark testing",
});
static TEST_NET_2: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("198.51.100.0-198.51.100.255").unwrap(),
description: "TEST-NET-2",
});
static TEST_NET_3: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("203.0.113.0-203.0.113.255").unwrap(),
description: "TEST-NET-3",
});
static MULTICAST: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("224.0.0.0-239.255.255.255").unwrap(),
description: "multicast",
});
static FUTURE_USE: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("240.0.0.0-255.255.255.255").unwrap(),
description: "future use",
});
static NODE_SCOPE_UNSPECIFIED: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("::").unwrap(),
description: "node-scope unicast unspecified",
});
static NODE_SCOPE_LOOPBACK: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("::1").unwrap(),
description: "node-scope unicast loopback",
});
static IPV4_MAPPED: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("::ffff:0.0.0.0-::ffff:255.255.255.255").unwrap(),
description: "IPv4-mapped",
});
static IPV4_COMPATIBLE: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("::-::255.255.255.255").unwrap(),
description: "IPv4-compatible",
});
static REMOTELY_TRIGGERED: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("100::-100::ffff:ffff:ffff:ffff").unwrap(),
description: "remotely triggered black hole",
});
static ORCHID: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("2001:10::-2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "ORCHID",
});
static DOCUMENTATION_PREFIX: LazyLock<Bogon> = LazyLock::new(|| {
Bogon {
range: IpCollection::new("2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff, 3fff::-3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff")
.unwrap(),
description: "documentation prefix",
}
});
static ULA: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "ULA",
});
static LINK_LOCAL_UNICAST: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "link-local unicast",
});
static SITE_LOCAL_UNICAST: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("fec0::-feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "site-local unicast",
});
static MULTICAST_V6: LazyLock<Bogon> = LazyLock::new(|| Bogon {
range: IpCollection::new("ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(),
description: "multicast v6",
});
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_is_bogon_no() {
assert_eq!(is_bogon(IpAddr::from_str("8.8.8.8").unwrap()), None);
assert_eq!(
is_bogon(IpAddr::from_str("2001:4860:4860::8888").unwrap()),
None
);
}
#[test]
fn test_is_bogon_this_network() {
assert_eq!(
is_bogon(IpAddr::from_str("0.1.2.3").unwrap()),
Some("\"this\" network")
);
}
#[test]
fn test_is_bogon_private_use_networks() {
assert_eq!(
is_bogon(IpAddr::from_str("10.1.2.3").unwrap()),
Some("private-use")
);
assert_eq!(
is_bogon(IpAddr::from_str("172.22.2.3").unwrap()),
Some("private-use")
);
assert_eq!(
is_bogon(IpAddr::from_str("192.168.255.3").unwrap()),
Some("private-use")
);
}
#[test]
fn test_is_bogon_carrier_grade() {
assert_eq!(
is_bogon(IpAddr::from_str("100.99.2.1").unwrap()),
Some("carrier-grade NAT")
);
}
#[test]
fn test_is_bogon_loopback() {
assert_eq!(
is_bogon(IpAddr::from_str("127.99.2.1").unwrap()),
Some("loopback")
);
}
#[test]
fn test_is_bogon_link_local() {
assert_eq!(
is_bogon(IpAddr::from_str("169.254.0.0").unwrap()),
Some("link local")
);
}
#[test]
fn test_is_bogon_ietf() {
assert_eq!(
is_bogon(IpAddr::from_str("192.0.0.255").unwrap()),
Some("IETF protocol assignments")
);
}
#[test]
fn test_is_bogon_test_net_1() {
assert_eq!(
is_bogon(IpAddr::from_str("192.0.2.128").unwrap()),
Some("TEST-NET-1")
);
}
#[test]
fn test_is_bogon_network_interconnect() {
assert_eq!(
is_bogon(IpAddr::from_str("198.18.2.128").unwrap()),
Some("network interconnect device benchmark testing")
);
}
#[test]
fn test_is_bogon_test_net_2() {
assert_eq!(
is_bogon(IpAddr::from_str("198.51.100.128").unwrap()),
Some("TEST-NET-2")
);
}
#[test]
fn test_is_bogon_test_net_3() {
assert_eq!(
is_bogon(IpAddr::from_str("203.0.113.128").unwrap()),
Some("TEST-NET-3")
);
}
#[test]
fn test_is_bogon_multicast() {
assert_eq!(
is_bogon(IpAddr::from_str("224.12.13.255").unwrap()),
Some("multicast")
);
}
#[test]
fn test_is_bogon_future_use() {
assert_eq!(
is_bogon(IpAddr::from_str("240.0.0.0").unwrap()),
Some("future use")
);
}
#[test]
fn test_node_scope_unspecified() {
assert_eq!(
is_bogon(IpAddr::from_str("::").unwrap()),
Some("node-scope unicast unspecified")
);
}
#[test]
fn test_node_scope_loopback() {
assert_eq!(
is_bogon(IpAddr::from_str("::1").unwrap()),
Some("node-scope unicast loopback")
);
}
#[test]
fn test_ipv4_mapped() {
assert_eq!(
is_bogon(IpAddr::from_str("::ffff:8.8.8.8").unwrap()),
Some("IPv4-mapped")
);
}
#[test]
fn test_ipv4_compatible() {
assert_eq!(
is_bogon(IpAddr::from_str("::8.8.8.8").unwrap()),
Some("IPv4-compatible")
);
}
#[test]
fn test_remotely_triggered() {
assert_eq!(
is_bogon(IpAddr::from_str("100::beef").unwrap()),
Some("remotely triggered black hole")
);
}
#[test]
fn test_orchid() {
assert_eq!(
is_bogon(IpAddr::from_str("2001:10::feed").unwrap()),
Some("ORCHID")
);
}
#[test]
fn test_documentation_prefix() {
assert_eq!(
is_bogon(IpAddr::from_str("2001:db8::fe90").unwrap()),
Some("documentation prefix")
);
assert_eq!(
is_bogon(IpAddr::from_str("3fff::").unwrap()),
Some("documentation prefix")
);
}
#[test]
fn test_ula() {
assert_eq!(is_bogon(IpAddr::from_str("fdff::").unwrap()), Some("ULA"));
}
#[test]
fn test_link_local_unicast() {
assert_eq!(
is_bogon(IpAddr::from_str("feaf::").unwrap()),
Some("link-local unicast")
);
}
#[test]
fn test_site_local_unicast() {
assert_eq!(
is_bogon(IpAddr::from_str("feea::1").unwrap()),
Some("site-local unicast")
);
}
#[test]
fn test_is_bogon_multicast_v6() {
assert_eq!(
is_bogon(IpAddr::from_str("ff02::1").unwrap()),
Some("multicast v6")
);
}
}