renet/src/wire/ip.rs

115 lines
3.1 KiB
Rust
Raw Normal View History

2016-12-13 01:26:06 +08:00
use core::fmt;
use super::Ipv4Address;
2016-12-13 01:26:06 +08:00
enum_with_unknown! {
/// Internetworking protocol type.
2016-12-13 01:26:06 +08:00
pub enum ProtocolType(u8) {
Icmp = 0x01,
Tcp = 0x06,
Udp = 0x11
}
}
impl fmt::Display for ProtocolType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&ProtocolType::Icmp => write!(f, "ICMP"),
&ProtocolType::Tcp => write!(f, "TCP"),
&ProtocolType::Udp => write!(f, "UDP"),
&ProtocolType::Unknown(id) => write!(f, "0x{:02x}", id)
}
}
}
/// An internetworking address.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum Address {
/// An invalid address.
/// May be used as a placeholder for storage where the address is not assigned yet.
Invalid,
/// An IPv4 address.
Ipv4(Ipv4Address)
}
impl Address {
/// Create an address wrapping an IPv4 address with the given octets.
pub const fn ipv4(octets: [u8; 4]) -> Address {
Address::Ipv4(Ipv4Address(octets))
}
/// Query whether the address is a valid unicast address.
pub fn is_unicast(&self) -> bool {
match self {
&Address::Invalid => false,
&Address::Ipv4(addr) => addr.is_unicast()
}
}
}
impl Default for Address {
fn default() -> Address {
Address::Invalid
}
}
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 {
&Address::Invalid => write!(f, "(invalid)"),
&Address::Ipv4(addr) => write!(f, "{}", addr)
}
}
}
pub mod checksum {
use byteorder::{ByteOrder, NetworkEndian};
use super::*;
/// Compute an RFC 1071 compliant checksum (without the final complement).
pub fn data(data: &[u8]) -> u16 {
let mut accum: u32 = 0;
for i in (0..data.len()).step_by(2) {
let word = NetworkEndian::read_u16(&data[i..i + 2]) as u32;
accum += word;
}
(((accum >> 16) as u16) + (accum as u16))
}
/// 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;
}
(((accum >> 16) as u16) + (accum as u16))
}
/// Compute an IP pseudo header checksum.
pub fn pseudo_header(src_addr: &Address, dst_addr: &Address,
protocol: ProtocolType, 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[..])
])
},
_ => panic!("Unexpected pseudo header ")
}
}
}