Add IPv6 address and cidr to wire
- Add the ipv6 feature - Ensure a travis build with the ipv6 feature enabled. - Add the necessary infrastructure to wire for ipv6 support. - Ipv6Address - Ipv6Cidr - Add Ipv6 Address and Cidr parsing to parsers - Add basic tests.v0.7.x
parent
8604eaa8f9
commit
b97c592671
|
@ -10,6 +10,8 @@ matrix:
|
|||
# actually test everything
|
||||
- rust: nightly
|
||||
env: FEATURES='default' MODE='test'
|
||||
- rust: nightly
|
||||
env: FEATURES='default proto-ipv6' MODE='test'
|
||||
- rust: nightly
|
||||
env: FEATURES='phy-raw_socket socket-udp' MODE='build'
|
||||
- rust: nightly
|
||||
|
|
|
@ -33,6 +33,7 @@ alloc = ["managed/alloc"]
|
|||
verbose = []
|
||||
"phy-raw_socket" = ["std", "libc"]
|
||||
"phy-tap_interface" = ["std", "libc"]
|
||||
"proto-ipv6" = []
|
||||
"socket-raw" = []
|
||||
"socket-udp" = []
|
||||
"socket-tcp" = []
|
||||
|
|
270
src/parsers.rs
270
src/parsers.rs
|
@ -1,6 +1,12 @@
|
|||
#![cfg_attr(not(feature = "proto-ipv6"), allow(dead_code))]
|
||||
|
||||
use core::str::FromStr;
|
||||
use core::result;
|
||||
use wire::{EthernetAddress, IpAddress, Ipv4Address, Ipv4Cidr, IpCidr};
|
||||
|
||||
use wire::{EthernetAddress, IpAddress, IpCidr};
|
||||
use wire::{Ipv4Address, Ipv4Cidr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use wire::{Ipv6Address, Ipv6Cidr};
|
||||
|
||||
type Result<T> = result::Result<T, ()>;
|
||||
|
||||
|
@ -17,6 +23,14 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn lookahead_char(&self, ch: u8) -> bool {
|
||||
if self.pos < self.data.len() {
|
||||
self.data[self.pos] == ch
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn advance(&mut self) -> Result<u8> {
|
||||
match self.data.get(self.pos) {
|
||||
Some(&chr) => {
|
||||
|
@ -62,6 +76,13 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn accept_str(&mut self, string: &[u8]) -> Result<()> {
|
||||
for byte in string.iter() {
|
||||
self.accept_char(*byte)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn accept_digit(&mut self, hex: bool) -> Result<u8> {
|
||||
let digit = self.advance()?;
|
||||
if digit >= b'0' && digit <= b'9' {
|
||||
|
@ -115,6 +136,103 @@ impl<'a> Parser<'a> {
|
|||
Err(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn accept_ipv6_part(&mut self, (head, tail): (&mut [u16; 8], &mut [u16; 6]),
|
||||
(head_idx, tail_idx): (&mut usize, &mut usize),
|
||||
mut use_tail: bool, is_cidr: bool) -> Result<()> {
|
||||
let double_colon = match self.try(|p| p.accept_str(b"::")) {
|
||||
Some(_) if !use_tail && *head_idx < 7 => {
|
||||
// Found a double colon. Start filling out the
|
||||
// tail and set the double colon flag in case
|
||||
// this is the last character we can parse.
|
||||
use_tail = true;
|
||||
true
|
||||
},
|
||||
Some(_) => {
|
||||
// This is a bad address. Only one double colon is
|
||||
// allowed and an address is only 128 bits.
|
||||
return Err(());
|
||||
}
|
||||
None => {
|
||||
if *head_idx != 0 || use_tail && *tail_idx != 0 {
|
||||
// If this is not the first number or the position following
|
||||
// a double colon, we expect there to be a single colon.
|
||||
self.accept_char(b':')?;
|
||||
}
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
match self.try(|p| p.accept_number(4, 0x10000, true)) {
|
||||
Some(part) if !use_tail && *head_idx < 8 => {
|
||||
// Valid u16 to be added to the address
|
||||
head[*head_idx] = part as u16;
|
||||
*head_idx += 1;
|
||||
Ok(())
|
||||
},
|
||||
Some(part) if *tail_idx < 6 => {
|
||||
// Valid u16 to be added to the address
|
||||
tail[*tail_idx] = part as u16;
|
||||
*tail_idx += 1;
|
||||
Ok(())
|
||||
},
|
||||
Some(_) => {
|
||||
// Tail or head section is too long
|
||||
Err(())
|
||||
}
|
||||
None if double_colon && (is_cidr || self.pos == self.data.len()) => {
|
||||
// The address ends with "::". E.g. 1234:: or ::
|
||||
Ok(())
|
||||
}
|
||||
None => {
|
||||
// Invalid address
|
||||
Err(())
|
||||
}
|
||||
}?;
|
||||
|
||||
if *head_idx + *tail_idx > 8 {
|
||||
// The head and tail indexes add up to a bad address length.
|
||||
Err(())
|
||||
} else if !self.lookahead_char(b':') {
|
||||
if *head_idx < 8 && !use_tail {
|
||||
// There was no double colon found, and the head is too short
|
||||
return Err(());
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
// Continue recursing
|
||||
self.accept_ipv6_part((head, tail), (head_idx, tail_idx), use_tail, is_cidr)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn accept_ipv6(&mut self, is_cidr: bool) -> Result<Ipv6Address> {
|
||||
// IPv6 addresses may contain a "::" to indicate a series of
|
||||
// 16 bit sections that evaluate to 0. E.g.
|
||||
//
|
||||
// fe80:0000:0000:0000:0000:0000:0000:0001
|
||||
//
|
||||
// May be written as
|
||||
//
|
||||
// fe80::1
|
||||
//
|
||||
// As a result, we need to find the first section of colon
|
||||
// delimited u16's before a possible "::", then the
|
||||
// possible second section after the "::", and finally
|
||||
// combine the second optional section to the end of the
|
||||
// final address.
|
||||
let (mut addr, mut tail) = ([0u16; 8], [0u16; 6]);
|
||||
let (mut head_idx, mut tail_idx) = (0, 0);
|
||||
|
||||
self.accept_ipv6_part((&mut addr, &mut tail), (&mut head_idx, &mut tail_idx), false, is_cidr)?;
|
||||
|
||||
// We need to copy the tail portion (the portion following the "::") to the
|
||||
// end of the address.
|
||||
addr[8 - tail_idx..].copy_from_slice(&tail[..tail_idx]);
|
||||
|
||||
Ok(Ipv6Address::from_parts(&addr))
|
||||
}
|
||||
|
||||
fn accept_ipv4(&mut self) -> Result<Ipv4Address> {
|
||||
let mut octets = [0u8; 4];
|
||||
for n in 0..4 {
|
||||
|
@ -130,6 +248,13 @@ impl<'a> Parser<'a> {
|
|||
if let Some(ipv4) = self.try(|p| p.accept_ipv4()) {
|
||||
return Ok(IpAddress::Ipv4(ipv4))
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
match self.try(|p| p.accept_ipv6(false)) {
|
||||
Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)),
|
||||
None => ()
|
||||
}
|
||||
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
@ -152,6 +277,16 @@ impl FromStr for Ipv4Address {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
impl FromStr for Ipv6Address {
|
||||
type Err = ();
|
||||
|
||||
/// Parse a string representation of an IPv6 address.
|
||||
fn from_str(s: &str) -> Result<Ipv6Address> {
|
||||
Parser::new(s).until_eof(|p| p.accept_ipv6(false))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for IpAddress {
|
||||
type Err = ();
|
||||
|
||||
|
@ -175,12 +310,37 @@ impl FromStr for Ipv4Cidr {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
impl FromStr for Ipv6Cidr {
|
||||
type Err = ();
|
||||
|
||||
/// Parse a string representation of an IPv6 CIDR.
|
||||
fn from_str(s: &str) -> Result<Ipv6Cidr> {
|
||||
Parser::new(s).until_eof(|p| {
|
||||
let ip = p.accept_ipv6(true)?;
|
||||
p.accept_char(b'/')?;
|
||||
let prefix_len = p.accept_number(3, 129, false)? as u8;
|
||||
Ok(Ipv6Cidr::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)
|
||||
if let Ok(ipv4) = Ipv4Cidr::from_str(s) {
|
||||
return Ok(IpCidr::Ipv4(ipv4))
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
match Ipv6Cidr::from_str(s) {
|
||||
Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)),
|
||||
Err(_) => ()
|
||||
}
|
||||
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,6 +348,21 @@ impl FromStr for IpCidr {
|
|||
mod test {
|
||||
use super::*;
|
||||
|
||||
macro_rules! check_cidr_test_array {
|
||||
($tests:expr, $from_str:path, $variant:path) => {
|
||||
for &(s, cidr) in &$tests {
|
||||
assert_eq!($from_str(s), cidr);
|
||||
assert_eq!(IpCidr::from_str(s), cidr.map($variant));
|
||||
|
||||
if let Ok(cidr) = cidr {
|
||||
assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr));
|
||||
assert_eq!(IpCidr::from_str(&format!("{}", cidr)),
|
||||
Ok($variant(cidr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mac() {
|
||||
assert_eq!(EthernetAddress::from_str(""), Err(()));
|
||||
|
@ -225,7 +400,52 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_ip() {
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn test_ipv6() {
|
||||
// Obviously not valid
|
||||
assert_eq!(Ipv6Address::from_str(""), Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
|
||||
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)));
|
||||
assert_eq!(Ipv6Address::from_str("::1"),
|
||||
Ok(Ipv6Address::LOOPBACK));
|
||||
assert_eq!(Ipv6Address::from_str("::"),
|
||||
Ok(Ipv6Address::UNSPECIFIED));
|
||||
assert_eq!(Ipv6Address::from_str("fe80::1"),
|
||||
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)));
|
||||
assert_eq!(Ipv6Address::from_str("1234:5678::"),
|
||||
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0)));
|
||||
assert_eq!(Ipv6Address::from_str("1234:5678::8765:4321"),
|
||||
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321)));
|
||||
// Two double colons in address
|
||||
assert_eq!(Ipv6Address::from_str("1234:5678::1::1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("4444:333:22:1::4"),
|
||||
Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4)));
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1::"),
|
||||
Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0)));
|
||||
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1"),
|
||||
Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1)));
|
||||
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"),
|
||||
Err(()));
|
||||
// Double colon appears too late indicating an address that is too long
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"),
|
||||
Err(()));
|
||||
// Section after double colon is too long for a valid address
|
||||
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"),
|
||||
Err(()));
|
||||
// Obviously too long
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"),
|
||||
Err(()));
|
||||
// Address is too short
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"),
|
||||
Err(()));
|
||||
// Long number
|
||||
assert_eq!(Ipv6Address::from_str("::000001"),
|
||||
Err(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ip_ipv4() {
|
||||
assert_eq!(IpAddress::from_str(""), Err(()));
|
||||
assert_eq!(IpAddress::from_str("1.2.3.4"),
|
||||
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4]))));
|
||||
|
@ -233,7 +453,16 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr() {
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn test_ip_ipv6() {
|
||||
assert_eq!(IpAddress::from_str(""), Err(()));
|
||||
assert_eq!(IpAddress::from_str("fe80::1"),
|
||||
Ok(IpAddress::Ipv6(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))));
|
||||
assert_eq!(IpAddress::from_str("x"), Err(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr_ipv4() {
|
||||
let tests = [
|
||||
("127.0.0.1/8",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8))),
|
||||
|
@ -252,15 +481,30 @@ mod test {
|
|||
("/32", Err(())),
|
||||
];
|
||||
|
||||
for &(s, cidr) in &tests {
|
||||
assert_eq!(Ipv4Cidr::from_str(s), cidr);
|
||||
assert_eq!(IpCidr::from_str(s), cidr.map(IpCidr::Ipv4));
|
||||
check_cidr_test_array!(tests, Ipv4Cidr::from_str, 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)));
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn test_cidr_ipv6() {
|
||||
let tests = [
|
||||
("fe80::1/64",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))),
|
||||
("fe80::/64",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 64u8))),
|
||||
("::1/128",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
|
||||
("::/128",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
|
||||
("fe80:0:0:0:0:0:0:1/64",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))),
|
||||
("fe80:0:0:0:0:0:0:1|64",
|
||||
Err(())),
|
||||
("fe80::|64",
|
||||
Err(())),
|
||||
("fe80::1::/64",
|
||||
Err(()))
|
||||
];
|
||||
check_cidr_test_array!(tests, Ipv6Cidr::from_str, IpCidr::Ipv6);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,6 +201,8 @@ impl<'a, 'b> RawSocket<'a, 'b> {
|
|||
let ipv4_repr = Ipv4Repr::parse(&packet, checksum_caps)?;
|
||||
Ok((IpRepr::Ipv4(ipv4_repr), packet.payload()))
|
||||
}
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
IpVersion::Ipv6 => Err(Error::Unrecognized),
|
||||
IpVersion::Unspecified => unreachable!(),
|
||||
IpVersion::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
|
|
133
src/wire/ip.rs
133
src/wire/ip.rs
|
@ -4,12 +4,16 @@ use core::convert::From;
|
|||
use {Error, Result};
|
||||
use phy::ChecksumCapabilities;
|
||||
use super::{Ipv4Address, Ipv4Packet, Ipv4Repr, Ipv4Cidr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use super::{Ipv6Address, Ipv6Cidr};
|
||||
|
||||
/// Internet protocol version.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub enum Version {
|
||||
Unspecified,
|
||||
Ipv4,
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Ipv6,
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
@ -22,6 +26,8 @@ impl Version {
|
|||
pub fn of_packet(data: &[u8]) -> Result<Version> {
|
||||
match data[0] >> 4 {
|
||||
4 => Ok(Version::Ipv4),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
6 => Ok(Version::Ipv6),
|
||||
_ => Err(Error::Unrecognized)
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +38,8 @@ impl fmt::Display for Version {
|
|||
match self {
|
||||
&Version::Unspecified => write!(f, "IPv?"),
|
||||
&Version::Ipv4 => write!(f, "IPv4"),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Version::Ipv6 => write!(f, "IPv6"),
|
||||
&Version::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -40,18 +48,30 @@ impl fmt::Display for Version {
|
|||
enum_with_unknown! {
|
||||
/// IP datagram encapsulated protocol.
|
||||
pub enum Protocol(u8) {
|
||||
Icmp = 0x01,
|
||||
Tcp = 0x06,
|
||||
Udp = 0x11
|
||||
HopByHop = 0x00,
|
||||
Icmp = 0x01,
|
||||
Tcp = 0x06,
|
||||
Udp = 0x11,
|
||||
Ipv6Route = 0x2b,
|
||||
Ipv6Frag = 0x2c,
|
||||
Icmpv6 = 0x3a,
|
||||
Ipv6NoNxt = 0x3b,
|
||||
Ipv6Opts = 0x3c
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Protocol {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Protocol::Icmp => write!(f, "ICMP"),
|
||||
&Protocol::Tcp => write!(f, "TCP"),
|
||||
&Protocol::Udp => write!(f, "UDP"),
|
||||
&Protocol::HopByHop => write!(f, "Hop-by-Hop"),
|
||||
&Protocol::Icmp => write!(f, "ICMP"),
|
||||
&Protocol::Tcp => write!(f, "TCP"),
|
||||
&Protocol::Udp => write!(f, "UDP"),
|
||||
&Protocol::Ipv6Route => write!(f, "IPv6-Route"),
|
||||
&Protocol::Ipv6Frag => write!(f, "IPv6-Frag"),
|
||||
&Protocol::Icmpv6 => write!(f, "ICMPv6"),
|
||||
&Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
|
||||
&Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
|
||||
&Protocol::Unknown(id) => write!(f, "0x{:02x}", id)
|
||||
}
|
||||
}
|
||||
|
@ -65,6 +85,9 @@ pub enum Address {
|
|||
Unspecified,
|
||||
/// An IPv4 address.
|
||||
Ipv4(Ipv4Address),
|
||||
/// An IPv6 address.
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Ipv6(Ipv6Address),
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive
|
||||
}
|
||||
|
@ -75,11 +98,20 @@ impl Address {
|
|||
Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
|
||||
}
|
||||
|
||||
/// Create an address wrapping an IPv6 address with the given octets.
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16,
|
||||
a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
|
||||
Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
|
||||
}
|
||||
|
||||
/// Query whether the address is a valid unicast address.
|
||||
pub fn is_unicast(&self) -> bool {
|
||||
match self {
|
||||
&Address::Unspecified => false,
|
||||
&Address::Ipv4(addr) => addr.is_unicast(),
|
||||
&Address::Unspecified => false,
|
||||
&Address::Ipv4(addr) => addr.is_unicast(),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Address::Ipv6(addr) => addr.is_unicast(),
|
||||
&Address::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -87,8 +119,10 @@ impl Address {
|
|||
/// Query whether the address is the broadcast address.
|
||||
pub fn is_broadcast(&self) -> bool {
|
||||
match self {
|
||||
&Address::Unspecified => false,
|
||||
&Address::Ipv4(addr) => addr.is_broadcast(),
|
||||
&Address::Unspecified => false,
|
||||
&Address::Ipv4(addr) => addr.is_broadcast(),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Address::Ipv6(_) => false,
|
||||
&Address::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -96,8 +130,10 @@ impl Address {
|
|||
/// Query whether the address falls into the "unspecified" range.
|
||||
pub fn is_unspecified(&self) -> bool {
|
||||
match self {
|
||||
&Address::Unspecified => true,
|
||||
&Address::Ipv4(addr) => addr.is_unspecified(),
|
||||
&Address::Unspecified => true,
|
||||
&Address::Ipv4(addr) => addr.is_unspecified(),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Address::Ipv6(addr) => addr.is_unspecified(),
|
||||
&Address::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -105,8 +141,10 @@ impl Address {
|
|||
/// Return an unspecified address that has the same IP version as `self`.
|
||||
pub fn to_unspecified(&self) -> Address {
|
||||
match self {
|
||||
&Address::Unspecified => Address::Unspecified,
|
||||
&Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED),
|
||||
&Address::Unspecified => Address::Unspecified,
|
||||
&Address::Ipv4(_) => Address::Ipv4(Ipv4Address::UNSPECIFIED),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Address::Ipv6(_) => Address::Ipv6(Ipv6Address::UNSPECIFIED),
|
||||
&Address::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -124,11 +162,20 @@ impl From<Ipv4Address> for Address {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
impl From<Ipv6Address> for Address {
|
||||
fn from(addr: Ipv6Address) -> Self {
|
||||
Address::Ipv6(addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Address::Unspecified => write!(f, "*"),
|
||||
&Address::Ipv4(addr) => write!(f, "{}", addr),
|
||||
&Address::Unspecified => write!(f, "*"),
|
||||
&Address::Ipv4(addr) => write!(f, "{}", addr),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Address::Ipv6(addr) => write!(f, "{}", addr),
|
||||
&Address::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -139,6 +186,8 @@ impl fmt::Display for Address {
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Cidr {
|
||||
Ipv4(Ipv4Cidr),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Ipv6(Ipv6Cidr),
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive,
|
||||
}
|
||||
|
@ -152,6 +201,8 @@ impl Cidr {
|
|||
pub fn new(addr: Address, prefix_len: u8) -> Cidr {
|
||||
match addr {
|
||||
Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
|
||||
Address::Unspecified =>
|
||||
panic!("a CIDR block cannot be based on an unspecified address"),
|
||||
Address::__Nonexhaustive =>
|
||||
|
@ -162,7 +213,9 @@ impl Cidr {
|
|||
/// Return the IP address of this CIDR block.
|
||||
pub fn address(&self) -> Address {
|
||||
match self {
|
||||
&Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
|
||||
&Cidr::Ipv4(cidr) => Address::Ipv4(cidr.address()),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Cidr::Ipv6(cidr) => Address::Ipv6(cidr.address()),
|
||||
&Cidr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +223,9 @@ impl Cidr {
|
|||
/// Return the prefix length of this CIDR block.
|
||||
pub fn prefix_len(&self) -> u8 {
|
||||
match self {
|
||||
&Cidr::Ipv4(cidr) => cidr.prefix_len(),
|
||||
&Cidr::Ipv4(cidr) => cidr.prefix_len(),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Cidr::Ipv6(cidr) => cidr.prefix_len(),
|
||||
&Cidr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -181,6 +236,12 @@ impl Cidr {
|
|||
match (self, addr) {
|
||||
(&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) =>
|
||||
cidr.contains_addr(addr),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) =>
|
||||
cidr.contains_addr(addr),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) =>
|
||||
false,
|
||||
(_, &Address::Unspecified) =>
|
||||
// a fully unspecified address covers both IPv4 and IPv6,
|
||||
// and no CIDR block can do that.
|
||||
|
@ -197,6 +258,12 @@ impl Cidr {
|
|||
match (self, subnet) {
|
||||
(&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) =>
|
||||
cidr.contains_subnet(other),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) =>
|
||||
cidr.contains_subnet(other),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) =>
|
||||
false,
|
||||
(&Cidr::__Nonexhaustive, _) |
|
||||
(_, &Cidr::__Nonexhaustive) =>
|
||||
unreachable!()
|
||||
|
@ -207,7 +274,9 @@ impl Cidr {
|
|||
impl fmt::Display for Cidr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Cidr::Ipv4(cidr) => write!(f, "{}", cidr),
|
||||
&Cidr::Ipv4(cidr) => write!(f, "{}", cidr),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Cidr::Ipv6(cidr) => write!(f, "{}", cidr),
|
||||
&Cidr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
@ -367,6 +436,13 @@ impl Repr {
|
|||
}))
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Repr::Unspecified {
|
||||
src_addr: Address::Ipv6(_),
|
||||
dst_addr: Address::Ipv6(_),
|
||||
..
|
||||
} => Err(Error::Unaddressable),
|
||||
|
||||
&Repr::Unspecified {
|
||||
src_addr: Address::Unspecified,
|
||||
dst_addr: Address::Ipv4(dst_addr),
|
||||
|
@ -388,11 +464,12 @@ impl Repr {
|
|||
}))
|
||||
}
|
||||
|
||||
&Repr::Unspecified { dst_addr: Address::Unspecified, .. } =>
|
||||
panic!("unspecified destination IP address"),
|
||||
|
||||
// &Repr::Unspecified { .. } =>
|
||||
// panic!("source and destination IP address families do not match"),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Repr::Unspecified {
|
||||
src_addr: Address::Unspecified,
|
||||
dst_addr: Address::Ipv6(_),
|
||||
..
|
||||
} => Err(Error::Unaddressable),
|
||||
|
||||
&Repr::Ipv4(mut repr) => {
|
||||
if repr.src_addr.is_unspecified() {
|
||||
|
@ -411,10 +488,10 @@ impl Repr {
|
|||
}
|
||||
},
|
||||
|
||||
&Repr::__Nonexhaustive |
|
||||
&Repr::Unspecified { src_addr: Address::__Nonexhaustive, .. } |
|
||||
&Repr::Unspecified { dst_addr: Address::__Nonexhaustive, .. } =>
|
||||
unreachable!()
|
||||
&Repr::Unspecified { .. } =>
|
||||
panic!("source and destination IP address families do not match"),
|
||||
|
||||
&Repr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
use core::fmt;
|
||||
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
pub use super::IpProtocol as Protocol;
|
||||
|
||||
/// A sixteen-octet IPv6 address.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
||||
pub struct Address(pub [u8; 16]);
|
||||
|
||||
impl Address {
|
||||
/// An unspecified address.
|
||||
pub const UNSPECIFIED: Address = Address([0x00; 16]);
|
||||
|
||||
/// Link local all routers multicast address.
|
||||
pub const LINK_LOCAL_ALL_NODES: Address =
|
||||
Address([0xff, 0x02, 0x00, 0x0, 0x00, 0x00, 0x00, 0x0,
|
||||
0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x1]);
|
||||
|
||||
/// Link local all nodes multicast address.
|
||||
pub const LINK_LOCAL_ALL_ROUTERS: Address =
|
||||
Address([0xff, 0x02, 0x00, 0x0, 0x00, 0x00, 0x00, 0x0,
|
||||
0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x2]);
|
||||
|
||||
/// Loopback address.
|
||||
pub const LOOPBACK: Address =
|
||||
Address([0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x0,
|
||||
0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x1]);
|
||||
|
||||
/// Construct an IPv6 address from parts.
|
||||
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16,
|
||||
a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
|
||||
let mut addr = [0u8; 16];
|
||||
NetworkEndian::write_u16(&mut addr[0..2], a0);
|
||||
NetworkEndian::write_u16(&mut addr[2..4], a1);
|
||||
NetworkEndian::write_u16(&mut addr[4..6], a2);
|
||||
NetworkEndian::write_u16(&mut addr[6..8], a3);
|
||||
NetworkEndian::write_u16(&mut addr[8..10], a4);
|
||||
NetworkEndian::write_u16(&mut addr[10..12], a5);
|
||||
NetworkEndian::write_u16(&mut addr[12..14], a6);
|
||||
NetworkEndian::write_u16(&mut addr[14..16], a7);
|
||||
Address(addr)
|
||||
}
|
||||
|
||||
/// Construct an IPv6 address from a sequence of octets, in big-endian.
|
||||
///
|
||||
/// # Panics
|
||||
/// The function panics if `data` is not sixteen octets long.
|
||||
pub fn from_bytes(data: &[u8]) -> Address {
|
||||
let mut bytes = [0; 16];
|
||||
bytes.copy_from_slice(data);
|
||||
Address(bytes)
|
||||
}
|
||||
|
||||
/// Construct an IPv6 address from a sequence of words, in big-endian.
|
||||
///
|
||||
/// # Panics
|
||||
/// The function panics if `data` is not 8 words long.
|
||||
pub fn from_parts(data: &[u16]) -> Address {
|
||||
assert!(data.len() >= 8);
|
||||
let mut bytes = [0; 16];
|
||||
for word_idx in 0..8 {
|
||||
let byte_idx = word_idx * 2;
|
||||
NetworkEndian::write_u16(&mut bytes[byte_idx..(byte_idx + 2)], data[word_idx]);
|
||||
}
|
||||
Address(bytes)
|
||||
}
|
||||
|
||||
/// Write a IPv6 address to the given slice.
|
||||
///
|
||||
/// # Panics
|
||||
/// The function panics if `data` is not 8 words long.
|
||||
pub fn write_parts(&self, data: &mut [u16]) {
|
||||
assert!(data.len() >= 8);
|
||||
for i in 0..8 {
|
||||
let byte_idx = i * 2;
|
||||
data[i] = NetworkEndian::read_u16(&self.0[byte_idx..(byte_idx + 2)]);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an IPv6 address as a sequence of octets, in big-endian.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Query whether the IPv6 address is an unicast address.
|
||||
pub fn is_unicast(&self) -> bool {
|
||||
!(self.is_multicast() || self.is_unspecified())
|
||||
}
|
||||
|
||||
/// Query whether the IPv6 address is a multicast address.
|
||||
pub fn is_multicast(&self) -> bool {
|
||||
self.0[0] == 0xff
|
||||
}
|
||||
|
||||
/// Query whether the IPv6 address is the "unspecified" address.
|
||||
pub fn is_unspecified(&self) -> bool {
|
||||
self.0 == [0x00; 16]
|
||||
}
|
||||
|
||||
/// Query whether the IPv6 address is in the "link-local" range.
|
||||
pub fn is_link_local(&self) -> bool {
|
||||
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00]
|
||||
}
|
||||
|
||||
/// Query whether the IPv6 address is the "loopback" address.
|
||||
pub fn is_loopback(&self) -> bool {
|
||||
*self == Self::LOOPBACK
|
||||
}
|
||||
|
||||
/// Helper function used to mask an addres given a prefix.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function panics if `mask` is greater than 128.
|
||||
pub(super) fn mask(&self, mask: u8) -> [u8; 16] {
|
||||
assert!(mask <= 128);
|
||||
let mut bytes = [0u8; 16];
|
||||
let idx = (mask as usize) / 8;
|
||||
let modulus = (mask as usize) % 8;
|
||||
let (first, second) = self.0.split_at(idx);
|
||||
bytes[0..idx].copy_from_slice(&first);
|
||||
if idx < 16 {
|
||||
let part = second[0];
|
||||
bytes[idx] = part & (!(0xff >> modulus) as u8);
|
||||
}
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
enum State {
|
||||
Head,
|
||||
HeadBody,
|
||||
Tail,
|
||||
TailBody
|
||||
}
|
||||
let mut words = [0u16; 8];
|
||||
self.write_parts(&mut words);
|
||||
let mut state = State::Head;
|
||||
for word in words.iter() {
|
||||
state = match (*word, &state) {
|
||||
// Once a u16 equal to zero write a double colon and
|
||||
// skip to the next non-zero u16.
|
||||
(0, &State::Head) | (0, &State::HeadBody) => {
|
||||
write!(f, "::")?;
|
||||
State::Tail
|
||||
},
|
||||
// Continue iterating without writing any characters until
|
||||
// we hit anothing non-zero value.
|
||||
(0, &State::Tail) => State::Tail,
|
||||
// When the state is Head or Tail write a u16 in hexadecimal
|
||||
// without the leading colon if the value is not 0.
|
||||
(_, &State::Head) => {
|
||||
write!(f, "{:x}", word)?;
|
||||
State::HeadBody
|
||||
},
|
||||
(_, &State::Tail) => {
|
||||
write!(f, "{:x}", word)?;
|
||||
State::TailBody
|
||||
},
|
||||
// Write the u16 with a leading colon when parsing a value
|
||||
// that isn't the first in a section
|
||||
(_, &State::HeadBody) | (_, &State::TailBody) => {
|
||||
write!(f, ":{:x}", word)?;
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A specification of an IPv6 CIDR block, containing an address and a variable-length
|
||||
/// subnet masking prefix length.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct Cidr {
|
||||
address: Address,
|
||||
prefix_len: u8,
|
||||
}
|
||||
|
||||
impl Cidr {
|
||||
/// Create an IPv6 CIDR block from the given address and prefix length.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function panics if the prefix length is larger than 128.
|
||||
pub fn new(address: Address, prefix_len: u8) -> Cidr {
|
||||
assert!(prefix_len <= 128);
|
||||
Cidr { address, prefix_len }
|
||||
}
|
||||
|
||||
/// Return the address of this IPv6 CIDR block.
|
||||
pub fn address(&self) -> Address {
|
||||
self.address
|
||||
}
|
||||
|
||||
/// Return the prefix length of this IPv6 CIDR block.
|
||||
pub fn prefix_len(&self) -> u8 {
|
||||
self.prefix_len
|
||||
}
|
||||
|
||||
/// Query whether the subnetwork described by this IPv6 CIDR block contains
|
||||
/// the given address.
|
||||
pub fn contains_addr(&self, addr: &Address) -> bool {
|
||||
// right shift by 128 is not legal
|
||||
if self.prefix_len == 0 { return true }
|
||||
|
||||
let shift = 128 - self.prefix_len;
|
||||
self.address.mask(shift) == addr.mask(shift)
|
||||
}
|
||||
|
||||
/// Query whether the subnetwork described by this IPV6 CIDR block contains
|
||||
/// the subnetwork described by the given IPv6 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)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{Address, Cidr};
|
||||
|
||||
static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01]);
|
||||
#[test]
|
||||
fn test_basic_multicast() {
|
||||
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unspecified());
|
||||
assert!(Address::LINK_LOCAL_ALL_ROUTERS.is_multicast());
|
||||
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_link_local());
|
||||
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_loopback());
|
||||
assert!(!Address::LINK_LOCAL_ALL_NODES.is_unspecified());
|
||||
assert!(Address::LINK_LOCAL_ALL_NODES.is_multicast());
|
||||
assert!(!Address::LINK_LOCAL_ALL_NODES.is_link_local());
|
||||
assert!(!Address::LINK_LOCAL_ALL_NODES.is_loopback());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_link_local() {
|
||||
assert!(!LINK_LOCAL_ADDR.is_unspecified());
|
||||
assert!(!LINK_LOCAL_ADDR.is_multicast());
|
||||
assert!(LINK_LOCAL_ADDR.is_link_local());
|
||||
assert!(!LINK_LOCAL_ADDR.is_loopback());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic_loopback() {
|
||||
assert!(!Address::LOOPBACK.is_unspecified());
|
||||
assert!(!Address::LOOPBACK.is_multicast());
|
||||
assert!(!Address::LOOPBACK.is_link_local());
|
||||
assert!(Address::LOOPBACK.is_loopback());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_address_format() {
|
||||
assert_eq!("ff02::1",
|
||||
format!("{}", Address::LINK_LOCAL_ALL_NODES));
|
||||
assert_eq!("fe80::1",
|
||||
format!("{}", LINK_LOCAL_ADDR));
|
||||
assert_eq!("fe80::7f00:0:1",
|
||||
format!("{}", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
|
||||
Address::LINK_LOCAL_ALL_NODES);
|
||||
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
|
||||
Address::LINK_LOCAL_ALL_ROUTERS);
|
||||
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1),
|
||||
Address::LOOPBACK);
|
||||
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0),
|
||||
Address::UNSPECIFIED);
|
||||
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
|
||||
LINK_LOCAL_ADDR);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_parts() {
|
||||
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
|
||||
Address::LINK_LOCAL_ALL_NODES);
|
||||
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
|
||||
Address::LINK_LOCAL_ALL_ROUTERS);
|
||||
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]),
|
||||
Address::LOOPBACK);
|
||||
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
Address::UNSPECIFIED);
|
||||
assert_eq!(Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]),
|
||||
LINK_LOCAL_ADDR);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_parts() {
|
||||
let mut bytes = [0u16; 8];
|
||||
{
|
||||
Address::LOOPBACK.write_parts(&mut bytes);
|
||||
assert_eq!(Address::LOOPBACK, Address::from_parts(&bytes));
|
||||
}
|
||||
{
|
||||
Address::LINK_LOCAL_ALL_ROUTERS.write_parts(&mut bytes);
|
||||
assert_eq!(Address::LINK_LOCAL_ALL_ROUTERS, Address::from_parts(&bytes));
|
||||
}
|
||||
{
|
||||
LINK_LOCAL_ADDR.write_parts(&mut bytes);
|
||||
assert_eq!(LINK_LOCAL_ADDR, Address::from_parts(&bytes));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mask() {
|
||||
let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1);
|
||||
assert_eq!(addr.mask(11), [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(addr.mask(15), [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(addr.mask(26), [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(addr.mask(128), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
assert_eq!(addr.mask(127), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr() {
|
||||
let cidr = Cidr::new(LINK_LOCAL_ADDR, 64);
|
||||
|
||||
let inside_subnet = [
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]
|
||||
];
|
||||
|
||||
let outside_subnet = [
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]
|
||||
];
|
||||
|
||||
let subnets = [
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||
65),
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
128),
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78],
|
||||
96)
|
||||
];
|
||||
|
||||
let not_subnets = [
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||
63),
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||
64),
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||
65),
|
||||
([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
128)
|
||||
];
|
||||
|
||||
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(a), p)) {
|
||||
assert!(cidr.contains_subnet(&subnet));
|
||||
}
|
||||
|
||||
for subnet in not_subnets.iter().map(
|
||||
|&(a, p)| Cidr::new(Address(a), p)) {
|
||||
assert!(!cidr.contains_subnet(&subnet));
|
||||
}
|
||||
|
||||
let cidr_without_prefix = Cidr::new(LINK_LOCAL_ADDR, 0);
|
||||
assert!(cidr_without_prefix.contains_addr(&Address::LOOPBACK));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "destination and source slices have different lengths")]
|
||||
fn from_bytes_too_long() {
|
||||
let _ = Address::from_bytes(&[0u8; 15]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "data.len() >= 8")]
|
||||
fn from_parts_too_long() {
|
||||
let _ = Address::from_parts(&[0u16; 7]);
|
||||
}
|
||||
}
|
|
@ -81,6 +81,8 @@ mod ethernet;
|
|||
mod arp;
|
||||
mod ip;
|
||||
mod ipv4;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ipv6;
|
||||
mod icmpv4;
|
||||
mod udp;
|
||||
mod tcp;
|
||||
|
@ -108,6 +110,10 @@ pub use self::ipv4::Packet as Ipv4Packet;
|
|||
pub use self::ipv4::Repr as Ipv4Repr;
|
||||
pub use self::ipv4::Cidr as Ipv4Cidr;
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::ipv6::{Address as Ipv6Address,
|
||||
Cidr as Ipv6Cidr};
|
||||
|
||||
pub use self::icmpv4::Message as Icmpv4Message;
|
||||
pub use self::icmpv4::DstUnreachable as Icmpv4DstUnreachable;
|
||||
pub use self::icmpv4::Redirect as Icmpv4Redirect;
|
||||
|
|
Loading…
Reference in New Issue