Implement UDP representation parsing and emission.
parent
5a721a7b11
commit
2b01a3dace
|
@ -3,14 +3,14 @@ extern crate smoltcp;
|
|||
|
||||
use std::env;
|
||||
use smoltcp::phy::{Tracer, TapInterface};
|
||||
use smoltcp::wire::{EthernetFrame, EthernetAddress};
|
||||
use smoltcp::iface::{ProtocolAddress, SliceArpCache, EthernetInterface};
|
||||
use smoltcp::wire::{EthernetFrame, EthernetAddress, InternetAddress};
|
||||
use smoltcp::iface::{SliceArpCache, EthernetInterface};
|
||||
|
||||
fn main() {
|
||||
let ifname = env::args().nth(1).unwrap();
|
||||
|
||||
let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||
let protocol_addrs = [ProtocolAddress::ipv4([192, 168, 69, 1])];
|
||||
let protocol_addrs = [InternetAddress::ipv4([192, 168, 69, 1])];
|
||||
|
||||
let device = TapInterface::new(ifname.as_ref()).unwrap();
|
||||
let device = Tracer::<_, EthernetFrame<&[u8]>>::new(device);
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
use wire::EthernetAddress;
|
||||
use super::ProtocolAddress;
|
||||
use wire::{EthernetAddress, InternetAddress};
|
||||
|
||||
/// An Address Resolution Protocol cache.
|
||||
///
|
||||
/// This cache maps protocol addresses to hardware addresses.
|
||||
pub trait Cache {
|
||||
/// Update the cache to map given protocol address to given hardware address.
|
||||
fn fill(&mut self, protocol_addr: ProtocolAddress, hardware_addr: EthernetAddress);
|
||||
fn fill(&mut self, protocol_addr: InternetAddress, hardware_addr: EthernetAddress);
|
||||
|
||||
/// Look up the hardware address corresponding for the given protocol address.
|
||||
fn lookup(&mut self, protocol_addr: ProtocolAddress) -> Option<EthernetAddress>;
|
||||
fn lookup(&mut self, protocol_addr: InternetAddress) -> Option<EthernetAddress>;
|
||||
}
|
||||
|
||||
/// An Address Resolution Protocol cache backed by a slice.
|
||||
|
@ -26,7 +25,7 @@ pub trait Cache {
|
|||
/// let mut arp_cache = SliceArpCache::new(&mut arp_cache_storage);
|
||||
/// ```
|
||||
pub struct SliceCache<'a> {
|
||||
storage: &'a mut [(ProtocolAddress, EthernetAddress, usize)],
|
||||
storage: &'a mut [(InternetAddress, EthernetAddress, usize)],
|
||||
counter: usize
|
||||
}
|
||||
|
||||
|
@ -35,7 +34,7 @@ impl<'a> SliceCache<'a> {
|
|||
///
|
||||
/// # Panics
|
||||
/// This function panics if `storage.len() == 0`.
|
||||
pub fn new(storage: &'a mut [(ProtocolAddress, EthernetAddress, usize)]) -> SliceCache<'a> {
|
||||
pub fn new(storage: &'a mut [(InternetAddress, EthernetAddress, usize)]) -> SliceCache<'a> {
|
||||
if storage.len() == 0 {
|
||||
panic!("ARP slice cache created with empty storage")
|
||||
}
|
||||
|
@ -50,9 +49,9 @@ impl<'a> SliceCache<'a> {
|
|||
}
|
||||
|
||||
/// Find an entry for the given protocol address, if any.
|
||||
fn find(&self, protocol_addr: ProtocolAddress) -> Option<usize> {
|
||||
// The order of comparison is important: any valid ProtocolAddress should
|
||||
// sort before ProtocolAddress::Invalid.
|
||||
fn find(&self, protocol_addr: InternetAddress) -> Option<usize> {
|
||||
// The order of comparison is important: any valid InternetAddress should
|
||||
// sort before InternetAddress::Invalid.
|
||||
self.storage.binary_search_by_key(&protocol_addr, |&(key, _, _)| key).ok()
|
||||
}
|
||||
|
||||
|
@ -68,14 +67,14 @@ impl<'a> SliceCache<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Cache for SliceCache<'a> {
|
||||
fn fill(&mut self, protocol_addr: ProtocolAddress, hardware_addr: EthernetAddress) {
|
||||
fn fill(&mut self, protocol_addr: InternetAddress, hardware_addr: EthernetAddress) {
|
||||
if let None = self.find(protocol_addr) {
|
||||
self.storage[self.lru()] = (protocol_addr, hardware_addr, self.counter);
|
||||
self.sort()
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup(&mut self, protocol_addr: ProtocolAddress) -> Option<EthernetAddress> {
|
||||
fn lookup(&mut self, protocol_addr: InternetAddress) -> Option<EthernetAddress> {
|
||||
if let Some(index) = self.find(protocol_addr) {
|
||||
let (_protocol_addr, hardware_addr, ref mut counter) = self.storage[index];
|
||||
self.counter += 1;
|
||||
|
@ -96,10 +95,10 @@ mod test {
|
|||
const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
|
||||
const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
|
||||
|
||||
const PADDR_A: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 0]);
|
||||
const PADDR_B: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 1]);
|
||||
const PADDR_C: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 2]);
|
||||
const PADDR_D: ProtocolAddress = ProtocolAddress::ipv4([0, 0, 0, 3]);
|
||||
const PADDR_A: InternetAddress = InternetAddress::ipv4([0, 0, 0, 0]);
|
||||
const PADDR_B: InternetAddress = InternetAddress::ipv4([0, 0, 0, 1]);
|
||||
const PADDR_C: InternetAddress = InternetAddress::ipv4([0, 0, 0, 2]);
|
||||
const PADDR_D: InternetAddress = InternetAddress::ipv4([0, 0, 0, 3]);
|
||||
|
||||
#[test]
|
||||
fn test_slice_cache() {
|
||||
|
|
|
@ -2,10 +2,10 @@ use Error;
|
|||
use phy::Device;
|
||||
use wire::{EthernetAddress, EthernetProtocolType, EthernetFrame};
|
||||
use wire::{ArpPacket, ArpRepr, ArpOperation};
|
||||
use wire::InternetProtocolType;
|
||||
use wire::{InternetAddress, InternetProtocolType};
|
||||
use wire::{Ipv4Packet, Ipv4Repr};
|
||||
use wire::{Icmpv4Packet, Icmpv4Repr};
|
||||
use super::{ProtocolAddress, ArpCache};
|
||||
use super::{ArpCache};
|
||||
|
||||
/// An Ethernet network interface.
|
||||
#[derive(Debug)]
|
||||
|
@ -13,7 +13,7 @@ pub struct Interface<'a, DeviceT: Device, ArpCacheT: ArpCache> {
|
|||
device: DeviceT,
|
||||
arp_cache: ArpCacheT,
|
||||
hardware_addr: EthernetAddress,
|
||||
protocol_addrs: &'a [ProtocolAddress]
|
||||
protocol_addrs: &'a [InternetAddress]
|
||||
}
|
||||
|
||||
impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT> {
|
||||
|
@ -48,7 +48,7 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
|
|||
}
|
||||
|
||||
/// Get the protocol addresses of the interface.
|
||||
pub fn protocol_addrs(&self) -> &'a [ProtocolAddress] {
|
||||
pub fn protocol_addrs(&self) -> &'a [InternetAddress] {
|
||||
self.protocol_addrs
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
|
|||
///
|
||||
/// # Panics
|
||||
/// This function panics if any of the addresses is not unicast.
|
||||
pub fn set_protocol_addrs(&mut self, addrs: &'a [ProtocolAddress]) {
|
||||
pub fn set_protocol_addrs(&mut self, addrs: &'a [InternetAddress]) {
|
||||
for addr in addrs {
|
||||
if !addr.is_unicast() {
|
||||
panic!("protocol address {} is not unicast", addr)
|
||||
|
@ -67,7 +67,7 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
|
|||
}
|
||||
|
||||
/// Checks whether the interface has the given protocol address assigned.
|
||||
pub fn has_protocol_addr<T: Into<ProtocolAddress>>(&self, addr: T) -> bool {
|
||||
pub fn has_protocol_addr<T: Into<InternetAddress>>(&self, addr: T) -> bool {
|
||||
let addr = addr.into();
|
||||
self.protocol_addrs.iter().any(|&probe| probe == addr)
|
||||
}
|
||||
|
|
|
@ -2,58 +2,9 @@
|
|||
//!
|
||||
//! The `iface` module deals with the *network interfaces*. It filters incoming frames,
|
||||
//! provides lookup and caching of hardware addresses, and handles management packets.
|
||||
use core::fmt;
|
||||
use wire;
|
||||
|
||||
mod arp_cache;
|
||||
mod ethernet;
|
||||
|
||||
pub use self::arp_cache::Cache as ArpCache;
|
||||
pub use self::arp_cache::SliceCache as SliceArpCache;
|
||||
pub use self::ethernet::Interface as EthernetInterface;
|
||||
|
||||
/// An internetworking protocol address.
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub enum ProtocolAddress {
|
||||
/// An invalid address.
|
||||
/// May be used as a placeholder for storage where the address is not assigned yet.
|
||||
Invalid,
|
||||
/// An IPv4 address.
|
||||
Ipv4(wire::Ipv4Address)
|
||||
}
|
||||
|
||||
impl ProtocolAddress {
|
||||
/// Create a protocol address wrapping an IPv4 address with the given octets.
|
||||
pub const fn ipv4(octets: [u8; 4]) -> ProtocolAddress {
|
||||
ProtocolAddress::Ipv4(wire::Ipv4Address(octets))
|
||||
}
|
||||
|
||||
/// Query whether the address is a valid unicast address.
|
||||
pub fn is_unicast(&self) -> bool {
|
||||
match self {
|
||||
&ProtocolAddress::Invalid => false,
|
||||
&ProtocolAddress::Ipv4(addr) => addr.is_unicast()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProtocolAddress {
|
||||
fn default() -> ProtocolAddress {
|
||||
ProtocolAddress::Invalid
|
||||
}
|
||||
}
|
||||
|
||||
impl From<wire::Ipv4Address> for ProtocolAddress {
|
||||
fn from(addr: wire::Ipv4Address) -> Self {
|
||||
ProtocolAddress::Ipv4(addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ProtocolAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&ProtocolAddress::Invalid => write!(f, "(invalid)"),
|
||||
&ProtocolAddress::Ipv4(addr) => write!(f, "{}", addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -291,7 +291,8 @@ impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
|
|||
match Repr::parse(self) {
|
||||
Ok(repr) => write!(f, "{}", repr),
|
||||
_ => {
|
||||
try!(write!(f, "ARP htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
|
||||
try!(write!(f, "ARP (unrecognized)"));
|
||||
try!(write!(f, " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
|
||||
self.hardware_type(), self.protocol_type(),
|
||||
self.hardware_len(), self.protocol_len(),
|
||||
self.operation()));
|
||||
|
|
|
@ -215,7 +215,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
/// Validate the header checksum.
|
||||
pub fn verify_checksum(&self) -> bool {
|
||||
let data = self.buffer.as_ref();
|
||||
checksum(data) == !0
|
||||
checksum::data(data) == !0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,7 +275,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
self.set_checksum(0);
|
||||
let checksum = {
|
||||
let data = self.buffer.as_ref();
|
||||
!checksum(data)
|
||||
!checksum::data(data)
|
||||
};
|
||||
self.set_checksum(checksum)
|
||||
}
|
||||
|
@ -310,8 +310,7 @@ pub enum Repr<'a> {
|
|||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an Internet Control Message Protocol version 4 packet and return
|
||||
/// a high-level representation, or return `Err(())` if the packet is not recognized
|
||||
/// or is malformed.
|
||||
/// a high-level representation.
|
||||
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&'a T>) -> Result<Repr<'a>, Error> {
|
||||
match (packet.msg_type(), packet.msg_code()) {
|
||||
(Type::EchoRequest, 0) => {
|
||||
|
@ -343,7 +342,8 @@ impl<'a> Repr<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Emit a high-level representation into an Internet Protocol version 4 packet.
|
||||
/// Emit a high-level representation into an Internet Control Message Protocol version 4
|
||||
/// packet.
|
||||
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, packet: &mut Packet<&mut T>) {
|
||||
packet.set_msg_code(0);
|
||||
match self {
|
||||
|
@ -372,8 +372,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
|||
match Repr::parse(self) {
|
||||
Ok(repr) => write!(f, "{}", repr),
|
||||
_ => {
|
||||
write!(f, "ICMPv4 type={} code={}",
|
||||
self.msg_type(), self.msg_code())
|
||||
try!(write!(f, "ICMPv4 (unrecognized)"));
|
||||
try!(write!(f, " type={} code={} cksum={:#04x}",
|
||||
self.msg_type(), self.msg_code(), self.checksum()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -397,8 +399,8 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
|||
fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
match Packet::new(buffer) {
|
||||
Err(err) => write!(f, "{}({})\n", indent, err),
|
||||
Ok(frame) => write!(f, "{}{}\n", indent, frame)
|
||||
Err(err) => write!(f, "{}({})\n", indent, err),
|
||||
Ok(packet) => write!(f, "{}{}\n", indent, packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
102
src/wire/ip.rs
102
src/wire/ip.rs
|
@ -1,8 +1,9 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use super::Ipv4Address;
|
||||
|
||||
enum_with_unknown! {
|
||||
/// Internet protocol type.
|
||||
/// Internetworking protocol type.
|
||||
pub enum ProtocolType(u8) {
|
||||
Icmp = 0x01,
|
||||
Tcp = 0x06,
|
||||
|
@ -21,12 +22,93 @@ impl fmt::Display for ProtocolType {
|
|||
}
|
||||
}
|
||||
|
||||
/// Compute an RFC 1071 compliant checksum (without the final complement).
|
||||
pub fn checksum(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))
|
||||
/// 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 ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -210,7 +210,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
/// Validate the header checksum.
|
||||
pub fn verify_checksum(&self) -> bool {
|
||||
let data = self.buffer.as_ref();
|
||||
checksum(&data[..self.header_len() as usize]) == !0
|
||||
checksum::data(&data[..self.header_len() as usize]) == !0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -332,7 +332,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
self.set_checksum(0);
|
||||
let checksum = {
|
||||
let data = self.buffer.as_ref();
|
||||
!checksum(&data[..self.header_len() as usize])
|
||||
!checksum::data(&data[..self.header_len() as usize])
|
||||
};
|
||||
self.set_checksum(checksum)
|
||||
}
|
||||
|
@ -357,8 +357,7 @@ pub struct Repr {
|
|||
}
|
||||
|
||||
impl Repr {
|
||||
/// Parse an Internet Protocol version 4 packet and return a high-level representation,
|
||||
/// or return `Err(())` if the packet is not recognized or is malformed.
|
||||
/// Parse an Internet Protocol version 4 packet and return a high-level representation.
|
||||
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr, Error> {
|
||||
// Version 4 is expected.
|
||||
if packet.version() != 4 { return Err(Error::Malformed) }
|
||||
|
@ -408,7 +407,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
|||
match Repr::parse(self) {
|
||||
Ok(repr) => write!(f, "{}", repr),
|
||||
_ => {
|
||||
try!(write!(f, "IPv4 src={} dst={} proto={} ttl={}",
|
||||
try!(write!(f, "IPv4 (unrecognized)"));
|
||||
try!(write!(f, " src={} dst={} proto={} ttl={}",
|
||||
self.src_addr(), self.dst_addr(), self.protocol(), self.ttl()));
|
||||
if self.version() != 4 {
|
||||
try!(write!(f, " ver={}", self.version()))
|
||||
|
@ -463,6 +463,8 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
|||
match packet.protocol() {
|
||||
ProtocolType::Icmp =>
|
||||
super::Icmpv4Packet::<&[u8]>::pretty_print(&packet.payload(), f, indent),
|
||||
ProtocolType::Udp =>
|
||||
super::UdpPacket::<&[u8]>::pretty_print(&packet.payload(), f, indent),
|
||||
_ => Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
//! `-Cpanic=abort`. The accessor and parsing functions never panic. The setter and emission
|
||||
//! functions only panic if the underlying buffer is too small.
|
||||
//!
|
||||
//! The data structures in the `wire` module do not perform validation of received data;
|
||||
//! that is the job of an upper layer. This includes the `Repr` family, which only validate
|
||||
//! as much as is necessary to build the representation.
|
||||
//! The `Frame` and `Packet` families of data structures in the `wire` module do not perform
|
||||
//! validation of received data except as needed to access the contents without panicking;
|
||||
//! the `Repr` family does.
|
||||
|
||||
macro_rules! enum_with_unknown {
|
||||
(
|
||||
|
@ -94,12 +94,12 @@ pub use self::ethernet::Address as EthernetAddress;
|
|||
pub use self::ethernet::Frame as EthernetFrame;
|
||||
|
||||
pub use self::arp::HardwareType as ArpHardwareType;
|
||||
pub use self::arp::ProtocolType as ArpProtocolType;
|
||||
pub use self::arp::Operation as ArpOperation;
|
||||
pub use self::arp::Packet as ArpPacket;
|
||||
pub use self::arp::Repr as ArpRepr;
|
||||
|
||||
pub use self::ip::ProtocolType as InternetProtocolType;
|
||||
pub use self::ip::Address as InternetAddress;
|
||||
|
||||
pub use self::ipv4::Address as Ipv4Address;
|
||||
pub use self::ipv4::Packet as Ipv4Packet;
|
||||
|
@ -114,3 +114,4 @@ pub use self::icmpv4::Packet as Icmpv4Packet;
|
|||
pub use self::icmpv4::Repr as Icmpv4Repr;
|
||||
|
||||
pub use self::udp::Packet as UdpPacket;
|
||||
pub use self::udp::Repr as UdpRepr;
|
||||
|
|
144
src/wire/udp.rs
144
src/wire/udp.rs
|
@ -1,7 +1,8 @@
|
|||
use core::{cmp, fmt};
|
||||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use Error;
|
||||
use super::{InternetProtocolType, InternetAddress};
|
||||
use super::ip::checksum;
|
||||
|
||||
/// A read/write wrapper around an User Datagram Protocol packet buffer.
|
||||
|
@ -71,9 +72,17 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
}
|
||||
|
||||
/// Validate the packet checksum.
|
||||
pub fn verify_checksum(&self) -> bool {
|
||||
///
|
||||
/// # Panics
|
||||
/// This function panics unless `src_addr` and `dst_addr` belong to the same family,
|
||||
/// and that family is IPv4 or IPv6.
|
||||
pub fn verify_checksum(&self, src_addr: &InternetAddress, dst_addr: &InternetAddress) -> bool {
|
||||
let data = self.buffer.as_ref();
|
||||
checksum(&data[..self.len() as usize]) == !0
|
||||
checksum::combine(&[
|
||||
checksum::pseudo_header(src_addr, dst_addr, InternetProtocolType::Udp,
|
||||
self.len() as u32),
|
||||
checksum::data(&data[..self.len() as usize])
|
||||
]) == !0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,11 +125,19 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
}
|
||||
|
||||
/// Compute and fill in the header checksum.
|
||||
pub fn fill_checksum(&mut self) {
|
||||
///
|
||||
/// # Panics
|
||||
/// This function panics unless `src_addr` and `dst_addr` belong to the same family,
|
||||
/// and that family is IPv4 or IPv6.
|
||||
pub fn fill_checksum(&mut self, src_addr: &InternetAddress, dst_addr: &InternetAddress) {
|
||||
self.set_checksum(0);
|
||||
let checksum = {
|
||||
let data = self.buffer.as_ref();
|
||||
!checksum(&data[..self.len() as usize])
|
||||
!checksum::combine(&[
|
||||
checksum::pseudo_header(src_addr, dst_addr, InternetProtocolType::Udp,
|
||||
self.len() as u32),
|
||||
checksum::data(&data[..self.len() as usize])
|
||||
])
|
||||
};
|
||||
self.set_checksum(checksum)
|
||||
}
|
||||
|
@ -135,13 +152,99 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A high-level representation of an User Datagram Protocol packet.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct Repr<'a> {
|
||||
pub src_port: u16,
|
||||
pub dst_port: u16,
|
||||
pub payload: &'a [u8]
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an User Datagram Protocol packet and return a high-level representation.
|
||||
pub fn parse<T: ?Sized>(packet: &Packet<&'a T>,
|
||||
src_addr: &InternetAddress,
|
||||
dst_addr: &InternetAddress) -> Result<Repr<'a>, Error>
|
||||
where T: AsRef<[u8]> {
|
||||
// Destination port cannot be omitted (but source port can be).
|
||||
if packet.dst_port() == 0 { return Err(Error::Malformed) }
|
||||
// Valid checksum is expected...
|
||||
if !packet.verify_checksum(src_addr, dst_addr) {
|
||||
match (src_addr, dst_addr) {
|
||||
(&InternetAddress::Ipv4(_), &InternetAddress::Ipv4(_))
|
||||
if packet.checksum() != 0 => {
|
||||
// ... except on UDP-over-IPv4, where it can be omitted.
|
||||
return Err(Error::Checksum)
|
||||
},
|
||||
_ => {
|
||||
return Err(Error::Checksum)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Repr {
|
||||
src_port: packet.src_port(),
|
||||
dst_port: packet.dst_port(),
|
||||
payload: packet.payload()
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the length of a packet that will be emitted from this high-level representation.
|
||||
pub fn len(&self) -> usize {
|
||||
field::PAYLOAD.start + self.payload.len()
|
||||
}
|
||||
|
||||
/// Emit a high-level representation into an User Datagram Protocol packet.
|
||||
pub fn emit<T: ?Sized>(&self, packet: &mut Packet<&mut T>,
|
||||
src_addr: &InternetAddress,
|
||||
dst_addr: &InternetAddress)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> {
|
||||
packet.set_src_port(self.src_port);
|
||||
packet.set_dst_port(self.dst_port);
|
||||
packet.set_len((field::PAYLOAD.start + self.payload.len()) as u16);
|
||||
packet.payload_mut().copy_from_slice(self.payload);
|
||||
packet.fill_checksum(src_addr, dst_addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Cannot use Repr::parse because we don't have the IP addresses.
|
||||
write!(f, "UDP src={} dst={} len={}",
|
||||
self.src_port(), self.dst_port(), self.payload().len())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Repr<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "UDP src={} dst={} len={}",
|
||||
self.src_port, self.dst_port, self.payload.len())
|
||||
}
|
||||
}
|
||||
|
||||
use super::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
||||
fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
match Packet::new(buffer) {
|
||||
Err(err) => write!(f, "{}({})\n", indent, err),
|
||||
Ok(packet) => write!(f, "{}{}\n", indent, packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use wire::Ipv4Address;
|
||||
use super::*;
|
||||
|
||||
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
|
||||
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
|
||||
|
||||
static PACKET_BYTES: [u8; 12] =
|
||||
[0xbf, 0x00, 0x00, 0x35,
|
||||
0x00, 0x0c, 0x95, 0xbe,
|
||||
0x00, 0x0c, 0x12, 0x4d,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
|
||||
static PAYLOAD_BYTES: [u8; 4] =
|
||||
|
@ -153,9 +256,9 @@ mod test {
|
|||
assert_eq!(packet.src_port(), 48896);
|
||||
assert_eq!(packet.dst_port(), 53);
|
||||
assert_eq!(packet.len(), 12);
|
||||
assert_eq!(packet.checksum(), 0x95be);
|
||||
assert_eq!(packet.checksum(), 0x124d);
|
||||
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
|
||||
assert_eq!(packet.verify_checksum(), true);
|
||||
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -167,7 +270,30 @@ mod test {
|
|||
packet.set_len(12);
|
||||
packet.set_checksum(0xffff);
|
||||
packet.payload_mut().copy_from_slice(&PAYLOAD_BYTES[..]);
|
||||
packet.fill_checksum();
|
||||
packet.fill_checksum(&SRC_ADDR.into(), &DST_ADDR.into());
|
||||
assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
|
||||
}
|
||||
|
||||
fn packet_repr() -> Repr<'static> {
|
||||
Repr {
|
||||
src_port: 48896,
|
||||
dst_port: 53,
|
||||
payload: &PAYLOAD_BYTES
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let packet = Packet::new(&PACKET_BYTES[..]).unwrap();
|
||||
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into()).unwrap();
|
||||
assert_eq!(repr, packet_repr());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_emit() {
|
||||
let mut bytes = vec![0; 12];
|
||||
let mut packet = Packet::new(&mut bytes).unwrap();
|
||||
packet_repr().emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into());
|
||||
assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue