Skip to content

Commit

Permalink
Merge pull request #46 from stormshield-guillaumed/mask-to-prefix
Browse files Browse the repository at this point in the history
Add mask to prefix conversion
  • Loading branch information
krisprice authored Dec 7, 2022
2 parents 6ef505c + 0a0b60c commit f48de8b
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 0 deletions.
58 changes: 58 additions & 0 deletions src/ipnet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::option::Option::{Some, None};

use crate::ipext::{IpAdd, IpSub, IpStep, IpAddrRange, Ipv4AddrRange, Ipv6AddrRange};
use crate::mask::{ip_mask_to_prefix, ipv4_mask_to_prefix, ipv6_mask_to_prefix};

/// An IP network address, either IPv4 or IPv6.
///
Expand Down Expand Up @@ -142,6 +143,25 @@ impl IpNet {
})
}

/// Creates a new IP network address from an `IpAddr` and netmask.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnet::{IpNet, PrefixLenError};
///
/// let net = IpNet::with_netmask(Ipv6Addr::LOCALHOST.into(), Ipv6Addr::from(0xffff_ffff_ffff_0000_0000_0000_0000_0000).into());
/// assert!(net.is_ok());
///
/// let bad_prefix_len = IpNet::with_netmask(Ipv6Addr::LOCALHOST.into(), Ipv6Addr::from(0xffff_ffff_ffff_0000_0001_0000_0000_0000).into());
/// assert_eq!(bad_prefix_len, Err(PrefixLenError));
/// ```
pub fn with_netmask(ip: IpAddr, netmask: IpAddr) -> Result<IpNet, PrefixLenError> {
let prefix = ip_mask_to_prefix(netmask)?;
Self::new(ip, prefix)
}

/// Returns a copy of the network with the address truncated to the
/// prefix length.
///
Expand Down Expand Up @@ -565,6 +585,25 @@ impl Ipv4Net {
Ok(Ipv4Net { addr: ip, prefix_len: prefix_len })
}

/// Creates a new IPv4 network address from an `Ipv4Addr` and netmask.
///
/// # Examples
///
/// ```
/// use std::net::Ipv4Addr;
/// use ipnet::{Ipv4Net, PrefixLenError};
///
/// let net = Ipv4Net::with_netmask(Ipv4Addr::new(10, 1, 1, 0), Ipv4Addr::new(255, 255, 255, 0));
/// assert!(net.is_ok());
///
/// let bad_prefix_len = Ipv4Net::with_netmask(Ipv4Addr::new(10, 1, 1, 0), Ipv4Addr::new(255, 255, 0, 1));
/// assert_eq!(bad_prefix_len, Err(PrefixLenError));
/// ```
pub fn with_netmask(ip: Ipv4Addr, netmask: Ipv4Addr) -> Result<Ipv4Net, PrefixLenError> {
let prefix = ipv4_mask_to_prefix(netmask)?;
Self::new(ip, prefix)
}

/// Returns a copy of the network with the address truncated to the
/// prefix length.
///
Expand Down Expand Up @@ -899,6 +938,25 @@ impl Ipv6Net {
Ok(Ipv6Net { addr: ip, prefix_len: prefix_len })
}

/// Creates a new IPv6 network address from an `Ipv6Addr` and netmask.
///
/// # Examples
///
/// ```
/// use std::net::Ipv6Addr;
/// use ipnet::{Ipv6Net, PrefixLenError};
///
/// let net = Ipv6Net::with_netmask(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 0), Ipv6Addr::from(0xffff_ff00_0000_0000_0000_0000_0000_0000));
/// assert!(net.is_ok());
///
/// let bad_prefix_len = Ipv6Net::with_netmask(Ipv6Addr::new(0xfd, 0, 0, 0, 0, 0, 0, 0), Ipv6Addr::from(0xffff_ff00_0000_0000_0001_0000_0000_0000));
/// assert_eq!(bad_prefix_len, Err(PrefixLenError));
/// ```
pub fn with_netmask(ip: Ipv6Addr, netmask: Ipv6Addr) -> Result<Ipv6Net, PrefixLenError> {
let prefix = ipv6_mask_to_prefix(netmask)?;
Self::new(ip, prefix)
}

