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
Dan Robertson 2017-11-02 03:55:10 +00:00 committed by whitequark
parent 8604eaa8f9
commit b97c592671
7 changed files with 786 additions and 41 deletions

View File

@ -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

View File

@ -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" = []

View File

@ -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);
}
}

View File

@ -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!()
}

View File

@ -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!()
}
}

413
src/wire/ipv6.rs Normal file
View File

@ -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]);
}
}

View File

@ -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;