2016-12-13 01:26:06 +08:00
|
|
|
use core::fmt;
|
2016-12-14 09:59:47 +08:00
|
|
|
|
2016-12-26 19:20:20 +08:00
|
|
|
use Error;
|
|
|
|
use super::{Ipv4Address, Ipv4Packet, Ipv4Repr};
|
2016-12-13 01:26:06 +08:00
|
|
|
|
|
|
|
enum_with_unknown! {
|
2016-12-20 21:54:11 +08:00
|
|
|
/// Internetworking protocol.
|
|
|
|
pub enum Protocol(u8) {
|
2016-12-13 01:26:06 +08:00
|
|
|
Icmp = 0x01,
|
|
|
|
Tcp = 0x06,
|
|
|
|
Udp = 0x11
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-20 21:54:11 +08:00
|
|
|
impl fmt::Display for Protocol {
|
2016-12-13 01:26:06 +08:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
2016-12-20 21:54:11 +08:00
|
|
|
&Protocol::Icmp => write!(f, "ICMP"),
|
|
|
|
&Protocol::Tcp => write!(f, "TCP"),
|
|
|
|
&Protocol::Udp => write!(f, "UDP"),
|
|
|
|
&Protocol::Unknown(id) => write!(f, "0x{:02x}", id)
|
2016-12-13 01:26:06 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-12-13 06:11:52 +08:00
|
|
|
|
2016-12-14 09:59:47 +08:00
|
|
|
/// An internetworking address.
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
|
|
|
pub enum Address {
|
2016-12-21 03:51:52 +08:00
|
|
|
/// An unspecified address.
|
2016-12-14 09:59:47 +08:00
|
|
|
/// May be used as a placeholder for storage where the address is not assigned yet.
|
2016-12-21 03:51:52 +08:00
|
|
|
Unspecified,
|
2016-12-14 09:59:47 +08:00
|
|
|
/// An IPv4 address.
|
|
|
|
Ipv4(Ipv4Address)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Address {
|
|
|
|
/// Create an address wrapping an IPv4 address with the given octets.
|
2016-12-20 21:54:11 +08:00
|
|
|
pub const fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
|
|
|
|
Address::Ipv4(Ipv4Address([a0, a1, a2, a3]))
|
2016-12-14 09:59:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Query whether the address is a valid unicast address.
|
|
|
|
pub fn is_unicast(&self) -> bool {
|
|
|
|
match self {
|
2016-12-21 03:51:52 +08:00
|
|
|
&Address::Unspecified => false,
|
|
|
|
&Address::Ipv4(addr) => addr.is_unicast()
|
2016-12-14 09:59:47 +08:00
|
|
|
}
|
|
|
|
}
|
2016-12-15 01:39:44 +08:00
|
|
|
|
|
|
|
/// Query whether the address falls into the "unspecified" range.
|
|
|
|
pub fn is_unspecified(&self) -> bool {
|
|
|
|
match self {
|
2016-12-21 03:51:52 +08:00
|
|
|
&Address::Unspecified => true,
|
|
|
|
&Address::Ipv4(addr) => addr.is_unspecified()
|
2016-12-15 01:39:44 +08:00
|
|
|
}
|
|
|
|
}
|
2016-12-14 09:59:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Address {
|
|
|
|
fn default() -> Address {
|
2016-12-21 03:51:52 +08:00
|
|
|
Address::Unspecified
|
2016-12-14 09:59:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Ipv4Address> for Address {
|
|
|
|
fn from(addr: Ipv4Address) -> Self {
|
|
|
|
Address::Ipv4(addr)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Address {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
2016-12-21 03:51:52 +08:00
|
|
|
&Address::Unspecified => write!(f, "*"),
|
|
|
|
&Address::Ipv4(addr) => write!(f, "{}", addr)
|
2016-12-14 09:59:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-15 01:39:44 +08:00
|
|
|
/// An internet endpoint address.
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
|
|
|
pub struct Endpoint {
|
|
|
|
pub addr: Address,
|
|
|
|
pub port: u16
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Endpoint {
|
2016-12-21 03:51:52 +08:00
|
|
|
pub const UNSPECIFIED: Endpoint = Endpoint { addr: Address::Unspecified, port: 0 };
|
2016-12-16 01:07:56 +08:00
|
|
|
|
2016-12-21 03:18:35 +08:00
|
|
|
/// Create an endpoint address from given address and port.
|
2016-12-25 05:52:23 +08:00
|
|
|
pub const fn new(addr: Address, port: u16) -> Endpoint {
|
2016-12-15 01:39:44 +08:00
|
|
|
Endpoint { addr: addr, port: port }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Endpoint {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}:{}", self.addr, self.port)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-26 19:20:20 +08:00
|
|
|
/// An IP packet representation.
|
|
|
|
///
|
|
|
|
/// This enum abstracts the various versions of IP packets. It either contains a concrete
|
|
|
|
/// high-level representation for some IP protocol version, or an unspecified representation,
|
|
|
|
/// which permits the `IpAddress::Unspecified` addresses.
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
pub enum IpRepr {
|
|
|
|
Unspecified {
|
|
|
|
src_addr: Address,
|
|
|
|
dst_addr: Address,
|
|
|
|
protocol: Protocol
|
|
|
|
},
|
|
|
|
Ipv4(Ipv4Repr),
|
|
|
|
#[doc(hidden)]
|
|
|
|
__Nonexhaustive
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IpRepr {
|
|
|
|
/// Return the source address.
|
|
|
|
pub fn src_addr(&self) -> Address {
|
|
|
|
match self {
|
|
|
|
&IpRepr::Unspecified { src_addr, .. } => src_addr,
|
|
|
|
&IpRepr::Ipv4(repr) => Address::Ipv4(repr.src_addr),
|
|
|
|
&IpRepr::__Nonexhaustive => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the destination address.
|
|
|
|
pub fn dst_addr(&self) -> Address {
|
|
|
|
match self {
|
|
|
|
&IpRepr::Unspecified { dst_addr, .. } => dst_addr,
|
|
|
|
&IpRepr::Ipv4(repr) => Address::Ipv4(repr.dst_addr),
|
|
|
|
&IpRepr::__Nonexhaustive => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the protocol.
|
|
|
|
pub fn protocol(&self) -> Protocol {
|
|
|
|
match self {
|
|
|
|
&IpRepr::Unspecified { protocol, .. } => protocol,
|
|
|
|
&IpRepr::Ipv4(repr) => repr.protocol,
|
|
|
|
&IpRepr::__Nonexhaustive => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert an unspecified representation into a concrete one, or return
|
|
|
|
/// `Err(Error::Unaddressable)` if not possible.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
/// This function panics if source and destination addresses belong to different families,
|
|
|
|
/// or the destination address is unspecified, since this indicates a logic error.
|
|
|
|
pub fn lower(&self, fallback_src_addrs: &[Address]) -> Result<IpRepr, Error> {
|
|
|
|
match self {
|
|
|
|
&IpRepr::Unspecified {
|
|
|
|
src_addr: Address::Ipv4(src_addr),
|
|
|
|
dst_addr: Address::Ipv4(dst_addr),
|
|
|
|
protocol
|
|
|
|
} => {
|
|
|
|
Ok(IpRepr::Ipv4(Ipv4Repr {
|
|
|
|
src_addr: src_addr,
|
|
|
|
dst_addr: dst_addr,
|
|
|
|
protocol: protocol
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
&IpRepr::Unspecified {
|
|
|
|
src_addr: Address::Unspecified,
|
|
|
|
dst_addr: Address::Ipv4(dst_addr),
|
|
|
|
protocol
|
|
|
|
} => {
|
|
|
|
let mut src_addr = None;
|
|
|
|
for addr in fallback_src_addrs {
|
|
|
|
match addr {
|
|
|
|
&Address::Ipv4(addr) => {
|
|
|
|
src_addr = Some(addr);
|
|
|
|
break
|
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(IpRepr::Ipv4(Ipv4Repr {
|
|
|
|
src_addr: try!(src_addr.ok_or(Error::Unaddressable)),
|
|
|
|
dst_addr: dst_addr,
|
|
|
|
protocol: protocol
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
&IpRepr::Unspecified { dst_addr: Address::Unspecified, .. } =>
|
|
|
|
panic!("unspecified destination IP address"),
|
|
|
|
// &IpRepr::Unspecified { .. } =>
|
|
|
|
// panic!("source and destination IP address families do not match"),
|
|
|
|
|
|
|
|
repr @ &IpRepr::Ipv4(_) => Ok(repr.clone()),
|
|
|
|
&IpRepr::__Nonexhaustive => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the length of a header that will be emitted from this high-level representation.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
/// This function panics if invoked on an unspecified representation.
|
|
|
|
pub fn buffer_len(&self) -> usize {
|
|
|
|
match self {
|
|
|
|
&IpRepr::Unspecified { .. } => panic!("unspecified IP representation"),
|
|
|
|
&IpRepr::Ipv4(repr) => repr.buffer_len(),
|
|
|
|
&IpRepr::__Nonexhaustive => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit this high-level representation into a buffer.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
/// This function panics if invoked on an unspecified representation.
|
|
|
|
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, buffer: T, payload_len: usize) {
|
|
|
|
match self {
|
|
|
|
&IpRepr::Unspecified { .. } => panic!("unspecified IP representation"),
|
|
|
|
&IpRepr::Ipv4(repr) => {
|
|
|
|
let mut packet = Ipv4Packet::new(buffer).expect("undersized buffer");
|
|
|
|
repr.emit(&mut packet, payload_len)
|
|
|
|
}
|
|
|
|
&IpRepr::__Nonexhaustive => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-14 09:59:47 +08:00
|
|
|
pub mod checksum {
|
|
|
|
use byteorder::{ByteOrder, NetworkEndian};
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
2016-12-28 02:43:49 +08:00
|
|
|
fn propagate_carries(word: u32) -> u16 {
|
|
|
|
let sum = (word >> 16) + (word & 0xffff);
|
|
|
|
((sum >> 16) as u16) + (sum as u16)
|
|
|
|
}
|
|
|
|
|
2016-12-14 09:59:47 +08:00
|
|
|
/// Compute an RFC 1071 compliant checksum (without the final complement).
|
|
|
|
pub fn data(data: &[u8]) -> u16 {
|
|
|
|
let mut accum: u32 = 0;
|
2016-12-28 08:05:31 +08:00
|
|
|
let mut i = 0;
|
|
|
|
while i < data.len() {
|
2016-12-20 08:17:29 +08:00
|
|
|
let word;
|
|
|
|
if i + 2 <= data.len() {
|
|
|
|
word = NetworkEndian::read_u16(&data[i..i + 2]) as u32
|
|
|
|
} else {
|
|
|
|
word = (data[i] as u32) << 8
|
|
|
|
}
|
2016-12-14 09:59:47 +08:00
|
|
|
accum += word;
|
2016-12-28 08:05:31 +08:00
|
|
|
i += 2;
|
2016-12-14 09:59:47 +08:00
|
|
|
}
|
2016-12-28 02:43:49 +08:00
|
|
|
propagate_carries(accum)
|
2016-12-14 09:59:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Combine several RFC 1071 compliant checksums.
|
|
|
|
pub fn combine(checksums: &[u16]) -> u16 {
|
|
|
|
let mut accum: u32 = 0;
|
|
|
|
for &word in checksums {
|
|
|
|
accum += word as u32;
|
|
|
|
}
|
2016-12-28 02:43:49 +08:00
|
|
|
propagate_carries(accum)
|
2016-12-14 09:59:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute an IP pseudo header checksum.
|
|
|
|
pub fn pseudo_header(src_addr: &Address, dst_addr: &Address,
|
2016-12-20 21:54:11 +08:00
|
|
|
protocol: Protocol, length: u32) -> u16 {
|
2016-12-14 09:59:47 +08:00
|
|
|
match (src_addr, dst_addr) {
|
|
|
|
(&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
|
|
|
|
let mut proto_len = [0u8; 4];
|
|
|
|
proto_len[1] = protocol.into();
|
|
|
|
NetworkEndian::write_u16(&mut proto_len[2..4], length as u16);
|
|
|
|
|
|
|
|
combine(&[
|
|
|
|
data(src_addr.as_bytes()),
|
|
|
|
data(dst_addr.as_bytes()),
|
|
|
|
data(&proto_len[..])
|
|
|
|
])
|
|
|
|
},
|
|
|
|
|
|
|
|
_ => panic!("Unexpected pseudo header ")
|
|
|
|
}
|
2016-12-13 06:11:52 +08:00
|
|
|
}
|
|
|
|
}
|