Merge pull request #506 from qiujiangkun/fix_clippy

Various clippy fix
This commit is contained in:
Dario Nieuwenhuis 2021-06-27 17:07:25 +02:00 committed by GitHub
commit ec416ceac4
67 changed files with 9850 additions and 6345 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -7,18 +7,18 @@
mod utils; mod utils;
use core::str; 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::phy::{Loopback, Medium};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer}; use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant}; use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
mod mock { mod mock {
use smoltcp::time::{Duration, Instant};
use core::cell::Cell; use core::cell::Cell;
use smoltcp::time::{Duration, Instant};
#[derive(Debug)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -41,9 +41,9 @@ mod mock {
#[cfg(feature = "std")] #[cfg(feature = "std")]
mod mock { mod mock {
use smoltcp::time::{Duration, Instant};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicUsize};
use smoltcp::time::{Duration, Instant};
// should be AtomicU64 but that's unstable // should be AtomicU64 but that's unstable
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -56,7 +56,8 @@ mod mock {
} }
pub fn advance(&self, duration: Duration) { 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 { pub fn elapsed(&self) -> Instant {
@ -78,7 +79,7 @@ fn main() {
utils::add_middleware_options(&mut opts, &mut free); utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, 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]; 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 ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
let mut iface = InterfaceBuilder::new(device) let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(EthernetAddress::default()) .ethernet_addr(EthernetAddress::default())
.neighbor_cache(neighbor_cache) .neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs) .ip_addrs(ip_addrs)
.finalize(); .finalize();
let server_socket = { let server_socket = {
// It is not strictly necessary to use a `static mut` and unsafe code here, but // 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 server_handle = socket_set.add(server_socket);
let client_handle = socket_set.add(client_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 did_connect = false;
let mut done = false; let mut done = false;
while !done && clock.elapsed() < Instant::from_millis(10_000) { while !done && clock.elapsed() < Instant::from_millis(10_000) {
match iface.poll(&mut socket_set, clock.elapsed()) { match iface.poll(&mut socket_set, clock.elapsed()) {
Ok(_) => {}, Ok(_) => {}
Err(e) => { Err(e) => {
debug!("poll error: {}", e); debug!("poll error: {}", e);
} }
@ -138,9 +139,10 @@ fn main() {
} }
if socket.can_recv() { if socket.can_recv() {
debug!("got {:?}", socket.recv(|buffer| { debug!(
(buffer.len(), str::from_utf8(buffer).unwrap()) "got {:?}",
})); socket.recv(|buffer| { (buffer.len(), str::from_utf8(buffer).unwrap()) })
);
socket.close(); socket.close();
done = true; done = true;
} }
@ -151,8 +153,12 @@ fn main() {
if !socket.is_open() { if !socket.is_open() {
if !did_connect { if !did_connect {
debug!("connecting"); debug!("connecting");
socket.connect((IpAddress::v4(127, 0, 0, 1), 1234), socket
(IpAddress::Unspecified, 65000)).unwrap(); .connect(
(IpAddress::v4(127, 0, 0, 1), 1234),
(IpAddress::Unspecified, 65000),
)
.unwrap();
did_connect = true; did_connect = true;
} }
} }
@ -169,8 +175,8 @@ fn main() {
Some(delay) => { Some(delay) => {
debug!("sleeping for {} ms", delay); debug!("sleeping for {} ms", delay);
clock.advance(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; mod utils;
use log::debug;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use log::debug;
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::wait as phy_wait; use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr, Ipv4Address, use smoltcp::socket::{
Ipv4Packet, IgmpPacket, IgmpRepr}; RawPacketMetadata, RawSocket, RawSocketBuffer, SocketSet, UdpPacketMetadata, UdpSocket,
use smoltcp::iface::{NeighborCache, InterfaceBuilder}; UdpSocketBuffer,
use smoltcp::socket::{SocketSet, };
RawSocket, RawSocketBuffer, RawPacketMetadata,
UdpSocket, UdpSocketBuffer, UdpPacketMetadata};
use smoltcp::time::Instant; use smoltcp::time::Instant;
use smoltcp::wire::{
EthernetAddress, IgmpPacket, IgmpRepr, IpAddress, IpCidr, IpProtocol, IpVersion, Ipv4Address,
Ipv4Packet,
};
const MDNS_PORT: u16 = 5353; const MDNS_PORT: u16 = 5353;
const MDNS_GROUP: [u8; 4] = [224, 0, 0, 251]; const MDNS_GROUP: [u8; 4] = [224, 0, 0, 251];
@ -26,10 +29,7 @@ fn main() {
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
device,
/*loopback=*/
false);
let neighbor_cache = NeighborCache::new(BTreeMap::new()); let neighbor_cache = NeighborCache::new(BTreeMap::new());
let local_addr = Ipv4Address::new(192, 168, 69, 2); 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 ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
let mut ipv4_multicast_storage = [None; 1]; let mut ipv4_multicast_storage = [None; 1];
let mut iface = InterfaceBuilder::new(device) let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr) .ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache) .neighbor_cache(neighbor_cache)
.ip_addrs([ip_addr]) .ip_addrs([ip_addr])
.ipv4_multicast_groups(&mut ipv4_multicast_storage[..]) .ipv4_multicast_groups(&mut ipv4_multicast_storage[..])
.finalize(); .finalize();
let now = Instant::now(); let now = Instant::now();
// Join a multicast group to receive mDNS traffic // 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![]); let mut sockets = SocketSet::new(vec![]);
@ -55,8 +57,10 @@ fn main() {
// Will not send IGMP // Will not send IGMP
let raw_tx_buffer = RawSocketBuffer::new(vec![], vec![]); let raw_tx_buffer = RawSocketBuffer::new(vec![], vec![]);
let raw_socket = RawSocket::new( let raw_socket = RawSocket::new(
IpVersion::Ipv4, IpProtocol::Igmp, IpVersion::Ipv4,
raw_rx_buffer, raw_tx_buffer IpProtocol::Igmp,
raw_rx_buffer,
raw_tx_buffer,
); );
let raw_handle = sockets.add(raw_socket); let raw_handle = sockets.add(raw_socket);
@ -70,9 +74,9 @@ fn main() {
loop { loop {
let timestamp = Instant::now(); let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) { match iface.poll(&mut sockets, timestamp) {
Ok(_) => {}, Ok(_) => {}
Err(e) => { Err(e) => {
debug!("poll error: {}",e); debug!("poll error: {}", e);
} }
} }
@ -82,7 +86,8 @@ fn main() {
if socket.can_recv() { if socket.can_recv() {
// For display purposes only - normally we wouldn't process incoming IGMP packets // For display purposes only - normally we wouldn't process incoming IGMP packets
// in the application layer // in the application layer
socket.recv() socket
.recv()
.and_then(Ipv4Packet::new_checked) .and_then(Ipv4Packet::new_checked)
.and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload())) .and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload()))
.and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet)) .and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet))
@ -97,8 +102,11 @@ fn main() {
} }
if socket.can_recv() { if socket.can_recv() {
socket.recv() socket
.map(|(data, sender)| println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender)) .recv()
.map(|(data, sender)| {
println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender)
})
.unwrap_or_else(|e| println!("Recv UDP error: {:?}", e)); .unwrap_or_else(|e| println!("Recv UDP error: {:?}", e));
} }
} }

View File

@ -1,21 +1,25 @@
mod utils; 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 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::iface::{InterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::Device;
use smoltcp::phy::wait as phy_wait; use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, use smoltcp::phy::Device;
Ipv6Address, Icmpv6Repr, Icmpv6Packet, use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer, SocketSet};
Ipv4Address, Icmpv4Repr, Icmpv4Packet}; use smoltcp::wire::{
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes}; EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr,
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint}; Ipv4Address, Ipv6Address,
};
use smoltcp::{
phy::Medium,
time::{Duration, Instant},
};
macro_rules! send_icmp_ping { macro_rules! send_icmp_ping {
( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr, ( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
@ -26,13 +30,11 @@ macro_rules! send_icmp_ping {
data: &$echo_payload, data: &$echo_payload,
}; };
let icmp_payload = $socket let icmp_payload = $socket.send(icmp_repr.buffer_len(), $remote_addr).unwrap();
.send(icmp_repr.buffer_len(), $remote_addr)
.unwrap();
let icmp_packet = $packet_type::new_unchecked(icmp_payload); let icmp_packet = $packet_type::new_unchecked(icmp_payload);
(icmp_repr, icmp_packet) (icmp_repr, icmp_packet)
}} }};
} }
macro_rules! get_icmp_pong { 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 $repr_type::EchoReply { seq_no, data, .. } = $repr {
if let Some(_) = $waiting_queue.get(&seq_no) { if let Some(_) = $waiting_queue.get(&seq_no) {
let packet_timestamp_ms = NetworkEndian::read_i64(data); let packet_timestamp_ms = NetworkEndian::read_i64(data);
println!("{} bytes from {}: icmp_seq={}, time={}ms", println!(
data.len(), $remote_addr, seq_no, "{} bytes from {}: icmp_seq={}, time={}ms",
$timestamp.total_millis() - packet_timestamp_ms); data.len(),
$remote_addr,
seq_no,
$timestamp.total_millis() - packet_timestamp_ms
);
$waiting_queue.remove(&seq_no); $waiting_queue.remove(&seq_no);
$received += 1; $received += 1;
} }
} }
}} }};
} }
fn main() { fn main() {
@ -57,26 +63,45 @@ fn main() {
let (mut opts, mut free) = utils::create_options(); let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free); utils::add_tuntap_options(&mut opts, &mut free);
utils::add_middleware_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(
opts.optopt("i", "interval", "c",
"Interval between successive packets sent (seconds) (default: 1)", "INTERVAL"); "count",
opts.optopt("", "timeout", "Amount of echo request packets to send (default: 4)",
"Maximum wait duration for an echo response packet (seconds) (default: 5)", "COUNT",
"TIMEOUT"); );
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"); free.push("ADDRESS");
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); 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 device_caps = device.capabilities();
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format"); 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 count = matches
let interval = matches.opt_str("interval") .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())) .map(|s| Duration::from_secs(u64::from_str(&s).unwrap()))
.unwrap_or_else(|| Duration::from_secs(1)); .unwrap_or_else(|| Duration::from_secs(1));
let timeout = Duration::from_secs( let timeout = Duration::from_secs(
matches.opt_str("timeout").map(|s| u64::from_str(&s).unwrap()).unwrap_or(5) matches
.opt_str("timeout")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(5),
); );
let neighbor_cache = NeighborCache::new(BTreeMap::new()); let neighbor_cache = NeighborCache::new(BTreeMap::new());
@ -89,9 +114,11 @@ fn main() {
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]); let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1); 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), let ip_addrs = [
IpCidr::new(src_ipv6, 64), IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)]; 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_v4_gw = Ipv4Address::new(192, 168, 69, 100);
let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100); let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100);
let mut routes_storage = [None; 2]; let mut routes_storage = [None; 2];
@ -101,8 +128,8 @@ fn main() {
let medium = device.capabilities().medium; let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device) let mut builder = InterfaceBuilder::new(device)
.ip_addrs(ip_addrs) .ip_addrs(ip_addrs)
.routes(routes); .routes(routes);
if medium == Medium::Ethernet { if medium == Medium::Ethernet {
builder = builder builder = builder
.ethernet_addr(ethernet_addr) .ethernet_addr(ethernet_addr)
@ -123,7 +150,7 @@ fn main() {
loop { loop {
let timestamp = Instant::now(); let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) { match iface.poll(&mut sockets, timestamp) {
Ok(_) => {}, Ok(_) => {}
Err(e) => { Err(e) => {
debug!("poll error: {}", e); debug!("poll error: {}", e);
} }
@ -137,25 +164,40 @@ fn main() {
send_at = timestamp; send_at = timestamp;
} }
if socket.can_send() && seq_no < count as u16 && if socket.can_send() && seq_no < count as u16 && send_at <= timestamp {
send_at <= timestamp {
NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis()); NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis());
match remote_addr { match remote_addr {
IpAddress::Ipv4(_) => { IpAddress::Ipv4(_) => {
let (icmp_repr, mut icmp_packet) = send_icmp_ping!( let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
Icmpv4Repr, Icmpv4Packet, ident, seq_no, Icmpv4Repr,
echo_payload, socket, remote_addr); Icmpv4Packet,
ident,
seq_no,
echo_payload,
socket,
remote_addr
);
icmp_repr.emit(&mut icmp_packet, &device_caps.checksum); icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);
}, }
IpAddress::Ipv6(_) => { IpAddress::Ipv6(_) => {
let (icmp_repr, mut icmp_packet) = send_icmp_ping!( let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
Icmpv6Repr, Icmpv6Packet, ident, seq_no, Icmpv6Repr,
echo_payload, socket, remote_addr); Icmpv6Packet,
icmp_repr.emit(&src_ipv6, &remote_addr, ident,
&mut icmp_packet, &device_caps.checksum); seq_no,
}, echo_payload,
_ => unimplemented!() socket,
remote_addr
);
icmp_repr.emit(
&src_ipv6,
&remote_addr,
&mut icmp_packet,
&device_caps.checksum,
);
}
_ => unimplemented!(),
} }
waiting_queue.insert(seq_no, timestamp); waiting_queue.insert(seq_no, timestamp);
@ -171,17 +213,36 @@ fn main() {
let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap(); let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap();
let icmp_repr = let icmp_repr =
Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap(); Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();
get_icmp_pong!(Icmpv4Repr, icmp_repr, payload, get_icmp_pong!(
waiting_queue, remote_addr, timestamp, received); Icmpv4Repr,
icmp_repr,
payload,
waiting_queue,
remote_addr,
timestamp,
received
);
} }
IpAddress::Ipv6(_) => { IpAddress::Ipv6(_) => {
let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap(); let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap();
let icmp_repr = Icmpv6Repr::parse(&remote_addr, &src_ipv6, let icmp_repr = Icmpv6Repr::parse(
&icmp_packet, &device_caps.checksum).unwrap(); &remote_addr,
get_icmp_pong!(Icmpv6Repr, icmp_repr, payload, &src_ipv6,
waiting_queue, remote_addr, timestamp, received); &icmp_packet,
}, &device_caps.checksum,
_ => unimplemented!() )
.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() { 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 => { Some(poll_at) if timestamp < poll_at => {
let resume_at = cmp::min(poll_at, send_at); let resume_at = cmp::min(poll_at, send_at);
phy_wait(fd, Some(resume_at - timestamp)).expect("wait error"); phy_wait(fd, Some(resume_at - timestamp)).expect("wait error");
}, }
Some(_) => (), Some(_) => (),
None => { None => {
phy_wait(fd, Some(send_at - timestamp)).expect("wait error"); phy_wait(fd, Some(send_at - timestamp)).expect("wait error");
@ -213,6 +274,10 @@ fn main() {
} }
println!("--- {} ping statistics ---", remote_addr); println!("--- {} ping statistics ---", remote_addr);
println!("{} packets transmitted, {} received, {:.0}% packet loss", println!(
seq_no, received, 100.0 * (seq_no - received) as f64 / seq_no as f64); "{} 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; mod utils;
use std::str; use log::debug;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Write; use std::fmt::Write;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use log::debug; use std::str;
use smoltcp::phy::{Device, Medium, wait as phy_wait}; use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
use smoltcp::socket::SocketSet; use smoltcp::socket::SocketSet;
use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
use smoltcp::time::{Duration, Instant}; use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
fn main() { fn main() {
utils::setup_logging(""); utils::setup_logging("");
@ -24,7 +24,7 @@ fn main() {
let mut matches = utils::parse_options(&opts, free); let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches); let device = utils::parse_tuntap_options(&mut matches);
let fd = device.as_raw_fd(); 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 neighbor_cache = NeighborCache::new(BTreeMap::new());
@ -52,12 +52,11 @@ fn main() {
let ip_addrs = [ let ip_addrs = [
IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24), 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(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 medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device) let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
.ip_addrs(ip_addrs);
if medium == Medium::Ethernet { if medium == Medium::Ethernet {
builder = builder builder = builder
.ethernet_addr(ethernet_addr) .ethernet_addr(ethernet_addr)
@ -66,7 +65,7 @@ fn main() {
let mut iface = builder.finalize(); let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]); 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 tcp1_handle = sockets.add(tcp1_socket);
let tcp2_handle = sockets.add(tcp2_socket); let tcp2_handle = sockets.add(tcp2_socket);
let tcp3_handle = sockets.add(tcp3_socket); let tcp3_handle = sockets.add(tcp3_socket);
@ -76,7 +75,7 @@ fn main() {
loop { loop {
let timestamp = Instant::now(); let timestamp = Instant::now();
match iface.poll(&mut sockets, timestamp) { match iface.poll(&mut sockets, timestamp) {
Ok(_) => {}, Ok(_) => {}
Err(e) => { Err(e) => {
debug!("poll error: {}", e); debug!("poll error: {}", e);
} }
@ -91,16 +90,21 @@ fn main() {
let client = match socket.recv() { let client = match socket.recv() {
Ok((data, endpoint)) => { Ok((data, endpoint)) => {
debug!("udp:6969 recv data: {:?} from {}", debug!(
str::from_utf8(data).unwrap(), endpoint); "udp:6969 recv data: {:?} from {}",
str::from_utf8(data).unwrap(),
endpoint
);
Some(endpoint) Some(endpoint)
} }
Err(_) => None Err(_) => None,
}; };
if let Some(endpoint) = client { if let Some(endpoint) = client {
let data = b"hello\n"; let data = b"hello\n";
debug!("udp:6969 send data: {:?}", debug!(
str::from_utf8(data.as_ref()).unwrap()); "udp:6969 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap()
);
socket.send_slice(data, endpoint).unwrap(); socket.send_slice(data, endpoint).unwrap();
} }
} }
@ -135,21 +139,27 @@ fn main() {
tcp_6970_active = socket.is_active(); tcp_6970_active = socket.is_active();
if socket.may_recv() { if socket.may_recv() {
let data = socket.recv(|buffer| { let data = socket
let recvd_len = buffer.len(); .recv(|buffer| {
let mut data = buffer.to_owned(); let recvd_len = buffer.len();
if !data.is_empty() { let mut data = buffer.to_owned();
debug!("tcp:6970 recv data: {:?}", if !data.is_empty() {
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); debug!(
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat(); "tcp:6970 recv data: {:?}",
data.reverse(); str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
data.extend(b"\n"); );
} data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
(recvd_len, data) data.reverse();
}).unwrap(); data.extend(b"\n");
}
(recvd_len, data)
})
.unwrap();
if socket.can_send() && !data.is_empty() { if socket.can_send() && !data.is_empty() {
debug!("tcp:6970 send data: {:?}", debug!(
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")); "tcp:6970 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
socket.send_slice(&data[..]).unwrap(); socket.send_slice(&data[..]).unwrap();
} }
} else if socket.may_send() { } else if socket.may_send() {
@ -168,12 +178,14 @@ fn main() {
} }
if socket.may_recv() { if socket.may_recv() {
socket.recv(|buffer| { socket
if !buffer.is_empty() { .recv(|buffer| {
debug!("tcp:6971 recv {:?} octets", buffer.len()); if !buffer.is_empty() {
} debug!("tcp:6971 recv {:?} octets", buffer.len());
(buffer.len(), ()) }
}).unwrap(); (buffer.len(), ())
})
.unwrap();
} else if socket.may_send() { } else if socket.may_send() {
socket.close(); socket.close();
} }
@ -187,15 +199,17 @@ fn main() {
} }
if socket.may_send() { if socket.may_send() {
socket.send(|data| { socket
if !data.is_empty() { .send(|data| {
debug!("tcp:6972 send {:?} octets", data.len()); if !data.is_empty() {
for (i, b) in data.iter_mut().enumerate() { debug!("tcp:6972 send {:?} octets", data.len());
*b = (i % 256) as u8; for (i, b) in data.iter_mut().enumerate() {
*b = (i % 256) as u8;
}
} }
} (data.len(), ())
(data.len(), ()) })
}).unwrap(); .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::env;
use std::os::unix::io::AsRawFd; 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() { fn main() {
let ifname = env::args().nth(1).unwrap(); let ifname = env::args().nth(1).unwrap();
@ -11,9 +11,14 @@ fn main() {
loop { loop {
phy_wait(socket.as_raw_fd(), None).unwrap(); phy_wait(socket.as_raw_fd(), None).unwrap();
let (rx_token, _) = socket.receive().unwrap(); let (rx_token, _) = socket.receive().unwrap();
rx_token.consume(Instant::now(), |buffer| { rx_token
println!("{}", PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer)); .consume(Instant::now(), |buffer| {
Ok(()) println!(
}).unwrap(); "{}",
PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer)
);
Ok(())
})
.unwrap();
} }
} }

View File

@ -1,43 +1,59 @@
#![allow(dead_code)] #![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")] #[cfg(feature = "log")]
use env_logger::Builder; 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")] #[cfg(feature = "phy-tuntap_interface")]
use smoltcp::phy::TunTapInterface; use smoltcp::phy::TunTapInterface;
use smoltcp::phy::{PcapWriter, PcapSink, PcapMode}; use smoltcp::phy::{Device, FaultInjector, Medium, Tracer};
use smoltcp::phy::RawSocket; use smoltcp::phy::{PcapMode, PcapSink, PcapWriter};
use smoltcp::time::{Duration, Instant}; use smoltcp::time::{Duration, Instant};
#[cfg(feature = "log")] #[cfg(feature = "log")]
pub fn setup_logging_with_clock<F>(filter: &str, since_startup: F) 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() Builder::new()
.format(move |buf, record| { .format(move |buf, record| {
let elapsed = since_startup(); let elapsed = since_startup();
let timestamp = format!("[{}]", elapsed); let timestamp = format!("[{}]", elapsed);
if record.target().starts_with("smoltcp::") { if record.target().starts_with("smoltcp::") {
writeln!(buf, "\x1b[0m{} ({}): {}\x1b[0m", timestamp, writeln!(
record.target().replace("smoltcp::", ""), record.args()) buf,
"\x1b[0m{} ({}): {}\x1b[0m",
timestamp,
record.target().replace("smoltcp::", ""),
record.args()
)
} else if record.level() == Level::Trace { } else if record.level() == Level::Trace {
let message = format!("{}", record.args()); let message = format!("{}", record.args());
writeln!(buf, "\x1b[37m{} {}\x1b[0m", timestamp, writeln!(
message.replace("\n", "\n ")) buf,
"\x1b[37m{} {}\x1b[0m",
timestamp,
message.replace("\n", "\n ")
)
} else { } else {
writeln!(buf, "\x1b[32m{} ({}): {}\x1b[0m", timestamp, writeln!(
record.target(), record.args()) buf,
"\x1b[32m{} ({}): {}\x1b[0m",
timestamp,
record.target(),
record.args()
)
} }
}) })
.filter(None, LevelFilter::Trace) .filter(None, LevelFilter::Trace)
@ -48,9 +64,7 @@ pub fn setup_logging_with_clock<F>(filter: &str, since_startup: F)
#[cfg(feature = "log")] #[cfg(feature = "log")]
pub fn setup_logging(filter: &str) { pub fn setup_logging(filter: &str) {
setup_logging_with_clock(filter, move || { setup_logging_with_clock(filter, Instant::now)
Instant::now()
})
} }
pub fn create_options() -> (Options, Vec<&'static str>) { pub fn create_options() -> (Options, Vec<&'static str>) {
@ -67,10 +81,17 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches {
} }
Ok(matches) => { Ok(matches) => {
if matches.opt_present("h") || matches.free.len() != free.len() { if matches.opt_present("h") || matches.free.len() != free.len() {
let brief = format!("Usage: {} [OPTION]... {}", let brief = format!(
env::args().next().unwrap(), free.join(" ")); "Usage: {} [OPTION]... {}",
env::args().next().unwrap(),
free.join(" ")
);
print!("{}", options.usage(&brief)); 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 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 { pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface {
let tun = matches.opt_str("tun"); let tun = matches.opt_str("tun");
let tap = matches.opt_str("tap"); let tap = matches.opt_str("tap");
match(tun,tap) { match (tun, tap) {
(Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(), (Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(),
(None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(), (None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(),
_ => panic!("You must specify exactly one of --tun or --tap"), _ => 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>) { pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) {
opts.optopt("", "pcap", "Write a packet capture file", "FILE"); opts.optopt("", "pcap", "Write a packet capture file", "FILE");
opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE"); opts.optopt(
opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE"); "",
opts.optopt("", "size-limit", "Drop packets larger than given size (octets)", "SIZE"); "drop-chance",
opts.optopt("", "tx-rate-limit", "Drop packets after transmit rate exceeds given limit \ "Chance of dropping a packet (%)",
(packets per interval)", "RATE"); "CHANCE",
opts.optopt("", "rx-rate-limit", "Drop packets after transmit rate exceeds given limit \ );
(packets per interval)", "RATE"); opts.optopt(
opts.optopt("", "shaping-interval", "Sets the interval for rate limiting (ms)", "RATE"); "",
"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) pub fn parse_middleware_options<D>(
-> FaultInjector<Tracer<PcapWriter<D, Rc<dyn PcapSink>>>> matches: &mut Matches,
where D: for<'a> Device<'a> 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()) let drop_chance = matches
.unwrap_or(0); .opt_str("drop-chance")
let corrupt_chance = matches.opt_str("corrupt-chance").map(|s| u8::from_str(&s).unwrap()) .map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0); .unwrap_or(0);
let size_limit = matches.opt_str("size-limit").map(|s| usize::from_str(&s).unwrap()) let corrupt_chance = matches
.unwrap_or(0); .opt_str("corrupt-chance")
let tx_rate_limit = matches.opt_str("tx-rate-limit").map(|s| u64::from_str(&s).unwrap()) .map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0); .unwrap_or(0);
let rx_rate_limit = matches.opt_str("rx-rate-limit").map(|s| u64::from_str(&s).unwrap()) let size_limit = matches
.unwrap_or(0); .opt_str("size-limit")
let shaping_interval = matches.opt_str("shaping-interval").map(|s| u64::from_str(&s).unwrap()) .map(|s| usize::from_str(&s).unwrap())
.unwrap_or(0); .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>; let pcap_writer: Box<dyn io::Write>;
if let Some(pcap_filename) = matches.opt_str("pcap") { 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()) 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( let device = PcapWriter::new(
device, device,
Rc::new(RefCell::new(pcap_writer)) as Rc<dyn PcapSink>, 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| { 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. 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")] #[cfg(feature = "medium-ethernet")]
mod neighbor; mod neighbor;
mod route; 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")] #[cfg(feature = "medium-ethernet")]
pub(crate) use self::neighbor::Answer as NeighborAnswer; pub(crate) use self::neighbor::Answer as NeighborAnswer;
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
pub use self::neighbor::Cache as NeighborCache; pub use self::neighbor::Cache as NeighborCache;
#[cfg(feature = "medium-ethernet")]
pub use self::neighbor::Neighbor;
pub use self::route::{Route, Routes}; pub use self::route::{Route, Routes};
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))] #[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]

View File

@ -3,8 +3,8 @@
use managed::ManagedMap; use managed::ManagedMap;
use crate::wire::{EthernetAddress, IpAddress};
use crate::time::{Duration, Instant}; use crate::time::{Duration, Instant};
use crate::wire::{EthernetAddress, IpAddress};
/// A cached neighbor. /// A cached neighbor.
/// ///
@ -14,7 +14,7 @@ use crate::time::{Duration, Instant};
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Neighbor { pub struct Neighbor {
hardware_addr: EthernetAddress, hardware_addr: EthernetAddress,
expires_at: Instant, expires_at: Instant,
} }
/// An answer to a neighbor cache lookup. /// An answer to a neighbor cache lookup.
@ -27,7 +27,7 @@ pub(crate) enum Answer {
NotFound, NotFound,
/// The neighbor address is not in the cache, or has expired, /// The neighbor address is not in the cache, or has expired,
/// and a lookup has been made recently. /// and a lookup has been made recently.
RateLimited RateLimited,
} }
impl Answer { impl Answer {
@ -61,10 +61,9 @@ impl Answer {
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct Cache<'a> { pub struct Cache<'a> {
storage: ManagedMap<'a, IpAddress, Neighbor>, storage: ManagedMap<'a, IpAddress, Neighbor>,
silent_until: Instant, silent_until: Instant,
gc_threshold: usize gc_threshold: usize,
} }
impl<'a> Cache<'a> { impl<'a> Cache<'a> {
@ -82,21 +81,32 @@ impl<'a> Cache<'a> {
/// # Panics /// # Panics
/// This function panics if `storage.len() == 0`. /// This function panics if `storage.len() == 0`.
pub fn new<T>(storage: T) -> Cache<'a> 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) Cache::new_with_limit(storage, Cache::GC_THRESHOLD)
} }
pub fn new_with_limit<T>(storage: T, gc_threshold: usize) -> Cache<'a> 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(); let mut storage = storage.into();
storage.clear(); 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, pub fn fill(
timestamp: Instant) { &mut self,
protocol_addr: IpAddress,
hardware_addr: EthernetAddress,
timestamp: Instant,
) {
debug_assert!(protocol_addr.is_unicast()); debug_assert!(protocol_addr.is_unicast());
debug_assert!(hardware_addr.is_unicast()); debug_assert!(hardware_addr.is_unicast());
@ -104,11 +114,12 @@ impl<'a> Cache<'a> {
let current_storage_size = self.storage.len(); let current_storage_size = self.storage.len();
match self.storage { match self.storage {
ManagedMap::Borrowed(_) => (), ManagedMap::Borrowed(_) => (),
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
ManagedMap::Owned(ref mut map) => { ManagedMap::Owned(ref mut map) => {
if current_storage_size >= self.gc_threshold { 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)) .map(|(key, value)| (*key, *value))
.filter(|(_, v)| timestamp < v.expires_at) .filter(|(_, v)| timestamp < v.expires_at)
.collect(); .collect();
@ -118,13 +129,18 @@ impl<'a> Cache<'a> {
} }
}; };
let neighbor = Neighbor { 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) { match self.storage.insert(protocol_addr, neighbor) {
Ok(Some(old_neighbor)) => { Ok(Some(old_neighbor)) => {
if old_neighbor.hardware_addr != hardware_addr { if old_neighbor.hardware_addr != hardware_addr {
net_trace!("replaced {} => {} (was {})", net_trace!(
protocol_addr, hardware_addr, old_neighbor.hardware_addr); "replaced {} => {} (was {})",
protocol_addr,
hardware_addr,
old_neighbor.hardware_addr
);
} }
} }
Ok(None) => { Ok(None) => {
@ -147,21 +163,23 @@ impl<'a> Cache<'a> {
} }
// Owned maps can extend themselves. // Owned maps can extend themselves.
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
ManagedMap::Owned(_) => unreachable!() ManagedMap::Owned(_) => unreachable!(),
}; };
let _old_neighbor = let _old_neighbor = self.storage.remove(&old_protocol_addr).unwrap();
self.storage.remove(&old_protocol_addr).unwrap();
match self.storage.insert(protocol_addr, neighbor) { match self.storage.insert(protocol_addr, neighbor) {
Ok(None) => { Ok(None) => {
net_trace!("filled {} => {} (evicted {} => {})", net_trace!(
protocol_addr, hardware_addr, "filled {} => {} (evicted {} => {})",
old_protocol_addr, _old_neighbor.hardware_addr); protocol_addr,
hardware_addr,
old_protocol_addr,
_old_neighbor.hardware_addr
);
} }
// We've covered everything else above. // We've covered everything else above.
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
} }
@ -171,10 +189,13 @@ impl<'a> Cache<'a> {
return Answer::Found(EthernetAddress::BROADCAST); return Answer::Found(EthernetAddress::BROADCAST);
} }
if let Some(&Neighbor { expires_at, hardware_addr }) = if let Some(&Neighbor {
self.storage.get(protocol_addr) { expires_at,
hardware_addr,
}) = self.storage.get(protocol_addr)
{
if timestamp < expires_at { if timestamp < expires_at {
return Answer::Found(hardware_addr) return Answer::Found(hardware_addr);
} }
} }
@ -193,9 +214,8 @@ impl<'a> Cache<'a> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; 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 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_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]); 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_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]); let mut cache = Cache::new(&mut cache_storage[..]);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found(), false); assert_eq!(
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false); 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)); 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!(
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false); cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(), Answer::Found(HADDR_A)
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) + Cache::ENTRY_LIFETIME * 2
)
.found(),
false
);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); 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] #[test]
@ -226,9 +276,19 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]); let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); 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!(
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(), cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
false); Answer::Found(HADDR_A)
);
assert_eq!(
cache
.lookup(
&MOCK_IP_ADDR_1,
Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
)
.found(),
false
);
} }
#[test] #[test]
@ -237,9 +297,15 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]); let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0)); 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)); 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] #[test]
@ -249,10 +315,20 @@ mod test {
cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50)); cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
// Adding third item after the expiration of the previous // Adding third item after the expiration of the previous
// two should garbage collect // 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.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] #[test]
@ -263,12 +339,28 @@ mod test {
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100)); 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_2, HADDR_B, Instant::from_millis(50));
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200)); 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!(
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found(), false); 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)); 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!(
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D)); 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] #[test]
@ -276,10 +368,19 @@ mod test {
let mut cache_storage = [Default::default(); 3]; let mut cache_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]); 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)); cache.limit_rate(Instant::from_millis(0));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), Answer::RateLimited); assert_eq!(
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), Answer::NotFound); 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 crate::time::Instant;
use core::ops::Bound; use core::ops::Bound;
use managed::ManagedMap;
use crate::{Error, Result}; use crate::wire::{IpAddress, IpCidr};
use crate::wire::{IpCidr, IpAddress};
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Address, Ipv4Cidr}; use crate::wire::{Ipv4Address, Ipv4Cidr};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Address, Ipv6Cidr}; use crate::wire::{Ipv6Address, Ipv6Cidr};
use crate::{Error, Result};
/// A prefix of addresses that should be routed via a router /// A prefix of addresses that should be routed via a router
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -70,7 +70,9 @@ impl<'a> Routes<'a> {
/// Creates a routing tables. The backing storage is **not** cleared /// Creates a routing tables. The backing storage is **not** cleared
/// upon creation. /// upon creation.
pub fn new<T>(storage: T) -> Routes<'a> 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(); let storage = storage.into();
Routes { storage } Routes { storage }
} }
@ -89,7 +91,7 @@ impl<'a> Routes<'a> {
let route = Route::new_ipv4_gateway(gateway); let route = Route::new_ipv4_gateway(gateway);
match self.storage.insert(cidr, route) { match self.storage.insert(cidr, route) {
Ok(route) => Ok(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); let route = Route::new_ipv6_gateway(gateway);
match self.storage.insert(cidr, route) { match self.storage.insert(cidr, route) {
Ok(route) => Ok(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) self.storage.remove(&cidr)
} }
pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
Option<IpAddress> {
assert!(addr.is_unicast()); assert!(addr.is_unicast());
let cidr = match addr { let cidr = match addr {
@ -133,10 +134,14 @@ impl<'a> Routes<'a> {
IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)), IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)),
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)), 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 // TODO: do something with route.preferred_until
if let Some(expires_at) = route.expires_at { if let Some(expires_at) = route.expires_at {
if timestamp > expires_at { if timestamp > expires_at {
@ -159,24 +164,28 @@ mod test {
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
mod mock { mod mock {
use super::super::*; use super::super::*;
pub const ADDR_1A: Ipv6Address = Ipv6Address( pub const ADDR_1A: Ipv6Address =
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]); Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_1B: Ipv6Address = Ipv6Address( pub const ADDR_1B: Ipv6Address =
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]); Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
pub const ADDR_1C: Ipv6Address = Ipv6Address( pub const ADDR_1C: Ipv6Address =
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]); Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
pub fn cidr_1() -> Ipv6Cidr { pub fn cidr_1() -> Ipv6Cidr {
Ipv6Cidr::new(Ipv6Address( Ipv6Cidr::new(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]), 64) Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]),
64,
)
} }
pub const ADDR_2A: Ipv6Address = Ipv6Address( pub const ADDR_2A: Ipv6Address =
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]); Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_2B: Ipv6Address = Ipv6Address( pub const ADDR_2B: Ipv6Address =
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]); Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
pub fn cidr_2() -> Ipv6Cidr { pub fn cidr_2() -> Ipv6Cidr {
Ipv6Cidr::new(Ipv6Address( Ipv6Cidr::new(
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]), 64) 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_storage = [None, None, None];
let mut routes = Routes::new(&mut routes_storage[..]); let mut routes = Routes::new(&mut routes_storage[..]);
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), None); assert_eq!(
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), None); routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), None); 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_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 { let route = Route {
via_router: ADDR_1A.into(), via_router: ADDR_1A.into(),
preferred_until: None, expires_at: None, preferred_until: None,
expires_at: None,
}; };
routes.update(|storage| { routes.update(|storage| {
storage.insert(cidr_1().into(), route).unwrap(); storage.insert(cidr_1().into(), route).unwrap();
}); });
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); assert_eq!(
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); 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_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 { let route2 = Route {
via_router: ADDR_2A.into(), via_router: ADDR_2A.into(),
@ -233,16 +273,46 @@ mod test {
storage.insert(cidr_2().into(), route2).unwrap(); storage.insert(cidr_2().into(), route2).unwrap();
}); });
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); assert_eq!(
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into())); 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_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!(
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)), Some(ADDR_1A.into())); routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)),
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)), Some(ADDR_1A.into())); 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_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)] #![cfg_attr(not(any(test, feature = "std")), no_std)]
#![deny(unsafe_code)] #![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 //! 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 //! 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( #[cfg(all(
feature = "socket", feature = "socket",
not(any( not(any(feature = "medium-ethernet", feature = "medium-ip",))
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"); 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 macros;
mod parsers; mod parsers;
pub mod storage;
pub mod phy;
pub mod wire;
pub mod iface; pub mod iface;
pub mod phy;
#[cfg(feature = "socket")] #[cfg(feature = "socket")]
pub mod socket; pub mod socket;
pub mod storage;
pub mod time; pub mod time;
pub mod wire;
/// The error type for the networking stack. /// The error type for the networking stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -178,16 +181,16 @@ pub type Result<T> = core::result::Result<T, Error>;
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Error::Exhausted => write!(f, "buffer space exhausted"), Error::Exhausted => write!(f, "buffer space exhausted"),
Error::Illegal => write!(f, "illegal operation"), Error::Illegal => write!(f, "illegal operation"),
Error::Unaddressable => write!(f, "unaddressable destination"), Error::Unaddressable => write!(f, "unaddressable destination"),
Error::Finished => write!(f, "operation finished"), Error::Finished => write!(f, "operation finished"),
Error::Truncated => write!(f, "truncated packet"), Error::Truncated => write!(f, "truncated packet"),
Error::Checksum => write!(f, "checksum error"), Error::Checksum => write!(f, "checksum error"),
Error::Unrecognized => write!(f, "unrecognized packet"), Error::Unrecognized => write!(f, "unrecognized packet"),
Error::Fragmented => write!(f, "fragmented packet"), Error::Fragmented => write!(f, "fragmented packet"),
Error::Malformed => write!(f, "malformed packet"), Error::Malformed => write!(f, "malformed packet"),
Error::Dropped => write!(f, "dropped by socket"), Error::Dropped => write!(f, "dropped by socket"),
} }
} }
} }

