renet/src/wire/ip.rs

514 lines
15 KiB
Rust
Raw Normal View History

2016-12-13 01:26:06 +08:00
use core::fmt;
use {Error, Result};
use super::{Ipv4Address, Ipv4Packet, Ipv4Repr};
2016-12-13 01:26:06 +08:00
2017-06-18 18:14:20 +08:00
/// Internet protocol version.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
2017-06-18 18:14:20 +08:00
pub enum Version {
2017-07-30 10:00:23 +08:00
Unspecified,
2017-06-18 18:14:20 +08:00
Ipv4,
#[doc(hidden)]
__Nonexhaustive,
}
2017-07-30 10:00:23 +08:00
impl Version {
/// Return the version of an IP packet stored in the provided buffer.
///
/// This function never returns `Ok(IpVersion::Unspecified)`; instead,
/// unknown versions result in `Err(Error::Unrecognized)`.
pub fn of_packet(data: &[u8]) -> Result<Version> {
match data[0] >> 4 {
4 => Ok(Version::Ipv4),
_ => Err(Error::Unrecognized)
}
}
}
2017-06-18 18:14:20 +08:00
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
2017-07-30 10:00:23 +08:00
&Version::Unspecified => write!(f, "IPv?"),
2017-06-18 18:14:20 +08:00
&Version::Ipv4 => write!(f, "IPv4"),
&Version::__Nonexhaustive => unreachable!()
}
}
}
2016-12-13 01:26:06 +08:00
enum_with_unknown! {
2017-06-18 18:14:20 +08:00
/// IP datagram encapsulated protocol.
pub enum Protocol(u8) {
2016-12-13 01:26:06 +08:00
Icmp = 0x01,
Tcp = 0x06,
Udp = 0x11
}
}
impl fmt::Display for Protocol {
2016-12-13 01:26:06 +08:00
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::Unknown(id) => write!(f, "0x{:02x}", id)
2016-12-13 01:26:06 +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.
/// May be used as a placeholder for storage where the address is not assigned yet.
2016-12-21 03:51:52 +08:00
Unspecified,
/// An IPv4 address.
Ipv4(Ipv4Address)
}
impl Address {
/// Create an address wrapping an IPv4 address with the given octets.
2016-12-28 08:08:01 +08:00
pub fn v4(a0: u8, a1: u8, a2: u8, a3: u8) -> Address {
2016-12-31 19:44:51 +08:00
Address::Ipv4(Ipv4Address::new(a0, a1, a2, a3))
}
/// 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-15 01:39:44 +08:00
/// 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()
}
}
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
}
}
/// Return an unspecified address that has the same IP version as `self`.
2017-07-27 19:26:07 +08:00
pub fn to_unspecified(&self) -> Address {
match self {
&Address::Unspecified => Address::Unspecified,
// &Address::Ipv4 => Address::Ipv4(Ipv4Address::UNSPECIFIED),
&Address::Ipv4(_) => Address::Ipv4(Ipv4Address(/*FIXME*/[0x00; 4])),
}
}
}
impl Default for Address {
fn default() -> Address {
2016-12-21 03:51:52 +08:00
Address::Unspecified
}
}
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-15 01:39:44 +08:00
/// An internet endpoint address.
2016-12-28 13:33:12 +08:00
///
/// An endpoint can be constructed from a port, in which case the address is unspecified.
2016-12-15 01:39:44 +08:00
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct Endpoint {
pub addr: Address,
pub port: u16
}
impl Endpoint {
// pub const UNSPECIFIED: Endpoint = Endpoint { addr: Address::Unspecified, port: 0 };
2016-12-16 01:07:56 +08:00
/// Create an endpoint address from given address and port.
2016-12-28 08:08:01 +08:00
pub fn new(addr: Address, port: u16) -> Endpoint {
2016-12-15 01:39:44 +08:00
Endpoint { addr: addr, port: port }
}
2016-12-28 12:56:49 +08:00
/// Query whether the endpoint has a specified address and port.
pub fn is_specified(&self) -> bool {
!self.addr.is_unspecified() && self.port != 0
2016-12-28 12:56:49 +08:00
}
2016-12-15 01:39:44 +08:00
}
impl fmt::Display for Endpoint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.addr, self.port)
}
}
2016-12-28 13:33:12 +08:00
impl From<u16> for Endpoint {
fn from(port: u16) -> Endpoint {
Endpoint { addr: Address::Unspecified, port: port }
}
}
2017-03-05 11:52:47 +08:00
impl<T: Into<Address>> From<(T, u16)> for Endpoint {
fn from((addr, port): (T, u16)) -> Endpoint {
Endpoint { addr: addr.into(), port: port }
}
}
/// 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, PartialEq, Eq)]
pub enum IpRepr {
Unspecified {
src_addr: Address,
dst_addr: Address,
protocol: Protocol,
payload_len: usize
},
Ipv4(Ipv4Repr),
#[doc(hidden)]
__Nonexhaustive
}
impl From<Ipv4Repr> for IpRepr {
fn from(repr: Ipv4Repr) -> IpRepr {
IpRepr::Ipv4(repr)
}
}
impl IpRepr {
2017-07-30 10:00:23 +08:00
/// Return the protocol version.
pub fn version(&self) -> Version {
match self {
&IpRepr::Unspecified { .. } => Version::Unspecified,
&IpRepr::Ipv4(_) => Version::Ipv4,
&IpRepr::__Nonexhaustive => unreachable!()
}
}
/// 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!()
}
}
/// Return the payload length.
pub fn payload_len(&self) -> usize {
match self {
&IpRepr::Unspecified { payload_len, .. } => payload_len,
&IpRepr::Ipv4(repr) => repr.payload_len,
&IpRepr::__Nonexhaustive => unreachable!()
}
}
/// Set the payload length.
pub fn set_payload_len(&mut self, length: usize) {
match self {
&mut IpRepr::Unspecified { ref mut payload_len, .. } =>
*payload_len = length,
&mut IpRepr::Ipv4(Ipv4Repr { ref mut payload_len, .. }) =>
*payload_len = length,
&mut 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> {
match self {
&IpRepr::Unspecified {
src_addr: Address::Ipv4(src_addr),
dst_addr: Address::Ipv4(dst_addr),
protocol, payload_len
} => {
Ok(IpRepr::Ipv4(Ipv4Repr {
src_addr: src_addr,
dst_addr: dst_addr,
protocol: protocol,
payload_len: payload_len
}))
}
&IpRepr::Unspecified {
src_addr: Address::Unspecified,
dst_addr: Address::Ipv4(dst_addr),
protocol, payload_len
} => {
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 {
2017-06-25 00:34:32 +08:00
src_addr: src_addr.ok_or(Error::Unaddressable)?,
dst_addr: dst_addr,
protocol: protocol,
payload_len: payload_len
}))
}
&IpRepr::Unspecified { dst_addr: Address::Unspecified, .. } =>
panic!("unspecified destination IP address"),
// &IpRepr::Unspecified { .. } =>
// panic!("source and destination IP address families do not match"),
&IpRepr::Ipv4(mut repr) => {
if repr.src_addr.is_unspecified() {
for addr in fallback_src_addrs {
match addr {
&Address::Ipv4(addr) => {
repr.src_addr = addr;
return Ok(IpRepr::Ipv4(repr));
}
_ => ()
}
}
Err(Error::Unaddressable)
} else {
Ok(IpRepr::Ipv4(repr))
}
},
&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) {
match self {
&IpRepr::Unspecified { .. } =>
panic!("unspecified IP representation"),
&IpRepr::Ipv4(repr) =>
repr.emit(&mut Ipv4Packet::new(buffer)),
&IpRepr::__Nonexhaustive =>
unreachable!()
}
}
/// Return the total length of a packet that will be emitted from this
/// high-level representation.
///
/// This is the same as `repr.buffer_len() + repr.payload_len()`.
///
/// # Panics
/// This function panics if invoked on an unspecified representation.
pub fn total_len(&self) -> usize {
self.buffer_len() + self.payload_len()
}
}
pub mod checksum {
use byteorder::{ByteOrder, NetworkEndian};
use super::*;
fn propagate_carries(word: u32) -> u16 {
let sum = (word >> 16) + (word & 0xffff);
((sum >> 16) as u16) + (sum as u16)
}
/// 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() {
let word;
if i + 2 <= data.len() {
word = NetworkEndian::read_u16(&data[i..i + 2]) as u32
} else {
word = (data[i] as u32) << 8
}
accum += word;
2016-12-28 08:05:31 +08:00
i += 2;
}
propagate_carries(accum)
}
/// 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;
}
propagate_carries(accum)
}
/// Compute an IP pseudo header checksum.
pub fn pseudo_header(src_addr: &Address, dst_addr: &Address,
protocol: Protocol, length: u32) -> u16 {
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[..])
])
},
2017-07-27 19:26:07 +08:00
_ => panic!("Unexpected pseudo header addresses: {}, {}",
src_addr, dst_addr)
}
}
}
#[cfg(test)]
mod test {
use super::*;
use wire::{Ipv4Address, IpProtocol, IpAddress, Ipv4Repr};
#[test]
fn ip_repr_lower() {
let ip_addr_a = Ipv4Address::new(1, 2, 3, 4);
let ip_addr_b = Ipv4Address::new(5, 6, 7, 8);
let proto = IpProtocol::Icmp;
let payload_len = 10;
assert_eq!(
IpRepr::Unspecified{
src_addr: IpAddress::Ipv4(ip_addr_a),
dst_addr: IpAddress::Ipv4(ip_addr_b),
protocol: proto,
payload_len
}.lower(&[]),
Ok(IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
payload_len
}))
);
assert_eq!(
IpRepr::Unspecified{
src_addr: IpAddress::Unspecified,
dst_addr: IpAddress::Ipv4(ip_addr_b),
protocol: proto,
payload_len
}.lower(&[]),
Err(Error::Unaddressable)
);
assert_eq!(
IpRepr::Unspecified{
src_addr: IpAddress::Unspecified,
dst_addr: IpAddress::Ipv4(ip_addr_b),
protocol: proto,
payload_len
}.lower(&[IpAddress::Ipv4(ip_addr_a)]),
Ok(IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
payload_len
}))
);
assert_eq!(
IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
payload_len
}).lower(&[]),
Ok(IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
payload_len
}))
);
assert_eq!(
IpRepr::Ipv4(Ipv4Repr{
src_addr: Ipv4Address::new(0, 0, 0, 0),
dst_addr: ip_addr_b,
protocol: proto,
payload_len
}).lower(&[]),
Err(Error::Unaddressable)
);
assert_eq!(
IpRepr::Ipv4(Ipv4Repr{
src_addr: Ipv4Address::new(0, 0, 0, 0),
dst_addr: ip_addr_b,
protocol: proto,
payload_len
}).lower(&[IpAddress::Ipv4(ip_addr_a)]),
Ok(IpRepr::Ipv4(Ipv4Repr{
src_addr: ip_addr_a,
dst_addr: ip_addr_b,
protocol: proto,
payload_len
}))
);
}
}