diff options
author | evuez <julien@mulga.net> | 2022-11-26 15:38:06 -0500 |
---|---|---|
committer | evuez <julien@mulga.net> | 2024-04-03 22:44:12 +0200 |
commit | 86098797034cbc7eb6db0cee54e17f8dcaedbc5d (patch) | |
tree | 29b6225ead843eb9022296a54657bbadfa1c4da0 /src/common/ip.rs | |
download | blom-main.tar.gz |
Diffstat (limited to 'src/common/ip.rs')
-rw-r--r-- | src/common/ip.rs | 195 |
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() + } +} |