View File

@ -1,4 +1,3 @@
#[cfg(feature = "log")] #[cfg(feature = "log")]
macro_rules! net_log { macro_rules! net_log {
(trace, $($arg:expr),*) => { log::trace!($($arg),*); }; (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::result;
use core::str::FromStr;
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
use crate::wire::EthernetAddress; use crate::wire::EthernetAddress;
@ -15,14 +18,14 @@ type Result<T> = result::Result<T, ()>;
struct Parser<'a> { struct Parser<'a> {
data: &'a [u8], data: &'a [u8],
pos: usize pos: usize,
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
fn new(data: &'a str) -> Parser<'a> { fn new(data: &'a str) -> Parser<'a> {
Parser { Parser {
data: data.as_bytes(), data: data.as_bytes(),
pos: 0 pos: 0,
} }
} }
@ -40,12 +43,14 @@ impl<'a> Parser<'a> {
self.pos += 1; self.pos += 1;
Ok(chr) Ok(chr)
} }
None => Err(()) None => Err(()),
} }
} }
fn try_do<F, T>(&mut self, f: F) -> Option<T> 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; let pos = self.pos;
match f(self) { match f(self) {
Ok(res) => Some(res), Ok(res) => Some(res),
@ -65,7 +70,9 @@ impl<'a> Parser<'a> {
} }
fn until_eof<F, T>(&mut self, f: F) -> Result<T> 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)?; let res = f(self)?;
self.accept_eof()?; self.accept_eof()?;
Ok(res) Ok(res)
@ -99,8 +106,7 @@ impl<'a> Parser<'a> {
} }
} }
fn accept_number(&mut self, max_digits: usize, max_value: u32, fn accept_number(&mut self, max_digits: usize, max_value: u32, hex: bool) -> Result<u32> {
hex: bool) -> Result<u32> {
let mut value = self.accept_digit(hex)? as u32; let mut value = self.accept_digit(hex)? as u32;
for _ in 1..max_digits { for _ in 1..max_digits {
match self.try_do(|p| p.accept_digit(hex)) { match self.try_do(|p| p.accept_digit(hex)) {
@ -108,7 +114,7 @@ impl<'a> Parser<'a> {
value *= if hex { 16 } else { 10 }; value *= if hex { 16 } else { 10 };
value += digit as u32; value += digit as u32;
} }
None => break None => break,
} }
} }
if value < max_value { if value < max_value {
@ -133,10 +139,10 @@ impl<'a> Parser<'a> {
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
fn accept_mac(&mut self) -> Result<EthernetAddress> { fn accept_mac(&mut self) -> Result<EthernetAddress> {
if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) { 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':')) { if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b':')) {
return Ok(mac) return Ok(mac);
} }
Err(()) Err(())
} }
@ -154,9 +160,13 @@ impl<'a> Parser<'a> {
} }
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
fn accept_ipv6_part(&mut self, (head, tail): (&mut [u16; 8], &mut [u16; 6]), fn accept_ipv6_part(
(head_idx, tail_idx): (&mut usize, &mut usize), &mut self,
mut use_tail: bool, is_cidr: bool) -> Result<()> { (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"::")) { let double_colon = match self.try_do(|p| p.accept_str(b"::")) {
Some(_) if !use_tail && *head_idx < 7 => { Some(_) if !use_tail && *head_idx < 7 => {
// Found a double colon. Start filling out the // Found a double colon. Start filling out the
@ -164,7 +174,7 @@ impl<'a> Parser<'a> {
// this is the last character we can parse. // this is the last character we can parse.
use_tail = true; use_tail = true;
true true
}, }
Some(_) => { Some(_) => {
// This is a bad address. Only one double colon is // This is a bad address. Only one double colon is
// allowed and an address is only 128 bits. // allowed and an address is only 128 bits.
@ -193,21 +203,20 @@ impl<'a> Parser<'a> {
}); });
} }
Ok(()) Ok(())
}, }
Some(part) if *tail_idx < 6 => { Some(part) if *tail_idx < 6 => {
// Valid u16 to be added to the address // Valid u16 to be added to the address
tail[*tail_idx] = part as u16; tail[*tail_idx] = part as u16;
*tail_idx += 1; *tail_idx += 1;
if *tail_idx == 1 && tail[0] == 0xffff if *tail_idx == 1 && tail[0] == 0xffff && head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
&& head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
self.try_do(|p| { self.try_do(|p| {
p.accept_char(b':')?; p.accept_char(b':')?;
p.accept_ipv4_mapped_ipv6_part(tail, tail_idx) p.accept_ipv4_mapped_ipv6_part(tail, tail_idx)
}); });
} }
Ok(()) Ok(())
}, }
Some(_) => { Some(_) => {
// Tail or head section is too long // Tail or head section is too long
Err(()) Err(())
@ -259,7 +268,12 @@ impl<'a> Parser<'a> {
let (mut addr, mut tail) = ([0u16; 8], [0u16; 6]); let (mut addr, mut tail) = ([0u16; 8], [0u16; 6]);
let (mut head_idx, mut tail_idx) = (0, 0); 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 // We need to copy the tail portion (the portion following the "::") to the
// end of the address. // end of the address.
@ -290,14 +304,14 @@ impl<'a> Parser<'a> {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv4()) { match self.try_do(|p| p.accept_ipv4()) {
Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)), Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)),
None => () None => (),
} }
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv6(false)) { match self.try_do(|p| p.accept_ipv6(false)) {
Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)), Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)),
None => () None => (),
} }
Err(()) Err(())
@ -314,7 +328,10 @@ impl<'a> Parser<'a> {
self.accept_number(5, 65535, false)? 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")] #[cfg(feature = "proto-ipv6")]
@ -326,10 +343,16 @@ impl<'a> Parser<'a> {
self.accept_char(b':')?; self.accept_char(b':')?;
let port = self.accept_number(5, 65535, false)?; 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 { } else {
let ip = self.accept_ipv6(false)?; 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)] #[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv4_endpoint()) { match self.try_do(|p| p.accept_ipv4_endpoint()) {
Some(ipv4) => return Ok(ipv4), Some(ipv4) => return Ok(ipv4),
None => () None => (),
} }
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv6_endpoint()) { match self.try_do(|p| p.accept_ipv6_endpoint()) {
Some(ipv6) => return Ok(ipv6), Some(ipv6) => return Ok(ipv6),
None => () None => (),
} }
Err(()) Err(())
@ -431,14 +454,14 @@ impl FromStr for IpCidr {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match Ipv4Cidr::from_str(s) { match Ipv4Cidr::from_str(s) {
Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)), Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)),
Err(_) => () Err(_) => (),
} }
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
match Ipv6Cidr::from_str(s) { match Ipv6Cidr::from_str(s) {
Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)), Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)),
Err(_) => () Err(_) => (),
} }
Err(()) Err(())
@ -449,7 +472,7 @@ impl FromStr for IpEndpoint {
type Err = (); type Err = ();
fn from_str(s: &str) -> Result<IpEndpoint> { fn from_str(s: &str) -> Result<IpEndpoint> {
Parser::new(s).until_eof(|p| Ok(p.accept_ip_endpoint()?)) Parser::new(s).until_eof(|p| p.accept_ip_endpoint())
} }
} }
@ -465,29 +488,40 @@ mod test {
if let Ok(cidr) = cidr { if let Ok(cidr) = cidr {
assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr)); assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr));
assert_eq!(IpCidr::from_str(&format!("{}", cidr)), assert_eq!(IpCidr::from_str(&format!("{}", cidr)), Ok($variant(cidr)));
Ok($variant(cidr)));
} }
} }
} };
} }
#[test] #[test]
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
fn test_mac() { fn test_mac() {
assert_eq!(EthernetAddress::from_str(""), Err(())); assert_eq!(EthernetAddress::from_str(""), Err(()));
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"), assert_eq!(
Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]))); EthernetAddress::from_str("02:00:00:00:00:00"),
assert_eq!(EthernetAddress::from_str("01:23:45:67:89:ab"), Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]))
Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab]))); );
assert_eq!(EthernetAddress::from_str("cd:ef:10:00:00:00"), assert_eq!(
Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00]))); EthernetAddress::from_str("01:23:45:67:89:ab"),
assert_eq!(EthernetAddress::from_str("00:00:00:ab:cd:ef"), Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab]))
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))); );
assert_eq!(EthernetAddress::from_str("00-00-00-ab-cd-ef"), assert_eq!(
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))); EthernetAddress::from_str("cd:ef:10:00:00:00"),
assert_eq!(EthernetAddress::from_str("AB-CD-EF-00-00-00"), Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00]))
Ok(EthernetAddress([0xab, 0xcd, 0xef, 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("100:00:00:00:00:00"), Err(()));
assert_eq!(EthernetAddress::from_str("002: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(())); assert_eq!(EthernetAddress::from_str("02:00:00:00:00:000"), Err(()));
@ -498,10 +532,14 @@ mod test {
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
fn test_ipv4() { fn test_ipv4() {
assert_eq!(Ipv4Address::from_str(""), Err(())); assert_eq!(Ipv4Address::from_str(""), Err(()));
assert_eq!(Ipv4Address::from_str("1.2.3.4"), assert_eq!(
Ok(Ipv4Address([1, 2, 3, 4]))); Ipv4Address::from_str("1.2.3.4"),
assert_eq!(Ipv4Address::from_str("001.2.3.4"), Ok(Ipv4Address([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("0001.2.3.4"), Err(()));
assert_eq!(Ipv4Address::from_str("999.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(())); assert_eq!(Ipv4Address::from_str("1.2.3.4.5"), Err(()));
@ -515,73 +553,87 @@ mod test {
fn test_ipv6() { fn test_ipv6() {
// Obviously not valid // Obviously not valid
assert_eq!(Ipv6Address::from_str(""), Err(())); assert_eq!(Ipv6Address::from_str(""), Err(()));
assert_eq!(Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"), assert_eq!(
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))); Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
assert_eq!(Ipv6Address::from_str("::1"), Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
Ok(Ipv6Address::LOOPBACK)); );
assert_eq!(Ipv6Address::from_str("::"), assert_eq!(Ipv6Address::from_str("::1"), Ok(Ipv6Address::LOOPBACK));
Ok(Ipv6Address::UNSPECIFIED)); assert_eq!(Ipv6Address::from_str("::"), Ok(Ipv6Address::UNSPECIFIED));
assert_eq!(Ipv6Address::from_str("fe80::1"), assert_eq!(
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))); Ipv6Address::from_str("fe80::1"),
assert_eq!(Ipv6Address::from_str("1234:5678::"), Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0))); );
assert_eq!(Ipv6Address::from_str("1234:5678::8765:4321"), assert_eq!(
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321))); 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 // Two double colons in address
assert_eq!(Ipv6Address::from_str("1234:5678::1::1"), assert_eq!(Ipv6Address::from_str("1234:5678::1::1"), Err(()));
Err(())); assert_eq!(
assert_eq!(Ipv6Address::from_str("4444:333:22:1::4"), Ipv6Address::from_str("4444:333:22:1::4"),
Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 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!(
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1"), Ipv6Address::from_str("1:1:1:1:1:1::"),
Ok(Ipv6Address::new(0, 0, 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:1"), );
Err(())); 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 // Double colon appears too late indicating an address that is too long
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"), assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"), Err(()));
Err(()));
// Section after double colon is too long for a valid address // Section after double colon is too long for a valid address
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
Err(()));
// Obviously too long // Obviously too long
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"), assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"), Err(()));
Err(()));
// Address is too short // Address is too short
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"), assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"), Err(()));
Err(()));
// Long number // Long number
assert_eq!(Ipv6Address::from_str("::000001"), assert_eq!(Ipv6Address::from_str("::000001"), Err(()));
Err(()));
// IPv4-Mapped address // IPv4-Mapped address
assert_eq!(Ipv6Address::from_str("::ffff:192.168.1.1"), assert_eq!(
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]))); Ipv6Address::from_str("::ffff:192.168.1.1"),
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"), Ok(Ipv6Address([
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]))); 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("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 // 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"), assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"), Err(()));
Err(()));
// Positions 1-5 must be 0 when IPv4 mapped // Positions 1-5 must be 0 when IPv4 mapped
assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"), assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"), Err(()));
Err(())); assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"), Err(()));
assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"),
Err(()));
// Out of range ipv4 octet // Out of range ipv4 octet
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"), assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"), Err(()));
Err(()));
// Invalid hex in ipv4 octet // Invalid hex in ipv4 octet
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"), assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"), Err(()));
Err(()));
} }
#[test] #[test]
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
fn test_ip_ipv4() { fn test_ip_ipv4() {
assert_eq!(IpAddress::from_str(""), Err(())); assert_eq!(IpAddress::from_str(""), Err(()));
assert_eq!(IpAddress::from_str("1.2.3.4"), assert_eq!(
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4])))); IpAddress::from_str("1.2.3.4"),
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4])))
);
assert_eq!(IpAddress::from_str("x"), Err(())); assert_eq!(IpAddress::from_str("x"), Err(()));
} }
@ -589,8 +641,12 @@ mod test {
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
fn test_ip_ipv6() { fn test_ip_ipv6() {
assert_eq!(IpAddress::from_str(""), Err(())); assert_eq!(IpAddress::from_str(""), Err(()));
assert_eq!(IpAddress::from_str("fe80::1"), assert_eq!(
Ok(IpAddress::Ipv6(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)))); 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(())); assert_eq!(IpAddress::from_str("x"), Err(()));
} }
@ -598,14 +654,22 @@ mod test {
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
fn test_cidr_ipv4() { fn test_cidr_ipv4() {
let tests = [ let tests = [
("127.0.0.1/8", (
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8))), "127.0.0.1/8",
("192.168.1.1/24", Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8)),
Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8))), ),
("8.8.8.8/32", (
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8))), "192.168.1.1/24",
("8.8.8.8/0", Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8)),
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8))), ),
(
"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(())), ("", Err(())),
("1", Err(())), ("1", Err(())),
("127.0.0.1", Err(())), ("127.0.0.1", Err(())),
@ -622,22 +686,32 @@ mod test {
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
fn test_cidr_ipv6() { fn test_cidr_ipv6() {
let tests = [ let tests = [
("fe80::1/64", (
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))), "fe80::1/64",
("fe80::/64", Ok(Ipv6Cidr::new(
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 64u8))), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
("::1/128", 64u8,
Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))), )),
("::/128", ),
Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))), (
("fe80:0:0:0:0:0:0:1/64", "fe80::/64",
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))), Ok(Ipv6Cidr::new(
("fe80:0:0:0:0:0:0:1|64", Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
Err(())), 64u8,
("fe80::|64", )),
Err(())), ),
("fe80::1::/64", ("::1/128", Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
Err(())) ("::/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); 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("x"), Err(()));
assert_eq!( assert_eq!(
IpEndpoint::from_str("127.0.0.1"), 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!( assert_eq!(
IpEndpoint::from_str("127.0.0.1:12345"), 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("x"), Err(()));
assert_eq!( assert_eq!(
IpEndpoint::from_str("fe80::1"), 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!( assert_eq!(
IpEndpoint::from_str("[fe80::1]:12345"), 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 core::cell::RefCell;
use crate::{Error, Result}; use crate::phy::{self, Device, DeviceCapabilities};
use crate::phy::{self, DeviceCapabilities, Device};
use crate::time::{Duration, Instant}; use crate::time::{Duration, Instant};
use crate::{Error, Result};
// We use our own RNG to stay compatible with #![no_std]. // 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. // 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))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct Config { struct Config {
corrupt_pct: u8, corrupt_pct: u8,
drop_pct: u8, drop_pct: u8,
reorder_pct: u8, reorder_pct: u8,
max_size: usize, max_size: usize,
max_tx_rate: u64, max_tx_rate: u64,
max_rx_rate: u64, max_rx_rate: u64,
interval: Duration, interval: Duration,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct State { struct State {
rng_seed: u32, rng_seed: u32,
refilled_at: Instant, refilled_at: Instant,
tx_bucket: u64, tx_bucket: u64,
rx_bucket: u64, rx_bucket: u64,
} }
impl State { impl State {
@ -48,7 +48,7 @@ impl State {
let buffer = buffer.as_mut(); let buffer = buffer.as_mut();
// We introduce a single bitflip, as the most likely, and the hardest to detect, error. // 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 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; buffer[index] ^= bit;
} }
@ -61,7 +61,9 @@ impl State {
} }
fn maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool { 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); self.refill(config, timestamp);
if self.tx_bucket > 0 { if self.tx_bucket > 0 {
@ -73,7 +75,9 @@ impl State {
} }
fn maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool { 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); self.refill(config, timestamp);
if self.rx_bucket > 0 { if self.rx_bucket > 0 {
@ -92,22 +96,22 @@ impl State {
/// or hardware limitations (such as a limited number or size of usable network buffers). /// or hardware limitations (such as a limited number or size of usable network buffers).
#[derive(Debug)] #[derive(Debug)]
pub struct FaultInjector<D: for<'a> Device<'a>> { pub struct FaultInjector<D: for<'a> Device<'a>> {
inner: D, inner: D,
state: RefCell<State>, state: RefCell<State>,
config: Config, config: Config,
} }
impl<D: for<'a> Device<'a>> FaultInjector<D> { impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// Create a fault injector device, using the given random number generator seed. /// Create a fault injector device, using the given random number generator seed.
pub fn new(inner: D, seed: u32) -> FaultInjector<D> { pub fn new(inner: D, seed: u32) -> FaultInjector<D> {
let state = State { let state = State {
rng_seed: seed, rng_seed: seed,
refilled_at: Instant::from_millis(0), refilled_at: Instant::from_millis(0),
tx_bucket: 0, tx_bucket: 0,
rx_bucket: 0, rx_bucket: 0,
}; };
FaultInjector { FaultInjector {
inner: inner, inner,
state: RefCell::new(state), state: RefCell::new(state),
config: Config::default(), config: Config::default(),
} }
@ -153,7 +157,9 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// # Panics /// # Panics
/// This function panics if the probability is not between 0% and 100%. /// This function panics if the probability is not between 0% and 100%.
pub fn set_corrupt_chance(&mut self, pct: u8) { 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 self.config.corrupt_pct = pct
} }
@ -162,7 +168,9 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// # Panics /// # Panics
/// This function panics if the probability is not between 0% and 100%. /// This function panics if the probability is not between 0% and 100%.
pub fn set_drop_chance(&mut self, pct: u8) { 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 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> 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 RxToken = RxToken<'a, <D as Device<'a>>::RxToken>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken>; 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)> { 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)| { inner.receive().map(|(rx_token, tx_token)| {
let rx = RxToken { let rx = RxToken {
state: &state, state,
config: config, config,
token: rx_token, token: rx_token,
corrupt: [0; MTU], corrupt: [0; MTU],
}; };
let tx = TxToken { let tx = TxToken {
state: &state, state,
config: config, config,
token: tx_token, token: tx_token,
junk: [0; MTU], junk: [0; MTU],
}; };
(rx, tx) (rx, tx)
}) })
} }
fn transmit(&'a mut self) -> Option<Self::TxToken> { 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 { inner.transmit().map(|token| TxToken {
state: &state, state,
config: config, config,
token: token, token,
junk: [0; MTU], junk: [0; MTU],
}) })
} }
} }
#[doc(hidden)] #[doc(hidden)]
pub struct RxToken<'a, Rx: phy::RxToken> { pub struct RxToken<'a, Rx: phy::RxToken> {
state: &'a RefCell<State>, state: &'a RefCell<State>,
config: Config, config: Config,
token: Rx, token: Rx,
corrupt: [u8; MTU], corrupt: [u8; MTU],
} }
impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> { impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> {
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R> 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) { if self.state.borrow_mut().maybe(self.config.drop_pct) {
net_trace!("rx: randomly dropping a packet"); 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"); 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| { token.consume(timestamp, |buffer| {
if config.max_size > 0 && buffer.as_ref().len() > config.max_size { if config.max_size > 0 && buffer.as_ref().len() > config.max_size {
net_trace!("rx: dropping a packet that is too large"); 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) { if state.borrow_mut().maybe(config.corrupt_pct) {
net_trace!("rx: randomly corrupting a packet"); net_trace!("rx: randomly corrupting a packet");
@ -273,15 +300,16 @@ impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> {
#[doc(hidden)] #[doc(hidden)]
pub struct TxToken<'a, Tx: phy::TxToken> { pub struct TxToken<'a, Tx: phy::TxToken> {
state: &'a RefCell<State>, state: &'a RefCell<State>,
config: Config, config: Config,
token: Tx, token: Tx,
junk: [u8; MTU], junk: [u8; MTU],
} }
impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> { 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> 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) { let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) {
net_trace!("tx: randomly dropping a packet"); 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 { } else if self.config.max_size > 0 && len > self.config.max_size {
net_trace!("tx: dropping a packet that is too large"); net_trace!("tx: dropping a packet that is too large");
true 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"); net_trace!("tx: dropping a packet because of rate limiting");
true true
} else { } else {
@ -300,7 +332,12 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
return f(&mut self.junk[..len]); return f(&mut self.junk[..len]);
} }
let Self { token, state, config, .. } = self; let Self {
token,
state,
config,
..
} = self;
token.consume(timestamp, len, |mut buf| { token.consume(timestamp, len, |mut buf| {
if state.borrow_mut().maybe(config.corrupt_pct) { if state.borrow_mut().maybe(config.corrupt_pct) {
net_trace!("tx: corrupting a packet"); net_trace!("tx: corrupting a packet");

View File

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

View File

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

View File

@ -11,7 +11,9 @@ and implementations of it:
[TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames [TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames
on the host OS. on the host OS.
*/ */
#![cfg_attr(feature = "medium-ethernet", doc = r##" #![cfg_attr(
feature = "medium-ethernet",
doc = r##"
# Examples # Examples
An implementation of the [Device](trait.Device.html) trait for a simple hardware 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::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 sys;
mod tracer;
mod fault_injector; mod fault_injector;
mod fuzz_injector; mod fuzz_injector;
mod pcap_writer;
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
mod loopback; mod loopback;
mod pcap_writer;
#[cfg(all(feature = "phy-raw_socket", unix))] #[cfg(all(feature = "phy-raw_socket", unix))]
mod raw_socket; 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; 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::sys::wait;
pub use self::tracer::Tracer;
pub use self::fault_injector::FaultInjector; pub use self::fault_injector::FaultInjector;
pub use self::fuzz_injector::{Fuzzer, FuzzInjector}; pub use self::fuzz_injector::{FuzzInjector, Fuzzer};
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
#[cfg(any(feature = "std", feature = "alloc"))] #[cfg(any(feature = "std", feature = "alloc"))]
pub use self::loopback::Loopback; pub use self::loopback::Loopback;
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
#[cfg(all(feature = "phy-raw_socket", unix))] #[cfg(all(feature = "phy-raw_socket", unix))]
pub use self::raw_socket::RawSocket; 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; pub use self::tuntap_interface::TunTapInterface;
/// A description of checksum behavior for a particular protocol. /// A description of checksum behavior for a particular protocol.
@ -142,7 +157,7 @@ impl Checksum {
pub fn rx(&self) -> bool { pub fn rx(&self) -> bool {
match *self { match *self {
Checksum::Both | Checksum::Rx => true, Checksum::Both | Checksum::Rx => true,
_ => false _ => false,
} }
} }
@ -150,7 +165,7 @@ impl Checksum {
pub fn tx(&self) -> bool { pub fn tx(&self) -> bool {
match *self { match *self {
Checksum::Both | Checksum::Tx => true, Checksum::Both | Checksum::Tx => true,
_ => false _ => false,
} }
} }
} }
@ -234,7 +249,9 @@ impl DeviceCapabilities {
pub fn ip_mtu(&self) -> usize { pub fn ip_mtu(&self) -> usize {
match self.medium { match self.medium {
#[cfg(feature = "medium-ethernet")] #[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")] #[cfg(feature = "medium-ip")]
Medium::Ip => self.max_transmission_unit, Medium::Ip => self.max_transmission_unit,
} }
@ -260,7 +277,6 @@ pub enum Medium {
Ip, Ip,
} }
impl Default for Medium { impl Default for Medium {
fn default() -> Medium { fn default() -> Medium {
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
@ -306,7 +322,8 @@ pub trait RxToken {
/// The timestamp must be a number of milliseconds, monotonically increasing since an /// The timestamp must be a number of milliseconds, monotonically increasing since an
/// arbitrary moment in time, such as system startup. /// arbitrary moment in time, such as system startup.
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R> 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. /// 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 /// The timestamp must be a number of milliseconds, monotonically increasing since an
/// arbitrary moment in time, such as system startup. /// arbitrary moment in time, such as system startup.
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R> 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")] #[cfg(feature = "std")]
use std::cell::RefCell; use std::cell::RefCell;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::io::Write; use std::io::Write;
use byteorder::{ByteOrder, NativeEndian};
use phy::Medium;
use crate::Result; use crate::phy::{self, Device, DeviceCapabilities};
use crate::phy::{self, DeviceCapabilities, Device};
use crate::time::Instant; use crate::time::Instant;
use crate::Result;
enum_with_unknown! { enum_with_unknown! {
/// Captured packet header type. /// Captured packet header type.
@ -28,7 +28,7 @@ pub enum PcapMode {
/// Capture only received packets. /// Capture only received packets.
RxOnly, RxOnly,
/// Capture only transmitted packets. /// Capture only transmitted packets.
TxOnly TxOnly,
} }
/// A packet capture sink. /// A packet capture sink.
@ -54,12 +54,12 @@ pub trait PcapSink {
/// ///
/// This method may be overridden e.g. if special synchronization is necessary. /// This method may be overridden e.g. if special synchronization is necessary.
fn global_header(&self, link_type: PcapLinkType) { fn global_header(&self, link_type: PcapLinkType) {
self.write_u32(0xa1b2c3d4); // magic number self.write_u32(0xa1b2c3d4); // magic number
self.write_u16(2); // major version self.write_u16(2); // major version
self.write_u16(4); // minor version self.write_u16(4); // minor version
self.write_u32(0); // timezone (= UTC) self.write_u32(0); // timezone (= UTC)
self.write_u32(0); // accuracy (not used) self.write_u32(0); // accuracy (not used)
self.write_u32(65535); // maximum packet length self.write_u32(65535); // maximum packet length
self.write_u32(link_type.into()); // link-layer header type 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) { fn packet_header(&self, timestamp: Instant, length: usize) {
assert!(length <= 65535); assert!(length <= 65535);
self.write_u32(timestamp.secs() as u32); // timestamp seconds self.write_u32(timestamp.secs() as u32); // timestamp seconds
self.write_u32(timestamp.millis() as u32); // timestamp microseconds self.write_u32(timestamp.millis() as u32); // timestamp microseconds
self.write_u32(length as u32); // captured length self.write_u32(length as u32); // captured length
self.write_u32(length as u32); // original length self.write_u32(length as u32); // original length
} }
/// Write the libpcap packet header followed by packet data into the sink. /// 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)] #[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PcapWriter<D, S> pub struct PcapWriter<D, S>
where D: for<'a> Device<'a>, where
S: PcapSink + Clone, D: for<'a> Device<'a>,
S: PcapSink + Clone,
{ {
lower: D, lower: D,
sink: S, sink: S,
mode: PcapMode, mode: PcapMode,
} }
impl<D: for<'a> Device<'a>, S: PcapSink + Clone> PcapWriter<D, S> { 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> impl<'a, D, S> Device<'a> for PcapWriter<D, S>
where D: for<'b> Device<'b>, where
S: PcapSink + Clone + 'a, D: for<'b> Device<'b>,
S: PcapSink + Clone + 'a,
{ {
type RxToken = RxToken<<D as Device<'a>>::RxToken, S>; type RxToken = RxToken<<D as Device<'a>>::RxToken, S>;
type TxToken = TxToken<<D as Device<'a>>::TxToken, 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)> { 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)| { lower.receive().map(|(rx_token, tx_token)| {
let rx = RxToken { token: rx_token, sink: sink.clone(), mode }; let rx = RxToken {
let tx = TxToken { token: tx_token, sink: sink.clone(), mode }; token: rx_token,
sink: sink.clone(),
mode,
};
let tx = TxToken {
token: tx_token,
sink: sink.clone(),
mode,
};
(rx, tx) (rx, tx)
}) })
} }
fn transmit(&'a mut self) -> Option<Self::TxToken> { fn transmit(&'a mut self) -> Option<Self::TxToken> {
let &mut Self { ref mut lower, ref sink, mode } = self; let &mut Self {
lower.transmit().map(|token| { ref mut lower,
TxToken { token, sink: sink.clone(), mode } 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)] #[doc(hidden)]
pub struct RxToken<Rx: phy::RxToken, S: PcapSink> { pub struct RxToken<Rx: phy::RxToken, S: PcapSink> {
token: Rx, token: Rx,
sink: S, sink: S,
mode: PcapMode, mode: PcapMode,
} }
impl<Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<Rx, S> { 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; let Self { token, sink, mode } = self;
token.consume(timestamp, |buffer| { token.consume(timestamp, |buffer| {
match mode { match mode {
PcapMode::Both | PcapMode::RxOnly => PcapMode::Both | PcapMode::RxOnly => sink.packet(timestamp, buffer.as_ref()),
sink.packet(timestamp, buffer.as_ref()), PcapMode::TxOnly => (),
PcapMode::TxOnly => ()
} }
f(buffer) f(buffer)
}) })
@ -194,21 +216,21 @@ impl<Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<Rx, S> {
#[doc(hidden)] #[doc(hidden)]
pub struct TxToken<Tx: phy::TxToken, S: PcapSink> { pub struct TxToken<Tx: phy::TxToken, S: PcapSink> {
token: Tx, token: Tx,
sink: S, sink: S,
mode: PcapMode mode: PcapMode,
} }
impl<Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<Tx, S> { 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> 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; let Self { token, sink, mode } = self;
token.consume(timestamp, len, |buffer| { token.consume(timestamp, len, |buffer| {
let result = f(buffer); let result = f(buffer);
match mode { match mode {
PcapMode::Both | PcapMode::TxOnly => PcapMode::Both | PcapMode::TxOnly => sink.packet(timestamp, buffer),
sink.packet(timestamp, &buffer), PcapMode::RxOnly => (),
PcapMode::RxOnly => ()
}; };
result result
}) })

View File

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

View File

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

View File

@ -1,10 +1,10 @@
#![allow(unused)] #![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 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 TUNSETIFF: libc::c_ulong = 0x400454CA;
pub const IFF_TUN: libc::c_int = 0x0001; pub const IFF_TUN: libc::c_int = 0x0001;
pub const IFF_TAP: libc::c_int = 0x0002; pub const IFF_TAP: libc::c_int = 0x0002;
pub const IFF_NO_PI: libc::c_int = 0x1000; pub const IFF_NO_PI: libc::c_int = 0x1000;

View File

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

View File

@ -1,12 +1,12 @@
use std::{mem, io};
use std::os::unix::io::{RawFd, AsRawFd};
use super::*; use super::*;
use crate::wire::EthernetFrame; use crate::wire::EthernetFrame;
use std::os::unix::io::{AsRawFd, RawFd};
use std::{io, mem};
#[derive(Debug)] #[derive(Debug)]
pub struct RawSocketDesc { pub struct RawSocketDesc {
lower: libc::c_int, lower: libc::c_int,
ifreq: ifreq ifreq: ifreq,
} }
impl AsRawFd for RawSocketDesc { impl AsRawFd for RawSocketDesc {
@ -18,41 +18,51 @@ impl AsRawFd for RawSocketDesc {
impl RawSocketDesc { impl RawSocketDesc {
pub fn new(name: &str) -> io::Result<RawSocketDesc> { pub fn new(name: &str) -> io::Result<RawSocketDesc> {
let lower = unsafe { let lower = unsafe {
let lower = libc::socket(libc::AF_PACKET, libc::SOCK_RAW | libc::SOCK_NONBLOCK, let lower = libc::socket(
imp::ETH_P_ALL.to_be() as i32); libc::AF_PACKET,
if lower == -1 { return Err(io::Error::last_os_error()) } 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 lower
}; };
Ok(RawSocketDesc { Ok(RawSocketDesc {
lower: lower, lower: lower,
ifreq: ifreq_for(name) ifreq: ifreq_for(name),
}) })
} }
pub fn interface_mtu(&mut self) -> io::Result<usize> { pub fn interface_mtu(&mut self) -> io::Result<usize> {
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.) // 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. // 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()) Ok(ip_mtu + EthernetFrame::<&[u8]>::header_len())
} }
pub fn bind_interface(&mut self) -> io::Result<()> { pub fn bind_interface(&mut self) -> io::Result<()> {
let sockaddr = libc::sockaddr_ll { 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_protocol: imp::ETH_P_ALL.to_be() as u16,
sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?, sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?,
sll_hatype: 1, sll_hatype: 1,
sll_pkttype: 0, sll_pkttype: 0,
sll_halen: 6, sll_halen: 6,
sll_addr: [0; 8] sll_addr: [0; 8],
}; };
unsafe { unsafe {
let res = libc::bind(self.lower, let res = libc::bind(
&sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr, self.lower,
mem::size_of::<libc::sockaddr_ll>() as u32); &sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
if res == -1 { return Err(io::Error::last_os_error()) } mem::size_of::<libc::sockaddr_ll>() as u32,
);
if res == -1 {
return Err(io::Error::last_os_error());
}
} }
Ok(()) Ok(())
@ -60,18 +70,30 @@ impl RawSocketDesc {
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> { pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe { unsafe {
let len = libc::recv(self.lower, buffer.as_mut_ptr() as *mut libc::c_void, let len = libc::recv(
buffer.len(), 0); self.lower,
if len == -1 { return Err(io::Error::last_os_error()) } 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) Ok(len as usize)
} }
} }
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> { pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe { unsafe {
let len = libc::send(self.lower, buffer.as_ptr() as *const libc::c_void, let len = libc::send(
buffer.len(), 0); self.lower,
if len == -1 { Err(io::Error::last_os_error()).unwrap() } 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) Ok(len as usize)
} }
} }
@ -79,6 +101,8 @@ impl RawSocketDesc {
impl Drop for RawSocketDesc { impl Drop for RawSocketDesc {
fn drop(&mut self) { 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 super::*;
use crate::{phy::Medium, wire::EthernetFrame}; use crate::{phy::Medium, wire::EthernetFrame};
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
#[derive(Debug)] #[derive(Debug)]
pub struct TunTapInterfaceDesc { pub struct TunTapInterfaceDesc {
@ -19,9 +19,13 @@ impl AsRawFd for TunTapInterfaceDesc {
impl TunTapInterfaceDesc { impl TunTapInterfaceDesc {
pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> { pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
let lower = unsafe { let lower = unsafe {
let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char, let lower = libc::open(
libc::O_RDWR | libc::O_NONBLOCK); "/dev/net/tun\0".as_ptr() as *const libc::c_char,
if lower == -1 { return Err(io::Error::last_os_error()) } libc::O_RDWR | libc::O_NONBLOCK,
);
if lower == -1 {
return Err(io::Error::last_os_error());
}
lower lower
}; };
@ -46,13 +50,17 @@ impl TunTapInterfaceDesc {
pub fn interface_mtu(&mut self) -> io::Result<usize> { pub fn interface_mtu(&mut self) -> io::Result<usize> {
let lower = unsafe { let lower = unsafe {
let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP); 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 lower
}; };
let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize); 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. // Propagate error after close, to ensure we always close.
let ip_mtu = ip_mtu?; let ip_mtu = ip_mtu?;
@ -71,18 +79,28 @@ impl TunTapInterfaceDesc {
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> { pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe { unsafe {
let len = libc::read(self.lower, buffer.as_mut_ptr() as *mut libc::c_void, let len = libc::read(
buffer.len()); self.lower,
if len == -1 { return Err(io::Error::last_os_error()) } 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) Ok(len as usize)
} }
} }
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> { pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe { unsafe {
let len = libc::write(self.lower, buffer.as_ptr() as *const libc::c_void, let len = libc::write(
buffer.len()); self.lower,
if len == -1 { Err(io::Error::last_os_error()).unwrap() } buffer.as_ptr() as *const libc::c_void,
buffer.len(),
);
if len == -1 {
Err(io::Error::last_os_error()).unwrap()
}
Ok(len as usize) Ok(len as usize)
} }
} }
@ -90,6 +108,8 @@ impl TunTapInterfaceDesc {
impl Drop for TunTapInterfaceDesc { impl Drop for TunTapInterfaceDesc {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { libc::close(self.lower); } unsafe {
libc::close(self.lower);
}
} }
} }

View File

@ -1,8 +1,11 @@
use core::fmt; use core::fmt;
use crate::{Result, wire::pretty_print::{PrettyIndent, PrettyPrint}}; use crate::phy::{self, Device, DeviceCapabilities, Medium};
use crate::phy::{self, DeviceCapabilities, Device, Medium};
use crate::time::Instant; use crate::time::Instant;
use crate::{
wire::pretty_print::{PrettyIndent, PrettyPrint},
Result,
};
/// A tracer device. /// A tracer device.
/// ///
@ -10,7 +13,7 @@ use crate::time::Instant;
/// using the provided writer function, and then passes them to another /// using the provided writer function, and then passes them to another
/// device. /// device.
pub struct Tracer<D: for<'a> Device<'a>> { pub struct Tracer<D: for<'a> Device<'a>> {
inner: D, inner: D,
writer: fn(Instant, Packet), writer: fn(Instant, Packet),
} }
@ -42,50 +45,78 @@ impl<D: for<'a> Device<'a>> Tracer<D> {
} }
impl<'a, D> Device<'a> for 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 RxToken = RxToken<<D as Device<'a>>::RxToken>;
type TxToken = TxToken<<D as Device<'a>>::TxToken>; 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)> { 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; let medium = inner.capabilities().medium;
inner.receive().map(|(rx_token, tx_token)| { inner.receive().map(|(rx_token, tx_token)| {
let rx = RxToken { token: rx_token, writer, medium }; let rx = RxToken {
let tx = TxToken { token: tx_token, writer, medium }; token: rx_token,
writer,
medium,
};
let tx = TxToken {
token: tx_token,
writer,
medium,
};
(rx, tx) (rx, tx)
}) })
} }
fn transmit(&'a mut self) -> Option<Self::TxToken> { 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; let medium = inner.capabilities().medium;
inner.transmit().map(|tx_token| { inner.transmit().map(|tx_token| TxToken {
TxToken { token: tx_token, medium, writer } token: tx_token,
medium,
writer,
}) })
} }
} }
#[doc(hidden)] #[doc(hidden)]
pub struct RxToken<Rx: phy::RxToken> { pub struct RxToken<Rx: phy::RxToken> {
token: Rx, token: Rx,
writer: fn(Instant, Packet), writer: fn(Instant, Packet),
medium: Medium, medium: Medium,
} }
impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> { impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R> 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| { token.consume(timestamp, |buffer| {
writer(timestamp, Packet{ writer(
buffer, timestamp,
medium, Packet {
prefix: "<- ", buffer,
}); medium,
prefix: "<- ",
},
);
f(buffer) f(buffer)
}) })
} }
@ -93,23 +124,31 @@ impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
#[doc(hidden)] #[doc(hidden)]
pub struct TxToken<Tx: phy::TxToken> { pub struct TxToken<Tx: phy::TxToken> {
token: Tx, token: Tx,
writer: fn(Instant, Packet), writer: fn(Instant, Packet),
medium: Medium, medium: Medium,
} }
impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> { impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R> 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| { token.consume(timestamp, len, |buffer| {
let result = f(buffer); let result = f(buffer);
writer(timestamp, Packet{ writer(
buffer, timestamp,
medium, Packet {
prefix: "-> ", buffer,
}); medium,
prefix: "-> ",
},
);
result result
}) })
} }
@ -126,15 +165,31 @@ impl<'a> fmt::Display for Packet<'a> {
let mut indent = PrettyIndent::new(self.prefix); let mut indent = PrettyIndent::new(self.prefix);
match self.medium { match self.medium {
#[cfg(feature = "medium-ethernet")] #[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")] #[cfg(feature = "medium-ip")]
Medium::Ip => match crate::wire::IpVersion::of_packet(&self.buffer) { Medium::Ip => match crate::wire::IpVersion::of_packet(self.buffer) {
#[cfg(feature = "proto-ipv4")] #[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")] #[cfg(feature = "proto-ipv6")]
Ok(crate::wire::IpVersion::Ipv6) => crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent), Ok(crate::wire::IpVersion::Ipv6) => {
_ => f.write_str("unrecognized IP version") 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::cell::RefCell;
use std::vec::Vec;
use std::rc::Rc;
use std::io; 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, Device, DeviceCapabilities, Medium};
use crate::phy::{self, sys, DeviceCapabilities, Device, Medium};
use crate::time::Instant; use crate::time::Instant;
use crate::Result;
/// A virtual TUN (IP) or TAP (Ethernet) interface. /// A virtual TUN (IP) or TAP (Ethernet) interface.
#[derive(Debug)] #[derive(Debug)]
pub struct TunTapInterface { pub struct TunTapInterface {
lower: Rc<RefCell<sys::TunTapInterfaceDesc>>, lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
mtu: usize, mtu: usize,
medium: Medium, medium: Medium,
} }
@ -59,13 +59,13 @@ impl<'a> Device<'a> for TunTapInterface {
Ok(size) => { Ok(size) => {
buffer.resize(size, 0); buffer.resize(size, 0);
let rx = RxToken { buffer }; let rx = RxToken { buffer };
let tx = TxToken { lower: self.lower.clone() }; let tx = TxToken {
lower: self.lower.clone(),
};
Some((rx, tx)) Some((rx, tx))
} }
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None,
None Err(err) => panic!("{}", err),
}
Err(err) => panic!("{}", err)
} }
} }
@ -78,12 +78,13 @@ impl<'a> Device<'a> for TunTapInterface {
#[doc(hidden)] #[doc(hidden)]
pub struct RxToken { pub struct RxToken {
buffer: Vec<u8> buffer: Vec<u8>,
} }
impl phy::RxToken for RxToken { impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R> 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[..]) f(&mut self.buffer[..])
} }
@ -96,7 +97,8 @@ pub struct TxToken {
impl phy::TxToken for TxToken { impl phy::TxToken for TxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R> 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 lower = self.lower.borrow_mut();
let mut buffer = vec![0; len]; 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::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}; use super::{PollAt, Socket};
@ -31,7 +31,7 @@ const PARAMETER_REQUEST_LIST: &[u8] = &[
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Config { pub struct Config {
/// IP address /// IP address
pub address: Ipv4Cidr, pub address: Ipv4Cidr,
/// Router address, also known as default gateway. Does not necessarily /// Router address, also known as default gateway. Does not necessarily
/// match the DHCP server's address. /// match the DHCP server's address.
@ -133,7 +133,7 @@ impl Dhcpv4Socket {
pub fn new() -> Self { pub fn new() -> Self {
Dhcpv4Socket { Dhcpv4Socket {
meta: SocketMeta::default(), meta: SocketMeta::default(),
state: ClientState::Discovering(DiscoverState{ state: ClientState::Discovering(DiscoverState {
retry_at: Instant::from_millis(0), retry_at: Instant::from_millis(0),
}), }),
config_changed: true, config_changed: true,
@ -159,7 +159,13 @@ impl Dhcpv4Socket {
PollAt::Time(t) 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; let src_ip = ip_repr.src_addr;
// This is enforced in interface.rs. // This is enforced in interface.rs.
@ -179,25 +185,37 @@ impl Dhcpv4Socket {
return Ok(()); return Ok(());
} }
}; };
if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() { return Ok(()) } if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() {
if dhcp_repr.transaction_id != self.transaction_id { return Ok(()) } return Ok(());
}
if dhcp_repr.transaction_id != self.transaction_id {
return Ok(());
}
let server_identifier = match dhcp_repr.server_identifier { let server_identifier = match dhcp_repr.server_identifier {
Some(server_identifier) => server_identifier, Some(server_identifier) => server_identifier,
None => { 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(()); return Ok(());
} }
}; };
net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier); net_debug!(
"DHCP recv {:?} from {} ({})",
match (&mut self.state, dhcp_repr.message_type){ dhcp_repr.message_type,
src_ip,
server_identifier
);
match (&mut self.state, dhcp_repr.message_type) {
(ClientState::Discovering(_state), DhcpMessageType::Offer) => { (ClientState::Discovering(_state), DhcpMessageType::Offer) => {
if !dhcp_repr.your_ip.is_unicast() { if !dhcp_repr.your_ip.is_unicast() {
net_debug!("DHCP ignoring OFFER because your_ip is not unicast"); net_debug!("DHCP ignoring OFFER because your_ip is not unicast");
return Ok(()) return Ok(());
} }
self.state = ClientState::Requesting(RequestState { self.state = ClientState::Requesting(RequestState {
retry_at: cx.now, retry_at: cx.now,
retry: 0, retry: 0,
@ -205,13 +223,15 @@ impl Dhcpv4Socket {
address: src_ip, address: src_ip,
identifier: server_identifier, 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) => { (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.config_changed = true;
self.state = ClientState::Renewing(RenewState{ self.state = ClientState::Renewing(RenewState {
server: state.server, server: state.server,
config, config,
renew_at, renew_at,
@ -223,7 +243,9 @@ impl Dhcpv4Socket {
self.reset(); self.reset();
} }
(ClientState::Renewing(state), DhcpMessageType::Ack) => { (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.renew_at = renew_at;
state.expires_at = expires_at; state.expires_at = expires_at;
if state.config != config { if state.config != config {
@ -236,19 +258,26 @@ impl Dhcpv4Socket {
self.reset(); 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(()) 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 { let subnet_mask = match dhcp_repr.subnet_mask {
Some(subnet_mask) => subnet_mask, Some(subnet_mask) => subnet_mask,
None => { None => {
net_debug!("DHCP ignoring ACK because missing subnet_mask"); net_debug!("DHCP ignoring ACK because missing subnet_mask");
return None return None;
} }
}; };
@ -256,16 +285,19 @@ impl Dhcpv4Socket {
Some(prefix_len) => prefix_len, Some(prefix_len) => prefix_len,
None => { None => {
net_debug!("DHCP ignoring ACK because subnet_mask is not a valid mask"); net_debug!("DHCP ignoring ACK because subnet_mask is not a valid mask");
return None return None;
} }
}; };
if !dhcp_repr.your_ip.is_unicast() { if !dhcp_repr.your_ip.is_unicast() {
net_debug!("DHCP ignoring ACK because your_ip is not 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 { if let Some(max_lease_duration) = max_lease_duration {
lease_duration = lease_duration.min(max_lease_duration); lease_duration = lease_duration.min(max_lease_duration);
} }
@ -275,20 +307,18 @@ impl Dhcpv4Socket {
let mut dns_servers = [None; DHCP_MAX_DNS_SERVER_COUNT]; let mut dns_servers = [None; DHCP_MAX_DNS_SERVER_COUNT];
if let Some(received) = dhcp_repr.dns_servers { if let Some(received) = dhcp_repr.dns_servers {
let mut i = 0; let mut i = 0;
for addr in received.iter() { for addr in received.iter().flatten() {
if let Some(addr) = addr{ if addr.is_unicast() {
if addr.is_unicast() { // This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT
// This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT dns_servers[i] = Some(*addr);
dns_servers[i] = Some(*addr); i += 1;
i += 1;
}
} }
} }
} }
let config = Config{ let config = Config {
address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len), address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len),
router: dhcp_repr.router, router: dhcp_repr.router,
dns_servers: dns_servers dns_servers: dns_servers,
}; };
// RFC 2131 indicates clients should renew a lease halfway through its expiration. // RFC 2131 indicates clients should renew a lease halfway through its expiration.
@ -299,9 +329,10 @@ impl Dhcpv4Socket {
} }
pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()> 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 {
// note: Dhcpv4Socket is only usable in ethernet mediums, so the
// unwrap can never fail. // unwrap can never fail.
let ethernet_addr = cx.ethernet_address.unwrap(); let ethernet_addr = cx.ethernet_address.unwrap();
@ -337,7 +368,7 @@ impl Dhcpv4Socket {
src_port: DHCP_CLIENT_PORT, src_port: DHCP_CLIENT_PORT,
dst_port: DHCP_SERVER_PORT, dst_port: DHCP_SERVER_PORT,
}; };
let mut ipv4_repr = Ipv4Repr { let mut ipv4_repr = Ipv4Repr {
src_addr: Ipv4Address::UNSPECIFIED, src_addr: Ipv4Address::UNSPECIFIED,
dst_addr: Ipv4Address::BROADCAST, dst_addr: Ipv4Address::BROADCAST,
@ -349,11 +380,15 @@ impl Dhcpv4Socket {
match &mut self.state { match &mut self.state {
ClientState::Discovering(state) => { ClientState::Discovering(state) => {
if cx.now < state.retry_at { if cx.now < state.retry_at {
return Err(Error::Exhausted) return Err(Error::Exhausted);
} }
// send packet // 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(); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
emit((ipv4_repr, udp_repr, dhcp_repr))?; emit((ipv4_repr, udp_repr, dhcp_repr))?;
@ -364,14 +399,14 @@ impl Dhcpv4Socket {
} }
ClientState::Requesting(state) => { ClientState::Requesting(state) => {
if cx.now < state.retry_at { if cx.now < state.retry_at {
return Err(Error::Exhausted) return Err(Error::Exhausted);
} }
if state.retry >= REQUEST_RETRIES { if state.retry >= REQUEST_RETRIES {
net_debug!("DHCP request retries exceeded, restarting discovery"); net_debug!("DHCP request retries exceeded, restarting discovery");
self.reset(); self.reset();
// return Ok so we get polled again // return Ok so we get polled again
return Ok(()) return Ok(());
} }
dhcp_repr.message_type = DhcpMessageType::Request; dhcp_repr.message_type = DhcpMessageType::Request;
@ -379,7 +414,11 @@ impl Dhcpv4Socket {
dhcp_repr.requested_ip = Some(state.requested_ip); dhcp_repr.requested_ip = Some(state.requested_ip);
dhcp_repr.server_identifier = Some(state.server.identifier); 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(); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
emit((ipv4_repr, udp_repr, dhcp_repr))?; emit((ipv4_repr, udp_repr, dhcp_repr))?;
@ -395,11 +434,11 @@ impl Dhcpv4Socket {
net_debug!("DHCP lease expired"); net_debug!("DHCP lease expired");
self.reset(); self.reset();
// return Ok so we get polled again // return Ok so we get polled again
return Ok(()) return Ok(());
} }
if cx.now < state.renew_at { if cx.now < state.renew_at {
return Err(Error::Exhausted) return Err(Error::Exhausted);
} }
ipv4_repr.src_addr = state.config.address.address(); ipv4_repr.src_addr = state.config.address.address();
@ -411,7 +450,7 @@ impl Dhcpv4Socket {
net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr); net_debug!("DHCP send renew to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr);
ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len(); ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
emit((ipv4_repr, udp_repr, dhcp_repr))?; emit((ipv4_repr, udp_repr, dhcp_repr))?;
// In both RENEWING and REBINDING states, if the client receives no // In both RENEWING and REBINDING states, if the client receives no
// response to its DHCPREQUEST message, the client SHOULD wait one-half // response to its DHCPREQUEST message, the client SHOULD wait one-half
// of the remaining time until T2 (in RENEWING state) and one-half of // of the remaining time until T2 (in RENEWING state) and one-half of
@ -440,7 +479,7 @@ impl Dhcpv4Socket {
if let ClientState::Renewing(_) = &self.state { if let ClientState::Renewing(_) = &self.state {
self.config_changed = true; self.config_changed = true;
} }
self.state = ClientState::Discovering(DiscoverState{ self.state = ClientState::Discovering(DiscoverState {
retry_at: Instant::from_millis(0), retry_at: Instant::from_millis(0),
}); });
} }

View File

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

View File

@ -1,6 +1,6 @@
use crate::wire::IpAddress; use crate::socket::{PollAt, SocketHandle};
use crate::socket::{SocketHandle, PollAt};
use crate::time::{Duration, Instant}; use crate::time::{Duration, Instant};
use crate::wire::IpAddress;
/// Neighbor dependency. /// Neighbor dependency.
/// ///
@ -16,7 +16,7 @@ enum NeighborState {
Waiting { Waiting {
neighbor: IpAddress, neighbor: IpAddress,
silent_until: Instant, silent_until: Instant,
} },
} }
impl Default for NeighborState { impl Default for NeighborState {
@ -36,7 +36,7 @@ pub struct Meta {
/// Mainly useful for debug output. /// Mainly useful for debug output.
pub(crate) handle: SocketHandle, pub(crate) handle: SocketHandle,
/// See [NeighborState](struct.NeighborState.html). /// See [NeighborState](struct.NeighborState.html).
neighbor_state: NeighborState, neighbor_state: NeighborState,
} }
impl Meta { impl Meta {
@ -46,34 +46,41 @@ impl Meta {
/// See also `iface::NeighborCache::SILENT_TIME`. /// See also `iface::NeighborCache::SILENT_TIME`.
pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration { millis: 3_000 }; 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 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 { match self.neighbor_state {
NeighborState::Active => NeighborState::Active => socket_poll_at,
socket_poll_at, NeighborState::Waiting { neighbor, .. } if has_neighbor(neighbor) => socket_poll_at,
NeighborState::Waiting { neighbor, .. } NeighborState::Waiting { silent_until, .. } => PollAt::Time(silent_until),
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 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 { match self.neighbor_state {
NeighborState::Active => NeighborState::Active => true,
true, NeighborState::Waiting {
NeighborState::Waiting { neighbor, silent_until } => { neighbor,
silent_until,
} => {
if has_neighbor(neighbor) { if has_neighbor(neighbor) {
net_trace!("{}: neighbor {} discovered, unsilencing", net_trace!(
self.handle, neighbor); "{}: neighbor {} discovered, unsilencing",
self.handle,
neighbor
);
self.neighbor_state = NeighborState::Active; self.neighbor_state = NeighborState::Active;
true true
} else if timestamp >= silent_until { } 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 true
} else { } else {
false false
@ -83,10 +90,15 @@ impl Meta {
} }
pub(crate) fn neighbor_missing(&mut self, timestamp: Instant, neighbor: IpAddress) { pub(crate) fn neighbor_missing(&mut self, timestamp: Instant, neighbor: IpAddress) {
net_trace!("{}: neighbor {} missing, silencing until t+{}", net_trace!(
self.handle, neighbor, Self::DISCOVERY_SILENT_TIME); "{}: neighbor {} missing, silencing until t+{}",
self.handle,
neighbor,
Self::DISCOVERY_SILENT_TIME
);
self.neighbor_state = NeighborState::Waiting { 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::phy::DeviceCapabilities;
use crate::time::Instant; 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; mod meta;
#[cfg(feature = "socket-raw")] #[cfg(feature = "socket-raw")]
mod raw; mod raw;
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] mod ref_;
mod icmp; mod set;
#[cfg(feature = "socket-udp")]
mod udp;
#[cfg(feature = "socket-tcp")] #[cfg(feature = "socket-tcp")]
mod tcp; mod tcp;
#[cfg(feature = "socket-dhcpv4")] #[cfg(feature = "socket-udp")]
mod dhcpv4; mod udp;
mod set;
mod ref_;
#[cfg(feature = "async")] #[cfg(feature = "async")]
mod waker; mod waker;
@ -36,30 +39,24 @@ pub(crate) use self::meta::Meta as SocketMeta;
pub(crate) use self::waker::WakerRegistration; pub(crate) use self::waker::WakerRegistration;
#[cfg(feature = "socket-raw")] #[cfg(feature = "socket-raw")]
pub use self::raw::{RawPacketMetadata, pub use self::raw::{RawPacketMetadata, RawSocket, RawSocketBuffer};
RawSocketBuffer,
RawSocket};
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))] #[cfg(all(
pub use self::icmp::{IcmpPacketMetadata, feature = "socket-icmp",
IcmpSocketBuffer, any(feature = "proto-ipv4", feature = "proto-ipv6")
Endpoint as IcmpEndpoint, ))]
IcmpSocket}; pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer};
#[cfg(feature = "socket-udp")] #[cfg(feature = "socket-udp")]
pub use self::udp::{UdpPacketMetadata, pub use self::udp::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
UdpSocketBuffer,
UdpSocket};
#[cfg(feature = "socket-tcp")] #[cfg(feature = "socket-tcp")]
pub use self::tcp::{SocketBuffer as TcpSocketBuffer, pub use self::tcp::{SocketBuffer as TcpSocketBuffer, State as TcpState, TcpSocket};
State as TcpState,
TcpSocket};
#[cfg(feature = "socket-dhcpv4")] #[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::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut};
pub use self::ref_::Ref as SocketRef; pub use self::ref_::Ref as SocketRef;
@ -91,7 +88,10 @@ pub(crate) enum PollAt {
pub enum Socket<'a> { pub enum Socket<'a> {
#[cfg(feature = "socket-raw")] #[cfg(feature = "socket-raw")]
Raw(RawSocket<'a>), 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>), Icmp(IcmpSocket<'a>),
#[cfg(feature = "socket-udp")] #[cfg(feature = "socket-udp")]
Udp(UdpSocket<'a>), Udp(UdpSocket<'a>),
@ -152,15 +152,13 @@ impl<'a> SocketSession for Socket<'a> {
/// A conversion trait for network sockets. /// A conversion trait for network sockets.
pub trait AnySocket<'a>: SocketSession + Sized { pub trait AnySocket<'a>: SocketSession + Sized {
fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) -> fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) -> Option<SocketRef<'c, Self>>;
Option<SocketRef<'c, Self>>;
} }
macro_rules! from_socket { macro_rules! from_socket {
($socket:ty, $variant:ident) => { ($socket:ty, $variant:ident) => {
impl<'a> AnySocket<'a> for $socket { impl<'a> AnySocket<'a> for $socket {
fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> Option<SocketRef<'c, Self>> {
Option<SocketRef<'c, Self>> {
if let Socket::$variant(ref mut socket) = SocketRef::into_inner(ref_) { if let Socket::$variant(ref mut socket) = SocketRef::into_inner(ref_) {
Some(SocketRef::new(socket)) Some(SocketRef::new(socket))
} else { } else {
@ -168,12 +166,15 @@ macro_rules! from_socket {
} }
} }
} }
} };
} }
#[cfg(feature = "socket-raw")] #[cfg(feature = "socket-raw")]
from_socket!(RawSocket<'a>, 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); from_socket!(IcmpSocket<'a>, Icmp);
#[cfg(feature = "socket-udp")] #[cfg(feature = "socket-udp")]
from_socket!(UdpSocket<'a>, Udp); from_socket!(UdpSocket<'a>, Udp);
@ -193,14 +194,13 @@ pub(crate) struct Context {
#[cfg(test)] #[cfg(test)]
impl Context { impl Context {
pub(crate) const DUMMY: Context = Context { pub(crate) const DUMMY: Context = Context {
caps: DeviceCapabilities { caps: DeviceCapabilities {
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
medium: crate::phy::Medium::Ethernet, medium: crate::phy::Medium::Ethernet,
#[cfg(not(feature = "medium-ethernet"))] #[cfg(not(feature = "medium-ethernet"))]
medium: crate::phy::Medium::Ip, medium: crate::phy::Medium::Ip,
checksum: crate::phy::ChecksumCapabilities{ checksum: crate::phy::ChecksumCapabilities {
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
icmpv4: crate::phy::Checksum::Both, icmpv4: crate::phy::Checksum::Both,
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
@ -216,7 +216,6 @@ impl Context {
max_transmission_unit: 1500, max_transmission_unit: 1500,
}, },
ethernet_address: None, ethernet_address: None,
now: Instant{millis: 0}, now: Instant { millis: 0 },
}; };
}
}

View File

@ -2,18 +2,18 @@ use core::cmp::min;
#[cfg(feature = "async")] #[cfg(feature = "async")]
use core::task::Waker; use core::task::Waker;
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities; use crate::phy::ChecksumCapabilities;
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
use crate::storage::{PacketBuffer, PacketMetadata};
#[cfg(feature = "async")] #[cfg(feature = "async")]
use crate::socket::WakerRegistration; 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")] #[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Repr, Ipv4Packet}; use crate::wire::{Ipv4Packet, Ipv4Repr};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Repr, Ipv6Packet}; use crate::wire::{Ipv6Packet, Ipv6Repr};
/// A UDP packet metadata. /// A UDP packet metadata.
pub type RawPacketMetadata = PacketMetadata<()>; pub type RawPacketMetadata = PacketMetadata<()>;
@ -28,10 +28,10 @@ pub type RawSocketBuffer<'a> = PacketBuffer<'a, ()>;
#[derive(Debug)] #[derive(Debug)]
pub struct RawSocket<'a> { pub struct RawSocket<'a> {
pub(crate) meta: SocketMeta, pub(crate) meta: SocketMeta,
ip_version: IpVersion, ip_version: IpVersion,
ip_protocol: IpProtocol, ip_protocol: IpProtocol,
rx_buffer: RawSocketBuffer<'a>, rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>, tx_buffer: RawSocketBuffer<'a>,
#[cfg(feature = "async")] #[cfg(feature = "async")]
rx_waker: WakerRegistration, rx_waker: WakerRegistration,
#[cfg(feature = "async")] #[cfg(feature = "async")]
@ -41,9 +41,12 @@ pub struct RawSocket<'a> {
impl<'a> RawSocket<'a> { impl<'a> RawSocket<'a> {
/// Create a raw IP socket bound to the given IP version and datagram protocol, /// Create a raw IP socket bound to the given IP version and datagram protocol,
/// with the given buffers. /// with the given buffers.
pub fn new(ip_version: IpVersion, ip_protocol: IpProtocol, pub fn new(
rx_buffer: RawSocketBuffer<'a>, ip_version: IpVersion,
tx_buffer: RawSocketBuffer<'a>) -> RawSocket<'a> { ip_protocol: IpProtocol,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>,
) -> RawSocket<'a> {
RawSocket { RawSocket {
meta: SocketMeta::default(), meta: SocketMeta::default(),
ip_version, ip_version,
@ -61,12 +64,12 @@ impl<'a> RawSocket<'a> {
/// ///
/// The waker is woken on state changes that might affect the return value /// The waker is woken on state changes that might affect the return value
/// of `recv` method calls, such as receiving data, or the socket closing. /// of `recv` method calls, such as receiving data, or the socket closing.
/// ///
/// Notes: /// Notes:
/// ///
/// - Only one waker can be registered at a time. If another waker was previously registered, /// - Only one waker can be registered at a time. If another waker was previously registered,
/// it is overwritten and will no longer be woken. /// it is overwritten and will no longer be woken.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has
/// necessarily changed. /// necessarily changed.
#[cfg(feature = "async")] #[cfg(feature = "async")]
@ -79,12 +82,12 @@ impl<'a> RawSocket<'a> {
/// The waker is woken on state changes that might affect the return value /// The waker is woken on state changes that might affect the return value
/// of `send` method calls, such as space becoming available in the transmit /// of `send` method calls, such as space becoming available in the transmit
/// buffer, or the socket closing. /// buffer, or the socket closing.
/// ///
/// Notes: /// Notes:
/// ///
/// - Only one waker can be registered at a time. If another waker was previously registered, /// - Only one waker can be registered at a time. If another waker was previously registered,
/// it is overwritten and will no longer be woken. /// it is overwritten and will no longer be woken.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes. /// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has /// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has
/// necessarily changed. /// necessarily changed.
#[cfg(feature = "async")] #[cfg(feature = "async")]
@ -160,9 +163,13 @@ impl<'a> RawSocket<'a> {
pub fn send(&mut self, size: usize) -> Result<&mut [u8]> { pub fn send(&mut self, size: usize) -> Result<&mut [u8]> {
let packet_buf = self.tx_buffer.enqueue(size, ())?; let packet_buf = self.tx_buffer.enqueue(size, ())?;
net_trace!("{}:{}:{}: buffer to send {} octets", net_trace!(
self.meta.handle, self.ip_version, self.ip_protocol, "{}:{}:{}: buffer to send {} octets",
packet_buf.len()); self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
Ok(packet_buf) Ok(packet_buf)
} }
@ -183,9 +190,13 @@ impl<'a> RawSocket<'a> {
pub fn recv(&mut self) -> Result<&[u8]> { pub fn recv(&mut self) -> Result<&[u8]> {
let ((), packet_buf) = self.rx_buffer.dequeue()?; let ((), packet_buf) = self.rx_buffer.dequeue()?;
net_trace!("{}:{}:{}: receive {} buffered octets", net_trace!(
self.meta.handle, self.ip_version, self.ip_protocol, "{}:{}:{}: receive {} buffered octets",
packet_buf.len()); self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
Ok(packet_buf) Ok(packet_buf)
} }
@ -200,8 +211,12 @@ impl<'a> RawSocket<'a> {
} }
pub(crate) fn accepts(&self, ip_repr: &IpRepr) -> bool { pub(crate) fn accepts(&self, ip_repr: &IpRepr) -> bool {
if ip_repr.version() != self.ip_version { return false } if ip_repr.version() != self.ip_version {
if ip_repr.protocol() != self.ip_protocol { return false } return false;
}
if ip_repr.protocol() != self.ip_protocol {
return false;
}
true true
} }
@ -210,14 +225,18 @@ impl<'a> RawSocket<'a> {
debug_assert!(self.accepts(ip_repr)); debug_assert!(self.accepts(ip_repr));
let header_len = ip_repr.buffer_len(); 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, ())?; let packet_buf = self.rx_buffer.enqueue(total_len, ())?;
ip_repr.emit(&mut packet_buf[..header_len], &cx.caps.checksum); ip_repr.emit(&mut packet_buf[..header_len], &cx.caps.checksum);
packet_buf[header_len..].copy_from_slice(payload); packet_buf[header_len..].copy_from_slice(payload);
net_trace!("{}:{}:{}: receiving {} octets", net_trace!(
self.meta.handle, self.ip_version, self.ip_protocol, "{}:{}:{}: receiving {} octets",
packet_buf.len()); self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
#[cfg(feature = "async")] #[cfg(feature = "async")]
self.rx_waker.wake(); self.rx_waker.wake();
@ -226,14 +245,21 @@ impl<'a> RawSocket<'a> {
} }
pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()> pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
where F: FnOnce((IpRepr, &[u8])) -> Result<()> { where
fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8], F: FnOnce((IpRepr, &[u8])) -> Result<()>,
_checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> { {
fn prepare<'a>(
protocol: IpProtocol,
buffer: &'a mut [u8],
_checksum_caps: &ChecksumCapabilities,
) -> Result<(IpRepr, &'a [u8])> {
match IpVersion::of_packet(buffer)? { match IpVersion::of_packet(buffer)? {
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
IpVersion::Ipv4 => { IpVersion::Ipv4 => {
let mut packet = Ipv4Packet::new_checked(buffer)?; 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() { if _checksum_caps.ipv4.tx() {
packet.fill_checksum(); packet.fill_checksum();
} else { } else {
@ -249,7 +275,9 @@ impl<'a> RawSocket<'a> {
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
IpVersion::Ipv6 => { IpVersion::Ipv6 => {
let packet = Ipv6Packet::new_checked(buffer)?; 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 packet = Ipv6Packet::new_unchecked(&*packet.into_inner());
let ipv6_repr = Ipv6Repr::parse(&packet)?; let ipv6_repr = Ipv6Repr::parse(&packet)?;
Ok((IpRepr::Ipv6(ipv6_repr), packet.payload())) 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_protocol = self.ip_protocol;
let ip_version = self.ip_version; let ip_version = self.ip_version;
self.tx_buffer.dequeue_with(|&mut (), packet_buf| { self.tx_buffer.dequeue_with(|&mut (), packet_buf| {
match prepare(ip_protocol, packet_buf, &cx.caps.checksum) { match prepare(ip_protocol, packet_buf, &cx.caps.checksum) {
Ok((ip_repr, raw_packet)) => { Ok((ip_repr, raw_packet)) => {
net_trace!("{}:{}:{}: sending {} octets", net_trace!(
handle, ip_version, ip_protocol, "{}:{}:{}: sending {} octets",
ip_repr.buffer_len() + raw_packet.len()); handle,
ip_version,
ip_protocol,
ip_repr.buffer_len() + raw_packet.len()
);
emit((ip_repr, raw_packet)) emit((ip_repr, raw_packet))
} }
Err(error) => { Err(error) => {
net_debug!("{}:{}:{}: dropping outgoing packet ({})", net_debug!(
handle, ip_version, ip_protocol, "{}:{}:{}: dropping outgoing packet ({})",
error); handle,
ip_version,
ip_protocol,
error
);
// Return Ok(()) so the packet is dequeued. // Return Ok(()) so the packet is dequeued.
Ok(()) Ok(())
} }
@ -302,26 +338,34 @@ impl<'a> Into<Socket<'a>> for RawSocket<'a> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*;
use crate::wire::IpRepr; use crate::wire::IpRepr;
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Address, Ipv4Repr}; use crate::wire::{Ipv4Address, Ipv4Repr};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Address, Ipv6Repr}; use crate::wire::{Ipv6Address, Ipv6Repr};
use super::*;
fn buffer(packets: usize) -> RawSocketBuffer<'static> { 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")] #[cfg(feature = "proto-ipv4")]
mod ipv4_locals { mod ipv4_locals {
use super::*; use super::*;
pub fn socket(rx_buffer: RawSocketBuffer<'static>, pub fn socket(
tx_buffer: RawSocketBuffer<'static>) rx_buffer: RawSocketBuffer<'static>,
-> RawSocket<'static> { tx_buffer: RawSocketBuffer<'static>,
RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO), ) -> RawSocket<'static> {
rx_buffer, tx_buffer) RawSocket::new(
IpVersion::Ipv4,
IpProtocol::Unknown(IP_PROTO),
rx_buffer,
tx_buffer,
)
} }
pub const IP_PROTO: u8 = 63; pub const IP_PROTO: u8 = 63;
@ -330,60 +374,54 @@ mod test {
dst_addr: Ipv4Address([10, 0, 0, 2]), dst_addr: Ipv4Address([10, 0, 0, 2]),
protocol: IpProtocol::Unknown(IP_PROTO), protocol: IpProtocol::Unknown(IP_PROTO),
payload_len: 4, payload_len: 4,
hop_limit: 64 hop_limit: 64,
}); });
pub const PACKET_BYTES: [u8; 24] = [ pub const PACKET_BYTES: [u8; 24] = [
0x45, 0x00, 0x00, 0x18, 0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x3f, 0x00, 0x00, 0x0a, 0x00,
0x00, 0x00, 0x40, 0x00, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00, 0xff,
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
]; ];
pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
} }
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
mod ipv6_locals { mod ipv6_locals {
use super::*; use super::*;
pub fn socket(rx_buffer: RawSocketBuffer<'static>, pub fn socket(
tx_buffer: RawSocketBuffer<'static>) rx_buffer: RawSocketBuffer<'static>,
-> RawSocket<'static> { tx_buffer: RawSocketBuffer<'static>,
RawSocket::new(IpVersion::Ipv6, IpProtocol::Unknown(IP_PROTO), ) -> RawSocket<'static> {
rx_buffer, tx_buffer) RawSocket::new(
IpVersion::Ipv6,
IpProtocol::Unknown(IP_PROTO),
rx_buffer,
tx_buffer,
)
} }
pub const IP_PROTO: u8 = 63; pub const IP_PROTO: u8 = 63;
pub const HEADER_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr { pub const HEADER_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, src_addr: Ipv6Address([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]), 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]), ]),
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), next_header: IpProtocol::Unknown(IP_PROTO),
payload_len: 4, payload_len: 4,
hop_limit: 64 hop_limit: 64,
}); });
pub const PACKET_BYTES: [u8; 44] = [ pub const PACKET_BYTES: [u8; 44] = [
0x60, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3f, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04, 0x3f, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xaa, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
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] = [ pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
0xaa, 0x00, 0x00, 0xff
];
} }
macro_rules! reusable_ip_specific_tests { macro_rules! reusable_ip_specific_tests {
@ -402,25 +440,33 @@ mod test {
let mut socket = $socket(buffer(0), buffer(1)); let mut socket = $socket(buffer(0), buffer(1));
assert!(socket.can_send()); assert!(socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), assert_eq!(
Err(Error::Exhausted)); socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted)
);
assert_eq!(socket.send_slice(&$packet[..]), Ok(())); assert_eq!(socket.send_slice(&$packet[..]), Ok(()));
assert_eq!(socket.send_slice(b""), Err(Error::Exhausted)); assert_eq!(socket.send_slice(b""), Err(Error::Exhausted));
assert!(!socket.can_send()); assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| { assert_eq!(
assert_eq!(ip_repr, $hdr); socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
assert_eq!(ip_payload, &$payload); assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
Err(Error::Unaddressable)
}),
Err(Error::Unaddressable) Err(Error::Unaddressable)
}), Err(Error::Unaddressable)); );
assert!(!socket.can_send()); assert!(!socket.can_send());
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| { assert_eq!(
assert_eq!(ip_repr, $hdr); socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
assert_eq!(ip_payload, &$payload); assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
Ok(())
}),
Ok(()) Ok(())
}), Ok(())); );
assert!(socket.can_send()); assert!(socket.can_send());
} }
@ -444,20 +490,32 @@ mod test {
buffer[..$packet.len()].copy_from_slice(&$packet[..]); buffer[..$packet.len()].copy_from_slice(&$packet[..]);
assert!(socket.accepts(&$hdr)); 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")] #[cfg(feature = "proto-ipv4")]
reusable_ip_specific_tests!(ipv4, ipv4_locals::socket, ipv4_locals::HEADER_REPR, reusable_ip_specific_tests!(
ipv4_locals::PACKET_BYTES, ipv4_locals::PACKET_PAYLOAD); ipv4,
ipv4_locals::socket,
ipv4_locals::HEADER_REPR,
ipv4_locals::PACKET_BYTES,
ipv4_locals::PACKET_PAYLOAD
);
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
reusable_ip_specific_tests!(ipv6, ipv6_locals::socket, ipv6_locals::HEADER_REPR, reusable_ip_specific_tests!(
ipv6_locals::PACKET_BYTES, ipv6_locals::PACKET_PAYLOAD); ipv6,
ipv6_locals::socket,
ipv6_locals::HEADER_REPR,
ipv6_locals::PACKET_BYTES,
ipv6_locals::PACKET_PAYLOAD
);
#[test] #[test]
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
@ -470,15 +528,13 @@ mod test {
Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6); Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6);
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
Ok(()));
let mut wrong_protocol = ipv4_locals::PACKET_BYTES; let mut wrong_protocol = ipv4_locals::PACKET_BYTES;
Ipv4Packet::new_unchecked(&mut wrong_protocol).set_protocol(IpProtocol::Tcp); Ipv4Packet::new_unchecked(&mut wrong_protocol).set_protocol(IpProtocol::Tcp);
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(())); assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
Ok(()));
} }
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
{ {
@ -488,15 +544,13 @@ mod test {
Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4); Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4);
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(())); assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
Ok(()));
let mut wrong_protocol = ipv6_locals::PACKET_BYTES; let mut wrong_protocol = ipv6_locals::PACKET_BYTES;
Ipv6Packet::new_unchecked(&mut wrong_protocol[..]).set_next_header(IpProtocol::Tcp); Ipv6Packet::new_unchecked(&mut wrong_protocol[..]).set_next_header(IpProtocol::Tcp);
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(())); assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
Ok(()));
} }
} }
@ -512,13 +566,25 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted)); assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD), assert_eq!(
Ok(())); socket.process(
&Context::DUMMY,
&ipv4_locals::HEADER_REPR,
&ipv4_locals::PACKET_PAYLOAD
),
Ok(())
);
assert!(socket.can_recv()); assert!(socket.can_recv());
assert!(socket.accepts(&ipv4_locals::HEADER_REPR)); assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD), assert_eq!(
Err(Error::Exhausted)); socket.process(
&Context::DUMMY,
&ipv4_locals::HEADER_REPR,
&ipv4_locals::PACKET_PAYLOAD
),
Err(Error::Exhausted)
);
assert_eq!(socket.recv(), Ok(&cksumd_packet[..])); assert_eq!(socket.recv(), Ok(&cksumd_packet[..]));
assert!(!socket.can_recv()); assert!(!socket.can_recv());
} }
@ -529,13 +595,25 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted)); assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD), assert_eq!(
Ok(())); socket.process(
&Context::DUMMY,
&ipv6_locals::HEADER_REPR,
&ipv6_locals::PACKET_PAYLOAD
),
Ok(())
);
assert!(socket.can_recv()); assert!(socket.can_recv());
assert!(socket.accepts(&ipv6_locals::HEADER_REPR)); assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD), assert_eq!(
Err(Error::Exhausted)); 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_eq!(socket.recv(), Ok(&ipv6_locals::PACKET_BYTES[..]));
assert!(!socket.can_recv()); assert!(!socket.can_recv());
} }
@ -545,16 +623,24 @@ mod test {
fn test_doesnt_accept_wrong_proto() { fn test_doesnt_accept_wrong_proto() {
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
{ {
let socket = RawSocket::new(IpVersion::Ipv4, let socket = RawSocket::new(
IpProtocol::Unknown(ipv4_locals::IP_PROTO+1), buffer(1), buffer(1)); IpVersion::Ipv4,
IpProtocol::Unknown(ipv4_locals::IP_PROTO + 1),
buffer(1),
buffer(1),
);
assert!(!socket.accepts(&ipv4_locals::HEADER_REPR)); assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
assert!(!socket.accepts(&ipv6_locals::HEADER_REPR)); assert!(!socket.accepts(&ipv6_locals::HEADER_REPR));
} }
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
{ {
let socket = RawSocket::new(IpVersion::Ipv6, let socket = RawSocket::new(
IpProtocol::Unknown(ipv6_locals::IP_PROTO+1), buffer(1), buffer(1)); IpVersion::Ipv6,
IpProtocol::Unknown(ipv6_locals::IP_PROTO + 1),
buffer(1),
buffer(1),
);
assert!(!socket.accepts(&ipv6_locals::HEADER_REPR)); assert!(!socket.accepts(&ipv6_locals::HEADER_REPR));
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
assert!(!socket.accepts(&ipv4_locals::HEADER_REPR)); assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -30,4 +30,4 @@ impl WakerRegistration {
pub fn wake(&mut self) { pub fn wake(&mut self) {
self.waker.take().map(|w| w.wake()); self.waker.take().map(|w| w.wake());
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian}; use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::wire::ip::checksum;
use crate::time::Duration; use crate::time::Duration;
use crate::wire::ip::checksum;
use crate::{Error, Result};
use crate::wire::Ipv4Address; use crate::wire::Ipv4Address;
@ -202,7 +202,8 @@ impl Repr {
/// Parse an Internet Group Management Protocol v1/v2 packet and return /// Parse an Internet Group Management Protocol v1/v2 packet and return
/// a high-level representation. /// a high-level representation.
pub fn parse<T>(packet: &Packet<&T>) -> Result<Repr> 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 // Check if the address is 0.0.0.0 or multicast
let addr = packet.group_addr(); let addr = packet.group_addr();
@ -226,13 +227,13 @@ impl Repr {
version, version,
}) })
} }
Message::MembershipReportV2 => { Message::MembershipReportV2 => Ok(Repr::MembershipReport {
Ok(Repr::MembershipReport { group_addr: packet.group_addr(),
group_addr: packet.group_addr(), version: IgmpVersion::Version2,
version: IgmpVersion::Version2, }),
}) Message::LeaveGroup => Ok(Repr::LeaveGroup {
} group_addr: packet.group_addr(),
Message::LeaveGroup => Ok(Repr::LeaveGroup { group_addr: packet.group_addr() }), }),
Message::MembershipReportV1 => { Message::MembershipReportV1 => {
// for backwards compatibility with IGMPv1 // for backwards compatibility with IGMPv1
Ok(Repr::MembershipReport { Ok(Repr::MembershipReport {
@ -252,20 +253,21 @@ impl Repr {
/// Emit a high-level representation into an Internet Group Management Protocol v2 packet. /// Emit a high-level representation into an Internet Group Management Protocol v2 packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>) 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 { match *self {
Repr::MembershipQuery { Repr::MembershipQuery {
max_resp_time, max_resp_time,
group_addr, group_addr,
version version,
} => { } => {
packet.set_msg_type(Message::MembershipQuery); packet.set_msg_type(Message::MembershipQuery);
match version { match version {
IgmpVersion::Version1 => IgmpVersion::Version1 => packet.set_max_resp_code(0),
packet.set_max_resp_code(0), IgmpVersion::Version2 => {
IgmpVersion::Version2 => packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time))
packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time)), }
} }
packet.set_group_address(group_addr); packet.set_group_address(group_addr);
} }
@ -336,20 +338,21 @@ impl<'a> fmt::Display for Repr {
group_addr, group_addr,
version, version,
} => { } => {
write!(f, write!(
"IGMP membership query max_resp_time={} group_addr={} version={:?}", f,
max_resp_time, "IGMP membership query max_resp_time={} group_addr={} version={:?}",
group_addr, max_resp_time, group_addr, version
version) )
} }
Repr::MembershipReport { Repr::MembershipReport {
group_addr, group_addr,
version, version,
} => { } => {
write!(f, write!(
"IGMP membership report group_addr={} version={:?}", f,
group_addr, "IGMP membership report group_addr={} version={:?}",
version) group_addr, version
)
} }
Repr::LeaveGroup { group_addr } => { Repr::LeaveGroup { group_addr } => {
write!(f, "IGMP leave group group_addr={})", 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}; use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> { impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, fn pretty_print(
f: &mut fmt::Formatter, buffer: &dyn AsRef<[u8]>,
indent: &mut PrettyIndent) f: &mut fmt::Formatter,
-> fmt::Result { indent: &mut PrettyIndent,
) -> fmt::Result {
match Packet::new_checked(buffer) { match Packet::new_checked(buffer) {
Err(err) => writeln!(f, "{}({})", indent, err), Err(err) => writeln!(f, "{}({})", indent, err),
Ok(packet) => writeln!(f, "{}{}", indent, packet), Ok(packet) => writeln!(f, "{}{}", indent, packet),
@ -376,7 +380,6 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test { mod test {
use super::*; use super::*;
static LEAVE_PACKET_BYTES: [u8; 8] = [0x17, 0x00, 0x02, 0x69, 0xe0, 0x00, 0x06, 0x96]; 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]; 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.msg_type(), Message::LeaveGroup);
assert_eq!(packet.max_resp_code(), 0); assert_eq!(packet.max_resp_code(), 0);
assert_eq!(packet.checksum(), 0x269); assert_eq!(packet.checksum(), 0x269);
assert_eq!(packet.group_addr(), assert_eq!(
Ipv4Address::from_bytes(&[224, 0, 6, 150])); packet.group_addr(),
Ipv4Address::from_bytes(&[224, 0, 6, 150])
);
assert_eq!(packet.verify_checksum(), true); assert_eq!(packet.verify_checksum(), true);
} }
@ -397,8 +402,10 @@ mod test {
assert_eq!(packet.msg_type(), Message::MembershipReportV2); assert_eq!(packet.msg_type(), Message::MembershipReportV2);
assert_eq!(packet.max_resp_code(), 0); assert_eq!(packet.max_resp_code(), 0);
assert_eq!(packet.checksum(), 0x08da); assert_eq!(packet.checksum(), 0x08da);
assert_eq!(packet.group_addr(), assert_eq!(
Ipv4Address::from_bytes(&[225, 0, 0, 37])); packet.group_addr(),
Ipv4Address::from_bytes(&[225, 0, 0, 37])
);
assert_eq!(packet.verify_checksum(), true); assert_eq!(packet.verify_checksum(), true);
} }
@ -437,9 +444,7 @@ mod test {
#[test] #[test]
fn duration_to_max_resp_time_max() { fn duration_to_max_resp_time_max() {
for duration in 31744..65536 { for duration in 31744..65536 {
let time = duration_to_max_resp_code( let time = duration_to_max_resp_code(Duration::from_millis(duration * 100));
Duration::from_millis(duration * 100)
);
assert_eq!(time, 0xFF); 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 byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities; use crate::phy::ChecksumCapabilities;
use crate::wire::ip::{checksum, pretty_print_ip_payload}; use crate::wire::ip::{checksum, pretty_print_ip_payload};
use crate::{Error, Result};
pub use super::IpProtocol as Protocol; pub use super::IpProtocol as Protocol;
@ -27,10 +27,10 @@ pub struct Address(pub [u8; 4]);
impl Address { impl Address {
/// An unspecified address. /// An unspecified address.
pub const UNSPECIFIED: Address = Address([0x00; 4]); pub const UNSPECIFIED: Address = Address([0x00; 4]);
/// The broadcast address. /// The broadcast address.
pub const BROADCAST: Address = Address([0xff; 4]); pub const BROADCAST: Address = Address([0xff; 4]);
/// All multicast-capable nodes /// All multicast-capable nodes
pub const MULTICAST_ALL_SYSTEMS: Address = Address([224, 0, 0, 1]); 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. /// Query whether the address is an unicast address.
pub fn is_unicast(&self) -> bool { pub fn is_unicast(&self) -> bool {
!(self.is_broadcast() || !(self.is_broadcast() || self.is_multicast() || self.is_unspecified())
self.is_multicast() ||
self.is_unspecified())
} }
/// Query whether the address is the broadcast address. /// Query whether the address is the broadcast address.
@ -115,7 +113,14 @@ impl fmt::Display for Address {
#[cfg(feature = "defmt")] #[cfg(feature = "defmt")]
impl defmt::Format for Address { impl defmt::Format for Address {
fn format(&self, f: defmt::Formatter) { 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. /// subnet masking prefix length.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)] #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct Cidr { pub struct Cidr {
address: Address, address: Address,
prefix_len: u8, prefix_len: u8,
} }
@ -137,14 +142,20 @@ impl Cidr {
// Replace with const panic (or assert) when stabilized // Replace with const panic (or assert) when stabilized
// see: https://github.com/rust-lang/rust/issues/51999 // see: https://github.com/rust-lang/rust/issues/51999
["Prefix length should be <= 32"][(prefix_len > 32) as usize]; ["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. /// Create an IPv4 CIDR block from the given address and network mask.
pub fn from_netmask(addr: Address, netmask: Address) -> Result<Cidr> { pub fn from_netmask(addr: Address, netmask: Address) -> Result<Cidr> {
let netmask = NetworkEndian::read_u32(&netmask.0[..]); let netmask = NetworkEndian::read_u32(&netmask.0[..]);
if netmask.leading_zeros() == 0 && netmask.trailing_zeros() == netmask.count_zeros() { 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 { } else {
Err(Error::Illegal) Err(Error::Illegal)
} }
@ -170,8 +181,8 @@ impl Cidr {
let data = [ let data = [
((number >> 24) & 0xff) as u8, ((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8, ((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8, ((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8, ((number >> 0) & 0xff) as u8,
]; ];
Address(data) Address(data)
@ -190,8 +201,8 @@ impl Cidr {
let data = [ let data = [
((number >> 24) & 0xff) as u8, ((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8, ((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8, ((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8, ((number >> 0) & 0xff) as u8,
]; ];
Some(Address(data)) Some(Address(data))
@ -206,14 +217,19 @@ impl Cidr {
self.address.0[2] & mask[2], self.address.0[2] & mask[2],
self.address.0[3] & mask[3], 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 /// Query whether the subnetwork described by this IPv4 CIDR block contains
/// the given address. /// the given address.
pub fn contains_addr(&self, addr: &Address) -> bool { pub fn contains_addr(&self, addr: &Address) -> bool {
// right shift by 32 is not legal // 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 shift = 32 - self.prefix_len;
let self_prefix = NetworkEndian::read_u32(self.address.as_bytes()) >> shift; let self_prefix = NetworkEndian::read_u32(self.address.as_bytes()) >> shift;
@ -245,18 +261,18 @@ impl defmt::Format for Cidr {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> { pub struct Packet<T: AsRef<[u8]>> {
buffer: T buffer: T,
} }
mod field { mod field {
use crate::wire::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 DSCP_ECN: usize = 1;
pub const LENGTH: Field = 2..4; pub const LENGTH: Field = 2..4;
pub const IDENT: Field = 4..6; pub const IDENT: Field = 4..6;
pub const FLG_OFF: Field = 6..8; pub const FLG_OFF: Field = 6..8;
pub const TTL: usize = 8; pub const TTL: usize = 8;
pub const PROTOCOL: usize = 9; pub const PROTOCOL: usize = 9;
pub const CHECKSUM: Field = 10..12; pub const CHECKSUM: Field = 10..12;
pub const SRC_ADDR: Field = 12..16; pub const SRC_ADDR: Field = 12..16;
@ -265,7 +281,6 @@ mod field {
pub const HEADER_LEN: usize = field::DST_ADDR.end; pub const HEADER_LEN: usize = field::DST_ADDR.end;
impl<T: AsRef<[u8]>> Packet<T> { impl<T: AsRef<[u8]>> Packet<T> {
/// Imbue a raw octet buffer with IPv4 packet structure. /// Imbue a raw octet buffer with IPv4 packet structure.
pub fn new_unchecked(buffer: T) -> Packet<T> { pub fn new_unchecked(buffer: T) -> Packet<T> {
@ -414,7 +429,9 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing /// # Fuzzing
/// This function always returns `true` when fuzzing. /// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self) -> bool { pub fn verify_checksum(&self) -> bool {
if cfg!(fuzzing) { return true } if cfg!(fuzzing) {
return true;
}
let data = self.buffer.as_ref(); let data = self.buffer.as_ref();
checksum::data(&data[..self.header_len() as usize]) == !0 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)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr { pub struct Repr {
pub src_addr: Address, pub src_addr: Address,
pub dst_addr: Address, pub dst_addr: Address,
pub protocol: Protocol, pub protocol: Protocol,
pub payload_len: usize, pub payload_len: usize,
pub hop_limit: u8 pub hop_limit: u8,
} }
impl Repr { impl Repr {
/// Parse an Internet Protocol version 4 packet and return a high-level representation. /// Parse an Internet Protocol version 4 packet and return a high-level representation.
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>, pub fn parse<T: AsRef<[u8]> + ?Sized>(
checksum_caps: &ChecksumCapabilities) -> Result<Repr> { packet: &Packet<&T>,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr> {
// Version 4 is expected. // Version 4 is expected.
if packet.version() != 4 { return Err(Error::Malformed) } if packet.version() != 4 {
return Err(Error::Malformed);
}
// Valid checksum is expected. // 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. // 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. // 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; 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 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 ECN values are acceptable, since ECN requires opt-in from both endpoints.
// All TTL values are acceptable, since we do not perform routing. // All TTL values are acceptable, since we do not perform routing.
Ok(Repr { Ok(Repr {
src_addr: packet.src_addr(), src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(), dst_addr: packet.dst_addr(),
protocol: packet.protocol(), protocol: packet.protocol(),
payload_len: payload_len, 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. /// 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_version(4);
packet.set_header_len(field::DST_ADDR.end as u8); packet.set_header_len(field::DST_ADDR.end as u8);
packet.set_dscp(0); 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), Ok(repr) => write!(f, "{}", repr),
Err(err) => { Err(err) => {
write!(f, "IPv4 ({})", err)?; write!(f, "IPv4 ({})", err)?;
write!(f, " src={} dst={} proto={} hop_limit={}", write!(
self.src_addr(), self.dst_addr(), self.protocol(), self.hop_limit())?; f,
" src={} dst={} proto={} hop_limit={}",
self.src_addr(),
self.dst_addr(),
self.protocol(),
self.hop_limit()
)?;
if self.version() != 4 { if self.version() != 4 {
write!(f, " ver={}", self.version())?; 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 { impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv4 src={} dst={} proto={}", write!(
self.src_addr, self.dst_addr, self.protocol) 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> { impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, fn pretty_print(
indent: &mut PrettyIndent) -> fmt::Result { buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
use crate::wire::ip::checksum::format_checksum; use crate::wire::ip::checksum::format_checksum;
let checksum_caps = ChecksumCapabilities::ignored(); let checksum_caps = ChecksumCapabilities::ignored();
let (ip_repr, payload) = match Packet::new_checked(buffer) { let (ip_repr, payload) = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err), Err(err) => return write!(f, "{}({})", indent, err),
Ok(ip_packet) => { Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) {
match Repr::parse(&ip_packet, &checksum_caps) { Err(_) => return Ok(()),
Err(_) => return Ok(()), Ok(ip_repr) => {
Ok(ip_repr) => { write!(f, "{}{}", indent, ip_repr)?;
write!(f, "{}{}", indent, ip_repr)?; format_checksum(f, ip_packet.verify_checksum())?;
format_checksum(f, ip_packet.verify_checksum())?; (ip_repr, ip_packet.payload())
(ip_repr, ip_packet.payload())
}
} }
} },
}; };
pretty_print_ip_payload(f, indent, ip_repr, payload) pretty_print_ip_payload(f, indent, ip_repr, payload)
@ -716,20 +757,12 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test { mod test {
use super::*; use super::*;
static PACKET_BYTES: [u8; 30] = static PACKET_BYTES: [u8; 30] = [
[0x45, 0x00, 0x00, 0x1e, 0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12, 0x13,
0x01, 0x02, 0x62, 0x03, 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
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] = static PAYLOAD_BYTES: [u8; 10] = [0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff];
[0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
#[test] #[test]
fn test_deconstruct() { fn test_deconstruct() {
@ -781,10 +814,14 @@ mod test {
bytes.extend(&PACKET_BYTES[..]); bytes.extend(&PACKET_BYTES[..]);
bytes.push(0); bytes.push(0);
assert_eq!(Packet::new_unchecked(&bytes).payload().len(), assert_eq!(
PAYLOAD_BYTES.len()); Packet::new_unchecked(&bytes).payload().len(),
assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(), PAYLOAD_BYTES.len()
PAYLOAD_BYTES.len()); );
assert_eq!(
Packet::new_unchecked(&mut bytes).payload_mut().len(),
PAYLOAD_BYTES.len()
);
} }
#[test] #[test]
@ -793,28 +830,23 @@ mod test {
bytes.extend(&PACKET_BYTES[..]); bytes.extend(&PACKET_BYTES[..]);
Packet::new_unchecked(&mut bytes).set_total_len(128); Packet::new_unchecked(&mut bytes).set_total_len(128);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated);
Error::Truncated);
} }
static REPR_PACKET_BYTES: [u8; 24] = static REPR_PACKET_BYTES: [u8; 24] = [
[0x45, 0x00, 0x00, 0x18, 0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x79, 0x11, 0x12, 0x13,
0x00, 0x00, 0x40, 0x00, 0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0xff,
0x40, 0x01, 0xd2, 0x79, ];
0x11, 0x12, 0x13, 0x14,
0x21, 0x22, 0x23, 0x24,
0xaa, 0x00, 0x00, 0xff];
static REPR_PAYLOAD_BYTES: [u8; 4] = static REPR_PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
[0xaa, 0x00, 0x00, 0xff];
fn packet_repr() -> Repr { fn packet_repr() -> Repr {
Repr { Repr {
src_addr: Address([0x11, 0x12, 0x13, 0x14]), src_addr: Address([0x11, 0x12, 0x13, 0x14]),
dst_addr: Address([0x21, 0x22, 0x23, 0x24]), dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
protocol: Protocol::Icmp, protocol: Protocol::Icmp,
payload_len: 4, payload_len: 4,
hop_limit: 64 hop_limit: 64,
} }
} }
@ -833,7 +865,10 @@ mod test {
packet.set_version(6); packet.set_version(6);
packet.fill_checksum(); packet.fill_checksum();
let packet = Packet::new_unchecked(&*packet.into_inner()); 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] #[test]
@ -876,28 +911,34 @@ mod test {
let cidr = Cidr::new(Address::new(192, 168, 1, 10), 24); let cidr = Cidr::new(Address::new(192, 168, 1, 10), 24);
let inside_subnet = [ let inside_subnet = [
[192, 168, 1, 0], [192, 168, 1, 1], [192, 168, 1, 0],
[192, 168, 1, 2], [192, 168, 1, 10], [192, 168, 1, 1],
[192, 168, 1, 127], [192, 168, 1, 255], [192, 168, 1, 2],
[192, 168, 1, 10],
[192, 168, 1, 127],
[192, 168, 1, 255],
]; ];
let outside_subnet = [ let outside_subnet = [
[192, 168, 0, 0], [127, 0, 0, 1], [192, 168, 0, 0],
[192, 168, 2, 0], [192, 168, 0, 255], [127, 0, 0, 1],
[ 0, 0, 0, 0], [255, 255, 255, 255], [192, 168, 2, 0],
[192, 168, 0, 255],
[0, 0, 0, 0],
[255, 255, 255, 255],
]; ];
let subnets = [ let subnets = [
([192, 168, 1, 0], 32), ([192, 168, 1, 0], 32),
([192, 168, 1, 255], 24), ([192, 168, 1, 255], 24),
([192, 168, 1, 10], 30), ([192, 168, 1, 10], 30),
]; ];
let not_subnets = [ let not_subnets = [
([192, 168, 1, 10], 23), ([192, 168, 1, 10], 23),
([127, 0, 0, 1], 8), ([127, 0, 0, 1], 8),
([192, 168, 1, 0], 0), ([192, 168, 1, 0], 0),
([192, 168, 0, 255], 32), ([192, 168, 0, 255], 32),
]; ];
for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) { for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
@ -908,13 +949,17 @@ mod test {
assert!(!cidr.contains_addr(&addr)); assert!(!cidr.contains_addr(&addr));
} }
for subnet in subnets.iter().map( for subnet in subnets
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) { .iter()
.map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p))
{
assert!(cidr.contains_subnet(&subnet)); assert!(cidr.contains_subnet(&subnet));
} }
for subnet in not_subnets.iter().map( for subnet in not_subnets
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) { .iter()
.map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p))
{
assert!(!cidr.contains_subnet(&subnet)); assert!(!cidr.contains_subnet(&subnet));
} }
@ -924,94 +969,175 @@ mod test {
#[test] #[test]
fn test_cidr_from_netmask() { fn test_cidr_from_netmask() {
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(), assert_eq!(
true); Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(),
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(), true
true); );
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(), assert_eq!(
Cidr::new(Address([0, 0, 0, 1]), 24)); Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(),
assert_eq!(Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(), true
Cidr::new(Address([192, 168, 0, 1]), 16)); );
assert_eq!(Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(), assert_eq!(
Cidr::new(Address([172, 16, 0, 1]), 12)); Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(),
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(), Cidr::new(Address([0, 0, 0, 1]), 24)
Cidr::new(Address([255, 255, 255, 1]), 24)); );
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255])).unwrap(), assert_eq!(
Cidr::new(Address([255, 255, 255, 255]), 32)); 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] #[test]
fn test_cidr_netmask() { fn test_cidr_netmask() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).netmask(), assert_eq!(
Address([0, 0, 0, 0])); Cidr::new(Address([0, 0, 0, 0]), 0).netmask(),
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).netmask(), Address([0, 0, 0, 0])
Address([255, 255, 255, 0])); );
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).netmask(), assert_eq!(
Address([255, 255, 255, 255])); Cidr::new(Address([0, 0, 0, 1]), 24).netmask(),
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).netmask(), Address([255, 255, 255, 0])
Address([255, 0, 0, 0])); );
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).netmask(), assert_eq!(
Address([255, 255, 0, 0])); Cidr::new(Address([0, 0, 0, 0]), 32).netmask(),
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).netmask(), Address([255, 255, 255, 255])
Address([255, 255, 0, 0])); );
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).netmask(), assert_eq!(
Address([255, 255, 128, 0])); Cidr::new(Address([127, 0, 0, 0]), 8).netmask(),
assert_eq!(Cidr::new(Address([172, 16, 0, 0]), 12).netmask(), Address([255, 0, 0, 0])
Address([255, 240, 0, 0])); );
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).netmask(), assert_eq!(
Address([255, 255, 255, 0])); Cidr::new(Address([192, 168, 0, 0]), 16).netmask(),
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).netmask(), Address([255, 255, 0, 0])
Address([255, 255, 255, 255])); );
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] #[test]
fn test_cidr_broadcast() { fn test_cidr_broadcast() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(), assert_eq!(
Address([255, 255, 255, 255])); Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(),
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(), Address([255, 255, 255, 255])
Address([0, 0, 0, 255])); );
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(), assert_eq!(
None); Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(),
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(), Address([0, 0, 0, 255])
Address([127, 255, 255, 255])); );
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).broadcast().unwrap(), assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(), None);
Address([192, 168, 255, 255])); assert_eq!(
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).broadcast().unwrap(), Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(),
Address([192, 168, 255, 255])); Address([127, 255, 255, 255])
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).broadcast().unwrap(), );
Address([192, 168, 127, 255])); assert_eq!(
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(), Cidr::new(Address([192, 168, 0, 0]), 16)
Address([172, 31, 255, 255])); .broadcast()
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).broadcast().unwrap(), .unwrap(),
Address([255, 255, 255, 255])); Address([192, 168, 255, 255])
assert_eq!(Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(), );
None); assert_eq!(
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(), Cidr::new(Address([192, 168, 1, 1]), 16)
None); .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] #[test]
fn test_cidr_network() { fn test_cidr_network() {
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).network(), assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 0)); Cidr::new(Address([0, 0, 0, 0]), 0).network(),
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).network(), Cidr::new(Address([0, 0, 0, 0]), 0)
Cidr::new(Address([0, 0, 0, 0]), 24)); );
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).network(), assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 32)); Cidr::new(Address([0, 0, 0, 1]), 24).network(),
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).network(), Cidr::new(Address([0, 0, 0, 0]), 24)
Cidr::new(Address([127, 0, 0, 0]), 8)); );
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).network(), assert_eq!(
Cidr::new(Address([192, 168, 0, 0]), 16)); Cidr::new(Address([0, 0, 0, 0]), 32).network(),
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).network(), Cidr::new(Address([0, 0, 0, 0]), 32)
Cidr::new(Address([192, 168, 0, 0]), 16)); );
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).network(), assert_eq!(
Cidr::new(Address([192, 168, 0, 0]), 17)); Cidr::new(Address([127, 0, 0, 0]), 8).network(),
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).network(), Cidr::new(Address([127, 0, 0, 0]), 8)
Cidr::new(Address([172, 16, 0, 0]), 12)); );
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).network(), assert_eq!(
Cidr::new(Address([255, 255, 255, 0]), 24)); Cidr::new(Address([192, 168, 0, 0]), 16).network(),
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).network(), Cidr::new(Address([192, 168, 0, 0]), 16)
Cidr::new(Address([255, 255, 255, 255]), 32)); );
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)] #![deny(missing_docs)]
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian}; use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::wire::ip::pretty_print_ip_payload; use crate::wire::ip::pretty_print_ip_payload;
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
use crate::wire::ipv4; use crate::wire::ipv4;
use crate::{Error, Result};
pub use super::IpProtocol as Protocol; pub use super::IpProtocol as Protocol;
@ -29,28 +29,30 @@ impl Address {
/// The link-local [all routers multicast address]. /// The link-local [all routers multicast address].
/// ///
/// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1 /// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const LINK_LOCAL_ALL_NODES: Address = pub const LINK_LOCAL_ALL_NODES: Address = Address([
Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); 0x01,
]);
/// The link-local [all nodes multicast address]. /// The link-local [all nodes multicast address].
/// ///
/// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1 /// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const LINK_LOCAL_ALL_ROUTERS: Address = pub const LINK_LOCAL_ALL_ROUTERS: Address = Address([
Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]); 0x02,
]);
/// The [loopback address]. /// The [loopback address].
/// ///
/// [loopback address]: https://tools.ietf.org/html/rfc4291#section-2.5.3 /// [loopback address]: https://tools.ietf.org/html/rfc4291#section-2.5.3
pub const LOOPBACK: Address = pub const LOOPBACK: Address = Address([
Address([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); 0x01,
]);
/// Construct an IPv6 address from parts. /// Construct an IPv6 address from parts.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
let mut addr = [0u8; 16]; let mut addr = [0u8; 16];
NetworkEndian::write_u16(&mut addr[0..2], a0); NetworkEndian::write_u16(&mut addr[0..2], a0);
NetworkEndian::write_u16(&mut addr[2..4], a1); 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 /// [link-local]: https://tools.ietf.org/html/rfc4291#section-2.5.6
pub fn is_link_local(&self) -> bool { pub fn is_link_local(&self) -> bool {
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00, self.0[0..8] == [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
0x00, 0x00, 0x00, 0x00]
} }
/// Query whether the IPv6 address is the [loopback address]. /// 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. /// Convert an IPv4 mapped IPv6 address to an IPv4 address.
pub fn as_ipv4(&self) -> Option<ipv4::Address> { pub fn as_ipv4(&self) -> Option<ipv4::Address> {
if self.is_ipv4_mapped() { 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 { } else {
None None
} }
@ -165,7 +168,7 @@ impl Address {
let idx = (mask as usize) / 8; let idx = (mask as usize) / 8;
let modulus = (mask as usize) % 8; let modulus = (mask as usize) % 8;
let (first, second) = self.0.split_at(idx); let (first, second) = self.0.split_at(idx);
bytes[0..idx].copy_from_slice(&first); bytes[0..idx].copy_from_slice(first);
if idx < 16 { if idx < 16 {
let part = second[0]; let part = second[0];
bytes[idx] = part & (!(0xff >> modulus) as u8); bytes[idx] = part & (!(0xff >> modulus) as u8);
@ -180,8 +183,10 @@ impl Address {
/// unicast. /// unicast.
pub fn solicited_node(&self) -> Address { pub fn solicited_node(&self) -> Address {
assert!(self.is_unicast()); assert!(self.is_unicast());
let mut bytes = [0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, let mut bytes = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
bytes[14..].copy_from_slice(&self.0[14..]); bytes[14..].copy_from_slice(&self.0[14..]);
Address(bytes) Address(bytes)
} }
@ -204,7 +209,11 @@ impl From<Address> for ::std::net::Ipv6Addr {
impl fmt::Display for Address { impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_ipv4_mapped() { 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 // The string representation of an IPv6 address should
@ -217,7 +226,7 @@ impl fmt::Display for Address {
Head, Head,
HeadBody, HeadBody,
Tail, Tail,
TailBody TailBody,
} }
let mut words = [0u16; 8]; let mut words = [0u16; 8];
self.write_parts(&mut words); self.write_parts(&mut words);
@ -229,7 +238,7 @@ impl fmt::Display for Address {
(0, &State::Head) | (0, &State::HeadBody) => { (0, &State::Head) | (0, &State::HeadBody) => {
write!(f, "::")?; write!(f, "::")?;
State::Tail State::Tail
}, }
// Continue iterating without writing any characters until // Continue iterating without writing any characters until
// we hit anothing non-zero value. // we hit anothing non-zero value.
(0, &State::Tail) => State::Tail, (0, &State::Tail) => State::Tail,
@ -238,11 +247,11 @@ impl fmt::Display for Address {
(_, &State::Head) => { (_, &State::Head) => {
write!(f, "{:x}", word)?; write!(f, "{:x}", word)?;
State::HeadBody State::HeadBody
}, }
(_, &State::Tail) => { (_, &State::Tail) => {
write!(f, "{:x}", word)?; write!(f, "{:x}", word)?;
State::TailBody State::TailBody
}, }
// Write the u16 with a leading colon when parsing a value // Write the u16 with a leading colon when parsing a value
// that isn't the first in a section // that isn't the first in a section
(_, &State::HeadBody) | (_, &State::TailBody) => { (_, &State::HeadBody) | (_, &State::TailBody) => {
@ -260,8 +269,9 @@ impl fmt::Display for Address {
impl From<ipv4::Address> for Address { impl From<ipv4::Address> for Address {
fn from(address: ipv4::Address) -> Self { fn from(address: ipv4::Address) -> Self {
let octets = address.0; let octets = address.0;
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, Address([
octets[0], octets[1], octets[2], octets[3]]) 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)] #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Cidr { pub struct Cidr {
address: Address, address: Address,
prefix_len: u8, prefix_len: u8,
} }
@ -278,12 +288,13 @@ impl Cidr {
/// The [solicited node prefix]. /// The [solicited node prefix].
/// ///
/// [solicited node prefix]: https://tools.ietf.org/html/rfc4291#section-2.7.1 /// [solicited node prefix]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const SOLICITED_NODE_PREFIX: Cidr = pub const SOLICITED_NODE_PREFIX: Cidr = Cidr {
Cidr { address: Address([
address: Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00,
0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00]), 0x00, 0x00,
prefix_len: 104 ]),
}; prefix_len: 104,
};
/// Create an IPv6 CIDR block from the given address and prefix length. /// 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. /// This function panics if the prefix length is larger than 128.
pub fn new(address: Address, prefix_len: u8) -> Cidr { pub fn new(address: Address, prefix_len: u8) -> Cidr {
assert!(prefix_len <= 128); assert!(prefix_len <= 128);
Cidr { address, prefix_len } Cidr {
address,
prefix_len,
}
} }
/// Return the address of this IPv6 CIDR block. /// Return the address of this IPv6 CIDR block.
@ -308,7 +322,9 @@ impl Cidr {
/// the given address. /// the given address.
pub fn contains_addr(&self, addr: &Address) -> bool { pub fn contains_addr(&self, addr: &Address) -> bool {
// right shift by 128 is not legal // 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; let shift = 128 - self.prefix_len;
self.address.mask(shift) == addr.mask(shift) self.address.mask(shift) == addr.mask(shift)
@ -332,7 +348,7 @@ impl fmt::Display for Cidr {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> { pub struct Packet<T: AsRef<[u8]>> {
buffer: T buffer: T,
} }
// Ranges and constants describing the IPv6 header // Ranges and constants describing the IPv6 header
@ -367,17 +383,17 @@ mod field {
pub const VER_TC_FLOW: Field = 0..4; pub const VER_TC_FLOW: Field = 0..4;
// 16-bit value representing the length of the payload. // 16-bit value representing the length of the payload.
// Note: Options are included in this length. // 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 // 8-bit value identifying the type of header following this
// one. Note: The same numbers are used in IPv4. // 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 // 8-bit value decremented by each node that forwards this
// packet. The packet is discarded when the value is 0. // 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. // 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. // 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. /// 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))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr { pub struct Repr {
/// IPv6 address of the source node. /// IPv6 address of the source node.
pub src_addr: Address, pub src_addr: Address,
/// IPv6 address of the destination node. /// IPv6 address of the destination node.
pub dst_addr: Address, pub dst_addr: Address,
/// Protocol contained in the next header. /// Protocol contained in the next header.
pub next_header: Protocol, pub next_header: Protocol,
/// Length of the payload including the extension headers. /// Length of the payload including the extension headers.
pub payload_len: usize, pub payload_len: usize,
/// The 8-bit hop limit field. /// The 8-bit hop limit field.
pub hop_limit: u8 pub hop_limit: u8,
} }
impl Repr { impl Repr {
@ -618,13 +634,15 @@ impl Repr {
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> { pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> {
// Ensure basic accessors will work // Ensure basic accessors will work
packet.check_len()?; packet.check_len()?;
if packet.version() != 6 { return Err(Error::Malformed); } if packet.version() != 6 {
return Err(Error::Malformed);
}
Ok(Repr { Ok(Repr {
src_addr: packet.src_addr(), src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(), dst_addr: packet.dst_addr(),
next_header: packet.next_header(), next_header: packet.next_header(),
payload_len: packet.payload_len() as usize, 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 { impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv6 src={} dst={} nxt_hdr={} hop_limit={}", write!(
self.src_addr, self.dst_addr, self.next_header, self.hop_limit) 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 // TODO: This is very similar to the implementation for IPv4. Make
// a way to have less copy and pasted code here. // a way to have less copy and pasted code here.
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> { impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, fn pretty_print(
indent: &mut PrettyIndent) -> fmt::Result { buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
let (ip_repr, payload) = match Packet::new_checked(buffer) { let (ip_repr, payload) = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err), Err(err) => return write!(f, "{}({})", indent, err),
Ok(ip_packet) => { Ok(ip_packet) => match Repr::parse(&ip_packet) {
match Repr::parse(&ip_packet) { Err(_) => return Ok(()),
Err(_) => return Ok(()), Ok(ip_repr) => {
Ok(ip_repr) => { write!(f, "{}{}", indent, ip_repr)?;
write!(f, "{}{}", indent, ip_repr)?; (ip_repr, ip_packet.payload())
(ip_repr, ip_packet.payload())
}
} }
} },
}; };
pretty_print_ip_payload(f, indent, ip_repr, payload) pretty_print_ip_payload(f, indent, ip_repr, payload)
@ -682,18 +704,18 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::Error;
use super::{Address, Cidr}; use super::{Address, Cidr};
use super::{Packet, Protocol, Repr}; use super::{Packet, Protocol, Repr};
use crate::wire::pretty_print::{PrettyPrinter}; use crate::wire::pretty_print::PrettyPrinter;
use crate::Error;
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
use crate::wire::ipv4::Address as Ipv4Address; use crate::wire::ipv4::Address as Ipv4Address;
static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00, static LINK_LOCAL_ADDR: Address = Address([
0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01]); ]);
#[test] #[test]
fn test_basic_multicast() { fn test_basic_multicast() {
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unspecified()); assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unspecified());
@ -724,48 +746,62 @@ mod test {
#[test] #[test]
fn test_address_format() { fn test_address_format() {
assert_eq!("ff02::1", assert_eq!("ff02::1", format!("{}", Address::LINK_LOCAL_ALL_NODES));
format!("{}", Address::LINK_LOCAL_ALL_NODES)); assert_eq!("fe80::1", format!("{}", LINK_LOCAL_ADDR));
assert_eq!("fe80::1", assert_eq!(
format!("{}", LINK_LOCAL_ADDR)); "fe80::7f00:0:1",
assert_eq!("fe80::7f00:0:1", format!(
format!("{}", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001))); "{}",
assert_eq!("::", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)
format!("{}", Address::UNSPECIFIED)); )
assert_eq!("::1", );
format!("{}", Address::LOOPBACK)); assert_eq!("::", format!("{}", Address::UNSPECIFIED));
assert_eq!("::1", format!("{}", Address::LOOPBACK));
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
assert_eq!("::ffff:192.168.1.1", assert_eq!(
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1)))); "::ffff:192.168.1.1",
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1)))
);
} }
#[test] #[test]
fn test_new() { fn test_new() {
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1), assert_eq!(
Address::LINK_LOCAL_ALL_NODES); Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2), Address::LINK_LOCAL_ALL_NODES
Address::LINK_LOCAL_ALL_ROUTERS); );
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1), assert_eq!(
Address::LOOPBACK); Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0), Address::LINK_LOCAL_ALL_ROUTERS
Address::UNSPECIFIED); );
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1), Address::LOOPBACK);
LINK_LOCAL_ADDR); 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] #[test]
fn test_from_parts() { fn test_from_parts() {
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]), assert_eq!(
Address::LINK_LOCAL_ALL_NODES); Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]), Address::LINK_LOCAL_ALL_NODES
Address::LINK_LOCAL_ALL_ROUTERS); );
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]), assert_eq!(
Address::LOOPBACK); Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]), Address::LINK_LOCAL_ALL_ROUTERS
Address::UNSPECIFIED); );
assert_eq!(Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]), assert_eq!(
LINK_LOCAL_ADDR); 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] #[test]
@ -788,18 +824,36 @@ mod test {
#[test] #[test]
fn test_mask() { fn test_mask() {
let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1); 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!(
assert_eq!(addr.mask(15), [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); addr.mask(11),
assert_eq!(addr.mask(26), [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); [0x01, 0x20, 0, 0, 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(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")] #[cfg(feature = "proto-ipv4")]
#[test] #[test]
fn test_is_ipv4_mapped() { fn test_is_ipv4_mapped() {
assert_eq!(false, Address::UNSPECIFIED.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")] #[cfg(feature = "proto-ipv4")]
@ -814,10 +868,14 @@ mod test {
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
#[test] #[test]
fn test_from_ipv4_address() { fn test_from_ipv4_address() {
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]), assert_eq!(
Address::from(Ipv4Address::new(192, 168, 1, 1))); Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 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(192, 168, 1, 1))
Address::from(Ipv4Address::new(222, 1, 41, 90))); );
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] #[test]
@ -825,52 +883,96 @@ mod test {
let cidr = Cidr::new(LINK_LOCAL_ADDR, 64); let cidr = Cidr::new(LINK_LOCAL_ADDR, 64);
let inside_subnet = [ 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
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, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x88,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff] ],
[
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 = [ let outside_subnet = [
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02] ],
[
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 = [ 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, 0xff, 0xff, 0xff, 0xff, 0xff,
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], ],
128), 65,
([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, 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 = [ 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, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], ],
64), 63,
([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, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], 0xff, 0xff, 0xff,
128) ],
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)) { for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
@ -881,13 +983,11 @@ mod test {
assert!(!cidr.contains_addr(&addr)); assert!(!cidr.contains_addr(&addr));
} }
for subnet in subnets.iter().map( for subnet in subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) {
|&(a, p)| Cidr::new(Address(a), p)) {
assert!(cidr.contains_subnet(&subnet)); assert!(cidr.contains_subnet(&subnet));
} }
for subnet in not_subnets.iter().map( for subnet in not_subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) {
|&(a, p)| Cidr::new(Address(a), p)) {
assert!(!cidr.contains_subnet(&subnet)); assert!(!cidr.contains_subnet(&subnet));
} }
@ -907,33 +1007,26 @@ mod test {
let _ = Address::from_parts(&[0u16; 7]); let _ = Address::from_parts(&[0u16; 7]);
} }
static REPR_PACKET_BYTES: [u8; 52] = [0x60, 0x00, 0x00, 0x00, static REPR_PACKET_BYTES: [u8; 52] = [
0x00, 0x0c, 0x11, 0x40, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 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, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x01, ];
0xff, 0x02, 0x00, 0x00, static REPR_PAYLOAD_BYTES: [u8; 12] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
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 { fn packet_repr() -> Repr {
Repr { Repr {
src_addr: Address([0xfe, 0x80, 0x00, 0x00, src_addr: Address([
0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x01]), ]),
dst_addr: Address::LINK_LOCAL_ALL_NODES, dst_addr: Address::LINK_LOCAL_ALL_NODES,
next_header: Protocol::Udp, next_header: Protocol::Udp,
payload_len: 12, 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.payload_len() as usize, REPR_PAYLOAD_BYTES.len());
assert_eq!(packet.next_header(), Protocol::Udp); assert_eq!(packet.next_header(), Protocol::Udp);
assert_eq!(packet.hop_limit(), 0x40); assert_eq!(packet.hop_limit(), 0x40);
assert_eq!(packet.src_addr(), Address([0xfe, 0x80, 0x00, 0x00, assert_eq!(
0x00, 0x00, 0x00, 0x00, packet.src_addr(),
0x00, 0x00, 0x00, 0x00, Address([
0x00, 0x00, 0x00, 0x01])); 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.dst_addr(), Address::LINK_LOCAL_ALL_NODES);
assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]); assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]);
} }
@ -976,15 +1072,14 @@ mod test {
packet.set_hop_limit(0xfe); packet.set_hop_limit(0xfe);
packet.set_src_addr(Address::LINK_LOCAL_ALL_ROUTERS); packet.set_src_addr(Address::LINK_LOCAL_ALL_ROUTERS);
packet.set_dst_addr(Address::LINK_LOCAL_ALL_NODES); 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 = [ let mut expected_bytes = [
0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
0xff, 0x02, 0x00, 0x00, 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, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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(); let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len();
expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]); expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
@ -998,10 +1093,14 @@ mod test {
bytes.extend(&REPR_PACKET_BYTES[..]); bytes.extend(&REPR_PACKET_BYTES[..]);
bytes.push(0); bytes.push(0);
assert_eq!(Packet::new_unchecked(&bytes).payload().len(), assert_eq!(
REPR_PAYLOAD_BYTES.len()); Packet::new_unchecked(&bytes).payload().len(),
assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(), REPR_PAYLOAD_BYTES.len()
REPR_PAYLOAD_BYTES.len()); );
assert_eq!(
Packet::new_unchecked(&mut bytes).payload_mut().len(),
REPR_PAYLOAD_BYTES.len()
);
} }
#[test] #[test]
@ -1010,8 +1109,7 @@ mod test {
bytes.extend(&REPR_PACKET_BYTES[..]); bytes.extend(&REPR_PACKET_BYTES[..]);
Packet::new_unchecked(&mut bytes).set_payload_len(0x80); Packet::new_unchecked(&mut bytes).set_payload_len(0x80);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated);
Error::Truncated);
} }
#[test] #[test]
@ -1063,7 +1161,12 @@ mod test {
#[test] #[test]
fn test_pretty_print() { fn test_pretty_print() {
assert_eq!(format!("{}", PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])), assert_eq!(
"\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4"); 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 crate::{Error, Result};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian}; use byteorder::{ByteOrder, NetworkEndian};
@ -9,7 +9,7 @@ pub use super::IpProtocol as Protocol;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Header<T: AsRef<[u8]>> { pub struct Header<T: AsRef<[u8]>> {
buffer: T buffer: T,
} }
// Format of the Fragment Header // Format of the Fragment Header
@ -25,13 +25,13 @@ mod field {
use crate::wire::field::*; use crate::wire::field::*;
// 8-bit identifier of the header immediately following this header. // 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. // 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. // 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 // 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> { impl<T: AsRef<[u8]>> Header<T> {
@ -170,17 +170,19 @@ pub struct Repr {
pub more_frags: bool, pub more_frags: bool,
/// The identification for every packet that is fragmented. /// The identification for every packet that is fragmented.
pub ident: u32, pub ident: u32,
} }
impl Repr { impl Repr {
/// Parse an IPv6 Fragment Header and return a high-level representation. /// 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 { Ok(Repr {
next_header: header.next_header(), next_header: header.next_header(),
frag_offset: header.frag_offset(), frag_offset: header.frag_offset(),
more_frags: header.more_frags(), more_frags: header.more_frags(),
ident: header.ident() ident: header.ident(),
}) })
} }
@ -202,8 +204,11 @@ impl Repr {
impl<'a> fmt::Display for Repr { impl<'a> fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv6 Fragment next_hdr={} offset={} more={} ident={}", write!(
self.next_header, self.frag_offset, self.more_frags, self.ident) 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::*; use super::*;
// A Fragment Header with more fragments remaining // A Fragment Header with more fragments remaining
static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1, static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x39];
0x0, 0x0, 0x30, 0x39];
// A Fragment Header with no more fragments remaining // A Fragment Header with no more fragments remaining
static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0, static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0, 0x0, 0x1, 0x9, 0x32];
0x0, 0x1, 0x9, 0x32];
#[test] #[test]
fn test_check_len() { fn test_check_len() {
// less than 8 bytes // less than 8 bytes
assert_eq!(Err(Error::Truncated), assert_eq!(
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len()); Err(Error::Truncated),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len()
);
// valid // valid
assert_eq!(Ok(()), assert_eq!(
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len()); Ok(()),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len()
);
} }
#[test] #[test]
@ -248,24 +255,48 @@ mod test {
fn test_repr_parse_valid() { fn test_repr_parse_valid() {
let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG); let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
let repr = Repr::parse(&header).unwrap(); let repr = Repr::parse(&header).unwrap();
assert_eq!(repr, assert_eq!(
Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 }); repr,
Repr {
next_header: Protocol::Tcp,
frag_offset: 0,
more_frags: true,
ident: 12345
}
);
let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG); let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
let repr = Repr::parse(&header).unwrap(); let repr = Repr::parse(&header).unwrap();
assert_eq!(repr, assert_eq!(
Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 }); repr,
Repr {
next_header: Protocol::Tcp,
frag_offset: 320,
more_frags: false,
ident: 67890
}
);
} }
#[test] #[test]
fn test_repr_emit() { 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 bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes); let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header); repr.emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..8]); 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 bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes); let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header); repr.emit(&mut header);

View File

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

View File

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

View File

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

View File

@ -6,9 +6,9 @@
use byteorder::{ByteOrder, NetworkEndian}; use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::wire::icmpv6::{field, Message, Packet}; use crate::wire::icmpv6::{field, Message, Packet};
use crate::wire::Ipv6Address; use crate::wire::Ipv6Address;
use crate::{Error, Result};
enum_with_unknown! { enum_with_unknown! {
/// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for /// 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)] #[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AddressRecord<T: AsRef<[u8]>> { pub struct AddressRecord<T: AsRef<[u8]>> {
buffer: T buffer: T,
} }
impl<T: AsRef<[u8]>> AddressRecord<T> { impl<T: AsRef<[u8]>> AddressRecord<T> {
@ -305,58 +305,61 @@ pub enum Repr<'a> {
qrv: u8, qrv: u8,
qqic: u8, qqic: u8,
num_srcs: u16, num_srcs: u16,
data: &'a [u8] data: &'a [u8],
}, },
Report { Report {
nr_mcast_addr_rcrds: u16, nr_mcast_addr_rcrds: u16,
data: &'a [u8] data: &'a [u8],
} },
} }
impl<'a> Repr<'a> { impl<'a> Repr<'a> {
/// Parse an MLDv2 packet and return a high-level representation. /// Parse an MLDv2 packet and return a high-level representation.
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>> 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() { match packet.msg_type() {
Message::MldQuery => { Message::MldQuery => Ok(Repr::Query {
Ok(Repr::Query { max_resp_code: packet.max_resp_code(),
max_resp_code: packet.max_resp_code(), mcast_addr: packet.mcast_addr(),
mcast_addr: packet.mcast_addr(), s_flag: packet.s_flag(),
s_flag: packet.s_flag(), qrv: packet.qrv(),
qrv: packet.qrv(), qqic: packet.qqic(),
qqic: packet.qqic(), num_srcs: packet.num_srcs(),
num_srcs: packet.num_srcs(), data: packet.payload(),
data: packet.payload() }),
}) Message::MldReport => Ok(Repr::Report {
}, nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(),
Message::MldReport => { data: packet.payload(),
Ok(Repr::Report { }),
nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(), _ => Err(Error::Unrecognized),
data: packet.payload()
})
},
_ => Err(Error::Unrecognized)
} }
} }
/// Return the length of a packet that will be emitted from this high-level representation. /// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize { pub fn buffer_len(&self) -> usize {
match self { match self {
Repr::Query { .. } => { Repr::Query { .. } => field::QUERY_NUM_SRCS.end,
field::QUERY_NUM_SRCS.end Repr::Report { .. } => field::NR_MCAST_RCRDS.end,
}
Repr::Report { .. } => {
field::NR_MCAST_RCRDS.end
}
} }
} }
/// Emit a high-level representation into an MLDv2 packet. /// Emit a high-level representation into an MLDv2 packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>) 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 { match self {
Repr::Query { max_resp_code, mcast_addr, s_flag, Repr::Query {
qrv, qqic, num_srcs, data } => { max_resp_code,
mcast_addr,
s_flag,
qrv,
qqic,
num_srcs,
data,
} => {
packet.set_msg_type(Message::MldQuery); packet.set_msg_type(Message::MldQuery);
packet.set_msg_code(0); packet.set_msg_code(0);
packet.clear_reserved(); packet.clear_reserved();
@ -371,8 +374,11 @@ impl<'a> Repr<'a> {
packet.set_qqic(*qqic); packet.set_qqic(*qqic);
packet.set_num_srcs(*num_srcs); packet.set_num_srcs(*num_srcs);
packet.payload_mut().copy_from_slice(&data[..]); 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_type(Message::MldReport);
packet.set_msg_code(0); packet.set_msg_code(0);
packet.clear_reserved(); packet.clear_reserved();
@ -385,74 +391,49 @@ impl<'a> Repr<'a> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::phy::ChecksumCapabilities;
use crate::wire::Icmpv6Repr;
use crate::wire::icmpv6::Message;
use super::*; use super::*;
use crate::phy::ChecksumCapabilities;
use crate::wire::icmpv6::Message;
use crate::wire::Icmpv6Repr;
static QUERY_PACKET_BYTES: [u8; 44] = static QUERY_PACKET_BYTES: [u8; 44] = [
[0x82, 0x00, 0x73, 0x74, 0x82, 0x00, 0x73, 0x74, 0x04, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x12, 0x00, 0x01, 0xff, 0x02,
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
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] = static QUERY_PACKET_PAYLOAD: [u8; 16] = [
[0xff, 0x02, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, ];
0x00, 0x00, 0x00, 0x02];
static REPORT_PACKET_BYTES: [u8; 44] = static REPORT_PACKET_BYTES: [u8; 44] = [
[0x8f, 0x00, 0x73, 0x85, 0x8f, 0x00, 0x73, 0x85, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02,
0x01, 0x00, 0x00, 0x01, 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,
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_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> { fn create_repr<'a>(ty: Message) -> Icmpv6Repr<'a> {
match ty { match ty {
Message::MldQuery => { Message::MldQuery => Icmpv6Repr::Mld(Repr::Query {
Icmpv6Repr::Mld(Repr::Query { max_resp_code: 0x400,
max_resp_code: 0x400, mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES,
mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES, s_flag: true,
s_flag: true, qrv: 0x02,
qrv: 0x02, qqic: 0x12,
qqic: 0x12, num_srcs: 0x01,
num_srcs: 0x01, data: &QUERY_PACKET_PAYLOAD,
data: &QUERY_PACKET_PAYLOAD }),
}) Message::MldReport => Icmpv6Repr::Mld(Repr::Report {
}, nr_mcast_addr_rcrds: 1,
Message::MldReport => { data: &REPORT_PACKET_PAYLOAD,
Icmpv6Repr::Mld(Repr::Report { }),
nr_mcast_addr_rcrds: 1,
data: &REPORT_PACKET_PAYLOAD
})
},
_ => { _ => {
panic!("Message type must be a MLDv2 message type"); panic!("Message type must be a MLDv2 message type");
} }
@ -471,8 +452,10 @@ mod test {
assert_eq!(packet.qrv(), 0x02); assert_eq!(packet.qrv(), 0x02);
assert_eq!(packet.qqic(), 0x12); assert_eq!(packet.qqic(), 0x12);
assert_eq!(packet.num_srcs(), 0x01); assert_eq!(packet.num_srcs(), 0x01);
assert_eq!(Ipv6Address::from_bytes(packet.payload()), assert_eq!(
Ipv6Address::LINK_LOCAL_ALL_ROUTERS); Ipv6Address::from_bytes(packet.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS
);
} }
#[test] #[test]
@ -487,10 +470,14 @@ mod test {
packet.set_qrv(0x02); packet.set_qrv(0x02);
packet.set_qqic(0x12); packet.set_qqic(0x12);
packet.set_num_srcs(0x01); 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.clear_reserved();
packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), packet.fill_checksum(
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into()); &Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
);
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); 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.aux_data_len(), 0x00);
assert_eq!(addr_rcrd.num_srcs(), 0x01); assert_eq!(addr_rcrd.num_srcs(), 0x01);
assert_eq!(addr_rcrd.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES); assert_eq!(addr_rcrd.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES);
assert_eq!(Ipv6Address::from_bytes(addr_rcrd.payload()), assert_eq!(
Ipv6Address::LINK_LOCAL_ALL_ROUTERS); Ipv6Address::from_bytes(addr_rcrd.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS
);
} }
#[test] #[test]
@ -524,31 +513,38 @@ mod test {
addr_rcrd.set_aux_data_len(0); addr_rcrd.set_aux_data_len(0);
addr_rcrd.set_num_srcs(1); addr_rcrd.set_num_srcs(1);
addr_rcrd.set_mcast_addr(Ipv6Address::LINK_LOCAL_ALL_NODES); 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()); .copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
} }
packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), packet.fill_checksum(
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into()); &Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
);
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
} }
#[test] #[test]
fn test_query_repr_parse() { fn test_query_repr_parse() {
let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]); let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]);
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), let repr = Icmpv6Repr::parse(
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&packet, &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&ChecksumCapabilities::default()); &packet,
&ChecksumCapabilities::default(),
);
assert_eq!(repr, Ok(create_repr(Message::MldQuery))); assert_eq!(repr, Ok(create_repr(Message::MldQuery)));
} }
#[test] #[test]
fn test_report_repr_parse() { fn test_report_repr_parse() {
let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]); let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]);
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), let repr = Icmpv6Repr::parse(
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&packet, &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&ChecksumCapabilities::default()); &packet,
&ChecksumCapabilities::default(),
);
assert_eq!(repr, Ok(create_repr(Message::MldReport))); assert_eq!(repr, Ok(create_repr(Message::MldReport)));
} }
@ -557,10 +553,12 @@ mod test {
let mut bytes = [0x2a; 44]; let mut bytes = [0x2a; 44];
let mut packet = Packet::new_unchecked(&mut bytes[..]); let mut packet = Packet::new_unchecked(&mut bytes[..]);
let repr = create_repr(Message::MldQuery); let repr = create_repr(Message::MldQuery);
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), repr.emit(
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&mut packet, &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&ChecksumCapabilities::default()); &mut packet,
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]); assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
} }
@ -569,10 +567,12 @@ mod test {
let mut bytes = [0x2a; 44]; let mut bytes = [0x2a; 44];
let mut packet = Packet::new_unchecked(&mut bytes[..]); let mut packet = Packet::new_unchecked(&mut bytes[..]);
let repr = create_repr(Message::MldReport); let repr = create_repr(Message::MldReport);
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(), repr.emit(
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(), &Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&mut packet, &Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&ChecksumCapabilities::default()); &mut packet,
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]); 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 { mod field {
pub type Field = ::core::ops::Range<usize>; 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; pub mod pretty_print;
#[cfg(feature = "medium-ethernet")]
mod ethernet;
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
mod arp; 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; pub(crate) mod ip;
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
mod ipv4; mod ipv4;
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
mod ipv6; mod ipv6;
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
mod ipv6option; mod ipv6fragment;
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
mod ipv6hopbyhop; mod ipv6hopbyhop;
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
mod ipv6fragment; mod ipv6option;
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
mod ipv6routing; mod ipv6routing;
#[cfg(feature = "proto-ipv4")]
mod icmpv4;
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
mod icmpv6; mod mld;
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
mod icmp;
#[cfg(feature = "proto-igmp")]
mod igmp;
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] #[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
mod ndisc; mod ndisc;
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))] #[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
mod ndiscoption; mod ndiscoption;
#[cfg(feature = "proto-ipv6")]
mod mld;
mod udp;
mod tcp; mod tcp;
#[cfg(feature = "proto-dhcpv4")] mod udp;
pub(crate) mod dhcpv4;
pub use self::pretty_print::PrettyPrinter; pub use self::pretty_print::PrettyPrinter;
#[cfg(feature = "medium-ethernet")] #[cfg(feature = "medium-ethernet")]
pub use self::ethernet::{EtherType as EthernetProtocol, pub use self::ethernet::{
Address as EthernetAddress, Address as EthernetAddress, EtherType as EthernetProtocol, Frame as EthernetFrame,
Frame as EthernetFrame, Repr as EthernetRepr, HEADER_LEN as ETHERNET_HEADER_LEN,
HEADER_LEN as ETHERNET_HEADER_LEN, };
Repr as EthernetRepr};
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))] #[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
pub use self::arp::{Hardware as ArpHardware, pub use self::arp::{
Operation as ArpOperation, Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr,
Packet as ArpPacket, };
Repr as ArpRepr};
pub use self::ip::{Version as IpVersion, pub use self::ip::{
Protocol as IpProtocol, Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol,
Address as IpAddress, Repr as IpRepr, Version as IpVersion,
Endpoint as IpEndpoint, };
Repr as IpRepr,
Cidr as IpCidr};
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
pub use self::ipv4::{Address as Ipv4Address, pub use self::ipv4::{
Packet as Ipv4Packet, Address as Ipv4Address, Cidr as Ipv4Cidr, Packet as Ipv4Packet, Repr as Ipv4Repr,
Repr as Ipv4Repr, HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU,
Cidr as Ipv4Cidr, };
HEADER_LEN as IPV4_HEADER_LEN,
MIN_MTU as IPV4_MIN_MTU};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
pub use self::ipv6::{Address as Ipv6Address, pub use self::ipv6::{
Packet as Ipv6Packet, Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr,
Repr as Ipv6Repr, HEADER_LEN as IPV6_HEADER_LEN, MIN_MTU as IPV6_MIN_MTU,
Cidr as Ipv6Cidr, };
HEADER_LEN as IPV6_HEADER_LEN,
MIN_MTU as IPV6_MIN_MTU};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
pub use self::ipv6option::{Ipv6Option, pub use self::ipv6option::{
Repr as Ipv6OptionRepr, FailureType as Ipv6OptionFailureType, Ipv6Option, Repr as Ipv6OptionRepr,
Type as Ipv6OptionType, Type as Ipv6OptionType,
FailureType as Ipv6OptionFailureType}; };
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr};
Repr as Ipv6HopByHopRepr};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr};
Repr as Ipv6FragmentRepr};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
pub use self::ipv6routing::{Header as Ipv6RoutingHeader, pub use self::ipv6routing::{Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr};
Repr as Ipv6RoutingRepr};
#[cfg(feature = "proto-ipv4")] #[cfg(feature = "proto-ipv4")]
pub use self::icmpv4::{Message as Icmpv4Message, pub use self::icmpv4::{
DstUnreachable as Icmpv4DstUnreachable, DstUnreachable as Icmpv4DstUnreachable, Message as Icmpv4Message, Packet as Icmpv4Packet,
Redirect as Icmpv4Redirect, ParamProblem as Icmpv4ParamProblem, Redirect as Icmpv4Redirect, Repr as Icmpv4Repr,
TimeExceeded as Icmpv4TimeExceeded, TimeExceeded as Icmpv4TimeExceeded,
ParamProblem as Icmpv4ParamProblem, };
Packet as Icmpv4Packet,
Repr as Icmpv4Repr};
#[cfg(feature = "proto-igmp")] #[cfg(feature = "proto-igmp")]
pub use self::igmp::{Packet as IgmpPacket, pub use self::igmp::{IgmpVersion, Packet as IgmpPacket, Repr as IgmpRepr};
Repr as IgmpRepr,
IgmpVersion};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
pub use self::icmpv6::{Message as Icmpv6Message, pub use self::icmpv6::{
DstUnreachable as Icmpv6DstUnreachable, DstUnreachable as Icmpv6DstUnreachable, Message as Icmpv6Message, Packet as Icmpv6Packet,
TimeExceeded as Icmpv6TimeExceeded, ParamProblem as Icmpv6ParamProblem, Repr as Icmpv6Repr, TimeExceeded as Icmpv6TimeExceeded,
ParamProblem as Icmpv6ParamProblem, };
Packet as Icmpv6Packet,
Repr as Icmpv6Repr};
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))] #[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
pub use self::icmp::Repr as IcmpRepr; 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"))] #[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
pub use self::ndisc::{Repr as NdiscRepr, pub use self::ndiscoption::{
RouterFlags as NdiscRouterFlags, NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
NeighborFlags as NdiscNeighborFlags}; PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
Repr as NdiscOptionRepr, Type as NdiscOptionType,
#[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};
#[cfg(feature = "proto-ipv6")] #[cfg(feature = "proto-ipv6")]
pub use self::mld::{AddressRecord as MldAddressRecord, pub use self::mld::{AddressRecord as MldAddressRecord, Repr as MldRepr};
Repr as MldRepr};
pub use self::udp::{Packet as UdpPacket, pub use self::udp::{Packet as UdpPacket, Repr as UdpRepr, HEADER_LEN as UDP_HEADER_LEN};
Repr as UdpRepr,
HEADER_LEN as UDP_HEADER_LEN};
pub use self::tcp::{SeqNumber as TcpSeqNumber, pub use self::tcp::{
Packet as TcpPacket, Control as TcpControl, Packet as TcpPacket, Repr as TcpRepr, SeqNumber as TcpSeqNumber,
TcpOption, TcpOption, HEADER_LEN as TCP_HEADER_LEN,
Repr as TcpRepr, };
Control as TcpControl,
HEADER_LEN as TCP_HEADER_LEN};
#[cfg(feature = "proto-dhcpv4")] #[cfg(feature = "proto-dhcpv4")]
pub use self::dhcpv4::{Packet as DhcpPacket, pub use self::dhcpv4::{
Repr as DhcpRepr, MessageType as DhcpMessageType, Packet as DhcpPacket, Repr as DhcpRepr,
MessageType as DhcpMessageType, CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
CLIENT_PORT as DHCP_CLIENT_PORT, SERVER_PORT as DHCP_SERVER_PORT,
SERVER_PORT as DHCP_SERVER_PORT, };
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT};

View File

@ -1,13 +1,13 @@
use byteorder::{ByteOrder, NetworkEndian};
use bitflags::bitflags; 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::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::{NdiscOption, NdiscOptionRepr, NdiscOptionType};
use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader}; use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader};
use crate::time::Duration; use crate::{Error, Result};
use crate::wire::Ipv6Address;
bitflags! { bitflags! {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -82,7 +82,6 @@ impl<T: AsRef<[u8]>> Packet<T> {
} }
} }
/// Getters for the Neighbor Solicitation message header. /// Getters for the Neighbor Solicitation message header.
/// See [RFC 4861 § 4.3]. /// See [RFC 4861 § 4.3].
/// ///
@ -194,7 +193,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr<'a> { pub enum Repr<'a> {
RouterSolicit { RouterSolicit {
lladdr: Option<EthernetAddress> lladdr: Option<EthernetAddress>,
}, },
RouterAdvert { RouterAdvert {
hop_limit: u8, hop_limit: u8,
@ -204,44 +203,47 @@ pub enum Repr<'a> {
retrans_time: Duration, retrans_time: Duration,
lladdr: Option<EthernetAddress>, lladdr: Option<EthernetAddress>,
mtu: Option<u32>, mtu: Option<u32>,
prefix_info: Option<NdiscPrefixInformation> prefix_info: Option<NdiscPrefixInformation>,
}, },
NeighborSolicit { NeighborSolicit {
target_addr: Ipv6Address, target_addr: Ipv6Address,
lladdr: Option<EthernetAddress> lladdr: Option<EthernetAddress>,
}, },
NeighborAdvert { NeighborAdvert {
flags: NeighborFlags, flags: NeighborFlags,
target_addr: Ipv6Address, target_addr: Ipv6Address,
lladdr: Option<EthernetAddress> lladdr: Option<EthernetAddress>,
}, },
Redirect { Redirect {
target_addr: Ipv6Address, target_addr: Ipv6Address,
dest_addr: Ipv6Address, dest_addr: Ipv6Address,
lladdr: Option<EthernetAddress>, lladdr: Option<EthernetAddress>,
redirected_hdr: Option<NdiscRedirectedHeader<'a>> redirected_hdr: Option<NdiscRedirectedHeader<'a>>,
} },
} }
impl<'a> Repr<'a> { impl<'a> Repr<'a> {
/// Parse an NDISC packet and return a high-level representation of the /// Parse an NDISC packet and return a high-level representation of the
/// packet. /// packet.
pub fn parse<T>(packet: &Packet<&'a T>) pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
-> Result<Repr<'a>> where
where T: AsRef<[u8]> + ?Sized { T: AsRef<[u8]> + ?Sized,
{
match packet.msg_type() { match packet.msg_type() {
Message::RouterSolicit => { Message::RouterSolicit => {
let lladdr = if !packet.payload().is_empty() { let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?; let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() { match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { return Err(Error::Unrecognized); } _ => {
return Err(Error::Unrecognized);
}
} }
} else { } else {
None None
}; };
Ok(Repr::RouterSolicit { lladdr }) Ok(Repr::RouterSolicit { lladdr })
}, }
Message::RouterAdvert => { Message::RouterAdvert => {
let mut offset = 0; let mut offset = 0;
let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None); 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::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
NdiscOptionRepr::Mtu(val) => mtu = Some(val), NdiscOptionRepr::Mtu(val) => mtu = Some(val),
NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info), NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info),
_ => { return Err(Error::Unrecognized); } _ => {
return Err(Error::Unrecognized);
}
} }
offset += opt.buffer_len(); offset += opt.buffer_len();
} }
@ -262,29 +266,36 @@ impl<'a> Repr<'a> {
router_lifetime: packet.router_lifetime(), router_lifetime: packet.router_lifetime(),
reachable_time: packet.reachable_time(), reachable_time: packet.reachable_time(),
retrans_time: packet.retrans_time(), retrans_time: packet.retrans_time(),
lladdr, mtu, prefix_info lladdr,
mtu,
prefix_info,
}) })
}, }
Message::NeighborSolicit => { Message::NeighborSolicit => {
let lladdr = if !packet.payload().is_empty() { let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?; let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() { match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()), NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { return Err(Error::Unrecognized); } _ => {
return Err(Error::Unrecognized);
}
} }
} else { } else {
None None
}; };
Ok(Repr::NeighborSolicit { Ok(Repr::NeighborSolicit {
target_addr: packet.target_addr(), lladdr target_addr: packet.target_addr(),
lladdr,
}) })
}, }
Message::NeighborAdvert => { Message::NeighborAdvert => {
let lladdr = if !packet.payload().is_empty() { let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?; let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() { match opt.option_type() {
NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()), NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()),
_ => { return Err(Error::Unrecognized); } _ => {
return Err(Error::Unrecognized);
}
} }
} else { } else {
None None
@ -292,9 +303,9 @@ impl<'a> Repr<'a> {
Ok(Repr::NeighborAdvert { Ok(Repr::NeighborAdvert {
flags: packet.neighbor_flags(), flags: packet.neighbor_flags(),
target_addr: packet.target_addr(), target_addr: packet.target_addr(),
lladdr lladdr,
}) })
}, }
Message::Redirect => { Message::Redirect => {
let mut offset = 0; let mut offset = 0;
let (mut lladdr, mut redirected_hdr) = (None, None); let (mut lladdr, mut redirected_hdr) = (None, None);
@ -304,43 +315,50 @@ impl<'a> Repr<'a> {
NdiscOptionType::SourceLinkLayerAddr => { NdiscOptionType::SourceLinkLayerAddr => {
lladdr = Some(opt.link_layer_addr()); lladdr = Some(opt.link_layer_addr());
offset += 8; offset += 8;
}, }
NdiscOptionType::RedirectedHeader => { NdiscOptionType::RedirectedHeader => {
if opt.data_len() < 6 { if opt.data_len() < 6 {
return Err(Error::Truncated) return Err(Error::Truncated);
} else { } else {
let ip_packet = let ip_packet =
Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]); Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]);
let ip_repr = Ipv6Repr::parse(&ip_packet)?; let ip_repr = Ipv6Repr::parse(&ip_packet)?;
let data = &opt.data()[offset + 8 + ip_repr.buffer_len()..]; let data = &opt.data()[offset + 8 + ip_repr.buffer_len()..];
redirected_hdr = Some(NdiscRedirectedHeader { redirected_hdr = Some(NdiscRedirectedHeader {
header: ip_repr, data header: ip_repr,
data,
}); });
offset += 8 + ip_repr.buffer_len() + data.len(); offset += 8 + ip_repr.buffer_len() + data.len();
} }
} }
_ => { return Err(Error::Unrecognized); } _ => {
return Err(Error::Unrecognized);
}
} }
} }
Ok(Repr::Redirect { Ok(Repr::Redirect {
target_addr: packet.target_addr(), target_addr: packet.target_addr(),
dest_addr: packet.dest_addr(), dest_addr: packet.dest_addr(),
lladdr, redirected_hdr lladdr,
redirected_hdr,
}) })
}, }
_ => Err(Error::Unrecognized) _ => Err(Error::Unrecognized),
} }
} }
pub fn buffer_len(&self) -> usize { pub fn buffer_len(&self) -> usize {
match self { match self {
&Repr::RouterSolicit { lladdr } => { &Repr::RouterSolicit { lladdr } => match lladdr {
match lladdr { Some(_) => field::UNUSED.end + 8,
Some(_) => field::UNUSED.end + 8, None => field::UNUSED.end,
None => field::UNUSED.end,
}
}, },
&Repr::RouterAdvert { lladdr, mtu, prefix_info, .. } => { &Repr::RouterAdvert {
lladdr,
mtu,
prefix_info,
..
} => {
let mut offset = 0; let mut offset = 0;
if lladdr.is_some() { if lladdr.is_some() {
offset += 8; offset += 8;
@ -352,14 +370,18 @@ impl<'a> Repr<'a> {
offset += 32; offset += 32;
} }
field::RETRANS_TM.end + offset field::RETRANS_TM.end + offset
}, }
&Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => { &Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => {
match lladdr { match lladdr {
Some(_) => field::TARGET_ADDR.end + 8, Some(_) => field::TARGET_ADDR.end + 8,
None => field::TARGET_ADDR.end, None => field::TARGET_ADDR.end,
} }
}, }
&Repr::Redirect { lladdr, redirected_hdr, .. } => { &Repr::Redirect {
lladdr,
redirected_hdr,
..
} => {
let mut offset = 0; let mut offset = 0;
if lladdr.is_some() { if lladdr.is_some() {
offset += 8; offset += 8;
@ -373,7 +395,9 @@ impl<'a> Repr<'a> {
} }
pub fn emit<T>(&self, packet: &mut Packet<&mut T>) 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 { match *self {
Repr::RouterSolicit { lladdr } => { Repr::RouterSolicit { lladdr } => {
packet.set_msg_type(Message::RouterSolicit); packet.set_msg_type(Message::RouterSolicit);
@ -383,10 +407,18 @@ impl<'a> Repr<'a> {
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); NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
} }
}, }
Repr::RouterAdvert { hop_limit, flags, router_lifetime, reachable_time, Repr::RouterAdvert {
retrans_time, lladdr, mtu, prefix_info } => { hop_limit,
flags,
router_lifetime,
reachable_time,
retrans_time,
lladdr,
mtu,
prefix_info,
} => {
packet.set_msg_type(Message::RouterAdvert); packet.set_msg_type(Message::RouterAdvert);
packet.set_msg_code(0); packet.set_msg_code(0);
packet.set_current_hop_limit(hop_limit); packet.set_current_hop_limit(hop_limit);
@ -396,8 +428,7 @@ impl<'a> Repr<'a> {
packet.set_retrans_time(retrans_time); packet.set_retrans_time(retrans_time);
let mut offset = 0; let mut offset = 0;
if let Some(lladdr) = lladdr { if let Some(lladdr) = lladdr {
let mut opt_pkt = let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
offset += 8; offset += 8;
} }
@ -412,34 +443,44 @@ impl<'a> Repr<'a> {
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt) 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_type(Message::NeighborSolicit);
packet.set_msg_code(0); packet.set_msg_code(0);
packet.clear_reserved(); packet.clear_reserved();
packet.set_target_addr(target_addr); packet.set_target_addr(target_addr);
if let Some(lladdr) = lladdr { if let Some(lladdr) = lladdr {
let mut opt_pkt = let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt); 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_type(Message::NeighborAdvert);
packet.set_msg_code(0); packet.set_msg_code(0);
packet.clear_reserved(); packet.clear_reserved();
packet.set_neighbor_flags(flags); packet.set_neighbor_flags(flags);
packet.set_target_addr(target_addr); packet.set_target_addr(target_addr);
if let Some(lladdr) = lladdr { if let Some(lladdr) = lladdr {
let mut opt_pkt = let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); 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_type(Message::Redirect);
packet.set_msg_code(0); packet.set_msg_code(0);
packet.clear_reserved(); packet.clear_reserved();
@ -447,11 +488,10 @@ impl<'a> Repr<'a> {
packet.set_dest_addr(dest_addr); packet.set_dest_addr(dest_addr);
let offset = match lladdr { let offset = match lladdr {
Some(lladdr) => { Some(lladdr) => {
let mut opt_pkt = let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt); NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
8 8
}, }
None => 0, None => 0,
}; };
if let Some(redirected_hdr) = redirected_hdr { if let Some(redirected_hdr) = redirected_hdr {
@ -459,28 +499,23 @@ impl<'a> Repr<'a> {
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]); NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt); NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt);
} }
}, }
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::phy::ChecksumCapabilities;
use super::*; 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::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
use crate::wire::Icmpv6Repr;
static ROUTER_ADVERT_BYTES: [u8; 24] = static ROUTER_ADVERT_BYTES: [u8; 24] = [
[0x86, 0x00, 0xa9, 0xde, 0x86, 0x00, 0xa9, 0xde, 0x40, 0x80, 0x03, 0x84, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03,
0x40, 0x80, 0x03, 0x84, 0x84, 0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56,
0x00, 0x00, 0x03, 0x84, ];
0x00, 0x00, 0x03, 0x84, static SOURCE_LINK_LAYER_OPT: [u8; 8] = [0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56];
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> { fn create_repr<'a>() -> Icmpv6Repr<'a> {
Icmpv6Repr::Ndisc(Repr::RouterAdvert { Icmpv6Repr::Ndisc(Repr::RouterAdvert {
@ -491,7 +526,7 @@ mod test {
retrans_time: Duration::from_millis(900), retrans_time: Duration::from_millis(900),
lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56])), lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56])),
mtu: None, mtu: None,
prefix_info: None prefix_info: None,
}) })
} }
@ -519,7 +554,9 @@ mod test {
packet.set_router_lifetime(Duration::from_secs(900)); packet.set_router_lifetime(Duration::from_secs(900));
packet.set_reachable_time(Duration::from_millis(900)); packet.set_reachable_time(Duration::from_millis(900));
packet.set_retrans_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); packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
} }
@ -527,17 +564,28 @@ mod test {
#[test] #[test]
fn test_router_advert_repr_parse() { fn test_router_advert_repr_parse() {
let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]); let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]);
assert_eq!(Icmpv6Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, assert_eq!(
&packet, &ChecksumCapabilities::default()).unwrap(), Icmpv6Repr::parse(
create_repr()); &MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&packet,
&ChecksumCapabilities::default()
)
.unwrap(),
create_repr()
);
} }
#[test] #[test]
fn test_router_advert_repr_emit() { fn test_router_advert_repr_emit() {
let mut bytes = vec![0x2a; 24]; let mut bytes = vec![0x2a; 24];
let mut packet = Packet::new_unchecked(&mut bytes[..]); let mut packet = Packet::new_unchecked(&mut bytes[..]);
create_repr().emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2, create_repr().emit(
&mut packet, &ChecksumCapabilities::default()); &MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&mut packet,
&ChecksumCapabilities::default(),
);
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]); 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 bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use crate::{Error, Result};
use crate::time::Duration; use crate::time::Duration;
use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr}; use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr};
use crate::{Error, Result};
enum_with_unknown! { enum_with_unknown! {
/// NDISC Option Type /// NDISC Option Type
@ -27,10 +27,10 @@ impl fmt::Display for Type {
match self { match self {
Type::SourceLinkLayerAddr => write!(f, "source link-layer address"), Type::SourceLinkLayerAddr => write!(f, "source link-layer address"),
Type::TargetLinkLayerAddr => write!(f, "target link-layer address"), Type::TargetLinkLayerAddr => write!(f, "target link-layer address"),
Type::PrefixInformation => write!(f, "prefix information"), Type::PrefixInformation => write!(f, "prefix information"),
Type::RedirectedHeader => write!(f, "redirected header"), Type::RedirectedHeader => write!(f, "redirected header"),
Type::Mtu => write!(f, "mtu"), Type::Mtu => write!(f, "mtu"),
Type::Unknown(id) => write!(f, "{}", id) Type::Unknown(id) => write!(f, "{}", id),
} }
} }
} }
@ -49,7 +49,7 @@ bitflags! {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NdiscOption<T: AsRef<[u8]>> { pub struct NdiscOption<T: AsRef<[u8]>> {
buffer: T buffer: T,
} }
// Format of an NDISC Option // Format of an NDISC Option
@ -67,11 +67,11 @@ mod field {
use crate::wire::field::*; use crate::wire::field::*;
// 8-bit identifier of the type of option. // 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. // 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. // 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. // Variable-length field. Option-Type-specific data.
pub fn DATA(length: u8) -> Field { pub fn DATA(length: u8) -> Field {
2..length as usize * 8 2..length as usize * 8
@ -83,7 +83,7 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Link-Layer Address // Link-Layer Address
pub const LL_ADDR: Field = 2..8; pub const LL_ADDR: Field = 2..8;
// Prefix Information Option fields. // Prefix Information Option fields.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -105,17 +105,17 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Prefix length. // Prefix length.
pub const PREFIX_LEN: usize = 2; pub const PREFIX_LEN: usize = 2;
// Flags field of prefix header. // Flags field of prefix header.
pub const FLAGS: usize = 3; pub const FLAGS: usize = 3;
// Valid lifetime. // Valid lifetime.
pub const VALID_LT: Field = 4..8; pub const VALID_LT: Field = 4..8;
// Preferred lifetime. // Preferred lifetime.
pub const PREF_LT: Field = 8..12; pub const PREF_LT: Field = 8..12;
// Reserved bits // Reserved bits
pub const PREF_RESERVED: Field = 12..16; pub const PREF_RESERVED: Field = 12..16;
// Prefix // Prefix
pub const PREFIX: Field = 16..32; pub const PREFIX: Field = 16..32;
// Redirected Header Option fields. // Redirected Header Option fields.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -129,10 +129,10 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Reserved bits. // Reserved bits.
pub const IP_RESERVED: Field = 4..8; pub const IP_RESERVED: Field = 4..8;
// Redirected header IP header + data. // Redirected header IP header + data.
pub const IP_DATA: usize = 8; pub const IP_DATA: usize = 8;
pub const REDIR_MIN_SZ: usize = 48; pub const REDIR_MIN_SZ: usize = 48;
// MTU Option fields // MTU Option fields
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -142,7 +142,7 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// MTU // MTU
pub const MTU: Field = 4..8; pub const MTU: Field = 4..8;
} }
/// Core getter methods relevant to any type of NDISC option. /// Core getter methods relevant to any type of NDISC option.
@ -180,16 +180,11 @@ impl<T: AsRef<[u8]>> NdiscOption<T> {
Err(Error::Truncated) Err(Error::Truncated)
} else { } else {
match self.option_type() { match self.option_type() {
Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Ok(()),
Ok(()), Type::PrefixInformation if data_range.end >= field::PREFIX.end => Ok(()),
Type::PrefixInformation if data_range.end >= field::PREFIX.end => Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => Ok(()),
Ok(()), Type::Unknown(_) => Ok(()),
Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => _ => Err(Error::Truncated),
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> { impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> {
/// Return a mutable pointer to the option data. /// Return a mutable pointer to the option data.
#[inline] #[inline]
@ -401,14 +395,14 @@ pub struct PrefixInformation {
pub flags: PrefixInfoFlags, pub flags: PrefixInfoFlags,
pub valid_lifetime: Duration, pub valid_lifetime: Duration,
pub preferred_lifetime: Duration, pub preferred_lifetime: Duration,
pub prefix: Ipv6Address pub prefix: Ipv6Address,
} }
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RedirectedHeader<'a> { pub struct RedirectedHeader<'a> {
pub header: Ipv6Repr, pub header: Ipv6Repr,
pub data: &'a [u8] pub data: &'a [u8],
} }
/// A high-level representation of an NDISC Option. /// A high-level representation of an NDISC Option.
@ -421,16 +415,18 @@ pub enum Repr<'a> {
RedirectedHeader(RedirectedHeader<'a>), RedirectedHeader(RedirectedHeader<'a>),
Mtu(u32), Mtu(u32),
Unknown { Unknown {
type_: u8, type_: u8,
length: u8, length: u8,
data: &'a [u8] data: &'a [u8],
}, },
} }
impl<'a> Repr<'a> { impl<'a> Repr<'a> {
/// Parse an NDISC Option and return a high-level representation. /// Parse an NDISC Option and return a high-level representation.
pub fn parse<T>(opt: &'a NdiscOption<&'a T>) -> Result<Repr<'a>> 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() { match opt.option_type() {
Type::SourceLinkLayerAddr => { Type::SourceLinkLayerAddr => {
if opt.data_len() == 1 { if opt.data_len() == 1 {
@ -438,14 +434,14 @@ impl<'a> Repr<'a> {
} else { } else {
Err(Error::Malformed) Err(Error::Malformed)
} }
}, }
Type::TargetLinkLayerAddr => { Type::TargetLinkLayerAddr => {
if opt.data_len() == 1 { if opt.data_len() == 1 {
Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr())) Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr()))
} else { } else {
Err(Error::Malformed) Err(Error::Malformed)
} }
}, }
Type::PrefixInformation => { Type::PrefixInformation => {
if opt.data_len() == 4 { if opt.data_len() == 4 {
Ok(Repr::PrefixInformation(PrefixInformation { Ok(Repr::PrefixInformation(PrefixInformation {
@ -453,12 +449,12 @@ impl<'a> Repr<'a> {
flags: opt.prefix_flags(), flags: opt.prefix_flags(),
valid_lifetime: opt.valid_lifetime(), valid_lifetime: opt.valid_lifetime(),
preferred_lifetime: opt.preferred_lifetime(), preferred_lifetime: opt.preferred_lifetime(),
prefix: opt.prefix() prefix: opt.prefix(),
})) }))
} else { } else {
Err(Error::Malformed) Err(Error::Malformed)
} }
}, }
Type::RedirectedHeader => { Type::RedirectedHeader => {
// If the options data length is less than 6, the option // If the options data length is less than 6, the option
// does not have enough data to fill out the IP header // 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)?; let ip_repr = Ipv6Repr::parse(&ip_packet)?;
Ok(Repr::RedirectedHeader(RedirectedHeader { Ok(Repr::RedirectedHeader(RedirectedHeader {
header: ip_repr, 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 => { Type::Mtu => {
if opt.data_len() == 1 { if opt.data_len() == 1 {
Ok(Repr::Mtu(opt.mtu())) Ok(Repr::Mtu(opt.mtu()))
} else { } else {
Err(Error::Malformed) 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. /// Return the length of a header that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize { pub fn buffer_len(&self) -> usize {
match self { match self {
&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) => &Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) => field::LL_ADDR.end,
field::LL_ADDR.end, &Repr::PrefixInformation(_) => field::PREFIX.end,
&Repr::PrefixInformation(_) => &Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
field::PREFIX.end, field::IP_DATA + header.buffer_len() + data.len()
&Repr::RedirectedHeader(RedirectedHeader { header, data }) => }
field::IP_DATA + header.buffer_len() + data.len(), &Repr::Mtu(_) => field::MTU.end,
&Repr::Mtu(_) => &Repr::Unknown { length, .. } => field::DATA(length).end,
field::MTU.end,
&Repr::Unknown { length, .. } =>
field::DATA(length).end
} }
} }
/// Emit a high-level representation into an NDISC Option. /// Emit a high-level representation into an NDISC Option.
pub fn emit<T>(&self, opt: &mut NdiscOption<&'a mut T>) 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 { match *self {
Repr::SourceLinkLayerAddr(addr) => { Repr::SourceLinkLayerAddr(addr) => {
opt.set_option_type(Type::SourceLinkLayerAddr); opt.set_option_type(Type::SourceLinkLayerAddr);
opt.set_data_len(1); opt.set_data_len(1);
opt.set_link_layer_addr(addr); opt.set_link_layer_addr(addr);
}, }
Repr::TargetLinkLayerAddr(addr) => { Repr::TargetLinkLayerAddr(addr) => {
opt.set_option_type(Type::TargetLinkLayerAddr); opt.set_option_type(Type::TargetLinkLayerAddr);
opt.set_data_len(1); opt.set_data_len(1);
opt.set_link_layer_addr(addr); opt.set_link_layer_addr(addr);
}, }
Repr::PrefixInformation(PrefixInformation { Repr::PrefixInformation(PrefixInformation {
prefix_len, flags, valid_lifetime, prefix_len,
preferred_lifetime, prefix flags,
valid_lifetime,
preferred_lifetime,
prefix,
}) => { }) => {
opt.clear_prefix_reserved(); opt.clear_prefix_reserved();
opt.set_option_type(Type::PrefixInformation); opt.set_option_type(Type::PrefixInformation);
@ -533,10 +529,8 @@ impl<'a> Repr<'a> {
opt.set_valid_lifetime(valid_lifetime); opt.set_valid_lifetime(valid_lifetime);
opt.set_preferred_lifetime(preferred_lifetime); opt.set_preferred_lifetime(preferred_lifetime);
opt.set_prefix(prefix); opt.set_prefix(prefix);
}, }
Repr::RedirectedHeader(RedirectedHeader { Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
header, data
}) => {
let data_len = data.len() / 8; let data_len = data.len() / 8;
opt.clear_redirected_reserved(); opt.clear_redirected_reserved();
opt.set_option_type(Type::RedirectedHeader); opt.set_option_type(Type::RedirectedHeader);
@ -552,7 +546,11 @@ impl<'a> Repr<'a> {
opt.set_data_len(1); opt.set_data_len(1);
opt.set_mtu(mtu); 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_option_type(Type::Unknown(id));
opt.set_data_len(length); opt.set_data_len(length);
opt.data_mut().copy_from_slice(data); opt.data_mut().copy_from_slice(data);
@ -567,67 +565,61 @@ impl<'a> fmt::Display for Repr<'a> {
match *self { match *self {
Repr::SourceLinkLayerAddr(addr) => { Repr::SourceLinkLayerAddr(addr) => {
write!(f, "SourceLinkLayer addr={}", addr) write!(f, "SourceLinkLayer addr={}", addr)
}, }
Repr::TargetLinkLayerAddr(addr) => { Repr::TargetLinkLayerAddr(addr) => {
write!(f, "TargetLinkLayer addr={}", addr) write!(f, "TargetLinkLayer addr={}", addr)
}, }
Repr::PrefixInformation(PrefixInformation { Repr::PrefixInformation(PrefixInformation {
prefix, prefix_len, prefix, prefix_len, ..
..
}) => { }) => {
write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len) write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len)
}, }
Repr::RedirectedHeader(RedirectedHeader { Repr::RedirectedHeader(RedirectedHeader { header, .. }) => {
header,
..
}) => {
write!(f, "RedirectedHeader header={}", header) write!(f, "RedirectedHeader header={}", header)
}, }
Repr::Mtu(mtu) => { Repr::Mtu(mtu) => {
write!(f, "MTU mtu={}", mtu) write!(f, "MTU mtu={}", mtu)
}, }
Repr::Unknown { type_: id, length, .. } => { Repr::Unknown {
type_: id, length, ..
} => {
write!(f, "Unknown({}) length={}", 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> { impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter, fn pretty_print(
indent: &mut PrettyIndent) -> fmt::Result { buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
match NdiscOption::new_checked(buffer) { match NdiscOption::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err), Err(err) => return write!(f, "{}({})", indent, err),
Ok(ndisc) => { Ok(ndisc) => match Repr::parse(&ndisc) {
match Repr::parse(&ndisc) { Err(_) => Ok(()),
Err(_) => Ok(()), Ok(repr) => {
Ok(repr) => { write!(f, "{}{}", indent, repr)
write!(f, "{}{}", indent, repr)
}
} }
} },
} }
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::Error; use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type};
use crate::time::Duration; use crate::time::Duration;
use crate::wire::{EthernetAddress, Ipv6Address}; use crate::wire::{EthernetAddress, Ipv6Address};
use super::{NdiscOption, Type, PrefixInfoFlags, PrefixInformation, Repr}; use crate::Error;
static PREFIX_OPT_BYTES: [u8; 32] = [ static PREFIX_OPT_BYTES: [u8; 32] = [
0x03, 0x04, 0x40, 0xc0, 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0x84, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x03, 0xe8, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01
]; ];
#[test] #[test]
@ -636,7 +628,10 @@ mod test {
assert_eq!(opt.option_type(), Type::PrefixInformation); assert_eq!(opt.option_type(), Type::PrefixInformation);
assert_eq!(opt.data_len(), 4); assert_eq!(opt.data_len(), 4);
assert_eq!(opt.prefix_len(), 64); 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.valid_lifetime(), Duration::from_secs(900));
assert_eq!(opt.preferred_lifetime(), Duration::from_secs(1000)); assert_eq!(opt.preferred_lifetime(), Duration::from_secs(1000));
assert_eq!(opt.prefix(), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)); assert_eq!(opt.prefix(), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
@ -658,11 +653,11 @@ mod test {
#[test] #[test]
fn test_short_packet() { fn test_short_packet() {
assert_eq!(NdiscOption::new_checked(&[0x00, 0x00]), Err(Error::Truncated)); assert_eq!(
let bytes = [ NdiscOption::new_checked(&[0x00, 0x00]),
0x03, 0x01, 0x00, 0x00, Err(Error::Truncated)
0x00, 0x00, 0x00, 0x00 );
]; let bytes = [0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
assert_eq!(NdiscOption::new_checked(&bytes), Err(Error::Truncated)); 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 mut bytes = [0x01, 0x01, 0x54, 0x52, 0x00, 0x12, 0x23, 0x34];
let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]); let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]);
{ {
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)), assert_eq!(
Ok(Repr::SourceLinkLayerAddr(addr))); Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::SourceLinkLayerAddr(addr))
);
} }
bytes[0] = 0x02; bytes[0] = 0x02;
{ {
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)), assert_eq!(
Ok(Repr::TargetLinkLayerAddr(addr))); Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::TargetLinkLayerAddr(addr))
);
} }
} }
@ -688,9 +687,12 @@ mod test {
flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF, flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
valid_lifetime: Duration::from_secs(900), valid_lifetime: Duration::from_secs(900),
preferred_lifetime: Duration::from_secs(1000), 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] #[test]
@ -701,7 +703,7 @@ mod test {
flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF, flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
valid_lifetime: Duration::from_secs(900), valid_lifetime: Duration::from_secs(900),
preferred_lifetime: Duration::from_secs(1000), 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); let mut opt = NdiscOption::new_unchecked(&mut bytes);
repr.emit(&mut opt); repr.emit(&mut opt);
@ -711,6 +713,9 @@ mod test {
#[test] #[test]
fn test_repr_parse_mtu() { fn test_repr_parse_mtu() {
let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc]; 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))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PrettyIndent { pub struct PrettyIndent {
prefix: &'static str, prefix: &'static str,
level: usize level: usize,
} }
impl PrettyIndent { impl PrettyIndent {
@ -72,24 +72,27 @@ pub trait PrettyPrint {
/// ///
/// `pretty_print` accepts a buffer and not a packet wrapper because the packet might /// `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. /// 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, fn pretty_print(
indent: &mut PrettyIndent) -> fmt::Result; buffer: &dyn AsRef<[u8]>,
fmt: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result;
} }
/// Wrapper for using a `PrettyPrint` where a `Display` is expected. /// Wrapper for using a `PrettyPrint` where a `Display` is expected.
pub struct PrettyPrinter<'a, T: PrettyPrint> { pub struct PrettyPrinter<'a, T: PrettyPrint> {
prefix: &'static str, prefix: &'static str,
buffer: &'a dyn AsRef<[u8]>, buffer: &'a dyn AsRef<[u8]>,
phantom: PhantomData<T> phantom: PhantomData<T>,
} }
impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> { impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> {
/// Format the listing with the recorded parameters when Display::fmt is called. /// 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> { pub fn new(prefix: &'static str, buffer: &'a dyn AsRef<[u8]>) -> PrettyPrinter<'a, T> {
PrettyPrinter { PrettyPrinter {
prefix: prefix, prefix: prefix,
buffer: buffer, buffer: buffer,
phantom: PhantomData phantom: PhantomData,
} }
} }
} }

View File

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

View File

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