Implement wire::{IpCidr/Ipv4Cidr}.
This commit is contained in:
parent
72481c2661
commit
f57de10625
|
@ -1,6 +1,6 @@
|
|||
use core::str::FromStr;
|
||||
use core::result;
|
||||
use wire::{EthernetAddress, IpAddress, Ipv4Address};
|
||||
use wire::{EthernetAddress, IpAddress, Ipv4Address, Ipv4Cidr, IpCidr};
|
||||
|
||||
type Result<T> = result::Result<T, ()>;
|
||||
|
||||
|
@ -164,6 +164,29 @@ impl FromStr for IpAddress {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Ipv4Cidr {
|
||||
type Err = ();
|
||||
|
||||
/// Parse a string representation of an IPv4 CIDR.
|
||||
fn from_str(s: &str) -> Result<Ipv4Cidr> {
|
||||
Parser::new(s).until_eof(|p| {
|
||||
let ip = p.accept_ipv4()?;
|
||||
p.accept_char(b'/')?;
|
||||
let prefix_len = p.accept_number(2, 33, false)? as u8;
|
||||
Ok(Ipv4Cidr::new(ip, prefix_len))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for IpCidr {
|
||||
type Err = ();
|
||||
|
||||
/// Parse a string representation of an IP CIDR.
|
||||
fn from_str(s: &str) -> Result<IpCidr> {
|
||||
Ipv4Cidr::from_str(s).map(IpCidr::Ipv4)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -212,4 +235,36 @@ mod test {
|
|||
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4]))));
|
||||
assert_eq!(IpAddress::from_str("x"), Err(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr() {
|
||||
let tests = [
|
||||
("127.0.0.1/8",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8))),
|
||||
("192.168.1.1/24",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8))),
|
||||
("8.8.8.8/32",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8))),
|
||||
("8.8.8.8/0",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8))),
|
||||
("", Err(())),
|
||||
("1", Err(())),
|
||||
("127.0.0.1", Err(())),
|
||||
("127.0.0.1/", Err(())),
|
||||
("127.0.0.1/33", Err(())),
|
||||
("127.0.0.1/111", Err(())),
|
||||
("/32", Err(())),
|
||||
];
|
||||
|
||||
for &(s, cidr) in &tests {
|
||||
assert_eq!(Ipv4Cidr::from_str(s), cidr);
|
||||
assert_eq!(IpCidr::from_str(s), cidr.map(IpCidr::Ipv4));
|
||||
|
||||
if let Ok(cidr) = cidr {
|
||||
assert_eq!(Ipv4Cidr::from_str(&format!("{}", cidr)), Ok(cidr));
|
||||
assert_eq!(IpCidr::from_str(&format!("{}", cidr)),
|
||||
Ok(IpCidr::Ipv4(cidr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use core::fmt;
|
||||
use core::convert::From;
|
||||
|
||||
use {Error, Result};
|
||||
use phy::ChecksumCapabilities;
|
||||
use super::{Ipv4Address, Ipv4Packet, Ipv4Repr};
|
||||
use super::{Ipv4Address, Ipv4Packet, Ipv4Repr, Ipv4Cidr};
|
||||
|
||||
/// Internet protocol version.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
|
@ -133,6 +134,85 @@ impl fmt::Display for Address {
|
|||
}
|
||||
}
|
||||
|
||||
/// A specification of a CIDR block, containing an address and a variable-length
|
||||
/// subnet masking prefix length.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Cidr {
|
||||
Ipv4(Ipv4Cidr),
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
||||
impl Cidr {
|
||||
/// Create a CIDR block from the given address and prefix length.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function panics if the given address is unspecified, or
|
||||
/// the given prefix length is invalid for the given address.
|
||||
pub fn new(addr: Address, prefix_len: u8) -> Cidr {
|
||||
match addr {
|
||||
Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
|
||||
Address::Unspecified =>
|
||||
panic!("a CIDR block cannot be based on an unspecified address"),
|
||||
Address::__Nonexhaustive =>
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the IP address of this CIDR block.
|
||||
pub fn address(&self) -> Address {
|
||||
match self {
|
||||
&Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
|
||||
&Cidr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the prefix length of this CIDR block.
|
||||
pub fn prefix_len(&self) -> u8 {
|
||||
match self {
|
||||
&Cidr::Ipv4(cidr) => cidr.prefix_len(),
|
||||
&Cidr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Query whether the subnetwork described by this CIDR block contains
|
||||
/// the given address.
|
||||
pub fn contains_addr(&self, addr: &Address) -> bool {
|
||||
match (self, addr) {
|
||||
(&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) =>
|
||||
cidr.contains_addr(addr),
|
||||
(_, &Address::Unspecified) =>
|
||||
// a fully unspecified address covers both IPv4 and IPv6,
|
||||
// and no CIDR block can do that.
|
||||
false,
|
||||
(&Cidr::__Nonexhaustive, _) |
|
||||
(_, &Address::__Nonexhaustive) =>
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Query whether the subnetwork described by this CIDR block contains
|
||||
/// the subnetwork described by the given CIDR block.
|
||||
pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
|
||||
match (self, subnet) {
|
||||
(&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) =>
|
||||
cidr.contains_subnet(other),
|
||||
(&Cidr::__Nonexhaustive, _) |
|
||||
(_, &Cidr::__Nonexhaustive) =>
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Cidr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Cidr::Ipv4(cidr) => write!(f, "{}", cidr),
|
||||
&Cidr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An internet endpoint address.
|
||||
///
|
||||
/// An endpoint can be constructed from a port, in which case the address is unspecified.
|
||||
|
|
|
@ -78,6 +78,56 @@ impl fmt::Display for Address {
|
|||
}
|
||||
}
|
||||
|
||||
/// A specification of an IPv4 CIDR block, containing an address and a variable-length
|
||||
/// subnet masking prefix length.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub struct Cidr {
|
||||
address: Address,
|
||||
prefix_len: u8,
|
||||
}
|
||||
|
||||
impl Cidr {
|
||||
/// Create an IPv4 CIDR block from the given address and prefix length.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function panics if the prefix length is larger than 32.
|
||||
pub fn new(address: Address, prefix_len: u8) -> Cidr {
|
||||
assert!(prefix_len <= 32);
|
||||
Cidr { address, prefix_len }
|
||||
}
|
||||
|
||||
/// Return the address of this IPv4 CIDR block.
|
||||
pub fn address(&self) -> Address {
|
||||
self.address
|
||||
}
|
||||
|
||||
/// Return the prefix length of this IPv4 CIDR block.
|
||||
pub fn prefix_len(&self) -> u8 {
|
||||
self.prefix_len
|
||||
}
|
||||
|
||||
/// Query whether the subnetwork described by this IPv4 CIDR block contains
|
||||
/// the given address.
|
||||
pub fn contains_addr(&self, addr: &Address) -> bool {
|
||||
let shift = 32 - self.prefix_len;
|
||||
let self_prefix = NetworkEndian::read_u32(self.address.as_bytes()) >> shift;
|
||||
let addr_prefix = NetworkEndian::read_u32(addr.as_bytes()) >> shift;
|
||||
self_prefix == addr_prefix
|
||||
}
|
||||
|
||||
/// Query whether the subnetwork described by this IPv4 CIDR block contains
|
||||
/// the subnetwork described by the given IPv4 CIDR block.
|
||||
pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
|
||||
self.prefix_len <= subnet.prefix_len && self.contains_addr(&subnet.address)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Cidr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}/{}", self.address, self.prefix_len)
|
||||
}
|
||||
}
|
||||
|
||||
/// A read/write wrapper around an Internet Protocol version 4 packet buffer.
|
||||
#[derive(Debug)]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
|
@ -721,4 +771,52 @@ mod test {
|
|||
assert!(!Address::BROADCAST.is_link_local());
|
||||
assert!(!Address::BROADCAST.is_loopback());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr() {
|
||||
let cidr = Cidr::new(Address::new(192, 168, 1, 10), 24);
|
||||
|
||||
let inside_subnet = [
|
||||
[192, 168, 1, 0], [192, 168, 1, 1],
|
||||
[192, 168, 1, 2], [192, 168, 1, 10],
|
||||
[192, 168, 1, 127], [192, 168, 1, 255],
|
||||
];
|
||||
|
||||
let outside_subnet = [
|
||||
[192, 168, 0, 0], [127, 0, 0, 1],
|
||||
[192, 168, 2, 0], [192, 168, 0, 255],
|
||||
[ 0, 0, 0, 0], [255, 255, 255, 255],
|
||||
];
|
||||
|
||||
let subnets = [
|
||||
([192, 168, 1, 0], 32),
|
||||
([192, 168, 1, 255], 24),
|
||||
([192, 168, 1, 10], 30),
|
||||
];
|
||||
|
||||
let not_subnets = [
|
||||
([192, 168, 1, 10], 23),
|
||||
([127, 0, 0, 1], 8),
|
||||
([192, 168, 1, 0], 0),
|
||||
([192, 168, 0, 255], 32),
|
||||
];
|
||||
|
||||
for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
|
||||
assert!(cidr.contains_addr(&addr));
|
||||
}
|
||||
|
||||
for addr in outside_subnet.iter().map(|a| Address::from_bytes(a)) {
|
||||
assert!(!cidr.contains_addr(&addr));
|
||||
}
|
||||
|
||||
for subnet in subnets.iter().map(
|
||||
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) {
|
||||
assert!(cidr.contains_subnet(&subnet));
|
||||
}
|
||||
|
||||
for subnet in not_subnets.iter().map(
|
||||
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) {
|
||||
assert!(!cidr.contains_subnet(&subnet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,10 +100,12 @@ pub use self::ip::Protocol as IpProtocol;
|
|||
pub use self::ip::Address as IpAddress;
|
||||
pub use self::ip::Endpoint as IpEndpoint;
|
||||
pub use self::ip::IpRepr as IpRepr;
|
||||
pub use self::ip::Cidr as IpCidr;
|
||||
|
||||
pub use self::ipv4::Address as Ipv4Address;
|
||||
pub use self::ipv4::Packet as Ipv4Packet;
|
||||
pub use self::ipv4::Repr as Ipv4Repr;
|
||||
pub use self::ipv4::Cidr as Ipv4Cidr;
|
||||
|
||||
pub use self::icmpv4::Message as Icmpv4Message;
|
||||
pub use self::icmpv4::DstUnreachable as Icmpv4DstUnreachable;
|
||||
|
|
Loading…
Reference in New Issue