aboutsummaryrefslogtreecommitdiff
path: root/src/common/ip.rs
diff options
context:
space:
mode:
authorevuez <julien@mulga.net>2022-11-26 15:38:06 -0500
committerevuez <julien@mulga.net>2024-04-03 22:44:12 +0200
commit86098797034cbc7eb6db0cee54e17f8dcaedbc5d (patch)
tree29b6225ead843eb9022296a54657bbadfa1c4da0 /src/common/ip.rs
downloadblom-main.tar.gz
Initial commitHEADmain
Diffstat (limited to 'src/common/ip.rs')
-rw-r--r--src/common/ip.rs195
1 files changed, 195 insertions, 0 deletions
diff --git a/src/common/ip.rs b/src/common/ip.rs
new file mode 100644
index 0000000..8c1e6b6
--- /dev/null
+++ b/src/common/ip.rs
@@ -0,0 +1,195 @@
+#![allow(clippy::from_over_into)]
+use crate::common::expr::Expr;
+use memchr::memchr;
+use std::cmp::Ordering;
+use std::collections::HashMap;
+use std::fmt;
+use std::fmt::Debug;
+use std::net::IpAddr;
+use std::net::Ipv4Addr;
+use std::net::Ipv6Addr;
+use std::str::FromStr;
+
+pub const BITMASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
+pub const BITCOUNT: u8 = 128;
+
+pub type Addr = IpAddr;
+
+#[derive(Debug)]
+pub enum Error {
+ InvalidAddr(String),
+ InvalidBlock(String),
+ InvalidMaskLength(String),
+ InvalidNibblesCount(usize),
+ MaskLengthTooLarge(u8),
+}
+
+#[derive(Copy, Clone, Eq)]
+pub struct Block {
+ pub addr: u128,
+ pub mlen: u8,
+}
+
+pub type Meta = HashMap<String, Expr>;
+
+impl Block {
+ fn new(addr: IpAddr, mlen: u8) -> Self {
+ let (ipv6_addr, ipv6_mlen) = match addr {
+ IpAddr::V4(addr) => {
+ assert!(mlen <= 32);
+ (addr.to_ipv6_mapped(), mlen + 96)
+ }
+ IpAddr::V6(addr) => {
+ assert!(mlen <= 128);
+ (addr, mlen)
+ }
+ };
+
+ let segments = ipv6_addr.segments();
+ let addr_bits = ((segments[0] as u128) << 112)
+ | ((segments[1] as u128) << 96)
+ | ((segments[2] as u128) << 80)
+ | ((segments[3] as u128) << 64)
+ | ((segments[4] as u128) << 48)
+ | ((segments[5] as u128) << 32)
+ | ((segments[6] as u128) << 16)
+ | (segments[7] as u128);
+
+ Self {
+ addr: addr_bits,
+ mlen: ipv6_mlen,
+ }
+ }
+
+ pub fn from_bytestring(bytestring: &[u8]) -> Result<Self, Error> {
+ match memchr(b'/', bytestring) {
+ Some(pos) => {
+ let addr = IpAddr::parse_ascii(&bytestring[0..pos]).map_err(|_| {
+ Error::InvalidAddr(
+ std::str::from_utf8(&bytestring[0..pos])
+ .unwrap()
+ .to_string(),
+ )
+ })?;
+
+ let mut mlen = 0;
+ for (i, byte) in bytestring[pos + 1..bytestring.len()]
+ .iter()
+ .rev()
+ .enumerate()
+ {
+ if *byte < 48 || *byte > 57 {
+ return Err(Error::InvalidBlock("Invalid1".to_string()));
+ }
+ mlen += (byte - 48) * 10u8.pow(i as u32);
+ }
+
+ Ok(Self::new(addr, mlen))
+ }
+ None => Err(Error::InvalidBlock(
+ std::str::from_utf8(bytestring).unwrap().to_string(),
+ )),
+ }
+ }
+
+ fn addr(&self) -> IpAddr {
+ addr_from_bits(self.addr)
+ }
+
+ pub fn start(&self) -> IpAddr {
+ let mask = 0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff ^ ((1 << (128 - self.mlen)) - 1);
+ addr_from_bits(self.addr & mask)
+ }
+ pub fn end(&self) -> IpAddr {
+ let mask = !(0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ffff ^ ((1 << (128 - self.mlen)) - 1));
+ addr_from_bits(self.addr | mask)
+ }
+
+ pub fn size(&self) -> u128 {
+ 2u128.pow(128 - self.mlen as u32)
+ }
+}
+
+fn addr_from_bits(bits: u128) -> IpAddr {
+ if bits & 0xffff_ffff_ffff_ffff_ffff_ffff_0000_0000 == 0xffff_0000_0000 {
+ IpAddr::V4(Ipv4Addr::from((bits & 0xff_ff_ff_ff) as u32))
+ } else {
+ IpAddr::V6(Ipv6Addr::from(bits))
+ }
+}
+
+impl FromStr for Block {
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Block, Error> {
+ let parts: Vec<&str> = s.splitn(2, '/').collect();
+ if parts.len() < 2 {
+ return Err(Error::InvalidBlock(s.to_string()));
+ }
+
+ let addr =
+ IpAddr::from_str(parts[0]).map_err(|_| Error::InvalidAddr(parts[0].to_string()))?;
+ let mlen = parts[1]
+ .parse::<u8>()
+ .map_err(|_| Error::InvalidMaskLength(parts[1].to_string()))?;
+
+ Ok(Self::new(addr, mlen))
+ }
+}
+
+impl fmt::Display for Block {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let addr = self.addr();
+ let mlen = if addr.is_ipv4() {
+ self.mlen - 96
+ } else {
+ self.mlen
+ };
+ write!(f, "{}/{}", addr, mlen)
+ }
+}
+
+impl fmt::Debug for Block {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let addr = self.addr();
+ let mlen = if addr.is_ipv4() {
+ self.mlen - 96
+ } else {
+ self.mlen
+ };
+ write!(f, "{}/{}", addr, mlen)
+ }
+}
+
+impl Ord for Block {
+ fn cmp(&self, other: &Self) -> Ordering {
+ (self.addr >> (BITCOUNT - self.mlen) as usize)
+ .cmp(&(other.addr >> ((BITCOUNT - other.mlen) % 32) as usize))
+ }
+}
+
+impl PartialEq for Block {
+ fn eq(&self, other: &Self) -> bool {
+ self.addr == other.addr && self.mlen == other.mlen
+ }
+}
+
+impl PartialOrd for Block {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(
+ (self.addr >> (BITCOUNT - self.mlen) as usize)
+ .cmp(&(other.addr >> ((BITCOUNT - other.mlen) % 32) as usize)),
+ )
+ }
+}
+
+impl Into<Vec<u8>> for Block {
+ fn into(self) -> Vec<u8> {
+ (&self).into()
+ }
+}
+
+impl Into<Vec<u8>> for &Block {
+ fn into(self) -> Vec<u8> {
+ self.to_string().into()
+ }
+}