blob: a3066b967cfdc10f6dce8f9d904e4fd3b7840a92 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std;
use std::fmt::{self, Debug, Display, Formatter};
use std::hash::Hash;
use byteorder::{ByteOrder, NetworkEndian};
use zerocopy::{AsBytes, FromBytes, Unaligned};
/// An IP protocol version.
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum IpVersion {
V4,
V6,
}
impl IpVersion {
/// The number for this IP protocol version.
///
/// 4 for `V4` and 6 for `V6`.
pub fn version_number(&self) -> u8 {
match self {
IpVersion::V4 => 4,
IpVersion::V6 => 6,
}
}
/// Is this IPv4?
pub fn is_v4(&self) -> bool {
*self == IpVersion::V4
}
/// Is this IPv6?
pub fn is_v6(&self) -> bool {
*self == IpVersion::V6
}
}
mod sealed {
// Ensure that only Ipv4 and Ipv6 can implement IpVersion and that only
// Ipv4Addr and Ipv6Addr can implement IpAddr.
pub trait Sealed {}
impl Sealed for super::Ipv4 {}
impl Sealed for super::Ipv6 {}
impl Sealed for super::Ipv4Addr {}
impl Sealed for super::Ipv6Addr {}
}
/// A trait for IP protocol versions.
///
/// `Ip` encapsulates the details of a version of the IP protocol. It includes
/// the `IpVersion` enum (`VERSION`) and address type (`Addr`). It is
/// implemented by `Ipv4` and `Ipv6`.
pub trait Ip: Sized + self::sealed::Sealed {
/// The IP version.
///
/// `V4` for IPv4 and `V6` for IPv6.
const VERSION: IpVersion;
/// The default loopback address.
///
/// When sending packets to a loopback interface, this address is used as
/// the source address. It is an address in the loopback subnet.
const LOOPBACK_ADDRESS: Self::Addr;
/// The subnet of loopback addresses.
///
/// Addresses in this subnet must not appear outside a host, and may only be
/// used for loopback interfaces.
const LOOPBACK_SUBNET: Subnet<Self::Addr>;
/// The address type for this IP version.
///
/// `Ipv4Addr` for IPv4 and `Ipv6Addr` for IPv6.
type Addr: IpAddr<Version = Self>;
}
/// IPv4.
///
/// `Ipv4` implements `Ip` for IPv4.
#[derive(Debug, Default)]
pub struct Ipv4;
impl Ip for Ipv4 {
const VERSION: IpVersion = IpVersion::V4;
// https://tools.ietf.org/html/rfc5735#section-3
const LOOPBACK_ADDRESS: Ipv4Addr = Ipv4Addr::new([127, 0, 0, 1]);
const LOOPBACK_SUBNET: Subnet<Ipv4Addr> = Subnet {
network: Ipv4Addr::new([127, 0, 0, 0]),
prefix: 8,
};
type Addr = Ipv4Addr;
}
impl Ipv4 {
/// The global broadcast address.
///
/// This address is considered to be a broadcast address on all networks
/// regardless of subnet address. This is distinct from the subnet-specific
/// broadcast address (e.g., 192.168.255.255 on the subnet 192.168.0.0/16).
pub const BROADCAST_ADDRESS: Ipv4Addr = Ipv4Addr::new([255, 255, 255, 255]);
}
/// IPv6.
///
/// `Ipv6` implements `Ip` for IPv6.
#[derive(Debug, Default)]
pub struct Ipv6;
impl Ip for Ipv6 {
const VERSION: IpVersion = IpVersion::V6;
const LOOPBACK_ADDRESS: Ipv6Addr =
Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
const LOOPBACK_SUBNET: Subnet<Ipv6Addr> = Subnet {
network: Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
prefix: 128,
};
type Addr = Ipv6Addr;
}
/// An IPv4 or IPv6 address.
pub trait IpAddr
where
Self: Sized + Eq + PartialEq + Hash + Copy + Display + Debug + self::sealed::Sealed,
{
/// The number of bytes in an address of this type.
///
/// 4 for IPv4 and 16 for IPv6.
const BYTES: u8;
/// The IP version type of this address.
///
/// `Ipv4` for `Ipv4Addr` and `Ipv6` for `Ipv6Addr`.
type Version: Ip<Addr = Self>;
/// Get the underlying bytes of the address.
fn bytes(&self) -> &[u8];
/// Mask off the top bits of the address.
///
/// Return a copy of `self` where all but the top `bits` bits are set to 0.
fn mask(&self, bits: u8) -> Self;
}
/// An IPv4 address.
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Ipv4Addr([u8; 4]);
unsafe impl FromBytes for Ipv4Addr {}
unsafe impl AsBytes for Ipv4Addr {}
unsafe impl Unaligned for Ipv4Addr {}
impl Ipv4Addr {
/// Create a new IPv4 address.
pub const fn new(bytes: [u8; 4]) -> Self {
Ipv4Addr(bytes)
}
/// Get the bytes of the IPv4 address.
pub const fn ipv4_bytes(&self) -> [u8; 4] {
self.0
}
}
impl IpAddr for Ipv4Addr {
const BYTES: u8 = 4;
type Version = Ipv4;
fn mask(&self, bits: u8) -> Self {
assert!(bits <= 32);
if bits == 0 {
// shifting left by the size of the value is undefined
Ipv4Addr([0; 4])
} else {
let mask = <u32>::max_value() << (32 - bits);
let masked = NetworkEndian::read_u32(&self.0) & mask;
let mut ret = Ipv4Addr::default();
NetworkEndian::write_u32(&mut ret.0, masked);
ret
}
}
fn bytes(&self) -> &[u8] {
&self.0
}
}
impl From<std::net::Ipv4Addr> for Ipv4Addr {
fn from(ip: std::net::Ipv4Addr) -> Self {
Ipv4Addr::new(ip.octets())
}
}
impl Display for Ipv4Addr {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}.{}.{}.{}", self.0[0], self.0[1], self.0[2], self.0[3])
}
}
impl Debug for Ipv4Addr {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
Display::fmt(self, f)
}
}
/// An IPv6 address.
#[derive(Copy, Clone, Default, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct Ipv6Addr([u8; 16]);
unsafe impl FromBytes for Ipv6Addr {}
unsafe impl AsBytes for Ipv6Addr {}
unsafe impl Unaligned for Ipv6Addr {}
impl Ipv6Addr {
/// Create a new IPv6 address.
pub const fn new(bytes: [u8; 16]) -> Self {
Ipv6Addr(bytes)
}
/// Get the bytes of the IPv6 address.
pub const fn ipv6_bytes(&self) -> [u8; 16] {
self.0
}
}
impl IpAddr for Ipv6Addr {
const BYTES: u8 = 16;
type Version = Ipv6;
fn mask(&self, bits: u8) -> Self {
assert!(bits <= 128);
if bits == 0 {
// shifting left by the size of the value is undefined
Ipv6Addr([0; 16])
} else {
let mask = <u128>::max_value() << (128 - bits);
let masked = NetworkEndian::read_u128(&self.0) & mask;
let mut ret = Ipv6Addr::default();
NetworkEndian::write_u128(&mut ret.0, masked);
ret
}
}
fn bytes(&self) -> &[u8] {
&self.0
}
}
impl From<std::net::Ipv6Addr> for Ipv6Addr {
fn from(ip: std::net::Ipv6Addr) -> Self {
Ipv6Addr::new(ip.octets())
}
}
impl Display for Ipv6Addr {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let to_u16 = |idx| NetworkEndian::read_u16(&self.0[idx..idx + 2]);
Display::fmt(
&std::net::Ipv6Addr::new(
to_u16(0),
to_u16(2),
to_u16(4),
to_u16(6),
to_u16(8),
to_u16(10),
to_u16(12),
to_u16(14),
),
f,
)
}
}
impl Debug for Ipv6Addr {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
Display::fmt(self, f)
}
}
/// An IP subnet.
///
/// `Subnet` is a combination of an IP network address and a prefix length.
#[derive(Copy, Clone)]
pub struct Subnet<A: IpAddr> {
// invariant: normalized to contain only prefix bits
network: A,
prefix: u8,
}
impl<A: IpAddr> Subnet<A> {
/// Create a new subnet.
///
/// Create a new subnet with the given network address and prefix length.
///
/// # Panics
///
/// `new` panics if `prefix` is longer than the number of bits in this type
/// of IP address (32 for IPv4 and 128 for IPv6).
pub fn new(network: A, prefix: u8) -> Subnet<A> {
assert!(prefix <= A::BYTES * 8);
let network = network.mask(prefix);
Subnet { network, prefix }
}
/// Get the network address component of this subnet.
///
/// `network` returns the network address component of this subnet. Any bits
/// beyond the prefix will be zero.
pub fn network(&self) -> A {
self.network
}
/// Get the prefix length component of this subnet.
pub fn prefix(&self) -> u8 {
self.prefix
}
/// Test whether an address is in this subnet.
///
/// Test whether `address` is in this subnet by testing whether the prefix
/// bits match the prefix bits of the subnet's network address. This is
/// equivalent to `subnet.network() == address.mask(subnet.prefix())`.
pub fn contains(&self, address: A) -> bool {
self.network == address.mask(self.prefix)
}
}
impl Subnet<Ipv4Addr> {
/// Get the broadcast address in this subnet.
pub fn broadcast(&self) -> Ipv4Addr {
let bits = 32 - self.prefix;
if bits == 32 {
// shifting right by the size of the value is undefined
Ipv4Addr([0xFF; 4])
} else {
let mask = <u32>::max_value() >> (32 - bits);
let masked = NetworkEndian::read_u32(&self.network.0) | mask;
let mut ret = Ipv4Addr::default();
NetworkEndian::write_u32(&mut ret.0, masked);
ret
}
}
}
impl<A: IpAddr> Display for Subnet<A> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}/{}", self.network, self.prefix)
}
}
impl<A: IpAddr> Debug for Subnet<A> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{}/{}", self.network, self.prefix)
}
}
/// An IP protocol or next header number.
///
/// For IPv4, this is the protocol number. For IPv6, this is the next header
/// number.
#[allow(missing_docs)]
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr(u8)]
pub enum IpProto {
Icmp = IpProto::ICMP,
Tcp = IpProto::TCP,
Udp = IpProto::UDP,
Icmpv6 = IpProto::ICMPV6,
}
impl IpProto {
const ICMP: u8 = 1;
const TCP: u8 = 6;
const UDP: u8 = 17;
const ICMPV6: u8 = 58;
/// Construct an `IpProto` from a `u8`.
///
/// `from_u8` returns the `IpProto` with the numerical value `u`, or `None`
/// if the value is unrecognized.
pub fn from_u8(u: u8) -> Option<IpProto> {
match u {
Self::ICMP => Some(IpProto::Icmp),
Self::TCP => Some(IpProto::Tcp),
Self::UDP => Some(IpProto::Udp),
Self::ICMPV6 => Some(IpProto::Icmpv6),
_ => None,
}
}
}
impl Display for IpProto {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(
f,
"{}",
match self {
IpProto::Icmp => "ICMP",
IpProto::Tcp => "TCP",
IpProto::Udp => "UDP",
IpProto::Icmpv6 => "ICMPv6",
}
)
}
}
impl Debug for IpProto {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
Display::fmt(self, f)
}
}
/// An IPv4 header option.
///
/// An IPv4 header option comprises metadata about the option (which is stored
/// in the kind byte) and the option itself. Note that all kind-byte-only
/// options are handled by the utilities in `wire::util::options`, so this type
/// only supports options with variable-length data.
///
/// See [Wikipedia] or [RFC 791] for more details.
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/IPv4#Options
/// [RFC 791]: https://tools.ietf.org/html/rfc791#page-15
pub struct Ipv4Option<'a> {
/// Whether this option needs to be copied into all fragments of a fragmented packet.
pub copied: bool,
// TODO(joshlf): include "Option Class"?
/// The variable-length option data.
pub data: Ipv4OptionData<'a>,
}
/// The data associated with an IPv4 header option.
///
/// `Ipv4OptionData` represents the variable-length data field of an IPv4 header
/// option.
#[allow(missing_docs)]
pub enum Ipv4OptionData<'a> {
// The maximum header length is 60 bytes, and the fixed-length header is 20
// bytes, so there are 40 bytes for the options. That leaves a maximum
// options size of 1 kind byte + 1 length byte + 38 data bytes.
/// Data for an unrecognized option kind.
///
/// Any unrecognized option kind will have its data parsed using this
/// variant. This allows code to copy unrecognized options into packets when
/// forwarding.
///
/// `data`'s length is in the range [0, 38].
Unrecognized { kind: u8, len: u8, data: &'a [u8] },
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! add_mask_test {
($name:ident, $addr:ident, $from_ip:expr => {
$($mask:expr => $to_ip:expr),*
}) => {
#[test]
fn $name() {
let from = $addr::new($from_ip);
$(
assert_eq!(from.mask($mask), $addr::new($to_ip), "(`{}`.mask({}))", from, $mask);
)*
}
};
($name:ident, $addr:ident, $from_ip:expr => {
$($mask:expr => $to_ip:expr),*,
}) => (
add_mask_test!($name, $addr, $from_ip => { $($mask => $to_ip),* });
)
}
add_mask_test!(v4_full_mask, Ipv4Addr, [255, 254, 253, 252] => {
32 => [255, 254, 253, 252],
28 => [255, 254, 253, 240],
24 => [255, 254, 253, 0],
20 => [255, 254, 240, 0],
16 => [255, 254, 0, 0],
12 => [255, 240, 0, 0],
8 => [255, 0, 0, 0],
4 => [240, 0, 0, 0],
0 => [0, 0, 0, 0],
});
add_mask_test!(v6_full_mask, Ipv6Addr,
[0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1, 0xF0] => {
128 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1, 0xF0],
112 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0xF3, 0xF2, 0x00, 0x00],
96 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0xF5, 0xF4, 0x00, 0x00, 0x00, 0x00],
80 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
64 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
48 => [0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
32 => [0xFF, 0xFE, 0xFD, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
16 => [0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
8 => [0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
0 => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
}
);
}