This commit is contained in:
Dario Nieuwenhuis 2021-06-27 09:31:59 +02:00
parent a803adb514
commit 98fe17890a
67 changed files with 9829 additions and 6322 deletions

View File

@ -1,22 +1,24 @@
#![feature(test)]
mod wire {
use test;
#[cfg(feature = "proto-ipv6")]
use smoltcp::wire::{Ipv6Address, Ipv6Repr, Ipv6Packet};
#[cfg(feature = "proto-ipv4")]
use smoltcp::wire::{Ipv4Address, Ipv4Repr, Ipv4Packet};
use smoltcp::phy::{ChecksumCapabilities};
use smoltcp::phy::ChecksumCapabilities;
use smoltcp::wire::{IpAddress, IpProtocol};
use smoltcp::wire::{TcpRepr, TcpPacket, TcpSeqNumber, TcpControl};
use smoltcp::wire::{UdpRepr, UdpPacket};
#[cfg(feature = "proto-ipv4")]
use smoltcp::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr};
#[cfg(feature = "proto-ipv6")]
use smoltcp::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr};
use smoltcp::wire::{TcpControl, TcpPacket, TcpRepr, TcpSeqNumber};
use smoltcp::wire::{UdpPacket, UdpRepr};
use test;
#[cfg(feature = "proto-ipv6")]
const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1]));
const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
]));
#[cfg(feature = "proto-ipv6")]
const DST_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2]));
const DST_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
]));
#[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))]
const SRC_ADDR: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
@ -26,42 +28,50 @@ mod wire {
#[bench]
#[cfg(any(feature = "proto-ipv6", feature = "proto-ipv4"))]
fn bench_emit_tcp(b: &mut test::Bencher) {
static PAYLOAD_BYTES: [u8; 400] =
[0x2a; 400];
static PAYLOAD_BYTES: [u8; 400] = [0x2a; 400];
let repr = TcpRepr {
src_port: 48896,
dst_port: 80,
seq_number: TcpSeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
control: TcpControl::Syn,
src_port: 48896,
dst_port: 80,
seq_number: TcpSeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
control: TcpControl::Syn,
max_seg_size: None,
window_scale: None,
payload: &PAYLOAD_BYTES
payload: &PAYLOAD_BYTES,
};
let mut bytes = vec![0xa5; repr.buffer_len()];
b.iter(|| {
let mut packet = TcpPacket::new(&mut bytes);
repr.emit(&mut packet, &SRC_ADDR, &DST_ADDR, &ChecksumCapabilities::default());
repr.emit(
&mut packet,
&SRC_ADDR,
&DST_ADDR,
&ChecksumCapabilities::default(),
);
});
}
#[bench]
#[cfg(any(feature = "proto-ipv6", feature = "proto-ipv4"))]
fn bench_emit_udp(b: &mut test::Bencher) {
static PAYLOAD_BYTES: [u8; 400] =
[0x2a; 400];
static PAYLOAD_BYTES: [u8; 400] = [0x2a; 400];
let repr = UdpRepr {
src_port: 48896,
dst_port: 80,
payload: &PAYLOAD_BYTES
payload: &PAYLOAD_BYTES,
};
let mut bytes = vec![0xa5; repr.buffer_len()];
b.iter(|| {
let mut packet = UdpPacket::new(&mut bytes);
repr.emit(&mut packet, &SRC_ADDR, &DST_ADDR, &ChecksumCapabilities::default());
repr.emit(
&mut packet,
&SRC_ADDR,
&DST_ADDR,
&ChecksumCapabilities::default(),
);
});
}
@ -69,11 +79,11 @@ mod wire {
#[cfg(feature = "proto-ipv4")]
fn bench_emit_ipv4(b: &mut test::Bencher) {
let repr = Ipv4Repr {
src_addr: Ipv4Address([192, 168, 1, 1]),
dst_addr: Ipv4Address([192, 168, 1, 2]),
protocol: IpProtocol::Tcp,
src_addr: Ipv4Address([192, 168, 1, 1]),
dst_addr: Ipv4Address([192, 168, 1, 2]),
protocol: IpProtocol::Tcp,
payload_len: 100,
hop_limit: 64
hop_limit: 64,
};
let mut bytes = vec![0xa5; repr.buffer_len()];
@ -87,13 +97,11 @@ mod wire {
#[cfg(feature = "proto-ipv6")]
fn bench_emit_ipv6(b: &mut test::Bencher) {
let repr = Ipv6Repr {
src_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1]),
dst_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2]),
src_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
dst_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]),
next_header: IpProtocol::Tcp,
payload_len: 100,
hop_limit: 64
hop_limit: 64,
};
let mut bytes = vec![0xa5; repr.buffer_len()];

View File

@ -2,28 +2,34 @@
mod utils;
use log::debug;
use std::cmp;
use std::collections::BTreeMap;
use std::sync::atomic::{Ordering, AtomicBool};
use std::thread;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::os::unix::io::AsRawFd;
use log::debug;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::SocketSet;
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
const AMOUNT: usize = 1_000_000_000;
enum Client { Reader, Writer }
enum Client {
Reader,
Writer,
}
fn client(kind: Client) {
let port = match kind { Client::Reader => 1234, Client::Writer => 1235 };
let port = match kind {
Client::Reader => 1234,
Client::Writer => 1235,
};
let mut stream = TcpStream::connect(("192.168.69.1", port)).unwrap();
let mut buffer = vec![0; 1_000_000];
@ -42,7 +48,7 @@ fn client(kind: Client) {
// print!("(P:{})", result);
processed += result
}
Err(err) => panic!("cannot process: {}", err)
Err(err) => panic!("cannot process: {}", err),
}
}
@ -69,11 +75,11 @@ fn main() {
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let mode = match matches.free[0].as_ref() {
"reader" => Client::Reader,
"writer" => Client::Writer,
_ => panic!("invalid mode")
_ => panic!("invalid mode"),
};
thread::spawn(move || client(mode));
@ -91,8 +97,7 @@ fn main() {
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs);
let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
@ -109,13 +114,12 @@ fn main() {
while !CLIENT_DONE.load(Ordering::SeqCst) {
let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
debug!("poll error: {}",e);
debug!("poll error: {}", e);
}
}
// tcp:1234: emit data
{
let mut socket = sockets.get::<TcpSocket>(tcp1_handle);
@ -125,10 +129,12 @@ fn main() {
if socket.can_send() {
if processed < AMOUNT {
let length = socket.send(|buffer| {
let length = cmp::min(buffer.len(), AMOUNT - processed);
(length, length)
}).unwrap();
let length = socket
.send(|buffer| {
let length = cmp::min(buffer.len(), AMOUNT - processed);
(length, length)
})
.unwrap();
processed += length;
}
}
@ -143,10 +149,12 @@ fn main() {
if socket.can_recv() {
if processed < AMOUNT {
let length = socket.recv(|buffer| {
let length = cmp::min(buffer.len(), AMOUNT - processed);
(length, length)
}).unwrap();
let length = socket
.recv(|buffer| {
let length = cmp::min(buffer.len(), AMOUNT - processed);
(length, length)
})
.unwrap();
processed += length;
}
}
@ -155,7 +163,7 @@ fn main() {
match iface.poll_at(&sockets, timestamp) {
Some(poll_at) if timestamp < poll_at => {
phy_wait(fd, Some(poll_at - timestamp)).expect("wait error");
},
}
Some(_) => (),
None => {
phy_wait(fd, default_timeout).expect("wait error");

View File

@ -1,15 +1,15 @@
mod utils;
use std::str::{self, FromStr};
use log::debug;
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use log::debug;
use std::str::{self, FromStr};
use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
fn main() {
utils::setup_logging("");
@ -24,7 +24,7 @@ fn main() {
let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
let port = u16::from_str(&matches.free[1]).expect("invalid port format");
@ -43,8 +43,8 @@ fn main() {
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs)
.routes(routes);
.ip_addrs(ip_addrs)
.routes(routes);
if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
@ -64,7 +64,7 @@ fn main() {
loop {
let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
debug!("poll error: {}", e);
}
@ -76,25 +76,31 @@ fn main() {
debug!("connected");
} else if !socket.is_active() && tcp_active {
debug!("disconnected");
break
break;
}
tcp_active = socket.is_active();
if socket.may_recv() {
let data = socket.recv(|data| {
let mut data = data.to_owned();
if !data.is_empty() {
debug!("recv data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
data.reverse();
data.extend(b"\n");
}
(data.len(), data)
}).unwrap();
let data = socket
.recv(|data| {
let mut data = data.to_owned();
if !data.is_empty() {
debug!(
"recv data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
data.reverse();
data.extend(b"\n");
}
(data.len(), data)
})
.unwrap();
if socket.can_send() && !data.is_empty() {
debug!("send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
debug!(
"send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
socket.send_slice(&data[..]).unwrap();
}
} else if socket.may_send() {

View File

@ -1,15 +1,18 @@
#![allow(clippy::option_map_unit_fn)]
mod utils;
use log::*;
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use log::*;
use smoltcp::{phy::{Device, Medium, wait as phy_wait}, time::Duration};
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Interface, Routes};
use smoltcp::socket::{SocketSet, Dhcpv4Socket, Dhcpv4Event};
use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes};
use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket, SocketSet};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr};
use smoltcp::{
phy::{wait as phy_wait, Device, Medium},
time::Duration,
};
fn main() {
#[cfg(feature = "log")]
@ -22,7 +25,7 @@ fn main() {
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
@ -32,8 +35,8 @@ fn main() {
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs)
.routes(routes);
.ip_addrs(ip_addrs)
.routes(routes);
if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
@ -92,11 +95,11 @@ fn main() {
}
fn set_ipv4_addr<DeviceT>(iface: &mut Interface<'_, DeviceT>, cidr: Ipv4Cidr)
where DeviceT: for<'d> Device<'d>
where
DeviceT: for<'d> Device<'d>,
{
iface.update_ip_addrs(|addrs| {
let dest = addrs.iter_mut().next().unwrap();
*dest = IpCidr::Ipv4(cidr);
});
}

View File

@ -1,16 +1,16 @@
mod utils;
use std::str::{self, FromStr};
use log::debug;
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use std::str::{self, FromStr};
use url::Url;
use log::debug;
use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address};
fn main() {
utils::setup_logging("");
@ -24,11 +24,10 @@ fn main() {
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
let url = Url::parse(&matches.free[1]).expect("invalid url format");
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 1024]);
@ -36,9 +35,11 @@ fn main() {
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)];
let ip_addrs = [
IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64),
];
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100);
let mut routes_storage = [None; 2];
@ -48,8 +49,8 @@ fn main() {
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs)
.routes(routes);
.ip_addrs(ip_addrs)
.routes(routes);
if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
@ -60,15 +61,19 @@ fn main() {
let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket);
enum State { Connect, Request, Response }
enum State {
Connect,
Request,
Response,
}
let mut state = State::Connect;
loop {
let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
debug!("poll error: {}",e);
debug!("poll error: {}", e);
}
}
@ -79,7 +84,9 @@ fn main() {
State::Connect if !socket.is_active() => {
debug!("connecting");
let local_port = 49152 + rand::random::<u16>() % 16384;
socket.connect((address, url.port().unwrap_or(80)), local_port).unwrap();
socket
.connect((address, url.port().unwrap_or(80)), local_port)
.unwrap();
State::Request
}
State::Request if socket.may_send() => {
@ -88,22 +95,26 @@ fn main() {
socket.send_slice(http_get.as_ref()).expect("cannot send");
let http_host = "Host: ".to_owned() + url.host_str().unwrap() + "\r\n";
socket.send_slice(http_host.as_ref()).expect("cannot send");
socket.send_slice(b"Connection: close\r\n").expect("cannot send");
socket
.send_slice(b"Connection: close\r\n")
.expect("cannot send");
socket.send_slice(b"\r\n").expect("cannot send");
State::Response
}
State::Response if socket.can_recv() => {
socket.recv(|data| {
println!("{}", str::from_utf8(data).unwrap_or("(invalid utf8)"));
(data.len(), ())
}).unwrap();
socket
.recv(|data| {
println!("{}", str::from_utf8(data).unwrap_or("(invalid utf8)"));
(data.len(), ())
})
.unwrap();
State::Response
}
State::Response if !socket.may_recv() => {
debug!("received complete response");
break
break;
}
_ => state
_ => state,
}
}

View File

@ -7,18 +7,18 @@
mod utils;
use core::str;
use log::{info, debug, error};
use log::{debug, error, info};
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{Loopback, Medium};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
#[cfg(not(feature = "std"))]
mod mock {
use smoltcp::time::{Duration, Instant};
use core::cell::Cell;
use smoltcp::time::{Duration, Instant};
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -41,9 +41,9 @@ mod mock {
#[cfg(feature = "std")]
mod mock {
use smoltcp::time::{Duration, Instant};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicUsize};
use smoltcp::time::{Duration, Instant};
// should be AtomicU64 but that's unstable
#[derive(Debug, Clone)]
@ -56,7 +56,8 @@ mod mock {
}
pub fn advance(&self, duration: Duration) {
self.0.fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
self.0
.fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
}
pub fn elapsed(&self) -> Instant {
@ -78,7 +79,7 @@ fn main() {
utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free);
utils::parse_middleware_options(&mut matches, device, /*loopback=*/true)
utils::parse_middleware_options(&mut matches, device, /*loopback=*/ true)
};
let mut neighbor_cache_entries = [None; 8];
@ -86,10 +87,10 @@ fn main() {
let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(EthernetAddress::default())
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.finalize();
.ethernet_addr(EthernetAddress::default())
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.finalize();
let server_socket = {
// It is not strictly necessary to use a `static mut` and unsafe code here, but
@ -116,12 +117,12 @@ fn main() {
let server_handle = socket_set.add(server_socket);
let client_handle = socket_set.add(client_socket);
let mut did_listen = false;
let mut did_listen = false;
let mut did_connect = false;
let mut done = false;
while !done && clock.elapsed() < Instant::from_millis(10_000) {
match iface.poll(&mut socket_set, clock.elapsed()) {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
debug!("poll error: {}", e);
}
@ -138,9 +139,10 @@ fn main() {
}
if socket.can_recv() {
debug!("got {:?}", socket.recv(|buffer| {
(buffer.len(), str::from_utf8(buffer).unwrap())
}));
debug!(
"got {:?}",
socket.recv(|buffer| { (buffer.len(), str::from_utf8(buffer).unwrap()) })
);
socket.close();
done = true;
}
@ -151,8 +153,12 @@ fn main() {
if !socket.is_open() {
if !did_connect {
debug!("connecting");
socket.connect((IpAddress::v4(127, 0, 0, 1), 1234),
(IpAddress::Unspecified, 65000)).unwrap();
socket
.connect(
(IpAddress::v4(127, 0, 0, 1), 1234),
(IpAddress::Unspecified, 65000),
)
.unwrap();
did_connect = true;
}
}
@ -169,8 +175,8 @@ fn main() {
Some(delay) => {
debug!("sleeping for {} ms", delay);
clock.advance(delay)
},
None => clock.advance(Duration::from_millis(1))
}
None => clock.advance(Duration::from_millis(1)),
}
}

View File

@ -1,17 +1,20 @@
mod utils;
use log::debug;
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use log::debug;
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr, Ipv4Address,
Ipv4Packet, IgmpPacket, IgmpRepr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
use smoltcp::socket::{SocketSet,
RawSocket, RawSocketBuffer, RawPacketMetadata,
UdpSocket, UdpSocketBuffer, UdpPacketMetadata};
use smoltcp::socket::{
RawPacketMetadata, RawSocket, RawSocketBuffer, SocketSet, UdpPacketMetadata, UdpSocket,
UdpSocketBuffer,
};
use smoltcp::time::Instant;
use smoltcp::wire::{
EthernetAddress, IgmpPacket, IgmpRepr, IpAddress, IpCidr, IpProtocol, IpVersion, Ipv4Address,
Ipv4Packet,
};
const MDNS_PORT: u16 = 5353;
const MDNS_GROUP: [u8; 4] = [224, 0, 0, 251];
@ -26,10 +29,7 @@ fn main() {
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches,
device,
/*loopback=*/
false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let local_addr = Ipv4Address::new(192, 168, 69, 2);
@ -38,15 +38,17 @@ fn main() {
let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
let mut ipv4_multicast_storage = [None; 1];
let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs([ip_addr])
.ipv4_multicast_groups(&mut ipv4_multicast_storage[..])
.finalize();
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs([ip_addr])
.ipv4_multicast_groups(&mut ipv4_multicast_storage[..])
.finalize();
let now = Instant::now();
// Join a multicast group to receive mDNS traffic
iface.join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now).unwrap();
iface
.join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now)
.unwrap();
let mut sockets = SocketSet::new(vec![]);
@ -55,8 +57,10 @@ fn main() {
// Will not send IGMP
let raw_tx_buffer = RawSocketBuffer::new(vec![], vec![]);
let raw_socket = RawSocket::new(
IpVersion::Ipv4, IpProtocol::Igmp,
raw_rx_buffer, raw_tx_buffer
IpVersion::Ipv4,
IpProtocol::Igmp,
raw_rx_buffer,
raw_tx_buffer,
);
let raw_handle = sockets.add(raw_socket);
@ -70,9 +74,9 @@ fn main() {
loop {
let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
debug!("poll error: {}",e);
debug!("poll error: {}", e);
}
}
@ -82,7 +86,8 @@ fn main() {
if socket.can_recv() {
// For display purposes only - normally we wouldn't process incoming IGMP packets
// in the application layer
socket.recv()
socket
.recv()
.and_then(Ipv4Packet::new_checked)
.and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload()))
.and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet))
@ -97,8 +102,11 @@ fn main() {
}
if socket.can_recv() {
socket.recv()
.map(|(data, sender)| println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender))
socket
.recv()
.map(|(data, sender)| {
println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender)
})
.unwrap_or_else(|e| println!("Recv UDP error: {:?}", e));
}
}

View File

@ -1,21 +1,25 @@
mod utils;
use std::str::FromStr;
use std::collections::BTreeMap;
use std::cmp;
use std::os::unix::io::AsRawFd;
use std::collections::HashMap;
use log::debug;
use byteorder::{ByteOrder, NetworkEndian};
use log::debug;
use std::cmp;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::os::unix::io::AsRawFd;
use std::str::FromStr;
use smoltcp::{phy::Medium, time::{Duration, Instant}};
use smoltcp::phy::Device;
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr,
Ipv6Address, Icmpv6Repr, Icmpv6Packet,
Ipv4Address, Icmpv4Repr, Icmpv4Packet};
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint};
use smoltcp::phy::Device;
use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer, SocketSet};
use smoltcp::wire::{
EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr,
Ipv4Address, Ipv6Address,
};
use smoltcp::{
phy::Medium,
time::{Duration, Instant},
};
macro_rules! send_icmp_ping {
( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
@ -26,13 +30,11 @@ macro_rules! send_icmp_ping {
data: &$echo_payload,
};
let icmp_payload = $socket
.send(icmp_repr.buffer_len(), $remote_addr)
.unwrap();
let icmp_payload = $socket.send(icmp_repr.buffer_len(), $remote_addr).unwrap();
let icmp_packet = $packet_type::new_unchecked(icmp_payload);
(icmp_repr, icmp_packet)
}}
}};
}
macro_rules! get_icmp_pong {
@ -41,14 +43,18 @@ macro_rules! get_icmp_pong {
if let $repr_type::EchoReply { seq_no, data, .. } = $repr {
if let Some(_) = $waiting_queue.get(&seq_no) {
let packet_timestamp_ms = NetworkEndian::read_i64(data);
println!("{} bytes from {}: icmp_seq={}, time={}ms",
data.len(), $remote_addr, seq_no,
$timestamp.total_millis() - packet_timestamp_ms);
println!(
"{} bytes from {}: icmp_seq={}, time={}ms",
data.len(),
$remote_addr,
seq_no,
$timestamp.total_millis() - packet_timestamp_ms
);
$waiting_queue.remove(&seq_no);
$received += 1;
}
}
}}
}};
}
fn main() {
@ -57,26 +63,45 @@ fn main() {
let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free);
opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT");
opts.optopt("i", "interval",
"Interval between successive packets sent (seconds) (default: 1)", "INTERVAL");
opts.optopt("", "timeout",
"Maximum wait duration for an echo response packet (seconds) (default: 5)",
"TIMEOUT");
opts.optopt(
"c",
"count",
"Amount of echo request packets to send (default: 4)",
"COUNT",
);
opts.optopt(
"i",
"interval",
"Interval between successive packets sent (seconds) (default: 1)",
"INTERVAL",
);
opts.optopt(
"",
"timeout",
"Maximum wait duration for an echo response packet (seconds) (default: 5)",
"TIMEOUT",
);
free.push("ADDRESS");
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let device_caps = device.capabilities();
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
let count = matches.opt_str("count").map(|s| usize::from_str(&s).unwrap()).unwrap_or(4);
let interval = matches.opt_str("interval")
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
let count = matches
.opt_str("count")
.map(|s| usize::from_str(&s).unwrap())
.unwrap_or(4);
let interval = matches
.opt_str("interval")
.map(|s| Duration::from_secs(u64::from_str(&s).unwrap()))
.unwrap_or_else(|| Duration::from_secs(1));
let timeout = Duration::from_secs(
matches.opt_str("timeout").map(|s| u64::from_str(&s).unwrap()).unwrap_or(5)
let timeout = Duration::from_secs(
matches
.opt_str("timeout")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(5),
);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
@ -89,9 +114,11 @@ fn main() {
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(src_ipv6, 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)];
let ip_addrs = [
IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(src_ipv6, 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64),
];
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100);
let mut routes_storage = [None; 2];
@ -101,8 +128,8 @@ fn main() {
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs)
.routes(routes);
.ip_addrs(ip_addrs)
.routes(routes);
if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
@ -123,7 +150,7 @@ fn main() {
loop {
let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
debug!("poll error: {}", e);
}
@ -137,25 +164,40 @@ fn main() {
send_at = timestamp;
}
if socket.can_send() && seq_no < count as u16 &&
send_at <= timestamp {
if socket.can_send() && seq_no < count as u16 && send_at <= timestamp {
NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis());
match remote_addr {
IpAddress::Ipv4(_) => {
let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
Icmpv4Repr, Icmpv4Packet, ident, seq_no,
echo_payload, socket, remote_addr);
Icmpv4Repr,
Icmpv4Packet,
ident,
seq_no,
echo_payload,
socket,
remote_addr
);
icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);
},
}
IpAddress::Ipv6(_) => {
let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
Icmpv6Repr, Icmpv6Packet, ident, seq_no,
echo_payload, socket, remote_addr);
icmp_repr.emit(&src_ipv6, &remote_addr,
&mut icmp_packet, &device_caps.checksum);
},
_ => unimplemented!()
Icmpv6Repr,
Icmpv6Packet,
ident,
seq_no,
echo_payload,
socket,
remote_addr
);
icmp_repr.emit(
&src_ipv6,
&remote_addr,
&mut icmp_packet,
&device_caps.checksum,
);
}
_ => unimplemented!(),
}
waiting_queue.insert(seq_no, timestamp);
@ -171,17 +213,36 @@ fn main() {
let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap();
let icmp_repr =
Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();
get_icmp_pong!(Icmpv4Repr, icmp_repr, payload,
waiting_queue, remote_addr, timestamp, received);
get_icmp_pong!(
Icmpv4Repr,
icmp_repr,
payload,
waiting_queue,
remote_addr,
timestamp,
received
);
}
IpAddress::Ipv6(_) => {
let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap();
let icmp_repr = Icmpv6Repr::parse(&remote_addr, &src_ipv6,
&icmp_packet, &device_caps.checksum).unwrap();
get_icmp_pong!(Icmpv6Repr, icmp_repr, payload,
waiting_queue, remote_addr, timestamp, received);
},
_ => unimplemented!()
let icmp_repr = Icmpv6Repr::parse(
&remote_addr,
&src_ipv6,
&icmp_packet,
&device_caps.checksum,
)
.unwrap();
get_icmp_pong!(
Icmpv6Repr,
icmp_repr,
payload,
waiting_queue,
remote_addr,
timestamp,
received
);
}
_ => unimplemented!(),
}
}
@ -195,7 +256,7 @@ fn main() {
});
if seq_no == count as u16 && waiting_queue.is_empty() {
break
break;
}
}
@ -204,7 +265,7 @@ fn main() {
Some(poll_at) if timestamp < poll_at => {
let resume_at = cmp::min(poll_at, send_at);
phy_wait(fd, Some(resume_at - timestamp)).expect("wait error");
},
}
Some(_) => (),
None => {
phy_wait(fd, Some(send_at - timestamp)).expect("wait error");
@ -213,6 +274,10 @@ fn main() {
}
println!("--- {} ping statistics ---", remote_addr);
println!("{} packets transmitted, {} received, {:.0}% packet loss",
seq_no, received, 100.0 * (seq_no - received) as f64 / seq_no as f64);
println!(
"{} packets transmitted, {} received, {:.0}% packet loss",
seq_no,
received,
100.0 * (seq_no - received) as f64 / seq_no as f64
);
}

View File

@ -1,18 +1,18 @@
mod utils;
use std::str;
use log::debug;
use std::collections::BTreeMap;
use std::fmt::Write;
use std::os::unix::io::AsRawFd;
use log::debug;
use std::str;
use smoltcp::phy::{Device, Medium, wait as phy_wait};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::SocketSet;
use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
fn main() {
utils::setup_logging("");
@ -24,7 +24,7 @@ fn main() {
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
@ -52,12 +52,11 @@ fn main() {
let ip_addrs = [
IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64),
];
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs);
let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
if medium == Medium::Ethernet {
builder = builder
.ethernet_addr(ethernet_addr)
@ -66,7 +65,7 @@ fn main() {
let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]);
let udp_handle = sockets.add(udp_socket);
let udp_handle = sockets.add(udp_socket);
let tcp1_handle = sockets.add(tcp1_socket);
let tcp2_handle = sockets.add(tcp2_socket);
let tcp3_handle = sockets.add(tcp3_socket);
@ -76,7 +75,7 @@ fn main() {
loop {
let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Ok(_) => {}
Err(e) => {
debug!("poll error: {}", e);
}
@ -91,16 +90,21 @@ fn main() {
let client = match socket.recv() {
Ok((data, endpoint)) => {
debug!("udp:6969 recv data: {:?} from {}",
str::from_utf8(data).unwrap(), endpoint);
debug!(
"udp:6969 recv data: {:?} from {}",
str::from_utf8(data).unwrap(),
endpoint
);
Some(endpoint)
}
Err(_) => None
Err(_) => None,
};
if let Some(endpoint) = client {
let data = b"hello\n";
debug!("udp:6969 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap());
debug!(
"udp:6969 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap()
);
socket.send_slice(data, endpoint).unwrap();
}
}
@ -135,21 +139,27 @@ fn main() {
tcp_6970_active = socket.is_active();
if socket.may_recv() {
let data = socket.recv(|buffer| {
let recvd_len = buffer.len();
let mut data = buffer.to_owned();
if !data.is_empty() {
debug!("tcp:6970 recv data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
data.reverse();
data.extend(b"\n");
}
(recvd_len, data)
}).unwrap();
let data = socket
.recv(|buffer| {
let recvd_len = buffer.len();
let mut data = buffer.to_owned();
if !data.is_empty() {
debug!(
"tcp:6970 recv data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
data.reverse();
data.extend(b"\n");
}
(recvd_len, data)
})
.unwrap();
if socket.can_send() && !data.is_empty() {
debug!("tcp:6970 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
debug!(
"tcp:6970 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
socket.send_slice(&data[..]).unwrap();
}
} else if socket.may_send() {
@ -168,12 +178,14 @@ fn main() {
}
if socket.may_recv() {
socket.recv(|buffer| {
if !buffer.is_empty() {
debug!("tcp:6971 recv {:?} octets", buffer.len());
}
(buffer.len(), ())
}).unwrap();
socket
.recv(|buffer| {
if !buffer.is_empty() {
debug!("tcp:6971 recv {:?} octets", buffer.len());
}
(buffer.len(), ())
})
.unwrap();
} else if socket.may_send() {
socket.close();
}
@ -187,15 +199,17 @@ fn main() {
}
if socket.may_send() {
socket.send(|data| {
if !data.is_empty() {
debug!("tcp:6972 send {:?} octets", data.len());
for (i, b) in data.iter_mut().enumerate() {
*b = (i % 256) as u8;
socket
.send(|data| {
if !data.is_empty() {
debug!("tcp:6972 send {:?} octets", data.len());
for (i, b) in data.iter_mut().enumerate() {
*b = (i % 256) as u8;
}
}
}
(data.len(), ())
}).unwrap();
(data.len(), ())
})
.unwrap();
}
}

View File

@ -1,9 +1,9 @@
use smoltcp::phy::wait as phy_wait;
use smoltcp::phy::{Device, RawSocket, RxToken};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetFrame, PrettyPrinter};
use std::env;
use std::os::unix::io::AsRawFd;
use smoltcp::phy::wait as phy_wait;
use smoltcp::phy::{Device, RxToken, RawSocket};
use smoltcp::wire::{PrettyPrinter, EthernetFrame};
use smoltcp::time::Instant;
fn main() {
let ifname = env::args().nth(1).unwrap();
@ -11,9 +11,14 @@ fn main() {
loop {
phy_wait(socket.as_raw_fd(), None).unwrap();
let (rx_token, _) = socket.receive().unwrap();
rx_token.consume(Instant::now(), |buffer| {
println!("{}", PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer));
Ok(())
}).unwrap();
rx_token
.consume(Instant::now(), |buffer| {
println!(
"{}",
PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer)
);
Ok(())
})
.unwrap();
}
}

View File

@ -1,43 +1,59 @@
#![allow(dead_code)]
use std::cell::RefCell;
use std::str::{self, FromStr};
use std::rc::Rc;
use std::io::{self, Write};
use std::fs::File;
use std::time::{SystemTime, UNIX_EPOCH};
use std::env;
use std::process;
#[cfg(feature = "log")]
use log::{Level, LevelFilter, trace};
#[cfg(feature = "log")]
use env_logger::Builder;
use getopts::{Options, Matches};
use getopts::{Matches, Options};
#[cfg(feature = "log")]
use log::{trace, Level, LevelFilter};
use std::cell::RefCell;
use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::process;
use std::rc::Rc;
use std::str::{self, FromStr};
use std::time::{SystemTime, UNIX_EPOCH};
use smoltcp::phy::{Device, Tracer, FaultInjector, Medium};
use smoltcp::phy::RawSocket;
#[cfg(feature = "phy-tuntap_interface")]
use smoltcp::phy::TunTapInterface;
use smoltcp::phy::{PcapWriter, PcapSink, PcapMode};
use smoltcp::phy::RawSocket;
use smoltcp::phy::{Device, FaultInjector, Medium, Tracer};
use smoltcp::phy::{PcapMode, PcapSink, PcapWriter};
use smoltcp::time::{Duration, Instant};
#[cfg(feature = "log")]
pub fn setup_logging_with_clock<F>(filter: &str, since_startup: F)
where F: Fn() -> Instant + Send + Sync + 'static {
where
F: Fn() -> Instant + Send + Sync + 'static,
{
Builder::new()
.format(move |buf, record| {
let elapsed = since_startup();
let timestamp = format!("[{}]", elapsed);
if record.target().starts_with("smoltcp::") {
writeln!(buf, "\x1b[0m{} ({}): {}\x1b[0m", timestamp,
record.target().replace("smoltcp::", ""), record.args())
writeln!(
buf,
"\x1b[0m{} ({}): {}\x1b[0m",
timestamp,
record.target().replace("smoltcp::", ""),
record.args()
)
} else if record.level() == Level::Trace {
let message = format!("{}", record.args());
writeln!(buf, "\x1b[37m{} {}\x1b[0m", timestamp,
message.replace("\n", "\n "))
writeln!(
buf,
"\x1b[37m{} {}\x1b[0m",
timestamp,
message.replace("\n", "\n ")
)
} else {
writeln!(buf, "\x1b[32m{} ({}): {}\x1b[0m", timestamp,
record.target(), record.args())
writeln!(
buf,
"\x1b[32m{} ({}): {}\x1b[0m",
timestamp,
record.target(),
record.args()
)
}
})
.filter(None, LevelFilter::Trace)
@ -48,9 +64,7 @@ pub fn setup_logging_with_clock<F>(filter: &str, since_startup: F)
#[cfg(feature = "log")]
pub fn setup_logging(filter: &str) {
setup_logging_with_clock(filter, move || {
Instant::now()
})
setup_logging_with_clock(filter, move || Instant::now())
}
pub fn create_options() -> (Options, Vec<&'static str>) {
@ -67,10 +81,17 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches {
}
Ok(matches) => {
if matches.opt_present("h") || matches.free.len() != free.len() {
let brief = format!("Usage: {} [OPTION]... {}",
env::args().next().unwrap(), free.join(" "));
let brief = format!(
"Usage: {} [OPTION]... {}",
env::args().next().unwrap(),
free.join(" ")
);
print!("{}", options.usage(&brief));
process::exit(if matches.free.len() != free.len() { 1 } else { 0 })
process::exit(if matches.free.len() != free.len() {
1
} else {
0
})
}
matches
}
@ -86,7 +107,7 @@ pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) {
pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface {
let tun = matches.opt_str("tun");
let tap = matches.opt_str("tap");
match(tun,tap) {
match (tun, tap) {
(Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(),
(None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(),
_ => panic!("You must specify exactly one of --tun or --tap"),
@ -100,32 +121,78 @@ pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket {
pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) {
opts.optopt("", "pcap", "Write a packet capture file", "FILE");
opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE");
opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE");
opts.optopt("", "size-limit", "Drop packets larger than given size (octets)", "SIZE");
opts.optopt("", "tx-rate-limit", "Drop packets after transmit rate exceeds given limit \
(packets per interval)", "RATE");
opts.optopt("", "rx-rate-limit", "Drop packets after transmit rate exceeds given limit \
(packets per interval)", "RATE");
opts.optopt("", "shaping-interval", "Sets the interval for rate limiting (ms)", "RATE");
opts.optopt(
"",
"drop-chance",
"Chance of dropping a packet (%)",
"CHANCE",
);
opts.optopt(
"",
"corrupt-chance",
"Chance of corrupting a packet (%)",
"CHANCE",
);
opts.optopt(
"",
"size-limit",
"Drop packets larger than given size (octets)",
"SIZE",
);
opts.optopt(
"",
"tx-rate-limit",
"Drop packets after transmit rate exceeds given limit \
(packets per interval)",
"RATE",
);
opts.optopt(
"",
"rx-rate-limit",
"Drop packets after transmit rate exceeds given limit \
(packets per interval)",
"RATE",
);
opts.optopt(
"",
"shaping-interval",
"Sets the interval for rate limiting (ms)",
"RATE",
);
}
pub fn parse_middleware_options<D>(matches: &mut Matches, device: D, loopback: bool)
-> FaultInjector<Tracer<PcapWriter<D, Rc<dyn PcapSink>>>>
where D: for<'a> Device<'a>
pub fn parse_middleware_options<D>(
matches: &mut Matches,
device: D,
loopback: bool,
) -> FaultInjector<Tracer<PcapWriter<D, Rc<dyn PcapSink>>>>
where
D: for<'a> Device<'a>,
{
let drop_chance = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let corrupt_chance = matches.opt_str("corrupt-chance").map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let size_limit = matches.opt_str("size-limit").map(|s| usize::from_str(&s).unwrap())
.unwrap_or(0);
let tx_rate_limit = matches.opt_str("tx-rate-limit").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let rx_rate_limit = matches.opt_str("rx-rate-limit").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let shaping_interval = matches.opt_str("shaping-interval").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let drop_chance = matches
.opt_str("drop-chance")
.map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let corrupt_chance = matches
.opt_str("corrupt-chance")
.map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let size_limit = matches
.opt_str("size-limit")
.map(|s| usize::from_str(&s).unwrap())
.unwrap_or(0);
let tx_rate_limit = matches
.opt_str("tx-rate-limit")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let rx_rate_limit = matches
.opt_str("rx-rate-limit")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let shaping_interval = matches
.opt_str("shaping-interval")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let pcap_writer: Box<dyn io::Write>;
if let Some(pcap_filename) = matches.opt_str("pcap") {
@ -134,12 +201,19 @@ pub fn parse_middleware_options<D>(matches: &mut Matches, device: D, loopback: b
pcap_writer = Box::new(io::sink())
}
let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();
let seed = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.subsec_nanos();
let device = PcapWriter::new(
device,
Rc::new(RefCell::new(pcap_writer)) as Rc<dyn PcapSink>,
if loopback { PcapMode::TxOnly } else { PcapMode::Both },
if loopback {
PcapMode::TxOnly
} else {
PcapMode::Both
},
);
let device = Tracer::new(device, |_timestamp, _printer| {

File diff suppressed because it is too large Load Diff

View File

@ -4,18 +4,18 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram
provides lookup and caching of hardware addresses, and handles management packets.
*/
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
mod interface;
#[cfg(feature = "medium-ethernet")]
mod neighbor;
mod route;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
mod interface;
#[cfg(feature = "medium-ethernet")]
pub use self::neighbor::Neighbor as Neighbor;
#[cfg(feature = "medium-ethernet")]
pub(crate) use self::neighbor::Answer as NeighborAnswer;
#[cfg(feature = "medium-ethernet")]
pub use self::neighbor::Cache as NeighborCache;
#[cfg(feature = "medium-ethernet")]
pub use self::neighbor::Neighbor;
pub use self::route::{Route, Routes};
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]

View File

@ -3,8 +3,8 @@
use managed::ManagedMap;
use crate::wire::{EthernetAddress, IpAddress};
use crate::time::{Duration, Instant};
use crate::wire::{EthernetAddress, IpAddress};
/// A cached neighbor.
///
@ -14,7 +14,7 @@ use crate::time::{Duration, Instant};
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Neighbor {
hardware_addr: EthernetAddress,
expires_at: Instant,
expires_at: Instant,
}
/// An answer to a neighbor cache lookup.
@ -27,7 +27,7 @@ pub(crate) enum Answer {
NotFound,
/// The neighbor address is not in the cache, or has expired,
/// and a lookup has been made recently.
RateLimited
RateLimited,
}
impl Answer {
@ -61,10 +61,9 @@ impl Answer {
/// ```
#[derive(Debug)]
pub struct Cache<'a> {
storage: ManagedMap<'a, IpAddress, Neighbor>,
storage: ManagedMap<'a, IpAddress, Neighbor>,
silent_until: Instant,
gc_threshold: usize
gc_threshold: usize,
}
impl<'a> Cache<'a> {
@ -82,21 +81,32 @@ impl<'a> Cache<'a> {
/// # Panics
/// This function panics if `storage.len() == 0`.
pub fn new<T>(storage: T) -> Cache<'a>
where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
where
T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
{
Cache::new_with_limit(storage, Cache::GC_THRESHOLD)
}
pub fn new_with_limit<T>(storage: T, gc_threshold: usize) -> Cache<'a>
where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
where
T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
{
let mut storage = storage.into();
storage.clear();
Cache { storage, gc_threshold, silent_until: Instant::from_millis(0) }
Cache {
storage,
gc_threshold,
silent_until: Instant::from_millis(0),
}
}
pub fn fill(&mut self, protocol_addr: IpAddress, hardware_addr: EthernetAddress,
timestamp: Instant) {
pub fn fill(
&mut self,
protocol_addr: IpAddress,
hardware_addr: EthernetAddress,
timestamp: Instant,
) {
debug_assert!(protocol_addr.is_unicast());
debug_assert!(hardware_addr.is_unicast());
@ -104,11 +114,12 @@ impl<'a> Cache<'a> {
let current_storage_size = self.storage.len();
match self.storage {
ManagedMap::Borrowed(_) => (),
ManagedMap::Borrowed(_) => (),
#[cfg(any(feature = "std", feature = "alloc"))]
ManagedMap::Owned(ref mut map) => {
if current_storage_size >= self.gc_threshold {
let new_btree_map = map.iter_mut()
let new_btree_map = map
.iter_mut()
.map(|(key, value)| (*key, *value))
.filter(|(_, v)| timestamp < v.expires_at)
.collect();
@ -118,13 +129,18 @@ impl<'a> Cache<'a> {
}
};
let neighbor = Neighbor {
expires_at: timestamp + Self::ENTRY_LIFETIME, hardware_addr
expires_at: timestamp + Self::ENTRY_LIFETIME,
hardware_addr,
};
match self.storage.insert(protocol_addr, neighbor) {
Ok(Some(old_neighbor)) => {
if old_neighbor.hardware_addr != hardware_addr {
net_trace!("replaced {} => {} (was {})",
protocol_addr, hardware_addr, old_neighbor.hardware_addr);
net_trace!(
"replaced {} => {} (was {})",
protocol_addr,
hardware_addr,
old_neighbor.hardware_addr
);
}
}
Ok(None) => {
@ -147,21 +163,23 @@ impl<'a> Cache<'a> {
}
// Owned maps can extend themselves.
#[cfg(any(feature = "std", feature = "alloc"))]
ManagedMap::Owned(_) => unreachable!()
ManagedMap::Owned(_) => unreachable!(),
};
let _old_neighbor =
self.storage.remove(&old_protocol_addr).unwrap();
let _old_neighbor = self.storage.remove(&old_protocol_addr).unwrap();
match self.storage.insert(protocol_addr, neighbor) {
Ok(None) => {
net_trace!("filled {} => {} (evicted {} => {})",
protocol_addr, hardware_addr,
old_protocol_addr, _old_neighbor.hardware_addr);
net_trace!(
"filled {} => {} (evicted {} => {})",
protocol_addr,
hardware_addr,
old_protocol_addr,
_old_neighbor.hardware_addr
);
}
// We've covered everything else above.
_ => unreachable!()
_ => unreachable!(),
}
}
}
}
@ -171,10 +189,13 @@ impl<'a> Cache<'a> {
return Answer::Found(EthernetAddress::BROADCAST);
}
if let Some(&Neighbor { expires_at, hardware_addr }) =
self.storage.get(protocol_addr) {
if let Some(&Neighbor {
expires_at,
hardware_addr,
}) = self.storage.get(protocol_addr)
{
if timestamp < expires_at {
return Answer::Found(hardware_addr)
return Answer::Found(hardware_addr);
}
}
@ -193,9 +214,8 @@ impl<'a> Cache<'a> {
#[cfg(test)]
mod test {
use super::*;
use std::collections::BTreeMap;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
use std::collections::BTreeMap;
const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
@ -207,17 +227,47 @@ mod test {
let mut cache_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
assert_eq!(
cache
.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
.found(),
false
);
assert_eq!(
cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
.found(),
false
);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(),
false);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_A)
);
assert_eq!(
cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
.found(),
false
);
assert_eq!(
cache
.lookup(
&MOCK_IP_ADDR_1,
Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
)
.found(),
false
);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
assert_eq!(
cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
.found(),
false
);
}
#[test]
@ -226,9 +276,19 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(),
false);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_A)
);
assert_eq!(
cache
.lookup(
&MOCK_IP_ADDR_1,
Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
)
.found(),
false
);
}
#[test]
@ -237,9 +297,15 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_A)
);
cache.fill(MOCK_IP_ADDR_1, HADDR_B, Instant::from_millis(0));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_B));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_B)
);
}
#[test]
@ -249,10 +315,20 @@ mod test {
cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
// Adding third item after the expiration of the previous
// two should garbage collect
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2);
cache.fill(
MOCK_IP_ADDR_3,
HADDR_C,
Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2,
);
assert_eq!(cache.storage.len(), 1);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Answer::Found(HADDR_C));
assert_eq!(
cache.lookup(
&MOCK_IP_ADDR_3,
Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2
),
Answer::Found(HADDR_C)
);
}
#[test]
@ -263,12 +339,28 @@ mod test {
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Answer::Found(HADDR_B));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found(), false);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)),
Answer::Found(HADDR_B)
);
assert_eq!(
cache
.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000))
.found(),
false
);
cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D));
assert_eq!(
cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000))
.found(),
false
);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)),
Answer::Found(HADDR_D)
);
}
#[test]
@ -276,10 +368,19 @@ mod test {
let mut cache_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::NotFound);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::NotFound
);
cache.limit_rate(Instant::from_millis(0));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), Answer::RateLimited);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), Answer::NotFound);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)),
Answer::RateLimited
);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)),
Answer::NotFound
);
}
}

View File

@ -1,13 +1,13 @@
use managed::ManagedMap;
use crate::time::Instant;
use core::ops::Bound;
use managed::ManagedMap;
use crate::{Error, Result};
use crate::wire::{IpCidr, IpAddress};
use crate::wire::{IpAddress, IpCidr};
#[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Address, Ipv4Cidr};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Address, Ipv6Cidr};
use crate::{Error, Result};
/// A prefix of addresses that should be routed via a router
#[derive(Debug, Clone, Copy)]
@ -70,7 +70,9 @@ impl<'a> Routes<'a> {
/// Creates a routing tables. The backing storage is **not** cleared
/// upon creation.
pub fn new<T>(storage: T) -> Routes<'a>
where T: Into<ManagedMap<'a, IpCidr, Route>> {
where
T: Into<ManagedMap<'a, IpCidr, Route>>,
{
let storage = storage.into();
Routes { storage }
}
@ -89,7 +91,7 @@ impl<'a> Routes<'a> {
let route = Route::new_ipv4_gateway(gateway);
match self.storage.insert(cidr, route) {
Ok(route) => Ok(route),
Err((_cidr, _route)) => Err(Error::Exhausted)
Err((_cidr, _route)) => Err(Error::Exhausted),
}
}
@ -102,7 +104,7 @@ impl<'a> Routes<'a> {
let route = Route::new_ipv6_gateway(gateway);
match self.storage.insert(cidr, route) {
Ok(route) => Ok(route),
Err((_cidr, _route)) => Err(Error::Exhausted)
Err((_cidr, _route)) => Err(Error::Exhausted),
}
}
@ -124,8 +126,7 @@ impl<'a> Routes<'a> {
self.storage.remove(&cidr)
}
pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) ->
Option<IpAddress> {
pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
assert!(addr.is_unicast());
let cidr = match addr {
@ -133,10 +134,14 @@ impl<'a> Routes<'a> {
IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)),
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)),
_ => unimplemented!()
_ => unimplemented!(),
};
for (prefix, route) in self.storage.range((Bound::Unbounded::<IpCidr>, Bound::Included(cidr))).rev() {
for (prefix, route) in self
.storage
.range((Bound::Unbounded::<IpCidr>, Bound::Included(cidr)))
.rev()
{
// TODO: do something with route.preferred_until
if let Some(expires_at) = route.expires_at {
if timestamp > expires_at {
@ -159,24 +164,28 @@ mod test {
#[cfg(feature = "proto-ipv6")]
mod mock {
use super::super::*;
pub const ADDR_1A: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_1B: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
pub const ADDR_1C: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
pub const ADDR_1A: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_1B: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
pub const ADDR_1C: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
pub fn cidr_1() -> Ipv6Cidr {
Ipv6Cidr::new(Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
Ipv6Cidr::new(
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]),
64,
)
}
pub const ADDR_2A: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_2B: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
pub const ADDR_2A: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_2B: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
pub fn cidr_2() -> Ipv6Cidr {
Ipv6Cidr::new(Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
Ipv6Cidr::new(
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]),
64,
)
}
}
@ -204,25 +213,56 @@ mod test {
let mut routes_storage = [None, None, None];
let mut routes = Routes::new(&mut routes_storage[..]);
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None);
assert_eq!(
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
None
);
let route = Route {
via_router: ADDR_1A.into(),
preferred_until: None, expires_at: None,
preferred_until: None,
expires_at: None,
};
routes.update(|storage| {
storage.insert(cidr_1().into(), route).unwrap();
});
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None);
assert_eq!(
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
None
);
let route2 = Route {
via_router: ADDR_2A.into(),
@ -233,16 +273,46 @@ mod test {
storage.insert(cidr_2().into(), route2).unwrap();
});
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), Some(ADDR_2A.into()));
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), Some(ADDR_2A.into()));
assert_eq!(
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
Some(ADDR_2A.into())
);
assert_eq!(
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
Some(ADDR_2A.into())
);
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)), Some(ADDR_2A.into()));
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)), Some(ADDR_2A.into()));
assert_eq!(
routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)),
Some(ADDR_2A.into())
);
assert_eq!(
routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)),
Some(ADDR_2A.into())
);
}
}

View File

@ -1,6 +1,12 @@
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![deny(unsafe_code)]
#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "medium-ethernet"), deny(unused))]
#![cfg_attr(
all(
any(feature = "proto-ipv4", feature = "proto-ipv6"),
feature = "medium-ethernet"
),
deny(unused)
)]
//! The _smoltcp_ library is built in a layered structure, with the layers corresponding
//! to the levels of API abstraction. Only the highest layers would be used by a typical
@ -107,10 +113,7 @@ compile_error!("If you enable the socket feature, you must enable at least one o
#[cfg(all(
feature = "socket",
not(any(
feature = "medium-ethernet",
feature = "medium-ip",
))
not(any(feature = "medium-ethernet", feature = "medium-ip",))
))]
compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet");
@ -123,13 +126,13 @@ use core::fmt;
mod macros;
mod parsers;
pub mod storage;
pub mod phy;
pub mod wire;
pub mod iface;
pub mod phy;
#[cfg(feature = "socket")]
pub mod socket;
pub mod storage;
pub mod time;
pub mod wire;
/// The error type for the networking stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -178,16 +181,16 @@ pub type Result<T> = core::result::Result<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Exhausted => write!(f, "buffer space exhausted"),
Error::Illegal => write!(f, "illegal operation"),
Error::Exhausted => write!(f, "buffer space exhausted"),
Error::Illegal => write!(f, "illegal operation"),
Error::Unaddressable => write!(f, "unaddressable destination"),
Error::Finished => write!(f, "operation finished"),
Error::Truncated => write!(f, "truncated packet"),
Error::Checksum => write!(f, "checksum error"),
Error::Unrecognized => write!(f, "unrecognized packet"),
Error::Fragmented => write!(f, "fragmented packet"),
Error::Malformed => write!(f, "malformed packet"),
Error::Dropped => write!(f, "dropped by socket"),
Error::Finished => write!(f, "operation finished"),
Error::Truncated => write!(f, "truncated packet"),
Error::Checksum => write!(f, "checksum error"),
Error::Unrecognized => write!(f, "unrecognized packet"),
Error::Fragmented => write!(f, "fragmented packet"),
Error::Malformed => write!(f, "malformed packet"),
Error::Dropped => write!(f, "dropped by socket"),
}
}
}

View File

@ -1,4 +1,3 @@
#[cfg(feature = "log")]
macro_rules! net_log {
(trace, $($arg:expr),*) => { log::trace!($($arg),*); };

View File

@ -1,7 +1,10 @@
#![cfg_attr(not(all(feature = "proto-ipv6", feature = "proto-ipv4")), allow(dead_code))]
#![cfg_attr(
not(all(feature = "proto-ipv6", feature = "proto-ipv4")),
allow(dead_code)
)]
use core::str::FromStr;
use core::result;
use core::str::FromStr;
#[cfg(feature = "medium-ethernet")]
use crate::wire::EthernetAddress;
@ -15,14 +18,14 @@ type Result<T> = result::Result<T, ()>;
struct Parser<'a> {
data: &'a [u8],
pos: usize
pos: usize,
}
impl<'a> Parser<'a> {
fn new(data: &'a str) -> Parser<'a> {
Parser {
data: data.as_bytes(),
pos: 0
pos: 0,
}
}
@ -40,12 +43,14 @@ impl<'a> Parser<'a> {
self.pos += 1;
Ok(chr)
}
None => Err(())
None => Err(()),
}
}
fn try_do<F, T>(&mut self, f: F) -> Option<T>
where F: FnOnce(&mut Parser<'a>) -> Result<T> {
where
F: FnOnce(&mut Parser<'a>) -> Result<T>,
{
let pos = self.pos;
match f(self) {
Ok(res) => Some(res),
@ -65,7 +70,9 @@ impl<'a> Parser<'a> {
}
fn until_eof<F, T>(&mut self, f: F) -> Result<T>
where F: FnOnce(&mut Parser<'a>) -> Result<T> {
where
F: FnOnce(&mut Parser<'a>) -> Result<T>,
{
let res = f(self)?;
self.accept_eof()?;
Ok(res)
@ -99,8 +106,7 @@ impl<'a> Parser<'a> {
}
}
fn accept_number(&mut self, max_digits: usize, max_value: u32,
hex: bool) -> Result<u32> {
fn accept_number(&mut self, max_digits: usize, max_value: u32, hex: bool) -> Result<u32> {
let mut value = self.accept_digit(hex)? as u32;
for _ in 1..max_digits {
match self.try_do(|p| p.accept_digit(hex)) {
@ -108,7 +114,7 @@ impl<'a> Parser<'a> {
value *= if hex { 16 } else { 10 };
value += digit as u32;
}
None => break
None => break,
}
}
if value < max_value {
@ -133,10 +139,10 @@ impl<'a> Parser<'a> {
#[cfg(feature = "medium-ethernet")]
fn accept_mac(&mut self) -> Result<EthernetAddress> {
if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) {
return Ok(mac)
return Ok(mac);
}
if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b':')) {
return Ok(mac)
return Ok(mac);
}
Err(())
}
@ -154,9 +160,13 @@ impl<'a> Parser<'a> {
}
#[cfg(feature = "proto-ipv6")]
fn accept_ipv6_part(&mut self, (head, tail): (&mut [u16; 8], &mut [u16; 6]),
(head_idx, tail_idx): (&mut usize, &mut usize),
mut use_tail: bool, is_cidr: bool) -> Result<()> {
fn accept_ipv6_part(
&mut self,
(head, tail): (&mut [u16; 8], &mut [u16; 6]),
(head_idx, tail_idx): (&mut usize, &mut usize),
mut use_tail: bool,
is_cidr: bool,
) -> Result<()> {
let double_colon = match self.try_do(|p| p.accept_str(b"::")) {
Some(_) if !use_tail && *head_idx < 7 => {
// Found a double colon. Start filling out the
@ -164,7 +174,7 @@ impl<'a> Parser<'a> {
// this is the last character we can parse.
use_tail = true;
true
},
}
Some(_) => {
// This is a bad address. Only one double colon is
// allowed and an address is only 128 bits.
@ -193,21 +203,20 @@ impl<'a> Parser<'a> {
});
}
Ok(())
},
}
Some(part) if *tail_idx < 6 => {
// Valid u16 to be added to the address
tail[*tail_idx] = part as u16;
*tail_idx += 1;
if *tail_idx == 1 && tail[0] == 0xffff
&& head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
if *tail_idx == 1 && tail[0] == 0xffff && head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
self.try_do(|p| {
p.accept_char(b':')?;
p.accept_ipv4_mapped_ipv6_part(tail, tail_idx)
});
}
Ok(())
},
}
Some(_) => {
// Tail or head section is too long
Err(())
@ -259,7 +268,12 @@ impl<'a> Parser<'a> {
let (mut addr, mut tail) = ([0u16; 8], [0u16; 6]);
let (mut head_idx, mut tail_idx) = (0, 0);
self.accept_ipv6_part((&mut addr, &mut tail), (&mut head_idx, &mut tail_idx), false, is_cidr)?;
self.accept_ipv6_part(
(&mut addr, &mut tail),
(&mut head_idx, &mut tail_idx),
false,
is_cidr,
)?;
// We need to copy the tail portion (the portion following the "::") to the
// end of the address.
@ -290,14 +304,14 @@ impl<'a> Parser<'a> {
#[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv4()) {
Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)),
None => ()
None => (),
}
#[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv6(false)) {
Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)),
None => ()
None => (),
}
Err(())
@ -314,7 +328,10 @@ impl<'a> Parser<'a> {
self.accept_number(5, 65535, false)?
};
Ok(IpEndpoint { addr: IpAddress::Ipv4(ip), port: port as u16 })
Ok(IpEndpoint {
addr: IpAddress::Ipv4(ip),
port: port as u16,
})
}
#[cfg(feature = "proto-ipv6")]
@ -326,10 +343,16 @@ impl<'a> Parser<'a> {
self.accept_char(b':')?;
let port = self.accept_number(5, 65535, false)?;
Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: port as u16 })
Ok(IpEndpoint {
addr: IpAddress::Ipv6(ip),
port: port as u16,
})
} else {
let ip = self.accept_ipv6(false)?;
Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: 0 })
Ok(IpEndpoint {
addr: IpAddress::Ipv6(ip),
port: 0,
})
}
}
@ -338,14 +361,14 @@ impl<'a> Parser<'a> {
#[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv4_endpoint()) {
Some(ipv4) => return Ok(ipv4),
None => ()
None => (),
}
#[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv6_endpoint()) {
Some(ipv6) => return Ok(ipv6),
None => ()
None => (),
}
Err(())
@ -431,14 +454,14 @@ impl FromStr for IpCidr {
#[allow(clippy::single_match)]
match Ipv4Cidr::from_str(s) {
Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)),
Err(_) => ()
Err(_) => (),
}
#[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)]
match Ipv6Cidr::from_str(s) {
Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)),
Err(_) => ()
Err(_) => (),
}
Err(())
@ -465,29 +488,40 @@ mod test {
if let Ok(cidr) = cidr {
assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr));
assert_eq!(IpCidr::from_str(&format!("{}", cidr)),
Ok($variant(cidr)));
assert_eq!(IpCidr::from_str(&format!("{}", cidr)), Ok($variant(cidr)));
}
}
}
};
}
#[test]
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
fn test_mac() {
assert_eq!(EthernetAddress::from_str(""), Err(()));
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"),
Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00])));
assert_eq!(EthernetAddress::from_str("01:23:45:67:89:ab"),
Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab])));
assert_eq!(EthernetAddress::from_str("cd:ef:10:00:00:00"),
Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00])));
assert_eq!(EthernetAddress::from_str("00:00:00:ab:cd:ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])));
assert_eq!(EthernetAddress::from_str("00-00-00-ab-cd-ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])));
assert_eq!(EthernetAddress::from_str("AB-CD-EF-00-00-00"),
Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00])));
assert_eq!(
EthernetAddress::from_str("02:00:00:00:00:00"),
Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]))
);
assert_eq!(
EthernetAddress::from_str("01:23:45:67:89:ab"),
Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab]))
);
assert_eq!(
EthernetAddress::from_str("cd:ef:10:00:00:00"),
Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00]))
);
assert_eq!(
EthernetAddress::from_str("00:00:00:ab:cd:ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
);
assert_eq!(
EthernetAddress::from_str("00-00-00-ab-cd-ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
);
assert_eq!(
EthernetAddress::from_str("AB-CD-EF-00-00-00"),
Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00]))
);
assert_eq!(EthernetAddress::from_str("100:00:00:00:00:00"), Err(()));
assert_eq!(EthernetAddress::from_str("002:00:00:00:00:00"), Err(()));
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:000"), Err(()));
@ -498,10 +532,14 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_ipv4() {
assert_eq!(Ipv4Address::from_str(""), Err(()));
assert_eq!(Ipv4Address::from_str("1.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4])));
assert_eq!(Ipv4Address::from_str("001.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4])));
assert_eq!(
Ipv4Address::from_str("1.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4]))
);
assert_eq!(
Ipv4Address::from_str("001.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4]))
);
assert_eq!(Ipv4Address::from_str("0001.2.3.4"), Err(()));
assert_eq!(Ipv4Address::from_str("999.2.3.4"), Err(()));
assert_eq!(Ipv4Address::from_str("1.2.3.4.5"), Err(()));
@ -515,73 +553,87 @@ mod test {
fn test_ipv6() {
// Obviously not valid
assert_eq!(Ipv6Address::from_str(""), Err(()));
assert_eq!(Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)));
assert_eq!(Ipv6Address::from_str("::1"),
Ok(Ipv6Address::LOOPBACK));
assert_eq!(Ipv6Address::from_str("::"),
Ok(Ipv6Address::UNSPECIFIED));
assert_eq!(Ipv6Address::from_str("fe80::1"),
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)));
assert_eq!(Ipv6Address::from_str("1234:5678::"),
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0)));
assert_eq!(Ipv6Address::from_str("1234:5678::8765:4321"),
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321)));
assert_eq!(
Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
);
assert_eq!(Ipv6Address::from_str("::1"), Ok(Ipv6Address::LOOPBACK));
assert_eq!(Ipv6Address::from_str("::"), Ok(Ipv6Address::UNSPECIFIED));
assert_eq!(
Ipv6Address::from_str("fe80::1"),
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
);
assert_eq!(
Ipv6Address::from_str("1234:5678::"),
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0))
);
assert_eq!(
Ipv6Address::from_str("1234:5678::8765:4321"),
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321))
);
// Two double colons in address
assert_eq!(Ipv6Address::from_str("1234:5678::1::1"),
Err(()));
assert_eq!(Ipv6Address::from_str("4444:333:22:1::4"),
Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4)));
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1::"),
Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0)));
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1"),
Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1)));
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"),
Err(()));
assert_eq!(Ipv6Address::from_str("1234:5678::1::1"), Err(()));
assert_eq!(
Ipv6Address::from_str("4444:333:22:1::4"),
Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4))
);
assert_eq!(
Ipv6Address::from_str("1:1:1:1:1:1::"),
Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0))
);
assert_eq!(
Ipv6Address::from_str("::1:1:1:1:1:1"),
Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1))
);
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
// Double colon appears too late indicating an address that is too long
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"),
Err(()));
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"), Err(()));
// Section after double colon is too long for a valid address
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"),
Err(()));
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
// Obviously too long
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"),
Err(()));
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"), Err(()));
// Address is too short
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"),
Err(()));
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"), Err(()));
// Long number
assert_eq!(Ipv6Address::from_str("::000001"),
Err(()));
assert_eq!(Ipv6Address::from_str("::000001"), Err(()));
// IPv4-Mapped address
assert_eq!(Ipv6Address::from_str("::ffff:192.168.1.1"),
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"),
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
assert_eq!(Ipv6Address::from_str("0::ffff:192.168.1.1"),
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
assert_eq!(
Ipv6Address::from_str("::ffff:192.168.1.1"),
Ok(Ipv6Address([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
]))
);
assert_eq!(
Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"),
Ok(Ipv6Address([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
]))
);
assert_eq!(
Ipv6Address::from_str("0::ffff:192.168.1.1"),
Ok(Ipv6Address([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
]))
);
// Only ffff is allowed in position 6 when IPv4 mapped
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"),
Err(()));
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"), Err(()));
// Positions 1-5 must be 0 when IPv4 mapped
assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"),
Err(()));
assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"),
Err(()));
assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"), Err(()));
assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"), Err(()));
// Out of range ipv4 octet
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"),
Err(()));
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"), Err(()));
// Invalid hex in ipv4 octet
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"),
Err(()));
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"), Err(()));
}
#[test]
#[cfg(feature = "proto-ipv4")]
fn test_ip_ipv4() {
assert_eq!(IpAddress::from_str(""), Err(()));
assert_eq!(IpAddress::from_str("1.2.3.4"),
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4]))));
assert_eq!(
IpAddress::from_str("1.2.3.4"),
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4])))
);
assert_eq!(IpAddress::from_str("x"), Err(()));
}
@ -589,8 +641,12 @@ mod test {
#[cfg(feature = "proto-ipv6")]
fn test_ip_ipv6() {
assert_eq!(IpAddress::from_str(""), Err(()));
assert_eq!(IpAddress::from_str("fe80::1"),
Ok(IpAddress::Ipv6(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))));
assert_eq!(
IpAddress::from_str("fe80::1"),
Ok(IpAddress::Ipv6(Ipv6Address::new(
0xfe80, 0, 0, 0, 0, 0, 0, 1
)))
);
assert_eq!(IpAddress::from_str("x"), Err(()));
}
@ -598,14 +654,22 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_cidr_ipv4() {
let tests = [
("127.0.0.1/8",
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8))),
("192.168.1.1/24",
Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8))),
("8.8.8.8/32",
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8))),
("8.8.8.8/0",
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8))),
(
"127.0.0.1/8",
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8)),
),
(
"192.168.1.1/24",
Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8)),
),
(
"8.8.8.8/32",
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8)),
),
(
"8.8.8.8/0",
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8)),
),
("", Err(())),
("1", Err(())),
("127.0.0.1", Err(())),
@ -622,22 +686,32 @@ mod test {
#[cfg(feature = "proto-ipv6")]
fn test_cidr_ipv6() {
let tests = [
("fe80::1/64",
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))),
("fe80::/64",
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 64u8))),
("::1/128",
Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
("::/128",
Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
("fe80:0:0:0:0:0:0:1/64",
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))),
("fe80:0:0:0:0:0:0:1|64",
Err(())),
("fe80::|64",
Err(())),
("fe80::1::/64",
Err(()))
(
"fe80::1/64",
Ok(Ipv6Cidr::new(
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
64u8,
)),
),
(
"fe80::/64",
Ok(Ipv6Cidr::new(
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
64u8,
)),
),
("::1/128", Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
("::/128", Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
(
"fe80:0:0:0:0:0:0:1/64",
Ok(Ipv6Cidr::new(
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
64u8,
)),
),
("fe80:0:0:0:0:0:0:1|64", Err(())),
("fe80::|64", Err(())),
("fe80::1::/64", Err(())),
];
check_cidr_test_array!(tests, Ipv6Cidr::from_str, IpCidr::Ipv6);
}
@ -649,11 +723,17 @@ mod test {
assert_eq!(IpEndpoint::from_str("x"), Err(()));
assert_eq!(
IpEndpoint::from_str("127.0.0.1"),
Ok(IpEndpoint { addr: IpAddress::v4(127, 0, 0, 1), port: 0 })
Ok(IpEndpoint {
addr: IpAddress::v4(127, 0, 0, 1),
port: 0
})
);
assert_eq!(
IpEndpoint::from_str("127.0.0.1:12345"),
Ok(IpEndpoint { addr: IpAddress::v4(127, 0, 0, 1), port: 12345 })
Ok(IpEndpoint {
addr: IpAddress::v4(127, 0, 0, 1),
port: 12345
})
);
}
@ -664,11 +744,17 @@ mod test {
assert_eq!(IpEndpoint::from_str("x"), Err(()));
assert_eq!(
IpEndpoint::from_str("fe80::1"),
Ok(IpEndpoint { addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), port: 0 })
Ok(IpEndpoint {
addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
port: 0
})
);
assert_eq!(
IpEndpoint::from_str("[fe80::1]:12345"),
Ok(IpEndpoint { addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), port: 12345 })
Ok(IpEndpoint {
addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
port: 12345
})
);
}
}

View File

@ -1,8 +1,8 @@
use core::cell::RefCell;
use crate::{Error, Result};
use crate::phy::{self, DeviceCapabilities, Device};
use crate::phy::{self, Device, DeviceCapabilities};
use crate::time::{Duration, Instant};
use crate::{Error, Result};
// We use our own RNG to stay compatible with #![no_std].
// The use of the RNG below has a slight bias, but it doesn't matter.
@ -22,21 +22,21 @@ const MTU: usize = 1536;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct Config {
corrupt_pct: u8,
drop_pct: u8,
drop_pct: u8,
reorder_pct: u8,
max_size: usize,
max_size: usize,
max_tx_rate: u64,
max_rx_rate: u64,
interval: Duration,
interval: Duration,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct State {
rng_seed: u32,
rng_seed: u32,
refilled_at: Instant,
tx_bucket: u64,
rx_bucket: u64,
tx_bucket: u64,
rx_bucket: u64,
}
impl State {
@ -48,7 +48,7 @@ impl State {
let buffer = buffer.as_mut();
// We introduce a single bitflip, as the most likely, and the hardest to detect, error.
let index = (xorshift32(&mut self.rng_seed) as usize) % buffer.len();
let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8;
let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8;
buffer[index] ^= bit;
}
@ -61,7 +61,9 @@ impl State {
}
fn maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool {
if config.max_tx_rate == 0 { return true }
if config.max_tx_rate == 0 {
return true;
}
self.refill(config, timestamp);
if self.tx_bucket > 0 {
@ -73,7 +75,9 @@ impl State {
}
fn maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool {
if config.max_rx_rate == 0 { return true }
if config.max_rx_rate == 0 {
return true;
}
self.refill(config, timestamp);
if self.rx_bucket > 0 {
@ -92,19 +96,19 @@ impl State {
/// or hardware limitations (such as a limited number or size of usable network buffers).
#[derive(Debug)]
pub struct FaultInjector<D: for<'a> Device<'a>> {
inner: D,
state: RefCell<State>,
config: Config,
inner: D,
state: RefCell<State>,
config: Config,
}
impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// Create a fault injector device, using the given random number generator seed.
pub fn new(inner: D, seed: u32) -> FaultInjector<D> {
let state = State {
rng_seed: seed,
rng_seed: seed,
refilled_at: Instant::from_millis(0),
tx_bucket: 0,
rx_bucket: 0,
tx_bucket: 0,
rx_bucket: 0,
};
FaultInjector {
inner: inner,
@ -153,7 +157,9 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// # Panics
/// This function panics if the probability is not between 0% and 100%.
pub fn set_corrupt_chance(&mut self, pct: u8) {
if pct > 100 { panic!("percentage out of range") }
if pct > 100 {
panic!("percentage out of range")
}
self.config.corrupt_pct = pct
}
@ -162,7 +168,9 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// # Panics
/// This function panics if the probability is not between 0% and 100%.
pub fn set_drop_chance(&mut self, pct: u8) {
if pct > 100 { panic!("percentage out of range") }
if pct > 100 {
panic!("percentage out of range")
}
self.config.drop_pct = pct
}
@ -189,7 +197,8 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
}
impl<'a, D> Device<'a> for FaultInjector<D>
where D: for<'b> Device<'b>,
where
D: for<'b> Device<'b>,
{
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken>;
@ -203,60 +212,78 @@ impl<'a, D> Device<'a> for FaultInjector<D>
}
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let &mut Self { ref mut inner, ref state, config } = self;
let &mut Self {
ref mut inner,
ref state,
config,
} = self;
inner.receive().map(|(rx_token, tx_token)| {
let rx = RxToken {
state: &state,
config: config,
token: rx_token,
state: &state,
config: config,
token: rx_token,
corrupt: [0; MTU],
};
let tx = TxToken {
state: &state,
config: config,
token: tx_token,
junk: [0; MTU],
state: &state,
config: config,
token: tx_token,
junk: [0; MTU],
};
(rx, tx)
})
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
let &mut Self { ref mut inner, ref state, config } = self;
let &mut Self {
ref mut inner,
ref state,
config,
} = self;
inner.transmit().map(|token| TxToken {
state: &state,
state: &state,
config: config,
token: token,
junk: [0; MTU],
junk: [0; MTU],
})
}
}
#[doc(hidden)]
pub struct RxToken<'a, Rx: phy::RxToken> {
state: &'a RefCell<State>,
config: Config,
token: Rx,
state: &'a RefCell<State>,
config: Config,
token: Rx,
corrupt: [u8; MTU],
}
impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> {
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
if self.state.borrow_mut().maybe(self.config.drop_pct) {
net_trace!("rx: randomly dropping a packet");
return Err(Error::Exhausted)
return Err(Error::Exhausted);
}
if !self.state.borrow_mut().maybe_receive(&self.config, timestamp) {
if !self
.state
.borrow_mut()
.maybe_receive(&self.config, timestamp)
{
net_trace!("rx: dropping a packet because of rate limiting");
return Err(Error::Exhausted)
return Err(Error::Exhausted);
}
let Self { token, config, state, mut corrupt } = self;
let Self {
token,
config,
state,
mut corrupt,
} = self;
token.consume(timestamp, |buffer| {
if config.max_size > 0 && buffer.as_ref().len() > config.max_size {
net_trace!("rx: dropping a packet that is too large");
return Err(Error::Exhausted)
return Err(Error::Exhausted);
}
if state.borrow_mut().maybe(config.corrupt_pct) {
net_trace!("rx: randomly corrupting a packet");
@ -273,15 +300,16 @@ impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> {
#[doc(hidden)]
pub struct TxToken<'a, Tx: phy::TxToken> {
state: &'a RefCell<State>,
state: &'a RefCell<State>,
config: Config,
token: Tx,
junk: [u8; MTU],
token: Tx,
junk: [u8; MTU],
}
impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
fn consume<R, F>(mut self, timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) {
net_trace!("tx: randomly dropping a packet");
@ -289,7 +317,11 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
} else if self.config.max_size > 0 && len > self.config.max_size {
net_trace!("tx: dropping a packet that is too large");
true
} else if !self.state.borrow_mut().maybe_transmit(&self.config, timestamp) {
} else if !self
.state
.borrow_mut()
.maybe_transmit(&self.config, timestamp)
{
net_trace!("tx: dropping a packet because of rate limiting");
true
} else {
@ -300,7 +332,12 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
return f(&mut self.junk[..len]);
}
let Self { token, state, config, .. } = self;
let Self {
token,
state,
config,
..
} = self;
token.consume(timestamp, len, |mut buf| {
if state.borrow_mut().maybe(config.corrupt_pct) {
net_trace!("tx: corrupting a packet");

View File

@ -1,6 +1,6 @@
use crate::Result;
use crate::phy::{self, DeviceCapabilities, Device};
use crate::phy::{self, Device, DeviceCapabilities};
use crate::time::Instant;
use crate::Result;
// This could be fixed once associated consts are stable.
const MTU: usize = 1536;
@ -20,7 +20,7 @@ pub trait Fuzzer {
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FuzzInjector<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> {
inner: D,
inner: D,
fuzz_tx: FTx,
fuzz_rx: FRx,
}
@ -29,7 +29,11 @@ pub struct FuzzInjector<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> {
impl<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx> {
/// Create a fuzz injector device.
pub fn new(inner: D, fuzz_tx: FTx, fuzz_rx: FRx) -> FuzzInjector<D, FTx, FRx> {
FuzzInjector { inner, fuzz_tx, fuzz_rx }
FuzzInjector {
inner,
fuzz_tx,
fuzz_rx,
}
}
/// Return the underlying device, consuming the fuzz injector.
@ -39,9 +43,10 @@ impl<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx>
}
impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector<D, FTx, FRx>
where D: for<'b> Device<'b>,
FTx: Fuzzer + 'a,
FRx: Fuzzer + 'a
where
D: for<'b> Device<'b>,
FTx: Fuzzer + 'a,
FRx: Fuzzer + 'a,
{
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken, FRx>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken, FTx>;
@ -55,38 +60,47 @@ impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector<D, FTx, FRx>
}
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let &mut Self { ref mut inner, ref fuzz_rx, ref fuzz_tx } = self;
let &mut Self {
ref mut inner,
ref fuzz_rx,
ref fuzz_tx,
} = self;
inner.receive().map(|(rx_token, tx_token)| {
let rx = RxToken {
fuzzer: fuzz_rx,
token: rx_token,
token: rx_token,
};
let tx = TxToken {
fuzzer: fuzz_tx,
token: tx_token,
token: tx_token,
};
(rx, tx)
})
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
let &mut Self { ref mut inner, fuzz_rx: _, ref fuzz_tx } = self;
let &mut Self {
ref mut inner,
fuzz_rx: _,
ref fuzz_tx,
} = self;
inner.transmit().map(|token| TxToken {
fuzzer: fuzz_tx,
token: token,
token: token,
})
}
}
#[doc(hidden)]
pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a>{
pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a> {
fuzzer: &'a F,
token: Rx,
token: Rx,
}
impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> {
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let Self { fuzzer, token } = self;
token.consume(timestamp, |buffer| {
@ -99,12 +113,13 @@ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> {
#[doc(hidden)]
pub struct TxToken<'a, Tx: phy::TxToken, F: Fuzzer + 'a> {
fuzzer: &'a F,
token: Tx,
token: Tx,
}
impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> {
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let Self { fuzzer, token } = self;
token.consume(timestamp, len, |mut buf| {

View File

@ -1,12 +1,12 @@
use alloc::vec::Vec;
#[cfg(not(feature = "rust-1_28"))]
use alloc::collections::VecDeque;
use alloc::vec::Vec;
#[cfg(feature = "rust-1_28")]
use alloc::VecDeque;
use crate::Result;
use crate::phy::{self, Device, DeviceCapabilities, Medium};
use crate::time::Instant;
use crate::Result;
/// A loopback device.
#[derive(Debug)]
@ -44,7 +44,9 @@ impl<'a> Device<'a> for Loopback {
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
self.queue.pop_front().map(move |buffer| {
let rx = RxToken { buffer };
let tx = TxToken { queue: &mut self.queue };
let tx = TxToken {
queue: &mut self.queue,
};
(rx, tx)
})
}
@ -63,7 +65,8 @@ pub struct RxToken {
impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
f(&mut self.buffer)
}
@ -76,7 +79,8 @@ pub struct TxToken<'a> {
impl<'a> phy::TxToken for TxToken<'a> {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let mut buffer = Vec::new();
buffer.resize(len, 0);

View File

@ -11,7 +11,9 @@ and implementations of it:
[TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames
on the host OS.
*/
#![cfg_attr(feature = "medium-ethernet", doc = r##"
#![cfg_attr(
feature = "medium-ethernet",
doc = r##"
# Examples
An implementation of the [Device](trait.Device.html) trait for a simple hardware
@ -84,37 +86,50 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> {
}
}
```
"##)]
"##
)]
use crate::Result;
use crate::time::Instant;
use crate::Result;
#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))]
#[cfg(all(
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
unix
))]
mod sys;
mod tracer;
mod fault_injector;
mod fuzz_injector;
mod pcap_writer;
#[cfg(any(feature = "std", feature = "alloc"))]
mod loopback;
mod pcap_writer;
#[cfg(all(feature = "phy-raw_socket", unix))]
mod raw_socket;
#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
mod tracer;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
mod tuntap_interface;
#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))]
#[cfg(all(
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
unix
))]
pub use self::sys::wait;
pub use self::tracer::Tracer;
pub use self::fault_injector::FaultInjector;
pub use self::fuzz_injector::{Fuzzer, FuzzInjector};
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
pub use self::fuzz_injector::{FuzzInjector, Fuzzer};
#[cfg(any(feature = "std", feature = "alloc"))]
pub use self::loopback::Loopback;
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
#[cfg(all(feature = "phy-raw_socket", unix))]
pub use self::raw_socket::RawSocket;
#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
pub use self::tracer::Tracer;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
pub use self::tuntap_interface::TunTapInterface;
/// A description of checksum behavior for a particular protocol.
@ -142,7 +157,7 @@ impl Checksum {
pub fn rx(&self) -> bool {
match *self {
Checksum::Both | Checksum::Rx => true,
_ => false
_ => false,
}
}
@ -150,7 +165,7 @@ impl Checksum {
pub fn tx(&self) -> bool {
match *self {
Checksum::Both | Checksum::Tx => true,
_ => false
_ => false,
}
}
}
@ -234,7 +249,9 @@ impl DeviceCapabilities {
pub fn ip_mtu(&self) -> usize {
match self.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len(),
Medium::Ethernet => {
self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len()
}
#[cfg(feature = "medium-ip")]
Medium::Ip => self.max_transmission_unit,
}
@ -260,7 +277,6 @@ pub enum Medium {
Ip,
}
impl Default for Medium {
fn default() -> Medium {
#[cfg(feature = "medium-ethernet")]
@ -306,7 +322,8 @@ pub trait RxToken {
/// The timestamp must be a number of milliseconds, monotonically increasing since an
/// arbitrary moment in time, such as system startup.
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>;
where
F: FnOnce(&mut [u8]) -> Result<R>;
}
/// A token to transmit a single network packet.
@ -321,5 +338,6 @@ pub trait TxToken {
/// The timestamp must be a number of milliseconds, monotonically increasing since an
/// arbitrary moment in time, such as system startup.
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>;
where
F: FnOnce(&mut [u8]) -> Result<R>;
}

View File

@ -1,13 +1,13 @@
use byteorder::{ByteOrder, NativeEndian};
use phy::Medium;
#[cfg(feature = "std")]
use std::cell::RefCell;
#[cfg(feature = "std")]
use std::io::Write;
use byteorder::{ByteOrder, NativeEndian};
use phy::Medium;
use crate::Result;
use crate::phy::{self, DeviceCapabilities, Device};
use crate::phy::{self, Device, DeviceCapabilities};
use crate::time::Instant;
use crate::Result;
enum_with_unknown! {
/// Captured packet header type.
@ -28,7 +28,7 @@ pub enum PcapMode {
/// Capture only received packets.
RxOnly,
/// Capture only transmitted packets.
TxOnly
TxOnly,
}
/// A packet capture sink.
@ -54,12 +54,12 @@ pub trait PcapSink {
///
/// This method may be overridden e.g. if special synchronization is necessary.
fn global_header(&self, link_type: PcapLinkType) {
self.write_u32(0xa1b2c3d4); // magic number
self.write_u16(2); // major version
self.write_u16(4); // minor version
self.write_u32(0); // timezone (= UTC)
self.write_u32(0); // accuracy (not used)
self.write_u32(65535); // maximum packet length
self.write_u32(0xa1b2c3d4); // magic number
self.write_u16(2); // major version
self.write_u16(4); // minor version
self.write_u32(0); // timezone (= UTC)
self.write_u32(0); // accuracy (not used)
self.write_u32(65535); // maximum packet length
self.write_u32(link_type.into()); // link-layer header type
}
@ -72,10 +72,10 @@ pub trait PcapSink {
fn packet_header(&self, timestamp: Instant, length: usize) {
assert!(length <= 65535);
self.write_u32(timestamp.secs() as u32); // timestamp seconds
self.write_u32(timestamp.millis() as u32); // timestamp microseconds
self.write_u32(length as u32); // captured length
self.write_u32(length as u32); // original length
self.write_u32(timestamp.secs() as u32); // timestamp seconds
self.write_u32(timestamp.millis() as u32); // timestamp microseconds
self.write_u32(length as u32); // captured length
self.write_u32(length as u32); // original length
}
/// Write the libpcap packet header followed by packet data into the sink.
@ -121,12 +121,13 @@ impl<T: Write> PcapSink for RefCell<T> {
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PcapWriter<D, S>
where D: for<'a> Device<'a>,
S: PcapSink + Clone,
where
D: for<'a> Device<'a>,
S: PcapSink + Clone,
{
lower: D,
sink: S,
mode: PcapMode,
sink: S,
mode: PcapMode,
}
impl<D: for<'a> Device<'a>, S: PcapSink + Clone> PcapWriter<D, S> {
@ -145,27 +146,49 @@ impl<D: for<'a> Device<'a>, S: PcapSink + Clone> PcapWriter<D, S> {
}
impl<'a, D, S> Device<'a> for PcapWriter<D, S>
where D: for<'b> Device<'b>,
S: PcapSink + Clone + 'a,
where
D: for<'b> Device<'b>,
S: PcapSink + Clone + 'a,
{
type RxToken = RxToken<<D as Device<'a>>::RxToken, S>;
type TxToken = TxToken<<D as Device<'a>>::TxToken, S>;
fn capabilities(&self) -> DeviceCapabilities { self.lower.capabilities() }
fn capabilities(&self) -> DeviceCapabilities {
self.lower.capabilities()
}
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let &mut Self { ref mut lower, ref sink, mode, .. } = self;
let &mut Self {
ref mut lower,
ref sink,
mode,
..
} = self;
lower.receive().map(|(rx_token, tx_token)| {
let rx = RxToken { token: rx_token, sink: sink.clone(), mode };
let tx = TxToken { token: tx_token, sink: sink.clone(), mode };
let rx = RxToken {
token: rx_token,
sink: sink.clone(),
mode,
};
let tx = TxToken {
token: tx_token,
sink: sink.clone(),
mode,
};
(rx, tx)
})
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
let &mut Self { ref mut lower, ref sink, mode } = self;
lower.transmit().map(|token| {
TxToken { token, sink: sink.clone(), mode }
let &mut Self {
ref mut lower,
ref sink,
mode,
} = self;
lower.transmit().map(|token| TxToken {
token,
sink: sink.clone(),
mode,
})
}
}
@ -173,8 +196,8 @@ impl<'a, D, S> Device<'a> for PcapWriter<D, S>
#[doc(hidden)]
pub struct RxToken<Rx: phy::RxToken, S: PcapSink> {
token: Rx,
sink: S,
mode: PcapMode,
sink: S,
mode: PcapMode,
}
impl<Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<Rx, S> {
@ -182,9 +205,8 @@ impl<Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<Rx, S> {
let Self { token, sink, mode } = self;
token.consume(timestamp, |buffer| {
match mode {
PcapMode::Both | PcapMode::RxOnly =>
sink.packet(timestamp, buffer.as_ref()),
PcapMode::TxOnly => ()
PcapMode::Both | PcapMode::RxOnly => sink.packet(timestamp, buffer.as_ref()),
PcapMode::TxOnly => (),
}
f(buffer)
})
@ -194,21 +216,21 @@ impl<Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<Rx, S> {
#[doc(hidden)]
pub struct TxToken<Tx: phy::TxToken, S: PcapSink> {
token: Tx,
sink: S,
mode: PcapMode
sink: S,
mode: PcapMode,
}
impl<Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<Tx, S> {
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let Self { token, sink, mode } = self;
token.consume(timestamp, len, |buffer| {
let result = f(buffer);
match mode {
PcapMode::Both | PcapMode::TxOnly =>
sink.packet(timestamp, &buffer),
PcapMode::RxOnly => ()
PcapMode::Both | PcapMode::TxOnly => sink.packet(timestamp, &buffer),
PcapMode::RxOnly => (),
};
result
})

View File

@ -1,18 +1,18 @@
use std::cell::RefCell;
use std::vec::Vec;
use std::rc::Rc;
use std::io;
use std::os::unix::io::{RawFd, AsRawFd};
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
use std::vec::Vec;
use crate::Result;
use crate::phy::{self, sys, DeviceCapabilities, Device, Medium};
use crate::phy::{self, sys, Device, DeviceCapabilities, Medium};
use crate::time::Instant;
use crate::Result;
/// A socket that captures or transmits the complete frame.
#[derive(Debug)]
pub struct RawSocket {
lower: Rc<RefCell<sys::RawSocketDesc>>,
mtu: usize
lower: Rc<RefCell<sys::RawSocketDesc>>,
mtu: usize,
}
impl AsRawFd for RawSocket {
@ -32,7 +32,7 @@ impl RawSocket {
let mtu = lower.interface_mtu()?;
Ok(RawSocket {
lower: Rc::new(RefCell::new(lower)),
mtu: mtu
mtu: mtu,
})
}
}
@ -56,13 +56,13 @@ impl<'a> Device<'a> for RawSocket {
Ok(size) => {
buffer.resize(size, 0);
let rx = RxToken { buffer };
let tx = TxToken { lower: self.lower.clone() };
let tx = TxToken {
lower: self.lower.clone(),
};
Some((rx, tx))
}
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
None
}
Err(err) => panic!("{}", err)
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None,
Err(err) => panic!("{}", err),
}
}
@ -75,12 +75,13 @@ impl<'a> Device<'a> for RawSocket {
#[doc(hidden)]
pub struct RxToken {
buffer: Vec<u8>
buffer: Vec<u8>,
}
impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
f(&mut self.buffer[..])
}
@ -88,12 +89,13 @@ impl phy::RxToken for RxToken {
#[doc(hidden)]
pub struct TxToken {
lower: Rc<RefCell<sys::RawSocketDesc>>,
lower: Rc<RefCell<sys::RawSocketDesc>>,
}
impl phy::TxToken for TxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let mut lower = self.lower.borrow_mut();
let mut buffer = vec![0; len];

View File

@ -4,8 +4,8 @@ use std::os::unix::io::{AsRawFd, RawFd};
use libc;
use crate::wire::ETHERNET_HEADER_LEN;
use super::{ifreq, ifreq_for};
use crate::wire::ETHERNET_HEADER_LEN;
/// set interface
#[cfg(any(target_os = "macos", target_os = "openbsd"))]

View File

@ -1,10 +1,10 @@
#![allow(unused)]
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
pub const ETH_P_ALL: libc::c_short = 0x0003;
pub const ETH_P_ALL: libc::c_short = 0x0003;
pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
pub const IFF_TUN: libc::c_int = 0x0001;
pub const IFF_TAP: libc::c_int = 0x0002;
pub const IFF_NO_PI: libc::c_int = 0x1000;
pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
pub const IFF_TUN: libc::c_int = 0x0001;
pub const IFF_TAP: libc::c_int = 0x0002;
pub const IFF_NO_PI: libc::c_int = 0x1000;

View File

@ -1,25 +1,45 @@
#![allow(unsafe_code)]
use std::{mem, ptr, io};
use std::os::unix::io::RawFd;
use crate::time::Duration;
use std::os::unix::io::RawFd;
use std::{io, mem, ptr};
#[cfg(any(target_os = "linux", target_os = "android"))]
#[path = "linux.rs"]
mod imp;
#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))]
pub mod raw_socket;
#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
#[cfg(all(
feature = "phy-raw_socket",
not(any(target_os = "linux", target_os = "android")),
unix
))]
pub mod bpf;
#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
#[cfg(all(
feature = "phy-raw_socket",
any(target_os = "linux", target_os = "android")
))]
pub mod raw_socket;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
pub mod tuntap_interface;
#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))]
pub use self::raw_socket::RawSocketDesc;
#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
#[cfg(all(
feature = "phy-raw_socket",
not(any(target_os = "linux", target_os = "android")),
unix
))]
pub use self::bpf::BpfDevice as RawSocketDesc;
#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
#[cfg(all(
feature = "phy-raw_socket",
any(target_os = "linux", target_os = "android")
))]
pub use self::raw_socket::RawSocketDesc;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
pub use self::tuntap_interface::TunTapInterfaceDesc;
/// Wait until given file descriptor becomes readable, but no longer than given timeout.
@ -44,35 +64,51 @@ pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
exceptfds.assume_init()
};
let mut timeout = libc::timeval { tv_sec: 0, tv_usec: 0 };
let timeout_ptr =
if let Some(duration) = duration {
timeout.tv_sec = duration.secs() as libc::time_t;
timeout.tv_usec = (duration.millis() * 1_000) as libc::suseconds_t;
&mut timeout as *mut _
} else {
ptr::null_mut()
};
let mut timeout = libc::timeval {
tv_sec: 0,
tv_usec: 0,
};
let timeout_ptr = if let Some(duration) = duration {
timeout.tv_sec = duration.secs() as libc::time_t;
timeout.tv_usec = (duration.millis() * 1_000) as libc::suseconds_t;
&mut timeout as *mut _
} else {
ptr::null_mut()
};
let res = libc::select(fd + 1, &mut readfds, &mut writefds, &mut exceptfds, timeout_ptr);
if res == -1 { return Err(io::Error::last_os_error()) }
let res = libc::select(
fd + 1,
&mut readfds,
&mut writefds,
&mut exceptfds,
timeout_ptr,
);
if res == -1 {
return Err(io::Error::last_os_error());
}
Ok(())
}
}
#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))]
#[cfg(all(
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"),
unix
))]
#[repr(C)]
#[derive(Debug)]
struct ifreq {
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */
}
#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))]
#[cfg(all(
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"),
unix
))]
fn ifreq_for(name: &str) -> ifreq {
let mut ifreq = ifreq {
ifr_name: [0; libc::IF_NAMESIZE],
ifr_data: 0
ifr_data: 0,
};
for (i, byte) in name.as_bytes().iter().enumerate() {
ifreq.ifr_name[i] = *byte as libc::c_char
@ -80,13 +116,20 @@ fn ifreq_for(name: &str) -> ifreq {
ifreq
}
#[cfg(all(any(target_os = "linux", target_os = "android"),
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")))]
fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq,
cmd: libc::c_ulong) -> io::Result<libc::c_int> {
#[cfg(all(
any(target_os = "linux", target_os = "android"),
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")
))]
fn ifreq_ioctl(
lower: libc::c_int,
ifreq: &mut ifreq,
cmd: libc::c_ulong,
) -> io::Result<libc::c_int> {
unsafe {
let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq);
if res == -1 { return Err(io::Error::last_os_error()) }
if res == -1 {
return Err(io::Error::last_os_error());
}
}
Ok(ifreq.ifr_data)

View File

@ -1,12 +1,12 @@
use std::{mem, io};
use std::os::unix::io::{RawFd, AsRawFd};
use super::*;
use crate::wire::EthernetFrame;
use std::os::unix::io::{AsRawFd, RawFd};
use std::{io, mem};
#[derive(Debug)]
pub struct RawSocketDesc {
lower: libc::c_int,
ifreq: ifreq
ifreq: ifreq,
}
impl AsRawFd for RawSocketDesc {
@ -18,41 +18,51 @@ impl AsRawFd for RawSocketDesc {
impl RawSocketDesc {
pub fn new(name: &str) -> io::Result<RawSocketDesc> {
let lower = unsafe {
let lower = libc::socket(libc::AF_PACKET, libc::SOCK_RAW | libc::SOCK_NONBLOCK,
imp::ETH_P_ALL.to_be() as i32);
if lower == -1 { return Err(io::Error::last_os_error()) }
let lower = libc::socket(
libc::AF_PACKET,
libc::SOCK_RAW | libc::SOCK_NONBLOCK,
imp::ETH_P_ALL.to_be() as i32,
);
if lower == -1 {
return Err(io::Error::last_os_error());
}
lower
};
Ok(RawSocketDesc {
lower: lower,
ifreq: ifreq_for(name)
ifreq: ifreq_for(name),
})
}
pub fn interface_mtu(&mut self) -> io::Result<usize> {
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
// smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
let ip_mtu = ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?;
let ip_mtu =
ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?;
Ok(ip_mtu + EthernetFrame::<&[u8]>::header_len())
}
pub fn bind_interface(&mut self) -> io::Result<()> {
let sockaddr = libc::sockaddr_ll {
sll_family: libc::AF_PACKET as u16,
sll_family: libc::AF_PACKET as u16,
sll_protocol: imp::ETH_P_ALL.to_be() as u16,
sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?,
sll_hatype: 1,
sll_pkttype: 0,
sll_halen: 6,
sll_addr: [0; 8]
sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?,
sll_hatype: 1,
sll_pkttype: 0,
sll_halen: 6,
sll_addr: [0; 8],
};
unsafe {
let res = libc::bind(self.lower,
&sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
mem::size_of::<libc::sockaddr_ll>() as u32);
if res == -1 { return Err(io::Error::last_os_error()) }
let res = libc::bind(
self.lower,
&sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
mem::size_of::<libc::sockaddr_ll>() as u32,
);
if res == -1 {
return Err(io::Error::last_os_error());
}
}
Ok(())
@ -60,18 +70,30 @@ impl RawSocketDesc {
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe {
let len = libc::recv(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(), 0);
if len == -1 { return Err(io::Error::last_os_error()) }
let len = libc::recv(
self.lower,
buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(),
0,
);
if len == -1 {
return Err(io::Error::last_os_error());
}
Ok(len as usize)
}
}
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe {
let len = libc::send(self.lower, buffer.as_ptr() as *const libc::c_void,
buffer.len(), 0);
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
let len = libc::send(
self.lower,
buffer.as_ptr() as *const libc::c_void,
buffer.len(),
0,
);
if len == -1 {
Err(io::Error::last_os_error()).unwrap()
}
Ok(len as usize)
}
}
@ -79,6 +101,8 @@ impl RawSocketDesc {
impl Drop for RawSocketDesc {
fn drop(&mut self) {
unsafe { libc::close(self.lower); }
unsafe {
libc::close(self.lower);
}
}
}

View File

@ -1,7 +1,7 @@
use std::io;
use std::os::unix::io::{RawFd, AsRawFd};
use super::*;
use crate::{phy::Medium, wire::EthernetFrame};
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
#[derive(Debug)]
pub struct TunTapInterfaceDesc {
@ -19,9 +19,13 @@ impl AsRawFd for TunTapInterfaceDesc {
impl TunTapInterfaceDesc {
pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
let lower = unsafe {
let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char,
libc::O_RDWR | libc::O_NONBLOCK);
if lower == -1 { return Err(io::Error::last_os_error()) }
let lower = libc::open(
"/dev/net/tun\0".as_ptr() as *const libc::c_char,
libc::O_RDWR | libc::O_NONBLOCK,
);
if lower == -1 {
return Err(io::Error::last_os_error());
}
lower
};
@ -46,13 +50,17 @@ impl TunTapInterfaceDesc {
pub fn interface_mtu(&mut self) -> io::Result<usize> {
let lower = unsafe {
let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP);
if lower == -1 { return Err(io::Error::last_os_error()) }
if lower == -1 {
return Err(io::Error::last_os_error());
}
lower
};
let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize);
unsafe { libc::close(lower); }
unsafe {
libc::close(lower);
}
// Propagate error after close, to ensure we always close.
let ip_mtu = ip_mtu?;
@ -71,18 +79,28 @@ impl TunTapInterfaceDesc {
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe {
let len = libc::read(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len());
if len == -1 { return Err(io::Error::last_os_error()) }
let len = libc::read(
self.lower,
buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(),
);
if len == -1 {
return Err(io::Error::last_os_error());
}
Ok(len as usize)
}
}
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe {
let len = libc::write(self.lower, buffer.as_ptr() as *const libc::c_void,
buffer.len());
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
let len = libc::write(
self.lower,
buffer.as_ptr() as *const libc::c_void,
buffer.len(),
);
if len == -1 {
Err(io::Error::last_os_error()).unwrap()
}
Ok(len as usize)
}
}
@ -90,6 +108,8 @@ impl TunTapInterfaceDesc {
impl Drop for TunTapInterfaceDesc {
fn drop(&mut self) {
unsafe { libc::close(self.lower); }
unsafe {
libc::close(self.lower);
}
}
}

View File

@ -1,8 +1,11 @@
use core::fmt;
use crate::{Result, wire::pretty_print::{PrettyIndent, PrettyPrint}};
use crate::phy::{self, DeviceCapabilities, Device, Medium};
use crate::phy::{self, Device, DeviceCapabilities, Medium};
use crate::time::Instant;
use crate::{
wire::pretty_print::{PrettyIndent, PrettyPrint},
Result,
};
/// A tracer device.
///
@ -10,7 +13,7 @@ use crate::time::Instant;
/// using the provided writer function, and then passes them to another
/// device.
pub struct Tracer<D: for<'a> Device<'a>> {
inner: D,
inner: D,
writer: fn(Instant, Packet),
}
@ -42,50 +45,78 @@ impl<D: for<'a> Device<'a>> Tracer<D> {
}
impl<'a, D> Device<'a> for Tracer<D>
where D: for<'b> Device<'b>,
where
D: for<'b> Device<'b>,
{
type RxToken = RxToken<<D as Device<'a>>::RxToken>;
type TxToken = TxToken<<D as Device<'a>>::TxToken>;
fn capabilities(&self) -> DeviceCapabilities { self.inner.capabilities() }
fn capabilities(&self) -> DeviceCapabilities {
self.inner.capabilities()
}
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let &mut Self { ref mut inner, writer, .. } = self;
let &mut Self {
ref mut inner,
writer,
..
} = self;
let medium = inner.capabilities().medium;
inner.receive().map(|(rx_token, tx_token)| {
let rx = RxToken { token: rx_token, writer, medium };
let tx = TxToken { token: tx_token, writer, medium };
let rx = RxToken {
token: rx_token,
writer,
medium,
};
let tx = TxToken {
token: tx_token,
writer,
medium,
};
(rx, tx)
})
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
let &mut Self { ref mut inner, writer } = self;
let &mut Self {
ref mut inner,
writer,
} = self;
let medium = inner.capabilities().medium;
inner.transmit().map(|tx_token| {
TxToken { token: tx_token, medium, writer }
inner.transmit().map(|tx_token| TxToken {
token: tx_token,
medium,
writer,
})
}
}
#[doc(hidden)]
pub struct RxToken<Rx: phy::RxToken> {
token: Rx,
writer: fn(Instant, Packet),
medium: Medium,
token: Rx,
writer: fn(Instant, Packet),
medium: Medium,
}
impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let Self { token, writer, medium } = self;
let Self {
token,
writer,
medium,
} = self;
token.consume(timestamp, |buffer| {
writer(timestamp, Packet{
buffer,
medium,
prefix: "<- ",
});
writer(
timestamp,
Packet {
buffer,
medium,
prefix: "<- ",
},
);
f(buffer)
})
}
@ -93,23 +124,31 @@ impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
#[doc(hidden)]
pub struct TxToken<Tx: phy::TxToken> {
token: Tx,
writer: fn(Instant, Packet),
medium: Medium,
token: Tx,
writer: fn(Instant, Packet),
medium: Medium,
}
impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let Self { token, writer, medium } = self;
let Self {
token,
writer,
medium,
} = self;
token.consume(timestamp, len, |buffer| {
let result = f(buffer);
writer(timestamp, Packet{
buffer,
medium,
prefix: "-> ",
});
writer(
timestamp,
Packet {
buffer,
medium,
prefix: "-> ",
},
);
result
})
}
@ -126,15 +165,31 @@ impl<'a> fmt::Display for Packet<'a> {
let mut indent = PrettyIndent::new(self.prefix);
match self.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(
&self.buffer,
f,
&mut indent,
),
#[cfg(feature = "medium-ip")]
Medium::Ip => match crate::wire::IpVersion::of_packet(&self.buffer) {
#[cfg(feature = "proto-ipv4")]
Ok(crate::wire::IpVersion::Ipv4) => crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
Ok(crate::wire::IpVersion::Ipv4) => {
crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(
&self.buffer,
f,
&mut indent,
)
}
#[cfg(feature = "proto-ipv6")]
Ok(crate::wire::IpVersion::Ipv6) => crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
_ => f.write_str("unrecognized IP version")
}
Ok(crate::wire::IpVersion::Ipv6) => {
crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(
&self.buffer,
f,
&mut indent,
)
}
_ => f.write_str("unrecognized IP version"),
},
}
}
}

View File

@ -1,18 +1,18 @@
use std::cell::RefCell;
use std::vec::Vec;
use std::rc::Rc;
use std::io;
use std::os::unix::io::{RawFd, AsRawFd};
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
use std::vec::Vec;
use crate::Result;
use crate::phy::{self, sys, DeviceCapabilities, Device, Medium};
use crate::phy::{self, sys, Device, DeviceCapabilities, Medium};
use crate::time::Instant;
use crate::Result;
/// A virtual TUN (IP) or TAP (Ethernet) interface.
#[derive(Debug)]
pub struct TunTapInterface {
lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
mtu: usize,
lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
mtu: usize,
medium: Medium,
}
@ -59,13 +59,13 @@ impl<'a> Device<'a> for TunTapInterface {
Ok(size) => {
buffer.resize(size, 0);
let rx = RxToken { buffer };
let tx = TxToken { lower: self.lower.clone() };
let tx = TxToken {
lower: self.lower.clone(),
};
Some((rx, tx))
}
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
None
}
Err(err) => panic!("{}", err)
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None,
Err(err) => panic!("{}", err),
}
}
@ -78,12 +78,13 @@ impl<'a> Device<'a> for TunTapInterface {
#[doc(hidden)]
pub struct RxToken {
buffer: Vec<u8>
buffer: Vec<u8>,
}
impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
f(&mut self.buffer[..])
}
@ -96,7 +97,8 @@ pub struct TxToken {
impl phy::TxToken for TxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where F: FnOnce(&mut [u8]) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
{
let mut lower = self.lower.borrow_mut();
let mut buffer = vec![0; len];

View File

@ -1,12 +1,12 @@
use crate::{Error, Result};
use crate::wire::{IpProtocol, IpAddress,
Ipv4Cidr, Ipv4Address, Ipv4Repr,
UdpRepr, UDP_HEADER_LEN,
DhcpPacket, DhcpRepr, DhcpMessageType, DHCP_CLIENT_PORT, DHCP_SERVER_PORT, DHCP_MAX_DNS_SERVER_COUNT};
use crate::wire::dhcpv4::{field as dhcpv4_field};
use crate::socket::{SocketMeta, Context};
use crate::time::{Instant, Duration};
use crate::socket::SocketHandle;
use crate::socket::{Context, SocketMeta};
use crate::time::{Duration, Instant};
use crate::wire::dhcpv4::field as dhcpv4_field;
use crate::wire::{
DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr,
UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
};
use crate::{Error, Result};
use super::{PollAt, Socket};
@ -133,7 +133,7 @@ impl Dhcpv4Socket {
pub fn new() -> Self {
Dhcpv4Socket {
meta: SocketMeta::default(),
state: ClientState::Discovering(DiscoverState{
state: ClientState::Discovering(DiscoverState {
retry_at: Instant::from_millis(0),
}),
config_changed: true,
@ -159,7 +159,13 @@ impl Dhcpv4Socket {
PollAt::Time(t)
}
pub(crate) fn process(&mut self, cx: &Context, ip_repr: &Ipv4Repr, repr: &UdpRepr, payload: &[u8]) -> Result<()> {
pub(crate) fn process(
&mut self,
cx: &Context,
ip_repr: &Ipv4Repr,
repr: &UdpRepr,
payload: &[u8],
) -> Result<()> {
let src_ip = ip_repr.src_addr;
// This is enforced in interface.rs.
@ -179,23 +185,35 @@ impl Dhcpv4Socket {
return Ok(());
}
};
if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() { return Ok(()) }
if dhcp_repr.transaction_id != self.transaction_id { return Ok(()) }
if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() {
return Ok(());
}
if dhcp_repr.transaction_id != self.transaction_id {
return Ok(());
}
let server_identifier = match dhcp_repr.server_identifier {
Some(server_identifier) => server_identifier,
None => {
net_debug!("DHCP ignoring {:?} because missing server_identifier", dhcp_repr.message_type);
net_debug!(
"DHCP ignoring {:?} because missing server_identifier",
dhcp_repr.message_type
);
return Ok(());
}
};
net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier);
net_debug!(
"DHCP recv {:?} from {} ({})",
dhcp_repr.message_type,
src_ip,
server_identifier
);
match (&mut self.state, dhcp_repr.message_type){
match (&mut self.state, dhcp_repr.message_type) {
(ClientState::Discovering(_state), DhcpMessageType::Offer) => {
if !dhcp_repr.your_ip.is_unicast() {
net_debug!("DHCP ignoring OFFER because your_ip is not unicast");
return Ok(())
return Ok(());
}
self.state = ClientState::Requesting(RequestState {
@ -205,13 +223,15 @@ impl Dhcpv4Socket {
address: src_ip,
identifier: server_identifier,
},
requested_ip: dhcp_repr.your_ip // use the offered ip
requested_ip: dhcp_repr.your_ip, // use the offered ip
});
}
(ClientState::Requesting(state), DhcpMessageType::Ack) => {
if let Some((config, renew_at, expires_at)) = Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration) {
if let Some((config, renew_at, expires_at)) =
Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration)
{
self.config_changed = true;
self.state = ClientState::Renewing(RenewState{
self.state = ClientState::Renewing(RenewState {
server: state.server,
config,
renew_at,
@ -223,7 +243,9 @@ impl Dhcpv4Socket {
self.reset();
}
(ClientState::Renewing(state), DhcpMessageType::Ack) => {
if let Some((config, renew_at, expires_at)) = Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration) {
if let Some((config, renew_at, expires_at)) =
Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration)
{
state.renew_at = renew_at;
state.expires_at = expires_at;
if state.config != config {
@ -236,19 +258,26 @@ impl Dhcpv4Socket {
self.reset();
}
_ => {
net_debug!("DHCP ignoring {:?}: unexpected in current state", dhcp_repr.message_type);
net_debug!(
"DHCP ignoring {:?}: unexpected in current state",
dhcp_repr.message_type
);
}
}
Ok(())
}
fn parse_ack(now: Instant, dhcp_repr: &DhcpRepr, max_lease_duration: Option<Duration>) -> Option<(Config, Instant, Instant)> {
fn parse_ack(
now: Instant,
dhcp_repr: &DhcpRepr,
max_lease_duration: Option<Duration>,
) -> Option<(Config, Instant, Instant)> {
let subnet_mask = match dhcp_repr.subnet_mask {
Some(subnet_mask) => subnet_mask,
None => {
net_debug!("DHCP ignoring ACK because missing subnet_mask");
return None
return None;
}
};
@ -256,16 +285,19 @@ impl Dhcpv4Socket {
Some(prefix_len) => prefix_len,
None => {
net_debug!("DHCP ignoring ACK because subnet_mask is not a valid mask");
return None
return None;
}
};
if !dhcp_repr.your_ip.is_unicast() {
net_debug!("DHCP ignoring ACK because your_ip is not unicast");
return None
return None;
}
let mut lease_duration = dhcp_repr.lease_duration.map(|d| Duration::from_secs(d as _)).unwrap_or(DEFAULT_LEASE_DURATION);
let mut lease_duration = dhcp_repr
.lease_duration
.map(|d| Duration::from_secs(d as _))
.unwrap_or(DEFAULT_LEASE_DURATION);
if let Some(max_lease_duration) = max_lease_duration {
lease_duration = lease_duration.min(max_lease_duration);
}
@ -276,7 +308,7 @@ impl Dhcpv4Socket {
if let Some(received) = dhcp_repr.dns_servers {
let mut i = 0;
for addr in received.iter() {
if let Some(addr) = addr{
if let Some(addr) = addr {
if addr.is_unicast() {
// This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT
dns_servers[i] = Some(*addr);
@ -285,10 +317,10 @@ impl Dhcpv4Socket {
}
}
}
let config = Config{
let config = Config {
address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len),
router: dhcp_repr.router,
dns_servers: dns_servers
dns_servers: dns_servers,
};
// RFC 2131 indicates clients should renew a lease halfway through its expiration.
@ -299,8 +331,9 @@ impl Dhcpv4Socket {
}
pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()> {
where
F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()>,
{
// note: Dhcpv4Socket is only usable in ethernet mediums, so the
// unwrap can never fail.
let ethernet_addr = cx.ethernet_address.unwrap();
@ -349,11 +382,15 @@ impl Dhcpv4Socket {
match &mut self.state {
ClientState::Discovering(state) => {
if cx.now < state.retry_at {
return Err(Error::Exhausted)
return Err(Error::Exhausted);
}
// send packet
net_debug!("DHCP send DISCOVER to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr);
net_debug!(
"DHCP send DISCOVER to {}: {:?}",
ipv4_repr.dst_addr,
dhcp_repr
);
ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
emit((ipv4_repr, udp_repr, dhcp_repr))?;
@ -364,14 +401,14 @@ impl Dhcpv4Socket {
}
ClientState::Requesting(state) => {
if cx.now < state.retry_at {
return Err(Error::Exhausted)
return Err(Error::Exhausted);
}
if state.retry >= REQUEST_RETRIES {
net_debug!("DHCP request retries exceeded, restarting discovery");
self.reset();
// return Ok so we get polled again
return Ok(())
return Ok(());
}
dhcp_repr.message_type = DhcpMessageType::Request;
@ -379,7 +416,11 @@ impl Dhcpv4Socket {
dhcp_repr.requested_ip = Some(state.requested_ip);
dhcp_repr.server_identifier = Some(state.server.identifier);
net_debug!("DHCP send request to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr);
net_debug!(
"DHCP send request to {}: {:?}",
ipv4_repr.dst_addr,
dhcp_repr
);
ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
emit((ipv4_repr, udp_repr, dhcp_repr))?;
@ -395,11 +436,11 @@ impl Dhcpv4Socket {
net_debug!("DHCP lease expired");
self.reset();
// return Ok so we get polled again
return Ok(())
return Ok(());
}
if cx.now < state.renew_at {
return Err(Error::Exhausted)
return Err(Error::Exhausted);
}
ipv4_repr.src_addr = state.config.address.address();
@ -440,7 +481,7 @@ impl Dhcpv4Socket {
if let ClientState::Renewing(_) = &self.state {
self.config_changed = true;
}
self.state = ClientState::Discovering(DiscoverState{
self.state = ClientState::Discovering(DiscoverState {
retry_at: Instant::from_millis(0),
});
}

View File

@ -2,20 +2,20 @@ use core::cmp;
#[cfg(feature = "async")]
use core::task::Waker;
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
use crate::storage::{PacketBuffer, PacketMetadata};
#[cfg(feature = "async")]
use crate::socket::WakerRegistration;
use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta};
use crate::storage::{PacketBuffer, PacketMetadata};
use crate::{Error, Result};
#[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Address, Ipv4Repr, Icmpv4Packet, Icmpv4Repr};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Address, Ipv6Repr, Icmpv6Packet, Icmpv6Repr};
use crate::wire::IcmpRepr;
use crate::wire::{UdpPacket, UdpRepr};
#[cfg(feature = "proto-ipv4")]
use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Address, Ipv4Repr};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Icmpv6Packet, Icmpv6Repr, Ipv6Address, Ipv6Repr};
use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr};
use crate::wire::{UdpPacket, UdpRepr};
/// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for
/// more details.
@ -26,7 +26,7 @@ use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr};
pub enum Endpoint {
Unspecified,
Ident(u16),
Udp(IpEndpoint)
Udp(IpEndpoint),
}
impl Endpoint {
@ -34,13 +34,15 @@ impl Endpoint {
match *self {
Endpoint::Ident(_) => true,
Endpoint::Udp(endpoint) => endpoint.port != 0,
Endpoint::Unspecified => false
Endpoint::Unspecified => false,
}
}
}
impl Default for Endpoint {
fn default() -> Endpoint { Endpoint::Unspecified }
fn default() -> Endpoint {
Endpoint::Unspecified
}
}
/// An ICMP packet metadata.
@ -64,7 +66,7 @@ pub struct IcmpSocket<'a> {
rx_buffer: IcmpSocketBuffer<'a>,
tx_buffer: IcmpSocketBuffer<'a>,
/// The endpoint this socket is communicating with
endpoint: Endpoint,
endpoint: Endpoint,
/// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
hop_limit: Option<u8>,
#[cfg(feature = "async")]
@ -75,13 +77,12 @@ pub struct IcmpSocket<'a> {
impl<'a> IcmpSocket<'a> {
/// Create an ICMP socket with the given buffers.
pub fn new(rx_buffer: IcmpSocketBuffer<'a>,
tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> {
pub fn new(rx_buffer: IcmpSocketBuffer<'a>, tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> {
IcmpSocket {
meta: SocketMeta::default(),
meta: SocketMeta::default(),
rx_buffer: rx_buffer,
tx_buffer: tx_buffer,
endpoint: Endpoint::default(),
endpoint: Endpoint::default(),
hop_limit: None,
#[cfg(feature = "async")]
rx_waker: WakerRegistration::new(),
@ -219,7 +220,9 @@ impl<'a> IcmpSocket<'a> {
return Err(Error::Unaddressable);
}
if self.is_open() { return Err(Error::Illegal) }
if self.is_open() {
return Err(Error::Illegal);
}
self.endpoint = endpoint;
@ -282,13 +285,17 @@ impl<'a> IcmpSocket<'a> {
/// size, and `Err(Error::Unaddressable)` if the remote address is unspecified.
pub fn send(&mut self, size: usize, endpoint: IpAddress) -> Result<&mut [u8]> {
if endpoint.is_unspecified() {
return Err(Error::Unaddressable)
return Err(Error::Unaddressable);
}
let packet_buf = self.tx_buffer.enqueue(size, endpoint)?;
net_trace!("{}:{}: buffer to send {} octets",
self.meta.handle, endpoint, size);
net_trace!(
"{}:{}: buffer to send {} octets",
self.meta.handle,
endpoint,
size
);
Ok(packet_buf)
}
@ -308,8 +315,12 @@ impl<'a> IcmpSocket<'a> {
pub fn recv(&mut self) -> Result<(&[u8], IpAddress)> {
let (endpoint, packet_buf) = self.rx_buffer.dequeue()?;
net_trace!("{}:{}: receive {} buffered octets",
self.meta.handle, endpoint, packet_buf.len());
net_trace!(
"{}:{}: receive {} buffered octets",
self.meta.handle,
endpoint,
packet_buf.len()
);
Ok((packet_buf, endpoint))
}
@ -332,19 +343,33 @@ impl<'a> IcmpSocket<'a> {
// accept Destination Unreachable messages with the data containing
// a UDP packet send from the local port we are bound to.
#[cfg(feature = "proto-ipv4")]
(&Endpoint::Udp(endpoint), &IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. }))
if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
(
&Endpoint::Udp(endpoint),
&IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. }),
) if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
let packet = UdpPacket::new_unchecked(data);
match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), &cx.caps.checksum) {
match UdpRepr::parse(
&packet,
&ip_repr.src_addr(),
&ip_repr.dst_addr(),
&cx.caps.checksum,
) {
Ok(repr) => endpoint.port == repr.src_port,
Err(_) => false,
}
}
#[cfg(feature = "proto-ipv6")]
(&Endpoint::Udp(endpoint), &IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. }))
if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
(
&Endpoint::Udp(endpoint),
&IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. }),
) if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
let packet = UdpPacket::new_unchecked(data);
match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), &cx.caps.checksum) {
match UdpRepr::parse(
&packet,
&ip_repr.src_addr(),
&ip_repr.dst_addr(),
&cx.caps.checksum,
) {
Ok(repr) => endpoint.port == repr.src_port,
Err(_) => false,
}
@ -353,44 +378,70 @@ impl<'a> IcmpSocket<'a> {
// Echo Request/Reply with the identifier field matching the endpoint
// port.
#[cfg(feature = "proto-ipv4")]
(&Endpoint::Ident(bound_ident),
&IcmpRepr::Ipv4(Icmpv4Repr::EchoRequest { ident, .. })) |
(&Endpoint::Ident(bound_ident),
&IcmpRepr::Ipv4(Icmpv4Repr::EchoReply { ident, .. })) =>
ident == bound_ident,
(
&Endpoint::Ident(bound_ident),
&IcmpRepr::Ipv4(Icmpv4Repr::EchoRequest { ident, .. }),
)
| (
&Endpoint::Ident(bound_ident),
&IcmpRepr::Ipv4(Icmpv4Repr::EchoReply { ident, .. }),
) => ident == bound_ident,
#[cfg(feature = "proto-ipv6")]
(&Endpoint::Ident(bound_ident),
&IcmpRepr::Ipv6(Icmpv6Repr::EchoRequest { ident, .. })) |
(&Endpoint::Ident(bound_ident),
&IcmpRepr::Ipv6(Icmpv6Repr::EchoReply { ident, .. })) =>
ident == bound_ident,
(
&Endpoint::Ident(bound_ident),
&IcmpRepr::Ipv6(Icmpv6Repr::EchoRequest { ident, .. }),
)
| (
&Endpoint::Ident(bound_ident),
&IcmpRepr::Ipv6(Icmpv6Repr::EchoReply { ident, .. }),
) => ident == bound_ident,
_ => false,
}
}
pub(crate) fn process(&mut self, _cx: &Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) -> Result<()> {
pub(crate) fn process(
&mut self,
_cx: &Context,
ip_repr: &IpRepr,
icmp_repr: &IcmpRepr,
) -> Result<()> {
match *icmp_repr {
#[cfg(feature = "proto-ipv4")]
IcmpRepr::Ipv4(ref icmp_repr) => {
let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(),
ip_repr.src_addr())?;
icmp_repr.emit(&mut Icmpv4Packet::new_unchecked(packet_buf),
&ChecksumCapabilities::default());
let packet_buf = self
.rx_buffer
.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?;
icmp_repr.emit(
&mut Icmpv4Packet::new_unchecked(packet_buf),
&ChecksumCapabilities::default(),
);
net_trace!("{}:{}: receiving {} octets",
self.meta.handle, icmp_repr.buffer_len(), packet_buf.len());
},
net_trace!(
"{}:{}: receiving {} octets",
self.meta.handle,
icmp_repr.buffer_len(),
packet_buf.len()
);
}
#[cfg(feature = "proto-ipv6")]
IcmpRepr::Ipv6(ref icmp_repr) => {
let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(),
ip_repr.src_addr())?;
icmp_repr.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(),
&mut Icmpv6Packet::new_unchecked(packet_buf),
&ChecksumCapabilities::default());
let packet_buf = self
.rx_buffer
.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?;
icmp_repr.emit(
&ip_repr.src_addr(),
&ip_repr.dst_addr(),
&mut Icmpv6Packet::new_unchecked(packet_buf),
&ChecksumCapabilities::default(),
);
net_trace!("{}:{}: receiving {} octets",
self.meta.handle, icmp_repr.buffer_len(), packet_buf.len());
},
net_trace!(
"{}:{}: receiving {} octets",
self.meta.handle,
icmp_repr.buffer_len(),
packet_buf.len()
);
}
}
#[cfg(feature = "async")]
@ -400,42 +451,52 @@ impl<'a> IcmpSocket<'a> {
}
pub(crate) fn dispatch<F>(&mut self, _cx: &Context, emit: F) -> Result<()>
where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>
where
F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>,
{
let handle = self.meta.handle;
let handle = self.meta.handle;
let hop_limit = self.hop_limit.unwrap_or(64);
self.tx_buffer.dequeue_with(|remote_endpoint, packet_buf| {
net_trace!("{}:{}: sending {} octets",
handle, remote_endpoint, packet_buf.len());
net_trace!(
"{}:{}: sending {} octets",
handle,
remote_endpoint,
packet_buf.len()
);
match *remote_endpoint {
#[cfg(feature = "proto-ipv4")]
IpAddress::Ipv4(ipv4_addr) => {
let packet = Icmpv4Packet::new_unchecked(&*packet_buf);
let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?;
let ip_repr = IpRepr::Ipv4(Ipv4Repr {
src_addr: Ipv4Address::default(),
dst_addr: ipv4_addr,
protocol: IpProtocol::Icmp,
src_addr: Ipv4Address::default(),
dst_addr: ipv4_addr,
protocol: IpProtocol::Icmp,
payload_len: repr.buffer_len(),
hop_limit: hop_limit,
hop_limit: hop_limit,
});
emit((ip_repr, IcmpRepr::Ipv4(repr)))
},
}
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(ipv6_addr) => {
let packet = Icmpv6Packet::new_unchecked(&*packet_buf);
let src_addr = Ipv6Address::default();
let repr = Icmpv6Repr::parse(&src_addr.into(), &ipv6_addr.into(), &packet, &ChecksumCapabilities::ignored())?;
let repr = Icmpv6Repr::parse(
&src_addr.into(),
&ipv6_addr.into(),
&packet,
&ChecksumCapabilities::ignored(),
)?;
let ip_repr = IpRepr::Ipv6(Ipv6Repr {
src_addr: src_addr,
dst_addr: ipv6_addr,
src_addr: src_addr,
dst_addr: ipv6_addr,
next_header: IpProtocol::Icmpv6,
payload_len: repr.buffer_len(),
hop_limit: hop_limit,
hop_limit: hop_limit,
});
emit((ip_repr, IcmpRepr::Ipv6(repr)))
},
_ => Err(Error::Unaddressable)
}
_ => Err(Error::Unaddressable),
}
})?;
@ -462,20 +523,25 @@ impl<'a> Into<Socket<'a>> for IcmpSocket<'a> {
#[cfg(test)]
mod tests_common {
pub use super::*;
pub use crate::phy::DeviceCapabilities;
pub use crate::wire::IpAddress;
pub use super::*;
pub fn buffer(packets: usize) -> IcmpSocketBuffer<'static> {
IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY; packets], vec![0; 66 * packets])
IcmpSocketBuffer::new(
vec![IcmpPacketMetadata::EMPTY; packets],
vec![0; 66 * packets],
)
}
pub fn socket(rx_buffer: IcmpSocketBuffer<'static>,
tx_buffer: IcmpSocketBuffer<'static>) -> IcmpSocket<'static> {
pub fn socket(
rx_buffer: IcmpSocketBuffer<'static>,
tx_buffer: IcmpSocketBuffer<'static>,
) -> IcmpSocket<'static> {
IcmpSocket::new(rx_buffer, tx_buffer)
}
pub const LOCAL_PORT: u16 = 53;
pub const LOCAL_PORT: u16 = 53;
pub static UDP_REPR: UdpRepr = UdpRepr {
src_port: 53,
@ -492,13 +558,16 @@ mod test_ipv4 {
use crate::wire::Icmpv4DstUnreachable;
const REMOTE_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x02]);
const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]);
const LOCAL_END_V4: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv4(LOCAL_IPV4), port: LOCAL_PORT };
const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]);
const LOCAL_END_V4: IpEndpoint = IpEndpoint {
addr: IpAddress::Ipv4(LOCAL_IPV4),
port: LOCAL_PORT,
};
static ECHOV4_REPR: Icmpv4Repr = Icmpv4Repr::EchoRequest {
ident: 0x1234,
seq_no: 0x5678,
data: &[0xff; 16]
ident: 0x1234,
seq_no: 0x5678,
data: &[0xff; 16],
};
static LOCAL_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
@ -506,7 +575,7 @@ mod test_ipv4 {
dst_addr: REMOTE_IPV4,
protocol: IpProtocol::Icmp,
payload_len: 24,
hop_limit: 0x40
hop_limit: 0x40,
});
static REMOTE_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
@ -514,14 +583,16 @@ mod test_ipv4 {
dst_addr: LOCAL_IPV4,
protocol: IpProtocol::Icmp,
payload_len: 24,
hop_limit: 0x40
hop_limit: 0x40,
});
#[test]
fn test_send_unaddressable() {
let mut socket = socket(buffer(0), buffer(1));
assert_eq!(socket.send_slice(b"abcdef", IpAddress::default()),
Err(Error::Unaddressable));
assert_eq!(
socket.send_slice(b"abcdef", IpAddress::default()),
Err(Error::Unaddressable)
);
assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV4.into()), Ok(()));
}
@ -530,34 +601,51 @@ mod test_ipv4 {
let mut socket = socket(buffer(0), buffer(1));
let checksum = ChecksumCapabilities::default();
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted));
assert_eq!(
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted)
);
// This buffer is too long
assert_eq!(socket.send_slice(&[0xff; 67], REMOTE_IPV4.into()), Err(Error::Truncated));
assert_eq!(
socket.send_slice(&[0xff; 67], REMOTE_IPV4.into()),
Err(Error::Truncated)
);
assert!(socket.can_send());
let mut bytes = [0xff; 24];
let mut packet = Icmpv4Packet::new_unchecked(&mut bytes);
ECHOV4_REPR.emit(&mut packet, &checksum);
assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(()));
assert_eq!(socket.send_slice(b"123456", REMOTE_IPV4.into()), Err(Error::Exhausted));
assert_eq!(
socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()),
Ok(())
);
assert_eq!(
socket.send_slice(b"123456", REMOTE_IPV4.into()),
Err(Error::Exhausted)
);
assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV4_REPR);
assert_eq!(icmp_repr, ECHOV4_REPR.into());
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV4_REPR);
assert_eq!(icmp_repr, ECHOV4_REPR.into());
Err(Error::Unaddressable)
}),
Err(Error::Unaddressable)
}), Err(Error::Unaddressable));
);
// buffer is not taken off of the tx queue due to the error
assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV4_REPR);
assert_eq!(icmp_repr, ECHOV4_REPR.into());
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV4_REPR);
assert_eq!(icmp_repr, ECHOV4_REPR.into());
Ok(())
}),
Ok(())
}), Ok(()));
);
// buffer is taken off of the queue this time
assert!(socket.can_send());
}
@ -573,17 +661,26 @@ mod test_ipv4 {
s.set_hop_limit(Some(0x2a));
assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(()));
assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr {
src_addr: Ipv4Address::UNSPECIFIED,
dst_addr: REMOTE_IPV4,
protocol: IpProtocol::Icmp,
payload_len: ECHOV4_REPR.buffer_len(),
hop_limit: 0x2a,
}));
assert_eq!(
s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()),
Ok(())
}), Ok(()));
);
assert_eq!(
s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
assert_eq!(
ip_repr,
IpRepr::Ipv4(Ipv4Repr {
src_addr: Ipv4Address::UNSPECIFIED,
dst_addr: REMOTE_IPV4,
protocol: IpProtocol::Icmp,
payload_len: ECHOV4_REPR.buffer_len(),
hop_limit: 0x2a,
})
);
Ok(())
}),
Ok(())
);
}
#[test]
@ -602,13 +699,17 @@ mod test_ipv4 {
let data = &packet.into_inner()[..];
assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()));
assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
Ok(()));
assert_eq!(
socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
Ok(())
);
assert!(socket.can_recv());
assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()));
assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
Err(Error::Exhausted));
assert_eq!(
socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
Err(Error::Exhausted)
);
assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV4.into())));
assert!(!socket.can_recv());
@ -623,9 +724,9 @@ mod test_ipv4 {
let mut bytes = [0xff; 20];
let mut packet = Icmpv4Packet::new_unchecked(&mut bytes);
let icmp_repr = Icmpv4Repr::EchoRequest {
ident: 0x4321,
ident: 0x4321,
seq_no: 0x5678,
data: &[0xff; 16]
data: &[0xff; 16],
};
icmp_repr.emit(&mut packet, &checksum);
@ -649,7 +750,8 @@ mod test_ipv4 {
&LOCAL_IPV4.into(),
UDP_PAYLOAD.len(),
|buf| buf.copy_from_slice(UDP_PAYLOAD),
&checksum);
&checksum,
);
let data = &packet.into_inner()[..];
@ -660,16 +762,16 @@ mod test_ipv4 {
dst_addr: REMOTE_IPV4,
protocol: IpProtocol::Icmp,
payload_len: 12,
hop_limit: 0x40
hop_limit: 0x40,
},
data: data
data: data,
};
let ip_repr = IpRepr::Unspecified {
src_addr: REMOTE_IPV4.into(),
dst_addr: LOCAL_IPV4.into(),
protocol: IpProtocol::Icmp,
payload_len: icmp_repr.buffer_len(),
hop_limit: 0x40
hop_limit: 0x40,
};
assert!(!socket.can_recv());
@ -677,14 +779,19 @@ mod test_ipv4 {
// Ensure we can accept ICMP error response to the bound
// UDP port
assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into()));
assert_eq!(socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
Ok(()));
assert_eq!(
socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
Ok(())
);
assert!(socket.can_recv());
let mut bytes = [0x00; 46];
let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]);
icmp_repr.emit(&mut packet, &checksum);
assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV4.into())));
assert_eq!(
socket.recv(),
Ok((&packet.into_inner()[..], REMOTE_IPV4.into()))
);
assert!(!socket.can_recv());
}
}
@ -695,15 +802,18 @@ mod test_ipv6 {
use crate::wire::Icmpv6DstUnreachable;
const REMOTE_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1]);
const LOCAL_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2]);
const LOCAL_END_V6: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv6(LOCAL_IPV6), port: LOCAL_PORT };
const REMOTE_IPV6: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
const LOCAL_IPV6: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
const LOCAL_END_V6: IpEndpoint = IpEndpoint {
addr: IpAddress::Ipv6(LOCAL_IPV6),
port: LOCAL_PORT,
};
static ECHOV6_REPR: Icmpv6Repr = Icmpv6Repr::EchoRequest {
ident: 0x1234,
seq_no: 0x5678,
data: &[0xff; 16]
ident: 0x1234,
seq_no: 0x5678,
data: &[0xff; 16],
};
static LOCAL_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
@ -711,7 +821,7 @@ mod test_ipv6 {
dst_addr: REMOTE_IPV6,
next_header: IpProtocol::Icmpv6,
payload_len: 24,
hop_limit: 0x40
hop_limit: 0x40,
});
static REMOTE_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
@ -719,14 +829,16 @@ mod test_ipv6 {
dst_addr: LOCAL_IPV6,
next_header: IpProtocol::Icmpv6,
payload_len: 24,
hop_limit: 0x40
hop_limit: 0x40,
});
#[test]
fn test_send_unaddressable() {
let mut socket = socket(buffer(0), buffer(1));
assert_eq!(socket.send_slice(b"abcdef", IpAddress::default()),
Err(Error::Unaddressable));
assert_eq!(
socket.send_slice(b"abcdef", IpAddress::default()),
Err(Error::Unaddressable)
);
assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV6.into()), Ok(()));
}
@ -735,34 +847,56 @@ mod test_ipv6 {
let mut socket = socket(buffer(0), buffer(1));
let checksum = ChecksumCapabilities::default();
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted));
assert_eq!(
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted)
);
// This buffer is too long
assert_eq!(socket.send_slice(&[0xff; 67], REMOTE_IPV6.into()), Err(Error::Truncated));
assert_eq!(
socket.send_slice(&[0xff; 67], REMOTE_IPV6.into()),
Err(Error::Truncated)
);
assert!(socket.can_send());
let mut bytes = vec![0xff; 24];
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
ECHOV6_REPR.emit(
&LOCAL_IPV6.into(),
&REMOTE_IPV6.into(),
&mut packet,
&checksum,
);
assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()));
assert_eq!(socket.send_slice(b"123456", REMOTE_IPV6.into()), Err(Error::Exhausted));
assert_eq!(
socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()),
Ok(())
);
assert_eq!(
socket.send_slice(b"123456", REMOTE_IPV6.into()),
Err(Error::Exhausted)
);
assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV6_REPR);
assert_eq!(icmp_repr, ECHOV6_REPR.into());
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV6_REPR);
assert_eq!(icmp_repr, ECHOV6_REPR.into());
Err(Error::Unaddressable)
}),
Err(Error::Unaddressable)
}), Err(Error::Unaddressable));
);
// buffer is not taken off of the tx queue due to the error
assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV6_REPR);
assert_eq!(icmp_repr, ECHOV6_REPR.into());
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV6_REPR);
assert_eq!(icmp_repr, ECHOV6_REPR.into());
Ok(())
}),
Ok(())
}), Ok(()));
);
// buffer is taken off of the queue this time
assert!(socket.can_send());
}
@ -774,21 +908,35 @@ mod test_ipv6 {
let mut bytes = vec![0xff; 24];
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
ECHOV6_REPR.emit(
&LOCAL_IPV6.into(),
&REMOTE_IPV6.into(),
&mut packet,
&checksum,
);
s.set_hop_limit(Some(0x2a));
assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()));
assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
assert_eq!(ip_repr, IpRepr::Ipv6(Ipv6Repr {
src_addr: Ipv6Address::UNSPECIFIED,
dst_addr: REMOTE_IPV6,
next_header: IpProtocol::Icmpv6,
payload_len: ECHOV6_REPR.buffer_len(),
hop_limit: 0x2a,
}));
assert_eq!(
s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()),
Ok(())
}), Ok(()));
);
assert_eq!(
s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
assert_eq!(
ip_repr,
IpRepr::Ipv6(Ipv6Repr {
src_addr: Ipv6Address::UNSPECIFIED,
dst_addr: REMOTE_IPV6,
next_header: IpProtocol::Icmpv6,
payload_len: ECHOV6_REPR.buffer_len(),
hop_limit: 0x2a,
})
);
Ok(())
}),
Ok(())
);
}
#[test]
@ -803,17 +951,26 @@ mod test_ipv6 {
let mut bytes = [0xff; 24];
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
ECHOV6_REPR.emit(
&LOCAL_IPV6.into(),
&REMOTE_IPV6.into(),
&mut packet,
&checksum,
);
let data = &packet.into_inner()[..];
assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()));
assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
Ok(()));
assert_eq!(
socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
Ok(())
);
assert!(socket.can_recv());
assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()));
assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
Err(Error::Exhausted));
assert_eq!(
socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
Err(Error::Exhausted)
);
assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV6.into())));
assert!(!socket.can_recv());
@ -828,11 +985,16 @@ mod test_ipv6 {
let mut bytes = [0xff; 20];
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
let icmp_repr = Icmpv6Repr::EchoRequest {
ident: 0x4321,
ident: 0x4321,
seq_no: 0x5678,
data: &[0xff; 16]
data: &[0xff; 16],
};
icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
icmp_repr.emit(
&LOCAL_IPV6.into(),
&REMOTE_IPV6.into(),
&mut packet,
&checksum,
);
// Ensure that a packet with an identifier that isn't the bound
// ID is not accepted
@ -854,7 +1016,8 @@ mod test_ipv6 {
&LOCAL_IPV6.into(),
UDP_PAYLOAD.len(),
|buf| buf.copy_from_slice(UDP_PAYLOAD),
&checksum);
&checksum,
);
let data = &packet.into_inner()[..];
@ -865,16 +1028,16 @@ mod test_ipv6 {
dst_addr: REMOTE_IPV6,
next_header: IpProtocol::Icmpv6,
payload_len: 12,
hop_limit: 0x40
hop_limit: 0x40,
},
data: data
data: data,
};
let ip_repr = IpRepr::Unspecified {
src_addr: REMOTE_IPV6.into(),
dst_addr: LOCAL_IPV6.into(),
protocol: IpProtocol::Icmpv6,
payload_len: icmp_repr.buffer_len(),
hop_limit: 0x40
hop_limit: 0x40,
};
assert!(!socket.can_recv());
@ -882,14 +1045,24 @@ mod test_ipv6 {
// Ensure we can accept ICMP error response to the bound
// UDP port
assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into()));
assert_eq!(socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
Ok(()));
assert_eq!(
socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
Ok(())
);
assert!(socket.can_recv());
let mut bytes = [0x00; 66];
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes[..]);
icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV6.into())));
icmp_repr.emit(
&LOCAL_IPV6.into(),
&REMOTE_IPV6.into(),
&mut packet,
&checksum,
);
assert_eq!(
socket.recv(),
Ok((&packet.into_inner()[..], REMOTE_IPV6.into()))
);
assert!(!socket.can_recv());
}
}

View File

@ -1,6 +1,6 @@
use crate::wire::IpAddress;
use crate::socket::{SocketHandle, PollAt};
use crate::socket::{PollAt, SocketHandle};
use crate::time::{Duration, Instant};
use crate::wire::IpAddress;
/// Neighbor dependency.
///
@ -16,7 +16,7 @@ enum NeighborState {
Waiting {
neighbor: IpAddress,
silent_until: Instant,
}
},
}
impl Default for NeighborState {
@ -36,7 +36,7 @@ pub struct Meta {
/// Mainly useful for debug output.
pub(crate) handle: SocketHandle,
/// See [NeighborState](struct.NeighborState.html).
neighbor_state: NeighborState,
neighbor_state: NeighborState,
}
impl Meta {
@ -47,33 +47,40 @@ impl Meta {
pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration { millis: 3_000 };
pub(crate) fn poll_at<F>(&self, socket_poll_at: PollAt, has_neighbor: F) -> PollAt
where F: Fn(IpAddress) -> bool
where
F: Fn(IpAddress) -> bool,
{
match self.neighbor_state {
NeighborState::Active =>
socket_poll_at,
NeighborState::Waiting { neighbor, .. }
if has_neighbor(neighbor) =>
socket_poll_at,
NeighborState::Waiting { silent_until, .. } =>
PollAt::Time(silent_until)
NeighborState::Active => socket_poll_at,
NeighborState::Waiting { neighbor, .. } if has_neighbor(neighbor) => socket_poll_at,
NeighborState::Waiting { silent_until, .. } => PollAt::Time(silent_until),
}
}
pub(crate) fn egress_permitted<F>(&mut self, timestamp: Instant, has_neighbor: F) -> bool
where F: Fn(IpAddress) -> bool
where
F: Fn(IpAddress) -> bool,
{
match self.neighbor_state {
NeighborState::Active =>
true,
NeighborState::Waiting { neighbor, silent_until } => {
NeighborState::Active => true,
NeighborState::Waiting {
neighbor,
silent_until,
} => {
if has_neighbor(neighbor) {
net_trace!("{}: neighbor {} discovered, unsilencing",
self.handle, neighbor);
net_trace!(
"{}: neighbor {} discovered, unsilencing",
self.handle,
neighbor
);
self.neighbor_state = NeighborState::Active;
true
} else if timestamp >= silent_until {
net_trace!("{}: neighbor {} silence timer expired, rediscovering", self.handle, neighbor);
net_trace!(
"{}: neighbor {} silence timer expired, rediscovering",
self.handle,
neighbor
);
true
} else {
false
@ -83,10 +90,15 @@ impl Meta {
}
pub(crate) fn neighbor_missing(&mut self, timestamp: Instant, neighbor: IpAddress) {
net_trace!("{}: neighbor {} missing, silencing until t+{}",
self.handle, neighbor, Self::DISCOVERY_SILENT_TIME);
net_trace!(
"{}: neighbor {} missing, silencing until t+{}",
self.handle,
neighbor,
Self::DISCOVERY_SILENT_TIME
);
self.neighbor_state = NeighborState::Waiting {
neighbor, silent_until: timestamp + Self::DISCOVERY_SILENT_TIME
neighbor,
silent_until: timestamp + Self::DISCOVERY_SILENT_TIME,
};
}
}

View File

@ -14,19 +14,22 @@ size for a buffer, allocate it, and let the networking stack use it.
use crate::phy::DeviceCapabilities;
use crate::time::Instant;
#[cfg(feature = "socket-dhcpv4")]
mod dhcpv4;
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
mod icmp;
mod meta;
#[cfg(feature = "socket-raw")]
mod raw;
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
mod icmp;
#[cfg(feature = "socket-udp")]
mod udp;
mod ref_;
mod set;
#[cfg(feature = "socket-tcp")]
mod tcp;
#[cfg(feature = "socket-dhcpv4")]
mod dhcpv4;
mod set;
mod ref_;
#[cfg(feature = "socket-udp")]
mod udp;
#[cfg(feature = "async")]
mod waker;
@ -36,30 +39,24 @@ pub(crate) use self::meta::Meta as SocketMeta;
pub(crate) use self::waker::WakerRegistration;
#[cfg(feature = "socket-raw")]
pub use self::raw::{RawPacketMetadata,
RawSocketBuffer,
RawSocket};
pub use self::raw::{RawPacketMetadata, RawSocket, RawSocketBuffer};
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
pub use self::icmp::{IcmpPacketMetadata,
IcmpSocketBuffer,
Endpoint as IcmpEndpoint,
IcmpSocket};
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer};
#[cfg(feature = "socket-udp")]
pub use self::udp::{UdpPacketMetadata,
UdpSocketBuffer,
UdpSocket};
pub use self::udp::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
#[cfg(feature = "socket-tcp")]
pub use self::tcp::{SocketBuffer as TcpSocketBuffer,
State as TcpState,
TcpSocket};
pub use self::tcp::{SocketBuffer as TcpSocketBuffer, State as TcpState, TcpSocket};
#[cfg(feature = "socket-dhcpv4")]
pub use self::dhcpv4::{Dhcpv4Socket, Config as Dhcpv4Config, Event as Dhcpv4Event};
pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event};
pub use self::set::{Set as SocketSet, Item as SocketSetItem, Handle as SocketHandle};
pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet};
pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut};
pub use self::ref_::Ref as SocketRef;
@ -91,7 +88,10 @@ pub(crate) enum PollAt {
pub enum Socket<'a> {
#[cfg(feature = "socket-raw")]
Raw(RawSocket<'a>),
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
Icmp(IcmpSocket<'a>),
#[cfg(feature = "socket-udp")]
Udp(UdpSocket<'a>),
@ -152,15 +152,13 @@ impl<'a> SocketSession for Socket<'a> {
/// A conversion trait for network sockets.
pub trait AnySocket<'a>: SocketSession + Sized {
fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) ->
Option<SocketRef<'c, Self>>;
fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) -> Option<SocketRef<'c, Self>>;
}
macro_rules! from_socket {
($socket:ty, $variant:ident) => {
impl<'a> AnySocket<'a> for $socket {
fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) ->
Option<SocketRef<'c, Self>> {
fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> Option<SocketRef<'c, Self>> {
if let Socket::$variant(ref mut socket) = SocketRef::into_inner(ref_) {
Some(SocketRef::new(socket))
} else {
@ -168,12 +166,15 @@ macro_rules! from_socket {
}
}
}
}
};
}
#[cfg(feature = "socket-raw")]
from_socket!(RawSocket<'a>, Raw);
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
from_socket!(IcmpSocket<'a>, Icmp);
#[cfg(feature = "socket-udp")]
from_socket!(UdpSocket<'a>, Udp);
@ -193,14 +194,13 @@ pub(crate) struct Context {
#[cfg(test)]
impl Context {
pub(crate) const DUMMY: Context = Context {
caps: DeviceCapabilities {
#[cfg(feature = "medium-ethernet")]
medium: crate::phy::Medium::Ethernet,
#[cfg(not(feature = "medium-ethernet"))]
medium: crate::phy::Medium::Ip,
checksum: crate::phy::ChecksumCapabilities{
checksum: crate::phy::ChecksumCapabilities {
#[cfg(feature = "proto-ipv4")]
icmpv4: crate::phy::Checksum::Both,
#[cfg(feature = "proto-ipv6")]
@ -216,7 +216,6 @@ impl Context {
max_transmission_unit: 1500,
},
ethernet_address: None,
now: Instant{millis: 0},
now: Instant { millis: 0 },
};
}

View File

@ -2,18 +2,18 @@ use core::cmp::min;
#[cfg(feature = "async")]
use core::task::Waker;
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
use crate::storage::{PacketBuffer, PacketMetadata};
#[cfg(feature = "async")]
use crate::socket::WakerRegistration;
use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta};
use crate::storage::{PacketBuffer, PacketMetadata};
use crate::{Error, Result};
use crate::wire::{IpVersion, IpRepr, IpProtocol};
use crate::wire::{IpProtocol, IpRepr, IpVersion};
#[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Repr, Ipv4Packet};
use crate::wire::{Ipv4Packet, Ipv4Repr};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Repr, Ipv6Packet};
use crate::wire::{Ipv6Packet, Ipv6Repr};
/// A UDP packet metadata.
pub type RawPacketMetadata = PacketMetadata<()>;
@ -28,10 +28,10 @@ pub type RawSocketBuffer<'a> = PacketBuffer<'a, ()>;
#[derive(Debug)]
pub struct RawSocket<'a> {
pub(crate) meta: SocketMeta,
ip_version: IpVersion,
ip_version: IpVersion,
ip_protocol: IpProtocol,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>,
#[cfg(feature = "async")]
rx_waker: WakerRegistration,
#[cfg(feature = "async")]
@ -41,9 +41,12 @@ pub struct RawSocket<'a> {
impl<'a> RawSocket<'a> {
/// Create a raw IP socket bound to the given IP version and datagram protocol,
/// with the given buffers.
pub fn new(ip_version: IpVersion, ip_protocol: IpProtocol,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>) -> RawSocket<'a> {
pub fn new(
ip_version: IpVersion,
ip_protocol: IpProtocol,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>,
) -> RawSocket<'a> {
RawSocket {
meta: SocketMeta::default(),
ip_version,
@ -160,9 +163,13 @@ impl<'a> RawSocket<'a> {
pub fn send(&mut self, size: usize) -> Result<&mut [u8]> {
let packet_buf = self.tx_buffer.enqueue(size, ())?;
net_trace!("{}:{}:{}: buffer to send {} octets",
self.meta.handle, self.ip_version, self.ip_protocol,
packet_buf.len());
net_trace!(
"{}:{}:{}: buffer to send {} octets",
self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
Ok(packet_buf)
}
@ -183,9 +190,13 @@ impl<'a> RawSocket<'a> {
pub fn recv(&mut self) -> Result<&[u8]> {
let ((), packet_buf) = self.rx_buffer.dequeue()?;
net_trace!("{}:{}:{}: receive {} buffered octets",
self.meta.handle, self.ip_version, self.ip_protocol,
packet_buf.len());
net_trace!(
"{}:{}:{}: receive {} buffered octets",
self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
Ok(packet_buf)
}
@ -200,8 +211,12 @@ impl<'a> RawSocket<'a> {
}
pub(crate) fn accepts(&self, ip_repr: &IpRepr) -> bool {
if ip_repr.version() != self.ip_version { return false }
if ip_repr.protocol() != self.ip_protocol { return false }
if ip_repr.version() != self.ip_version {
return false;
}
if ip_repr.protocol() != self.ip_protocol {
return false;
}
true
}
@ -210,14 +225,18 @@ impl<'a> RawSocket<'a> {
debug_assert!(self.accepts(ip_repr));
let header_len = ip_repr.buffer_len();
let total_len = header_len + payload.len();
let total_len = header_len + payload.len();
let packet_buf = self.rx_buffer.enqueue(total_len, ())?;
ip_repr.emit(&mut packet_buf[..header_len], &cx.caps.checksum);
packet_buf[header_len..].copy_from_slice(payload);
net_trace!("{}:{}:{}: receiving {} octets",
self.meta.handle, self.ip_version, self.ip_protocol,
packet_buf.len());
net_trace!(
"{}:{}:{}: receiving {} octets",
self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
#[cfg(feature = "async")]
self.rx_waker.wake();
@ -226,14 +245,21 @@ impl<'a> RawSocket<'a> {
}
pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
where F: FnOnce((IpRepr, &[u8])) -> Result<()> {
fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8],
_checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> {
where
F: FnOnce((IpRepr, &[u8])) -> Result<()>,
{
fn prepare<'a>(
protocol: IpProtocol,
buffer: &'a mut [u8],
_checksum_caps: &ChecksumCapabilities,
) -> Result<(IpRepr, &'a [u8])> {
match IpVersion::of_packet(buffer)? {
#[cfg(feature = "proto-ipv4")]
IpVersion::Ipv4 => {
let mut packet = Ipv4Packet::new_checked(buffer)?;
if packet.protocol() != protocol { return Err(Error::Unaddressable) }
if packet.protocol() != protocol {
return Err(Error::Unaddressable);
}
if _checksum_caps.ipv4.tx() {
packet.fill_checksum();
} else {
@ -249,7 +275,9 @@ impl<'a> RawSocket<'a> {
#[cfg(feature = "proto-ipv6")]
IpVersion::Ipv6 => {
let packet = Ipv6Packet::new_checked(buffer)?;
if packet.next_header() != protocol { return Err(Error::Unaddressable) }
if packet.next_header() != protocol {
return Err(Error::Unaddressable);
}
let packet = Ipv6Packet::new_unchecked(&*packet.into_inner());
let ipv6_repr = Ipv6Repr::parse(&packet)?;
Ok((IpRepr::Ipv6(ipv6_repr), packet.payload()))
@ -258,21 +286,29 @@ impl<'a> RawSocket<'a> {
}
}
let handle = self.meta.handle;
let handle = self.meta.handle;
let ip_protocol = self.ip_protocol;
let ip_version = self.ip_version;
let ip_version = self.ip_version;
self.tx_buffer.dequeue_with(|&mut (), packet_buf| {
match prepare(ip_protocol, packet_buf, &cx.caps.checksum) {
Ok((ip_repr, raw_packet)) => {
net_trace!("{}:{}:{}: sending {} octets",
handle, ip_version, ip_protocol,
ip_repr.buffer_len() + raw_packet.len());
net_trace!(
"{}:{}:{}: sending {} octets",
handle,
ip_version,
ip_protocol,
ip_repr.buffer_len() + raw_packet.len()
);
emit((ip_repr, raw_packet))
}
Err(error) => {
net_debug!("{}:{}:{}: dropping outgoing packet ({})",
handle, ip_version, ip_protocol,
error);
net_debug!(
"{}:{}:{}: dropping outgoing packet ({})",
handle,
ip_version,
ip_protocol,
error
);
// Return Ok(()) so the packet is dequeued.
Ok(())
}
@ -302,26 +338,34 @@ impl<'a> Into<Socket<'a>> for RawSocket<'a> {
#[cfg(test)]
mod test {
use super::*;
use crate::wire::IpRepr;
#[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Address, Ipv4Repr};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Address, Ipv6Repr};
use super::*;
fn buffer(packets: usize) -> RawSocketBuffer<'static> {
RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets])
RawSocketBuffer::new(
vec![RawPacketMetadata::EMPTY; packets],
vec![0; 48 * packets],
)
}
#[cfg(feature = "proto-ipv4")]
mod ipv4_locals {
use super::*;
pub fn socket(rx_buffer: RawSocketBuffer<'static>,
tx_buffer: RawSocketBuffer<'static>)
-> RawSocket<'static> {
RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO),
rx_buffer, tx_buffer)
pub fn socket(
rx_buffer: RawSocketBuffer<'static>,
tx_buffer: RawSocketBuffer<'static>,
) -> RawSocket<'static> {
RawSocket::new(
IpVersion::Ipv4,
IpProtocol::Unknown(IP_PROTO),
rx_buffer,
tx_buffer,
)
}
pub const IP_PROTO: u8 = 63;
@ -330,60 +374,54 @@ mod test {
dst_addr: Ipv4Address([10, 0, 0, 2]),
protocol: IpProtocol::Unknown(IP_PROTO),
payload_len: 4,
hop_limit: 64
hop_limit: 64,
});
pub const PACKET_BYTES: [u8; 24] = [
0x45, 0x00, 0x00, 0x18,
0x00, 0x00, 0x40, 0x00,
0x40, 0x3f, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x01,
0x0a, 0x00, 0x00, 0x02,
0xaa, 0x00, 0x00, 0xff
];
pub const PACKET_PAYLOAD: [u8; 4] = [
0xaa, 0x00, 0x00, 0xff
0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x3f, 0x00, 0x00, 0x0a, 0x00,
0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00, 0xff,
];
pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
}
#[cfg(feature = "proto-ipv6")]
mod ipv6_locals {
use super::*;
pub fn socket(rx_buffer: RawSocketBuffer<'static>,
tx_buffer: RawSocketBuffer<'static>)
-> RawSocket<'static> {
RawSocket::new(IpVersion::Ipv6, IpProtocol::Unknown(IP_PROTO),
rx_buffer, tx_buffer)
pub fn socket(
rx_buffer: RawSocketBuffer<'static>,
tx_buffer: RawSocketBuffer<'static>,
) -> RawSocket<'static> {
RawSocket::new(
IpVersion::Ipv6,
IpProtocol::Unknown(IP_PROTO),
rx_buffer,
tx_buffer,
)
}
pub const IP_PROTO: u8 = 63;
pub const HEADER_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]),
dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]),
src_addr: Ipv6Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]),
dst_addr: Ipv6Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02,
]),
next_header: IpProtocol::Unknown(IP_PROTO),
payload_len: 4,
hop_limit: 64
hop_limit: 64,
});
pub const PACKET_BYTES: [u8; 44] = [
0x60, 0x00, 0x00, 0x00,
0x00, 0x04, 0x3f, 0x40,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
0xaa, 0x00, 0x00, 0xff
0x60, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3f, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xaa, 0x00,
0x00, 0xff,
];
pub const PACKET_PAYLOAD: [u8; 4] = [
0xaa, 0x00, 0x00, 0xff
];
pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
}
macro_rules! reusable_ip_specific_tests {
@ -402,25 +440,33 @@ mod test {
let mut socket = $socket(buffer(0), buffer(1));
assert!(socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted));
assert_eq!(
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted)
);
assert_eq!(socket.send_slice(&$packet[..]), Ok(()));
assert_eq!(socket.send_slice(b""), Err(Error::Exhausted));
assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
Err(Error::Unaddressable)
}),
Err(Error::Unaddressable)
}), Err(Error::Unaddressable));
);
assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
Ok(())
}),
Ok(())
}), Ok(()));
);
assert!(socket.can_send());
}
@ -444,20 +490,32 @@ mod test {
buffer[..$packet.len()].copy_from_slice(&$packet[..]);
assert!(socket.accepts(&$hdr));
assert_eq!(socket.process(&Context::DUMMY, &$hdr, &buffer), Err(Error::Truncated));
assert_eq!(
socket.process(&Context::DUMMY, &$hdr, &buffer),
Err(Error::Truncated)
);
}
}
}
};
}
#[cfg(feature = "proto-ipv4")]
reusable_ip_specific_tests!(ipv4, ipv4_locals::socket, ipv4_locals::HEADER_REPR,
ipv4_locals::PACKET_BYTES, ipv4_locals::PACKET_PAYLOAD);
reusable_ip_specific_tests!(
ipv4,
ipv4_locals::socket,
ipv4_locals::HEADER_REPR,
ipv4_locals::PACKET_BYTES,
ipv4_locals::PACKET_PAYLOAD
);
#[cfg(feature = "proto-ipv6")]
reusable_ip_specific_tests!(ipv6, ipv6_locals::socket, ipv6_locals::HEADER_REPR,
ipv6_locals::PACKET_BYTES, ipv6_locals::PACKET_PAYLOAD);
reusable_ip_specific_tests!(
ipv6,
ipv6_locals::socket,
ipv6_locals::HEADER_REPR,
ipv6_locals::PACKET_BYTES,
ipv6_locals::PACKET_PAYLOAD
);
#[test]
#[cfg(feature = "proto-ipv4")]
@ -470,15 +528,13 @@ mod test {
Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6);
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
let mut wrong_protocol = ipv4_locals::PACKET_BYTES;
Ipv4Packet::new_unchecked(&mut wrong_protocol).set_protocol(IpProtocol::Tcp);
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
}
#[cfg(feature = "proto-ipv6")]
{
@ -488,15 +544,13 @@ mod test {
Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4);
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
let mut wrong_protocol = ipv6_locals::PACKET_BYTES;
Ipv6Packet::new_unchecked(&mut wrong_protocol[..]).set_next_header(IpProtocol::Tcp);
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
}
}
@ -512,13 +566,25 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD),
Ok(()));
assert_eq!(
socket.process(
&Context::DUMMY,
&ipv4_locals::HEADER_REPR,
&ipv4_locals::PACKET_PAYLOAD
),
Ok(())
);
assert!(socket.can_recv());
assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD),
Err(Error::Exhausted));
assert_eq!(
socket.process(
&Context::DUMMY,
&ipv4_locals::HEADER_REPR,
&ipv4_locals::PACKET_PAYLOAD
),
Err(Error::Exhausted)
);
assert_eq!(socket.recv(), Ok(&cksumd_packet[..]));
assert!(!socket.can_recv());
}
@ -529,13 +595,25 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD),
Ok(()));
assert_eq!(
socket.process(
&Context::DUMMY,
&ipv6_locals::HEADER_REPR,
&ipv6_locals::PACKET_PAYLOAD
),
Ok(())
);
assert!(socket.can_recv());
assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD),
Err(Error::Exhausted));
assert_eq!(
socket.process(
&Context::DUMMY,
&ipv6_locals::HEADER_REPR,
&ipv6_locals::PACKET_PAYLOAD
),
Err(Error::Exhausted)
);
assert_eq!(socket.recv(), Ok(&ipv6_locals::PACKET_BYTES[..]));
assert!(!socket.can_recv());
}
@ -545,16 +623,24 @@ mod test {
fn test_doesnt_accept_wrong_proto() {
#[cfg(feature = "proto-ipv4")]
{
let socket = RawSocket::new(IpVersion::Ipv4,
IpProtocol::Unknown(ipv4_locals::IP_PROTO+1), buffer(1), buffer(1));
let socket = RawSocket::new(
IpVersion::Ipv4,
IpProtocol::Unknown(ipv4_locals::IP_PROTO + 1),
buffer(1),
buffer(1),
);
assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));
#[cfg(feature = "proto-ipv6")]
assert!(!socket.accepts(&ipv6_locals::HEADER_REPR));
}
#[cfg(feature = "proto-ipv6")]
{
let socket = RawSocket::new(IpVersion::Ipv6,
IpProtocol::Unknown(ipv6_locals::IP_PROTO+1), buffer(1), buffer(1));
let socket = RawSocket::new(
IpVersion::Ipv6,
IpProtocol::Unknown(ipv6_locals::IP_PROTO + 1),
buffer(1),
buffer(1),
);
assert!(!socket.accepts(&ipv6_locals::HEADER_REPR));
#[cfg(feature = "proto-ipv4")]
assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));

View File

@ -1,6 +1,5 @@
use core::ops::{Deref, DerefMut};
/// A trait for tracking a socket usage session.
///
/// Allows implementation of custom drop logic that runs only if the socket was changed
@ -13,7 +12,10 @@ pub trait Session {
#[cfg(feature = "socket-raw")]
impl<'a> Session for crate::socket::RawSocket<'a> {}
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
impl<'a> Session for crate::socket::IcmpSocket<'a> {}
#[cfg(feature = "socket-udp")]
impl<'a> Session for crate::socket::UdpSocket<'a> {}
@ -41,7 +43,9 @@ impl<'a, T: Session + 'a> Ref<'a, T> {
///
/// [into_inner]: #method.into_inner
pub fn new(socket: &'a mut T) -> Self {
Ref { socket: Some(socket) }
Ref {
socket: Some(socket),
}
}
/// Unwrap a smart pointer to a socket.

View File

@ -1,9 +1,9 @@
use core::{fmt, slice};
use managed::ManagedSlice;
use crate::socket::{Socket, SocketRef, AnySocket};
#[cfg(feature = "socket-tcp")]
use crate::socket::TcpState;
use crate::socket::{AnySocket, Socket, SocketRef};
/// An item of a socket set.
///
@ -12,7 +12,7 @@ use crate::socket::TcpState;
#[derive(Debug)]
pub struct Item<'a> {
socket: Socket<'a>,
refs: usize
refs: usize,
}
/// A handle, identifying a socket in a set.
@ -31,13 +31,15 @@ impl fmt::Display for Handle {
/// The lifetime `'a` is used when storing a `Socket<'a>`.
#[derive(Debug)]
pub struct Set<'a> {
sockets: ManagedSlice<'a, Option<Item<'a>>>
sockets: ManagedSlice<'a, Option<Item<'a>>>,
}
impl<'a> Set<'a> {
/// Create a socket set using the provided storage.
pub fn new<SocketsT>(sockets: SocketsT) -> Set<'a>
where SocketsT: Into<ManagedSlice<'a, Option<Item<'a>>>> {
where
SocketsT: Into<ManagedSlice<'a, Option<Item<'a>>>>,
{
let sockets = sockets.into();
Set { sockets }
}
@ -47,10 +49,10 @@ impl<'a> Set<'a> {
/// # Panics
/// This function panics if the storage is fixed-size (not a `Vec`) and is full.
pub fn add<T>(&mut self, socket: T) -> Handle
where T: Into<Socket<'a>>
where
T: Into<Socket<'a>>,
{
fn put<'a>(index: usize, slot: &mut Option<Item<'a>>,
mut socket: Socket<'a>) -> Handle {
fn put<'a>(index: usize, slot: &mut Option<Item<'a>>, mut socket: Socket<'a>) -> Handle {
net_trace!("[{}]: adding", index);
let handle = Handle(index);
socket.meta_mut().handle = handle;
@ -62,7 +64,7 @@ impl<'a> Set<'a> {
for (index, slot) in self.sockets.iter_mut().enumerate() {
if slot.is_none() {
return put(index, slot, socket)
return put(index, slot, socket);
}
}
@ -86,11 +88,9 @@ impl<'a> Set<'a> {
/// or the socket has the wrong type.
pub fn get<T: AnySocket<'a>>(&mut self, handle: Handle) -> SocketRef<T> {
match self.sockets[handle.0].as_mut() {
Some(item) => {
T::downcast(SocketRef::new(&mut item.socket))
.expect("handle refers to a socket of a wrong type")
}
None => panic!("handle does not refer to a valid socket")
Some(item) => T::downcast(SocketRef::new(&mut item.socket))
.expect("handle refers to a socket of a wrong type"),
None => panic!("handle does not refer to a valid socket"),
}
}
@ -102,7 +102,7 @@ impl<'a> Set<'a> {
net_trace!("[{}]: removing", handle.0);
match self.sockets[handle.0].take() {
Some(item) => item.socket,
None => panic!("handle does not refer to a valid socket")
None => panic!("handle does not refer to a valid socket"),
}
}
@ -124,10 +124,12 @@ impl<'a> Set<'a> {
/// or if the reference count is already zero.
pub fn release(&mut self, handle: Handle) {
let refs = &mut self.sockets[handle.0]
.as_mut()
.expect("handle does not refer to a valid socket")
.refs;
if *refs == 0 { panic!("decreasing reference count past zero") }
.as_mut()
.expect("handle does not refer to a valid socket")
.refs;
if *refs == 0 {
panic!("decreasing reference count past zero")
}
*refs -= 1
}
@ -138,27 +140,31 @@ impl<'a> Set<'a> {
pub fn prune(&mut self) {
for (index, item) in self.sockets.iter_mut().enumerate() {
let mut may_remove = false;
if let Some(Item { refs: 0, ref mut socket }) = *item {
if let Some(Item {
refs: 0,
ref mut socket,
}) = *item
{
match *socket {
#[cfg(feature = "socket-raw")]
Socket::Raw(_) =>
may_remove = true,
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
Socket::Icmp(_) =>
may_remove = true,
Socket::Raw(_) => may_remove = true,
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
Socket::Icmp(_) => may_remove = true,
#[cfg(feature = "socket-udp")]
Socket::Udp(_) =>
may_remove = true,
Socket::Udp(_) => may_remove = true,
#[cfg(feature = "socket-tcp")]
Socket::Tcp(ref mut socket) =>
Socket::Tcp(ref mut socket) => {
if socket.state() == TcpState::Closed {
may_remove = true
} else {
socket.close()
},
}
}
#[cfg(feature = "socket-dhcpv4")]
Socket::Dhcpv4(_) =>
may_remove = true,
Socket::Dhcpv4(_) => may_remove = true,
}
}
if may_remove {
@ -170,12 +176,16 @@ impl<'a> Set<'a> {
/// Iterate every socket in this set.
pub fn iter<'d>(&'d self) -> Iter<'d, 'a> {
Iter { lower: self.sockets.iter() }
Iter {
lower: self.sockets.iter(),
}
}
/// Iterate every socket in this set, as SocketRef.
pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'a> {
IterMut { lower: self.sockets.iter_mut() }
IterMut {
lower: self.sockets.iter_mut(),
}
}
}
@ -184,7 +194,7 @@ impl<'a> Set<'a> {
/// This struct is created by the [iter](struct.SocketSet.html#method.iter)
/// on [socket sets](struct.SocketSet.html).
pub struct Iter<'a, 'b: 'a> {
lower: slice::Iter<'a, Option<Item<'b>>>
lower: slice::Iter<'a, Option<Item<'b>>>,
}
impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> {
@ -193,7 +203,7 @@ impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> {
fn next(&mut self) -> Option<Self::Item> {
while let Some(item_opt) = self.lower.next() {
if let Some(item) = item_opt.as_ref() {
return Some(&item.socket)
return Some(&item.socket);
}
}
None
@ -214,7 +224,7 @@ impl<'a, 'b: 'a> Iterator for IterMut<'a, 'b> {
fn next(&mut self) -> Option<Self::Item> {
while let Some(item_opt) = self.lower.next() {
if let Some(item) = item_opt.as_mut() {
return Some(SocketRef::new(&mut item.socket))
return Some(SocketRef::new(&mut item.socket));
}
}
None

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,12 @@ use core::cmp::min;
#[cfg(feature = "async")]
use core::task::Waker;
use crate::{Error, Result};
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
use crate::storage::{PacketBuffer, PacketMetadata};
use crate::wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr};
#[cfg(feature = "async")]
use crate::socket::WakerRegistration;
use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta};
use crate::storage::{PacketBuffer, PacketMetadata};
use crate::wire::{IpEndpoint, IpProtocol, IpRepr, UdpRepr};
use crate::{Error, Result};
/// A UDP packet metadata.
pub type UdpPacketMetadata = PacketMetadata<IpEndpoint>;
@ -22,7 +22,7 @@ pub type UdpSocketBuffer<'a> = PacketBuffer<'a, IpEndpoint>;
#[derive(Debug)]
pub struct UdpSocket<'a> {
pub(crate) meta: SocketMeta,
endpoint: IpEndpoint,
endpoint: IpEndpoint,
rx_buffer: UdpSocketBuffer<'a>,
tx_buffer: UdpSocketBuffer<'a>,
/// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
@ -35,11 +35,10 @@ pub struct UdpSocket<'a> {
impl<'a> UdpSocket<'a> {
/// Create an UDP socket with the given buffers.
pub fn new(rx_buffer: UdpSocketBuffer<'a>,
tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> {
pub fn new(rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> {
UdpSocket {
meta: SocketMeta::default(),
endpoint: IpEndpoint::default(),
meta: SocketMeta::default(),
endpoint: IpEndpoint::default(),
rx_buffer: rx_buffer,
tx_buffer: tx_buffer,
hop_limit: None,
@ -131,9 +130,13 @@ impl<'a> UdpSocket<'a> {
/// if the port in the given endpoint is zero.
pub fn bind<T: Into<IpEndpoint>>(&mut self, endpoint: T) -> Result<()> {
let endpoint = endpoint.into();
if endpoint.port == 0 { return Err(Error::Unaddressable) }
if endpoint.port == 0 {
return Err(Error::Unaddressable);
}
if self.is_open() { return Err(Error::Illegal) }
if self.is_open() {
return Err(Error::Illegal);
}
self.endpoint = endpoint;
@ -212,13 +215,22 @@ impl<'a> UdpSocket<'a> {
/// and `Err(Error::Truncated)` if there is not enough transmit buffer capacity
/// to ever send this packet.
pub fn send(&mut self, size: usize, endpoint: IpEndpoint) -> Result<&mut [u8]> {
if self.endpoint.port == 0 { return Err(Error::Unaddressable) }
if !endpoint.is_specified() { return Err(Error::Unaddressable) }
if self.endpoint.port == 0 {
return Err(Error::Unaddressable);
}
if !endpoint.is_specified() {
return Err(Error::Unaddressable);
}
let payload_buf = self.tx_buffer.enqueue(size, endpoint)?;
net_trace!("{}:{}:{}: buffer to send {} octets",
self.meta.handle, self.endpoint, endpoint, size);
net_trace!(
"{}:{}:{}: buffer to send {} octets",
self.meta.handle,
self.endpoint,
endpoint,
size
);
Ok(payload_buf)
}
@ -237,9 +249,13 @@ impl<'a> UdpSocket<'a> {
pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint)> {
let (endpoint, payload_buf) = self.rx_buffer.dequeue()?;
net_trace!("{}:{}:{}: receive {} buffered octets",
self.meta.handle, self.endpoint,
endpoint, payload_buf.len());
net_trace!(
"{}:{}:{}: receive {} buffered octets",
self.meta.handle,
self.endpoint,
endpoint,
payload_buf.len()
);
Ok((payload_buf, endpoint))
}
@ -263,10 +279,14 @@ impl<'a> UdpSocket<'a> {
let handle = self.meta.handle;
let endpoint = self.endpoint;
self.rx_buffer.peek().map(|(remote_endpoint, payload_buf)| {
net_trace!("{}:{}:{}: peek {} buffered octets",
handle, endpoint,
remote_endpoint, payload_buf.len());
(payload_buf, remote_endpoint)
net_trace!(
"{}:{}:{}: peek {} buffered octets",
handle,
endpoint,
remote_endpoint,
payload_buf.len()
);
(payload_buf, remote_endpoint)
})
}
@ -284,26 +304,46 @@ impl<'a> UdpSocket<'a> {
}
pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &UdpRepr) -> bool {
if self.endpoint.port != repr.dst_port { return false }
if !self.endpoint.addr.is_unspecified() &&
self.endpoint.addr != ip_repr.dst_addr() &&
!ip_repr.dst_addr().is_broadcast() &&
!ip_repr.dst_addr().is_multicast() { return false }
if self.endpoint.port != repr.dst_port {
return false;
}
if !self.endpoint.addr.is_unspecified()
&& self.endpoint.addr != ip_repr.dst_addr()
&& !ip_repr.dst_addr().is_broadcast()
&& !ip_repr.dst_addr().is_multicast()
{
return false;
}
true
}
pub(crate) fn process(&mut self, _cx: &Context, ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8]) -> Result<()> {
pub(crate) fn process(
&mut self,
_cx: &Context,
ip_repr: &IpRepr,
repr: &UdpRepr,
payload: &[u8],
) -> Result<()> {
debug_assert!(self.accepts(ip_repr, repr));
let size = payload.len();
let endpoint = IpEndpoint { addr: ip_repr.src_addr(), port: repr.src_port };
self.rx_buffer.enqueue(size, endpoint)?.copy_from_slice(payload);
let endpoint = IpEndpoint {
addr: ip_repr.src_addr(),
port: repr.src_port,
};
self.rx_buffer
.enqueue(size, endpoint)?
.copy_from_slice(payload);
net_trace!("{}:{}:{}: receiving {} octets",
self.meta.handle, self.endpoint,
endpoint, size);
net_trace!(
"{}:{}:{}: receiving {} octets",
self.meta.handle,
self.endpoint,
endpoint,
size
);
#[cfg(feature = "async")]
self.rx_waker.wake();
@ -312,29 +352,36 @@ impl<'a> UdpSocket<'a> {
}
pub(crate) fn dispatch<F>(&mut self, _cx: &Context, emit: F) -> Result<()>
where F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()> {
let handle = self.handle();
let endpoint = self.endpoint;
where
F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()>,
{
let handle = self.handle();
let endpoint = self.endpoint;
let hop_limit = self.hop_limit.unwrap_or(64);
self.tx_buffer.dequeue_with(|remote_endpoint, payload_buf| {
net_trace!("{}:{}:{}: sending {} octets",
handle, endpoint,
endpoint, payload_buf.len());
self.tx_buffer
.dequeue_with(|remote_endpoint, payload_buf| {
net_trace!(
"{}:{}:{}: sending {} octets",
handle,
endpoint,
endpoint,
payload_buf.len()
);
let repr = UdpRepr {
src_port: endpoint.port,
dst_port: remote_endpoint.port,
};
let ip_repr = IpRepr::Unspecified {
src_addr: endpoint.addr,
dst_addr: remote_endpoint.addr,
protocol: IpProtocol::Udp,
payload_len: repr.header_len() + payload_buf.len(),
hop_limit: hop_limit,
};
emit((ip_repr, repr, payload_buf))
})?;
let repr = UdpRepr {
src_port: endpoint.port,
dst_port: remote_endpoint.port,
};
let ip_repr = IpRepr::Unspecified {
src_addr: endpoint.addr,
dst_addr: remote_endpoint.addr,
protocol: IpProtocol::Udp,
payload_len: repr.header_len() + payload_buf.len(),
hop_limit: hop_limit,
};
emit((ip_repr, repr, payload_buf))
})?;
#[cfg(feature = "async")]
self.tx_waker.wake();
@ -359,29 +406,39 @@ impl<'a> Into<Socket<'a>> for UdpSocket<'a> {
#[cfg(test)]
mod test {
use crate::wire::{IpAddress, IpRepr, UdpRepr};
use super::*;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3};
#[cfg(feature = "proto-ipv4")]
use crate::wire::Ipv4Repr;
#[cfg(feature = "proto-ipv6")]
use crate::wire::Ipv6Repr;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3};
use super::*;
use crate::wire::{IpAddress, IpRepr, UdpRepr};
fn buffer(packets: usize) -> UdpSocketBuffer<'static> {
UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; packets], vec![0; 16 * packets])
UdpSocketBuffer::new(
vec![UdpPacketMetadata::EMPTY; packets],
vec![0; 16 * packets],
)
}
fn socket(rx_buffer: UdpSocketBuffer<'static>,
tx_buffer: UdpSocketBuffer<'static>)
-> UdpSocket<'static> {
fn socket(
rx_buffer: UdpSocketBuffer<'static>,
tx_buffer: UdpSocketBuffer<'static>,
) -> UdpSocket<'static> {
UdpSocket::new(rx_buffer, tx_buffer)
}
const LOCAL_PORT: u16 = 53;
const REMOTE_PORT: u16 = 49500;
const LOCAL_PORT: u16 = 53;
const REMOTE_PORT: u16 = 49500;
pub const LOCAL_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_1, port: LOCAL_PORT };
pub const REMOTE_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_2, port: REMOTE_PORT };
pub const LOCAL_END: IpEndpoint = IpEndpoint {
addr: MOCK_IP_ADDR_1,
port: LOCAL_PORT,
};
pub const REMOTE_END: IpEndpoint = IpEndpoint {
addr: MOCK_IP_ADDR_2,
port: REMOTE_PORT,
};
pub const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified {
src_addr: MOCK_IP_ADDR_1,
@ -411,7 +468,7 @@ mod test {
dst_addr: dst,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 64
hop_limit: 64,
}),
#[cfg(feature = "proto-ipv6")]
(IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
@ -419,9 +476,9 @@ mod test {
dst_addr: dst,
next_header: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 64
hop_limit: 64,
}),
_ => unreachable!()
_ => unreachable!(),
}
}
@ -448,14 +505,31 @@ mod test {
#[test]
fn test_send_unaddressable() {
let mut socket = socket(buffer(0), buffer(1));
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Err(Error::Unaddressable));
assert_eq!(
socket.send_slice(b"abcdef", REMOTE_END),
Err(Error::Unaddressable)
);
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
assert_eq!(socket.send_slice(b"abcdef",
IpEndpoint { addr: IpAddress::Unspecified, ..REMOTE_END }),
Err(Error::Unaddressable));
assert_eq!(socket.send_slice(b"abcdef",
IpEndpoint { port: 0, ..REMOTE_END }),
Err(Error::Unaddressable));
assert_eq!(
socket.send_slice(
b"abcdef",
IpEndpoint {
addr: IpAddress::Unspecified,
..REMOTE_END
}
),
Err(Error::Unaddressable)
);
assert_eq!(
socket.send_slice(
b"abcdef",
IpEndpoint {
port: 0,
..REMOTE_END
}
),
Err(Error::Unaddressable)
);
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(()));
}
@ -465,27 +539,38 @@ mod test {
assert_eq!(socket.bind(LOCAL_END), Ok(()));
assert!(socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted));
assert_eq!(
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted)
);
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(()));
assert_eq!(socket.send_slice(b"123456", REMOTE_END), Err(Error::Exhausted));
assert_eq!(
socket.send_slice(b"123456", REMOTE_END),
Err(Error::Exhausted)
);
assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(udp_repr, LOCAL_UDP_REPR);
assert_eq!(payload, PAYLOAD);
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(udp_repr, LOCAL_UDP_REPR);
assert_eq!(payload, PAYLOAD);
Err(Error::Unaddressable)
}),
Err(Error::Unaddressable)
}), Err(Error::Unaddressable));
);
assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(udp_repr, LOCAL_UDP_REPR);
assert_eq!(payload, PAYLOAD);
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(udp_repr, LOCAL_UDP_REPR);
assert_eq!(payload, PAYLOAD);
Ok(())
}),
Ok(())
}), Ok(()));
);
assert!(socket.can_send());
}
@ -498,13 +583,27 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
Ok(()));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Ok(())
);
assert!(socket.can_recv());
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
Err(Error::Exhausted));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Err(Error::Exhausted)
);
assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
assert!(!socket.can_recv());
}
@ -516,8 +615,15 @@ mod test {
assert_eq!(socket.peek(), Err(Error::Exhausted));
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
Ok(()));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Ok(())
);
assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END)));
assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
assert_eq!(socket.peek(), Err(Error::Exhausted));
@ -529,8 +635,15 @@ mod test {
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
Ok(()));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Ok(())
);
let mut slice = [0; 4];
assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END)));
@ -542,8 +655,15 @@ mod test {
let mut socket = socket(buffer(1), buffer(0));
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
Ok(()));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Ok(())
);
let mut slice = [0; 4];
assert_eq!(socket.peek_slice(&mut slice[..]), Ok((4, &REMOTE_END)));
@ -560,16 +680,22 @@ mod test {
s.set_hop_limit(Some(0x2a));
assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _, _)| {
assert_eq!(ip_repr, IpRepr::Unspecified{
src_addr: MOCK_IP_ADDR_1,
dst_addr: MOCK_IP_ADDR_2,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 0x2a,
});
assert_eq!(
s.dispatch(&Context::DUMMY, |(ip_repr, _, _)| {
assert_eq!(
ip_repr,
IpRepr::Unspecified {
src_addr: MOCK_IP_ADDR_1,
dst_addr: MOCK_IP_ADDR_2,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 0x2a,
}
);
Ok(())
}),
Ok(())
}), Ok(()));
);
}
#[test]
@ -593,7 +719,7 @@ mod test {
dst_addr: dst,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 64
hop_limit: 64,
}),
#[cfg(feature = "proto-ipv6")]
(IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
@ -601,9 +727,9 @@ mod test {
dst_addr: dst,
next_header: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 64
hop_limit: 64,
}),
_ => unreachable!()
_ => unreachable!(),
}
}
@ -623,8 +749,11 @@ mod test {
assert_eq!(socket.bind(LOCAL_END), Ok(()));
let too_large = b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefx";
assert_eq!(socket.send_slice(too_large, REMOTE_END), Err(Error::Truncated));
assert_eq!(socket.send_slice(&too_large[..16*4], REMOTE_END), Ok(()));
assert_eq!(
socket.send_slice(too_large, REMOTE_END),
Err(Error::Truncated)
);
assert_eq!(socket.send_slice(&too_large[..16 * 4], REMOTE_END), Ok(()));
}
#[test]
@ -637,7 +766,10 @@ mod test {
src_port: REMOTE_PORT,
dst_port: LOCAL_PORT,
};
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]), Ok(()));
assert_eq!(
socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]),
Ok(())
);
assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END)));
}

View File

@ -8,29 +8,44 @@ pub struct TooManyHolesError;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct Contig {
hole_size: usize,
data_size: usize
data_size: usize,
}
impl fmt::Display for Contig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.has_hole() { write!(f, "({})", self.hole_size)?; }
if self.has_hole() && self.has_data() { write!(f, " ")?; }
if self.has_data() { write!(f, "{}", self.data_size)?; }
if self.has_hole() {
write!(f, "({})", self.hole_size)?;
}
if self.has_hole() && self.has_data() {
write!(f, " ")?;
}
if self.has_data() {
write!(f, "{}", self.data_size)?;
}
Ok(())
}
}
impl Contig {
fn empty() -> Contig {
Contig { hole_size: 0, data_size: 0 }
Contig {
hole_size: 0,
data_size: 0,
}
}
fn hole(size: usize) -> Contig {
Contig { hole_size: size, data_size: 0 }
Contig {
hole_size: size,
data_size: 0,
}
}
fn hole_and_data(hole_size: usize, data_size: usize) -> Contig {
Contig { hole_size, data_size }
Contig {
hole_size,
data_size,
}
}
fn has_hole(&self) -> bool {
@ -66,10 +81,10 @@ impl Contig {
}
}
#[cfg(feature = "std")]
use std::boxed::Box;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
#[cfg(feature = "std")]
use std::boxed::Box;
#[cfg(any(feature = "std", feature = "alloc"))]
const CONTIG_COUNT: usize = 32;
@ -93,7 +108,9 @@ impl fmt::Display for Assembler {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[ ")?;
for contig in self.contigs.iter() {
if contig.is_empty() { break }
if contig.is_empty() {
break;
}
write!(f, "{} ", contig)?;
}
write!(f, "]")?;
@ -115,10 +132,7 @@ impl Assembler {
/// FIXME(whitequark): remove this once I'm certain enough that the assembler works well.
#[allow(dead_code)]
pub(crate) fn total_size(&self) -> usize {
self.contigs
.iter()
.map(|contig| contig.total_size())
.sum()
self.contigs.iter().map(|contig| contig.total_size()).sum()
}
fn front(&self) -> Contig {
@ -143,7 +157,7 @@ impl Assembler {
self.contigs[i] = self.contigs[i + 1];
if !self.contigs[i].has_data() {
self.contigs[i + 1] = Contig::empty();
return &mut self.contigs[i]
return &mut self.contigs[i];
}
}
@ -156,7 +170,9 @@ impl Assembler {
fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, TooManyHolesError> {
debug_assert!(!self.contigs[at].is_empty());
if !self.back().is_empty() { return Err(TooManyHolesError) }
if !self.back().is_empty() {
return Err(TooManyHolesError);
}
for i in (at + 1..self.contigs.len()).rev() {
self.contigs[i] = self.contigs[i - 1];
@ -201,11 +217,11 @@ impl Assembler {
// The range being added covers a part of the hole but not of the data
// in this contig, add a new contig containing the range.
{
let inserted = self.add_contig_at(index)?;
*inserted = Contig::hole_and_data(offset, size);
let inserted = self.add_contig_at(index)?;
*inserted = Contig::hole_and_data(offset, size);
}
// Previous contigs[index] got moved to contigs[index+1]
self.contigs[index+1].shrink_hole_by(offset + size);
self.contigs[index + 1].shrink_hole_by(offset + size);
index += 2;
} else {
unreachable!()
@ -215,7 +231,7 @@ impl Assembler {
if offset >= contig.total_size() {
offset = offset.saturating_sub(contig.total_size());
} else {
size = (offset + size).saturating_sub(contig.total_size());
size = (offset + size).saturating_sub(contig.total_size());
offset = 0;
}
}
@ -258,7 +274,7 @@ pub struct AssemblerIter<'a> {
offset: usize,
index: usize,
left: usize,
right: usize
right: usize,
}
impl<'a> AssemblerIter<'a> {
@ -268,7 +284,7 @@ impl<'a> AssemblerIter<'a> {
offset: offset,
index: 0,
left: 0,
right: 0
right: 0,
}
}
}
@ -297,8 +313,8 @@ impl<'a> Iterator for AssemblerIter<'a> {
#[cfg(test)]
mod test {
use std::vec::Vec;
use super::*;
use std::vec::Vec;
impl From<Vec<(usize, usize)>> for Assembler {
fn from(vec: Vec<(usize, usize)>) -> Assembler {
@ -307,7 +323,10 @@ mod test {
#[cfg(any(feature = "std", feature = "alloc"))]
let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]);
for (i, &(hole_size, data_size)) in vec.iter().enumerate() {
contigs[i] = Contig { hole_size, data_size };
contigs[i] = Contig {
hole_size,
data_size,
};
}
Assembler { contigs }
}
@ -412,9 +431,9 @@ mod test {
#[test]
fn test_rejected_add_keeps_state() {
let mut assr = Assembler::new(CONTIG_COUNT*20);
for c in 1..=CONTIG_COUNT-1 {
assert_eq!(assr.add(c*10, 3), Ok(()));
let mut assr = Assembler::new(CONTIG_COUNT * 20);
for c in 1..=CONTIG_COUNT - 1 {
assert_eq!(assr.add(c * 10, 3), Ok(()));
}
// Maximum of allowed holes is reached
let assr_before = assr.clone();
@ -440,7 +459,6 @@ mod test {
let mut assr = contigs![(0, 4), (4, 4)];
assert_eq!(assr.remove_front(), Some(4));
assert_eq!(assr, contigs![(4, 4), (4, 0)]);
}
#[test]

View File

@ -6,12 +6,12 @@ or `alloc` crates being available, and heap-allocated memory.
*/
mod assembler;
mod ring_buffer;
mod packet_buffer;
mod ring_buffer;
pub use self::assembler::Assembler;
pub use self::ring_buffer::RingBuffer;
pub use self::packet_buffer::{PacketBuffer, PacketMetadata};
pub use self::ring_buffer::RingBuffer;
/// A trait for setting a value to a known state.
///

View File

@ -1,31 +1,34 @@
use managed::ManagedSlice;
use crate::{Error, Result};
use crate::storage::RingBuffer;
use crate::{Error, Result};
/// Size and header of a packet.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PacketMetadata<H> {
size: usize,
header: Option<H>
size: usize,
header: Option<H>,
}
impl<H> PacketMetadata<H> {
/// Empty packet description.
pub const EMPTY: PacketMetadata<H> = PacketMetadata { size: 0, header: None };
pub const EMPTY: PacketMetadata<H> = PacketMetadata {
size: 0,
header: None,
};
fn padding(size: usize) -> PacketMetadata<H> {
PacketMetadata {
size: size,
header: None
size: size,
header: None,
}
}
fn packet(size: usize, header: H) -> PacketMetadata<H> {
PacketMetadata {
size: size,
header: Some(header)
size: size,
header: Some(header),
}
}
@ -36,9 +39,9 @@ impl<H> PacketMetadata<H> {
/// An UDP packet ring buffer.
#[derive(Debug)]
pub struct PacketBuffer<'a, H: 'a> {
pub struct PacketBuffer<'a, H: 'a> {
metadata_ring: RingBuffer<'a, PacketMetadata<H>>,
payload_ring: RingBuffer<'a, u8>,
payload_ring: RingBuffer<'a, u8>,
}
impl<'a, H> PacketBuffer<'a, H> {
@ -47,12 +50,13 @@ impl<'a, H> PacketBuffer<'a, H> {
/// Metadata storage limits the maximum _number_ of packets in the buffer and payload
/// storage limits the maximum _total size_ of packets.
pub fn new<MS, PS>(metadata_storage: MS, payload_storage: PS) -> PacketBuffer<'a, H>
where MS: Into<ManagedSlice<'a, PacketMetadata<H>>>,
PS: Into<ManagedSlice<'a, u8>>,
where
MS: Into<ManagedSlice<'a, PacketMetadata<H>>>,
PS: Into<ManagedSlice<'a, u8>>,
{
PacketBuffer {
metadata_ring: RingBuffer::new(metadata_storage),
payload_ring: RingBuffer::new(payload_storage),
payload_ring: RingBuffer::new(payload_storage),
}
}
@ -75,25 +79,25 @@ impl<'a, H> PacketBuffer<'a, H> {
/// does not have enough spare payload space.
pub fn enqueue(&mut self, size: usize, header: H) -> Result<&mut [u8]> {
if self.payload_ring.capacity() < size {
return Err(Error::Truncated)
return Err(Error::Truncated);
}
if self.metadata_ring.is_full() {
return Err(Error::Exhausted)
return Err(Error::Exhausted);
}
let window = self.payload_ring.window();
let contig_window = self.payload_ring.contiguous_window();
if window < size {
return Err(Error::Exhausted)
return Err(Error::Exhausted);
} else if contig_window < size {
if window - contig_window < size {
// The buffer length is larger than the current contiguous window
// and is larger than the contiguous window will be after adding
// the padding necessary to circle around to the beginning of the
// ring buffer.
return Err(Error::Exhausted)
return Err(Error::Exhausted);
} else {
// Add padding to the end of the ring buffer so that the
// contiguous window is at the beginning of the ring buffer.
@ -110,7 +114,10 @@ impl<'a, H> PacketBuffer<'a, H> {
}
fn dequeue_padding(&mut self) {
let Self { ref mut metadata_ring, ref mut payload_ring } = *self;
let Self {
ref mut metadata_ring,
ref mut payload_ring,
} = *self;
let _ = metadata_ring.dequeue_one_with(|metadata| {
if metadata.is_padding() {
@ -125,22 +132,32 @@ impl<'a, H> PacketBuffer<'a, H> {
/// Call `f` with a single packet from the buffer, and dequeue the packet if `f`
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty.
pub fn dequeue_with<'c, R, F>(&'c mut self, f: F) -> Result<R>
where F: FnOnce(&mut H, &'c mut [u8]) -> Result<R> {
where
F: FnOnce(&mut H, &'c mut [u8]) -> Result<R>,
{
self.dequeue_padding();
let Self { ref mut metadata_ring, ref mut payload_ring } = *self;
let Self {
ref mut metadata_ring,
ref mut payload_ring,
} = *self;
metadata_ring.dequeue_one_with(move |metadata| {
let PacketMetadata { ref mut header, size } = *metadata;
let PacketMetadata {
ref mut header,
size,
} = *metadata;
payload_ring.dequeue_many_with(|payload_buf| {
debug_assert!(payload_buf.len() >= size);
payload_ring
.dequeue_many_with(|payload_buf| {
debug_assert!(payload_buf.len() >= size);
match f(header.as_mut().unwrap(), &mut payload_buf[..size]) {
Ok(val) => (size, Ok(val)),
Err(err) => (0, Err(err)),
}
}).1
match f(header.as_mut().unwrap(), &mut payload_buf[..size]) {
Ok(val) => (size, Ok(val)),
Err(err) => (0, Err(err)),
}
})
.1
})
}
@ -149,7 +166,10 @@ impl<'a, H> PacketBuffer<'a, H> {
pub fn dequeue(&mut self) -> Result<(H, &mut [u8])> {
self.dequeue_padding();
let PacketMetadata { ref mut header, size } = *self.metadata_ring.dequeue_one()?;
let PacketMetadata {
ref mut header,
size,
} = *self.metadata_ring.dequeue_one()?;
let payload_buf = self.payload_ring.dequeue_many(size);
debug_assert!(payload_buf.len() == size);
@ -164,7 +184,10 @@ impl<'a, H> PacketBuffer<'a, H> {
self.dequeue_padding();
if let Some(metadata) = self.metadata_ring.get_allocated(0, 1).first() {
Ok((metadata.header.as_ref().unwrap(), self.payload_ring.get_allocated(0, metadata.size)))
Ok((
metadata.header.as_ref().unwrap(),
self.payload_ring.get_allocated(0, metadata.size),
))
} else {
Err(Error::Exhausted)
}
@ -193,8 +216,7 @@ mod test {
use super::*;
fn buffer() -> PacketBuffer<'static, ()> {
PacketBuffer::new(vec![PacketMetadata::EMPTY; 4],
vec![0u8; 16])
PacketBuffer::new(vec![PacketMetadata::EMPTY; 4], vec![0u8; 16])
}
#[test]
@ -237,7 +259,10 @@ mod test {
let mut buffer = buffer();
assert!(buffer.enqueue(12, ()).is_ok());
assert!(buffer.dequeue().is_ok());
buffer.enqueue(12, ()).unwrap().copy_from_slice(b"abcdefghijkl");
buffer
.enqueue(12, ())
.unwrap()
.copy_from_slice(b"abcdefghijkl");
}
#[test]
@ -250,13 +275,17 @@ mod test {
assert_eq!(buffer.metadata_ring.len(), 3);
assert!(buffer.dequeue().is_ok());
assert!(buffer.dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>).is_err());
assert!(buffer
.dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>)
.is_err());
assert_eq!(buffer.metadata_ring.len(), 1);
assert!(buffer.dequeue_with(|&mut (), payload| {
assert_eq!(payload, &b"abcd"[..]);
Ok(())
}).is_ok());
assert!(buffer
.dequeue_with(|&mut (), payload| {
assert_eq!(payload, &b"abcd"[..]);
Ok(())
})
.is_ok());
assert_eq!(buffer.metadata_ring.len(), 0);
}

View File

@ -4,8 +4,8 @@
use core::cmp;
use managed::ManagedSlice;
use crate::{Error, Result};
use crate::storage::Resettable;
use crate::{Error, Result};
/// A ring buffer.
///
@ -25,7 +25,7 @@ use crate::storage::Resettable;
pub struct RingBuffer<'a, T: 'a> {
storage: ManagedSlice<'a, T>,
read_at: usize,
length: usize,
length: usize,
}
impl<'a, T: 'a> RingBuffer<'a, T> {
@ -33,19 +33,20 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
///
/// During creation, every element in `storage` is reset.
pub fn new<S>(storage: S) -> RingBuffer<'a, T>
where S: Into<ManagedSlice<'a, T>>,
where
S: Into<ManagedSlice<'a, T>>,
{
RingBuffer {
storage: storage.into(),
read_at: 0,
length: 0,
length: 0,
}
}
/// Clear the ring buffer.
pub fn clear(&mut self) {
self.read_at = 0;
self.length = 0;
self.length = 0;
}
/// Return the maximum number of elements in the ring buffer.
@ -55,7 +56,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Clear the ring buffer, and reset every element.
pub fn reset(&mut self)
where T: Resettable {
where
T: Resettable,
{
self.clear();
for elem in self.storage.iter_mut() {
elem.reset();
@ -112,8 +115,12 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Call `f` with a single buffer element, and enqueue the element if `f`
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is full.
pub fn enqueue_one_with<'b, R, F>(&'b mut self, f: F) -> Result<R>
where F: FnOnce(&'b mut T) -> Result<R> {
if self.is_full() { return Err(Error::Exhausted) }
where
F: FnOnce(&'b mut T) -> Result<R>,
{
if self.is_full() {
return Err(Error::Exhausted);
}
let index = self.get_idx_unchecked(self.length);
match f(&mut self.storage[index]) {
@ -121,7 +128,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
self.length += 1;
Ok(result)
}
Err(error) => Err(error)
Err(error) => Err(error),
}
}
@ -136,8 +143,12 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Call `f` with a single buffer element, and dequeue the element if `f`
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty.
pub fn dequeue_one_with<'b, R, F>(&'b mut self, f: F) -> Result<R>
where F: FnOnce(&'b mut T) -> Result<R> {
if self.is_empty() { return Err(Error::Exhausted) }
where
F: FnOnce(&'b mut T) -> Result<R>,
{
if self.is_empty() {
return Err(Error::Exhausted);
}
let next_at = self.get_idx_unchecked(1);
match f(&mut self.storage[self.read_at]) {
@ -146,7 +157,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
self.read_at = next_at;
Ok(result)
}
Err(error) => Err(error)
Err(error) => Err(error),
}
}
@ -169,7 +180,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// This function panics if the amount of elements returned by `f` is larger
/// than the size of the slice passed into it.
pub fn enqueue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R)
where F: FnOnce(&'b mut [T]) -> (usize, R) {
where
F: FnOnce(&'b mut [T]) -> (usize, R),
{
if self.length == 0 {
// Ring is currently empty. Reset `read_at` to optimize
// for contiguous space.
@ -194,14 +207,17 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
self.enqueue_many_with(|buf| {
let size = cmp::min(size, buf.len());
(size, &mut buf[..size])
}).1
})
.1
}
/// Enqueue as many elements from the given slice into the buffer as possible,
/// and return the amount of elements that could fit.
// #[must_use]
pub fn enqueue_slice(&mut self, data: &[T]) -> usize
where T: Copy {
where
T: Copy,
{
let (size_1, data) = self.enqueue_many_with(|buf| {
let size = cmp::min(buf.len(), data.len());
buf[..size].copy_from_slice(&data[..size]);
@ -222,7 +238,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// This function panics if the amount of elements returned by `f` is larger
/// than the size of the slice passed into it.
pub fn dequeue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R)
where F: FnOnce(&'b mut [T]) -> (usize, R) {
where
F: FnOnce(&'b mut [T]) -> (usize, R),
{
let capacity = self.capacity();
let max_size = cmp::min(self.len(), capacity - self.read_at);
let (size, result) = f(&mut self.storage[self.read_at..self.read_at + max_size]);
@ -246,14 +264,17 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
self.dequeue_many_with(|buf| {
let size = cmp::min(size, buf.len());
(size, &mut buf[..size])
}).1
})
.1
}
/// Dequeue as many elements from the buffer into the given slice as possible,
/// and return the amount of elements that could fit.
// #[must_use]
pub fn dequeue_slice(&mut self, data: &mut [T]) -> usize
where T: Copy {
where
T: Copy,
{
let (size_1, data) = self.dequeue_many_with(|buf| {
let size = cmp::min(buf.len(), data.len());
data[..size].copy_from_slice(&buf[..size]);
@ -277,13 +298,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
pub fn get_unallocated(&mut self, offset: usize, mut size: usize) -> &mut [T] {
let start_at = self.get_idx(self.length + offset);
// We can't access past the end of unallocated data.
if offset > self.window() { return &mut [] }
if offset > self.window() {
return &mut [];
}
// We can't enqueue more than there is free space.
let clamped_window = self.window() - offset;
if size > clamped_window { size = clamped_window }
if size > clamped_window {
size = clamped_window
}
// We can't contiguously enqueue past the end of the storage.
let until_end = self.capacity() - start_at;
if size > until_end { size = until_end }
if size > until_end {
size = until_end
}
&mut self.storage[start_at..start_at + size]
}
@ -293,7 +320,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// the amount written.
// #[must_use]
pub fn write_unallocated(&mut self, offset: usize, data: &[T]) -> usize
where T: Copy {
where
T: Copy,
{
let (size_1, offset, data) = {
let slice = self.get_unallocated(offset, data.len());
let slice_len = slice.len();
@ -324,13 +353,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
pub fn get_allocated(&self, offset: usize, mut size: usize) -> &[T] {
let start_at = self.get_idx(offset);
// We can't read past the end of the allocated data.
if offset > self.length { return &mut [] }
if offset > self.length {
return &mut [];
}
// We can't read more than we have allocated.
let clamped_length = self.length - offset;
if size > clamped_length { size = clamped_length }
if size > clamped_length {
size = clamped_length
}
// We can't contiguously dequeue past the end of the storage.
let until_end = self.capacity() - start_at;
if size > until_end { size = until_end }
if size > until_end {
size = until_end
}
&self.storage[start_at..start_at + size]
}
@ -340,7 +375,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// the amount read.
// #[must_use]
pub fn read_allocated(&mut self, offset: usize, data: &mut [T]) -> usize
where T: Copy {
where
T: Copy,
{
let (size_1, offset, data) = {
let slice = self.get_allocated(offset, data.len());
data[..slice.len()].copy_from_slice(slice);
@ -402,8 +439,10 @@ mod test {
#[test]
fn test_buffer_enqueue_dequeue_one_with() {
let mut ring = RingBuffer::new(vec![0; 5]);
assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted));
assert_eq!(
ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted)
);
ring.enqueue_one_with(Ok).unwrap();
assert!(!ring.is_empty());
@ -414,15 +453,19 @@ mod test {
assert!(!ring.is_empty());
}
assert!(ring.is_full());
assert_eq!(ring.enqueue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted));
assert_eq!(
ring.enqueue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted)
);
for i in 0..5 {
assert_eq!(ring.dequeue_one_with(|e| Ok(*e)).unwrap(), i);
assert!(!ring.is_full());
}
assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted));
assert_eq!(
ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted)
);
assert!(ring.is_empty());
}
@ -454,11 +497,14 @@ mod test {
fn test_buffer_enqueue_many_with() {
let mut ring = RingBuffer::new(vec![b'.'; 12]);
assert_eq!(ring.enqueue_many_with(|buf| {
assert_eq!(buf.len(), 12);
buf[0..2].copy_from_slice(b"ab");
assert_eq!(
ring.enqueue_many_with(|buf| {
assert_eq!(buf.len(), 12);
buf[0..2].copy_from_slice(b"ab");
(2, true)
}),
(2, true)
}), (2, true));
);
assert_eq!(ring.len(), 2);
assert_eq!(&ring.storage[..], b"ab..........");
@ -545,12 +591,15 @@ mod test {
assert_eq!(ring.enqueue_slice(b"abcdefghijkl"), 12);
assert_eq!(ring.dequeue_many_with(|buf| {
assert_eq!(buf.len(), 12);
assert_eq!(buf, b"abcdefghijkl");
buf[..4].copy_from_slice(b"....");
assert_eq!(
ring.dequeue_many_with(|buf| {
assert_eq!(buf.len(), 12);
assert_eq!(buf, b"abcdefghijkl");
buf[..4].copy_from_slice(b"....");
(4, true)
}),
(4, true)
}), (4, true));
);
assert_eq!(ring.len(), 8);
assert_eq!(&ring.storage[..], b"....efghijkl");
@ -679,7 +728,7 @@ mod test {
let mut ring = RingBuffer::new(vec![b'.'; 12]);
assert_eq!(ring.get_allocated(16, 4), b"");
assert_eq!(ring.get_allocated(0, 4), b"");
assert_eq!(ring.get_allocated(0, 4), b"");
ring.enqueue_slice(b"abcd");
assert_eq!(ring.get_allocated(0, 8), b"abcd");
@ -711,7 +760,6 @@ mod test {
let mut data = [0; 6];
assert_eq!(ring.read_allocated(6, &mut data[..]), 3);
assert_eq!(&data[..], b"mno\x00\x00\x00");
}
#[test]

View File

@ -10,7 +10,7 @@ absolute and relative time.
[Duration]: struct.Duration.html
*/
use core::{ops, fmt};
use core::{fmt, ops};
/// A representation of an absolute time value.
///
@ -30,12 +30,16 @@ pub struct Instant {
impl Instant {
/// Create a new `Instant` from a number of milliseconds.
pub fn from_millis<T: Into<i64>>(millis: T) -> Instant {
Instant { millis: millis.into() }
Instant {
millis: millis.into(),
}
}
/// Create a new `Instant` from a number of seconds.
pub fn from_secs<T: Into<i64>>(secs: T) -> Instant {
Instant { millis: secs.into() * 1000 }
Instant {
millis: secs.into() * 1000,
}
}
/// Create a new `Instant` from the current [std::time::SystemTime].
@ -79,7 +83,8 @@ impl From<::std::time::Instant> for Instant {
#[cfg(feature = "std")]
impl From<::std::time::SystemTime> for Instant {
fn from(other: ::std::time::SystemTime) -> Instant {
let n = other.duration_since(::std::time::UNIX_EPOCH)
let n = other
.duration_since(::std::time::UNIX_EPOCH)
.expect("start time must not be before the unix epoch");
Self::from_millis(n.as_secs() as i64 * 1000 + n.subsec_millis() as i64)
}
@ -149,7 +154,9 @@ impl Duration {
/// Create a new `Instant` from a number of seconds.
pub const fn from_secs(secs: u64) -> Duration {
Duration { millis: secs * 1000 }
Duration {
millis: secs * 1000,
}
}
/// The fractional number of milliseconds in this `Duration`.
@ -193,14 +200,19 @@ impl ops::Sub<Duration> for Duration {
fn sub(self, rhs: Duration) -> Duration {
Duration::from_millis(
self.millis.checked_sub(rhs.total_millis()).expect("overflow when subtracting durations"))
self.millis
.checked_sub(rhs.total_millis())
.expect("overflow when subtracting durations"),
)
}
}
impl ops::SubAssign<Duration> for Duration {
fn sub_assign(&mut self, rhs: Duration) {
self.millis = self.millis.checked_sub(
rhs.total_millis()).expect("overflow when subtracting durations");
self.millis = self
.millis
.checked_sub(rhs.total_millis())
.expect("overflow when subtracting durations");
}
}
@ -262,17 +274,13 @@ impl ops::ShrAssign<u32> for Duration {
impl From<::core::time::Duration> for Duration {
fn from(other: ::core::time::Duration) -> Duration {
Duration::from_millis(
other.as_secs() * 1000 + other.subsec_millis() as u64
)
Duration::from_millis(other.as_secs() * 1000 + other.subsec_millis() as u64)
}
}
impl Into<::core::time::Duration> for Duration {
fn into(self) -> ::core::time::Duration {
::core::time::Duration::from_millis(
self.total_millis()
)
::core::time::Duration::from_millis(self.total_millis())
}
}
@ -283,9 +291,15 @@ mod test {
#[test]
fn test_instant_ops() {
// std::ops::Add
assert_eq!(Instant::from_millis(4) + Duration::from_millis(6), Instant::from_millis(10));
assert_eq!(
Instant::from_millis(4) + Duration::from_millis(6),
Instant::from_millis(10)
);
// std::ops::Sub
assert_eq!(Instant::from_millis(7) - Duration::from_millis(5), Instant::from_millis(2));
assert_eq!(
Instant::from_millis(7) - Duration::from_millis(5),
Instant::from_millis(2)
);
}
#[test]
@ -306,19 +320,30 @@ mod test {
#[cfg(feature = "std")]
fn test_instant_conversions() {
let mut epoc: ::std::time::SystemTime = Instant::from_millis(0).into();
assert_eq!(Instant::from(::std::time::UNIX_EPOCH),
Instant::from_millis(0));
assert_eq!(
Instant::from(::std::time::UNIX_EPOCH),
Instant::from_millis(0)
);
assert_eq!(epoc, ::std::time::UNIX_EPOCH);
epoc = Instant::from_millis(2085955200i64 * 1000).into();
assert_eq!(epoc, ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200));
assert_eq!(
epoc,
::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200)
);
}
#[test]
fn test_duration_ops() {
// std::ops::Add
assert_eq!(Duration::from_millis(40) + Duration::from_millis(2), Duration::from_millis(42));
assert_eq!(
Duration::from_millis(40) + Duration::from_millis(2),
Duration::from_millis(42)
);
// std::ops::Sub
assert_eq!(Duration::from_millis(555) - Duration::from_millis(42), Duration::from_millis(513));
assert_eq!(
Duration::from_millis(555) - Duration::from_millis(42),
Duration::from_millis(513)
);
// std::ops::Mul
assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286));
// std::ops::Div

View File

@ -1,5 +1,5 @@
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
@ -24,7 +24,7 @@ enum_with_unknown! {
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
mod field {
@ -34,9 +34,9 @@ mod field {
pub const HTYPE: Field = 0..2;
pub const PTYPE: Field = 2..4;
pub const HLEN: usize = 4;
pub const PLEN: usize = 5;
pub const OPER: Field = 6..8;
pub const HLEN: usize = 4;
pub const PLEN: usize = 5;
pub const OPER: Field = 6..8;
#[inline]
pub fn SHA(hardware_len: u8, _protocol_len: u8) -> Field {
@ -263,7 +263,7 @@ pub enum Repr {
source_hardware_addr: EthernetAddress,
source_protocol_addr: Ipv4Address,
target_hardware_addr: EthernetAddress,
target_protocol_addr: Ipv4Address
target_protocol_addr: Ipv4Address,
},
}
@ -271,22 +271,20 @@ impl Repr {
/// Parse an Address Resolution Protocol packet and return a high-level representation,
/// or return `Err(Error::Unrecognized)` if the packet is not recognized.
pub fn parse<T: AsRef<[u8]>>(packet: &Packet<T>) -> Result<Repr> {
match (packet.hardware_type(), packet.protocol_type(),
packet.hardware_len(), packet.protocol_len()) {
(Hardware::Ethernet, Protocol::Ipv4, 6, 4) => {
Ok(Repr::EthernetIpv4 {
operation: packet.operation(),
source_hardware_addr:
EthernetAddress::from_bytes(packet.source_hardware_addr()),
source_protocol_addr:
Ipv4Address::from_bytes(packet.source_protocol_addr()),
target_hardware_addr:
EthernetAddress::from_bytes(packet.target_hardware_addr()),
target_protocol_addr:
Ipv4Address::from_bytes(packet.target_protocol_addr())
})
},
_ => Err(Error::Unrecognized)
match (
packet.hardware_type(),
packet.protocol_type(),
packet.hardware_len(),
packet.protocol_len(),
) {
(Hardware::Ethernet, Protocol::Ipv4, 6, 4) => Ok(Repr::EthernetIpv4 {
operation: packet.operation(),
source_hardware_addr: EthernetAddress::from_bytes(packet.source_hardware_addr()),
source_protocol_addr: Ipv4Address::from_bytes(packet.source_protocol_addr()),
target_hardware_addr: EthernetAddress::from_bytes(packet.target_hardware_addr()),
target_protocol_addr: Ipv4Address::from_bytes(packet.target_protocol_addr()),
}),
_ => Err(Error::Unrecognized),
}
}
@ -302,8 +300,10 @@ impl Repr {
match *self {
Repr::EthernetIpv4 {
operation,
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
} => {
packet.set_hardware_type(Hardware::Ethernet);
packet.set_protocol_type(Protocol::Ipv4);
@ -314,7 +314,7 @@ impl Repr {
packet.set_source_protocol_addr(source_protocol_addr.as_bytes());
packet.set_target_hardware_addr(target_hardware_addr.as_bytes());
packet.set_target_protocol_addr(target_protocol_addr.as_bytes());
},
}
}
}
}
@ -325,13 +325,23 @@ impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
Ok(repr) => write!(f, "{}", repr),
_ => {
write!(f, "ARP (unrecognized)")?;
write!(f, " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
self.hardware_type(), self.protocol_type(),
self.hardware_len(), self.protocol_len(),
self.operation())?;
write!(f, " sha={:?} spa={:?} tha={:?} tpa={:?}",
self.source_hardware_addr(), self.source_protocol_addr(),
self.target_hardware_addr(), self.target_protocol_addr())?;
write!(
f,
" htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
self.hardware_type(),
self.protocol_type(),
self.hardware_len(),
self.protocol_len(),
self.operation()
)?;
write!(
f,
" sha={:?} spa={:?} tha={:?} tpa={:?}",
self.source_hardware_addr(),
self.source_protocol_addr(),
self.target_hardware_addr(),
self.target_protocol_addr()
)?;
Ok(())
}
}
@ -343,26 +353,36 @@ impl fmt::Display for Repr {
match *self {
Repr::EthernetIpv4 {
operation,
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
} => {
write!(f, "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}",
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr,
operation)
},
write!(
f,
"ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}",
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
operation
)
}
}
}
}
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
match Packet::new_checked(buffer) {
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet)
Ok(packet) => write!(f, "{}{}", indent, packet),
}
}
}
@ -371,16 +391,10 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test {
use super::*;
static PACKET_BYTES: [u8; 28] =
[0x00, 0x01,
0x08, 0x00,
0x06,
0x04,
0x00, 0x01,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x21, 0x22, 0x23, 0x24,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x41, 0x42, 0x43, 0x44];
static PACKET_BYTES: [u8; 28] = [
0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x21,
0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x41, 0x42, 0x43, 0x44,
];
#[test]
fn test_deconstruct() {
@ -390,9 +404,15 @@ mod test {
assert_eq!(packet.hardware_len(), 6);
assert_eq!(packet.protocol_len(), 4);
assert_eq!(packet.operation(), Operation::Request);
assert_eq!(packet.source_hardware_addr(), &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
assert_eq!(
packet.source_hardware_addr(),
&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]
);
assert_eq!(packet.source_protocol_addr(), &[0x21, 0x22, 0x23, 0x24]);
assert_eq!(packet.target_hardware_addr(), &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]);
assert_eq!(
packet.target_hardware_addr(),
&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]
);
assert_eq!(packet.target_protocol_addr(), &[0x41, 0x42, 0x43, 0x44]);
}
@ -415,14 +435,14 @@ mod test {
fn packet_repr() -> Repr {
Repr::EthernetIpv4 {
operation: Operation::Request,
source_hardware_addr:
EthernetAddress::from_bytes(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]),
source_protocol_addr:
Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
target_hardware_addr:
EthernetAddress::from_bytes(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]),
target_protocol_addr:
Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44])
source_hardware_addr: EthernetAddress::from_bytes(&[
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
]),
source_protocol_addr: Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
target_hardware_addr: EthernetAddress::from_bytes(&[
0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
]),
target_protocol_addr: Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44]),
}
}

View File

@ -2,9 +2,9 @@
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::wire::{EthernetAddress, Ipv4Address};
use crate::wire::arp::Hardware;
use crate::wire::{EthernetAddress, Ipv4Address};
use crate::{Error, Result};
pub const SERVER_PORT: u16 = 67;
pub const CLIENT_PORT: u16 = 68;
@ -37,8 +37,11 @@ enum_with_unknown! {
impl MessageType {
fn opcode(&self) -> OpCode {
match *self {
MessageType::Discover | MessageType::Inform | MessageType::Request |
MessageType::Decline | MessageType::Release => OpCode::Request,
MessageType::Discover
| MessageType::Inform
| MessageType::Request
| MessageType::Decline
| MessageType::Release => OpCode::Request,
MessageType::Offer | MessageType::Ack | MessageType::Nak => OpCode::Reply,
MessageType::Unknown(_) => OpCode::Unknown(0),
}
@ -59,7 +62,7 @@ pub enum DhcpOption<'a> {
Router(Ipv4Address),
SubnetMask(Ipv4Address),
MaximumDhcpMessageSize(u16),
Other { kind: u8, data: &'a [u8] }
Other { kind: u8, data: &'a [u8] },
}
impl<'a> DhcpOption<'a> {
@ -81,12 +84,10 @@ impl<'a> DhcpOption<'a> {
skip_len = length + 2;
let data = buffer.get(2..skip_len).ok_or(Error::Truncated)?;
match (kind, length) {
(field::OPT_END, _) |
(field::OPT_PAD, _) =>
unreachable!(),
(field::OPT_END, _) | (field::OPT_PAD, _) => unreachable!(),
(field::OPT_DHCP_MESSAGE_TYPE, 1) => {
option = DhcpOption::MessageType(MessageType::from(data[0]));
},
}
(field::OPT_REQUESTED_IP, 4) => {
option = DhcpOption::RequestedIp(Ipv4Address::from_bytes(data));
}
@ -95,7 +96,8 @@ impl<'a> DhcpOption<'a> {
if hardware_type != Hardware::Ethernet {
return Err(Error::Unrecognized);
}
option = DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..]));
option =
DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..]));
}
(field::OPT_SERVER_IDENTIFIER, 4) => {
option = DhcpOption::ServerIdentifier(Ipv4Address::from_bytes(data));
@ -107,13 +109,20 @@ impl<'a> DhcpOption<'a> {
option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data));
}
(field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]]));
option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([
data[0], data[1],
]));
}
(field::OPT_IP_LEASE_TIME, 4) => {
option = DhcpOption::IpLeaseTime(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
option = DhcpOption::IpLeaseTime(u32::from_be_bytes([
data[0], data[1], data[2], data[3],
]))
}
(_, _) => {
option = DhcpOption::Other { kind: kind, data: data };
option = DhcpOption::Other {
kind: kind,
data: data,
};
}
}
}
@ -126,20 +135,14 @@ impl<'a> DhcpOption<'a> {
&DhcpOption::EndOfList => 1,
&DhcpOption::Pad => 1,
&DhcpOption::MessageType(_) => 3,
&DhcpOption::ClientIdentifier(eth_addr) => {
3 + eth_addr.as_bytes().len()
}
&DhcpOption::RequestedIp(ip) |
&DhcpOption::ServerIdentifier(ip) |
&DhcpOption::Router(ip) |
&DhcpOption::SubnetMask(ip) => {
2 + ip.as_bytes().len()
},
&DhcpOption::MaximumDhcpMessageSize(_) => {
4
}
&DhcpOption::ClientIdentifier(eth_addr) => 3 + eth_addr.as_bytes().len(),
&DhcpOption::RequestedIp(ip)
| &DhcpOption::ServerIdentifier(ip)
| &DhcpOption::Router(ip)
| &DhcpOption::SubnetMask(ip) => 2 + ip.as_bytes().len(),
&DhcpOption::MaximumDhcpMessageSize(_) => 4,
&DhcpOption::IpLeaseTime(_) => 6,
&DhcpOption::Other { data, .. } => 2 + data.len()
&DhcpOption::Other { data, .. } => 2 + data.len(),
}
}
@ -168,19 +171,19 @@ impl<'a> DhcpOption<'a> {
buffer[2] = u16::from(Hardware::Ethernet) as u8;
buffer[3..9].copy_from_slice(eth_addr.as_bytes());
}
DhcpOption::RequestedIp(ip) => {
DhcpOption::RequestedIp(ip) => {
buffer[0] = field::OPT_REQUESTED_IP;
buffer[2..6].copy_from_slice(ip.as_bytes());
}
DhcpOption::ServerIdentifier(ip) => {
DhcpOption::ServerIdentifier(ip) => {
buffer[0] = field::OPT_SERVER_IDENTIFIER;
buffer[2..6].copy_from_slice(ip.as_bytes());
}
DhcpOption::Router(ip) => {
DhcpOption::Router(ip) => {
buffer[0] = field::OPT_ROUTER;
buffer[2..6].copy_from_slice(ip.as_bytes());
}
DhcpOption::SubnetMask(mask) => {
DhcpOption::SubnetMask(mask) => {
buffer[0] = field::OPT_SUBNET_MASK;
buffer[2..6].copy_from_slice(mask.as_bytes());
}
@ -192,7 +195,10 @@ impl<'a> DhcpOption<'a> {
buffer[0] = field::OPT_IP_LEASE_TIME;
buffer[2..6].copy_from_slice(&lease_time.to_be_bytes()[..]);
}
DhcpOption::Other { kind, data: provided } => {
DhcpOption::Other {
kind,
data: provided,
} => {
buffer[0] = kind;
buffer[2..skip_length].copy_from_slice(provided);
}
@ -207,7 +213,7 @@ impl<'a> DhcpOption<'a> {
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
pub(crate) mod field {
@ -691,7 +697,7 @@ pub struct Repr<'a> {
/// The maximum size dhcp packet the interface can receive
pub max_size: Option<u16>,
/// The DHCP IP lease duration, specified in seconds.
pub lease_duration: Option<u32>
pub lease_duration: Option<u32>,
}
impl<'a> Repr<'a> {
@ -700,22 +706,39 @@ impl<'a> Repr<'a> {
let mut len = field::OPTIONS.start;
// message type and end-of-options options
len += 3 + 1;
if self.requested_ip.is_some() { len += 6; }
if self.client_identifier.is_some() { len += 9; }
if self.server_identifier.is_some() { len += 6; }
if self.max_size.is_some() { len += 4; }
if self.router.is_some() { len += 6; }
if self.subnet_mask.is_some() { len += 6; }
if self.lease_duration.is_some() { len += 6; }
if let Some(list) = self.parameter_request_list { len += list.len() + 2; }
if self.requested_ip.is_some() {
len += 6;
}
if self.client_identifier.is_some() {
len += 9;
}
if self.server_identifier.is_some() {
len += 6;
}
if self.max_size.is_some() {
len += 4;
}
if self.router.is_some() {
len += 6;
}
if self.subnet_mask.is_some() {
len += 6;
}
if self.lease_duration.is_some() {
len += 6;
}
if let Some(list) = self.parameter_request_list {
len += list.len() + 2;
}
len
}
/// Parse a DHCP packet and return a high-level representation.
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Self>
where T: AsRef<[u8]> + ?Sized {
where
T: AsRef<[u8]> + ?Sized,
{
let transaction_id = packet.transaction_id();
let client_hardware_address = packet.client_hardware_address();
let client_ip = packet.client_ip();
@ -753,12 +776,12 @@ impl<'a> Repr<'a> {
let (next_options, option) = DhcpOption::parse(options)?;
match option {
DhcpOption::EndOfList => break,
DhcpOption::Pad => {},
DhcpOption::Pad => {}
DhcpOption::MessageType(value) => {
if value.opcode() == packet.opcode() {
message_type = Ok(value);
}
},
}
DhcpOption::RequestedIp(ip) => {
requested_ip = Some(ip);
}
@ -773,24 +796,30 @@ impl<'a> Repr<'a> {
}
DhcpOption::SubnetMask(mask) => {
subnet_mask = Some(mask);
},
}
DhcpOption::MaximumDhcpMessageSize(size) => {
max_size = Some(size);
}
DhcpOption::IpLeaseTime(duration) => {
lease_duration = Some(duration);
}
DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => {
DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST,
data,
} => {
parameter_request_list = Some(data);
}
DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => {
DhcpOption::Other {
kind: field::OPT_DOMAIN_NAME_SERVER,
data,
} => {
let mut servers = [None; MAX_DNS_SERVER_COUNT];
for (server, chunk) in servers.iter_mut().zip(data.chunks(4)) {
*server = Some(Ipv4Address::from_bytes(chunk));
}
dns_servers = Some(servers);
}
DhcpOption::Other {..} => {}
DhcpOption::Other { .. } => {}
}
options = next_options;
}
@ -798,9 +827,21 @@ impl<'a> Repr<'a> {
let broadcast = packet.broadcast_flag();
Ok(Repr {
transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip,
broadcast, requested_ip, server_identifier, router,
subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size,
transaction_id,
client_hardware_address,
client_ip,
your_ip,
server_ip,
relay_agent_ip,
broadcast,
requested_ip,
server_identifier,
router,
subnet_mask,
client_identifier,
parameter_request_list,
dns_servers,
max_size,
lease_duration,
message_type: message_type?,
})
@ -809,7 +850,9 @@ impl<'a> Repr<'a> {
/// Emit a high-level representation into a Dynamic Host
/// Configuration Protocol packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
packet.set_sname_and_boot_file_to_zero();
packet.set_opcode(self.message_type.opcode());
packet.set_hardware_type(Hardware::Ethernet);
@ -850,7 +893,11 @@ impl<'a> Repr<'a> {
options = DhcpOption::IpLeaseTime(duration).emit(options);
}
if let Some(list) = self.parameter_request_list {
options = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list }.emit(options);
options = DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST,
data: list,
}
.emit(options);
}
DhcpOption::EndOfList.emit(options);
}
@ -861,75 +908,80 @@ impl<'a> Repr<'a> {
#[cfg(test)]
mod test {
use crate::wire::Ipv4Address;
use super::*;
use crate::wire::Ipv4Address;
const MAGIC_COOKIE: u32 = 0x63825363;
static DISCOVER_BYTES: &[u8] = &[
0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x82, 0x01,
0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00, 0x00,
0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
0x82, 0x01, 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00,
0x00, 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
static ACK_DNS_SERVER_BYTES: &[u8] = &[
0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06, 0x91,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17, 0xeb, 0xc9,
0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x2b,
0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68, 0x79, 0x73,
0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00, 0x03, 0x04, 0x0a,
0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0xa3, 0x01, 0x4a,
0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0xa3,
0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08, 0xff
0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06,
0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17,
0xeb, 0xc9, 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00,
0x2b, 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68,
0x79, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00,
0x03, 0x04, 0x0a, 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a,
0x07, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03,
0xa3, 0x01, 0x4a, 0x04, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08,
0xff,
];
static ACK_LEASE_TIME_BYTES: &[u8] = &[
0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91, 0x62, 0xd2,
0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56, 0x01,
0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91,
0x62, 0xd2, 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56,
0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
@ -971,9 +1023,13 @@ mod test {
assert_eq!(options.len(), 6 + 1 + 7);
let (options, client_id) = DhcpOption::parse(options).unwrap();
assert_eq!(client_id, DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42]
});
assert_eq!(
client_id,
DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST,
data: &[1, 3, 6, 42]
}
);
assert_eq!(options.len(), 1 + 7);
let (options, client_id) = DhcpOption::parse(options).unwrap();
@ -1007,7 +1063,8 @@ mod test {
options = DhcpOption::RequestedIp(IP_NULL).emit(options);
options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(options);
let option = DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42],
kind: field::OPT_PARAMETER_REQUEST_LIST,
data: &[1, 3, 6, 42],
};
options = option.emit(options);
DhcpOption::EndOfList.emit(options);
@ -1106,7 +1163,10 @@ mod test {
let rest = dhcp_option.emit(&mut bytes);
assert_eq!(rest.len(), 0);
}
assert_eq!(&bytes[0..2], &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]);
assert_eq!(
&bytes[0..2],
&[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]
);
assert_eq!(&bytes[2..], DATA);
}
@ -1118,10 +1178,14 @@ mod test {
// The packet described by ACK_BYTES advertises 4 DNS servers
// Here we ensure that we correctly parse the first 3 into our fixed
// length-3 array (see issue #305)
assert_eq!(repr.dns_servers, Some([
Some(Ipv4Address([163, 1, 74, 6])),
Some(Ipv4Address([163, 1, 74, 7])),
Some(Ipv4Address([163, 1, 74, 3]))]));
assert_eq!(
repr.dns_servers,
Some([
Some(Ipv4Address([163, 1, 74, 6])),
Some(Ipv4Address([163, 1, 74, 7])),
Some(Ipv4Address([163, 1, 74, 3]))
])
);
}
#[test]

View File

@ -1,5 +1,5 @@
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
@ -17,8 +17,8 @@ impl fmt::Display for EtherType {
match *self {
EtherType::Ipv4 => write!(f, "IPv4"),
EtherType::Ipv6 => write!(f, "IPv6"),
EtherType::Arp => write!(f, "ARP"),
EtherType::Unknown(id) => write!(f, "0x{:04x}", id)
EtherType::Arp => write!(f, "ARP"),
EtherType::Unknown(id) => write!(f, "0x{:04x}", id),
}
}
}
@ -49,8 +49,7 @@ impl Address {
/// Query whether the address is an unicast address.
pub fn is_unicast(&self) -> bool {
!(self.is_broadcast() ||
self.is_multicast())
!(self.is_broadcast() || self.is_multicast())
}
/// Query whether this address is the broadcast address.
@ -72,8 +71,11 @@ impl Address {
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.0;
write!(f, "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5])
write!(
f,
"{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
)
}
}
@ -81,16 +83,16 @@ impl fmt::Display for Address {
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Frame<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
mod field {
use crate::wire::field::*;
pub const DESTINATION: Field = 0..6;
pub const SOURCE: Field = 6..12;
pub const ETHERTYPE: Field = 12..14;
pub const PAYLOAD: Rest = 14..;
pub const DESTINATION: Field = 0..6;
pub const SOURCE: Field = 6..12;
pub const ETHERTYPE: Field = 12..14;
pub const PAYLOAD: Rest = 14..;
}
/// The Ethernet header length
@ -209,19 +211,27 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Frame<T> {
impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "EthernetII src={} dst={} type={}",
self.src_addr(), self.dst_addr(), self.ethertype())
write!(
f,
"EthernetII src={} dst={} type={}",
self.src_addr(),
self.dst_addr(),
self.ethertype()
)
}
}
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
let frame = match Frame::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(frame) => frame
Err(err) => return write!(f, "{}({})", indent, err),
Ok(frame) => frame,
};
write!(f, "{}{}", indent, frame)?;
@ -241,7 +251,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
indent.increase(f)?;
super::Ipv6Packet::<&[u8]>::pretty_print(&frame.payload(), f, indent)
}
_ => Ok(())
_ => Ok(()),
}
}
}
@ -250,9 +260,9 @@ impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr {
pub src_addr: Address,
pub dst_addr: Address,
pub ethertype: EtherType,
pub src_addr: Address,
pub dst_addr: Address,
pub ethertype: EtherType,
}
impl Repr {
@ -300,32 +310,32 @@ mod test_ipv4 {
// Tests that are valid only with "proto-ipv4"
use super::*;
static FRAME_BYTES: [u8; 64] =
[0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x08, 0x00,
0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
static FRAME_BYTES: [u8; 64] = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x00, 0xaa,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff,
];
static PAYLOAD_BYTES: [u8; 50] =
[0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
static PAYLOAD_BYTES: [u8; 50] = [
0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff,
];
#[test]
fn test_deconstruct() {
let frame = Frame::new_unchecked(&FRAME_BYTES[..]);
assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
assert_eq!(
frame.dst_addr(),
Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
);
assert_eq!(
frame.src_addr(),
Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
);
assert_eq!(frame.ethertype(), EtherType::Ipv4);
assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
}
@ -348,28 +358,30 @@ mod test_ipv6 {
// Tests that are valid only with "proto-ipv6"
use super::*;
static FRAME_BYTES: [u8; 54] =
[0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x86, 0xdd,
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
static FRAME_BYTES: [u8; 54] = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x86, 0xdd, 0x60,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
];
static PAYLOAD_BYTES: [u8; 40] =
[0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
static PAYLOAD_BYTES: [u8; 40] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
];
#[test]
fn test_deconstruct() {
let frame = Frame::new_unchecked(&FRAME_BYTES[..]);
assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
assert_eq!(
frame.dst_addr(),
Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
);
assert_eq!(
frame.src_addr(),
Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
);
assert_eq!(frame.ethertype(), EtherType::Ipv6);
assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
}

View File

@ -1,10 +1,10 @@
use core::{cmp, fmt};
use byteorder::{ByteOrder, NetworkEndian};
use core::{cmp, fmt};
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::checksum;
use crate::wire::{Ipv4Packet, Ipv4Repr};
use crate::{Error, Result};
enum_with_unknown! {
/// Internet protocol control message type.
@ -35,17 +35,17 @@ enum_with_unknown! {
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Message::EchoReply => write!(f, "echo reply"),
Message::EchoReply => write!(f, "echo reply"),
Message::DstUnreachable => write!(f, "destination unreachable"),
Message::Redirect => write!(f, "message redirect"),
Message::EchoRequest => write!(f, "echo request"),
Message::RouterAdvert => write!(f, "router advertisement"),
Message::RouterSolicit => write!(f, "router solicitation"),
Message::TimeExceeded => write!(f, "time exceeded"),
Message::ParamProblem => write!(f, "parameter problem"),
Message::Timestamp => write!(f, "timestamp"),
Message::Redirect => write!(f, "message redirect"),
Message::EchoRequest => write!(f, "echo request"),
Message::RouterAdvert => write!(f, "router advertisement"),
Message::RouterSolicit => write!(f, "router solicitation"),
Message::TimeExceeded => write!(f, "time exceeded"),
Message::ParamProblem => write!(f, "parameter problem"),
Message::Timestamp => write!(f, "timestamp"),
Message::TimestampReply => write!(f, "timestamp reply"),
Message::Unknown(id) => write!(f, "{}", id)
Message::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -91,40 +91,25 @@ enum_with_unknown! {
impl fmt::Display for DstUnreachable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DstUnreachable::NetUnreachable =>
write!(f, "destination network unreachable"),
DstUnreachable::HostUnreachable =>
write!(f, "destination host unreachable"),
DstUnreachable::ProtoUnreachable =>
write!(f, "destination protocol unreachable"),
DstUnreachable::PortUnreachable =>
write!(f, "destination port unreachable"),
DstUnreachable::FragRequired =>
write!(f, "fragmentation required, and DF flag set"),
DstUnreachable::SrcRouteFailed =>
write!(f, "source route failed"),
DstUnreachable::DstNetUnknown =>
write!(f, "destination network unknown"),
DstUnreachable::DstHostUnknown =>
write!(f, "destination host unknown"),
DstUnreachable::SrcHostIsolated =>
write!(f, "source host isolated"),
DstUnreachable::NetProhibited =>
write!(f, "network administratively prohibited"),
DstUnreachable::HostProhibited =>
write!(f, "host administratively prohibited"),
DstUnreachable::NetUnreachToS =>
write!(f, "network unreachable for ToS"),
DstUnreachable::HostUnreachToS =>
write!(f, "host unreachable for ToS"),
DstUnreachable::CommProhibited =>
write!(f, "communication administratively prohibited"),
DstUnreachable::HostPrecedViol =>
write!(f, "host precedence violation"),
DstUnreachable::PrecedCutoff =>
write!(f, "precedence cutoff in effect"),
DstUnreachable::Unknown(id) =>
write!(f, "{}", id)
DstUnreachable::NetUnreachable => write!(f, "destination network unreachable"),
DstUnreachable::HostUnreachable => write!(f, "destination host unreachable"),
DstUnreachable::ProtoUnreachable => write!(f, "destination protocol unreachable"),
DstUnreachable::PortUnreachable => write!(f, "destination port unreachable"),
DstUnreachable::FragRequired => write!(f, "fragmentation required, and DF flag set"),
DstUnreachable::SrcRouteFailed => write!(f, "source route failed"),
DstUnreachable::DstNetUnknown => write!(f, "destination network unknown"),
DstUnreachable::DstHostUnknown => write!(f, "destination host unknown"),
DstUnreachable::SrcHostIsolated => write!(f, "source host isolated"),
DstUnreachable::NetProhibited => write!(f, "network administratively prohibited"),
DstUnreachable::HostProhibited => write!(f, "host administratively prohibited"),
DstUnreachable::NetUnreachToS => write!(f, "network unreachable for ToS"),
DstUnreachable::HostUnreachToS => write!(f, "host unreachable for ToS"),
DstUnreachable::CommProhibited => {
write!(f, "communication administratively prohibited")
}
DstUnreachable::HostPrecedViol => write!(f, "host precedence violation"),
DstUnreachable::PrecedCutoff => write!(f, "precedence cutoff in effect"),
DstUnreachable::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -169,17 +154,17 @@ enum_with_unknown! {
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
mod field {
use crate::wire::field::*;
pub const TYPE: usize = 0;
pub const CODE: usize = 1;
pub const CHECKSUM: Field = 2..4;
pub const TYPE: usize = 0;
pub const CODE: usize = 1;
pub const CHECKSUM: Field = 2..4;
pub const UNUSED: Field = 4..8;
pub const UNUSED: Field = 4..8;
pub const ECHO_IDENT: Field = 4..6;
pub const ECHO_SEQNO: Field = 6..8;
@ -268,10 +253,10 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// The result depends on the value of the message type field.
pub fn header_len(&self) -> usize {
match self.msg_type() {
Message::EchoRequest => field::ECHO_SEQNO.end,
Message::EchoReply => field::ECHO_SEQNO.end,
Message::EchoRequest => field::ECHO_SEQNO.end,
Message::EchoReply => field::ECHO_SEQNO.end,
Message::DstUnreachable => field::UNUSED.end,
_ => field::UNUSED.end // make a conservative assumption
_ => field::UNUSED.end, // make a conservative assumption
}
}
@ -280,7 +265,9 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self) -> bool {
if cfg!(fuzzing) { return true }
if cfg!(fuzzing) {
return true;
}
let data = self.buffer.as_ref();
checksum::data(data) == !0
@ -371,47 +358,49 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
#[non_exhaustive]
pub enum Repr<'a> {
EchoRequest {
ident: u16,
ident: u16,
seq_no: u16,
data: &'a [u8]
data: &'a [u8],
},
EchoReply {
ident: u16,
ident: u16,
seq_no: u16,
data: &'a [u8]
data: &'a [u8],
},
DstUnreachable {
reason: DstUnreachable,
header: Ipv4Repr,
data: &'a [u8]
data: &'a [u8],
},
}
impl<'a> Repr<'a> {
/// Parse an Internet Control Message Protocol version 4 packet and return
/// a high-level representation.
pub fn parse<T>(packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities)
-> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(
packet: &Packet<&'a T>,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
// Valid checksum is expected.
if checksum_caps.icmpv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) }
if checksum_caps.icmpv4.rx() && !packet.verify_checksum() {
return Err(Error::Checksum);
}
match (packet.msg_type(), packet.msg_code()) {
(Message::EchoRequest, 0) => {
Ok(Repr::EchoRequest {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.data()
})
},
(Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.data(),
}),
(Message::EchoReply, 0) => {
Ok(Repr::EchoReply {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.data()
})
},
(Message::EchoReply, 0) => Ok(Repr::EchoReply {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.data(),
}),
(Message::DstUnreachable, code) => {
let ip_packet = Ipv4Packet::new_checked(packet.data())?;
@ -419,7 +408,9 @@ impl<'a> Repr<'a> {
let payload = &packet.data()[ip_packet.header_len() as usize..];
// RFC 792 requires exactly eight bytes to be returned.
// We allow more, since there isn't a reason not to, but require at least eight.
if payload.len() < 8 { return Err(Error::Truncated) }
if payload.len() < 8 {
return Err(Error::Truncated);
}
Ok(Repr::DstUnreachable {
reason: DstUnreachable::from(code),
@ -428,22 +419,21 @@ impl<'a> Repr<'a> {
dst_addr: ip_packet.dst_addr(),
protocol: ip_packet.protocol(),
payload_len: payload.len(),
hop_limit: ip_packet.hop_limit()
hop_limit: ip_packet.hop_limit(),
},
data: payload
data: payload,
})
}
_ => Err(Error::Unrecognized)
_ => Err(Error::Unrecognized),
}
}
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
&Repr::EchoRequest { data, .. } |
&Repr::EchoReply { data, .. } => {
&Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
field::ECHO_SEQNO.end + data.len()
},
}
&Repr::DstUnreachable { header, data, .. } => {
field::UNUSED.end + header.buffer_len() + data.len()
}
@ -453,28 +443,42 @@ impl<'a> Repr<'a> {
/// Emit a high-level representation into an Internet Control Message Protocol version 4
/// packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
packet.set_msg_code(0);
match *self {
Repr::EchoRequest { ident, seq_no, data } => {
Repr::EchoRequest {
ident,
seq_no,
data,
} => {
packet.set_msg_type(Message::EchoRequest);
packet.set_msg_code(0);
packet.set_echo_ident(ident);
packet.set_echo_seq_no(seq_no);
let data_len = cmp::min(packet.data_mut().len(), data.len());
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
},
}
Repr::EchoReply { ident, seq_no, data } => {
Repr::EchoReply {
ident,
seq_no,
data,
} => {
packet.set_msg_type(Message::EchoReply);
packet.set_msg_code(0);
packet.set_echo_ident(ident);
packet.set_echo_seq_no(seq_no);
let data_len = cmp::min(packet.data_mut().len(), data.len());
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
},
}
Repr::DstUnreachable { reason, header, data } => {
Repr::DstUnreachable {
reason,
header,
data,
} => {
packet.set_msg_type(Message::DstUnreachable);
packet.set_msg_code(reason.into());
@ -503,9 +507,10 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
write!(f, "ICMPv4 ({})", err)?;
write!(f, " type={:?}", self.msg_type())?;
match self.msg_type() {
Message::DstUnreachable =>
write!(f, " code={:?}", DstUnreachable::from(self.msg_code())),
_ => write!(f, " code={}", self.msg_code())
Message::DstUnreachable => {
write!(f, " code={:?}", DstUnreachable::from(self.msg_code()))
}
_ => write!(f, " code={}", self.msg_code()),
}
}
}
@ -515,27 +520,46 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Repr::EchoRequest { ident, seq_no, data } =>
write!(f, "ICMPv4 echo request id={} seq={} len={}",
ident, seq_no, data.len()),
Repr::EchoReply { ident, seq_no, data } =>
write!(f, "ICMPv4 echo reply id={} seq={} len={}",
ident, seq_no, data.len()),
Repr::DstUnreachable { reason, .. } =>
write!(f, "ICMPv4 destination unreachable ({})",
reason),
Repr::EchoRequest {
ident,
seq_no,
data,
} => write!(
f,
"ICMPv4 echo request id={} seq={} len={}",
ident,
seq_no,
data.len()
),
Repr::EchoReply {
ident,
seq_no,
data,
} => write!(
f,
"ICMPv4 echo reply id={} seq={} len={}",
ident,
seq_no,
data.len()
),
Repr::DstUnreachable { reason, .. } => {
write!(f, "ICMPv4 destination unreachable ({})", reason)
}
}
}
}
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
let packet = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(packet) => packet
Err(err) => return write!(f, "{}({})", indent, err),
Ok(packet) => packet,
};
write!(f, "{}{}", indent, packet)?;
@ -544,7 +568,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
indent.increase(f)?;
super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent)
}
_ => Ok(())
_ => Ok(()),
}
}
}
@ -553,13 +577,11 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test {
use super::*;
static ECHO_PACKET_BYTES: [u8; 12] =
[0x08, 0x00, 0x8e, 0xfe,
0x12, 0x34, 0xab, 0xcd,
0xaa, 0x00, 0x00, 0xff];
static ECHO_PACKET_BYTES: [u8; 12] = [
0x08, 0x00, 0x8e, 0xfe, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
];
static ECHO_DATA_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
static ECHO_DATA_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
#[test]
fn test_echo_deconstruct() {
@ -590,7 +612,7 @@ mod test {
Repr::EchoRequest {
ident: 0x1234,
seq_no: 0xabcd,
data: &ECHO_DATA_BYTES
data: &ECHO_DATA_BYTES,
}
}
@ -612,8 +634,7 @@ mod test {
#[test]
fn test_check_len() {
let bytes = [0x0b, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00];
let bytes = [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
assert_eq!(Packet::new_checked(&[]), Err(Error::Truncated));
assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error::Truncated));
assert!(Packet::new_checked(&bytes[..]).is_ok());

View File

@ -1,13 +1,13 @@
use core::{cmp, fmt};
use byteorder::{ByteOrder, NetworkEndian};
use core::{cmp, fmt};
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::checksum;
use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
use crate::wire::MldRepr;
#[cfg(feature = "medium-ethernet")]
use crate::wire::NdiscRepr;
use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
use crate::{Error, Result};
enum_with_unknown! {
/// Internet protocol control message type.
@ -57,8 +57,11 @@ impl Message {
/// [NDISC]: https://tools.ietf.org/html/rfc4861
pub fn is_ndisc(&self) -> bool {
match *self {
Message::RouterSolicit | Message::RouterAdvert | Message::NeighborSolicit |
Message::NeighborAdvert | Message::Redirect => true,
Message::RouterSolicit
| Message::RouterAdvert
| Message::NeighborSolicit
| Message::NeighborAdvert
| Message::Redirect => true,
_ => false,
}
}
@ -78,20 +81,20 @@ impl Message {
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Message::DstUnreachable => write!(f, "destination unreachable"),
Message::PktTooBig => write!(f, "packet too big"),
Message::TimeExceeded => write!(f, "time exceeded"),
Message::ParamProblem => write!(f, "parameter problem"),
Message::EchoReply => write!(f, "echo reply"),
Message::EchoRequest => write!(f, "echo request"),
Message::RouterSolicit => write!(f, "router solicitation"),
Message::RouterAdvert => write!(f, "router advertisement"),
Message::DstUnreachable => write!(f, "destination unreachable"),
Message::PktTooBig => write!(f, "packet too big"),
Message::TimeExceeded => write!(f, "time exceeded"),
Message::ParamProblem => write!(f, "parameter problem"),
Message::EchoReply => write!(f, "echo reply"),
Message::EchoRequest => write!(f, "echo request"),
Message::RouterSolicit => write!(f, "router solicitation"),
Message::RouterAdvert => write!(f, "router advertisement"),
Message::NeighborSolicit => write!(f, "neighbor solicitation"),
Message::NeighborAdvert => write!(f, "neighbor advert"),
Message::Redirect => write!(f, "redirect"),
Message::MldQuery => write!(f, "multicast listener query"),
Message::MldReport => write!(f, "multicast listener report"),
Message::Unknown(id) => write!(f, "{}", id)
Message::NeighborAdvert => write!(f, "neighbor advert"),
Message::Redirect => write!(f, "redirect"),
Message::MldQuery => write!(f, "multicast listener query"),
Message::MldReport => write!(f, "multicast listener report"),
Message::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -119,22 +122,19 @@ enum_with_unknown! {
impl fmt::Display for DstUnreachable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DstUnreachable::NoRoute =>
write!(f, "no route to destination"),
DstUnreachable::AdminProhibit =>
write!(f, "communication with destination administratively prohibited"),
DstUnreachable::BeyondScope =>
write!(f, "beyond scope of source address"),
DstUnreachable::AddrUnreachable =>
write!(f, "address unreachable"),
DstUnreachable::PortUnreachable =>
write!(f, "port unreachable"),
DstUnreachable::FailedPolicy =>
write!(f, "source address failed ingress/egress policy"),
DstUnreachable::RejectRoute =>
write!(f, "reject route to destination"),
DstUnreachable::Unknown(id) =>
write!(f, "{}", id)
DstUnreachable::NoRoute => write!(f, "no route to destination"),
DstUnreachable::AdminProhibit => write!(
f,
"communication with destination administratively prohibited"
),
DstUnreachable::BeyondScope => write!(f, "beyond scope of source address"),
DstUnreachable::AddrUnreachable => write!(f, "address unreachable"),
DstUnreachable::PortUnreachable => write!(f, "port unreachable"),
DstUnreachable::FailedPolicy => {
write!(f, "source address failed ingress/egress policy")
}
DstUnreachable::RejectRoute => write!(f, "reject route to destination"),
DstUnreachable::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -154,14 +154,10 @@ enum_with_unknown! {
impl fmt::Display for ParamProblem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParamProblem::ErroneousHdrField =>
write!(f, "erroneous header field."),
ParamProblem::UnrecognizedNxtHdr =>
write!(f, "unrecognized next header type."),
ParamProblem::UnrecognizedOption =>
write!(f, "unrecognized IPv6 option."),
ParamProblem::Unknown(id) =>
write!(f, "{}", id)
ParamProblem::ErroneousHdrField => write!(f, "erroneous header field."),
ParamProblem::UnrecognizedNxtHdr => write!(f, "unrecognized next header type."),
ParamProblem::UnrecognizedOption => write!(f, "unrecognized IPv6 option."),
ParamProblem::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -179,12 +175,9 @@ enum_with_unknown! {
impl fmt::Display for TimeExceeded {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
TimeExceeded::HopLimitExceeded =>
write!(f, "hop limit exceeded in transit"),
TimeExceeded::FragReassemExceeded =>
write!(f, "fragment reassembly time exceeded"),
TimeExceeded::Unknown(id) =>
write!(f, "{}", id)
TimeExceeded::HopLimitExceeded => write!(f, "hop limit exceeded in transit"),
TimeExceeded::FragReassemExceeded => write!(f, "fragment reassembly time exceeded"),
TimeExceeded::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -193,7 +186,7 @@ impl fmt::Display for TimeExceeded {
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
pub(super) buffer: T
pub(super) buffer: T,
}
// Ranges and constants describing key boundaries in the ICMPv6 header.
@ -201,54 +194,54 @@ pub(super) mod field {
use crate::wire::field::*;
// ICMPv6: See https://tools.ietf.org/html/rfc4443
pub const TYPE: usize = 0;
pub const CODE: usize = 1;
pub const CHECKSUM: Field = 2..4;
pub const TYPE: usize = 0;
pub const CODE: usize = 1;
pub const CHECKSUM: Field = 2..4;
pub const UNUSED: Field = 4..8;
pub const MTU: Field = 4..8;
pub const POINTER: Field = 4..8;
pub const ECHO_IDENT: Field = 4..6;
pub const ECHO_SEQNO: Field = 6..8;
pub const UNUSED: Field = 4..8;
pub const MTU: Field = 4..8;
pub const POINTER: Field = 4..8;
pub const ECHO_IDENT: Field = 4..6;
pub const ECHO_SEQNO: Field = 6..8;
pub const HEADER_END: usize = 8;
pub const HEADER_END: usize = 8;
// NDISC: See https://tools.ietf.org/html/rfc4861
// Router Advertisement message offsets
pub const CUR_HOP_LIMIT: usize = 4;
pub const ROUTER_FLAGS: usize = 5;
pub const ROUTER_LT: Field = 6..8;
pub const REACHABLE_TM: Field = 8..12;
pub const RETRANS_TM: Field = 12..16;
pub const CUR_HOP_LIMIT: usize = 4;
pub const ROUTER_FLAGS: usize = 5;
pub const ROUTER_LT: Field = 6..8;
pub const REACHABLE_TM: Field = 8..12;
pub const RETRANS_TM: Field = 12..16;
// Neighbor Solicitation message offsets
pub const TARGET_ADDR: Field = 8..24;
pub const TARGET_ADDR: Field = 8..24;
// Neighbor Advertisement message offsets
pub const NEIGH_FLAGS: usize = 4;
pub const NEIGH_FLAGS: usize = 4;
// Redirected Header message offsets
pub const DEST_ADDR: Field = 24..40;
pub const DEST_ADDR: Field = 24..40;
// MLD:
// - https://tools.ietf.org/html/rfc3810
// - https://tools.ietf.org/html/rfc3810
// Multicast Listener Query message
pub const MAX_RESP_CODE: Field = 4..6;
pub const QUERY_RESV: Field = 6..8;
pub const QUERY_MCAST_ADDR: Field = 8..24;
pub const SQRV: usize = 24;
pub const QQIC: usize = 25;
pub const QUERY_NUM_SRCS: Field = 26..28;
pub const MAX_RESP_CODE: Field = 4..6;
pub const QUERY_RESV: Field = 6..8;
pub const QUERY_MCAST_ADDR: Field = 8..24;
pub const SQRV: usize = 24;
pub const QQIC: usize = 25;
pub const QUERY_NUM_SRCS: Field = 26..28;
// Multicast Listener Report Message
pub const RECORD_RESV: Field = 4..6;
pub const NR_MCAST_RCRDS: Field = 6..8;
pub const RECORD_RESV: Field = 4..6;
pub const NR_MCAST_RCRDS: Field = 6..8;
// Multicast Address Record Offsets
pub const RECORD_TYPE: usize = 0;
pub const AUX_DATA_LEN: usize = 1;
pub const RECORD_NUM_SRCS: Field = 2..4;
pub const RECORD_TYPE: usize = 0;
pub const AUX_DATA_LEN: usize = 1;
pub const RECORD_NUM_SRCS: Field = 2..4;
pub const RECORD_MCAST_ADDR: Field = 4..20;
}
@ -333,29 +326,28 @@ impl<T: AsRef<[u8]>> Packet<T> {
NetworkEndian::read_u32(&data[field::POINTER])
}
/// Return the header length. The result depends on the value of
/// the message type field.
pub fn header_len(&self) -> usize {
match self.msg_type() {
Message::DstUnreachable => field::UNUSED.end,
Message::PktTooBig => field::MTU.end,
Message::TimeExceeded => field::UNUSED.end,
Message::ParamProblem => field::POINTER.end,
Message::EchoRequest => field::ECHO_SEQNO.end,
Message::EchoReply => field::ECHO_SEQNO.end,
Message::RouterSolicit => field::UNUSED.end,
Message::RouterAdvert => field::RETRANS_TM.end,
Message::DstUnreachable => field::UNUSED.end,
Message::PktTooBig => field::MTU.end,
Message::TimeExceeded => field::UNUSED.end,
Message::ParamProblem => field::POINTER.end,
Message::EchoRequest => field::ECHO_SEQNO.end,
Message::EchoReply => field::ECHO_SEQNO.end,
Message::RouterSolicit => field::UNUSED.end,
Message::RouterAdvert => field::RETRANS_TM.end,
Message::NeighborSolicit => field::TARGET_ADDR.end,
Message::NeighborAdvert => field::TARGET_ADDR.end,
Message::Redirect => field::DEST_ADDR.end,
Message::MldQuery => field::QUERY_NUM_SRCS.end,
Message::MldReport => field::NR_MCAST_RCRDS.end,
Message::NeighborAdvert => field::TARGET_ADDR.end,
Message::Redirect => field::DEST_ADDR.end,
Message::MldQuery => field::QUERY_NUM_SRCS.end,
Message::MldReport => field::NR_MCAST_RCRDS.end,
// For packets that are not included in RFC 4443, do not
// include the last 32 bits of the ICMPv6 header in
// `header_bytes`. This must be done so that these bytes
// can be accessed in the `payload`.
_ => field::CHECKSUM.end
_ => field::CHECKSUM.end,
}
}
@ -364,13 +356,14 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
if cfg!(fuzzing) { return true }
if cfg!(fuzzing) {
return true;
}
let data = self.buffer.as_ref();
checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6,
data.len() as u32),
checksum::data(data)
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
checksum::data(data),
]) == !0
}
}
@ -409,16 +402,18 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
#[inline]
pub fn clear_reserved(&mut self) {
match self.msg_type() {
Message::RouterSolicit | Message::NeighborSolicit |
Message::NeighborAdvert | Message::Redirect => {
Message::RouterSolicit
| Message::NeighborSolicit
| Message::NeighborAdvert
| Message::Redirect => {
let data = self.buffer.as_mut();
NetworkEndian::write_u32(&mut data[field::UNUSED], 0);
},
}
Message::MldQuery => {
let data = self.buffer.as_mut();
NetworkEndian::write_u16(&mut data[field::QUERY_RESV], 0);
data[field::SQRV] &= 0xf;
},
}
Message::MldReport => {
let data = self.buffer.as_mut();
NetworkEndian::write_u16(&mut data[field::RECORD_RESV], 0);
@ -479,9 +474,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
let checksum = {
let data = self.buffer.as_ref();
!checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6,
data.len() as u32),
checksum::data(data)
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
checksum::data(data),
])
};
self.set_checksum(checksum)
@ -510,33 +504,33 @@ pub enum Repr<'a> {
DstUnreachable {
reason: DstUnreachable,
header: Ipv6Repr,
data: &'a [u8]
data: &'a [u8],
},
PktTooBig {
mtu: u32,
header: Ipv6Repr,
data: &'a [u8]
data: &'a [u8],
},
TimeExceeded {
reason: TimeExceeded,
header: Ipv6Repr,
data: &'a [u8]
data: &'a [u8],
},
ParamProblem {
reason: ParamProblem,
reason: ParamProblem,
pointer: u32,
header: Ipv6Repr,
data: &'a [u8]
header: Ipv6Repr,
data: &'a [u8],
},
EchoRequest {
ident: u16,
ident: u16,
seq_no: u16,
data: &'a [u8]
data: &'a [u8],
},
EchoReply {
ident: u16,
ident: u16,
seq_no: u16,
data: &'a [u8]
data: &'a [u8],
},
#[cfg(feature = "medium-ethernet")]
Ndisc(NdiscRepr<'a>),
@ -546,29 +540,37 @@ pub enum Repr<'a> {
impl<'a> Repr<'a> {
/// Parse an Internet Control Message Protocol version 6 packet and return
/// a high-level representation.
pub fn parse<T>(src_addr: &IpAddress, dst_addr: &IpAddress,
packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities)
-> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>)
-> Result<(&'a [u8], Ipv6Repr)>
where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(
src_addr: &IpAddress,
dst_addr: &IpAddress,
packet: &Packet<&'a T>,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>) -> Result<(&'a [u8], Ipv6Repr)>
where
T: AsRef<[u8]> + ?Sized,
{
let ip_packet = Ipv6Packet::new_checked(packet.payload())?;
let payload = &packet.payload()[ip_packet.header_len() as usize..];
if payload.len() < 8 { return Err(Error::Truncated) }
if payload.len() < 8 {
return Err(Error::Truncated);
}
let repr = Ipv6Repr {
src_addr: ip_packet.src_addr(),
dst_addr: ip_packet.dst_addr(),
next_header: ip_packet.next_header(),
payload_len: payload.len(),
hop_limit: ip_packet.hop_limit()
hop_limit: ip_packet.hop_limit(),
};
Ok((payload, repr))
}
// Valid checksum is expected.
if checksum_caps.icmpv6.rx() && !packet.verify_checksum(src_addr, dst_addr) {
return Err(Error::Checksum)
return Err(Error::Checksum);
}
match (packet.msg_type(), packet.msg_code()) {
@ -577,85 +579,80 @@ impl<'a> Repr<'a> {
Ok(Repr::DstUnreachable {
reason: DstUnreachable::from(code),
header: repr,
data: payload
data: payload,
})
},
}
(Message::PktTooBig, 0) => {
let (payload, repr) = create_packet_from_payload(packet)?;
Ok(Repr::PktTooBig {
mtu: packet.pkt_too_big_mtu(),
header: repr,
data: payload
data: payload,
})
},
}
(Message::TimeExceeded, code) => {
let (payload, repr) = create_packet_from_payload(packet)?;
Ok(Repr::TimeExceeded {
reason: TimeExceeded::from(code),
header: repr,
data: payload
data: payload,
})
},
}
(Message::ParamProblem, code) => {
let (payload, repr) = create_packet_from_payload(packet)?;
Ok(Repr::ParamProblem {
reason: ParamProblem::from(code),
pointer: packet.param_problem_ptr(),
header: repr,
data: payload
data: payload,
})
},
(Message::EchoRequest, 0) => {
Ok(Repr::EchoRequest {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.payload()
})
},
(Message::EchoReply, 0) => {
Ok(Repr::EchoReply {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.payload()
})
},
}
(Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.payload(),
}),
(Message::EchoReply, 0) => Ok(Repr::EchoReply {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.payload(),
}),
#[cfg(feature = "medium-ethernet")]
(msg_type, 0) if msg_type.is_ndisc() => {
NdiscRepr::parse(packet).map(Repr::Ndisc)
},
(msg_type, 0) if msg_type.is_mld() => {
MldRepr::parse(packet).map(Repr::Mld)
},
_ => Err(Error::Unrecognized)
(msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc),
(msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld),
_ => Err(Error::Unrecognized),
}
}
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
&Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } |
&Repr::TimeExceeded { header, data, .. } | &Repr::ParamProblem { header, data, .. } => {
&Repr::DstUnreachable { header, data, .. }
| &Repr::PktTooBig { header, data, .. }
| &Repr::TimeExceeded { header, data, .. }
| &Repr::ParamProblem { header, data, .. } => {
field::UNUSED.end + header.buffer_len() + data.len()
}
&Repr::EchoRequest { data, .. } |
&Repr::EchoReply { data, .. } => {
&Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
field::ECHO_SEQNO.end + data.len()
},
}
#[cfg(feature = "medium-ethernet")]
&Repr::Ndisc(ndisc) => {
ndisc.buffer_len()
},
&Repr::Mld(mld) => {
mld.buffer_len()
},
&Repr::Ndisc(ndisc) => ndisc.buffer_len(),
&Repr::Mld(mld) => mld.buffer_len(),
}
}
/// Emit a high-level representation into an Internet Control Message Protocol version 6
/// packet.
pub fn emit<T>(&self, src_addr: &IpAddress, dst_addr: &IpAddress,
packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
pub fn emit<T>(
&self,
src_addr: &IpAddress,
dst_addr: &IpAddress,
packet: &mut Packet<&mut T>,
checksum_caps: &ChecksumCapabilities,
) where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
fn emit_contained_packet(buffer: &mut [u8], header: Ipv6Repr, data: &[u8]) {
let mut ip_packet = Ipv6Packet::new_unchecked(buffer);
header.emit(&mut ip_packet);
@ -664,12 +661,16 @@ impl<'a> Repr<'a> {
}
match *self {
Repr::DstUnreachable { reason, header, data } => {
Repr::DstUnreachable {
reason,
header,
data,
} => {
packet.set_msg_type(Message::DstUnreachable);
packet.set_msg_code(reason.into());
emit_contained_packet(packet.payload_mut(), header, &data);
},
}
Repr::PktTooBig { mtu, header, data } => {
packet.set_msg_type(Message::PktTooBig);
@ -677,49 +678,62 @@ impl<'a> Repr<'a> {
packet.set_pkt_too_big_mtu(mtu);
emit_contained_packet(packet.payload_mut(), header, &data);
},
}
Repr::TimeExceeded { reason, header, data } => {
Repr::TimeExceeded {
reason,
header,
data,
} => {
packet.set_msg_type(Message::TimeExceeded);
packet.set_msg_code(reason.into());
emit_contained_packet(packet.payload_mut(), header, &data);
},
}
Repr::ParamProblem { reason, pointer, header, data } => {
Repr::ParamProblem {
reason,
pointer,
header,
data,
} => {
packet.set_msg_type(Message::ParamProblem);
packet.set_msg_code(reason.into());
packet.set_param_problem_ptr(pointer);
emit_contained_packet(packet.payload_mut(), header, &data);
},
}
Repr::EchoRequest { ident, seq_no, data } => {
Repr::EchoRequest {
ident,
seq_no,
data,
} => {
packet.set_msg_type(Message::EchoRequest);
packet.set_msg_code(0);
packet.set_echo_ident(ident);
packet.set_echo_seq_no(seq_no);
let data_len = cmp::min(packet.payload_mut().len(), data.len());
packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
},
}
Repr::EchoReply { ident, seq_no, data } => {
Repr::EchoReply {
ident,
seq_no,
data,
} => {
packet.set_msg_type(Message::EchoReply);
packet.set_msg_code(0);
packet.set_echo_ident(ident);
packet.set_echo_seq_no(seq_no);
let data_len = cmp::min(packet.payload_mut().len(), data.len());
packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
},
}
#[cfg(feature = "medium-ethernet")]
Repr::Ndisc(ndisc) => {
ndisc.emit(packet)
},
Repr::Ndisc(ndisc) => ndisc.emit(packet),
Repr::Mld(mld) => {
mld.emit(packet)
},
Repr::Mld(mld) => mld.emit(packet),
}
if checksum_caps.icmpv6.tx() {
@ -733,60 +747,39 @@ impl<'a> Repr<'a> {
#[cfg(test)]
mod test {
use crate::wire::{Ipv6Address, Ipv6Repr, IpProtocol};
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
use super::*;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
use crate::wire::{IpProtocol, Ipv6Address, Ipv6Repr};
static ECHO_PACKET_BYTES: [u8; 12] =
[0x80, 0x00, 0x19, 0xb3,
0x12, 0x34, 0xab, 0xcd,
0xaa, 0x00, 0x00, 0xff];
static ECHO_PACKET_BYTES: [u8; 12] = [
0x80, 0x00, 0x19, 0xb3, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
];
static ECHO_PACKET_PAYLOAD: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
static ECHO_PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
static PKT_TOO_BIG_BYTES: [u8; 60] =
[0x02, 0x00, 0x0f, 0xc9,
0x00, 0x00, 0x05, 0xdc,
0x60, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x11, 0x40,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x12, 0x4d,
0xaa, 0x00, 0x00, 0xff];
static PKT_TOO_BIG_BYTES: [u8; 60] = [
0x02, 0x00, 0x0f, 0xc9, 0x00, 0x00, 0x05, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11,
0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
];
static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] =
[0x60, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x11, 0x40,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x12, 0x4d,
0xaa, 0x00, 0x00, 0xff];
static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00,
0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
];
static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] =
[0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x12, 0x4d,
0xaa, 0x00, 0x00, 0xff];
static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] = [
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
];
fn echo_packet_repr() -> Repr<'static> {
Repr::EchoRequest {
ident: 0x1234,
seq_no: 0xabcd,
data: &ECHO_PACKET_PAYLOAD
data: &ECHO_PACKET_PAYLOAD,
}
}
@ -794,17 +787,17 @@ mod test {
Repr::PktTooBig {
mtu: 1500,
header: Ipv6Repr {
src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]),
dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02]),
src_addr: Ipv6Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
]),
dst_addr: Ipv6Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02,
]),
next_header: IpProtocol::Udp,
payload_len: 12,
hop_limit: 0x40
hop_limit: 0x40,
},
data: &PKT_TOO_BIG_UDP_PAYLOAD,
}
@ -819,7 +812,10 @@ mod test {
assert_eq!(packet.echo_ident(), 0x1234);
assert_eq!(packet.echo_seq_no(), 0xabcd);
assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]);
assert_eq!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), true);
assert_eq!(
packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2),
true
);
assert!(!packet.msg_type().is_error());
}
@ -831,7 +827,9 @@ mod test {
packet.set_msg_code(0);
packet.set_echo_ident(0x1234);
packet.set_echo_seq_no(0xabcd);
packet.payload_mut().copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
packet
.payload_mut()
.copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
}
@ -839,8 +837,13 @@ mod test {
#[test]
fn test_echo_repr_parse() {
let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
let repr = Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&packet, &ChecksumCapabilities::default()).unwrap();
let repr = Repr::parse(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&packet,
&ChecksumCapabilities::default(),
)
.unwrap();
assert_eq!(repr, echo_packet_repr());
}
@ -849,8 +852,12 @@ mod test {
let repr = echo_packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&mut packet, &ChecksumCapabilities::default());
repr.emit(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&mut packet,
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
}
@ -862,7 +869,10 @@ mod test {
assert_eq!(packet.checksum(), 0x0fc9);
assert_eq!(packet.pkt_too_big_mtu(), 1500);
assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]);
assert_eq!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), true);
assert_eq!(
packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2),
true
);
assert!(packet.msg_type().is_error());
}
@ -873,7 +883,9 @@ mod test {
packet.set_msg_type(Message::PktTooBig);
packet.set_msg_code(0);
packet.set_pkt_too_big_mtu(1500);
packet.payload_mut().copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
packet
.payload_mut()
.copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
}
@ -881,8 +893,13 @@ mod test {
#[test]
fn test_too_big_repr_parse() {
let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]);
let repr = Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&packet, &ChecksumCapabilities::default()).unwrap();
let repr = Repr::parse(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&packet,
&ChecksumCapabilities::default(),
)
.unwrap();
assert_eq!(repr, too_big_packet_repr());
}
@ -891,8 +908,12 @@ mod test {
let repr = too_big_packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&mut packet, &ChecksumCapabilities::default());
repr.emit(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&mut packet,
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
}
}

View File

@ -1,9 +1,9 @@
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::wire::ip::checksum;
use crate::time::Duration;
use crate::wire::ip::checksum;
use crate::{Error, Result};
use crate::wire::Ipv4Address;
@ -202,7 +202,8 @@ impl Repr {
/// Parse an Internet Group Management Protocol v1/v2 packet and return
/// a high-level representation.
pub fn parse<T>(packet: &Packet<&T>) -> Result<Repr>
where T: AsRef<[u8]> + ?Sized
where
T: AsRef<[u8]> + ?Sized,
{
// Check if the address is 0.0.0.0 or multicast
let addr = packet.group_addr();
@ -226,13 +227,13 @@ impl Repr {
version,
})
}
Message::MembershipReportV2 => {
Ok(Repr::MembershipReport {
group_addr: packet.group_addr(),
version: IgmpVersion::Version2,
})
}
Message::LeaveGroup => Ok(Repr::LeaveGroup { group_addr: packet.group_addr() }),
Message::MembershipReportV2 => Ok(Repr::MembershipReport {
group_addr: packet.group_addr(),
version: IgmpVersion::Version2,
}),
Message::LeaveGroup => Ok(Repr::LeaveGroup {
group_addr: packet.group_addr(),
}),
Message::MembershipReportV1 => {
// for backwards compatibility with IGMPv1
Ok(Repr::MembershipReport {
@ -252,20 +253,21 @@ impl Repr {
/// Emit a high-level representation into an Internet Group Management Protocol v2 packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
match *self {
Repr::MembershipQuery {
max_resp_time,
group_addr,
version
version,
} => {
packet.set_msg_type(Message::MembershipQuery);
match version {
IgmpVersion::Version1 =>
packet.set_max_resp_code(0),
IgmpVersion::Version2 =>
packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time)),
IgmpVersion::Version1 => packet.set_max_resp_code(0),
IgmpVersion::Version2 => {
packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time))
}
}
packet.set_group_address(group_addr);
}
@ -336,20 +338,21 @@ impl<'a> fmt::Display for Repr {
group_addr,
version,
} => {
write!(f,
"IGMP membership query max_resp_time={} group_addr={} version={:?}",
max_resp_time,
group_addr,
version)
write!(
f,
"IGMP membership query max_resp_time={} group_addr={} version={:?}",
max_resp_time, group_addr, version
)
}
Repr::MembershipReport {
group_addr,
version,
} => {
write!(f,
"IGMP membership report group_addr={} version={:?}",
group_addr,
version)
write!(
f,
"IGMP membership report group_addr={} version={:?}",
group_addr, version
)
}
Repr::LeaveGroup { group_addr } => {
write!(f, "IGMP leave group group_addr={})", group_addr)
@ -361,10 +364,11 @@ impl<'a> fmt::Display for Repr {
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent)
-> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
match Packet::new_checked(buffer) {
Err(err) => writeln!(f, "{}({})", indent, err),
Ok(packet) => writeln!(f, "{}{}", indent, packet),
@ -376,7 +380,6 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test {
use super::*;
static LEAVE_PACKET_BYTES: [u8; 8] = [0x17, 0x00, 0x02, 0x69, 0xe0, 0x00, 0x06, 0x96];
static REPORT_PACKET_BYTES: [u8; 8] = [0x16, 0x00, 0x08, 0xda, 0xe1, 0x00, 0x00, 0x25];
@ -386,8 +389,10 @@ mod test {
assert_eq!(packet.msg_type(), Message::LeaveGroup);
assert_eq!(packet.max_resp_code(), 0);
assert_eq!(packet.checksum(), 0x269);
assert_eq!(packet.group_addr(),
Ipv4Address::from_bytes(&[224, 0, 6, 150]));
assert_eq!(
packet.group_addr(),
Ipv4Address::from_bytes(&[224, 0, 6, 150])
);
assert_eq!(packet.verify_checksum(), true);
}
@ -397,8 +402,10 @@ mod test {
assert_eq!(packet.msg_type(), Message::MembershipReportV2);
assert_eq!(packet.max_resp_code(), 0);
assert_eq!(packet.checksum(), 0x08da);
assert_eq!(packet.group_addr(),
Ipv4Address::from_bytes(&[225, 0, 0, 37]));
assert_eq!(
packet.group_addr(),
Ipv4Address::from_bytes(&[225, 0, 0, 37])
);
assert_eq!(packet.verify_checksum(), true);
}
@ -437,9 +444,7 @@ mod test {
#[test]
fn duration_to_max_resp_time_max() {
for duration in 31744..65536 {
let time = duration_to_max_resp_code(
Duration::from_millis(duration * 100)
);
let time = duration_to_max_resp_code(Duration::from_millis(duration * 100));
assert_eq!(time, 0xFF);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::{checksum, pretty_print_ip_payload};
use crate::{Error, Result};
pub use super::IpProtocol as Protocol;
@ -27,10 +27,10 @@ pub struct Address(pub [u8; 4]);
impl Address {
/// An unspecified address.
pub const UNSPECIFIED: Address = Address([0x00; 4]);
pub const UNSPECIFIED: Address = Address([0x00; 4]);
/// The broadcast address.
pub const BROADCAST: Address = Address([0xff; 4]);
pub const BROADCAST: Address = Address([0xff; 4]);
/// All multicast-capable nodes
pub const MULTICAST_ALL_SYSTEMS: Address = Address([224, 0, 0, 1]);
@ -60,9 +60,7 @@ impl Address {
/// Query whether the address is an unicast address.
pub fn is_unicast(&self) -> bool {
!(self.is_broadcast() ||
self.is_multicast() ||
self.is_unspecified())
!(self.is_broadcast() || self.is_multicast() || self.is_unspecified())
}
/// Query whether the address is the broadcast address.
@ -115,7 +113,14 @@ impl fmt::Display for Address {
#[cfg(feature = "defmt")]
impl defmt::Format for Address {
fn format(&self, f: defmt::Formatter) {
defmt::write!(f, "{=u8}.{=u8}.{=u8}.{=u8}", self.0[0], self.0[1], self.0[2], self.0[3])
defmt::write!(
f,
"{=u8}.{=u8}.{=u8}.{=u8}",
self.0[0],
self.0[1],
self.0[2],
self.0[3]
)
}
}
@ -123,7 +128,7 @@ impl defmt::Format for Address {
/// subnet masking prefix length.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct Cidr {
address: Address,
address: Address,
prefix_len: u8,
}
@ -137,14 +142,20 @@ impl Cidr {
// Replace with const panic (or assert) when stabilized
// see: https://github.com/rust-lang/rust/issues/51999
["Prefix length should be <= 32"][(prefix_len > 32) as usize];
Cidr { address, prefix_len }
Cidr {
address,
prefix_len,
}
}
/// Create an IPv4 CIDR block from the given address and network mask.
pub fn from_netmask(addr: Address, netmask: Address) -> Result<Cidr> {
let netmask = NetworkEndian::read_u32(&netmask.0[..]);
if netmask.leading_zeros() == 0 && netmask.trailing_zeros() == netmask.count_zeros() {
Ok(Cidr { address: addr, prefix_len: netmask.count_ones() as u8 })
Ok(Cidr {
address: addr,
prefix_len: netmask.count_ones() as u8,
})
} else {
Err(Error::Illegal)
}
@ -170,8 +181,8 @@ impl Cidr {
let data = [
((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
];
Address(data)
@ -190,8 +201,8 @@ impl Cidr {
let data = [
((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
];
Some(Address(data))
@ -206,14 +217,19 @@ impl Cidr {
self.address.0[2] & mask[2],
self.address.0[3] & mask[3],
];
Cidr { address: Address(network), prefix_len: self.prefix_len }
Cidr {
address: Address(network),
prefix_len: self.prefix_len,
}
}
/// Query whether the subnetwork described by this IPv4 CIDR block contains
/// the given address.
pub fn contains_addr(&self, addr: &Address) -> bool {
// right shift by 32 is not legal
if self.prefix_len == 0 { return true }
if self.prefix_len == 0 {
return true;
}
let shift = 32 - self.prefix_len;
let self_prefix = NetworkEndian::read_u32(self.address.as_bytes()) >> shift;
@ -245,18 +261,18 @@ impl defmt::Format for Cidr {
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
mod field {
use crate::wire::field::*;
pub const VER_IHL: usize = 0;
pub const VER_IHL: usize = 0;
pub const DSCP_ECN: usize = 1;
pub const LENGTH: Field = 2..4;
pub const IDENT: Field = 4..6;
pub const FLG_OFF: Field = 6..8;
pub const TTL: usize = 8;
pub const LENGTH: Field = 2..4;
pub const IDENT: Field = 4..6;
pub const FLG_OFF: Field = 6..8;
pub const TTL: usize = 8;
pub const PROTOCOL: usize = 9;
pub const CHECKSUM: Field = 10..12;
pub const SRC_ADDR: Field = 12..16;
@ -265,7 +281,6 @@ mod field {
pub const HEADER_LEN: usize = field::DST_ADDR.end;
impl<T: AsRef<[u8]>> Packet<T> {
/// Imbue a raw octet buffer with IPv4 packet structure.
pub fn new_unchecked(buffer: T) -> Packet<T> {
@ -414,7 +429,9 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self) -> bool {
if cfg!(fuzzing) { return true }
if cfg!(fuzzing) {
return true;
}
let data = self.buffer.as_ref();
checksum::data(&data[..self.header_len() as usize]) == !0
@ -572,36 +589,46 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr {
pub src_addr: Address,
pub dst_addr: Address,
pub protocol: Protocol,
pub src_addr: Address,
pub dst_addr: Address,
pub protocol: Protocol,
pub payload_len: usize,
pub hop_limit: u8
pub hop_limit: u8,
}
impl Repr {
/// Parse an Internet Protocol version 4 packet and return a high-level representation.
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>,
checksum_caps: &ChecksumCapabilities) -> Result<Repr> {
pub fn parse<T: AsRef<[u8]> + ?Sized>(
packet: &Packet<&T>,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr> {
// Version 4 is expected.
if packet.version() != 4 { return Err(Error::Malformed) }
if packet.version() != 4 {
return Err(Error::Malformed);
}
// Valid checksum is expected.
if checksum_caps.ipv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) }
if checksum_caps.ipv4.rx() && !packet.verify_checksum() {
return Err(Error::Checksum);
}
// We do not support fragmentation.
if packet.more_frags() || packet.frag_offset() != 0 { return Err(Error::Fragmented) }
if packet.more_frags() || packet.frag_offset() != 0 {
return Err(Error::Fragmented);
}
// Since the packet is not fragmented, it must include the entire payload.
let payload_len = packet.total_len() as usize - packet.header_len() as usize;
if packet.payload().len() < payload_len { return Err(Error::Truncated) }
if packet.payload().len() < payload_len {
return Err(Error::Truncated);
}
// All DSCP values are acceptable, since they are of no concern to receiving endpoint.
// All ECN values are acceptable, since ECN requires opt-in from both endpoints.
// All TTL values are acceptable, since we do not perform routing.
Ok(Repr {
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
protocol: packet.protocol(),
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
protocol: packet.protocol(),
payload_len: payload_len,
hop_limit: packet.hop_limit()
hop_limit: packet.hop_limit(),
})
}
@ -612,7 +639,11 @@ impl Repr {
}
/// Emit a high-level representation into an Internet Protocol version 4 packet.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>, checksum_caps: &ChecksumCapabilities) {
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
&self,
packet: &mut Packet<T>,
checksum_caps: &ChecksumCapabilities,
) {
packet.set_version(4);
packet.set_header_len(field::DST_ADDR.end as u8);
packet.set_dscp(0);
@ -645,8 +676,14 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
Ok(repr) => write!(f, "{}", repr),
Err(err) => {
write!(f, "IPv4 ({})", err)?;
write!(f, " src={} dst={} proto={} hop_limit={}",
self.src_addr(), self.dst_addr(), self.protocol(), self.hop_limit())?;
write!(
f,
" src={} dst={} proto={} hop_limit={}",
self.src_addr(),
self.dst_addr(),
self.protocol(),
self.hop_limit()
)?;
if self.version() != 4 {
write!(f, " ver={}", self.version())?;
}
@ -680,32 +717,36 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv4 src={} dst={} proto={}",
self.src_addr, self.dst_addr, self.protocol)
write!(
f,
"IPv4 src={} dst={} proto={}",
self.src_addr, self.dst_addr, self.protocol
)
}
}
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
use crate::wire::ip::checksum::format_checksum;
let checksum_caps = ChecksumCapabilities::ignored();
let (ip_repr, payload) = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(ip_packet) => {
match Repr::parse(&ip_packet, &checksum_caps) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{}{}", indent, ip_repr)?;
format_checksum(f, ip_packet.verify_checksum())?;
(ip_repr, ip_packet.payload())
}
Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{}{}", indent, ip_repr)?;
format_checksum(f, ip_packet.verify_checksum())?;
(ip_repr, ip_packet.payload())
}
}
},
};
pretty_print_ip_payload(f, indent, ip_repr, payload)
@ -716,20 +757,12 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test {
use super::*;
static PACKET_BYTES: [u8; 30] =
[0x45, 0x00, 0x00, 0x1e,
0x01, 0x02, 0x62, 0x03,
0x1a, 0x01, 0xd5, 0x6e,
0x11, 0x12, 0x13, 0x14,
0x21, 0x22, 0x23, 0x24,
0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
static PACKET_BYTES: [u8; 30] = [
0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12, 0x13,
0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
];
static PAYLOAD_BYTES: [u8; 10] =
[0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
static PAYLOAD_BYTES: [u8; 10] = [0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff];
#[test]
fn test_deconstruct() {
@ -781,10 +814,14 @@ mod test {
bytes.extend(&PACKET_BYTES[..]);
bytes.push(0);
assert_eq!(Packet::new_unchecked(&bytes).payload().len(),
PAYLOAD_BYTES.len());
assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(),
PAYLOAD_BYTES.len());
assert_eq!(
Packet::new_unchecked(&bytes).payload().len(),
PAYLOAD_BYTES.len()
);
assert_eq!(
Packet::new_unchecked(&mut bytes).payload_mut().len(),
PAYLOAD_BYTES.len()
);
}
#[test]
@ -793,28 +830,23 @@ mod test {
bytes.extend(&PACKET_BYTES[..]);
Packet::new_unchecked(&mut bytes).set_total_len(128);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(),
Error::Truncated);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated);
}
static REPR_PACKET_BYTES: [u8; 24] =
[0x45, 0x00, 0x00, 0x18,
0x00, 0x00, 0x40, 0x00,
0x40, 0x01, 0xd2, 0x79,
0x11, 0x12, 0x13, 0x14,
0x21, 0x22, 0x23, 0x24,
0xaa, 0x00, 0x00, 0xff];
static REPR_PACKET_BYTES: [u8; 24] = [
0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x79, 0x11, 0x12, 0x13,
0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0xff,
];
static REPR_PAYLOAD_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
static REPR_PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
fn packet_repr() -> Repr {
Repr {
src_addr: Address([0x11, 0x12, 0x13, 0x14]),
dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
protocol: Protocol::Icmp,
src_addr: Address([0x11, 0x12, 0x13, 0x14]),
dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
protocol: Protocol::Icmp,
payload_len: 4,
hop_limit: 64
hop_limit: 64,
}
}
@ -833,7 +865,10 @@ mod test {
packet.set_version(6);
packet.fill_checksum();
let packet = Packet::new_unchecked(&*packet.into_inner());
assert_eq!(Repr::parse(&packet, &ChecksumCapabilities::default()), Err(Error::Malformed));
assert_eq!(
Repr::parse(&packet, &ChecksumCapabilities::default()),
Err(Error::Malformed)
);
}
#[test]
@ -876,28 +911,34 @@ mod test {
let cidr = Cidr::new(Address::new(192, 168, 1, 10), 24);
let inside_subnet = [
[192, 168, 1, 0], [192, 168, 1, 1],
[192, 168, 1, 2], [192, 168, 1, 10],
[192, 168, 1, 127], [192, 168, 1, 255],
[192, 168, 1, 0],
[192, 168, 1, 1],
[192, 168, 1, 2],
[192, 168, 1, 10],
[192, 168, 1, 127],
[192, 168, 1, 255],
];
let outside_subnet = [
[192, 168, 0, 0], [127, 0, 0, 1],
[192, 168, 2, 0], [192, 168, 0, 255],
[ 0, 0, 0, 0], [255, 255, 255, 255],
[192, 168, 0, 0],
[127, 0, 0, 1],
[192, 168, 2, 0],
[192, 168, 0, 255],
[0, 0, 0, 0],
[255, 255, 255, 255],
];
let subnets = [
([192, 168, 1, 0], 32),
([192, 168, 1, 255], 24),
([192, 168, 1, 10], 30),
([192, 168, 1, 0], 32),
([192, 168, 1, 255], 24),
([192, 168, 1, 10], 30),
];
let not_subnets = [
([192, 168, 1, 10], 23),
([127, 0, 0, 1], 8),
([192, 168, 1, 0], 0),
([192, 168, 0, 255], 32),
([192, 168, 1, 10], 23),
([127, 0, 0, 1], 8),
([192, 168, 1, 0], 0),
([192, 168, 0, 255], 32),
];
for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
@ -908,13 +949,17 @@ mod test {
assert!(!cidr.contains_addr(&addr));
}
for subnet in subnets.iter().map(
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) {
for subnet in subnets
.iter()
.map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p))
{
assert!(cidr.contains_subnet(&subnet));
}
for subnet in not_subnets.iter().map(
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) {
for subnet in not_subnets
.iter()
.map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p))
{
assert!(!cidr.contains_subnet(&subnet));
}
@ -924,94 +969,175 @@ mod test {
#[test]
fn test_cidr_from_netmask() {
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(),
true);
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(),
true);
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([0, 0, 0, 1]), 24));
assert_eq!(Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(),
Cidr::new(Address([192, 168, 0, 1]), 16));
assert_eq!(Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(),
Cidr::new(Address([172, 16, 0, 1]), 12));
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([255, 255, 255, 1]), 24));
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255])).unwrap(),
Cidr::new(Address([255, 255, 255, 255]), 32));
assert_eq!(
Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(),
true
);
assert_eq!(
Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(),
true
);
assert_eq!(
Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([0, 0, 0, 1]), 24)
);
assert_eq!(
Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(),
Cidr::new(Address([192, 168, 0, 1]), 16)
);
assert_eq!(
Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(),
Cidr::new(Address([172, 16, 0, 1]), 12)
);
assert_eq!(
Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([255, 255, 255, 1]), 24)
);
assert_eq!(
Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255]))
.unwrap(),
Cidr::new(Address([255, 255, 255, 255]), 32)
);
}
#[test]
fn test_cidr_netmask() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).netmask(),
Address([0, 0, 0, 0]));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).netmask(),
Address([255, 255, 255, 0]));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).netmask(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).netmask(),
Address([255, 0, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).netmask(),
Address([255, 255, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).netmask(),
Address([255, 255, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).netmask(),
Address([255, 255, 128, 0]));
assert_eq!(Cidr::new(Address([172, 16, 0, 0]), 12).netmask(),
Address([255, 240, 0, 0]));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).netmask(),
Address([255, 255, 255, 0]));
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).netmask(),
Address([255, 255, 255, 255]));
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 0).netmask(),
Address([0, 0, 0, 0])
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 1]), 24).netmask(),
Address([255, 255, 255, 0])
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 32).netmask(),
Address([255, 255, 255, 255])
);
assert_eq!(
Cidr::new(Address([127, 0, 0, 0]), 8).netmask(),
Address([255, 0, 0, 0])
);
assert_eq!(
Cidr::new(Address([192, 168, 0, 0]), 16).netmask(),
Address([255, 255, 0, 0])
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 16).netmask(),
Address([255, 255, 0, 0])
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 17).netmask(),
Address([255, 255, 128, 0])
);
assert_eq!(
Cidr::new(Address([172, 16, 0, 0]), 12).netmask(),
Address([255, 240, 0, 0])
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 1]), 24).netmask(),
Address([255, 255, 255, 0])
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 255]), 32).netmask(),
Address([255, 255, 255, 255])
);
}
#[test]
fn test_cidr_broadcast() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(),
Address([0, 0, 0, 255]));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(),
None);
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(),
Address([127, 255, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).broadcast().unwrap(),
Address([192, 168, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).broadcast().unwrap(),
Address([192, 168, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).broadcast().unwrap(),
Address([192, 168, 127, 255]));
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(),
Address([172, 31, 255, 255]));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).broadcast().unwrap(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(),
None);
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(),
None);
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(),
Address([255, 255, 255, 255])
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(),
Address([0, 0, 0, 255])
);
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(), None);
assert_eq!(
Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(),
Address([127, 255, 255, 255])
);
assert_eq!(
Cidr::new(Address([192, 168, 0, 0]), 16)
.broadcast()
.unwrap(),
Address([192, 168, 255, 255])
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 16)
.broadcast()
.unwrap(),
Address([192, 168, 255, 255])
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 17)
.broadcast()
.unwrap(),
Address([192, 168, 127, 255])
);
assert_eq!(
Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(),
Address([172, 31, 255, 255])
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 1]), 24)
.broadcast()
.unwrap(),
Address([255, 255, 255, 255])
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(),
None
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(),
None
);
}
#[test]
fn test_cidr_network() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).network(),
Cidr::new(Address([0, 0, 0, 0]), 0));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).network(),
Cidr::new(Address([0, 0, 0, 0]), 24));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).network(),
Cidr::new(Address([0, 0, 0, 0]), 32));
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).network(),
Cidr::new(Address([127, 0, 0, 0]), 8));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).network(),
Cidr::new(Address([192, 168, 0, 0]), 17));
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).network(),
Cidr::new(Address([172, 16, 0, 0]), 12));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).network(),
Cidr::new(Address([255, 255, 255, 0]), 24));
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).network(),
Cidr::new(Address([255, 255, 255, 255]), 32));
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 0).network(),
Cidr::new(Address([0, 0, 0, 0]), 0)
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 1]), 24).network(),
Cidr::new(Address([0, 0, 0, 0]), 24)
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 32).network(),
Cidr::new(Address([0, 0, 0, 0]), 32)
);
assert_eq!(
Cidr::new(Address([127, 0, 0, 0]), 8).network(),
Cidr::new(Address([127, 0, 0, 0]), 8)
);
assert_eq!(
Cidr::new(Address([192, 168, 0, 0]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16)
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16)
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 17).network(),
Cidr::new(Address([192, 168, 0, 0]), 17)
);
assert_eq!(
Cidr::new(Address([172, 16, 0, 1]), 12).network(),
Cidr::new(Address([172, 16, 0, 0]), 12)
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 1]), 24).network(),
Cidr::new(Address([255, 255, 255, 0]), 24)
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 255]), 32).network(),
Cidr::new(Address([255, 255, 255, 255]), 32)
);
}
}

View File

@ -1,12 +1,12 @@
#![deny(missing_docs)]
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::wire::ip::pretty_print_ip_payload;
#[cfg(feature = "proto-ipv4")]
use crate::wire::ipv4;
use crate::{Error, Result};
pub use super::IpProtocol as Protocol;
@ -29,28 +29,30 @@ impl Address {
/// The link-local [all routers multicast address].
///
/// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const LINK_LOCAL_ALL_NODES: Address =
Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
pub const LINK_LOCAL_ALL_NODES: Address = Address([
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01,
]);
/// The link-local [all nodes multicast address].
///
/// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const LINK_LOCAL_ALL_ROUTERS: Address =
Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]);
pub const LINK_LOCAL_ALL_ROUTERS: Address = Address([
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02,
]);
/// The [loopback address].
///
/// [loopback address]: https://tools.ietf.org/html/rfc4291#section-2.5.3
pub const LOOPBACK: Address =
Address([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
pub const LOOPBACK: Address = Address([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01,
]);
/// Construct an IPv6 address from parts.
#[allow(clippy::too_many_arguments)]
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16,
a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
let mut addr = [0u8; 16];
NetworkEndian::write_u16(&mut addr[0..2], a0);
NetworkEndian::write_u16(&mut addr[2..4], a1);
@ -127,8 +129,7 @@ impl Address {
///
/// [link-local]: https://tools.ietf.org/html/rfc4291#section-2.5.6
pub fn is_link_local(&self) -> bool {
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00]
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
}
/// Query whether the IPv6 address is the [loopback address].
@ -149,7 +150,9 @@ impl Address {
/// Convert an IPv4 mapped IPv6 address to an IPv4 address.
pub fn as_ipv4(&self) -> Option<ipv4::Address> {
if self.is_ipv4_mapped() {
Some(ipv4::Address::new(self.0[12], self.0[13], self.0[14], self.0[15]))
Some(ipv4::Address::new(
self.0[12], self.0[13], self.0[14], self.0[15],
))
} else {
None
}
@ -180,8 +183,10 @@ impl Address {
/// unicast.
pub fn solicited_node(&self) -> Address {
assert!(self.is_unicast());
let mut bytes = [0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let mut bytes = [
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
bytes[14..].copy_from_slice(&self.0[14..]);
Address(bytes)
}
@ -204,7 +209,11 @@ impl From<Address> for ::std::net::Ipv6Addr {
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_ipv4_mapped() {
return write!(f, "::ffff:{}.{}.{}.{}", self.0[12], self.0[13], self.0[14], self.0[15])
return write!(
f,
"::ffff:{}.{}.{}.{}",
self.0[12], self.0[13], self.0[14], self.0[15]
);
}
// The string representation of an IPv6 address should
@ -217,7 +226,7 @@ impl fmt::Display for Address {
Head,
HeadBody,
Tail,
TailBody
TailBody,
}
let mut words = [0u16; 8];
self.write_parts(&mut words);
@ -229,7 +238,7 @@ impl fmt::Display for Address {
(0, &State::Head) | (0, &State::HeadBody) => {
write!(f, "::")?;
State::Tail
},
}
// Continue iterating without writing any characters until
// we hit anothing non-zero value.
(0, &State::Tail) => State::Tail,
@ -238,11 +247,11 @@ impl fmt::Display for Address {
(_, &State::Head) => {
write!(f, "{:x}", word)?;
State::HeadBody
},
}
(_, &State::Tail) => {
write!(f, "{:x}", word)?;
State::TailBody
},
}
// Write the u16 with a leading colon when parsing a value
// that isn't the first in a section
(_, &State::HeadBody) | (_, &State::TailBody) => {
@ -260,8 +269,9 @@ impl fmt::Display for Address {
impl From<ipv4::Address> for Address {
fn from(address: ipv4::Address) -> Self {
let octets = address.0;
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff,
octets[0], octets[1], octets[2], octets[3]])
Address([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, octets[0], octets[1], octets[2], octets[3],
])
}
}
@ -270,7 +280,7 @@ impl From<ipv4::Address> for Address {
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Cidr {
address: Address,
address: Address,
prefix_len: u8,
}
@ -278,12 +288,13 @@ impl Cidr {
/// The [solicited node prefix].
///
/// [solicited node prefix]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const SOLICITED_NODE_PREFIX: Cidr =
Cidr {
address: Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00]),
prefix_len: 104
};
pub const SOLICITED_NODE_PREFIX: Cidr = Cidr {
address: Address([
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00,
0x00, 0x00,
]),
prefix_len: 104,
};
/// Create an IPv6 CIDR block from the given address and prefix length.
///
@ -291,7 +302,10 @@ impl Cidr {
/// This function panics if the prefix length is larger than 128.
pub fn new(address: Address, prefix_len: u8) -> Cidr {
assert!(prefix_len <= 128);
Cidr { address, prefix_len }
Cidr {
address,
prefix_len,
}
}
/// Return the address of this IPv6 CIDR block.
@ -308,7 +322,9 @@ impl Cidr {
/// the given address.
pub fn contains_addr(&self, addr: &Address) -> bool {
// right shift by 128 is not legal
if self.prefix_len == 0 { return true }
if self.prefix_len == 0 {
return true;
}
let shift = 128 - self.prefix_len;
self.address.mask(shift) == addr.mask(shift)
@ -332,7 +348,7 @@ impl fmt::Display for Cidr {
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
// Ranges and constants describing the IPv6 header
@ -367,17 +383,17 @@ mod field {
pub const VER_TC_FLOW: Field = 0..4;
// 16-bit value representing the length of the payload.
// Note: Options are included in this length.
pub const LENGTH: Field = 4..6;
pub const LENGTH: Field = 4..6;
// 8-bit value identifying the type of header following this
// one. Note: The same numbers are used in IPv4.
pub const NXT_HDR: usize = 6;
pub const NXT_HDR: usize = 6;
// 8-bit value decremented by each node that forwards this
// packet. The packet is discarded when the value is 0.
pub const HOP_LIMIT: usize = 7;
pub const HOP_LIMIT: usize = 7;
// IPv6 address of the source node.
pub const SRC_ADDR: Field = 8..24;
pub const SRC_ADDR: Field = 8..24;
// IPv6 address of the destination node.
pub const DST_ADDR: Field = 24..40;
pub const DST_ADDR: Field = 24..40;
}
/// Length of an IPv6 header.
@ -602,15 +618,15 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr {
/// IPv6 address of the source node.
pub src_addr: Address,
pub src_addr: Address,
/// IPv6 address of the destination node.
pub dst_addr: Address,
pub dst_addr: Address,
/// Protocol contained in the next header.
pub next_header: Protocol,
/// Length of the payload including the extension headers.
pub payload_len: usize,
/// The 8-bit hop limit field.
pub hop_limit: u8
pub hop_limit: u8,
}
impl Repr {
@ -618,13 +634,15 @@ impl Repr {
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> {
// Ensure basic accessors will work
packet.check_len()?;
if packet.version() != 6 { return Err(Error::Malformed); }
if packet.version() != 6 {
return Err(Error::Malformed);
}
Ok(Repr {
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
next_header: packet.next_header(),
payload_len: packet.payload_len() as usize,
hop_limit: packet.hop_limit()
hop_limit: packet.hop_limit(),
})
}
@ -651,29 +669,33 @@ impl Repr {
impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
self.src_addr, self.dst_addr, self.next_header, self.hop_limit)
write!(
f,
"IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
self.src_addr, self.dst_addr, self.next_header, self.hop_limit
)
}
}
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
// TODO: This is very similar to the implementation for IPv4. Make
// a way to have less copy and pasted code here.
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
let (ip_repr, payload) = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(ip_packet) => {
match Repr::parse(&ip_packet) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{}{}", indent, ip_repr)?;
(ip_repr, ip_packet.payload())
}
Ok(ip_packet) => match Repr::parse(&ip_packet) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{}{}", indent, ip_repr)?;
(ip_repr, ip_packet.payload())
}
}
},
};
pretty_print_ip_payload(f, indent, ip_repr, payload)
@ -682,18 +704,18 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
#[cfg(test)]
mod test {
use crate::Error;
use super::{Address, Cidr};
use super::{Packet, Protocol, Repr};
use crate::wire::pretty_print::{PrettyPrinter};
use crate::wire::pretty_print::PrettyPrinter;
use crate::Error;
#[cfg(feature = "proto-ipv4")]
use crate::wire::ipv4::Address as Ipv4Address;
static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]);
static LINK_LOCAL_ADDR: Address = Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01,
]);
#[test]
fn test_basic_multicast() {
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unspecified());
@ -724,48 +746,62 @@ mod test {
#[test]
fn test_address_format() {
assert_eq!("ff02::1",
format!("{}", Address::LINK_LOCAL_ALL_NODES));
assert_eq!("fe80::1",
format!("{}", LINK_LOCAL_ADDR));
assert_eq!("fe80::7f00:0:1",
format!("{}", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)));
assert_eq!("::",
format!("{}", Address::UNSPECIFIED));
assert_eq!("::1",
format!("{}", Address::LOOPBACK));
assert_eq!("ff02::1", format!("{}", Address::LINK_LOCAL_ALL_NODES));
assert_eq!("fe80::1", format!("{}", LINK_LOCAL_ADDR));
assert_eq!(
"fe80::7f00:0:1",
format!(
"{}",
Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)
)
);
assert_eq!("::", format!("{}", Address::UNSPECIFIED));
assert_eq!("::1", format!("{}", Address::LOOPBACK));
#[cfg(feature = "proto-ipv4")]
assert_eq!("::ffff:192.168.1.1",
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1))));
assert_eq!(
"::ffff:192.168.1.1",
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1)))
);
}
#[test]
fn test_new() {
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
Address::LINK_LOCAL_ALL_NODES);
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
Address::LINK_LOCAL_ALL_ROUTERS);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1),
Address::LOOPBACK);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0),
Address::UNSPECIFIED);
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
LINK_LOCAL_ADDR);
assert_eq!(
Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
Address::LINK_LOCAL_ALL_NODES
);
assert_eq!(
Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
Address::LINK_LOCAL_ALL_ROUTERS
);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1), Address::LOOPBACK);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0), Address::UNSPECIFIED);
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), LINK_LOCAL_ADDR);
}
#[test]
fn test_from_parts() {
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
Address::LINK_LOCAL_ALL_NODES);
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
Address::LINK_LOCAL_ALL_ROUTERS);
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]),
Address::LOOPBACK);
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]),
Address::UNSPECIFIED);
assert_eq!(Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]),
LINK_LOCAL_ADDR);
assert_eq!(
Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
Address::LINK_LOCAL_ALL_NODES
);
assert_eq!(
Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
Address::LINK_LOCAL_ALL_ROUTERS
);
assert_eq!(
Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]),
Address::LOOPBACK
);
assert_eq!(
Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]),
Address::UNSPECIFIED
);
assert_eq!(
Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]),
LINK_LOCAL_ADDR
);
}
#[test]
@ -788,18 +824,36 @@ mod test {
#[test]
fn test_mask() {
let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1);
assert_eq!(addr.mask(11), [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(15), [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(26), [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(128), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
assert_eq!(addr.mask(127), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(
addr.mask(11),
[0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
addr.mask(15),
[0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
addr.mask(26),
[0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
addr.mask(128),
[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
);
assert_eq!(
addr.mask(127),
[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[cfg(feature = "proto-ipv4")]
#[test]
fn test_is_ipv4_mapped() {
assert_eq!(false, Address::UNSPECIFIED.is_ipv4_mapped());
assert_eq!(true, Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped());
assert_eq!(
true,
Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped()
);
}
#[cfg(feature = "proto-ipv4")]
@ -814,10 +868,14 @@ mod test {
#[cfg(feature = "proto-ipv4")]
#[test]
fn test_from_ipv4_address() {
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]),
Address::from(Ipv4Address::new(192, 168, 1, 1)));
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]),
Address::from(Ipv4Address::new(222, 1, 41, 90)));
assert_eq!(
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]),
Address::from(Ipv4Address::new(192, 168, 1, 1))
);
assert_eq!(
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]),
Address::from(Ipv4Address::new(222, 1, 41, 90))
);
}
#[test]
@ -825,52 +883,96 @@ mod test {
let cidr = Cidr::new(LINK_LOCAL_ADDR, 64);
let inside_subnet = [
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02,
],
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
0x77, 0x88,
],
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
],
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff,
],
];
let outside_subnet = [
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
],
[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
],
[
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
],
[
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02,
],
];
let subnets = [
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
65),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
128),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78],
96)
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
65,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
],
128,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
0x34, 0x56, 0x78,
],
96,
),
];
let not_subnets = [
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
63),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
64),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
65),
([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
128)
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
63,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
64,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
65,
),
(
[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
],
128,
),
];
for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
@ -881,13 +983,11 @@ mod test {
assert!(!cidr.contains_addr(&addr));
}
for subnet in subnets.iter().map(
|&(a, p)| Cidr::new(Address(a), p)) {
for subnet in subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) {
assert!(cidr.contains_subnet(&subnet));
}
for subnet in not_subnets.iter().map(
|&(a, p)| Cidr::new(Address(a), p)) {
for subnet in not_subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) {
assert!(!cidr.contains_subnet(&subnet));
}
@ -907,33 +1007,26 @@ mod test {
let _ = Address::from_parts(&[0u16; 7]);
}
static REPR_PACKET_BYTES: [u8; 52] = [0x60, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x11, 0x40,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x02,
0x00, 0x0c, 0x02, 0x4e,
0xff, 0xff, 0xff, 0xff];
static REPR_PAYLOAD_BYTES: [u8; 12] = [0x00, 0x01, 0x00, 0x02,
0x00, 0x0c, 0x02, 0x4e,
0xff, 0xff, 0xff, 0xff];
static REPR_PACKET_BYTES: [u8; 52] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00,
0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
];
static REPR_PAYLOAD_BYTES: [u8; 12] = [
0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
];
fn packet_repr() -> Repr {
Repr {
src_addr: Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]),
dst_addr: Address::LINK_LOCAL_ALL_NODES,
src_addr: Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]),
dst_addr: Address::LINK_LOCAL_ALL_NODES,
next_header: Protocol::Udp,
payload_len: 12,
hop_limit: 64
hop_limit: 64,
}
}
@ -948,10 +1041,13 @@ mod test {
assert_eq!(packet.payload_len() as usize, REPR_PAYLOAD_BYTES.len());
assert_eq!(packet.next_header(), Protocol::Udp);
assert_eq!(packet.hop_limit(), 0x40);
assert_eq!(packet.src_addr(), Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]));
assert_eq!(
packet.src_addr(),
Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01
])
);
assert_eq!(packet.dst_addr(), Address::LINK_LOCAL_ALL_NODES);
assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]);
}
@ -976,15 +1072,14 @@ mod test {
packet.set_hop_limit(0xfe);
packet.set_src_addr(Address::LINK_LOCAL_ALL_ROUTERS);
packet.set_dst_addr(Address::LINK_LOCAL_ALL_NODES);
packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
packet
.payload_mut()
.copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
let mut expected_bytes = [
0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe,
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len();
expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
@ -998,10 +1093,14 @@ mod test {
bytes.extend(&REPR_PACKET_BYTES[..]);
bytes.push(0);
assert_eq!(Packet::new_unchecked(&bytes).payload().len(),
REPR_PAYLOAD_BYTES.len());
assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(),
REPR_PAYLOAD_BYTES.len());
assert_eq!(
Packet::new_unchecked(&bytes).payload().len(),
REPR_PAYLOAD_BYTES.len()
);
assert_eq!(
Packet::new_unchecked(&mut bytes).payload_mut().len(),
REPR_PAYLOAD_BYTES.len()
);
}
#[test]
@ -1010,8 +1109,7 @@ mod test {
bytes.extend(&REPR_PACKET_BYTES[..]);
Packet::new_unchecked(&mut bytes).set_payload_len(0x80);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(),
Error::Truncated);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated);
}
#[test]
@ -1063,7 +1161,12 @@ mod test {
#[test]
fn test_pretty_print() {
assert_eq!(format!("{}", PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])),
"\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4");
assert_eq!(
format!(
"{}",
PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])
),
"\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4"
);
}
}

View File

@ -1,5 +1,5 @@
use core::fmt;
use crate::{Error, Result};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
@ -9,7 +9,7 @@ pub use super::IpProtocol as Protocol;
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Header<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
// Format of the Fragment Header
@ -25,13 +25,13 @@ mod field {
use crate::wire::field::*;
// 8-bit identifier of the header immediately following this header.
pub const NXT_HDR: usize = 0;
pub const NXT_HDR: usize = 0;
// 8-bit reserved field.
pub const RESERVED: usize = 1;
pub const RESERVED: usize = 1;
// 16-bit field containing the fragment offset, reserved and more fragments values.
pub const FR_OF_M: Field = 2..4;
pub const FR_OF_M: Field = 2..4;
// 32-bit field identifying the fragmented packet
pub const IDENT: Field = 4..8;
pub const IDENT: Field = 4..8;
}
impl<T: AsRef<[u8]>> Header<T> {
@ -170,17 +170,19 @@ pub struct Repr {
pub more_frags: bool,
/// The identification for every packet that is fragmented.
pub ident: u32,
}
impl Repr {
/// Parse an IPv6 Fragment Header and return a high-level representation.
pub fn parse<T>(header: &Header<&T>) -> Result<Repr> where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(header: &Header<&T>) -> Result<Repr>
where
T: AsRef<[u8]> + ?Sized,
{
Ok(Repr {
next_header: header.next_header(),
frag_offset: header.frag_offset(),
more_frags: header.more_frags(),
ident: header.ident()
ident: header.ident(),
})
}
@ -202,8 +204,11 @@ impl Repr {
impl<'a> fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv6 Fragment next_hdr={} offset={} more={} ident={}",
self.next_header, self.frag_offset, self.more_frags, self.ident)
write!(
f,
"IPv6 Fragment next_hdr={} offset={} more={} ident={}",
self.next_header, self.frag_offset, self.more_frags, self.ident
)
}
}
@ -212,21 +217,23 @@ mod test {
use super::*;
// A Fragment Header with more fragments remaining
static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1,
0x0, 0x0, 0x30, 0x39];
static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x39];
// A Fragment Header with no more fragments remaining
static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0,
0x0, 0x1, 0x9, 0x32];
static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0, 0x0, 0x1, 0x9, 0x32];
#[test]
fn test_check_len() {
// less than 8 bytes
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len());
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len()
);
// valid
assert_eq!(Ok(()),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len());
assert_eq!(
Ok(()),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len()
);
}
#[test]
@ -248,24 +255,48 @@ mod test {
fn test_repr_parse_valid() {
let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr,
Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 });
assert_eq!(
repr,
Repr {
next_header: Protocol::Tcp,
frag_offset: 0,
more_frags: true,
ident: 12345
}
);
let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr,
Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 });
assert_eq!(
repr,
Repr {
next_header: Protocol::Tcp,
frag_offset: 320,
more_frags: false,
ident: 67890
}
);
}
#[test]
fn test_repr_emit() {
let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 };
let repr = Repr {
next_header: Protocol::Tcp,
frag_offset: 0,
more_frags: true,
ident: 12345,
};
let mut bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..8]);
let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 };
let repr = Repr {
next_header: Protocol::Tcp,
frag_offset: 320,
more_frags: false,
ident: 67890,
};
let mut bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);

View File

@ -1,14 +1,14 @@
use core::fmt;
use crate::{Error, Result};
use core::fmt;
use crate::wire::ipv6option::Ipv6OptionsIterator;
pub use super::IpProtocol as Protocol;
use crate::wire::ipv6option::Ipv6OptionsIterator;
/// A read/write wrapper around an IPv6 Hop-by-Hop Options Header.
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Header<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
// Format of the Hop-by-Hop Options Header
@ -31,13 +31,13 @@ mod field {
use crate::wire::field::*;
// Minimum size of the header.
pub const MIN_HEADER_SIZE: usize = 8;
pub const MIN_HEADER_SIZE: usize = 8;
// 8-bit identifier of the header immediately following this header.
pub const NXT_HDR: usize = 0;
pub const NXT_HDR: usize = 0;
// 8-bit unsigned integer. Length of the OPTIONS field in 8-octet units,
// not including the first 8 octets.
pub const LENGTH: usize = 1;
pub const LENGTH: usize = 1;
// Variable-length field. Option-Type-specific data.
//
// Length of the header is in 8-octet units, not including the first 8 octets. The first two
@ -111,7 +111,7 @@ impl<T: AsRef<[u8]>> Header<T> {
impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> {
/// Return the option data.
#[inline]
pub fn options(&self) -> &'a[u8] {
pub fn options(&self) -> &'a [u8] {
let data = self.buffer.as_ref();
&data[field::OPTIONS(data[field::LENGTH])]
}
@ -163,18 +163,21 @@ pub struct Repr<'a> {
/// The type of header immediately following the Hop-by-Hop Options header.
pub next_header: Protocol,
/// Length of the Hop-by-Hop Options header in 8-octet units, not including the first 8 octets.
pub length: u8,
pub length: u8,
/// The options contained in the Hop-by-Hop Options header.
pub options: &'a [u8]
pub options: &'a [u8],
}
impl<'a> Repr<'a> {
/// Parse an IPv6 Hop-by-Hop Options Header and return a high-level representation.
pub fn parse<T>(header: &Header<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(header: &Header<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
Ok(Repr {
next_header: header.next_header(),
length: header.header_len(),
options: header.options()
options: header.options(),
})
}
@ -199,7 +202,11 @@ impl<'a> Repr<'a> {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv6 Hop-by-Hop Options next_hdr={} length={} ", self.next_header, self.length)
write!(
f,
"IPv6 Hop-by-Hop Options next_hdr={} length={} ",
self.next_header, self.length
)
}
}
@ -208,36 +215,43 @@ mod test {
use super::*;
// A Hop-by-Hop Option header with a PadN option of option data length 4.
static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4,
0x0, 0x0, 0x0, 0x0];
static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4, 0x0, 0x0, 0x0, 0x0];
// A Hop-by-Hop Option header with a PadN option of option data length 12.
static REPR_PACKET_PAD12: [u8; 16] = [0x06, 0x1, 0x1, 0x12,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0];
static REPR_PACKET_PAD12: [u8; 16] = [
0x06, 0x1, 0x1, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
#[test]
fn test_check_len() {
// zero byte buffer
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len());
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len()
);
// no length field
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len());
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len()
);
// less than 8 bytes
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len());
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len()
);
// valid
assert_eq!(Ok(()),
Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
// valid
assert_eq!(Ok(()),
Header::new_unchecked(&REPR_PACKET_PAD12).check_len());
assert_eq!(
Ok(()),
Header::new_unchecked(&REPR_PACKET_PAD12).check_len()
);
// length field value greater than number of bytes
let header: [u8; 8] = [0x06, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&header).check_len());
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&header).check_len()
);
}
#[test]
@ -259,19 +273,27 @@ mod test {
bytes.extend(&REPR_PACKET_PAD4[..]);
bytes.push(0);
assert_eq!(Header::new_unchecked(&bytes).options().len(),
REPR_PACKET_PAD4[2..].len());
assert_eq!(Header::new_unchecked(&mut bytes).options_mut().len(),
REPR_PACKET_PAD4[2..].len());
assert_eq!(
Header::new_unchecked(&bytes).options().len(),
REPR_PACKET_PAD4[2..].len()
);
assert_eq!(
Header::new_unchecked(&mut bytes).options_mut().len(),
REPR_PACKET_PAD4[2..].len()
);
let mut bytes = vec![];
bytes.extend(&REPR_PACKET_PAD12[..]);
bytes.push(0);
assert_eq!(Header::new_unchecked(&bytes).options().len(),
REPR_PACKET_PAD12[2..].len());
assert_eq!(Header::new_unchecked(&mut bytes).options_mut().len(),
REPR_PACKET_PAD12[2..].len());
assert_eq!(
Header::new_unchecked(&bytes).options().len(),
REPR_PACKET_PAD12[2..].len()
);
assert_eq!(
Header::new_unchecked(&mut bytes).options_mut().len(),
REPR_PACKET_PAD12[2..].len()
);
}
#[test]
@ -295,26 +317,44 @@ mod test {
fn test_repr_parse_valid() {
let header = Header::new_unchecked(&REPR_PACKET_PAD4);
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr, Repr {
next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..]
});
assert_eq!(
repr,
Repr {
next_header: Protocol::Tcp,
length: 0,
options: &REPR_PACKET_PAD4[2..]
}
);
let header = Header::new_unchecked(&REPR_PACKET_PAD12);
let repr = Repr::parse(&header).unwrap();
assert_eq!(repr, Repr {
next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..]
});
assert_eq!(
repr,
Repr {
next_header: Protocol::Tcp,
length: 1,
options: &REPR_PACKET_PAD12[2..]
}
);
}
#[test]
fn test_repr_emit() {
let repr = Repr{ next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..] };
let repr = Repr {
next_header: Protocol::Tcp,
length: 0,
options: &REPR_PACKET_PAD4[2..],
};
let mut bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);
assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]);
let repr = Repr{ next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..] };
let repr = Repr {
next_header: Protocol::Tcp,
length: 1,
options: &REPR_PACKET_PAD12[2..],
};
let mut bytes = [0u8; 16];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);

View File

@ -1,5 +1,5 @@
use core::fmt;
use crate::{Error, Result};
use core::fmt;
enum_with_unknown! {
/// IPv6 Extension Header Option Type
@ -14,9 +14,9 @@ enum_with_unknown! {
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Pad1 => write!(f, "Pad1"),
Type::PadN => write!(f, "PadN"),
Type::Unknown(id) => write!(f, "{}", id)
Type::Pad1 => write!(f, "Pad1"),
Type::PadN => write!(f, "PadN"),
Type::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -40,11 +40,11 @@ enum_with_unknown! {
impl fmt::Display for FailureType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FailureType::Skip => write!(f, "skip"),
FailureType::Discard => write!(f, "discard"),
FailureType::DiscardSendAll => write!(f, "discard and send error"),
FailureType::Skip => write!(f, "skip"),
FailureType::Discard => write!(f, "discard"),
FailureType::DiscardSendAll => write!(f, "discard and send error"),
FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"),
FailureType::Unknown(id) => write!(f, "Unknown({})", id),
FailureType::Unknown(id) => write!(f, "Unknown({})", id),
}
}
}
@ -60,7 +60,7 @@ impl From<Type> for FailureType {
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Ipv6Option<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
// Format of Option
@ -77,9 +77,9 @@ mod field {
use crate::wire::field::*;
// 8-bit identifier of the type of option.
pub const TYPE: usize = 0;
pub const TYPE: usize = 0;
// 8-bit unsigned integer. Length of the DATA field of this option, in octets.
pub const LENGTH: usize = 1;
pub const LENGTH: usize = 1;
// Variable-length field. Option-Type-specific data.
pub fn DATA(length: u8) -> Field {
2..length as usize + 2
@ -221,27 +221,26 @@ pub enum Repr<'a> {
Pad1,
PadN(u8),
Unknown {
type_: Type,
type_: Type,
length: u8,
data: &'a [u8]
data: &'a [u8],
},
}
impl<'a> Repr<'a> {
/// Parse an IPv6 Extension Header Option and return a high-level representation.
pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
match opt.option_type() {
Type::Pad1 =>
Ok(Repr::Pad1),
Type::PadN =>
Ok(Repr::PadN(opt.data_len())),
unknown_type @ Type::Unknown(_) => {
Ok(Repr::Unknown {
type_: unknown_type,
length: opt.data_len(),
data: opt.data(),
})
}
Type::Pad1 => Ok(Repr::Pad1),
Type::PadN => Ok(Repr::PadN(opt.data_len())),
unknown_type @ Type::Unknown(_) => Ok(Repr::Unknown {
type_: unknown_type,
length: opt.data_len(),
data: opt.data(),
}),
}
}
@ -249,18 +248,15 @@ impl<'a> Repr<'a> {
pub fn buffer_len(&self) -> usize {
match *self {
Repr::Pad1 => 1,
Repr::PadN(length) =>
field::DATA(length).end,
Repr::Unknown{ length, .. } =>
field::DATA(length).end,
Repr::PadN(length) => field::DATA(length).end,
Repr::Unknown { length, .. } => field::DATA(length).end,
}
}
/// Emit a high-level representation into an IPv6 Extension Header Option.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, opt: &mut Ipv6Option<&'a mut T>) {
match *self {
Repr::Pad1 =>
opt.set_option_type(Type::Pad1),
Repr::Pad1 => opt.set_option_type(Type::Pad1),
Repr::PadN(len) => {
opt.set_option_type(Type::PadN);
opt.set_data_len(len);
@ -269,7 +265,11 @@ impl<'a> Repr<'a> {
*x = 0
}
}
Repr::Unknown{ type_, length, data } => {
Repr::Unknown {
type_,
length,
data,
} => {
opt.set_option_type(type_);
opt.set_data_len(length);
opt.data_mut().copy_from_slice(&data[..length as usize]);
@ -285,7 +285,7 @@ pub struct Ipv6OptionsIterator<'a> {
pos: usize,
length: usize,
data: &'a [u8],
hit_error: bool
hit_error: bool,
}
impl<'a> Ipv6OptionsIterator<'a> {
@ -301,7 +301,8 @@ impl<'a> Ipv6OptionsIterator<'a> {
Ipv6OptionsIterator {
pos: 0,
hit_error: false,
length, data
length,
data,
}
}
}
@ -314,18 +315,16 @@ impl<'a> Iterator for Ipv6OptionsIterator<'a> {
// If we still have data to parse and we have not previously
// hit an error, attempt to parse the next option.
match Ipv6Option::new_checked(&self.data[self.pos..]) {
Ok(hdr) => {
match Repr::parse(&hdr) {
Ok(repr) => {
self.pos += repr.buffer_len();
Some(Ok(repr))
}
Err(e) => {
self.hit_error = true;
Some(Err(e))
}
Ok(hdr) => match Repr::parse(&hdr) {
Ok(repr) => {
self.pos += repr.buffer_len();
Some(Ok(repr))
}
}
Err(e) => {
self.hit_error = true;
Some(Err(e))
}
},
Err(e) => {
self.hit_error = true;
Some(Err(e))
@ -343,12 +342,9 @@ impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv6 Option ")?;
match *self {
Repr::Pad1 =>
write!(f, "{} ", Type::Pad1),
Repr::PadN(len) =>
write!(f, "{} length={} ", Type::PadN, len),
Repr::Unknown{ type_, length, .. } =>
write!(f, "{} length={} ", type_, length),
Repr::Pad1 => write!(f, "{} ", Type::Pad1),
Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len),
Repr::Unknown { type_, length, .. } => write!(f, "{} length={} ", type_, length),
}
}
}
@ -357,35 +353,49 @@ impl<'a> fmt::Display for Repr<'a> {
mod test {
use super::*;
static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0];
static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0];
static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0];
static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0];
static IPV6OPTION_BYTES_UNKNOWN: [u8; 5] = [0xff, 0x3, 0x0, 0x0, 0x0];
#[test]
fn test_check_len() {
let bytes = [0u8];
// zero byte buffer
assert_eq!(Err(Error::Truncated),
Ipv6Option::new_unchecked(&bytes[..0]).check_len());
assert_eq!(
Err(Error::Truncated),
Ipv6Option::new_unchecked(&bytes[..0]).check_len()
);
// pad1
assert_eq!(Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len());
assert_eq!(
Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len()
);
// padn with truncated data
assert_eq!(Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len());
assert_eq!(
Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len()
);
// padn
assert_eq!(Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len());
assert_eq!(
Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len()
);
// unknown option type with truncated data
assert_eq!(Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len());
assert_eq!(Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len());
assert_eq!(
Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len()
);
// unknown type
assert_eq!(Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len());
assert_eq!(
Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len()
);
}
#[test]
@ -402,7 +412,7 @@ mod test {
assert_eq!(opt.option_type(), Type::Pad1);
// two octets of padding
let bytes: [u8; 2] = [0x1, 0x0];
let bytes: [u8; 2] = [0x1, 0x0];
let opt = Ipv6Option::new_unchecked(&bytes);
assert_eq!(opt.option_type(), Type::PadN);
assert_eq!(opt.data_len(), 0);
@ -414,14 +424,14 @@ mod test {
assert_eq!(opt.data(), &[0]);
// extra bytes in buffer
let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff];
let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff];
let opt = Ipv6Option::new_unchecked(&bytes);
assert_eq!(opt.option_type(), Type::PadN);
assert_eq!(opt.data_len(), 7);
assert_eq!(opt.data(), &[0, 0, 0, 0, 0, 0, 0]);
// unrecognized option
let bytes: [u8; 1] = [0xff];
let bytes: [u8; 1] = [0xff];
let opt = Ipv6Option::new_unchecked(&bytes);
assert_eq!(opt.option_type(), Type::Unknown(255));
@ -447,7 +457,14 @@ mod test {
let data = [0u8; 3];
let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN);
let unknown = Repr::parse(&opt).unwrap();
assert_eq!(unknown, Repr::Unknown { type_: Type::Unknown(255), length: 3, data: &data });
assert_eq!(
unknown,
Repr::Unknown {
type_: Type::Unknown(255),
length: 3,
data: &data
}
);
}
#[test]
@ -465,7 +482,11 @@ mod test {
assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PADN);
let data = [0u8; 3];
let repr = Repr::Unknown { type_: Type::Unknown(255), length: 3, data: &data };
let repr = Repr::Unknown {
type_: Type::Unknown(255),
length: 3,
data: &data,
};
let mut bytes = [254u8; 5]; // don't assume bytes are initialized to zero
let mut opt = Ipv6Option::new_unchecked(&mut bytes);
repr.emit(&mut opt);
@ -488,10 +509,10 @@ mod test {
#[test]
fn test_options_iter() {
let options = [0x00, 0x01, 0x01, 0x00,
0x01, 0x02, 0x00, 0x00,
0x01, 0x00, 0x00, 0x11,
0x00, 0x01, 0x08, 0x00];
let options = [
0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x01,
0x08, 0x00,
];
let mut iterator = Ipv6OptionsIterator::new(&options, 0);
assert_eq!(iterator.next(), None);
@ -504,8 +525,14 @@ mod test {
(2, Ok(Repr::PadN(2))) => continue,
(3, Ok(Repr::PadN(0))) => continue,
(4, Ok(Repr::Pad1)) => continue,
(5, Ok(Repr::Unknown { type_: Type::Unknown(0x11), length: 0, .. })) =>
continue,
(
5,
Ok(Repr::Unknown {
type_: Type::Unknown(0x11),
length: 0,
..
}),
) => continue,
(6, Err(Error::Truncated)) => continue,
(i, res) => panic!("Unexpected option `{:?}` at index {}", res, i),
}

View File

@ -1,5 +1,5 @@
use core::fmt;
use crate::{Error, Result};
use core::fmt;
use crate::wire::IpProtocol as Protocol;
use crate::wire::Ipv6Address as Address;
@ -37,14 +37,14 @@ enum_with_unknown! {
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Type0 => write!(f, "Type0"),
Type::Nimrod => write!(f, "Nimrod"),
Type::Type2 => write!(f, "Type2"),
Type::Rpl => write!(f, "Rpl"),
Type::Experiment1 => write!(f, "Experiment1"),
Type::Experiment2 => write!(f, "Experiment2"),
Type::Reserved => write!(f, "Reserved"),
Type::Unknown(id) => write!(f, "{}", id)
Type::Type0 => write!(f, "Type0"),
Type::Nimrod => write!(f, "Nimrod"),
Type::Type2 => write!(f, "Type2"),
Type::Rpl => write!(f, "Rpl"),
Type::Experiment1 => write!(f, "Experiment1"),
Type::Experiment2 => write!(f, "Experiment2"),
Type::Reserved => write!(f, "Reserved"),
Type::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -53,7 +53,7 @@ impl fmt::Display for Type {
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Header<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
// Format of the Routing Header
@ -76,15 +76,15 @@ mod field {
use crate::wire::field::*;
// Minimum size of the header.
pub const MIN_HEADER_SIZE: usize = 4;
pub const MIN_HEADER_SIZE: usize = 4;
// 8-bit identifier of the header immediately following this header.
pub const NXT_HDR: usize = 0;
pub const NXT_HDR: usize = 0;
// 8-bit unsigned integer. Length of the DATA field in 8-octet units,
// not including the first 8 octets.
pub const LENGTH: usize = 1;
pub const LENGTH: usize = 1;
// 8-bit identifier of a particular Routing header variant.
pub const TYPE: usize = 2;
pub const TYPE: usize = 2;
// 8-bit unsigned integer. The number of route segments remaining.
pub const SEG_LEFT: usize = 3;
// Variable-length field. Routing-Type-specific data.
@ -130,9 +130,9 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8-bit field containing the CmprI and CmprE values.
pub const CMPR: usize = 4;
pub const CMPR: usize = 4;
// 8-bit field containing the Pad value.
pub const PAD: usize = 5;
pub const PAD: usize = 5;
// Variable length field containing addresses
pub fn ADDRESSES(length_field: u8) -> Field {
let data = DATA(length_field);
@ -234,7 +234,6 @@ impl<T: AsRef<[u8]>> Header<T> {
data[field::CMPR] >> 4
}
/// Return the number of prefix octects elided from the last address (`addresses[n]`).
///
/// # Panics
@ -316,7 +315,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
data[7] = 0;
}
_ => panic!("Unrecognized routing type when clearing reserved fields.")
_ => panic!("Unrecognized routing type when clearing reserved fields."),
}
}
}
@ -395,59 +394,57 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
pub enum Repr<'a> {
Type2 {
/// The type of header immediately following the Routing header.
next_header: Protocol,
next_header: Protocol,
/// Length of the Routing header in 8-octet units, not including the first 8 octets.
length: u8,
length: u8,
/// Number of route segments remaining.
segments_left: u8,
segments_left: u8,
/// The home address of the destination mobile node.
home_address: Address,
},
Rpl {
/// The type of header immediately following the Routing header.
next_header: Protocol,
next_header: Protocol,
/// Length of the Routing header in 8-octet units, not including the first 8 octets.
length: u8,
length: u8,
/// Number of route segments remaining.
segments_left: u8,
segments_left: u8,
/// Number of prefix octets from each segment, except the last segment, that are elided.
cmpr_i: u8,
cmpr_i: u8,
/// Number of prefix octets from the last segment that are elided.
cmpr_e: u8,
cmpr_e: u8,
/// Number of octets that are used for padding after `address[n]` at the end of the
/// RPL Source Route Header.
pad: u8,
pad: u8,
/// Vector of addresses, numbered 1 to `n`.
addresses: &'a[u8],
addresses: &'a [u8],
},
}
impl<'a> Repr<'a> {
/// Parse an IPv6 Routing Header and return a high-level representation.
pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
match header.routing_type() {
Type::Type2 => {
Ok(Repr::Type2 {
next_header: header.next_header(),
length: header.header_len(),
segments_left: header.segments_left(),
home_address: header.home_address(),
})
}
Type::Rpl => {
Ok(Repr::Rpl {
next_header: header.next_header(),
length: header.header_len(),
segments_left: header.segments_left(),
cmpr_i: header.cmpr_i(),
cmpr_e: header.cmpr_e(),
pad: header.pad(),
addresses: header.addresses(),
})
}
Type::Type2 => Ok(Repr::Type2 {
next_header: header.next_header(),
length: header.header_len(),
segments_left: header.segments_left(),
home_address: header.home_address(),
}),
Type::Rpl => Ok(Repr::Rpl {
next_header: header.next_header(),
length: header.header_len(),
segments_left: header.segments_left(),
cmpr_i: header.cmpr_i(),
cmpr_e: header.cmpr_e(),
pad: header.pad(),
addresses: header.addresses(),
}),
_ => Err(Error::Unrecognized)
_ => Err(Error::Unrecognized),
}
}
@ -455,16 +452,19 @@ impl<'a> Repr<'a> {
/// representation.
pub fn buffer_len(&self) -> usize {
match self {
&Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => {
field::DATA(length).end
}
&Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => field::DATA(length).end,
}
}
/// Emit a high-level representation into an IPv6 Routing Header.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
match *self {
Repr::Type2 { next_header, length, segments_left, home_address } => {
Repr::Type2 {
next_header,
length,
segments_left,
home_address,
} => {
header.set_next_header(next_header);
header.set_header_len(length);
header.set_routing_type(Type::Type2);
@ -472,7 +472,15 @@ impl<'a> Repr<'a> {
header.clear_reserved();
header.set_home_address(home_address);
}
Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, addresses } => {
Repr::Rpl {
next_header,
length,
segments_left,
cmpr_i,
cmpr_e,
pad,
addresses,
} => {
header.set_next_header(next_header);
header.set_header_len(length);
header.set_routing_type(Type::Rpl);
@ -490,11 +498,31 @@ impl<'a> Repr<'a> {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Repr::Type2 { next_header, length, segments_left, home_address } => {
write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}",
next_header, length, Type::Type2, segments_left, home_address)
Repr::Type2 {
next_header,
length,
segments_left,
home_address,
} => {
write!(
f,
"IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}",
next_header,
length,
Type::Type2,
segments_left,
home_address
)
}
Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, .. } => {
Repr::Rpl {
next_header,
length,
segments_left,
cmpr_i,
cmpr_e,
pad,
..
} => {
write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}",
next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad)
}
@ -507,12 +535,10 @@ mod test {
use super::*;
// A Type 2 Routing Header
static BYTES_TYPE2: [u8; 24] = [0x6, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1];
static BYTES_TYPE2: [u8; 24] = [
0x6, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
];
// A representation of a Type 2 Routing header
static REPR_TYPE2: Repr = Repr::Type2 {
@ -523,16 +549,11 @@ mod test {
};
// A Source Routing Header with full IPv6 addresses in bytes
static BYTES_SRH_FULL: [u8; 40] = [0x6, 0x4, 0x3, 0x2,
0x0, 0x0, 0x0, 0x0,
0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2,
0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x1];
static BYTES_SRH_FULL: [u8; 40] = [
0x6, 0x4, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x1,
];
// A representation of a Source Routing Header with full IPv6 addresses
static REPR_SRH_FULL: Repr = Repr::Rpl {
@ -542,21 +563,16 @@ mod test {
cmpr_i: 0,
cmpr_e: 0,
pad: 0,
addresses: &[0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2,
0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x1]
addresses: &[
0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1,
],
};
// A Source Routing Header with elided IPv6 addresses in bytes
static BYTES_SRH_ELIDED: [u8; 16] = [0x6, 0x1, 0x3, 0x2,
0xfe, 0x50, 0x0, 0x0,
0x2, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0];
static BYTES_SRH_ELIDED: [u8; 16] = [
0x6, 0x1, 0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
];
// A representation of a Source Routing Header with elided IPv6 addresses
static REPR_SRH_ELIDED: Repr = Repr::Rpl {
@ -566,20 +582,37 @@ mod test {
cmpr_i: 15,
cmpr_e: 14,
pad: 5,
addresses: &[0x2, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0]
addresses: &[0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0],
};
#[test]
fn test_check_len() {
// less than min header size
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_TYPE2[..3]).check_len());
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_FULL[..3]).check_len());
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..3]).check_len());
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_TYPE2[..3]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_SRH_FULL[..3]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_SRH_ELIDED[..3]).check_len()
);
// less than specfied length field
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_TYPE2[..23]).check_len());
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_FULL[..39]).check_len());
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..11]).check_len());
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_TYPE2[..23]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_SRH_FULL[..39]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_SRH_ELIDED[..11]).check_len()
);
// valid
assert_eq!(Ok(()), Header::new(&BYTES_TYPE2[..]).check_len());
assert_eq!(Ok(()), Header::new(&BYTES_SRH_FULL[..]).check_len());

View File

@ -6,9 +6,9 @@
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::wire::icmpv6::{field, Message, Packet};
use crate::wire::Ipv6Address;
use crate::{Error, Result};
enum_with_unknown! {
/// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for
@ -168,7 +168,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AddressRecord<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
impl<T: AsRef<[u8]>> AddressRecord<T> {
@ -305,58 +305,61 @@ pub enum Repr<'a> {
qrv: u8,
qqic: u8,
num_srcs: u16,
data: &'a [u8]
data: &'a [u8],
},
Report {
nr_mcast_addr_rcrds: u16,
data: &'a [u8]
}
data: &'a [u8],
},
}
impl<'a> Repr<'a> {
/// Parse an MLDv2 packet and return a high-level representation.
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
where
T: AsRef<[u8]> + ?Sized,
{
match packet.msg_type() {
Message::MldQuery => {
Ok(Repr::Query {
max_resp_code: packet.max_resp_code(),
mcast_addr: packet.mcast_addr(),
s_flag: packet.s_flag(),
qrv: packet.qrv(),
qqic: packet.qqic(),
num_srcs: packet.num_srcs(),
data: packet.payload()
})
},
Message::MldReport => {
Ok(Repr::Report {
nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(),
data: packet.payload()
})
},
_ => Err(Error::Unrecognized)
Message::MldQuery => Ok(Repr::Query {
max_resp_code: packet.max_resp_code(),
mcast_addr: packet.mcast_addr(),
s_flag: packet.s_flag(),
qrv: packet.qrv(),
qqic: packet.qqic(),
num_srcs: packet.num_srcs(),
data: packet.payload(),
}),
Message::MldReport => Ok(Repr::Report {
nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(),
data: packet.payload(),
}),
_ => Err(Error::Unrecognized),
}
}
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
Repr::Query { .. } => {
field::QUERY_NUM_SRCS.end
}
Repr::Report { .. } => {
field::NR_MCAST_RCRDS.end
}
Repr::Query { .. } => field::QUERY_NUM_SRCS.end,
Repr::Report { .. } => field::NR_MCAST_RCRDS.end,
}
}
/// Emit a high-level representation into an MLDv2 packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
match self {
Repr::Query { max_resp_code, mcast_addr, s_flag,
qrv, qqic, num_srcs, data } => {
Repr::Query {
max_resp_code,
mcast_addr,
s_flag,
qrv,
qqic,
num_srcs,
data,
} => {
packet.set_msg_type(Message::MldQuery);
packet.set_msg_code(0);
packet.clear_reserved();
@ -371,8 +374,11 @@ impl<'a> Repr<'a> {
packet.set_qqic(*qqic);
packet.set_num_srcs(*num_srcs);
packet.payload_mut().copy_from_slice(&data[..]);
},
Repr::Report { nr_mcast_addr_rcrds, data } => {
}
Repr::Report {
nr_mcast_addr_rcrds,
data,
} => {
packet.set_msg_type(Message::MldReport);
packet.set_msg_code(0);
packet.clear_reserved();
@ -385,74 +391,49 @@ impl<'a> Repr<'a> {
#[cfg(test)]
mod test {
use crate::phy::ChecksumCapabilities;
use crate::wire::Icmpv6Repr;
use crate::wire::icmpv6::Message;
use super::*;
use crate::phy::ChecksumCapabilities;
use crate::wire::icmpv6::Message;
use crate::wire::Icmpv6Repr;
static QUERY_PACKET_BYTES: [u8; 44] =
[0x82, 0x00, 0x73, 0x74,
0x04, 0x00, 0x00, 0x00,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x0a, 0x12, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static QUERY_PACKET_BYTES: [u8; 44] = [
0x82, 0x00, 0x73, 0x74, 0x04, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x12, 0x00, 0x01, 0xff, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
];
static QUERY_PACKET_PAYLOAD: [u8; 16] =
[0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static QUERY_PACKET_PAYLOAD: [u8; 16] = [
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02,
];
static REPORT_PACKET_BYTES: [u8; 44] =
[0x8f, 0x00, 0x73, 0x85,
0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static REPORT_PACKET_PAYLOAD: [u8; 36] =
[0x01, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static REPORT_PACKET_BYTES: [u8; 44] = [
0x8f, 0x00, 0x73, 0x85, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
];
static REPORT_PACKET_PAYLOAD: [u8; 36] = [
0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
];
fn create_repr<'a>(ty: Message) -> Icmpv6Repr<'a> {
match ty {
Message::MldQuery => {
Icmpv6Repr::Mld(Repr::Query {
max_resp_code: 0x400,
mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES,
s_flag: true,
qrv: 0x02,
qqic: 0x12,
num_srcs: 0x01,
data: &QUERY_PACKET_PAYLOAD
})
},
Message::MldReport => {
Icmpv6Repr::Mld(Repr::Report {
nr_mcast_addr_rcrds: 1,
data: &REPORT_PACKET_PAYLOAD
})
},
Message::MldQuery => Icmpv6Repr::Mld(Repr::Query {
max_resp_code: 0x400,
mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES,
s_flag: true,
qrv: 0x02,
qqic: 0x12,
num_srcs: 0x01,
data: &QUERY_PACKET_PAYLOAD,
}),
Message::MldReport => Icmpv6Repr::Mld(Repr::Report {
nr_mcast_addr_rcrds: 1,
data: &REPORT_PACKET_PAYLOAD,
}),
_ => {
panic!("Message type must be a MLDv2 message type");
}
@ -471,8 +452,10 @@ mod test {
assert_eq!(packet.qrv(), 0x02);
assert_eq!(packet.qqic(), 0x12);
assert_eq!(packet.num_srcs(), 0x01);
assert_eq!(Ipv6Address::from_bytes(packet.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS);
assert_eq!(
Ipv6Address::from_bytes(packet.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS
);
}
#[test]
@ -487,10 +470,14 @@ mod test {
packet.set_qrv(0x02);
packet.set_qqic(0x12);
packet.set_num_srcs(0x01);
packet.payload_mut().copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
packet
.payload_mut()
.copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
packet.clear_reserved();
packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into());
packet.fill_checksum(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
);
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
}
@ -506,8 +493,10 @@ mod test {
assert_eq!(addr_rcrd.aux_data_len(), 0x00);
assert_eq!(addr_rcrd.num_srcs(), 0x01);
assert_eq!(addr_rcrd.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES);
assert_eq!(Ipv6Address::from_bytes(addr_rcrd.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS);
assert_eq!(
Ipv6Address::from_bytes(addr_rcrd.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS
);
}
#[test]
@ -524,31 +513,38 @@ mod test {
addr_rcrd.set_aux_data_len(0);
addr_rcrd.set_num_srcs(1);
addr_rcrd.set_mcast_addr(Ipv6Address::LINK_LOCAL_ALL_NODES);
addr_rcrd.payload_mut()
addr_rcrd
.payload_mut()
.copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
}
packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into());
packet.fill_checksum(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
);
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
}
#[test]
fn test_query_repr_parse() {
let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]);
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default());
let repr = Icmpv6Repr::parse(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default(),
);
assert_eq!(repr, Ok(create_repr(Message::MldQuery)));
}
#[test]
fn test_report_repr_parse() {
let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]);
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default());
let repr = Icmpv6Repr::parse(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default(),
);
assert_eq!(repr, Ok(create_repr(Message::MldReport)));
}
@ -557,10 +553,12 @@ mod test {
let mut bytes = [0x2a; 44];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
let repr = create_repr(Message::MldQuery);
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default());
repr.emit(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
}
@ -569,10 +567,12 @@ mod test {
let mut bytes = [0x2a; 44];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
let repr = create_repr(Message::MldReport);
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default());
repr.emit(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
}
}

View File

@ -72,161 +72,136 @@ let mut buffer = vec![0; repr.buffer_len() + repr.payload_len];
mod field {
pub type Field = ::core::ops::Range<usize>;
pub type Rest = ::core::ops::RangeFrom<usize>;
pub type Rest = ::core::ops::RangeFrom<usize>;
}
pub mod pretty_print;
#[cfg(feature = "medium-ethernet")]
mod ethernet;
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
mod arp;
#[cfg(feature = "proto-dhcpv4")]
pub(crate) mod dhcpv4;
#[cfg(feature = "medium-ethernet")]
mod ethernet;
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
mod icmp;
#[cfg(feature = "proto-ipv4")]
mod icmpv4;
#[cfg(feature = "proto-ipv6")]
mod icmpv6;
#[cfg(feature = "proto-igmp")]
mod igmp;
pub(crate) mod ip;
#[cfg(feature = "proto-ipv4")]
mod ipv4;
#[cfg(feature = "proto-ipv6")]
mod ipv6;
#[cfg(feature = "proto-ipv6")]
mod ipv6option;
mod ipv6fragment;
#[cfg(feature = "proto-ipv6")]
mod ipv6hopbyhop;
#[cfg(feature = "proto-ipv6")]
mod ipv6fragment;
mod ipv6option;
#[cfg(feature = "proto-ipv6")]
mod ipv6routing;
#[cfg(feature = "proto-ipv4")]
mod icmpv4;
#[cfg(feature = "proto-ipv6")]
mod icmpv6;
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
mod icmp;
#[cfg(feature = "proto-igmp")]
mod igmp;
mod mld;
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
mod ndisc;
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
mod ndiscoption;
#[cfg(feature = "proto-ipv6")]
mod mld;
mod udp;
mod tcp;
#[cfg(feature = "proto-dhcpv4")]
pub(crate) mod dhcpv4;
mod udp;
pub use self::pretty_print::PrettyPrinter;
#[cfg(feature = "medium-ethernet")]
pub use self::ethernet::{EtherType as EthernetProtocol,
Address as EthernetAddress,
Frame as EthernetFrame,
HEADER_LEN as ETHERNET_HEADER_LEN,
Repr as EthernetRepr};
pub use self::ethernet::{
Address as EthernetAddress, EtherType as EthernetProtocol, Frame as EthernetFrame,
Repr as EthernetRepr, HEADER_LEN as ETHERNET_HEADER_LEN,
};
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
pub use self::arp::{Hardware as ArpHardware,
Operation as ArpOperation,
Packet as ArpPacket,
Repr as ArpRepr};
pub use self::arp::{
Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr,
};
pub use self::ip::{Version as IpVersion,
Protocol as IpProtocol,
Address as IpAddress,
Endpoint as IpEndpoint,
Repr as IpRepr,
Cidr as IpCidr};
pub use self::ip::{
Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol,
Repr as IpRepr, Version as IpVersion,
};
#[cfg(feature = "proto-ipv4")]
pub use self::ipv4::{Address as Ipv4Address,
Packet as Ipv4Packet,
Repr as Ipv4Repr,
Cidr as Ipv4Cidr,
HEADER_LEN as IPV4_HEADER_LEN,
MIN_MTU as IPV4_MIN_MTU};
pub use self::ipv4::{
Address as Ipv4Address, Cidr as Ipv4Cidr, Packet as Ipv4Packet, Repr as Ipv4Repr,
HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU,
};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6::{Address as Ipv6Address,
Packet as Ipv6Packet,
Repr as Ipv6Repr,
Cidr as Ipv6Cidr,
HEADER_LEN as IPV6_HEADER_LEN,
MIN_MTU as IPV6_MIN_MTU};
pub use self::ipv6::{
Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr,
HEADER_LEN as IPV6_HEADER_LEN, MIN_MTU as IPV6_MIN_MTU,
};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6option::{Ipv6Option,
Repr as Ipv6OptionRepr,
Type as Ipv6OptionType,
FailureType as Ipv6OptionFailureType};
pub use self::ipv6option::{
FailureType as Ipv6OptionFailureType, Ipv6Option, Repr as Ipv6OptionRepr,
Type as Ipv6OptionType,
};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader,
Repr as Ipv6HopByHopRepr};
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader,
Repr as Ipv6FragmentRepr};
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6routing::{Header as Ipv6RoutingHeader,
Repr as Ipv6RoutingRepr};
pub use self::ipv6routing::{Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr};
#[cfg(feature = "proto-ipv4")]
pub use self::icmpv4::{Message as Icmpv4Message,
DstUnreachable as Icmpv4DstUnreachable,
Redirect as Icmpv4Redirect,
TimeExceeded as Icmpv4TimeExceeded,
ParamProblem as Icmpv4ParamProblem,
Packet as Icmpv4Packet,
Repr as Icmpv4Repr};
pub use self::icmpv4::{
DstUnreachable as Icmpv4DstUnreachable, Message as Icmpv4Message, Packet as Icmpv4Packet,
ParamProblem as Icmpv4ParamProblem, Redirect as Icmpv4Redirect, Repr as Icmpv4Repr,
TimeExceeded as Icmpv4TimeExceeded,
};
#[cfg(feature = "proto-igmp")]
pub use self::igmp::{Packet as IgmpPacket,
Repr as IgmpRepr,
IgmpVersion};
pub use self::igmp::{IgmpVersion, Packet as IgmpPacket, Repr as IgmpRepr};
#[cfg(feature = "proto-ipv6")]
pub use self::icmpv6::{Message as Icmpv6Message,
DstUnreachable as Icmpv6DstUnreachable,
TimeExceeded as Icmpv6TimeExceeded,
ParamProblem as Icmpv6ParamProblem,
Packet as Icmpv6Packet,
Repr as Icmpv6Repr};
pub use self::icmpv6::{
DstUnreachable as Icmpv6DstUnreachable, Message as Icmpv6Message, Packet as Icmpv6Packet,
ParamProblem as Icmpv6ParamProblem, Repr as Icmpv6Repr, TimeExceeded as Icmpv6TimeExceeded,
};
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
pub use self::icmp::Repr as IcmpRepr;
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
pub use self::ndisc::{
NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags,
};
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
pub use self::ndisc::{Repr as NdiscRepr,
RouterFlags as NdiscRouterFlags,
NeighborFlags as NdiscNeighborFlags};
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
pub use self::ndiscoption::{NdiscOption,
Repr as NdiscOptionRepr,
Type as NdiscOptionType,
PrefixInformation as NdiscPrefixInformation,
RedirectedHeader as NdiscRedirectedHeader,
PrefixInfoFlags as NdiscPrefixInfoFlags};
pub use self::ndiscoption::{
NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
Repr as NdiscOptionRepr, Type as NdiscOptionType,
};
#[cfg(feature = "proto-ipv6")]
pub use self::mld::{AddressRecord as MldAddressRecord,
Repr as MldRepr};
pub use self::mld::{AddressRecord as MldAddressRecord, Repr as MldRepr};
pub use self::udp::{Packet as UdpPacket,
Repr as UdpRepr,
HEADER_LEN as UDP_HEADER_LEN};
pub use self::udp::{Packet as UdpPacket, Repr as UdpRepr, HEADER_LEN as UDP_HEADER_LEN};
pub use self::tcp::{SeqNumber as TcpSeqNumber,
Packet as TcpPacket,
TcpOption,
Repr as TcpRepr,
Control as TcpControl,
HEADER_LEN as TCP_HEADER_LEN};
pub use self::tcp::{
Control as TcpControl, Packet as TcpPacket, Repr as TcpRepr, SeqNumber as TcpSeqNumber,
TcpOption, HEADER_LEN as TCP_HEADER_LEN,
};
#[cfg(feature = "proto-dhcpv4")]
pub use self::dhcpv4::{Packet as DhcpPacket,
Repr as DhcpRepr,
MessageType as DhcpMessageType,
CLIENT_PORT as DHCP_CLIENT_PORT,
SERVER_PORT as DHCP_SERVER_PORT,
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT};
pub use self::dhcpv4::{
MessageType as DhcpMessageType, Packet as DhcpPacket, Repr as DhcpRepr,
CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
SERVER_PORT as DHCP_SERVER_PORT,
};

View File

@ -1,13 +1,13 @@
use byteorder::{ByteOrder, NetworkEndian};
use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::time::Duration;
use crate::wire::icmpv6::{field, Message, Packet};
use crate::wire::{EthernetAddress, Ipv6Repr, Ipv6Packet};
use crate::wire::Ipv6Address;
use crate::wire::{EthernetAddress, Ipv6Packet, Ipv6Repr};
use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType};
use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader};
use crate::time::Duration;
use crate::wire::Ipv6Address;
use crate::{Error, Result};
bitflags! {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -82,7 +82,6 @@ impl<T: AsRef<[u8]>> Packet<T> {
}
}
/// Getters for the Neighbor Solicitation message header.
/// See [RFC 4861 § 4.3].
///
@ -194,7 +193,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr<'a> {
RouterSolicit {
lladdr: Option<EthernetAddress>
lladdr: Option<EthernetAddress>,
},
RouterAdvert {
hop_limit: u8,
@ -204,44 +203,47 @@ pub enum Repr<'a> {
retrans_time: Duration,
lladdr: Option<EthernetAddress>,
mtu: Option<u32>,
prefix_info: Option<NdiscPrefixInformation>
prefix_info: Option<NdiscPrefixInformation>,
},
NeighborSolicit {
target_addr: Ipv6Address,
lladdr: Option<EthernetAddress>
lladdr: Option<EthernetAddress>,
},
NeighborAdvert {
flags: NeighborFlags,
target_addr: Ipv6Address,
lladdr: Option<EthernetAddress>
lladdr: Option<EthernetAddress>,
},
Redirect {
target_addr: Ipv6Address,
dest_addr: Ipv6Address,
lladdr: Option<EthernetAddress>,
redirected_hdr: Option<NdiscRedirectedHeader<'a>>
}
redirected_hdr: Option<NdiscRedirectedHeader<'a>>,
},
}
impl<'a> Repr<'a> {
/// Parse an NDISC packet and return a high-level representation of the
/// packet.
pub fn parse<T>(packet: &Packet<&'a T>)
-> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
match packet.msg_type() {
Message::RouterSolicit => {
let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { return Err(Error::Unrecognized); }
_ => {
return Err(Error::Unrecognized);
}
}
} else {
None
};
Ok(Repr::RouterSolicit { lladdr })
},
}
Message::RouterAdvert => {
let mut offset = 0;
let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None);
@ -252,7 +254,9 @@ impl<'a> Repr<'a> {
NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
NdiscOptionRepr::Mtu(val) => mtu = Some(val),
NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info),
_ => { return Err(Error::Unrecognized); }
_ => {
return Err(Error::Unrecognized);
}
}
offset += opt.buffer_len();
}
@ -262,29 +266,36 @@ impl<'a> Repr<'a> {
router_lifetime: packet.router_lifetime(),
reachable_time: packet.reachable_time(),
retrans_time: packet.retrans_time(),
lladdr, mtu, prefix_info
lladdr,
mtu,
prefix_info,
})
},
}
Message::NeighborSolicit => {
let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { return Err(Error::Unrecognized); }
_ => {
return Err(Error::Unrecognized);
}
}
} else {
None
};
Ok(Repr::NeighborSolicit {
target_addr: packet.target_addr(), lladdr
target_addr: packet.target_addr(),
lladdr,
})
},
}
Message::NeighborAdvert => {
let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() {
NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { return Err(Error::Unrecognized); }
_ => {
return Err(Error::Unrecognized);
}
}
} else {
None
@ -292,9 +303,9 @@ impl<'a> Repr<'a> {
Ok(Repr::NeighborAdvert {
flags: packet.neighbor_flags(),
target_addr: packet.target_addr(),
lladdr
lladdr,
})
},
}
Message::Redirect => {
let mut offset = 0;
let (mut lladdr, mut redirected_hdr) = (None, None);
@ -304,43 +315,50 @@ impl<'a> Repr<'a> {
NdiscOptionType::SourceLinkLayerAddr => {
lladdr = Some(opt.link_layer_addr());
offset += 8;
},
}
NdiscOptionType::RedirectedHeader => {
if opt.data_len() < 6 {
return Err(Error::Truncated)
return Err(Error::Truncated);
} else {
let ip_packet =
Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]);
let ip_repr = Ipv6Repr::parse(&ip_packet)?;
let data = &opt.data()[offset + 8 + ip_repr.buffer_len()..];
redirected_hdr = Some(NdiscRedirectedHeader {
header: ip_repr, data
header: ip_repr,
data,
});
offset += 8 + ip_repr.buffer_len() + data.len();
}
}
_ => { return Err(Error::Unrecognized); }
_ => {
return Err(Error::Unrecognized);
}
}
}
Ok(Repr::Redirect {
target_addr: packet.target_addr(),
dest_addr: packet.dest_addr(),
lladdr, redirected_hdr
lladdr,
redirected_hdr,
})
},
_ => Err(Error::Unrecognized)
}
_ => Err(Error::Unrecognized),
}
}
pub fn buffer_len(&self) -> usize {
match self {
&Repr::RouterSolicit { lladdr } => {
match lladdr {
Some(_) => field::UNUSED.end + 8,
None => field::UNUSED.end,
}
&Repr::RouterSolicit { lladdr } => match lladdr {
Some(_) => field::UNUSED.end + 8,
None => field::UNUSED.end,
},
&Repr::RouterAdvert { lladdr, mtu, prefix_info, .. } => {
&Repr::RouterAdvert {
lladdr,
mtu,
prefix_info,
..
} => {
let mut offset = 0;
if lladdr.is_some() {
offset += 8;
@ -352,14 +370,18 @@ impl<'a> Repr<'a> {
offset += 32;
}
field::RETRANS_TM.end + offset
},
}
&Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => {
match lladdr {
Some(_) => field::TARGET_ADDR.end + 8,
None => field::TARGET_ADDR.end,
}
},
&Repr::Redirect { lladdr, redirected_hdr, .. } => {
}
&Repr::Redirect {
lladdr,
redirected_hdr,
..
} => {
let mut offset = 0;
if lladdr.is_some() {
offset += 8;
@ -373,7 +395,9 @@ impl<'a> Repr<'a> {
}
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
match *self {
Repr::RouterSolicit { lladdr } => {
packet.set_msg_type(Message::RouterSolicit);
@ -383,10 +407,18 @@ impl<'a> Repr<'a> {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
}
},
}
Repr::RouterAdvert { hop_limit, flags, router_lifetime, reachable_time,
retrans_time, lladdr, mtu, prefix_info } => {
Repr::RouterAdvert {
hop_limit,
flags,
router_lifetime,
reachable_time,
retrans_time,
lladdr,
mtu,
prefix_info,
} => {
packet.set_msg_type(Message::RouterAdvert);
packet.set_msg_code(0);
packet.set_current_hop_limit(hop_limit);
@ -396,8 +428,7 @@ impl<'a> Repr<'a> {
packet.set_retrans_time(retrans_time);
let mut offset = 0;
if let Some(lladdr) = lladdr {
let mut opt_pkt =
NdiscOption::new_unchecked(packet.payload_mut());
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
offset += 8;
}
@ -412,34 +443,44 @@ impl<'a> Repr<'a> {
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt)
}
},
}
Repr::NeighborSolicit { target_addr, lladdr } => {
Repr::NeighborSolicit {
target_addr,
lladdr,
} => {
packet.set_msg_type(Message::NeighborSolicit);
packet.set_msg_code(0);
packet.clear_reserved();
packet.set_target_addr(target_addr);
if let Some(lladdr) = lladdr {
let mut opt_pkt =
NdiscOption::new_unchecked(packet.payload_mut());
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
}
},
}
Repr::NeighborAdvert { flags, target_addr, lladdr } => {
Repr::NeighborAdvert {
flags,
target_addr,
lladdr,
} => {
packet.set_msg_type(Message::NeighborAdvert);
packet.set_msg_code(0);
packet.clear_reserved();
packet.set_neighbor_flags(flags);
packet.set_target_addr(target_addr);
if let Some(lladdr) = lladdr {
let mut opt_pkt =
NdiscOption::new_unchecked(packet.payload_mut());
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
}
},
}
Repr::Redirect { target_addr, dest_addr, lladdr, redirected_hdr } => {
Repr::Redirect {
target_addr,
dest_addr,
lladdr,
redirected_hdr,
} => {
packet.set_msg_type(Message::Redirect);
packet.set_msg_code(0);
packet.clear_reserved();
@ -447,11 +488,10 @@ impl<'a> Repr<'a> {
packet.set_dest_addr(dest_addr);
let offset = match lladdr {
Some(lladdr) => {
let mut opt_pkt =
NdiscOption::new_unchecked(packet.payload_mut());
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
8
},
}
None => 0,
};
if let Some(redirected_hdr) = redirected_hdr {
@ -459,28 +499,23 @@ impl<'a> Repr<'a> {
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt);
}
},
}
}
}
}
#[cfg(test)]
mod test {
use crate::phy::ChecksumCapabilities;
use super::*;
use crate::wire::Icmpv6Repr;
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
use crate::wire::Icmpv6Repr;
static ROUTER_ADVERT_BYTES: [u8; 24] =
[0x86, 0x00, 0xa9, 0xde,
0x40, 0x80, 0x03, 0x84,
0x00, 0x00, 0x03, 0x84,
0x00, 0x00, 0x03, 0x84,
0x01, 0x01, 0x52, 0x54,
0x00, 0x12, 0x34, 0x56];
static SOURCE_LINK_LAYER_OPT: [u8; 8] =
[0x01, 0x01, 0x52, 0x54,
0x00, 0x12, 0x34, 0x56];
static ROUTER_ADVERT_BYTES: [u8; 24] = [
0x86, 0x00, 0xa9, 0xde, 0x40, 0x80, 0x03, 0x84, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03,
0x84, 0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56,
];
static SOURCE_LINK_LAYER_OPT: [u8; 8] = [0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56];
fn create_repr<'a>() -> Icmpv6Repr<'a> {
Icmpv6Repr::Ndisc(Repr::RouterAdvert {
@ -491,7 +526,7 @@ mod test {
retrans_time: Duration::from_millis(900),
lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56])),
mtu: None,
prefix_info: None
prefix_info: None,
})
}
@ -519,7 +554,9 @@ mod test {
packet.set_router_lifetime(Duration::from_secs(900));
packet.set_reachable_time(Duration::from_millis(900));
packet.set_retrans_time(Duration::from_millis(900));
packet.payload_mut().copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
packet
.payload_mut()
.copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
}
@ -527,17 +564,28 @@ mod test {
#[test]
fn test_router_advert_repr_parse() {
let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]);
assert_eq!(Icmpv6Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&packet, &ChecksumCapabilities::default()).unwrap(),
create_repr());
assert_eq!(
Icmpv6Repr::parse(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&packet,
&ChecksumCapabilities::default()
)
.unwrap(),
create_repr()
);
}
#[test]
fn test_router_advert_repr_emit() {
let mut bytes = vec![0x2a; 24];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
create_repr().emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&mut packet, &ChecksumCapabilities::default());
create_repr().emit(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&mut packet,
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
}
}

View File

@ -1,10 +1,10 @@
use core::fmt;
use byteorder::{NetworkEndian, ByteOrder};
use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::time::Duration;
use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr};
use crate::{Error, Result};
enum_with_unknown! {
/// NDISC Option Type
@ -27,10 +27,10 @@ impl fmt::Display for Type {
match self {
Type::SourceLinkLayerAddr => write!(f, "source link-layer address"),
Type::TargetLinkLayerAddr => write!(f, "target link-layer address"),
Type::PrefixInformation => write!(f, "prefix information"),
Type::RedirectedHeader => write!(f, "redirected header"),
Type::Mtu => write!(f, "mtu"),
Type::Unknown(id) => write!(f, "{}", id)
Type::PrefixInformation => write!(f, "prefix information"),
Type::RedirectedHeader => write!(f, "redirected header"),
Type::Mtu => write!(f, "mtu"),
Type::Unknown(id) => write!(f, "{}", id),
}
}
}
@ -49,7 +49,7 @@ bitflags! {
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NdiscOption<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
// Format of an NDISC Option
@ -67,11 +67,11 @@ mod field {
use crate::wire::field::*;
// 8-bit identifier of the type of option.
pub const TYPE: usize = 0;
pub const TYPE: usize = 0;
// 8-bit unsigned integer. Length of the option, in units of 8 octests.
pub const LENGTH: usize = 1;
pub const LENGTH: usize = 1;
// Minimum length of an option.
pub const MIN_OPT_LEN: usize = 8;
pub const MIN_OPT_LEN: usize = 8;
// Variable-length field. Option-Type-specific data.
pub fn DATA(length: u8) -> Field {
2..length as usize * 8
@ -83,7 +83,7 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Link-Layer Address
pub const LL_ADDR: Field = 2..8;
pub const LL_ADDR: Field = 2..8;
// Prefix Information Option fields.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -105,17 +105,17 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Prefix length.
pub const PREFIX_LEN: usize = 2;
pub const PREFIX_LEN: usize = 2;
// Flags field of prefix header.
pub const FLAGS: usize = 3;
pub const FLAGS: usize = 3;
// Valid lifetime.
pub const VALID_LT: Field = 4..8;
pub const VALID_LT: Field = 4..8;
// Preferred lifetime.
pub const PREF_LT: Field = 8..12;
pub const PREF_LT: Field = 8..12;
// Reserved bits
pub const PREF_RESERVED: Field = 12..16;
// Prefix
pub const PREFIX: Field = 16..32;
pub const PREFIX: Field = 16..32;
// Redirected Header Option fields.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -129,10 +129,10 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Reserved bits.
pub const IP_RESERVED: Field = 4..8;
pub const IP_RESERVED: Field = 4..8;
// Redirected header IP header + data.
pub const IP_DATA: usize = 8;
pub const REDIR_MIN_SZ: usize = 48;
pub const IP_DATA: usize = 8;
pub const REDIR_MIN_SZ: usize = 48;
// MTU Option fields
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -142,7 +142,7 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// MTU
pub const MTU: Field = 4..8;
pub const MTU: Field = 4..8;
}
/// Core getter methods relevant to any type of NDISC option.
@ -180,16 +180,11 @@ impl<T: AsRef<[u8]>> NdiscOption<T> {
Err(Error::Truncated)
} else {
match self.option_type() {
Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu =>
Ok(()),
Type::PrefixInformation if data_range.end >= field::PREFIX.end =>
Ok(()),
Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ =>
Ok(()),
Type::Unknown(_) =>
Ok(()),
_ =>
Err(Error::Truncated),
Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Ok(()),
Type::PrefixInformation if data_range.end >= field::PREFIX.end => Ok(()),
Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => Ok(()),
Type::Unknown(_) => Ok(()),
_ => Err(Error::Truncated),
}
}
}
@ -371,7 +366,6 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
}
}
impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> {
/// Return a mutable pointer to the option data.
#[inline]
@ -401,14 +395,14 @@ pub struct PrefixInformation {
pub flags: PrefixInfoFlags,
pub valid_lifetime: Duration,
pub preferred_lifetime: Duration,
pub prefix: Ipv6Address
pub prefix: Ipv6Address,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RedirectedHeader<'a> {
pub header: Ipv6Repr,
pub data: &'a [u8]
pub data: &'a [u8],
}
/// A high-level representation of an NDISC Option.
@ -421,16 +415,18 @@ pub enum Repr<'a> {
RedirectedHeader(RedirectedHeader<'a>),
Mtu(u32),
Unknown {
type_: u8,
type_: u8,
length: u8,
data: &'a [u8]
data: &'a [u8],
},
}
impl<'a> Repr<'a> {
/// Parse an NDISC Option and return a high-level representation.
pub fn parse<T>(opt: &'a NdiscOption<&'a T>) -> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
where
T: AsRef<[u8]> + ?Sized,
{
match opt.option_type() {
Type::SourceLinkLayerAddr => {
if opt.data_len() == 1 {
@ -438,14 +434,14 @@ impl<'a> Repr<'a> {
} else {
Err(Error::Malformed)
}
},
}
Type::TargetLinkLayerAddr => {
if opt.data_len() == 1 {
Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr()))
} else {
Err(Error::Malformed)
}
},
}
Type::PrefixInformation => {
if opt.data_len() == 4 {
Ok(Repr::PrefixInformation(PrefixInformation {
@ -453,12 +449,12 @@ impl<'a> Repr<'a> {
flags: opt.prefix_flags(),
valid_lifetime: opt.valid_lifetime(),
preferred_lifetime: opt.preferred_lifetime(),
prefix: opt.prefix()
prefix: opt.prefix(),
}))
} else {
Err(Error::Malformed)
}
},
}
Type::RedirectedHeader => {
// If the options data length is less than 6, the option
// does not have enough data to fill out the IP header
@ -470,60 +466,60 @@ impl<'a> Repr<'a> {
let ip_repr = Ipv6Repr::parse(&ip_packet)?;
Ok(Repr::RedirectedHeader(RedirectedHeader {
header: ip_repr,
data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..]
data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..],
}))
}
},
}
Type::Mtu => {
if opt.data_len() == 1 {
Ok(Repr::Mtu(opt.mtu()))
} else {
Err(Error::Malformed)
}
},
Type::Unknown(id) => {
Ok(Repr::Unknown {
type_: id,
length: opt.data_len(),
data: opt.data()
})
}
Type::Unknown(id) => Ok(Repr::Unknown {
type_: id,
length: opt.data_len(),
data: opt.data(),
}),
}
}
/// Return the length of a header that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) =>
field::LL_ADDR.end,
&Repr::PrefixInformation(_) =>
field::PREFIX.end,
&Repr::RedirectedHeader(RedirectedHeader { header, data }) =>
field::IP_DATA + header.buffer_len() + data.len(),
&Repr::Mtu(_) =>
field::MTU.end,
&Repr::Unknown { length, .. } =>
field::DATA(length).end
&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) => field::LL_ADDR.end,
&Repr::PrefixInformation(_) => field::PREFIX.end,
&Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
field::IP_DATA + header.buffer_len() + data.len()
}
&Repr::Mtu(_) => field::MTU.end,
&Repr::Unknown { length, .. } => field::DATA(length).end,
}
}
/// Emit a high-level representation into an NDISC Option.
pub fn emit<T>(&self, opt: &mut NdiscOption<&'a mut T>)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
match *self {
Repr::SourceLinkLayerAddr(addr) => {
opt.set_option_type(Type::SourceLinkLayerAddr);
opt.set_data_len(1);
opt.set_link_layer_addr(addr);
},
}
Repr::TargetLinkLayerAddr(addr) => {
opt.set_option_type(Type::TargetLinkLayerAddr);
opt.set_data_len(1);
opt.set_link_layer_addr(addr);
},
}
Repr::PrefixInformation(PrefixInformation {
prefix_len, flags, valid_lifetime,
preferred_lifetime, prefix
prefix_len,
flags,
valid_lifetime,
preferred_lifetime,
prefix,
}) => {
opt.clear_prefix_reserved();
opt.set_option_type(Type::PrefixInformation);
@ -533,10 +529,8 @@ impl<'a> Repr<'a> {
opt.set_valid_lifetime(valid_lifetime);
opt.set_preferred_lifetime(preferred_lifetime);
opt.set_prefix(prefix);
},
Repr::RedirectedHeader(RedirectedHeader {
header, data
}) => {
}
Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
let data_len = data.len() / 8;
opt.clear_redirected_reserved();
opt.set_option_type(Type::RedirectedHeader);
@ -552,7 +546,11 @@ impl<'a> Repr<'a> {
opt.set_data_len(1);
opt.set_mtu(mtu);
}
Repr::Unknown { type_: id, length, data } => {
Repr::Unknown {
type_: id,
length,
data,
} => {
opt.set_option_type(Type::Unknown(id));
opt.set_data_len(length);
opt.data_mut().copy_from_slice(data);
@ -567,67 +565,61 @@ impl<'a> fmt::Display for Repr<'a> {
match *self {
Repr::SourceLinkLayerAddr(addr) => {
write!(f, "SourceLinkLayer addr={}", addr)
},
}
Repr::TargetLinkLayerAddr(addr) => {
write!(f, "TargetLinkLayer addr={}", addr)
},
}
Repr::PrefixInformation(PrefixInformation {
prefix, prefix_len,
..
prefix, prefix_len, ..
}) => {
write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len)
},
Repr::RedirectedHeader(RedirectedHeader {
header,
..
}) => {
}
Repr::RedirectedHeader(RedirectedHeader { header, .. }) => {
write!(f, "RedirectedHeader header={}", header)
},
}
Repr::Mtu(mtu) => {
write!(f, "MTU mtu={}", mtu)
},
Repr::Unknown { type_: id, length, .. } => {
}
Repr::Unknown {
type_: id, length, ..
} => {
write!(f, "Unknown({}) length={}", id, length)
}
}
}
}
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
match NdiscOption::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(ndisc) => {
match Repr::parse(&ndisc) {
Err(_) => Ok(()),
Ok(repr) => {
write!(f, "{}{}", indent, repr)
}
Ok(ndisc) => match Repr::parse(&ndisc) {
Err(_) => Ok(()),
Ok(repr) => {
write!(f, "{}{}", indent, repr)
}
}
},
}
}
}
#[cfg(test)]
mod test {
use crate::Error;
use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type};
use crate::time::Duration;
use crate::wire::{EthernetAddress, Ipv6Address};
use super::{NdiscOption, Type, PrefixInfoFlags, PrefixInformation, Repr};
use crate::Error;
static PREFIX_OPT_BYTES: [u8; 32] = [
0x03, 0x04, 0x40, 0xc0,
0x00, 0x00, 0x03, 0x84,
0x00, 0x00, 0x03, 0xe8,
0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00,
0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
];
#[test]
@ -636,7 +628,10 @@ mod test {
assert_eq!(opt.option_type(), Type::PrefixInformation);
assert_eq!(opt.data_len(), 4);
assert_eq!(opt.prefix_len(), 64);
assert_eq!(opt.prefix_flags(), PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF);
assert_eq!(
opt.prefix_flags(),
PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF
);
assert_eq!(opt.valid_lifetime(), Duration::from_secs(900));
assert_eq!(opt.preferred_lifetime(), Duration::from_secs(1000));
assert_eq!(opt.prefix(), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
@ -658,11 +653,11 @@ mod test {
#[test]
fn test_short_packet() {
assert_eq!(NdiscOption::new_checked(&[0x00, 0x00]), Err(Error::Truncated));
let bytes = [
0x03, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
];
assert_eq!(
NdiscOption::new_checked(&[0x00, 0x00]),
Err(Error::Truncated)
);
let bytes = [0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
assert_eq!(NdiscOption::new_checked(&bytes), Err(Error::Truncated));
}
@ -671,13 +666,17 @@ mod test {
let mut bytes = [0x01, 0x01, 0x54, 0x52, 0x00, 0x12, 0x23, 0x34];
let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]);
{
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::SourceLinkLayerAddr(addr)));
assert_eq!(
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::SourceLinkLayerAddr(addr))
);
}
bytes[0] = 0x02;
{
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::TargetLinkLayerAddr(addr)));
assert_eq!(
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::TargetLinkLayerAddr(addr))
);
}
}
@ -688,9 +687,12 @@ mod test {
flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
valid_lifetime: Duration::from_secs(900),
preferred_lifetime: Duration::from_secs(1000),
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
});
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)), Ok(repr));
assert_eq!(
Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)),
Ok(repr)
);
}
#[test]
@ -701,7 +703,7 @@ mod test {
flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
valid_lifetime: Duration::from_secs(900),
preferred_lifetime: Duration::from_secs(1000),
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
});
let mut opt = NdiscOption::new_unchecked(&mut bytes);
repr.emit(&mut opt);
@ -711,6 +713,9 @@ mod test {
#[test]
fn test_repr_parse_mtu() {
let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc];
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)), Ok(Repr::Mtu(1500)));
assert_eq!(
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::Mtu(1500))
);
}
}

View File

@ -37,7 +37,7 @@ use core::marker::PhantomData;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PrettyIndent {
prefix: &'static str,
level: usize
level: usize,
}
impl PrettyIndent {
@ -72,24 +72,27 @@ pub trait PrettyPrint {
///
/// `pretty_print` accepts a buffer and not a packet wrapper because the packet might
/// be truncated, and so it might not be possible to create the packet wrapper.
fn pretty_print(buffer: &dyn AsRef<[u8]>, fmt: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result;
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
fmt: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result;
}
/// Wrapper for using a `PrettyPrint` where a `Display` is expected.
pub struct PrettyPrinter<'a, T: PrettyPrint> {
prefix: &'static str,
buffer: &'a dyn AsRef<[u8]>,
phantom: PhantomData<T>
prefix: &'static str,
buffer: &'a dyn AsRef<[u8]>,
phantom: PhantomData<T>,
}
impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> {
/// Format the listing with the recorded parameters when Display::fmt is called.
pub fn new(prefix: &'static str, buffer: &'a dyn AsRef<[u8]>) -> PrettyPrinter<'a, T> {
PrettyPrinter {
prefix: prefix,
buffer: buffer,
phantom: PhantomData
prefix: prefix,
buffer: buffer,
phantom: PhantomData,
}
}
}

View File

@ -1,10 +1,10 @@
use core::{i32, ops, cmp, fmt};
use byteorder::{ByteOrder, NetworkEndian};
use core::{cmp, fmt, i32, ops};
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::{IpProtocol, IpAddress};
use crate::wire::ip::checksum;
use crate::wire::{IpAddress, IpProtocol};
use crate::{Error, Result};
/// A TCP sequence number.
///
@ -70,7 +70,7 @@ impl cmp::PartialOrd for SeqNumber {
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
mod field {
@ -80,12 +80,12 @@ mod field {
pub const SRC_PORT: Field = 0..2;
pub const DST_PORT: Field = 2..4;
pub const SEQ_NUM: Field = 4..8;
pub const ACK_NUM: Field = 8..12;
pub const FLAGS: Field = 12..14;
pub const SEQ_NUM: Field = 4..8;
pub const ACK_NUM: Field = 8..12;
pub const FLAGS: Field = 12..14;
pub const WIN_SIZE: Field = 14..16;
pub const CHECKSUM: Field = 16..18;
pub const URGENT: Field = 18..20;
pub const URGENT: Field = 18..20;
pub fn OPTIONS(length: u8) -> Field {
URGENT.end..(length as usize)
@ -99,14 +99,14 @@ mod field {
pub const FLG_URG: u16 = 0x020;
pub const FLG_ECE: u16 = 0x040;
pub const FLG_CWR: u16 = 0x080;
pub const FLG_NS: u16 = 0x100;
pub const FLG_NS: u16 = 0x100;
pub const OPT_END: u8 = 0x00;
pub const OPT_NOP: u8 = 0x01;
pub const OPT_MSS: u8 = 0x02;
pub const OPT_WS: u8 = 0x03;
pub const OPT_WS: u8 = 0x03;
pub const OPT_SACKPERM: u8 = 0x04;
pub const OPT_SACKRNG: u8 = 0x05;
pub const OPT_SACKRNG: u8 = 0x05;
}
pub const HEADER_LEN: usize = field::URGENT.end;
@ -289,8 +289,12 @@ impl<T: AsRef<[u8]>> Packet<T> {
pub fn segment_len(&self) -> usize {
let data = self.buffer.as_ref();
let mut length = data.len() - self.header_len() as usize;
if self.syn() { length += 1 }
if self.fin() { length += 1 }
if self.syn() {
length += 1
}
if self.fin() {
length += 1
}
length
}
@ -333,13 +337,14 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
if cfg!(fuzzing) { return true }
if cfg!(fuzzing) {
return true;
}
let data = self.buffer.as_ref();
checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp,
data.len() as u32),
checksum::data(data)
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32),
checksum::data(data),
]) == !0
}
}
@ -405,7 +410,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_fin(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_FIN } else { raw & !field::FLG_FIN };
let raw = if value {
raw | field::FLG_FIN
} else {
raw & !field::FLG_FIN
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -414,7 +423,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_syn(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_SYN } else { raw & !field::FLG_SYN };
let raw = if value {
raw | field::FLG_SYN
} else {
raw & !field::FLG_SYN
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -423,7 +436,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_rst(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_RST } else { raw & !field::FLG_RST };
let raw = if value {
raw | field::FLG_RST
} else {
raw & !field::FLG_RST
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -432,7 +449,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_psh(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_PSH } else { raw & !field::FLG_PSH };
let raw = if value {
raw | field::FLG_PSH
} else {
raw & !field::FLG_PSH
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -441,7 +462,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_ack(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_ACK } else { raw & !field::FLG_ACK };
let raw = if value {
raw | field::FLG_ACK
} else {
raw & !field::FLG_ACK
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -450,7 +475,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_urg(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_URG } else { raw & !field::FLG_URG };
let raw = if value {
raw | field::FLG_URG
} else {
raw & !field::FLG_URG
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -459,7 +488,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_ece(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_ECE } else { raw & !field::FLG_ECE };
let raw = if value {
raw | field::FLG_ECE
} else {
raw & !field::FLG_ECE
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -468,7 +501,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_cwr(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_CWR } else { raw & !field::FLG_CWR };
let raw = if value {
raw | field::FLG_CWR
} else {
raw & !field::FLG_CWR
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -477,7 +514,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_ns(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value { raw | field::FLG_NS } else { raw & !field::FLG_NS };
let raw = if value {
raw | field::FLG_NS
} else {
raw & !field::FLG_NS
};
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -521,9 +562,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
let checksum = {
let data = self.buffer.as_ref();
!checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp,
data.len() as u32),
checksum::data(data)
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32),
checksum::data(data),
])
};
self.set_checksum(checksum)
@ -562,7 +602,7 @@ pub enum TcpOption<'a> {
WindowScale(u8),
SackPermitted,
SackRange([Option<(u32, u32)>; 3]),
Unknown { kind: u8, data: &'a [u8] }
Unknown { kind: u8, data: &'a [u8] },
}
impl<'a> TcpOption<'a> {
@ -581,24 +621,18 @@ impl<'a> TcpOption<'a> {
length = *buffer.get(1).ok_or(Error::Truncated)? as usize;
let data = buffer.get(2..length).ok_or(Error::Truncated)?;
match (kind, length) {
(field::OPT_END, _) |
(field::OPT_NOP, _) =>
unreachable!(),
(field::OPT_MSS, 4) =>
option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data)),
(field::OPT_MSS, _) =>
return Err(Error::Malformed),
(field::OPT_WS, 3) =>
option = TcpOption::WindowScale(data[0]),
(field::OPT_WS, _) =>
return Err(Error::Malformed),
(field::OPT_SACKPERM, 2) =>
option = TcpOption::SackPermitted,
(field::OPT_SACKPERM, _) =>
return Err(Error::Malformed),
(field::OPT_END, _) | (field::OPT_NOP, _) => unreachable!(),
(field::OPT_MSS, 4) => {
option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data))
}
(field::OPT_MSS, _) => return Err(Error::Malformed),
(field::OPT_WS, 3) => option = TcpOption::WindowScale(data[0]),
(field::OPT_WS, _) => return Err(Error::Malformed),
(field::OPT_SACKPERM, 2) => option = TcpOption::SackPermitted,
(field::OPT_SACKPERM, _) => return Err(Error::Malformed),
(field::OPT_SACKRNG, n) => {
if n < 10 || (n-2) % 8 != 0 {
return Err(Error::Malformed)
if n < 10 || (n - 2) % 8 != 0 {
return Err(Error::Malformed);
}
if n > 26 {
// It's possible for a remote to send 4 SACK blocks, but extremely rare.
@ -622,19 +656,16 @@ impl<'a> TcpOption<'a> {
*nmut = if left < data.len() {
let mid = left + 4;
let right = mid + 4;
let range_left = NetworkEndian::read_u32(
&data[left..mid]);
let range_right = NetworkEndian::read_u32(
&data[mid..right]);
let range_left = NetworkEndian::read_u32(&data[left..mid]);
let range_right = NetworkEndian::read_u32(&data[mid..right]);
Some((range_left, range_right))
} else {
None
};
});
option = TcpOption::SackRange(sack_ranges);
},
(_, _) =>
option = TcpOption::Unknown { kind, data }
}
(_, _) => option = TcpOption::Unknown { kind, data },
}
}
}
@ -649,7 +680,7 @@ impl<'a> TcpOption<'a> {
TcpOption::WindowScale(_) => 3,
TcpOption::SackPermitted => 2,
TcpOption::SackRange(s) => s.iter().filter(|s| s.is_some()).count() * 8 + 2,
TcpOption::Unknown { data, .. } => 2 + data.len()
TcpOption::Unknown { data, .. } => 2 + data.len(),
}
}
@ -657,23 +688,21 @@ impl<'a> TcpOption<'a> {
let length;
match *self {
TcpOption::EndOfList => {
length = 1;
length = 1;
// There may be padding space which also should be initialized.
for p in buffer.iter_mut() {
*p = field::OPT_END;
}
}
TcpOption::NoOperation => {
length = 1;
length = 1;
buffer[0] = field::OPT_NOP;
}
_ => {
length = self.buffer_len();
length = self.buffer_len();
buffer[1] = length as u8;
match self {
&TcpOption::EndOfList |
&TcpOption::NoOperation =>
unreachable!(),
&TcpOption::EndOfList | &TcpOption::NoOperation => unreachable!(),
&TcpOption::MaxSegmentSize(value) => {
buffer[0] = field::OPT_MSS;
NetworkEndian::write_u16(&mut buffer[2..], value)
@ -687,14 +716,21 @@ impl<'a> TcpOption<'a> {
}
&TcpOption::SackRange(slice) => {
buffer[0] = field::OPT_SACKRNG;
slice.iter().filter(|s| s.is_some()).enumerate().for_each(|(i, s)| {
let (first, second) = *s.as_ref().unwrap();
let pos = i * 8 + 2;
NetworkEndian::write_u32(&mut buffer[pos..], first);
NetworkEndian::write_u32(&mut buffer[pos+4..], second);
});
slice
.iter()
.filter(|s| s.is_some())
.enumerate()
.for_each(|(i, s)| {
let (first, second) = *s.as_ref().unwrap();
let pos = i * 8 + 2;
NetworkEndian::write_u32(&mut buffer[pos..], first);
NetworkEndian::write_u32(&mut buffer[pos + 4..], second);
});
}
&TcpOption::Unknown { kind, data: provided } => {
&TcpOption::Unknown {
kind,
data: provided,
} => {
buffer[0] = kind;
buffer[2..].copy_from_slice(provided)
}
@ -713,7 +749,7 @@ pub enum Control {
Psh,
Syn,
Fin,
Rst
Rst,
}
#[allow(clippy::len_without_is_empty)]
@ -721,8 +757,8 @@ impl Control {
/// Return the length of a control flag, in terms of sequence space.
pub fn len(self) -> usize {
match self {
Control::Syn | Control::Fin => 1,
_ => 0
Control::Syn | Control::Fin => 1,
_ => 0,
}
}
@ -730,7 +766,7 @@ impl Control {
pub fn quash_psh(self) -> Control {
match self {
Control::Psh => Control::None,
_ => self
_ => self,
}
}
}
@ -739,46 +775,54 @@ impl Control {
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr<'a> {
pub src_port: u16,
pub dst_port: u16,
pub control: Control,
pub seq_number: SeqNumber,
pub ack_number: Option<SeqNumber>,
pub window_len: u16,
pub src_port: u16,
pub dst_port: u16,
pub control: Control,
pub seq_number: SeqNumber,
pub ack_number: Option<SeqNumber>,
pub window_len: u16,
pub window_scale: Option<u8>,
pub max_seg_size: Option<u16>,
pub sack_permitted: bool,
pub sack_ranges: [Option<(u32, u32)>; 3],
pub payload: &'a [u8]
pub sack_ranges: [Option<(u32, u32)>; 3],
pub payload: &'a [u8],
}
impl<'a> Repr<'a> {
/// Parse a Transmission Control Protocol packet and return a high-level representation.
pub fn parse<T>(packet: &Packet<&'a T>, src_addr: &IpAddress, dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities) -> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(
packet: &Packet<&'a T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
// Source and destination ports must be present.
if packet.src_port() == 0 { return Err(Error::Malformed) }
if packet.dst_port() == 0 { return Err(Error::Malformed) }
if packet.src_port() == 0 {
return Err(Error::Malformed);
}
if packet.dst_port() == 0 {
return Err(Error::Malformed);
}
// Valid checksum is expected.
if checksum_caps.tcp.rx() && !packet.verify_checksum(src_addr, dst_addr) {
return Err(Error::Checksum)
return Err(Error::Checksum);
}
let control =
match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) {
(false, false, false, false) => Control::None,
(false, false, false, true) => Control::Psh,
(true, false, false, _) => Control::Syn,
(false, true, false, _) => Control::Fin,
(false, false, true , _) => Control::Rst,
_ => return Err(Error::Malformed)
};
let ack_number =
match packet.ack() {
true => Some(packet.ack_number()),
false => None
};
let control = match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) {
(false, false, false, false) => Control::None,
(false, false, false, true) => Control::Psh,
(true, false, false, _) => Control::Syn,
(false, true, false, _) => Control::Fin,
(false, false, true, _) => Control::Rst,
_ => return Err(Error::Malformed),
};
let ack_number = match packet.ack() {
true => Some(packet.ack_number()),
false => None,
};
// The PSH flag is ignored.
// The URG flag and the urgent field is ignored. This behavior is standards-compliant,
// however, most deployed systems (e.g. Linux) are *not* standards-compliant, and would
@ -794,41 +838,44 @@ impl<'a> Repr<'a> {
match option {
TcpOption::EndOfList => break,
TcpOption::NoOperation => (),
TcpOption::MaxSegmentSize(value) =>
max_seg_size = Some(value),
TcpOption::MaxSegmentSize(value) => max_seg_size = Some(value),
TcpOption::WindowScale(value) => {
// RFC 1323: Thus, the shift count must be limited to 14 (which allows windows
// of 2**30 = 1 Gbyte). If a Window Scale option is received with a shift.cnt
// value exceeding 14, the TCP should log the error but use 14 instead of the
// specified value.
window_scale = if value > 14 {
net_debug!("{}:{}:{}:{}: parsed window scaling factor >14, setting to 14", src_addr, packet.src_port(), dst_addr, packet.dst_port());
net_debug!(
"{}:{}:{}:{}: parsed window scaling factor >14, setting to 14",
src_addr,
packet.src_port(),
dst_addr,
packet.dst_port()
);
Some(14)
} else {
Some(value)
};
},
TcpOption::SackPermitted =>
sack_permitted = true,
TcpOption::SackRange(slice) =>
sack_ranges = slice,
}
TcpOption::SackPermitted => sack_permitted = true,
TcpOption::SackRange(slice) => sack_ranges = slice,
_ => (),
}
options = next_options;
}
Ok(Repr {
src_port: packet.src_port(),
dst_port: packet.dst_port(),
control: control,
seq_number: packet.seq_number(),
ack_number: ack_number,
window_len: packet.window_len(),
src_port: packet.src_port(),
dst_port: packet.dst_port(),
control: control,
seq_number: packet.seq_number(),
ack_number: ack_number,
window_len: packet.window_len(),
window_scale: window_scale,
max_seg_size: max_seg_size,
sack_permitted: sack_permitted,
sack_ranges: sack_ranges,
payload: packet.payload()
sack_ranges: sack_ranges,
payload: packet.payload(),
})
}
@ -847,9 +894,11 @@ impl<'a> Repr<'a> {
if self.sack_permitted {
length += 2;
}
let sack_range_len: usize = self.sack_ranges.iter().map(
|o| o.map(|_| 8).unwrap_or(0)
).sum();
let sack_range_len: usize = self
.sack_ranges
.iter()
.map(|o| o.map(|_| 8).unwrap_or(0))
.sum();
if sack_range_len > 0 {
length += sack_range_len + 2;
}
@ -865,9 +914,15 @@ impl<'a> Repr<'a> {
}
/// Emit a high-level representation into a Transmission Control Protocol packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>, src_addr: &IpAddress, dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
pub fn emit<T>(
&self,
packet: &mut Packet<&mut T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities,
) where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
packet.set_src_port(self.src_port);
packet.set_dst_port(self.dst_port);
packet.set_seq_number(self.seq_number);
@ -877,24 +932,28 @@ impl<'a> Repr<'a> {
packet.clear_flags();
match self.control {
Control::None => (),
Control::Psh => packet.set_psh(true),
Control::Syn => packet.set_syn(true),
Control::Fin => packet.set_fin(true),
Control::Rst => packet.set_rst(true)
Control::Psh => packet.set_psh(true),
Control::Syn => packet.set_syn(true),
Control::Fin => packet.set_fin(true),
Control::Rst => packet.set_rst(true),
}
packet.set_ack(self.ack_number.is_some());
{
let mut options = packet.options_mut();
if let Some(value) = self.max_seg_size {
let tmp = options; options = TcpOption::MaxSegmentSize(value).emit(tmp);
let tmp = options;
options = TcpOption::MaxSegmentSize(value).emit(tmp);
}
if let Some(value) = self.window_scale {
let tmp = options; options = TcpOption::WindowScale(value).emit(tmp);
let tmp = options;
options = TcpOption::WindowScale(value).emit(tmp);
}
if self.sack_permitted {
let tmp = options; options = TcpOption::SackPermitted.emit(tmp);
let tmp = options;
options = TcpOption::SackPermitted.emit(tmp);
} else if self.ack_number.is_some() && self.sack_ranges.iter().any(|s| s.is_some()) {
let tmp = options; options = TcpOption::SackRange(self.sack_ranges).emit(tmp);
let tmp = options;
options = TcpOption::SackRange(self.sack_ranges).emit(tmp);
}
if !options.is_empty() {
@ -922,8 +981,8 @@ impl<'a> Repr<'a> {
pub fn is_empty(&self) -> bool {
match self.control {
_ if !self.payload.is_empty() => false,
Control::Syn | Control::Fin | Control::Rst => false,
Control::None | Control::Psh => true
Control::Syn | Control::Fin | Control::Rst => false,
Control::None | Control::Psh => true,
}
}
}
@ -931,15 +990,28 @@ impl<'a> Repr<'a> {
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, "TCP src={} dst={}",
self.src_port(), self.dst_port())?;
if self.syn() { write!(f, " syn")? }
if self.fin() { write!(f, " fin")? }
if self.rst() { write!(f, " rst")? }
if self.psh() { write!(f, " psh")? }
if self.ece() { write!(f, " ece")? }
if self.cwr() { write!(f, " cwr")? }
if self.ns() { write!(f, " ns" )? }
write!(f, "TCP src={} dst={}", self.src_port(), self.dst_port())?;
if self.syn() {
write!(f, " syn")?
}
if self.fin() {
write!(f, " fin")?
}
if self.rst() {
write!(f, " rst")?
}
if self.psh() {
write!(f, " psh")?
}
if self.ece() {
write!(f, " ece")?
}
if self.cwr() {
write!(f, " cwr")?
}
if self.ns() {
write!(f, " ns")?
}
write!(f, " seq={}", self.seq_number())?;
if self.ack() {
write!(f, " ack={}", self.ack_number())?;
@ -952,24 +1024,18 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
let mut options = self.options();
while !options.is_empty() {
let (next_options, option) =
match TcpOption::parse(options) {
Ok(res) => res,
Err(err) => return write!(f, " ({})", err)
};
let (next_options, option) = match TcpOption::parse(options) {
Ok(res) => res,
Err(err) => return write!(f, " ({})", err),
};
match option {
TcpOption::EndOfList => break,
TcpOption::NoOperation => (),
TcpOption::MaxSegmentSize(value) =>
write!(f, " mss={}", value)?,
TcpOption::WindowScale(value) =>
write!(f, " ws={}", value)?,
TcpOption::SackPermitted =>
write!(f, " sACK")?,
TcpOption::SackRange(slice) =>
write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s
TcpOption::Unknown { kind, .. } =>
write!(f, " opt({})", kind)?,
TcpOption::MaxSegmentSize(value) => write!(f, " mss={}", value)?,
TcpOption::WindowScale(value) => write!(f, " ws={}", value)?,
TcpOption::SackPermitted => write!(f, " sACK")?,
TcpOption::SackRange(slice) => write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s
TcpOption::Unknown { kind, .. } => write!(f, " opt({})", kind)?,
}
options = next_options;
}
@ -979,14 +1045,13 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TCP src={} dst={}",
self.src_port, self.dst_port)?;
write!(f, "TCP src={} dst={}", self.src_port, self.dst_port)?;
match self.control {
Control::Syn => write!(f, " syn")?,
Control::Fin => write!(f, " fin")?,
Control::Rst => write!(f, " rst")?,
Control::Psh => write!(f, " psh")?,
Control::None => ()
Control::None => (),
}
write!(f, " seq={}", self.seq_number)?;
if let Some(ack_number) = self.ack_number {
@ -1001,23 +1066,26 @@ impl<'a> fmt::Display for Repr<'a> {
}
}
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
match Packet::new_checked(buffer) {
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet)
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "proto-ipv4")]
use crate::wire::Ipv4Address;
use super::*;
#[cfg(feature = "proto-ipv4")]
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
@ -1025,22 +1093,16 @@ mod test {
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
#[cfg(feature = "proto-ipv4")]
static PACKET_BYTES: [u8; 28] =
[0xbf, 0x00, 0x00, 0x50,
0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef,
0x60, 0x35, 0x01, 0x23,
0x01, 0xb6, 0x02, 0x01,
0x03, 0x03, 0x0c, 0x01,
0xaa, 0x00, 0x00, 0xff];
static PACKET_BYTES: [u8; 28] = [
0xbf, 0x00, 0x00, 0x50, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x60, 0x35, 0x01,
0x23, 0x01, 0xb6, 0x02, 0x01, 0x03, 0x03, 0x0c, 0x01, 0xaa, 0x00, 0x00, 0xff,
];
#[cfg(feature = "proto-ipv4")]
static OPTION_BYTES: [u8; 4] =
[0x03, 0x03, 0x0c, 0x01];
static OPTION_BYTES: [u8; 4] = [0x03, 0x03, 0x0c, 0x01];
#[cfg(feature = "proto-ipv4")]
static PAYLOAD_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
static PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
#[test]
#[cfg(feature = "proto-ipv4")]
@ -1062,7 +1124,10 @@ mod test {
assert_eq!(packet.checksum(), 0x01b6);
assert_eq!(packet.options(), &OPTION_BYTES[..]);
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
assert_eq!(
packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()),
true
);
}
#[test]
@ -1107,28 +1172,25 @@ mod test {
}
#[cfg(feature = "proto-ipv4")]
static SYN_PACKET_BYTES: [u8; 24] =
[0xbf, 0x00, 0x00, 0x50,
0x01, 0x23, 0x45, 0x67,
0x00, 0x00, 0x00, 0x00,
0x50, 0x02, 0x01, 0x23,
0x7a, 0x8d, 0x00, 0x00,
0xaa, 0x00, 0x00, 0xff];
static SYN_PACKET_BYTES: [u8; 24] = [
0xbf, 0x00, 0x00, 0x50, 0x01, 0x23, 0x45, 0x67, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x01,
0x23, 0x7a, 0x8d, 0x00, 0x00, 0xaa, 0x00, 0x00, 0xff,
];
#[cfg(feature = "proto-ipv4")]
fn packet_repr() -> Repr<'static> {
Repr {
src_port: 48896,
dst_port: 80,
seq_number: SeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
src_port: 48896,
dst_port: 80,
seq_number: SeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
window_scale: None,
control: Control::Syn,
control: Control::Syn,
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
payload: &PAYLOAD_BYTES
sack_ranges: [None, None, None],
payload: &PAYLOAD_BYTES,
}
}
@ -1136,7 +1198,13 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_parse() {
let packet = Packet::new_unchecked(&SYN_PACKET_BYTES[..]);
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()).unwrap();
let repr = Repr::parse(
&packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
&ChecksumCapabilities::default(),
)
.unwrap();
assert_eq!(repr, packet_repr());
}
@ -1146,7 +1214,12 @@ mod test {
let repr = packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default());
repr.emit(
&mut packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]);
}
@ -1159,57 +1232,62 @@ mod test {
}
macro_rules! assert_option_parses {
($opt:expr, $data:expr) => ({
($opt:expr, $data:expr) => {{
assert_eq!(TcpOption::parse($data), Ok((&[][..], $opt)));
let buffer = &mut [0; 40][..$opt.buffer_len()];
assert_eq!($opt.emit(buffer), &mut []);
assert_eq!(&*buffer, $data);
})
}};
}
#[test]
fn test_tcp_options() {
assert_option_parses!(TcpOption::EndOfList,
&[0x00]);
assert_option_parses!(TcpOption::NoOperation,
&[0x01]);
assert_option_parses!(TcpOption::MaxSegmentSize(1500),
&[0x02, 0x04, 0x05, 0xdc]);
assert_option_parses!(TcpOption::WindowScale(12),
&[0x03, 0x03, 0x0c]);
assert_option_parses!(TcpOption::SackPermitted,
&[0x4, 0x02]);
assert_option_parses!(TcpOption::SackRange([Some((500, 1500)), None, None]),
&[0x05, 0x0a,
0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x05, 0xdc]);
assert_option_parses!(TcpOption::SackRange([Some((875, 1225)), Some((1500, 2500)), None]),
&[0x05, 0x12,
0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x04, 0xc9,
0x00, 0x00, 0x05, 0xdc, 0x00, 0x00, 0x09, 0xc4]);
assert_option_parses!(TcpOption::SackRange([Some((875000, 1225000)),
Some((1500000, 2500000)),
Some((876543210, 876654320))]),
&[0x05, 0x1a,
0x00, 0x0d, 0x59, 0xf8, 0x00, 0x12, 0xb1, 0x28,
0x00, 0x16, 0xe3, 0x60, 0x00, 0x26, 0x25, 0xa0,
0x34, 0x3e, 0xfc, 0xea, 0x34, 0x40, 0xae, 0xf0]);
assert_option_parses!(TcpOption::Unknown { kind: 12, data: &[1, 2, 3][..] },
&[0x0c, 0x05, 0x01, 0x02, 0x03])
assert_option_parses!(TcpOption::EndOfList, &[0x00]);
assert_option_parses!(TcpOption::NoOperation, &[0x01]);
assert_option_parses!(TcpOption::MaxSegmentSize(1500), &[0x02, 0x04, 0x05, 0xdc]);
assert_option_parses!(TcpOption::WindowScale(12), &[0x03, 0x03, 0x0c]);
assert_option_parses!(TcpOption::SackPermitted, &[0x4, 0x02]);
assert_option_parses!(
TcpOption::SackRange([Some((500, 1500)), None, None]),
&[0x05, 0x0a, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x05, 0xdc]
);
assert_option_parses!(
TcpOption::SackRange([Some((875, 1225)), Some((1500, 2500)), None]),
&[
0x05, 0x12, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x04, 0xc9, 0x00, 0x00, 0x05, 0xdc,
0x00, 0x00, 0x09, 0xc4
]
);
assert_option_parses!(
TcpOption::SackRange([
Some((875000, 1225000)),
Some((1500000, 2500000)),
Some((876543210, 876654320))
]),
&[
0x05, 0x1a, 0x00, 0x0d, 0x59, 0xf8, 0x00, 0x12, 0xb1, 0x28, 0x00, 0x16, 0xe3, 0x60,
0x00, 0x26, 0x25, 0xa0, 0x34, 0x3e, 0xfc, 0xea, 0x34, 0x40, 0xae, 0xf0
]
);
assert_option_parses!(
TcpOption::Unknown {
kind: 12,
data: &[1, 2, 3][..]
},
&[0x0c, 0x05, 0x01, 0x02, 0x03]
)
}
#[test]
fn test_malformed_tcp_options() {
assert_eq!(TcpOption::parse(&[]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc, 0x01]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0x2, 0x02]),
Err(Error::Malformed));
assert_eq!(TcpOption::parse(&[0x3, 0x02]),
Err(Error::Malformed));
assert_eq!(TcpOption::parse(&[]), Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc]), Err(Error::Truncated));
assert_eq!(
TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]),
Err(Error::Truncated)
);
assert_eq!(TcpOption::parse(&[0xc, 0x01]), Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0x2, 0x02]), Err(Error::Malformed));
assert_eq!(TcpOption::parse(&[0x3, 0x02]), Err(Error::Malformed));
}
}

View File

@ -1,16 +1,16 @@
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::{IpProtocol, IpAddress};
use crate::wire::ip::checksum;
use crate::wire::{IpAddress, IpProtocol};
use crate::{Error, Result};
/// A read/write wrapper around an User Datagram Protocol packet buffer.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T
buffer: T,
}
mod field {
@ -20,7 +20,7 @@ mod field {
pub const SRC_PORT: Field = 0..2;
pub const DST_PORT: Field = 2..4;
pub const LENGTH: Field = 4..6;
pub const LENGTH: Field = 4..6;
pub const CHECKSUM: Field = 6..8;
pub fn PAYLOAD(length: u16) -> Field {
@ -113,13 +113,14 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
if cfg!(fuzzing) { return true }
if cfg!(fuzzing) {
return true;
}
let data = self.buffer.as_ref();
checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp,
self.len() as u32),
checksum::data(&data[..self.len() as usize])
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32),
checksum::data(&data[..self.len() as usize]),
]) == !0
}
}
@ -173,9 +174,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
let checksum = {
let data = self.buffer.as_ref();
!checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp,
self.len() as u32),
checksum::data(&data[..self.len() as usize])
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32),
checksum::data(&data[..self.len() as usize]),
])
};
// UDP checksum value of 0 means no checksum; if the checksum really is zero,
@ -210,21 +210,26 @@ pub struct Repr {
impl Repr {
/// Parse an User Datagram Protocol packet and return a high-level representation.
pub fn parse<T>(packet: &Packet<&T>, src_addr: &IpAddress, dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities) -> Result<Repr>
where T: AsRef<[u8]> + ?Sized {
pub fn parse<T>(
packet: &Packet<&T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr>
where
T: AsRef<[u8]> + ?Sized,
{
// Destination port cannot be omitted (but source port can be).
if packet.dst_port() == 0 { return Err(Error::Malformed) }
if packet.dst_port() == 0 {
return Err(Error::Malformed);
}
// Valid checksum is expected...
if checksum_caps.udp.rx() && !packet.verify_checksum(src_addr, dst_addr) {
match (src_addr, dst_addr) {
// ... except on UDP-over-IPv4, where it can be omitted.
#[cfg(feature = "proto-ipv4")]
(&IpAddress::Ipv4(_), &IpAddress::Ipv4(_))
if packet.checksum() == 0 => (),
_ => {
return Err(Error::Checksum)
}
(&IpAddress::Ipv4(_), &IpAddress::Ipv4(_)) if packet.checksum() == 0 => (),
_ => return Err(Error::Checksum),
}
}
@ -240,13 +245,17 @@ impl Repr {
}
/// Emit a high-level representation into an User Datagram Protocol packet.
pub fn emit<T: ?Sized>(&self, packet: &mut Packet<&mut T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
payload_len: usize,
emit_payload: impl FnOnce(&mut [u8]),
checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> {
pub fn emit<T: ?Sized>(
&self,
packet: &mut Packet<&mut T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
payload_len: usize,
emit_payload: impl FnOnce(&mut [u8]),
checksum_caps: &ChecksumCapabilities,
) where
T: AsRef<[u8]> + AsMut<[u8]>,
{
packet.set_src_port(self.src_port);
packet.set_dst_port(self.dst_port);
packet.set_len((HEADER_LEN + payload_len) as u16);
@ -265,8 +274,13 @@ impl Repr {
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())
write!(
f,
"UDP src={} dst={} len={}",
self.src_port(),
self.dst_port(),
self.payload().len()
)
}
}
@ -276,23 +290,26 @@ impl fmt::Display for Repr {
}
}
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
match Packet::new_checked(buffer) {
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet)
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "proto-ipv4")]
use crate::wire::Ipv4Address;
use super::*;
#[cfg(feature = "proto-ipv4")]
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
@ -300,20 +317,17 @@ mod test {
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
#[cfg(feature = "proto-ipv4")]
static PACKET_BYTES: [u8; 12] =
[0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x12, 0x4d,
0xaa, 0x00, 0x00, 0xff];
static PACKET_BYTES: [u8; 12] = [
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
];
#[cfg(feature = "proto-ipv4")]
static NO_CHECKSUM_PACKET: [u8; 12] =
[0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x00, 0x00,
0xaa, 0x00, 0x00, 0xff];
static NO_CHECKSUM_PACKET: [u8; 12] = [
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x00, 0x00, 0xaa, 0x00, 0x00, 0xff,
];
#[cfg(feature = "proto-ipv4")]
static PAYLOAD_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
static PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
#[test]
#[cfg(feature = "proto-ipv4")]
@ -324,7 +338,10 @@ mod test {
assert_eq!(packet.len(), 12);
assert_eq!(packet.checksum(), 0x124d);
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
assert_eq!(
packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()),
true
);
}
#[test]
@ -373,8 +390,13 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_parse() {
let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(),
&ChecksumCapabilities::default()).unwrap();
let repr = Repr::parse(
&packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
&ChecksumCapabilities::default(),
)
.unwrap();
assert_eq!(repr, packet_repr());
}
@ -384,10 +406,14 @@ mod test {
let repr = packet_repr();
let mut bytes = vec![0xa5; repr.header_len() + PAYLOAD_BYTES.len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(),
PAYLOAD_BYTES.len(),
|payload| payload.copy_from_slice(&PAYLOAD_BYTES),
&ChecksumCapabilities::default());
repr.emit(
&mut packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
PAYLOAD_BYTES.len(),
|payload| payload.copy_from_slice(&PAYLOAD_BYTES),
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
}
@ -395,8 +421,13 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_checksum_omitted() {
let packet = Packet::new_unchecked(&NO_CHECKSUM_PACKET[..]);
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(),
&ChecksumCapabilities::default()).unwrap();
let repr = Repr::parse(
&packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
&ChecksumCapabilities::default(),
)
.unwrap();
assert_eq!(repr, packet_repr());
}
}

View File

@ -1,15 +1,18 @@
use std::cell::RefCell;
use std::io::{self, Read, Write};
use std::path::Path;
use std::fs::File;
use std::env;
use std::process::exit;
use getopts::Options;
use smoltcp::phy::{PcapLinkType, PcapSink};
use smoltcp::time::Instant;
use getopts::Options;
use std::cell::RefCell;
use std::env;
use std::fs::File;
use std::io::{self, Read, Write};
use std::path::Path;
use std::process::exit;
fn convert(packet_filename: &Path, pcap_filename: &Path, link_type: PcapLinkType)
-> io::Result<()> {
fn convert(
packet_filename: &Path,
pcap_filename: &Path,
link_type: PcapLinkType,
) -> io::Result<()> {
let mut packet_file = File::open(packet_filename)?;
let mut packet = Vec::new();
packet_file.read_to_end(&mut packet)?;
@ -35,31 +38,37 @@ fn main() {
let mut opts = Options::new();
opts.optflag("h", "help", "print this help menu");
opts.optopt("t", "link-type", "set link type (one of: ethernet ip)", "TYPE");
opts.optopt(
"t",
"link-type",
"set link type (one of: ethernet ip)",
"TYPE",
);
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(e) => {
eprintln!("{}", e);
return
return;
}
};
let link_type =
match matches.opt_str("t").as_ref().map(|s| &s[..]) {
Some("ethernet") => Some(PcapLinkType::Ethernet),
Some("ip") => Some(PcapLinkType::Ip),
_ => None
};
let link_type = match matches.opt_str("t").as_ref().map(|s| &s[..]) {
Some("ethernet") => Some(PcapLinkType::Ethernet),
Some("ip") => Some(PcapLinkType::Ip),
_ => None,
};
if matches.opt_present("h") || matches.free.len() != 2 || link_type.is_none() {
print_usage(&program, opts);
return
return;
}
match convert(Path::new(&matches.free[0]),
Path::new(&matches.free[1]),
link_type.unwrap()) {
match convert(
Path::new(&matches.free[0]),
Path::new(&matches.free[1]),
link_type.unwrap(),
) {
Ok(()) => (),
Err(e) => {
eprintln!("Cannot convert packet to pcap: {}", e);