/// Returns a copy of the network with the address truncated to the
/// prefix length.
///
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,12 @@ extern crate schemars;
pub use self::ipext::{IpAdd, IpSub, IpBitAnd, IpBitOr, IpAddrRange, Ipv4AddrRange, Ipv6AddrRange};
pub use self::ipnet::{IpNet, Ipv4Net, Ipv6Net, PrefixLenError, IpSubnets, Ipv4Subnets, Ipv6Subnets};
pub use self::parser::AddrParseError;
pub use self::mask::{ip_mask_to_prefix, ipv4_mask_to_prefix, ipv6_mask_to_prefix};

mod ipext;
mod ipnet;
mod parser;
mod mask;
#[cfg(feature = "serde")]
mod ipnet_serde;
#[cfg(feature = "schemars")]
Expand Down
114 changes: 114 additions & 0 deletions src/mask.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use crate::PrefixLenError;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

/// Converts a `IpAddr` network mask into a prefix.
///
/// # Errors
/// If the mask is invalid this will return an `PrefixLenError`.
pub fn ip_mask_to_prefix(mask: IpAddr) -> Result<u8, PrefixLenError> {
match mask {
IpAddr::V4(mask) => ipv4_mask_to_prefix(mask),
IpAddr::V6(mask) => ipv6_mask_to_prefix(mask),
}
}

/// Converts a `Ipv4Addr` network mask into a prefix.
///
/// # Errors
/// If the mask is invalid this will return an `PrefixLenError`.
pub fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, PrefixLenError> {
let mask = u32::from(mask);

let prefix = mask.leading_ones();
if (mask << prefix) == 0 {
Ok(prefix as u8)
} else {
Err(PrefixLenError)
}
}

/// Converts a `Ipv6Addr` network mask into a prefix.
///
/// # Errors
/// If the mask is invalid this will return an `PrefixLenError`.
pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, PrefixLenError> {
let mask = u128::from(mask);

let prefix = mask.leading_ones();
if (mask << prefix) == 0 {
Ok(prefix as u8)
} else {
Err(PrefixLenError)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{Ipv4Net, Ipv6Net};

#[test]
fn v4_mask_to_prefix() {
let mask = Ipv4Addr::new(255, 255, 255, 128);
let prefix = ipv4_mask_to_prefix(mask);
assert_eq!(prefix, Ok(25));
}

#[test]
fn invalid_v4_mask_to_prefix() {
let mask = Ipv4Addr::new(255, 0, 255, 0);
let prefix = ipv4_mask_to_prefix(mask);
assert!(prefix.is_err());
}

#[test]
fn ipv4net_with_netmask() {
{
// Positive test-case.
let addr = Ipv4Addr::new(127, 0, 0, 1);
let mask = Ipv4Addr::new(255, 0, 0, 0);
let net = Ipv4Net::with_netmask(addr, mask).unwrap();
let expected = Ipv4Net::new(Ipv4Addr::new(127, 0, 0, 1), 8).unwrap();
assert_eq!(net, expected);
}
{
// Negative test-case.
let addr = Ipv4Addr::new(127, 0, 0, 1);
let mask = Ipv4Addr::new(255, 0, 255, 0);
Ipv4Net::with_netmask(addr, mask).unwrap_err();
}
}

#[test]
fn v6_mask_to_prefix() {
let mask = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0, 0, 0, 0, 0);
let prefix = ipv6_mask_to_prefix(mask);
assert_eq!(prefix, Ok(48));
}

#[test]
fn invalid_v6_mask_to_prefix() {
let mask = Ipv6Addr::new(0, 0, 0xffff, 0xffff, 0, 0, 0, 0);
let prefix = ipv6_mask_to_prefix(mask);
assert!(prefix.is_err());
}

#[test]
fn ipv6net_with_netmask() {
{
// Positive test-case.
let addr = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2);
let mask = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0, 0, 0, 0, 0);
let net = Ipv6Net::with_netmask(addr, mask).unwrap();
let expected =
Ipv6Net::new(Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2), 48).unwrap();
assert_eq!(net, expected);
}
{
// Negative test-case.
let addr = Ipv6Addr::new(0xff01, 0, 0, 0x17, 0, 0, 0, 0x2);
let mask = Ipv6Addr::new(0, 0, 0xffff, 0xffff, 0, 0, 0, 0);
Ipv6Net::with_netmask(addr, mask).unwrap_err();
}
}
}

0 comments on commit f48de8b

Please sign in to comment.