commit
ec416ceac4
|
@ -1,22 +1,24 @@
|
|||
#![feature(test)]
|
||||
|
||||
mod wire {
|
||||
use test;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use smoltcp::wire::{Ipv6Address, Ipv6Repr, Ipv6Packet};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use smoltcp::wire::{Ipv4Address, Ipv4Repr, Ipv4Packet};
|
||||
use smoltcp::phy::{ChecksumCapabilities};
|
||||
use smoltcp::phy::ChecksumCapabilities;
|
||||
use smoltcp::wire::{IpAddress, IpProtocol};
|
||||
use smoltcp::wire::{TcpRepr, TcpPacket, TcpSeqNumber, TcpControl};
|
||||
use smoltcp::wire::{UdpRepr, UdpPacket};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use smoltcp::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use smoltcp::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr};
|
||||
use smoltcp::wire::{TcpControl, TcpPacket, TcpRepr, TcpSeqNumber};
|
||||
use smoltcp::wire::{UdpPacket, UdpRepr};
|
||||
use test;
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1]));
|
||||
const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([
|
||||
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||
]));
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
const DST_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 2]));
|
||||
const DST_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([
|
||||
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
|
||||
]));
|
||||
|
||||
#[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))]
|
||||
const SRC_ADDR: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
|
||||
|
@ -26,8 +28,7 @@ mod wire {
|
|||
#[bench]
|
||||
#[cfg(any(feature = "proto-ipv6", feature = "proto-ipv4"))]
|
||||
fn bench_emit_tcp(b: &mut test::Bencher) {
|
||||
static PAYLOAD_BYTES: [u8; 400] =
|
||||
[0x2a; 400];
|
||||
static PAYLOAD_BYTES: [u8; 400] = [0x2a; 400];
|
||||
let repr = TcpRepr {
|
||||
src_port: 48896,
|
||||
dst_port: 80,
|
||||
|
@ -37,31 +38,40 @@ mod wire {
|
|||
control: TcpControl::Syn,
|
||||
max_seg_size: None,
|
||||
window_scale: None,
|
||||
payload: &PAYLOAD_BYTES
|
||||
payload: &PAYLOAD_BYTES,
|
||||
};
|
||||
let mut bytes = vec![0xa5; repr.buffer_len()];
|
||||
|
||||
b.iter(|| {
|
||||
let mut packet = TcpPacket::new(&mut bytes);
|
||||
repr.emit(&mut packet, &SRC_ADDR, &DST_ADDR, &ChecksumCapabilities::default());
|
||||
repr.emit(
|
||||
&mut packet,
|
||||
&SRC_ADDR,
|
||||
&DST_ADDR,
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
#[cfg(any(feature = "proto-ipv6", feature = "proto-ipv4"))]
|
||||
fn bench_emit_udp(b: &mut test::Bencher) {
|
||||
static PAYLOAD_BYTES: [u8; 400] =
|
||||
[0x2a; 400];
|
||||
static PAYLOAD_BYTES: [u8; 400] = [0x2a; 400];
|
||||
let repr = UdpRepr {
|
||||
src_port: 48896,
|
||||
dst_port: 80,
|
||||
payload: &PAYLOAD_BYTES
|
||||
payload: &PAYLOAD_BYTES,
|
||||
};
|
||||
let mut bytes = vec![0xa5; repr.buffer_len()];
|
||||
|
||||
b.iter(|| {
|
||||
let mut packet = UdpPacket::new(&mut bytes);
|
||||
repr.emit(&mut packet, &SRC_ADDR, &DST_ADDR, &ChecksumCapabilities::default());
|
||||
repr.emit(
|
||||
&mut packet,
|
||||
&SRC_ADDR,
|
||||
&DST_ADDR,
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -73,7 +83,7 @@ mod wire {
|
|||
dst_addr: Ipv4Address([192, 168, 1, 2]),
|
||||
protocol: IpProtocol::Tcp,
|
||||
payload_len: 100,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
};
|
||||
let mut bytes = vec![0xa5; repr.buffer_len()];
|
||||
|
||||
|
@ -87,13 +97,11 @@ mod wire {
|
|||
#[cfg(feature = "proto-ipv6")]
|
||||
fn bench_emit_ipv6(b: &mut test::Bencher) {
|
||||
let repr = Ipv6Repr {
|
||||
src_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1]),
|
||||
dst_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 2]),
|
||||
src_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
|
||||
dst_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]),
|
||||
next_header: IpProtocol::Tcp,
|
||||
payload_len: 100,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
};
|
||||
let mut bytes = vec![0xa5; repr.buffer_len()];
|
||||
|
||||
|
|
|
@ -2,28 +2,34 @@
|
|||
|
||||
mod utils;
|
||||
|
||||
use log::debug;
|
||||
use std::cmp;
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::atomic::{Ordering, AtomicBool};
|
||||
use std::thread;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use log::debug;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::thread;
|
||||
|
||||
use smoltcp::phy::{Device, Medium, wait as phy_wait};
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
|
||||
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
|
||||
use smoltcp::phy::{wait as phy_wait, Device, Medium};
|
||||
use smoltcp::socket::SocketSet;
|
||||
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
|
||||
use smoltcp::time::{Duration, Instant};
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||
|
||||
const AMOUNT: usize = 1_000_000_000;
|
||||
|
||||
enum Client { Reader, Writer }
|
||||
enum Client {
|
||||
Reader,
|
||||
Writer,
|
||||
}
|
||||
|
||||
fn client(kind: Client) {
|
||||
let port = match kind { Client::Reader => 1234, Client::Writer => 1235 };
|
||||
let port = match kind {
|
||||
Client::Reader => 1234,
|
||||
Client::Writer => 1235,
|
||||
};
|
||||
let mut stream = TcpStream::connect(("192.168.69.1", port)).unwrap();
|
||||
let mut buffer = vec![0; 1_000_000];
|
||||
|
||||
|
@ -42,7 +48,7 @@ fn client(kind: Client) {
|
|||
// print!("(P:{})", result);
|
||||
processed += result
|
||||
}
|
||||
Err(err) => panic!("cannot process: {}", err)
|
||||
Err(err) => panic!("cannot process: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,11 +75,11 @@ fn main() {
|
|||
let mut matches = utils::parse_options(&opts, free);
|
||||
let device = utils::parse_tuntap_options(&mut matches);
|
||||
let fd = device.as_raw_fd();
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
|
||||
let mode = match matches.free[0].as_ref() {
|
||||
"reader" => Client::Reader,
|
||||
"writer" => Client::Writer,
|
||||
_ => panic!("invalid mode")
|
||||
_ => panic!("invalid mode"),
|
||||
};
|
||||
|
||||
thread::spawn(move || client(mode));
|
||||
|
@ -91,8 +97,7 @@ fn main() {
|
|||
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
|
||||
let medium = device.capabilities().medium;
|
||||
let mut builder = InterfaceBuilder::new(device)
|
||||
.ip_addrs(ip_addrs);
|
||||
let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
|
||||
if medium == Medium::Ethernet {
|
||||
builder = builder
|
||||
.ethernet_addr(ethernet_addr)
|
||||
|
@ -109,13 +114,12 @@ fn main() {
|
|||
while !CLIENT_DONE.load(Ordering::SeqCst) {
|
||||
let timestamp = Instant::now();
|
||||
match iface.poll(&mut sockets, timestamp) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
debug!("poll error: {}",e);
|
||||
debug!("poll error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// tcp:1234: emit data
|
||||
{
|
||||
let mut socket = sockets.get::<TcpSocket>(tcp1_handle);
|
||||
|
@ -125,10 +129,12 @@ fn main() {
|
|||
|
||||
if socket.can_send() {
|
||||
if processed < AMOUNT {
|
||||
let length = socket.send(|buffer| {
|
||||
let length = socket
|
||||
.send(|buffer| {
|
||||
let length = cmp::min(buffer.len(), AMOUNT - processed);
|
||||
(length, length)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
processed += length;
|
||||
}
|
||||
}
|
||||
|
@ -143,10 +149,12 @@ fn main() {
|
|||
|
||||
if socket.can_recv() {
|
||||
if processed < AMOUNT {
|
||||
let length = socket.recv(|buffer| {
|
||||
let length = socket
|
||||
.recv(|buffer| {
|
||||
let length = cmp::min(buffer.len(), AMOUNT - processed);
|
||||
(length, length)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
processed += length;
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +163,7 @@ fn main() {
|
|||
match iface.poll_at(&sockets, timestamp) {
|
||||
Some(poll_at) if timestamp < poll_at => {
|
||||
phy_wait(fd, Some(poll_at - timestamp)).expect("wait error");
|
||||
},
|
||||
}
|
||||
Some(_) => (),
|
||||
None => {
|
||||
phy_wait(fd, default_timeout).expect("wait error");
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
mod utils;
|
||||
|
||||
use std::str::{self, FromStr};
|
||||
use log::debug;
|
||||
use std::collections::BTreeMap;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use log::debug;
|
||||
use std::str::{self, FromStr};
|
||||
|
||||
use smoltcp::phy::{Device, Medium, wait as phy_wait};
|
||||
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr};
|
||||
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
|
||||
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
|
||||
use smoltcp::phy::{wait as phy_wait, Device, Medium};
|
||||
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
|
||||
|
||||
fn main() {
|
||||
utils::setup_logging("");
|
||||
|
@ -24,7 +24,7 @@ fn main() {
|
|||
let device = utils::parse_tuntap_options(&mut matches);
|
||||
|
||||
let fd = device.as_raw_fd();
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
|
||||
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
|
||||
let port = u16::from_str(&matches.free[1]).expect("invalid port format");
|
||||
|
||||
|
@ -64,7 +64,7 @@ fn main() {
|
|||
loop {
|
||||
let timestamp = Instant::now();
|
||||
match iface.poll(&mut sockets, timestamp) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
debug!("poll error: {}", e);
|
||||
}
|
||||
|
@ -76,25 +76,31 @@ fn main() {
|
|||
debug!("connected");
|
||||
} else if !socket.is_active() && tcp_active {
|
||||
debug!("disconnected");
|
||||
break
|
||||
break;
|
||||
}
|
||||
tcp_active = socket.is_active();
|
||||
|
||||
if socket.may_recv() {
|
||||
let data = socket.recv(|data| {
|
||||
let data = socket
|
||||
.recv(|data| {
|
||||
let mut data = data.to_owned();
|
||||
if !data.is_empty() {
|
||||
debug!("recv data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
|
||||
debug!(
|
||||
"recv data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
|
||||
);
|
||||
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
|
||||
data.reverse();
|
||||
data.extend(b"\n");
|
||||
}
|
||||
(data.len(), data)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
if socket.can_send() && !data.is_empty() {
|
||||
debug!("send data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
|
||||
debug!(
|
||||
"send data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
|
||||
);
|
||||
socket.send_slice(&data[..]).unwrap();
|
||||
}
|
||||
} else if socket.may_send() {
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
#![allow(clippy::option_map_unit_fn)]
|
||||
mod utils;
|
||||
|
||||
use log::*;
|
||||
use std::collections::BTreeMap;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use log::*;
|
||||
|
||||
use smoltcp::{phy::{Device, Medium, wait as phy_wait}, time::Duration};
|
||||
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr};
|
||||
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Interface, Routes};
|
||||
use smoltcp::socket::{SocketSet, Dhcpv4Socket, Dhcpv4Event};
|
||||
use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes};
|
||||
use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket, SocketSet};
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr};
|
||||
use smoltcp::{
|
||||
phy::{wait as phy_wait, Device, Medium},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "log")]
|
||||
|
@ -22,7 +25,7 @@ fn main() {
|
|||
let mut matches = utils::parse_options(&opts, free);
|
||||
let device = utils::parse_tuntap_options(&mut matches);
|
||||
let fd = device.as_raw_fd();
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
|
||||
|
||||
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||
|
@ -92,11 +95,11 @@ fn main() {
|
|||
}
|
||||
|
||||
fn set_ipv4_addr<DeviceT>(iface: &mut Interface<'_, DeviceT>, cidr: Ipv4Cidr)
|
||||
where DeviceT: for<'d> Device<'d>
|
||||
where
|
||||
DeviceT: for<'d> Device<'d>,
|
||||
{
|
||||
iface.update_ip_addrs(|addrs| {
|
||||
let dest = addrs.iter_mut().next().unwrap();
|
||||
*dest = IpCidr::Ipv4(cidr);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
mod utils;
|
||||
|
||||
use std::str::{self, FromStr};
|
||||
use log::debug;
|
||||
use std::collections::BTreeMap;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::str::{self, FromStr};
|
||||
use url::Url;
|
||||
use log::debug;
|
||||
|
||||
use smoltcp::phy::{Device, Medium, wait as phy_wait};
|
||||
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr};
|
||||
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
|
||||
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
|
||||
use smoltcp::phy::{wait as phy_wait, Device, Medium};
|
||||
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address};
|
||||
|
||||
fn main() {
|
||||
utils::setup_logging("");
|
||||
|
@ -24,11 +24,10 @@ fn main() {
|
|||
let mut matches = utils::parse_options(&opts, free);
|
||||
let device = utils::parse_tuntap_options(&mut matches);
|
||||
let fd = device.as_raw_fd();
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
|
||||
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
|
||||
let url = Url::parse(&matches.free[1]).expect("invalid url format");
|
||||
|
||||
|
||||
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||
|
||||
let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 1024]);
|
||||
|
@ -36,9 +35,11 @@ fn main() {
|
|||
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);
|
||||
|
||||
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
|
||||
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
|
||||
let ip_addrs = [
|
||||
IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
|
||||
IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64),
|
||||
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)];
|
||||
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64),
|
||||
];
|
||||
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
|
||||
let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100);
|
||||
let mut routes_storage = [None; 2];
|
||||
|
@ -60,15 +61,19 @@ fn main() {
|
|||
let mut sockets = SocketSet::new(vec![]);
|
||||
let tcp_handle = sockets.add(tcp_socket);
|
||||
|
||||
enum State { Connect, Request, Response }
|
||||
enum State {
|
||||
Connect,
|
||||
Request,
|
||||
Response,
|
||||
}
|
||||
let mut state = State::Connect;
|
||||
|
||||
loop {
|
||||
let timestamp = Instant::now();
|
||||
match iface.poll(&mut sockets, timestamp) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
debug!("poll error: {}",e);
|
||||
debug!("poll error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +84,9 @@ fn main() {
|
|||
State::Connect if !socket.is_active() => {
|
||||
debug!("connecting");
|
||||
let local_port = 49152 + rand::random::<u16>() % 16384;
|
||||
socket.connect((address, url.port().unwrap_or(80)), local_port).unwrap();
|
||||
socket
|
||||
.connect((address, url.port().unwrap_or(80)), local_port)
|
||||
.unwrap();
|
||||
State::Request
|
||||
}
|
||||
State::Request if socket.may_send() => {
|
||||
|
@ -88,22 +95,26 @@ fn main() {
|
|||
socket.send_slice(http_get.as_ref()).expect("cannot send");
|
||||
let http_host = "Host: ".to_owned() + url.host_str().unwrap() + "\r\n";
|
||||
socket.send_slice(http_host.as_ref()).expect("cannot send");
|
||||
socket.send_slice(b"Connection: close\r\n").expect("cannot send");
|
||||
socket
|
||||
.send_slice(b"Connection: close\r\n")
|
||||
.expect("cannot send");
|
||||
socket.send_slice(b"\r\n").expect("cannot send");
|
||||
State::Response
|
||||
}
|
||||
State::Response if socket.can_recv() => {
|
||||
socket.recv(|data| {
|
||||
socket
|
||||
.recv(|data| {
|
||||
println!("{}", str::from_utf8(data).unwrap_or("(invalid utf8)"));
|
||||
(data.len(), ())
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
State::Response
|
||||
}
|
||||
State::Response if !socket.may_recv() => {
|
||||
debug!("received complete response");
|
||||
break
|
||||
break;
|
||||
}
|
||||
_ => state
|
||||
_ => state,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,18 +7,18 @@
|
|||
mod utils;
|
||||
|
||||
use core::str;
|
||||
use log::{info, debug, error};
|
||||
use log::{debug, error, info};
|
||||
|
||||
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
|
||||
use smoltcp::phy::{Loopback, Medium};
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
|
||||
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
|
||||
use smoltcp::time::{Duration, Instant};
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod mock {
|
||||
use smoltcp::time::{Duration, Instant};
|
||||
use core::cell::Cell;
|
||||
use smoltcp::time::{Duration, Instant};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
@ -41,9 +41,9 @@ mod mock {
|
|||
|
||||
#[cfg(feature = "std")]
|
||||
mod mock {
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{Ordering, AtomicUsize};
|
||||
use smoltcp::time::{Duration, Instant};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
// should be AtomicU64 but that's unstable
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -56,7 +56,8 @@ mod mock {
|
|||
}
|
||||
|
||||
pub fn advance(&self, duration: Duration) {
|
||||
self.0.fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
|
||||
self.0
|
||||
.fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
pub fn elapsed(&self) -> Instant {
|
||||
|
@ -78,7 +79,7 @@ fn main() {
|
|||
utils::add_middleware_options(&mut opts, &mut free);
|
||||
|
||||
let mut matches = utils::parse_options(&opts, free);
|
||||
utils::parse_middleware_options(&mut matches, device, /*loopback=*/true)
|
||||
utils::parse_middleware_options(&mut matches, device, /*loopback=*/ true)
|
||||
};
|
||||
|
||||
let mut neighbor_cache_entries = [None; 8];
|
||||
|
@ -121,7 +122,7 @@ fn main() {
|
|||
let mut done = false;
|
||||
while !done && clock.elapsed() < Instant::from_millis(10_000) {
|
||||
match iface.poll(&mut socket_set, clock.elapsed()) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
debug!("poll error: {}", e);
|
||||
}
|
||||
|
@ -138,9 +139,10 @@ fn main() {
|
|||
}
|
||||
|
||||
if socket.can_recv() {
|
||||
debug!("got {:?}", socket.recv(|buffer| {
|
||||
(buffer.len(), str::from_utf8(buffer).unwrap())
|
||||
}));
|
||||
debug!(
|
||||
"got {:?}",
|
||||
socket.recv(|buffer| { (buffer.len(), str::from_utf8(buffer).unwrap()) })
|
||||
);
|
||||
socket.close();
|
||||
done = true;
|
||||
}
|
||||
|
@ -151,8 +153,12 @@ fn main() {
|
|||
if !socket.is_open() {
|
||||
if !did_connect {
|
||||
debug!("connecting");
|
||||
socket.connect((IpAddress::v4(127, 0, 0, 1), 1234),
|
||||
(IpAddress::Unspecified, 65000)).unwrap();
|
||||
socket
|
||||
.connect(
|
||||
(IpAddress::v4(127, 0, 0, 1), 1234),
|
||||
(IpAddress::Unspecified, 65000),
|
||||
)
|
||||
.unwrap();
|
||||
did_connect = true;
|
||||
}
|
||||
}
|
||||
|
@ -169,8 +175,8 @@ fn main() {
|
|||
Some(delay) => {
|
||||
debug!("sleeping for {} ms", delay);
|
||||
clock.advance(delay)
|
||||
},
|
||||
None => clock.advance(Duration::from_millis(1))
|
||||
}
|
||||
None => clock.advance(Duration::from_millis(1)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
mod utils;
|
||||
|
||||
use log::debug;
|
||||
use std::collections::BTreeMap;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use log::debug;
|
||||
|
||||
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
|
||||
use smoltcp::phy::wait as phy_wait;
|
||||
use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr, Ipv4Address,
|
||||
Ipv4Packet, IgmpPacket, IgmpRepr};
|
||||
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
|
||||
use smoltcp::socket::{SocketSet,
|
||||
RawSocket, RawSocketBuffer, RawPacketMetadata,
|
||||
UdpSocket, UdpSocketBuffer, UdpPacketMetadata};
|
||||
use smoltcp::socket::{
|
||||
RawPacketMetadata, RawSocket, RawSocketBuffer, SocketSet, UdpPacketMetadata, UdpSocket,
|
||||
UdpSocketBuffer,
|
||||
};
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{
|
||||
EthernetAddress, IgmpPacket, IgmpRepr, IpAddress, IpCidr, IpProtocol, IpVersion, Ipv4Address,
|
||||
Ipv4Packet,
|
||||
};
|
||||
|
||||
const MDNS_PORT: u16 = 5353;
|
||||
const MDNS_GROUP: [u8; 4] = [224, 0, 0, 251];
|
||||
|
@ -26,10 +29,7 @@ fn main() {
|
|||
let mut matches = utils::parse_options(&opts, free);
|
||||
let device = utils::parse_tuntap_options(&mut matches);
|
||||
let fd = device.as_raw_fd();
|
||||
let device = utils::parse_middleware_options(&mut matches,
|
||||
device,
|
||||
/*loopback=*/
|
||||
false);
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
|
||||
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||
|
||||
let local_addr = Ipv4Address::new(192, 168, 69, 2);
|
||||
|
@ -46,7 +46,9 @@ fn main() {
|
|||
|
||||
let now = Instant::now();
|
||||
// Join a multicast group to receive mDNS traffic
|
||||
iface.join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now).unwrap();
|
||||
iface
|
||||
.join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now)
|
||||
.unwrap();
|
||||
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
|
||||
|
@ -55,8 +57,10 @@ fn main() {
|
|||
// Will not send IGMP
|
||||
let raw_tx_buffer = RawSocketBuffer::new(vec![], vec![]);
|
||||
let raw_socket = RawSocket::new(
|
||||
IpVersion::Ipv4, IpProtocol::Igmp,
|
||||
raw_rx_buffer, raw_tx_buffer
|
||||
IpVersion::Ipv4,
|
||||
IpProtocol::Igmp,
|
||||
raw_rx_buffer,
|
||||
raw_tx_buffer,
|
||||
);
|
||||
let raw_handle = sockets.add(raw_socket);
|
||||
|
||||
|
@ -70,9 +74,9 @@ fn main() {
|
|||
loop {
|
||||
let timestamp = Instant::now();
|
||||
match iface.poll(&mut sockets, timestamp) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
debug!("poll error: {}",e);
|
||||
debug!("poll error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,7 +86,8 @@ fn main() {
|
|||
if socket.can_recv() {
|
||||
// For display purposes only - normally we wouldn't process incoming IGMP packets
|
||||
// in the application layer
|
||||
socket.recv()
|
||||
socket
|
||||
.recv()
|
||||
.and_then(Ipv4Packet::new_checked)
|
||||
.and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload()))
|
||||
.and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet))
|
||||
|
@ -97,8 +102,11 @@ fn main() {
|
|||
}
|
||||
|
||||
if socket.can_recv() {
|
||||
socket.recv()
|
||||
.map(|(data, sender)| println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender))
|
||||
socket
|
||||
.recv()
|
||||
.map(|(data, sender)| {
|
||||
println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender)
|
||||
})
|
||||
.unwrap_or_else(|e| println!("Recv UDP error: {:?}", e));
|
||||
}
|
||||
}
|
||||
|
|
177
examples/ping.rs
177
examples/ping.rs
|
@ -1,21 +1,25 @@
|
|||
mod utils;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::collections::BTreeMap;
|
||||
use std::cmp;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::collections::HashMap;
|
||||
use log::debug;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use log::debug;
|
||||
use std::cmp;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::str::FromStr;
|
||||
|
||||
use smoltcp::{phy::Medium, time::{Duration, Instant}};
|
||||
use smoltcp::phy::Device;
|
||||
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
|
||||
use smoltcp::phy::wait as phy_wait;
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr,
|
||||
Ipv6Address, Icmpv6Repr, Icmpv6Packet,
|
||||
Ipv4Address, Icmpv4Repr, Icmpv4Packet};
|
||||
use smoltcp::iface::{NeighborCache, InterfaceBuilder, Routes};
|
||||
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint};
|
||||
use smoltcp::phy::Device;
|
||||
use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer, SocketSet};
|
||||
use smoltcp::wire::{
|
||||
EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr,
|
||||
Ipv4Address, Ipv6Address,
|
||||
};
|
||||
use smoltcp::{
|
||||
phy::Medium,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
macro_rules! send_icmp_ping {
|
||||
( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
|
||||
|
@ -26,13 +30,11 @@ macro_rules! send_icmp_ping {
|
|||
data: &$echo_payload,
|
||||
};
|
||||
|
||||
let icmp_payload = $socket
|
||||
.send(icmp_repr.buffer_len(), $remote_addr)
|
||||
.unwrap();
|
||||
let icmp_payload = $socket.send(icmp_repr.buffer_len(), $remote_addr).unwrap();
|
||||
|
||||
let icmp_packet = $packet_type::new_unchecked(icmp_payload);
|
||||
(icmp_repr, icmp_packet)
|
||||
}}
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! get_icmp_pong {
|
||||
|
@ -41,14 +43,18 @@ macro_rules! get_icmp_pong {
|
|||
if let $repr_type::EchoReply { seq_no, data, .. } = $repr {
|
||||
if let Some(_) = $waiting_queue.get(&seq_no) {
|
||||
let packet_timestamp_ms = NetworkEndian::read_i64(data);
|
||||
println!("{} bytes from {}: icmp_seq={}, time={}ms",
|
||||
data.len(), $remote_addr, seq_no,
|
||||
$timestamp.total_millis() - packet_timestamp_ms);
|
||||
println!(
|
||||
"{} bytes from {}: icmp_seq={}, time={}ms",
|
||||
data.len(),
|
||||
$remote_addr,
|
||||
seq_no,
|
||||
$timestamp.total_millis() - packet_timestamp_ms
|
||||
);
|
||||
$waiting_queue.remove(&seq_no);
|
||||
$received += 1;
|
||||
}
|
||||
}
|
||||
}}
|
||||
}};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -57,26 +63,45 @@ fn main() {
|
|||
let (mut opts, mut free) = utils::create_options();
|
||||
utils::add_tuntap_options(&mut opts, &mut free);
|
||||
utils::add_middleware_options(&mut opts, &mut free);
|
||||
opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT");
|
||||
opts.optopt("i", "interval",
|
||||
"Interval between successive packets sent (seconds) (default: 1)", "INTERVAL");
|
||||
opts.optopt("", "timeout",
|
||||
opts.optopt(
|
||||
"c",
|
||||
"count",
|
||||
"Amount of echo request packets to send (default: 4)",
|
||||
"COUNT",
|
||||
);
|
||||
opts.optopt(
|
||||
"i",
|
||||
"interval",
|
||||
"Interval between successive packets sent (seconds) (default: 1)",
|
||||
"INTERVAL",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"timeout",
|
||||
"Maximum wait duration for an echo response packet (seconds) (default: 5)",
|
||||
"TIMEOUT");
|
||||
"TIMEOUT",
|
||||
);
|
||||
free.push("ADDRESS");
|
||||
|
||||
let mut matches = utils::parse_options(&opts, free);
|
||||
let device = utils::parse_tuntap_options(&mut matches);
|
||||
let fd = device.as_raw_fd();
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
|
||||
let device_caps = device.capabilities();
|
||||
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
|
||||
let count = matches.opt_str("count").map(|s| usize::from_str(&s).unwrap()).unwrap_or(4);
|
||||
let interval = matches.opt_str("interval")
|
||||
let count = matches
|
||||
.opt_str("count")
|
||||
.map(|s| usize::from_str(&s).unwrap())
|
||||
.unwrap_or(4);
|
||||
let interval = matches
|
||||
.opt_str("interval")
|
||||
.map(|s| Duration::from_secs(u64::from_str(&s).unwrap()))
|
||||
.unwrap_or_else(|| Duration::from_secs(1));
|
||||
let timeout = Duration::from_secs(
|
||||
matches.opt_str("timeout").map(|s| u64::from_str(&s).unwrap()).unwrap_or(5)
|
||||
matches
|
||||
.opt_str("timeout")
|
||||
.map(|s| u64::from_str(&s).unwrap())
|
||||
.unwrap_or(5),
|
||||
);
|
||||
|
||||
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||
|
@ -89,9 +114,11 @@ fn main() {
|
|||
|
||||
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
|
||||
let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1);
|
||||
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
|
||||
let ip_addrs = [
|
||||
IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
|
||||
IpCidr::new(src_ipv6, 64),
|
||||
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)];
|
||||
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64),
|
||||
];
|
||||
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
|
||||
let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100);
|
||||
let mut routes_storage = [None; 2];
|
||||
|
@ -123,7 +150,7 @@ fn main() {
|
|||
loop {
|
||||
let timestamp = Instant::now();
|
||||
match iface.poll(&mut sockets, timestamp) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
debug!("poll error: {}", e);
|
||||
}
|
||||
|
@ -137,25 +164,40 @@ fn main() {
|
|||
send_at = timestamp;
|
||||
}
|
||||
|
||||
if socket.can_send() && seq_no < count as u16 &&
|
||||
send_at <= timestamp {
|
||||
if socket.can_send() && seq_no < count as u16 && send_at <= timestamp {
|
||||
NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis());
|
||||
|
||||
match remote_addr {
|
||||
IpAddress::Ipv4(_) => {
|
||||
let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
|
||||
Icmpv4Repr, Icmpv4Packet, ident, seq_no,
|
||||
echo_payload, socket, remote_addr);
|
||||
Icmpv4Repr,
|
||||
Icmpv4Packet,
|
||||
ident,
|
||||
seq_no,
|
||||
echo_payload,
|
||||
socket,
|
||||
remote_addr
|
||||
);
|
||||
icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);
|
||||
},
|
||||
}
|
||||
IpAddress::Ipv6(_) => {
|
||||
let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
|
||||
Icmpv6Repr, Icmpv6Packet, ident, seq_no,
|
||||
echo_payload, socket, remote_addr);
|
||||
icmp_repr.emit(&src_ipv6, &remote_addr,
|
||||
&mut icmp_packet, &device_caps.checksum);
|
||||
},
|
||||
_ => unimplemented!()
|
||||
Icmpv6Repr,
|
||||
Icmpv6Packet,
|
||||
ident,
|
||||
seq_no,
|
||||
echo_payload,
|
||||
socket,
|
||||
remote_addr
|
||||
);
|
||||
icmp_repr.emit(
|
||||
&src_ipv6,
|
||||
&remote_addr,
|
||||
&mut icmp_packet,
|
||||
&device_caps.checksum,
|
||||
);
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
||||
waiting_queue.insert(seq_no, timestamp);
|
||||
|
@ -171,17 +213,36 @@ fn main() {
|
|||
let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap();
|
||||
let icmp_repr =
|
||||
Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();
|
||||
get_icmp_pong!(Icmpv4Repr, icmp_repr, payload,
|
||||
waiting_queue, remote_addr, timestamp, received);
|
||||
get_icmp_pong!(
|
||||
Icmpv4Repr,
|
||||
icmp_repr,
|
||||
payload,
|
||||
waiting_queue,
|
||||
remote_addr,
|
||||
timestamp,
|
||||
received
|
||||
);
|
||||
}
|
||||
IpAddress::Ipv6(_) => {
|
||||
let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap();
|
||||
let icmp_repr = Icmpv6Repr::parse(&remote_addr, &src_ipv6,
|
||||
&icmp_packet, &device_caps.checksum).unwrap();
|
||||
get_icmp_pong!(Icmpv6Repr, icmp_repr, payload,
|
||||
waiting_queue, remote_addr, timestamp, received);
|
||||
},
|
||||
_ => unimplemented!()
|
||||
let icmp_repr = Icmpv6Repr::parse(
|
||||
&remote_addr,
|
||||
&src_ipv6,
|
||||
&icmp_packet,
|
||||
&device_caps.checksum,
|
||||
)
|
||||
.unwrap();
|
||||
get_icmp_pong!(
|
||||
Icmpv6Repr,
|
||||
icmp_repr,
|
||||
payload,
|
||||
waiting_queue,
|
||||
remote_addr,
|
||||
timestamp,
|
||||
received
|
||||
);
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +256,7 @@ fn main() {
|
|||
});
|
||||
|
||||
if seq_no == count as u16 && waiting_queue.is_empty() {
|
||||
break
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,7 +265,7 @@ fn main() {
|
|||
Some(poll_at) if timestamp < poll_at => {
|
||||
let resume_at = cmp::min(poll_at, send_at);
|
||||
phy_wait(fd, Some(resume_at - timestamp)).expect("wait error");
|
||||
},
|
||||
}
|
||||
Some(_) => (),
|
||||
None => {
|
||||
phy_wait(fd, Some(send_at - timestamp)).expect("wait error");
|
||||
|
@ -213,6 +274,10 @@ fn main() {
|
|||
}
|
||||
|
||||
println!("--- {} ping statistics ---", remote_addr);
|
||||
println!("{} packets transmitted, {} received, {:.0}% packet loss",
|
||||
seq_no, received, 100.0 * (seq_no - received) as f64 / seq_no as f64);
|
||||
println!(
|
||||
"{} packets transmitted, {} received, {:.0}% packet loss",
|
||||
seq_no,
|
||||
received,
|
||||
100.0 * (seq_no - received) as f64 / seq_no as f64
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
mod utils;
|
||||
|
||||
use std::str;
|
||||
use log::debug;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Write;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use log::debug;
|
||||
use std::str;
|
||||
|
||||
use smoltcp::phy::{Device, Medium, wait as phy_wait};
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||
use smoltcp::iface::{NeighborCache, InterfaceBuilder};
|
||||
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
|
||||
use smoltcp::phy::{wait as phy_wait, Device, Medium};
|
||||
use smoltcp::socket::SocketSet;
|
||||
use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata};
|
||||
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
|
||||
use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
|
||||
use smoltcp::time::{Duration, Instant};
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
|
||||
|
||||
fn main() {
|
||||
utils::setup_logging("");
|
||||
|
@ -24,7 +24,7 @@ fn main() {
|
|||
let mut matches = utils::parse_options(&opts, free);
|
||||
let device = utils::parse_tuntap_options(&mut matches);
|
||||
let fd = device.as_raw_fd();
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
|
||||
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
|
||||
|
||||
let neighbor_cache = NeighborCache::new(BTreeMap::new());
|
||||
|
||||
|
@ -52,12 +52,11 @@ fn main() {
|
|||
let ip_addrs = [
|
||||
IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
|
||||
IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64),
|
||||
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)
|
||||
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64),
|
||||
];
|
||||
|
||||
let medium = device.capabilities().medium;
|
||||
let mut builder = InterfaceBuilder::new(device)
|
||||
.ip_addrs(ip_addrs);
|
||||
let mut builder = InterfaceBuilder::new(device).ip_addrs(ip_addrs);
|
||||
if medium == Medium::Ethernet {
|
||||
builder = builder
|
||||
.ethernet_addr(ethernet_addr)
|
||||
|
@ -76,7 +75,7 @@ fn main() {
|
|||
loop {
|
||||
let timestamp = Instant::now();
|
||||
match iface.poll(&mut sockets, timestamp) {
|
||||
Ok(_) => {},
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
debug!("poll error: {}", e);
|
||||
}
|
||||
|
@ -91,16 +90,21 @@ fn main() {
|
|||
|
||||
let client = match socket.recv() {
|
||||
Ok((data, endpoint)) => {
|
||||
debug!("udp:6969 recv data: {:?} from {}",
|
||||
str::from_utf8(data).unwrap(), endpoint);
|
||||
debug!(
|
||||
"udp:6969 recv data: {:?} from {}",
|
||||
str::from_utf8(data).unwrap(),
|
||||
endpoint
|
||||
);
|
||||
Some(endpoint)
|
||||
}
|
||||
Err(_) => None
|
||||
Err(_) => None,
|
||||
};
|
||||
if let Some(endpoint) = client {
|
||||
let data = b"hello\n";
|
||||
debug!("udp:6969 send data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap());
|
||||
debug!(
|
||||
"udp:6969 send data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap()
|
||||
);
|
||||
socket.send_slice(data, endpoint).unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -135,21 +139,27 @@ fn main() {
|
|||
tcp_6970_active = socket.is_active();
|
||||
|
||||
if socket.may_recv() {
|
||||
let data = socket.recv(|buffer| {
|
||||
let data = socket
|
||||
.recv(|buffer| {
|
||||
let recvd_len = buffer.len();
|
||||
let mut data = buffer.to_owned();
|
||||
if !data.is_empty() {
|
||||
debug!("tcp:6970 recv data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
|
||||
debug!(
|
||||
"tcp:6970 recv data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
|
||||
);
|
||||
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
|
||||
data.reverse();
|
||||
data.extend(b"\n");
|
||||
}
|
||||
(recvd_len, data)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
if socket.can_send() && !data.is_empty() {
|
||||
debug!("tcp:6970 send data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
|
||||
debug!(
|
||||
"tcp:6970 send data: {:?}",
|
||||
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
|
||||
);
|
||||
socket.send_slice(&data[..]).unwrap();
|
||||
}
|
||||
} else if socket.may_send() {
|
||||
|
@ -168,12 +178,14 @@ fn main() {
|
|||
}
|
||||
|
||||
if socket.may_recv() {
|
||||
socket.recv(|buffer| {
|
||||
socket
|
||||
.recv(|buffer| {
|
||||
if !buffer.is_empty() {
|
||||
debug!("tcp:6971 recv {:?} octets", buffer.len());
|
||||
}
|
||||
(buffer.len(), ())
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
} else if socket.may_send() {
|
||||
socket.close();
|
||||
}
|
||||
|
@ -187,7 +199,8 @@ fn main() {
|
|||
}
|
||||
|
||||
if socket.may_send() {
|
||||
socket.send(|data| {
|
||||
socket
|
||||
.send(|data| {
|
||||
if !data.is_empty() {
|
||||
debug!("tcp:6972 send {:?} octets", data.len());
|
||||
for (i, b) in data.iter_mut().enumerate() {
|
||||
|
@ -195,7 +208,8 @@ fn main() {
|
|||
}
|
||||
}
|
||||
(data.len(), ())
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use smoltcp::phy::wait as phy_wait;
|
||||
use smoltcp::phy::{Device, RawSocket, RxToken};
|
||||
use smoltcp::time::Instant;
|
||||
use smoltcp::wire::{EthernetFrame, PrettyPrinter};
|
||||
use std::env;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use smoltcp::phy::wait as phy_wait;
|
||||
use smoltcp::phy::{Device, RxToken, RawSocket};
|
||||
use smoltcp::wire::{PrettyPrinter, EthernetFrame};
|
||||
use smoltcp::time::Instant;
|
||||
|
||||
fn main() {
|
||||
let ifname = env::args().nth(1).unwrap();
|
||||
|
@ -11,9 +11,14 @@ fn main() {
|
|||
loop {
|
||||
phy_wait(socket.as_raw_fd(), None).unwrap();
|
||||
let (rx_token, _) = socket.receive().unwrap();
|
||||
rx_token.consume(Instant::now(), |buffer| {
|
||||
println!("{}", PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer));
|
||||
rx_token
|
||||
.consume(Instant::now(), |buffer| {
|
||||
println!(
|
||||
"{}",
|
||||
PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer)
|
||||
);
|
||||
Ok(())
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,43 +1,59 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::str::{self, FromStr};
|
||||
use std::rc::Rc;
|
||||
use std::io::{self, Write};
|
||||
use std::fs::File;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use std::env;
|
||||
use std::process;
|
||||
#[cfg(feature = "log")]
|
||||
use log::{Level, LevelFilter, trace};
|
||||
#[cfg(feature = "log")]
|
||||
use env_logger::Builder;
|
||||
use getopts::{Options, Matches};
|
||||
use getopts::{Matches, Options};
|
||||
#[cfg(feature = "log")]
|
||||
use log::{trace, Level, LevelFilter};
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Write};
|
||||
use std::process;
|
||||
use std::rc::Rc;
|
||||
use std::str::{self, FromStr};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use smoltcp::phy::{Device, Tracer, FaultInjector, Medium};
|
||||
use smoltcp::phy::RawSocket;
|
||||
#[cfg(feature = "phy-tuntap_interface")]
|
||||
use smoltcp::phy::TunTapInterface;
|
||||
use smoltcp::phy::{PcapWriter, PcapSink, PcapMode};
|
||||
use smoltcp::phy::RawSocket;
|
||||
use smoltcp::phy::{Device, FaultInjector, Medium, Tracer};
|
||||
use smoltcp::phy::{PcapMode, PcapSink, PcapWriter};
|
||||
use smoltcp::time::{Duration, Instant};
|
||||
|
||||
#[cfg(feature = "log")]
|
||||
pub fn setup_logging_with_clock<F>(filter: &str, since_startup: F)
|
||||
where F: Fn() -> Instant + Send + Sync + 'static {
|
||||
where
|
||||
F: Fn() -> Instant + Send + Sync + 'static,
|
||||
{
|
||||
Builder::new()
|
||||
.format(move |buf, record| {
|
||||
let elapsed = since_startup();
|
||||
let timestamp = format!("[{}]", elapsed);
|
||||
if record.target().starts_with("smoltcp::") {
|
||||
writeln!(buf, "\x1b[0m{} ({}): {}\x1b[0m", timestamp,
|
||||
record.target().replace("smoltcp::", ""), record.args())
|
||||
writeln!(
|
||||
buf,
|
||||
"\x1b[0m{} ({}): {}\x1b[0m",
|
||||
timestamp,
|
||||
record.target().replace("smoltcp::", ""),
|
||||
record.args()
|
||||
)
|
||||
} else if record.level() == Level::Trace {
|
||||
let message = format!("{}", record.args());
|
||||
writeln!(buf, "\x1b[37m{} {}\x1b[0m", timestamp,
|
||||
message.replace("\n", "\n "))
|
||||
writeln!(
|
||||
buf,
|
||||
"\x1b[37m{} {}\x1b[0m",
|
||||
timestamp,
|
||||
message.replace("\n", "\n ")
|
||||
)
|
||||
} else {
|
||||
writeln!(buf, "\x1b[32m{} ({}): {}\x1b[0m", timestamp,
|
||||
record.target(), record.args())
|
||||
writeln!(
|
||||
buf,
|
||||
"\x1b[32m{} ({}): {}\x1b[0m",
|
||||
timestamp,
|
||||
record.target(),
|
||||
record.args()
|
||||
)
|
||||
}
|
||||
})
|
||||
.filter(None, LevelFilter::Trace)
|
||||
|
@ -48,9 +64,7 @@ pub fn setup_logging_with_clock<F>(filter: &str, since_startup: F)
|
|||
|
||||
#[cfg(feature = "log")]
|
||||
pub fn setup_logging(filter: &str) {
|
||||
setup_logging_with_clock(filter, move || {
|
||||
Instant::now()
|
||||
})
|
||||
setup_logging_with_clock(filter, Instant::now)
|
||||
}
|
||||
|
||||
pub fn create_options() -> (Options, Vec<&'static str>) {
|
||||
|
@ -67,10 +81,17 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches {
|
|||
}
|
||||
Ok(matches) => {
|
||||
if matches.opt_present("h") || matches.free.len() != free.len() {
|
||||
let brief = format!("Usage: {} [OPTION]... {}",
|
||||
env::args().next().unwrap(), free.join(" "));
|
||||
let brief = format!(
|
||||
"Usage: {} [OPTION]... {}",
|
||||
env::args().next().unwrap(),
|
||||
free.join(" ")
|
||||
);
|
||||
print!("{}", options.usage(&brief));
|
||||
process::exit(if matches.free.len() != free.len() { 1 } else { 0 })
|
||||
process::exit(if matches.free.len() != free.len() {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
})
|
||||
}
|
||||
matches
|
||||
}
|
||||
|
@ -86,7 +107,7 @@ pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) {
|
|||
pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface {
|
||||
let tun = matches.opt_str("tun");
|
||||
let tap = matches.opt_str("tap");
|
||||
match(tun,tap) {
|
||||
match (tun, tap) {
|
||||
(Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(),
|
||||
(None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(),
|
||||
_ => panic!("You must specify exactly one of --tun or --tap"),
|
||||
|
@ -100,31 +121,77 @@ pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket {
|
|||
|
||||
pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) {
|
||||
opts.optopt("", "pcap", "Write a packet capture file", "FILE");
|
||||
opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE");
|
||||
opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE");
|
||||
opts.optopt("", "size-limit", "Drop packets larger than given size (octets)", "SIZE");
|
||||
opts.optopt("", "tx-rate-limit", "Drop packets after transmit rate exceeds given limit \
|
||||
(packets per interval)", "RATE");
|
||||
opts.optopt("", "rx-rate-limit", "Drop packets after transmit rate exceeds given limit \
|
||||
(packets per interval)", "RATE");
|
||||
opts.optopt("", "shaping-interval", "Sets the interval for rate limiting (ms)", "RATE");
|
||||
opts.optopt(
|
||||
"",
|
||||
"drop-chance",
|
||||
"Chance of dropping a packet (%)",
|
||||
"CHANCE",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"corrupt-chance",
|
||||
"Chance of corrupting a packet (%)",
|
||||
"CHANCE",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"size-limit",
|
||||
"Drop packets larger than given size (octets)",
|
||||
"SIZE",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"tx-rate-limit",
|
||||
"Drop packets after transmit rate exceeds given limit \
|
||||
(packets per interval)",
|
||||
"RATE",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"rx-rate-limit",
|
||||
"Drop packets after transmit rate exceeds given limit \
|
||||
(packets per interval)",
|
||||
"RATE",
|
||||
);
|
||||
opts.optopt(
|
||||
"",
|
||||
"shaping-interval",
|
||||
"Sets the interval for rate limiting (ms)",
|
||||
"RATE",
|
||||
);
|
||||
}
|
||||
|
||||
pub fn parse_middleware_options<D>(matches: &mut Matches, device: D, loopback: bool)
|
||||
-> FaultInjector<Tracer<PcapWriter<D, Rc<dyn PcapSink>>>>
|
||||
where D: for<'a> Device<'a>
|
||||
pub fn parse_middleware_options<D>(
|
||||
matches: &mut Matches,
|
||||
device: D,
|
||||
loopback: bool,
|
||||
) -> FaultInjector<Tracer<PcapWriter<D, Rc<dyn PcapSink>>>>
|
||||
where
|
||||
D: for<'a> Device<'a>,
|
||||
{
|
||||
let drop_chance = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap())
|
||||
let drop_chance = matches
|
||||
.opt_str("drop-chance")
|
||||
.map(|s| u8::from_str(&s).unwrap())
|
||||
.unwrap_or(0);
|
||||
let corrupt_chance = matches.opt_str("corrupt-chance").map(|s| u8::from_str(&s).unwrap())
|
||||
let corrupt_chance = matches
|
||||
.opt_str("corrupt-chance")
|
||||
.map(|s| u8::from_str(&s).unwrap())
|
||||
.unwrap_or(0);
|
||||
let size_limit = matches.opt_str("size-limit").map(|s| usize::from_str(&s).unwrap())
|
||||
let size_limit = matches
|
||||
.opt_str("size-limit")
|
||||
.map(|s| usize::from_str(&s).unwrap())
|
||||
.unwrap_or(0);
|
||||
let tx_rate_limit = matches.opt_str("tx-rate-limit").map(|s| u64::from_str(&s).unwrap())
|
||||
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())
|
||||
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())
|
||||
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>;
|
||||
|
@ -134,12 +201,19 @@ pub fn parse_middleware_options<D>(matches: &mut Matches, device: D, loopback: b
|
|||
pcap_writer = Box::new(io::sink())
|
||||
}
|
||||
|
||||
let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();
|
||||
let seed = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.subsec_nanos();
|
||||
|
||||
let device = PcapWriter::new(
|
||||
device,
|
||||
Rc::new(RefCell::new(pcap_writer)) as Rc<dyn PcapSink>,
|
||||
if loopback { PcapMode::TxOnly } else { PcapMode::Both },
|
||||
if loopback {
|
||||
PcapMode::TxOnly
|
||||
} else {
|
||||
PcapMode::Both
|
||||
},
|
||||
);
|
||||
|
||||
let device = Tracer::new(device, |_timestamp, _printer| {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,18 +4,18 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram
|
|||
provides lookup and caching of hardware addresses, and handles management packets.
|
||||
*/
|
||||
|
||||
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
|
||||
mod interface;
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
mod neighbor;
|
||||
mod route;
|
||||
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
|
||||
mod interface;
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
pub use self::neighbor::Neighbor as Neighbor;
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
pub(crate) use self::neighbor::Answer as NeighborAnswer;
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
pub use self::neighbor::Cache as NeighborCache;
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
pub use self::neighbor::Neighbor;
|
||||
pub use self::route::{Route, Routes};
|
||||
|
||||
#[cfg(any(feature = "medium-ethernet", feature = "medium-ip"))]
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
use managed::ManagedMap;
|
||||
|
||||
use crate::wire::{EthernetAddress, IpAddress};
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::wire::{EthernetAddress, IpAddress};
|
||||
|
||||
/// A cached neighbor.
|
||||
///
|
||||
|
@ -27,7 +27,7 @@ pub(crate) enum Answer {
|
|||
NotFound,
|
||||
/// The neighbor address is not in the cache, or has expired,
|
||||
/// and a lookup has been made recently.
|
||||
RateLimited
|
||||
RateLimited,
|
||||
}
|
||||
|
||||
impl Answer {
|
||||
|
@ -63,8 +63,7 @@ impl Answer {
|
|||
pub struct Cache<'a> {
|
||||
storage: ManagedMap<'a, IpAddress, Neighbor>,
|
||||
silent_until: Instant,
|
||||
gc_threshold: usize
|
||||
|
||||
gc_threshold: usize,
|
||||
}
|
||||
|
||||
impl<'a> Cache<'a> {
|
||||
|
@ -82,21 +81,32 @@ impl<'a> Cache<'a> {
|
|||
/// # Panics
|
||||
/// This function panics if `storage.len() == 0`.
|
||||
pub fn new<T>(storage: T) -> Cache<'a>
|
||||
where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
|
||||
|
||||
where
|
||||
T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
|
||||
{
|
||||
Cache::new_with_limit(storage, Cache::GC_THRESHOLD)
|
||||
}
|
||||
|
||||
pub fn new_with_limit<T>(storage: T, gc_threshold: usize) -> Cache<'a>
|
||||
where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
|
||||
where
|
||||
T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
|
||||
{
|
||||
let mut storage = storage.into();
|
||||
storage.clear();
|
||||
|
||||
Cache { storage, gc_threshold, silent_until: Instant::from_millis(0) }
|
||||
Cache {
|
||||
storage,
|
||||
gc_threshold,
|
||||
silent_until: Instant::from_millis(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill(&mut self, protocol_addr: IpAddress, hardware_addr: EthernetAddress,
|
||||
timestamp: Instant) {
|
||||
pub fn fill(
|
||||
&mut self,
|
||||
protocol_addr: IpAddress,
|
||||
hardware_addr: EthernetAddress,
|
||||
timestamp: Instant,
|
||||
) {
|
||||
debug_assert!(protocol_addr.is_unicast());
|
||||
debug_assert!(hardware_addr.is_unicast());
|
||||
|
||||
|
@ -108,7 +118,8 @@ impl<'a> Cache<'a> {
|
|||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
ManagedMap::Owned(ref mut map) => {
|
||||
if current_storage_size >= self.gc_threshold {
|
||||
let new_btree_map = map.iter_mut()
|
||||
let new_btree_map = map
|
||||
.iter_mut()
|
||||
.map(|(key, value)| (*key, *value))
|
||||
.filter(|(_, v)| timestamp < v.expires_at)
|
||||
.collect();
|
||||
|
@ -118,13 +129,18 @@ impl<'a> Cache<'a> {
|
|||
}
|
||||
};
|
||||
let neighbor = Neighbor {
|
||||
expires_at: timestamp + Self::ENTRY_LIFETIME, hardware_addr
|
||||
expires_at: timestamp + Self::ENTRY_LIFETIME,
|
||||
hardware_addr,
|
||||
};
|
||||
match self.storage.insert(protocol_addr, neighbor) {
|
||||
Ok(Some(old_neighbor)) => {
|
||||
if old_neighbor.hardware_addr != hardware_addr {
|
||||
net_trace!("replaced {} => {} (was {})",
|
||||
protocol_addr, hardware_addr, old_neighbor.hardware_addr);
|
||||
net_trace!(
|
||||
"replaced {} => {} (was {})",
|
||||
protocol_addr,
|
||||
hardware_addr,
|
||||
old_neighbor.hardware_addr
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(None) => {
|
||||
|
@ -147,21 +163,23 @@ impl<'a> Cache<'a> {
|
|||
}
|
||||
// Owned maps can extend themselves.
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
ManagedMap::Owned(_) => unreachable!()
|
||||
ManagedMap::Owned(_) => unreachable!(),
|
||||
};
|
||||
|
||||
let _old_neighbor =
|
||||
self.storage.remove(&old_protocol_addr).unwrap();
|
||||
let _old_neighbor = self.storage.remove(&old_protocol_addr).unwrap();
|
||||
match self.storage.insert(protocol_addr, neighbor) {
|
||||
Ok(None) => {
|
||||
net_trace!("filled {} => {} (evicted {} => {})",
|
||||
protocol_addr, hardware_addr,
|
||||
old_protocol_addr, _old_neighbor.hardware_addr);
|
||||
net_trace!(
|
||||
"filled {} => {} (evicted {} => {})",
|
||||
protocol_addr,
|
||||
hardware_addr,
|
||||
old_protocol_addr,
|
||||
_old_neighbor.hardware_addr
|
||||
);
|
||||
}
|
||||
// We've covered everything else above.
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -171,10 +189,13 @@ impl<'a> Cache<'a> {
|
|||
return Answer::Found(EthernetAddress::BROADCAST);
|
||||
}
|
||||
|
||||
if let Some(&Neighbor { expires_at, hardware_addr }) =
|
||||
self.storage.get(protocol_addr) {
|
||||
if let Some(&Neighbor {
|
||||
expires_at,
|
||||
hardware_addr,
|
||||
}) = self.storage.get(protocol_addr)
|
||||
{
|
||||
if timestamp < expires_at {
|
||||
return Answer::Found(hardware_addr)
|
||||
return Answer::Found(hardware_addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,9 +214,8 @@ impl<'a> Cache<'a> {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::collections::BTreeMap;
|
||||
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
|
||||
const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
|
||||
|
@ -207,17 +227,47 @@ mod test {
|
|||
let mut cache_storage = [Default::default(); 3];
|
||||
let mut cache = Cache::new(&mut cache_storage[..]);
|
||||
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found(), false);
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
|
||||
assert_eq!(
|
||||
cache
|
||||
.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
|
||||
.found(),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
cache
|
||||
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
|
||||
.found(),
|
||||
false
|
||||
);
|
||||
|
||||
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(),
|
||||
false);
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
|
||||
Answer::Found(HADDR_A)
|
||||
);
|
||||
assert_eq!(
|
||||
cache
|
||||
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
|
||||
.found(),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
cache
|
||||
.lookup(
|
||||
&MOCK_IP_ADDR_1,
|
||||
Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
|
||||
)
|
||||
.found(),
|
||||
false
|
||||
);
|
||||
|
||||
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
|
||||
assert_eq!(
|
||||
cache
|
||||
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
|
||||
.found(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -226,9 +276,19 @@ mod test {
|
|||
let mut cache = Cache::new(&mut cache_storage[..]);
|
||||
|
||||
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(),
|
||||
false);
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
|
||||
Answer::Found(HADDR_A)
|
||||
);
|
||||
assert_eq!(
|
||||
cache
|
||||
.lookup(
|
||||
&MOCK_IP_ADDR_1,
|
||||
Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
|
||||
)
|
||||
.found(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -237,9 +297,15 @@ mod test {
|
|||
let mut cache = Cache::new(&mut cache_storage[..]);
|
||||
|
||||
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
|
||||
Answer::Found(HADDR_A)
|
||||
);
|
||||
cache.fill(MOCK_IP_ADDR_1, HADDR_B, Instant::from_millis(0));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_B));
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
|
||||
Answer::Found(HADDR_B)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -249,10 +315,20 @@ mod test {
|
|||
cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
|
||||
// Adding third item after the expiration of the previous
|
||||
// two should garbage collect
|
||||
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2);
|
||||
cache.fill(
|
||||
MOCK_IP_ADDR_3,
|
||||
HADDR_C,
|
||||
Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2,
|
||||
);
|
||||
|
||||
assert_eq!(cache.storage.len(), 1);
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Answer::Found(HADDR_C));
|
||||
assert_eq!(
|
||||
cache.lookup(
|
||||
&MOCK_IP_ADDR_3,
|
||||
Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2
|
||||
),
|
||||
Answer::Found(HADDR_C)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -263,12 +339,28 @@ mod test {
|
|||
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
|
||||
cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
|
||||
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Answer::Found(HADDR_B));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found(), false);
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)),
|
||||
Answer::Found(HADDR_B)
|
||||
);
|
||||
assert_eq!(
|
||||
cache
|
||||
.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000))
|
||||
.found(),
|
||||
false
|
||||
);
|
||||
|
||||
cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)).found(), false);
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D));
|
||||
assert_eq!(
|
||||
cache
|
||||
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000))
|
||||
.found(),
|
||||
false
|
||||
);
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)),
|
||||
Answer::Found(HADDR_D)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -276,10 +368,19 @@ mod test {
|
|||
let mut cache_storage = [Default::default(); 3];
|
||||
let mut cache = Cache::new(&mut cache_storage[..]);
|
||||
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::NotFound);
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
|
||||
Answer::NotFound
|
||||
);
|
||||
|
||||
cache.limit_rate(Instant::from_millis(0));
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), Answer::RateLimited);
|
||||
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), Answer::NotFound);
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)),
|
||||
Answer::RateLimited
|
||||
);
|
||||
assert_eq!(
|
||||
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)),
|
||||
Answer::NotFound
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use managed::ManagedMap;
|
||||
use crate::time::Instant;
|
||||
use core::ops::Bound;
|
||||
use managed::ManagedMap;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::wire::{IpCidr, IpAddress};
|
||||
use crate::wire::{IpAddress, IpCidr};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::{Ipv4Address, Ipv4Cidr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use crate::wire::{Ipv6Address, Ipv6Cidr};
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// A prefix of addresses that should be routed via a router
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -70,7 +70,9 @@ impl<'a> Routes<'a> {
|
|||
/// Creates a routing tables. The backing storage is **not** cleared
|
||||
/// upon creation.
|
||||
pub fn new<T>(storage: T) -> Routes<'a>
|
||||
where T: Into<ManagedMap<'a, IpCidr, Route>> {
|
||||
where
|
||||
T: Into<ManagedMap<'a, IpCidr, Route>>,
|
||||
{
|
||||
let storage = storage.into();
|
||||
Routes { storage }
|
||||
}
|
||||
|
@ -89,7 +91,7 @@ impl<'a> Routes<'a> {
|
|||
let route = Route::new_ipv4_gateway(gateway);
|
||||
match self.storage.insert(cidr, route) {
|
||||
Ok(route) => Ok(route),
|
||||
Err((_cidr, _route)) => Err(Error::Exhausted)
|
||||
Err((_cidr, _route)) => Err(Error::Exhausted),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +104,7 @@ impl<'a> Routes<'a> {
|
|||
let route = Route::new_ipv6_gateway(gateway);
|
||||
match self.storage.insert(cidr, route) {
|
||||
Ok(route) => Ok(route),
|
||||
Err((_cidr, _route)) => Err(Error::Exhausted)
|
||||
Err((_cidr, _route)) => Err(Error::Exhausted),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,8 +126,7 @@ impl<'a> Routes<'a> {
|
|||
self.storage.remove(&cidr)
|
||||
}
|
||||
|
||||
pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) ->
|
||||
Option<IpAddress> {
|
||||
pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
|
||||
assert!(addr.is_unicast());
|
||||
|
||||
let cidr = match addr {
|
||||
|
@ -133,10 +134,14 @@ impl<'a> Routes<'a> {
|
|||
IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)),
|
||||
_ => unimplemented!()
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
for (prefix, route) in self.storage.range((Bound::Unbounded::<IpCidr>, Bound::Included(cidr))).rev() {
|
||||
for (prefix, route) in self
|
||||
.storage
|
||||
.range((Bound::Unbounded::<IpCidr>, Bound::Included(cidr)))
|
||||
.rev()
|
||||
{
|
||||
// TODO: do something with route.preferred_until
|
||||
if let Some(expires_at) = route.expires_at {
|
||||
if timestamp > expires_at {
|
||||
|
@ -159,24 +164,28 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv6")]
|
||||
mod mock {
|
||||
use super::super::*;
|
||||
pub const ADDR_1A: Ipv6Address = Ipv6Address(
|
||||
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
pub const ADDR_1B: Ipv6Address = Ipv6Address(
|
||||
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
|
||||
pub const ADDR_1C: Ipv6Address = Ipv6Address(
|
||||
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
|
||||
pub const ADDR_1A: Ipv6Address =
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
pub const ADDR_1B: Ipv6Address =
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
|
||||
pub const ADDR_1C: Ipv6Address =
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
|
||||
pub fn cidr_1() -> Ipv6Cidr {
|
||||
Ipv6Cidr::new(Ipv6Address(
|
||||
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
|
||||
Ipv6Cidr::new(
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
64,
|
||||
)
|
||||
}
|
||||
|
||||
pub const ADDR_2A: Ipv6Address = Ipv6Address(
|
||||
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
pub const ADDR_2B: Ipv6Address = Ipv6Address(
|
||||
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
|
||||
pub const ADDR_2A: Ipv6Address =
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
pub const ADDR_2B: Ipv6Address =
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
|
||||
pub fn cidr_2() -> Ipv6Cidr {
|
||||
Ipv6Cidr::new(Ipv6Address(
|
||||
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
|
||||
Ipv6Cidr::new(
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
64,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,25 +213,56 @@ mod test {
|
|||
let mut routes_storage = [None, None, None];
|
||||
let mut routes = Routes::new(&mut routes_storage[..]);
|
||||
|
||||
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), None);
|
||||
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), None);
|
||||
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), None);
|
||||
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None);
|
||||
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
|
||||
None
|
||||
);
|
||||
|
||||
let route = Route {
|
||||
via_router: ADDR_1A.into(),
|
||||
preferred_until: None, expires_at: None,
|
||||
preferred_until: None,
|
||||
expires_at: None,
|
||||
};
|
||||
routes.update(|storage| {
|
||||
storage.insert(cidr_1().into(), route).unwrap();
|
||||
});
|
||||
|
||||
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None);
|
||||
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
|
||||
None
|
||||
);
|
||||
|
||||
let route2 = Route {
|
||||
via_router: ADDR_2A.into(),
|
||||
|
@ -233,16 +273,46 @@ mod test {
|
|||
storage.insert(cidr_2().into(), route2).unwrap();
|
||||
});
|
||||
|
||||
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), Some(ADDR_2A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), Some(ADDR_2A.into()));
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
|
||||
Some(ADDR_2A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
|
||||
Some(ADDR_2A.into())
|
||||
);
|
||||
|
||||
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)), Some(ADDR_2A.into()));
|
||||
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)), Some(ADDR_2A.into()));
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)),
|
||||
Some(ADDR_1A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)),
|
||||
Some(ADDR_2A.into())
|
||||
);
|
||||
assert_eq!(
|
||||
routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)),
|
||||
Some(ADDR_2A.into())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -1,6 +1,12 @@
|
|||
#![cfg_attr(not(any(test, feature = "std")), no_std)]
|
||||
#![deny(unsafe_code)]
|
||||
#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "medium-ethernet"), deny(unused))]
|
||||
#![cfg_attr(
|
||||
all(
|
||||
any(feature = "proto-ipv4", feature = "proto-ipv6"),
|
||||
feature = "medium-ethernet"
|
||||
),
|
||||
deny(unused)
|
||||
)]
|
||||
|
||||
//! The _smoltcp_ library is built in a layered structure, with the layers corresponding
|
||||
//! to the levels of API abstraction. Only the highest layers would be used by a typical
|
||||
|
@ -107,10 +113,7 @@ compile_error!("If you enable the socket feature, you must enable at least one o
|
|||
|
||||
#[cfg(all(
|
||||
feature = "socket",
|
||||
not(any(
|
||||
feature = "medium-ethernet",
|
||||
feature = "medium-ip",
|
||||
))
|
||||
not(any(feature = "medium-ethernet", feature = "medium-ip",))
|
||||
))]
|
||||
compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet");
|
||||
|
||||
|
@ -123,13 +126,13 @@ use core::fmt;
|
|||
mod macros;
|
||||
mod parsers;
|
||||
|
||||
pub mod storage;
|
||||
pub mod phy;
|
||||
pub mod wire;
|
||||
pub mod iface;
|
||||
pub mod phy;
|
||||
#[cfg(feature = "socket")]
|
||||
pub mod socket;
|
||||
pub mod storage;
|
||||
pub mod time;
|
||||
pub mod wire;
|
||||
|
||||
/// The error type for the networking stack.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#[cfg(feature = "log")]
|
||||
macro_rules! net_log {
|
||||
(trace, $($arg:expr),*) => { log::trace!($($arg),*); };
|
||||
|
|
344
src/parsers.rs
344
src/parsers.rs
|
@ -1,7 +1,10 @@
|
|||
#![cfg_attr(not(all(feature = "proto-ipv6", feature = "proto-ipv4")), allow(dead_code))]
|
||||
#![cfg_attr(
|
||||
not(all(feature = "proto-ipv6", feature = "proto-ipv4")),
|
||||
allow(dead_code)
|
||||
)]
|
||||
|
||||
use core::str::FromStr;
|
||||
use core::result;
|
||||
use core::str::FromStr;
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
use crate::wire::EthernetAddress;
|
||||
|
@ -15,14 +18,14 @@ type Result<T> = result::Result<T, ()>;
|
|||
|
||||
struct Parser<'a> {
|
||||
data: &'a [u8],
|
||||
pos: usize
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn new(data: &'a str) -> Parser<'a> {
|
||||
Parser {
|
||||
data: data.as_bytes(),
|
||||
pos: 0
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,12 +43,14 @@ impl<'a> Parser<'a> {
|
|||
self.pos += 1;
|
||||
Ok(chr)
|
||||
}
|
||||
None => Err(())
|
||||
None => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn try_do<F, T>(&mut self, f: F) -> Option<T>
|
||||
where F: FnOnce(&mut Parser<'a>) -> Result<T> {
|
||||
where
|
||||
F: FnOnce(&mut Parser<'a>) -> Result<T>,
|
||||
{
|
||||
let pos = self.pos;
|
||||
match f(self) {
|
||||
Ok(res) => Some(res),
|
||||
|
@ -65,7 +70,9 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
fn until_eof<F, T>(&mut self, f: F) -> Result<T>
|
||||
where F: FnOnce(&mut Parser<'a>) -> Result<T> {
|
||||
where
|
||||
F: FnOnce(&mut Parser<'a>) -> Result<T>,
|
||||
{
|
||||
let res = f(self)?;
|
||||
self.accept_eof()?;
|
||||
Ok(res)
|
||||
|
@ -99,8 +106,7 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn accept_number(&mut self, max_digits: usize, max_value: u32,
|
||||
hex: bool) -> Result<u32> {
|
||||
fn accept_number(&mut self, max_digits: usize, max_value: u32, hex: bool) -> Result<u32> {
|
||||
let mut value = self.accept_digit(hex)? as u32;
|
||||
for _ in 1..max_digits {
|
||||
match self.try_do(|p| p.accept_digit(hex)) {
|
||||
|
@ -108,7 +114,7 @@ impl<'a> Parser<'a> {
|
|||
value *= if hex { 16 } else { 10 };
|
||||
value += digit as u32;
|
||||
}
|
||||
None => break
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
if value < max_value {
|
||||
|
@ -133,10 +139,10 @@ impl<'a> Parser<'a> {
|
|||
#[cfg(feature = "medium-ethernet")]
|
||||
fn accept_mac(&mut self) -> Result<EthernetAddress> {
|
||||
if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) {
|
||||
return Ok(mac)
|
||||
return Ok(mac);
|
||||
}
|
||||
if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b':')) {
|
||||
return Ok(mac)
|
||||
return Ok(mac);
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
|
@ -154,9 +160,13 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn accept_ipv6_part(&mut self, (head, tail): (&mut [u16; 8], &mut [u16; 6]),
|
||||
fn accept_ipv6_part(
|
||||
&mut self,
|
||||
(head, tail): (&mut [u16; 8], &mut [u16; 6]),
|
||||
(head_idx, tail_idx): (&mut usize, &mut usize),
|
||||
mut use_tail: bool, is_cidr: bool) -> Result<()> {
|
||||
mut use_tail: bool,
|
||||
is_cidr: bool,
|
||||
) -> Result<()> {
|
||||
let double_colon = match self.try_do(|p| p.accept_str(b"::")) {
|
||||
Some(_) if !use_tail && *head_idx < 7 => {
|
||||
// Found a double colon. Start filling out the
|
||||
|
@ -164,7 +174,7 @@ impl<'a> Parser<'a> {
|
|||
// this is the last character we can parse.
|
||||
use_tail = true;
|
||||
true
|
||||
},
|
||||
}
|
||||
Some(_) => {
|
||||
// This is a bad address. Only one double colon is
|
||||
// allowed and an address is only 128 bits.
|
||||
|
@ -193,21 +203,20 @@ impl<'a> Parser<'a> {
|
|||
});
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
Some(part) if *tail_idx < 6 => {
|
||||
// Valid u16 to be added to the address
|
||||
tail[*tail_idx] = part as u16;
|
||||
*tail_idx += 1;
|
||||
|
||||
if *tail_idx == 1 && tail[0] == 0xffff
|
||||
&& head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
|
||||
if *tail_idx == 1 && tail[0] == 0xffff && head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
|
||||
self.try_do(|p| {
|
||||
p.accept_char(b':')?;
|
||||
p.accept_ipv4_mapped_ipv6_part(tail, tail_idx)
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
Some(_) => {
|
||||
// Tail or head section is too long
|
||||
Err(())
|
||||
|
@ -259,7 +268,12 @@ impl<'a> Parser<'a> {
|
|||
let (mut addr, mut tail) = ([0u16; 8], [0u16; 6]);
|
||||
let (mut head_idx, mut tail_idx) = (0, 0);
|
||||
|
||||
self.accept_ipv6_part((&mut addr, &mut tail), (&mut head_idx, &mut tail_idx), false, is_cidr)?;
|
||||
self.accept_ipv6_part(
|
||||
(&mut addr, &mut tail),
|
||||
(&mut head_idx, &mut tail_idx),
|
||||
false,
|
||||
is_cidr,
|
||||
)?;
|
||||
|
||||
// We need to copy the tail portion (the portion following the "::") to the
|
||||
// end of the address.
|
||||
|
@ -290,14 +304,14 @@ impl<'a> Parser<'a> {
|
|||
#[allow(clippy::single_match)]
|
||||
match self.try_do(|p| p.accept_ipv4()) {
|
||||
Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)),
|
||||
None => ()
|
||||
None => (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
#[allow(clippy::single_match)]
|
||||
match self.try_do(|p| p.accept_ipv6(false)) {
|
||||
Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)),
|
||||
None => ()
|
||||
None => (),
|
||||
}
|
||||
|
||||
Err(())
|
||||
|
@ -314,7 +328,10 @@ impl<'a> Parser<'a> {
|
|||
self.accept_number(5, 65535, false)?
|
||||
};
|
||||
|
||||
Ok(IpEndpoint { addr: IpAddress::Ipv4(ip), port: port as u16 })
|
||||
Ok(IpEndpoint {
|
||||
addr: IpAddress::Ipv4(ip),
|
||||
port: port as u16,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
|
@ -326,10 +343,16 @@ impl<'a> Parser<'a> {
|
|||
self.accept_char(b':')?;
|
||||
let port = self.accept_number(5, 65535, false)?;
|
||||
|
||||
Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: port as u16 })
|
||||
Ok(IpEndpoint {
|
||||
addr: IpAddress::Ipv6(ip),
|
||||
port: port as u16,
|
||||
})
|
||||
} else {
|
||||
let ip = self.accept_ipv6(false)?;
|
||||
Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: 0 })
|
||||
Ok(IpEndpoint {
|
||||
addr: IpAddress::Ipv6(ip),
|
||||
port: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,14 +361,14 @@ impl<'a> Parser<'a> {
|
|||
#[allow(clippy::single_match)]
|
||||
match self.try_do(|p| p.accept_ipv4_endpoint()) {
|
||||
Some(ipv4) => return Ok(ipv4),
|
||||
None => ()
|
||||
None => (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
#[allow(clippy::single_match)]
|
||||
match self.try_do(|p| p.accept_ipv6_endpoint()) {
|
||||
Some(ipv6) => return Ok(ipv6),
|
||||
None => ()
|
||||
None => (),
|
||||
}
|
||||
|
||||
Err(())
|
||||
|
@ -431,14 +454,14 @@ impl FromStr for IpCidr {
|
|||
#[allow(clippy::single_match)]
|
||||
match Ipv4Cidr::from_str(s) {
|
||||
Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)),
|
||||
Err(_) => ()
|
||||
Err(_) => (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
#[allow(clippy::single_match)]
|
||||
match Ipv6Cidr::from_str(s) {
|
||||
Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)),
|
||||
Err(_) => ()
|
||||
Err(_) => (),
|
||||
}
|
||||
|
||||
Err(())
|
||||
|
@ -449,7 +472,7 @@ impl FromStr for IpEndpoint {
|
|||
type Err = ();
|
||||
|
||||
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 {
|
||||
assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr));
|
||||
assert_eq!(IpCidr::from_str(&format!("{}", cidr)),
|
||||
Ok($variant(cidr)));
|
||||
}
|
||||
assert_eq!(IpCidr::from_str(&format!("{}", cidr)), Ok($variant(cidr)));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
|
||||
fn test_mac() {
|
||||
assert_eq!(EthernetAddress::from_str(""), Err(()));
|
||||
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"),
|
||||
Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00])));
|
||||
assert_eq!(EthernetAddress::from_str("01:23:45:67:89:ab"),
|
||||
Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab])));
|
||||
assert_eq!(EthernetAddress::from_str("cd:ef:10:00:00:00"),
|
||||
Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00])));
|
||||
assert_eq!(EthernetAddress::from_str("00:00:00:ab:cd:ef"),
|
||||
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])));
|
||||
assert_eq!(EthernetAddress::from_str("00-00-00-ab-cd-ef"),
|
||||
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])));
|
||||
assert_eq!(EthernetAddress::from_str("AB-CD-EF-00-00-00"),
|
||||
Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00])));
|
||||
assert_eq!(
|
||||
EthernetAddress::from_str("02:00:00:00:00:00"),
|
||||
Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00]))
|
||||
);
|
||||
assert_eq!(
|
||||
EthernetAddress::from_str("01:23:45:67:89:ab"),
|
||||
Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab]))
|
||||
);
|
||||
assert_eq!(
|
||||
EthernetAddress::from_str("cd:ef:10:00:00:00"),
|
||||
Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00]))
|
||||
);
|
||||
assert_eq!(
|
||||
EthernetAddress::from_str("00:00:00:ab:cd:ef"),
|
||||
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
|
||||
);
|
||||
assert_eq!(
|
||||
EthernetAddress::from_str("00-00-00-ab-cd-ef"),
|
||||
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
|
||||
);
|
||||
assert_eq!(
|
||||
EthernetAddress::from_str("AB-CD-EF-00-00-00"),
|
||||
Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00]))
|
||||
);
|
||||
assert_eq!(EthernetAddress::from_str("100:00:00:00:00:00"), Err(()));
|
||||
assert_eq!(EthernetAddress::from_str("002:00:00:00:00:00"), Err(()));
|
||||
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:000"), Err(()));
|
||||
|
@ -498,10 +532,14 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
fn test_ipv4() {
|
||||
assert_eq!(Ipv4Address::from_str(""), Err(()));
|
||||
assert_eq!(Ipv4Address::from_str("1.2.3.4"),
|
||||
Ok(Ipv4Address([1, 2, 3, 4])));
|
||||
assert_eq!(Ipv4Address::from_str("001.2.3.4"),
|
||||
Ok(Ipv4Address([1, 2, 3, 4])));
|
||||
assert_eq!(
|
||||
Ipv4Address::from_str("1.2.3.4"),
|
||||
Ok(Ipv4Address([1, 2, 3, 4]))
|
||||
);
|
||||
assert_eq!(
|
||||
Ipv4Address::from_str("001.2.3.4"),
|
||||
Ok(Ipv4Address([1, 2, 3, 4]))
|
||||
);
|
||||
assert_eq!(Ipv4Address::from_str("0001.2.3.4"), Err(()));
|
||||
assert_eq!(Ipv4Address::from_str("999.2.3.4"), Err(()));
|
||||
assert_eq!(Ipv4Address::from_str("1.2.3.4.5"), Err(()));
|
||||
|
@ -515,73 +553,87 @@ mod test {
|
|||
fn test_ipv6() {
|
||||
// Obviously not valid
|
||||
assert_eq!(Ipv6Address::from_str(""), Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
|
||||
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)));
|
||||
assert_eq!(Ipv6Address::from_str("::1"),
|
||||
Ok(Ipv6Address::LOOPBACK));
|
||||
assert_eq!(Ipv6Address::from_str("::"),
|
||||
Ok(Ipv6Address::UNSPECIFIED));
|
||||
assert_eq!(Ipv6Address::from_str("fe80::1"),
|
||||
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)));
|
||||
assert_eq!(Ipv6Address::from_str("1234:5678::"),
|
||||
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0)));
|
||||
assert_eq!(Ipv6Address::from_str("1234:5678::8765:4321"),
|
||||
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321)));
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
|
||||
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
|
||||
);
|
||||
assert_eq!(Ipv6Address::from_str("::1"), Ok(Ipv6Address::LOOPBACK));
|
||||
assert_eq!(Ipv6Address::from_str("::"), Ok(Ipv6Address::UNSPECIFIED));
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("fe80::1"),
|
||||
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
|
||||
);
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("1234:5678::"),
|
||||
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0))
|
||||
);
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("1234:5678::8765:4321"),
|
||||
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321))
|
||||
);
|
||||
// Two double colons in address
|
||||
assert_eq!(Ipv6Address::from_str("1234:5678::1::1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("4444:333:22:1::4"),
|
||||
Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4)));
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1::"),
|
||||
Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0)));
|
||||
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1"),
|
||||
Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1)));
|
||||
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("1234:5678::1::1"), Err(()));
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("4444:333:22:1::4"),
|
||||
Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4))
|
||||
);
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("1:1:1:1:1:1::"),
|
||||
Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0))
|
||||
);
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("::1:1:1:1:1:1"),
|
||||
Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1))
|
||||
);
|
||||
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
|
||||
// Double colon appears too late indicating an address that is too long
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"), Err(()));
|
||||
// Section after double colon is too long for a valid address
|
||||
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
|
||||
// Obviously too long
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"), Err(()));
|
||||
// Address is too short
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"), Err(()));
|
||||
// Long number
|
||||
assert_eq!(Ipv6Address::from_str("::000001"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("::000001"), Err(()));
|
||||
// IPv4-Mapped address
|
||||
assert_eq!(Ipv6Address::from_str("::ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
|
||||
assert_eq!(Ipv6Address::from_str("0::ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("::ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
|
||||
]))
|
||||
);
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
|
||||
]))
|
||||
);
|
||||
assert_eq!(
|
||||
Ipv6Address::from_str("0::ffff:192.168.1.1"),
|
||||
Ok(Ipv6Address([
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
|
||||
]))
|
||||
);
|
||||
// Only ffff is allowed in position 6 when IPv4 mapped
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"), Err(()));
|
||||
// Positions 1-5 must be 0 when IPv4 mapped
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"), Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"), Err(()));
|
||||
// Out of range ipv4 octet
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"), Err(()));
|
||||
// Invalid hex in ipv4 octet
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"),
|
||||
Err(()));
|
||||
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"), Err(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
fn test_ip_ipv4() {
|
||||
assert_eq!(IpAddress::from_str(""), Err(()));
|
||||
assert_eq!(IpAddress::from_str("1.2.3.4"),
|
||||
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4]))));
|
||||
assert_eq!(
|
||||
IpAddress::from_str("1.2.3.4"),
|
||||
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4])))
|
||||
);
|
||||
assert_eq!(IpAddress::from_str("x"), Err(()));
|
||||
}
|
||||
|
||||
|
@ -589,8 +641,12 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv6")]
|
||||
fn test_ip_ipv6() {
|
||||
assert_eq!(IpAddress::from_str(""), Err(()));
|
||||
assert_eq!(IpAddress::from_str("fe80::1"),
|
||||
Ok(IpAddress::Ipv6(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))));
|
||||
assert_eq!(
|
||||
IpAddress::from_str("fe80::1"),
|
||||
Ok(IpAddress::Ipv6(Ipv6Address::new(
|
||||
0xfe80, 0, 0, 0, 0, 0, 0, 1
|
||||
)))
|
||||
);
|
||||
assert_eq!(IpAddress::from_str("x"), Err(()));
|
||||
}
|
||||
|
||||
|
@ -598,14 +654,22 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
fn test_cidr_ipv4() {
|
||||
let tests = [
|
||||
("127.0.0.1/8",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8))),
|
||||
("192.168.1.1/24",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8))),
|
||||
("8.8.8.8/32",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8))),
|
||||
("8.8.8.8/0",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8))),
|
||||
(
|
||||
"127.0.0.1/8",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8)),
|
||||
),
|
||||
(
|
||||
"192.168.1.1/24",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8)),
|
||||
),
|
||||
(
|
||||
"8.8.8.8/32",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8)),
|
||||
),
|
||||
(
|
||||
"8.8.8.8/0",
|
||||
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8)),
|
||||
),
|
||||
("", Err(())),
|
||||
("1", Err(())),
|
||||
("127.0.0.1", Err(())),
|
||||
|
@ -622,22 +686,32 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv6")]
|
||||
fn test_cidr_ipv6() {
|
||||
let tests = [
|
||||
("fe80::1/64",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))),
|
||||
("fe80::/64",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 64u8))),
|
||||
("::1/128",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
|
||||
("::/128",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
|
||||
("fe80:0:0:0:0:0:0:1/64",
|
||||
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))),
|
||||
("fe80:0:0:0:0:0:0:1|64",
|
||||
Err(())),
|
||||
("fe80::|64",
|
||||
Err(())),
|
||||
("fe80::1::/64",
|
||||
Err(()))
|
||||
(
|
||||
"fe80::1/64",
|
||||
Ok(Ipv6Cidr::new(
|
||||
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
|
||||
64u8,
|
||||
)),
|
||||
),
|
||||
(
|
||||
"fe80::/64",
|
||||
Ok(Ipv6Cidr::new(
|
||||
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
|
||||
64u8,
|
||||
)),
|
||||
),
|
||||
("::1/128", Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
|
||||
("::/128", Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
|
||||
(
|
||||
"fe80:0:0:0:0:0:0:1/64",
|
||||
Ok(Ipv6Cidr::new(
|
||||
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
|
||||
64u8,
|
||||
)),
|
||||
),
|
||||
("fe80:0:0:0:0:0:0:1|64", Err(())),
|
||||
("fe80::|64", Err(())),
|
||||
("fe80::1::/64", Err(())),
|
||||
];
|
||||
check_cidr_test_array!(tests, Ipv6Cidr::from_str, IpCidr::Ipv6);
|
||||
}
|
||||
|
@ -649,11 +723,17 @@ mod test {
|
|||
assert_eq!(IpEndpoint::from_str("x"), Err(()));
|
||||
assert_eq!(
|
||||
IpEndpoint::from_str("127.0.0.1"),
|
||||
Ok(IpEndpoint { addr: IpAddress::v4(127, 0, 0, 1), port: 0 })
|
||||
Ok(IpEndpoint {
|
||||
addr: IpAddress::v4(127, 0, 0, 1),
|
||||
port: 0
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
IpEndpoint::from_str("127.0.0.1:12345"),
|
||||
Ok(IpEndpoint { addr: IpAddress::v4(127, 0, 0, 1), port: 12345 })
|
||||
Ok(IpEndpoint {
|
||||
addr: IpAddress::v4(127, 0, 0, 1),
|
||||
port: 12345
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -664,11 +744,17 @@ mod test {
|
|||
assert_eq!(IpEndpoint::from_str("x"), Err(()));
|
||||
assert_eq!(
|
||||
IpEndpoint::from_str("fe80::1"),
|
||||
Ok(IpEndpoint { addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), port: 0 })
|
||||
Ok(IpEndpoint {
|
||||
addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
|
||||
port: 0
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
IpEndpoint::from_str("[fe80::1]:12345"),
|
||||
Ok(IpEndpoint { addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), port: 12345 })
|
||||
Ok(IpEndpoint {
|
||||
addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
|
||||
port: 12345
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use core::cell::RefCell;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::{self, DeviceCapabilities, Device};
|
||||
use crate::phy::{self, Device, DeviceCapabilities};
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::{Error, Result};
|
||||
|
||||
// We use our own RNG to stay compatible with #![no_std].
|
||||
// The use of the RNG below has a slight bias, but it doesn't matter.
|
||||
|
@ -61,7 +61,9 @@ impl State {
|
|||
}
|
||||
|
||||
fn maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool {
|
||||
if config.max_tx_rate == 0 { return true }
|
||||
if config.max_tx_rate == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.refill(config, timestamp);
|
||||
if self.tx_bucket > 0 {
|
||||
|
@ -73,7 +75,9 @@ impl State {
|
|||
}
|
||||
|
||||
fn maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool {
|
||||
if config.max_rx_rate == 0 { return true }
|
||||
if config.max_rx_rate == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.refill(config, timestamp);
|
||||
if self.rx_bucket > 0 {
|
||||
|
@ -107,7 +111,7 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
|
|||
rx_bucket: 0,
|
||||
};
|
||||
FaultInjector {
|
||||
inner: inner,
|
||||
inner,
|
||||
state: RefCell::new(state),
|
||||
config: Config::default(),
|
||||
}
|
||||
|
@ -153,7 +157,9 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
|
|||
/// # Panics
|
||||
/// This function panics if the probability is not between 0% and 100%.
|
||||
pub fn set_corrupt_chance(&mut self, pct: u8) {
|
||||
if pct > 100 { panic!("percentage out of range") }
|
||||
if pct > 100 {
|
||||
panic!("percentage out of range")
|
||||
}
|
||||
self.config.corrupt_pct = pct
|
||||
}
|
||||
|
||||
|
@ -162,7 +168,9 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
|
|||
/// # Panics
|
||||
/// This function panics if the probability is not between 0% and 100%.
|
||||
pub fn set_drop_chance(&mut self, pct: u8) {
|
||||
if pct > 100 { panic!("percentage out of range") }
|
||||
if pct > 100 {
|
||||
panic!("percentage out of range")
|
||||
}
|
||||
self.config.drop_pct = pct
|
||||
}
|
||||
|
||||
|
@ -189,7 +197,8 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
|
|||
}
|
||||
|
||||
impl<'a, D> Device<'a> for FaultInjector<D>
|
||||
where D: for<'b> Device<'b>,
|
||||
where
|
||||
D: for<'b> Device<'b>,
|
||||
{
|
||||
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken>;
|
||||
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken>;
|
||||
|
@ -203,17 +212,21 @@ impl<'a, D> Device<'a> for FaultInjector<D>
|
|||
}
|
||||
|
||||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
let &mut Self { ref mut inner, ref state, config } = self;
|
||||
let &mut Self {
|
||||
ref mut inner,
|
||||
ref state,
|
||||
config,
|
||||
} = self;
|
||||
inner.receive().map(|(rx_token, tx_token)| {
|
||||
let rx = RxToken {
|
||||
state: &state,
|
||||
config: config,
|
||||
state,
|
||||
config,
|
||||
token: rx_token,
|
||||
corrupt: [0; MTU],
|
||||
};
|
||||
let tx = TxToken {
|
||||
state: &state,
|
||||
config: config,
|
||||
state,
|
||||
config,
|
||||
token: tx_token,
|
||||
junk: [0; MTU],
|
||||
};
|
||||
|
@ -222,11 +235,15 @@ impl<'a, D> Device<'a> for FaultInjector<D>
|
|||
}
|
||||
|
||||
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
||||
let &mut Self { ref mut inner, ref state, config } = self;
|
||||
let &mut Self {
|
||||
ref mut inner,
|
||||
ref state,
|
||||
config,
|
||||
} = self;
|
||||
inner.transmit().map(|token| TxToken {
|
||||
state: &state,
|
||||
config: config,
|
||||
token: token,
|
||||
state,
|
||||
config,
|
||||
token,
|
||||
junk: [0; MTU],
|
||||
})
|
||||
}
|
||||
|
@ -242,21 +259,31 @@ pub struct RxToken<'a, Rx: phy::RxToken> {
|
|||
|
||||
impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> {
|
||||
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
if self.state.borrow_mut().maybe(self.config.drop_pct) {
|
||||
net_trace!("rx: randomly dropping a packet");
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
if !self.state.borrow_mut().maybe_receive(&self.config, timestamp) {
|
||||
if !self
|
||||
.state
|
||||
.borrow_mut()
|
||||
.maybe_receive(&self.config, timestamp)
|
||||
{
|
||||
net_trace!("rx: dropping a packet because of rate limiting");
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
let Self { token, config, state, mut corrupt } = self;
|
||||
let Self {
|
||||
token,
|
||||
config,
|
||||
state,
|
||||
mut corrupt,
|
||||
} = self;
|
||||
token.consume(timestamp, |buffer| {
|
||||
if config.max_size > 0 && buffer.as_ref().len() > config.max_size {
|
||||
net_trace!("rx: dropping a packet that is too large");
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
if state.borrow_mut().maybe(config.corrupt_pct) {
|
||||
net_trace!("rx: randomly corrupting a packet");
|
||||
|
@ -281,7 +308,8 @@ pub struct TxToken<'a, Tx: phy::TxToken> {
|
|||
|
||||
impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
|
||||
fn consume<R, F>(mut self, timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) {
|
||||
net_trace!("tx: randomly dropping a packet");
|
||||
|
@ -289,7 +317,11 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
|
|||
} else if self.config.max_size > 0 && len > self.config.max_size {
|
||||
net_trace!("tx: dropping a packet that is too large");
|
||||
true
|
||||
} else if !self.state.borrow_mut().maybe_transmit(&self.config, timestamp) {
|
||||
} else if !self
|
||||
.state
|
||||
.borrow_mut()
|
||||
.maybe_transmit(&self.config, timestamp)
|
||||
{
|
||||
net_trace!("tx: dropping a packet because of rate limiting");
|
||||
true
|
||||
} else {
|
||||
|
@ -300,7 +332,12 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
|
|||
return f(&mut self.junk[..len]);
|
||||
}
|
||||
|
||||
let Self { token, state, config, .. } = self;
|
||||
let Self {
|
||||
token,
|
||||
state,
|
||||
config,
|
||||
..
|
||||
} = self;
|
||||
token.consume(timestamp, len, |mut buf| {
|
||||
if state.borrow_mut().maybe(config.corrupt_pct) {
|
||||
net_trace!("tx: corrupting a packet");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::Result;
|
||||
use crate::phy::{self, DeviceCapabilities, Device};
|
||||
use crate::phy::{self, Device, DeviceCapabilities};
|
||||
use crate::time::Instant;
|
||||
use crate::Result;
|
||||
|
||||
// This could be fixed once associated consts are stable.
|
||||
const MTU: usize = 1536;
|
||||
|
@ -29,7 +29,11 @@ pub struct FuzzInjector<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> {
|
|||
impl<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx> {
|
||||
/// Create a fuzz injector device.
|
||||
pub fn new(inner: D, fuzz_tx: FTx, fuzz_rx: FRx) -> FuzzInjector<D, FTx, FRx> {
|
||||
FuzzInjector { inner, fuzz_tx, fuzz_rx }
|
||||
FuzzInjector {
|
||||
inner,
|
||||
fuzz_tx,
|
||||
fuzz_rx,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the underlying device, consuming the fuzz injector.
|
||||
|
@ -39,9 +43,10 @@ impl<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx>
|
|||
}
|
||||
|
||||
impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector<D, FTx, FRx>
|
||||
where D: for<'b> Device<'b>,
|
||||
where
|
||||
D: for<'b> Device<'b>,
|
||||
FTx: Fuzzer + 'a,
|
||||
FRx: Fuzzer + 'a
|
||||
FRx: Fuzzer + 'a,
|
||||
{
|
||||
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken, FRx>;
|
||||
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken, FTx>;
|
||||
|
@ -55,7 +60,11 @@ impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector<D, FTx, FRx>
|
|||
}
|
||||
|
||||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
let &mut Self { ref mut inner, ref fuzz_rx, ref fuzz_tx } = self;
|
||||
let &mut Self {
|
||||
ref mut inner,
|
||||
ref fuzz_rx,
|
||||
ref fuzz_tx,
|
||||
} = self;
|
||||
inner.receive().map(|(rx_token, tx_token)| {
|
||||
let rx = RxToken {
|
||||
fuzzer: fuzz_rx,
|
||||
|
@ -70,7 +79,11 @@ impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector<D, FTx, FRx>
|
|||
}
|
||||
|
||||
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
||||
let &mut Self { ref mut inner, fuzz_rx: _, ref fuzz_tx } = self;
|
||||
let &mut Self {
|
||||
ref mut inner,
|
||||
fuzz_rx: _,
|
||||
ref fuzz_tx,
|
||||
} = self;
|
||||
inner.transmit().map(|token| TxToken {
|
||||
fuzzer: fuzz_tx,
|
||||
token: token,
|
||||
|
@ -79,14 +92,15 @@ impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector<D, FTx, FRx>
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a>{
|
||||
pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a> {
|
||||
fuzzer: &'a F,
|
||||
token: Rx,
|
||||
}
|
||||
|
||||
impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> {
|
||||
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let Self { fuzzer, token } = self;
|
||||
token.consume(timestamp, |buffer| {
|
||||
|
@ -104,7 +118,8 @@ pub struct TxToken<'a, Tx: phy::TxToken, F: Fuzzer + 'a> {
|
|||
|
||||
impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> {
|
||||
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let Self { fuzzer, token } = self;
|
||||
token.consume(timestamp, len, |mut buf| {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use alloc::vec::Vec;
|
||||
#[cfg(not(feature = "rust-1_28"))]
|
||||
use alloc::collections::VecDeque;
|
||||
use alloc::vec::Vec;
|
||||
#[cfg(feature = "rust-1_28")]
|
||||
use alloc::VecDeque;
|
||||
|
||||
use crate::Result;
|
||||
use crate::phy::{self, Device, DeviceCapabilities, Medium};
|
||||
use crate::time::Instant;
|
||||
use crate::Result;
|
||||
|
||||
/// A loopback device.
|
||||
#[derive(Debug)]
|
||||
|
@ -44,7 +44,9 @@ impl<'a> Device<'a> for Loopback {
|
|||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
self.queue.pop_front().map(move |buffer| {
|
||||
let rx = RxToken { buffer };
|
||||
let tx = TxToken { queue: &mut self.queue };
|
||||
let tx = TxToken {
|
||||
queue: &mut self.queue,
|
||||
};
|
||||
(rx, tx)
|
||||
})
|
||||
}
|
||||
|
@ -63,7 +65,8 @@ pub struct RxToken {
|
|||
|
||||
impl phy::RxToken for RxToken {
|
||||
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
f(&mut self.buffer)
|
||||
}
|
||||
|
@ -76,7 +79,8 @@ pub struct TxToken<'a> {
|
|||
|
||||
impl<'a> phy::TxToken for TxToken<'a> {
|
||||
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let mut buffer = Vec::new();
|
||||
buffer.resize(len, 0);
|
||||
|
|
|
@ -11,7 +11,9 @@ and implementations of it:
|
|||
[TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames
|
||||
on the host OS.
|
||||
*/
|
||||
#![cfg_attr(feature = "medium-ethernet", doc = r##"
|
||||
#![cfg_attr(
|
||||
feature = "medium-ethernet",
|
||||
doc = r##"
|
||||
# Examples
|
||||
|
||||
An implementation of the [Device](trait.Device.html) trait for a simple hardware
|
||||
|
@ -84,37 +86,50 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> {
|
|||
}
|
||||
}
|
||||
```
|
||||
"##)]
|
||||
"##
|
||||
)]
|
||||
|
||||
use crate::Result;
|
||||
use crate::time::Instant;
|
||||
use crate::Result;
|
||||
|
||||
#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))]
|
||||
#[cfg(all(
|
||||
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
|
||||
unix
|
||||
))]
|
||||
mod sys;
|
||||
|
||||
mod tracer;
|
||||
mod fault_injector;
|
||||
mod fuzz_injector;
|
||||
mod pcap_writer;
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
mod loopback;
|
||||
mod pcap_writer;
|
||||
#[cfg(all(feature = "phy-raw_socket", unix))]
|
||||
mod raw_socket;
|
||||
#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
|
||||
mod tracer;
|
||||
#[cfg(all(
|
||||
feature = "phy-tuntap_interface",
|
||||
any(target_os = "linux", target_os = "android")
|
||||
))]
|
||||
mod tuntap_interface;
|
||||
|
||||
#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"), unix))]
|
||||
#[cfg(all(
|
||||
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
|
||||
unix
|
||||
))]
|
||||
pub use self::sys::wait;
|
||||
|
||||
pub use self::tracer::Tracer;
|
||||
pub use self::fault_injector::FaultInjector;
|
||||
pub use self::fuzz_injector::{Fuzzer, FuzzInjector};
|
||||
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
|
||||
pub use self::fuzz_injector::{FuzzInjector, Fuzzer};
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
pub use self::loopback::Loopback;
|
||||
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
|
||||
#[cfg(all(feature = "phy-raw_socket", unix))]
|
||||
pub use self::raw_socket::RawSocket;
|
||||
#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
|
||||
pub use self::tracer::Tracer;
|
||||
#[cfg(all(
|
||||
feature = "phy-tuntap_interface",
|
||||
any(target_os = "linux", target_os = "android")
|
||||
))]
|
||||
pub use self::tuntap_interface::TunTapInterface;
|
||||
|
||||
/// A description of checksum behavior for a particular protocol.
|
||||
|
@ -142,7 +157,7 @@ impl Checksum {
|
|||
pub fn rx(&self) -> bool {
|
||||
match *self {
|
||||
Checksum::Both | Checksum::Rx => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,7 +165,7 @@ impl Checksum {
|
|||
pub fn tx(&self) -> bool {
|
||||
match *self {
|
||||
Checksum::Both | Checksum::Tx => true,
|
||||
_ => false
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -234,7 +249,9 @@ impl DeviceCapabilities {
|
|||
pub fn ip_mtu(&self) -> usize {
|
||||
match self.medium {
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
Medium::Ethernet => self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len(),
|
||||
Medium::Ethernet => {
|
||||
self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len()
|
||||
}
|
||||
#[cfg(feature = "medium-ip")]
|
||||
Medium::Ip => self.max_transmission_unit,
|
||||
}
|
||||
|
@ -260,7 +277,6 @@ pub enum Medium {
|
|||
Ip,
|
||||
}
|
||||
|
||||
|
||||
impl Default for Medium {
|
||||
fn default() -> Medium {
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
|
@ -306,7 +322,8 @@ pub trait RxToken {
|
|||
/// The timestamp must be a number of milliseconds, monotonically increasing since an
|
||||
/// arbitrary moment in time, such as system startup.
|
||||
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>;
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>;
|
||||
}
|
||||
|
||||
/// A token to transmit a single network packet.
|
||||
|
@ -321,5 +338,6 @@ pub trait TxToken {
|
|||
/// The timestamp must be a number of milliseconds, monotonically increasing since an
|
||||
/// arbitrary moment in time, such as system startup.
|
||||
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>;
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use byteorder::{ByteOrder, NativeEndian};
|
||||
use phy::Medium;
|
||||
#[cfg(feature = "std")]
|
||||
use std::cell::RefCell;
|
||||
#[cfg(feature = "std")]
|
||||
use std::io::Write;
|
||||
use byteorder::{ByteOrder, NativeEndian};
|
||||
use phy::Medium;
|
||||
|
||||
use crate::Result;
|
||||
use crate::phy::{self, DeviceCapabilities, Device};
|
||||
use crate::phy::{self, Device, DeviceCapabilities};
|
||||
use crate::time::Instant;
|
||||
use crate::Result;
|
||||
|
||||
enum_with_unknown! {
|
||||
/// Captured packet header type.
|
||||
|
@ -28,7 +28,7 @@ pub enum PcapMode {
|
|||
/// Capture only received packets.
|
||||
RxOnly,
|
||||
/// Capture only transmitted packets.
|
||||
TxOnly
|
||||
TxOnly,
|
||||
}
|
||||
|
||||
/// A packet capture sink.
|
||||
|
@ -121,7 +121,8 @@ impl<T: Write> PcapSink for RefCell<T> {
|
|||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PcapWriter<D, S>
|
||||
where D: for<'a> Device<'a>,
|
||||
where
|
||||
D: for<'a> Device<'a>,
|
||||
S: PcapSink + Clone,
|
||||
{
|
||||
lower: D,
|
||||
|
@ -145,27 +146,49 @@ impl<D: for<'a> Device<'a>, S: PcapSink + Clone> PcapWriter<D, S> {
|
|||
}
|
||||
|
||||
impl<'a, D, S> Device<'a> for PcapWriter<D, S>
|
||||
where D: for<'b> Device<'b>,
|
||||
where
|
||||
D: for<'b> Device<'b>,
|
||||
S: PcapSink + Clone + 'a,
|
||||
{
|
||||
type RxToken = RxToken<<D as Device<'a>>::RxToken, S>;
|
||||
type TxToken = TxToken<<D as Device<'a>>::TxToken, S>;
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities { self.lower.capabilities() }
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
self.lower.capabilities()
|
||||
}
|
||||
|
||||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
let &mut Self { ref mut lower, ref sink, mode, .. } = self;
|
||||
let &mut Self {
|
||||
ref mut lower,
|
||||
ref sink,
|
||||
mode,
|
||||
..
|
||||
} = self;
|
||||
lower.receive().map(|(rx_token, tx_token)| {
|
||||
let rx = RxToken { token: rx_token, sink: sink.clone(), mode };
|
||||
let tx = TxToken { token: tx_token, sink: sink.clone(), mode };
|
||||
let rx = RxToken {
|
||||
token: rx_token,
|
||||
sink: sink.clone(),
|
||||
mode,
|
||||
};
|
||||
let tx = TxToken {
|
||||
token: tx_token,
|
||||
sink: sink.clone(),
|
||||
mode,
|
||||
};
|
||||
(rx, tx)
|
||||
})
|
||||
}
|
||||
|
||||
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
||||
let &mut Self { ref mut lower, ref sink, mode } = self;
|
||||
lower.transmit().map(|token| {
|
||||
TxToken { token, sink: sink.clone(), mode }
|
||||
let &mut Self {
|
||||
ref mut lower,
|
||||
ref sink,
|
||||
mode,
|
||||
} = self;
|
||||
lower.transmit().map(|token| TxToken {
|
||||
token,
|
||||
sink: sink.clone(),
|
||||
mode,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -182,9 +205,8 @@ impl<Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<Rx, S> {
|
|||
let Self { token, sink, mode } = self;
|
||||
token.consume(timestamp, |buffer| {
|
||||
match mode {
|
||||
PcapMode::Both | PcapMode::RxOnly =>
|
||||
sink.packet(timestamp, buffer.as_ref()),
|
||||
PcapMode::TxOnly => ()
|
||||
PcapMode::Both | PcapMode::RxOnly => sink.packet(timestamp, buffer.as_ref()),
|
||||
PcapMode::TxOnly => (),
|
||||
}
|
||||
f(buffer)
|
||||
})
|
||||
|
@ -195,20 +217,20 @@ impl<Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<Rx, S> {
|
|||
pub struct TxToken<Tx: phy::TxToken, S: PcapSink> {
|
||||
token: Tx,
|
||||
sink: S,
|
||||
mode: PcapMode
|
||||
mode: PcapMode,
|
||||
}
|
||||
|
||||
impl<Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<Tx, S> {
|
||||
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let Self { token, sink, mode } = self;
|
||||
token.consume(timestamp, len, |buffer| {
|
||||
let result = f(buffer);
|
||||
match mode {
|
||||
PcapMode::Both | PcapMode::TxOnly =>
|
||||
sink.packet(timestamp, &buffer),
|
||||
PcapMode::RxOnly => ()
|
||||
PcapMode::Both | PcapMode::TxOnly => sink.packet(timestamp, buffer),
|
||||
PcapMode::RxOnly => (),
|
||||
};
|
||||
result
|
||||
})
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
use std::cell::RefCell;
|
||||
use std::vec::Vec;
|
||||
use std::rc::Rc;
|
||||
use std::io;
|
||||
use std::os::unix::io::{RawFd, AsRawFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::rc::Rc;
|
||||
use std::vec::Vec;
|
||||
|
||||
use crate::Result;
|
||||
use crate::phy::{self, sys, DeviceCapabilities, Device, Medium};
|
||||
use crate::phy::{self, sys, Device, DeviceCapabilities, Medium};
|
||||
use crate::time::Instant;
|
||||
use crate::Result;
|
||||
|
||||
/// A socket that captures or transmits the complete frame.
|
||||
#[derive(Debug)]
|
||||
pub struct RawSocket {
|
||||
lower: Rc<RefCell<sys::RawSocketDesc>>,
|
||||
mtu: usize
|
||||
mtu: usize,
|
||||
}
|
||||
|
||||
impl AsRawFd for RawSocket {
|
||||
|
@ -32,7 +32,7 @@ impl RawSocket {
|
|||
let mtu = lower.interface_mtu()?;
|
||||
Ok(RawSocket {
|
||||
lower: Rc::new(RefCell::new(lower)),
|
||||
mtu: mtu
|
||||
mtu: mtu,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -56,13 +56,13 @@ impl<'a> Device<'a> for RawSocket {
|
|||
Ok(size) => {
|
||||
buffer.resize(size, 0);
|
||||
let rx = RxToken { buffer };
|
||||
let tx = TxToken { lower: self.lower.clone() };
|
||||
let tx = TxToken {
|
||||
lower: self.lower.clone(),
|
||||
};
|
||||
Some((rx, tx))
|
||||
}
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
|
||||
None
|
||||
}
|
||||
Err(err) => panic!("{}", err)
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None,
|
||||
Err(err) => panic!("{}", err),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,12 +75,13 @@ impl<'a> Device<'a> for RawSocket {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub struct RxToken {
|
||||
buffer: Vec<u8>
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl phy::RxToken for RxToken {
|
||||
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
f(&mut self.buffer[..])
|
||||
}
|
||||
|
@ -93,7 +94,8 @@ pub struct TxToken {
|
|||
|
||||
impl phy::TxToken for TxToken {
|
||||
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let mut lower = self.lower.borrow_mut();
|
||||
let mut buffer = vec![0; len];
|
||||
|
|
|
@ -4,8 +4,8 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
|||
|
||||
use libc;
|
||||
|
||||
use crate::wire::ETHERNET_HEADER_LEN;
|
||||
use super::{ifreq, ifreq_for};
|
||||
use crate::wire::ETHERNET_HEADER_LEN;
|
||||
|
||||
/// set interface
|
||||
#[cfg(any(target_os = "macos", target_os = "openbsd"))]
|
||||
|
|
|
@ -1,25 +1,45 @@
|
|||
#![allow(unsafe_code)]
|
||||
|
||||
use std::{mem, ptr, io};
|
||||
use std::os::unix::io::RawFd;
|
||||
use crate::time::Duration;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::{io, mem, ptr};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[path = "linux.rs"]
|
||||
mod imp;
|
||||
|
||||
#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))]
|
||||
pub mod raw_socket;
|
||||
#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
|
||||
#[cfg(all(
|
||||
feature = "phy-raw_socket",
|
||||
not(any(target_os = "linux", target_os = "android")),
|
||||
unix
|
||||
))]
|
||||
pub mod bpf;
|
||||
#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
|
||||
#[cfg(all(
|
||||
feature = "phy-raw_socket",
|
||||
any(target_os = "linux", target_os = "android")
|
||||
))]
|
||||
pub mod raw_socket;
|
||||
#[cfg(all(
|
||||
feature = "phy-tuntap_interface",
|
||||
any(target_os = "linux", target_os = "android")
|
||||
))]
|
||||
pub mod tuntap_interface;
|
||||
|
||||
#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))]
|
||||
pub use self::raw_socket::RawSocketDesc;
|
||||
#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
|
||||
#[cfg(all(
|
||||
feature = "phy-raw_socket",
|
||||
not(any(target_os = "linux", target_os = "android")),
|
||||
unix
|
||||
))]
|
||||
pub use self::bpf::BpfDevice as RawSocketDesc;
|
||||
#[cfg(all(feature = "phy-tuntap_interface", any(target_os = "linux", target_os = "android")))]
|
||||
#[cfg(all(
|
||||
feature = "phy-raw_socket",
|
||||
any(target_os = "linux", target_os = "android")
|
||||
))]
|
||||
pub use self::raw_socket::RawSocketDesc;
|
||||
#[cfg(all(
|
||||
feature = "phy-tuntap_interface",
|
||||
any(target_os = "linux", target_os = "android")
|
||||
))]
|
||||
pub use self::tuntap_interface::TunTapInterfaceDesc;
|
||||
|
||||
/// Wait until given file descriptor becomes readable, but no longer than given timeout.
|
||||
|
@ -44,9 +64,11 @@ pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
|
|||
exceptfds.assume_init()
|
||||
};
|
||||
|
||||
let mut timeout = libc::timeval { tv_sec: 0, tv_usec: 0 };
|
||||
let timeout_ptr =
|
||||
if let Some(duration) = duration {
|
||||
let mut timeout = libc::timeval {
|
||||
tv_sec: 0,
|
||||
tv_usec: 0,
|
||||
};
|
||||
let timeout_ptr = if let Some(duration) = duration {
|
||||
timeout.tv_sec = duration.secs() as libc::time_t;
|
||||
timeout.tv_usec = (duration.millis() * 1_000) as libc::suseconds_t;
|
||||
&mut timeout as *mut _
|
||||
|
@ -54,25 +76,39 @@ pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
|
|||
ptr::null_mut()
|
||||
};
|
||||
|
||||
let res = libc::select(fd + 1, &mut readfds, &mut writefds, &mut exceptfds, timeout_ptr);
|
||||
if res == -1 { return Err(io::Error::last_os_error()) }
|
||||
let res = libc::select(
|
||||
fd + 1,
|
||||
&mut readfds,
|
||||
&mut writefds,
|
||||
&mut exceptfds,
|
||||
timeout_ptr,
|
||||
);
|
||||
if res == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))]
|
||||
#[cfg(all(
|
||||
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"),
|
||||
unix
|
||||
))]
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct ifreq {
|
||||
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
|
||||
ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
|
||||
ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */
|
||||
}
|
||||
|
||||
#[cfg(all(any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"), unix))]
|
||||
#[cfg(all(
|
||||
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"),
|
||||
unix
|
||||
))]
|
||||
fn ifreq_for(name: &str) -> ifreq {
|
||||
let mut ifreq = ifreq {
|
||||
ifr_name: [0; libc::IF_NAMESIZE],
|
||||
ifr_data: 0
|
||||
ifr_data: 0,
|
||||
};
|
||||
for (i, byte) in name.as_bytes().iter().enumerate() {
|
||||
ifreq.ifr_name[i] = *byte as libc::c_char
|
||||
|
@ -80,13 +116,20 @@ fn ifreq_for(name: &str) -> ifreq {
|
|||
ifreq
|
||||
}
|
||||
|
||||
#[cfg(all(any(target_os = "linux", target_os = "android"),
|
||||
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")))]
|
||||
fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq,
|
||||
cmd: libc::c_ulong) -> io::Result<libc::c_int> {
|
||||
#[cfg(all(
|
||||
any(target_os = "linux", target_os = "android"),
|
||||
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")
|
||||
))]
|
||||
fn ifreq_ioctl(
|
||||
lower: libc::c_int,
|
||||
ifreq: &mut ifreq,
|
||||
cmd: libc::c_ulong,
|
||||
) -> io::Result<libc::c_int> {
|
||||
unsafe {
|
||||
let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq);
|
||||
if res == -1 { return Err(io::Error::last_os_error()) }
|
||||
if res == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ifreq.ifr_data)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use std::{mem, io};
|
||||
use std::os::unix::io::{RawFd, AsRawFd};
|
||||
use super::*;
|
||||
use crate::wire::EthernetFrame;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::{io, mem};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RawSocketDesc {
|
||||
lower: libc::c_int,
|
||||
ifreq: ifreq
|
||||
ifreq: ifreq,
|
||||
}
|
||||
|
||||
impl AsRawFd for RawSocketDesc {
|
||||
|
@ -18,22 +18,28 @@ impl AsRawFd for RawSocketDesc {
|
|||
impl RawSocketDesc {
|
||||
pub fn new(name: &str) -> io::Result<RawSocketDesc> {
|
||||
let lower = unsafe {
|
||||
let lower = libc::socket(libc::AF_PACKET, libc::SOCK_RAW | libc::SOCK_NONBLOCK,
|
||||
imp::ETH_P_ALL.to_be() as i32);
|
||||
if lower == -1 { return Err(io::Error::last_os_error()) }
|
||||
let lower = libc::socket(
|
||||
libc::AF_PACKET,
|
||||
libc::SOCK_RAW | libc::SOCK_NONBLOCK,
|
||||
imp::ETH_P_ALL.to_be() as i32,
|
||||
);
|
||||
if lower == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
lower
|
||||
};
|
||||
|
||||
Ok(RawSocketDesc {
|
||||
lower: lower,
|
||||
ifreq: ifreq_for(name)
|
||||
ifreq: ifreq_for(name),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn interface_mtu(&mut self) -> io::Result<usize> {
|
||||
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
|
||||
// smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
|
||||
let ip_mtu = ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?;
|
||||
let ip_mtu =
|
||||
ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?;
|
||||
Ok(ip_mtu + EthernetFrame::<&[u8]>::header_len())
|
||||
}
|
||||
|
||||
|
@ -45,14 +51,18 @@ impl RawSocketDesc {
|
|||
sll_hatype: 1,
|
||||
sll_pkttype: 0,
|
||||
sll_halen: 6,
|
||||
sll_addr: [0; 8]
|
||||
sll_addr: [0; 8],
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let res = libc::bind(self.lower,
|
||||
let res = libc::bind(
|
||||
self.lower,
|
||||
&sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
|
||||
mem::size_of::<libc::sockaddr_ll>() as u32);
|
||||
if res == -1 { return Err(io::Error::last_os_error()) }
|
||||
mem::size_of::<libc::sockaddr_ll>() as u32,
|
||||
);
|
||||
if res == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -60,18 +70,30 @@ impl RawSocketDesc {
|
|||
|
||||
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let len = libc::recv(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
|
||||
buffer.len(), 0);
|
||||
if len == -1 { return Err(io::Error::last_os_error()) }
|
||||
let len = libc::recv(
|
||||
self.lower,
|
||||
buffer.as_mut_ptr() as *mut libc::c_void,
|
||||
buffer.len(),
|
||||
0,
|
||||
);
|
||||
if len == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(len as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let len = libc::send(self.lower, buffer.as_ptr() as *const libc::c_void,
|
||||
buffer.len(), 0);
|
||||
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
|
||||
let len = libc::send(
|
||||
self.lower,
|
||||
buffer.as_ptr() as *const libc::c_void,
|
||||
buffer.len(),
|
||||
0,
|
||||
);
|
||||
if len == -1 {
|
||||
Err(io::Error::last_os_error()).unwrap()
|
||||
}
|
||||
Ok(len as usize)
|
||||
}
|
||||
}
|
||||
|
@ -79,6 +101,8 @@ impl RawSocketDesc {
|
|||
|
||||
impl Drop for RawSocketDesc {
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::close(self.lower); }
|
||||
unsafe {
|
||||
libc::close(self.lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::io;
|
||||
use std::os::unix::io::{RawFd, AsRawFd};
|
||||
use super::*;
|
||||
use crate::{phy::Medium, wire::EthernetFrame};
|
||||
use std::io;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TunTapInterfaceDesc {
|
||||
|
@ -19,9 +19,13 @@ impl AsRawFd for TunTapInterfaceDesc {
|
|||
impl TunTapInterfaceDesc {
|
||||
pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
|
||||
let lower = unsafe {
|
||||
let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char,
|
||||
libc::O_RDWR | libc::O_NONBLOCK);
|
||||
if lower == -1 { return Err(io::Error::last_os_error()) }
|
||||
let lower = libc::open(
|
||||
"/dev/net/tun\0".as_ptr() as *const libc::c_char,
|
||||
libc::O_RDWR | libc::O_NONBLOCK,
|
||||
);
|
||||
if lower == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
lower
|
||||
};
|
||||
|
||||
|
@ -46,13 +50,17 @@ impl TunTapInterfaceDesc {
|
|||
pub fn interface_mtu(&mut self) -> io::Result<usize> {
|
||||
let lower = unsafe {
|
||||
let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP);
|
||||
if lower == -1 { return Err(io::Error::last_os_error()) }
|
||||
if lower == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
lower
|
||||
};
|
||||
|
||||
let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize);
|
||||
|
||||
unsafe { libc::close(lower); }
|
||||
unsafe {
|
||||
libc::close(lower);
|
||||
}
|
||||
|
||||
// Propagate error after close, to ensure we always close.
|
||||
let ip_mtu = ip_mtu?;
|
||||
|
@ -71,18 +79,28 @@ impl TunTapInterfaceDesc {
|
|||
|
||||
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let len = libc::read(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
|
||||
buffer.len());
|
||||
if len == -1 { return Err(io::Error::last_os_error()) }
|
||||
let len = libc::read(
|
||||
self.lower,
|
||||
buffer.as_mut_ptr() as *mut libc::c_void,
|
||||
buffer.len(),
|
||||
);
|
||||
if len == -1 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
Ok(len as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let len = libc::write(self.lower, buffer.as_ptr() as *const libc::c_void,
|
||||
buffer.len());
|
||||
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
|
||||
let len = libc::write(
|
||||
self.lower,
|
||||
buffer.as_ptr() as *const libc::c_void,
|
||||
buffer.len(),
|
||||
);
|
||||
if len == -1 {
|
||||
Err(io::Error::last_os_error()).unwrap()
|
||||
}
|
||||
Ok(len as usize)
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +108,8 @@ impl TunTapInterfaceDesc {
|
|||
|
||||
impl Drop for TunTapInterfaceDesc {
|
||||
fn drop(&mut self) {
|
||||
unsafe { libc::close(self.lower); }
|
||||
unsafe {
|
||||
libc::close(self.lower);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use core::fmt;
|
||||
|
||||
use crate::{Result, wire::pretty_print::{PrettyIndent, PrettyPrint}};
|
||||
use crate::phy::{self, DeviceCapabilities, Device, Medium};
|
||||
use crate::phy::{self, Device, DeviceCapabilities, Medium};
|
||||
use crate::time::Instant;
|
||||
use crate::{
|
||||
wire::pretty_print::{PrettyIndent, PrettyPrint},
|
||||
Result,
|
||||
};
|
||||
|
||||
/// A tracer device.
|
||||
///
|
||||
|
@ -42,28 +45,48 @@ impl<D: for<'a> Device<'a>> Tracer<D> {
|
|||
}
|
||||
|
||||
impl<'a, D> Device<'a> for Tracer<D>
|
||||
where D: for<'b> Device<'b>,
|
||||
where
|
||||
D: for<'b> Device<'b>,
|
||||
{
|
||||
type RxToken = RxToken<<D as Device<'a>>::RxToken>;
|
||||
type TxToken = TxToken<<D as Device<'a>>::TxToken>;
|
||||
|
||||
fn capabilities(&self) -> DeviceCapabilities { self.inner.capabilities() }
|
||||
fn capabilities(&self) -> DeviceCapabilities {
|
||||
self.inner.capabilities()
|
||||
}
|
||||
|
||||
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
|
||||
let &mut Self { ref mut inner, writer, .. } = self;
|
||||
let &mut Self {
|
||||
ref mut inner,
|
||||
writer,
|
||||
..
|
||||
} = self;
|
||||
let medium = inner.capabilities().medium;
|
||||
inner.receive().map(|(rx_token, tx_token)| {
|
||||
let rx = RxToken { token: rx_token, writer, medium };
|
||||
let tx = TxToken { token: tx_token, writer, medium };
|
||||
let rx = RxToken {
|
||||
token: rx_token,
|
||||
writer,
|
||||
medium,
|
||||
};
|
||||
let tx = TxToken {
|
||||
token: tx_token,
|
||||
writer,
|
||||
medium,
|
||||
};
|
||||
(rx, tx)
|
||||
})
|
||||
}
|
||||
|
||||
fn transmit(&'a mut self) -> Option<Self::TxToken> {
|
||||
let &mut Self { ref mut inner, writer } = self;
|
||||
let &mut Self {
|
||||
ref mut inner,
|
||||
writer,
|
||||
} = self;
|
||||
let medium = inner.capabilities().medium;
|
||||
inner.transmit().map(|tx_token| {
|
||||
TxToken { token: tx_token, medium, writer }
|
||||
inner.transmit().map(|tx_token| TxToken {
|
||||
token: tx_token,
|
||||
medium,
|
||||
writer,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -77,15 +100,23 @@ pub struct RxToken<Rx: phy::RxToken> {
|
|||
|
||||
impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
|
||||
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let Self { token, writer, medium } = self;
|
||||
let Self {
|
||||
token,
|
||||
writer,
|
||||
medium,
|
||||
} = self;
|
||||
token.consume(timestamp, |buffer| {
|
||||
writer(timestamp, Packet{
|
||||
writer(
|
||||
timestamp,
|
||||
Packet {
|
||||
buffer,
|
||||
medium,
|
||||
prefix: "<- ",
|
||||
});
|
||||
},
|
||||
);
|
||||
f(buffer)
|
||||
})
|
||||
}
|
||||
|
@ -100,16 +131,24 @@ pub struct TxToken<Tx: phy::TxToken> {
|
|||
|
||||
impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
|
||||
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let Self { token, writer, medium } = self;
|
||||
let Self {
|
||||
token,
|
||||
writer,
|
||||
medium,
|
||||
} = self;
|
||||
token.consume(timestamp, len, |buffer| {
|
||||
let result = f(buffer);
|
||||
writer(timestamp, Packet{
|
||||
writer(
|
||||
timestamp,
|
||||
Packet {
|
||||
buffer,
|
||||
medium,
|
||||
prefix: "-> ",
|
||||
});
|
||||
},
|
||||
);
|
||||
result
|
||||
})
|
||||
}
|
||||
|
@ -126,15 +165,31 @@ impl<'a> fmt::Display for Packet<'a> {
|
|||
let mut indent = PrettyIndent::new(self.prefix);
|
||||
match self.medium {
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
|
||||
Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(
|
||||
&self.buffer,
|
||||
f,
|
||||
&mut indent,
|
||||
),
|
||||
#[cfg(feature = "medium-ip")]
|
||||
Medium::Ip => match crate::wire::IpVersion::of_packet(&self.buffer) {
|
||||
Medium::Ip => match crate::wire::IpVersion::of_packet(self.buffer) {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
Ok(crate::wire::IpVersion::Ipv4) => crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Ok(crate::wire::IpVersion::Ipv6) => crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(&self.buffer, f, &mut indent),
|
||||
_ => f.write_str("unrecognized IP version")
|
||||
Ok(crate::wire::IpVersion::Ipv4) => {
|
||||
crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(
|
||||
&self.buffer,
|
||||
f,
|
||||
&mut indent,
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Ok(crate::wire::IpVersion::Ipv6) => {
|
||||
crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(
|
||||
&self.buffer,
|
||||
f,
|
||||
&mut indent,
|
||||
)
|
||||
}
|
||||
_ => f.write_str("unrecognized IP version"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
use std::cell::RefCell;
|
||||
use std::vec::Vec;
|
||||
use std::rc::Rc;
|
||||
use std::io;
|
||||
use std::os::unix::io::{RawFd, AsRawFd};
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::rc::Rc;
|
||||
use std::vec::Vec;
|
||||
|
||||
use crate::Result;
|
||||
use crate::phy::{self, sys, DeviceCapabilities, Device, Medium};
|
||||
use crate::phy::{self, sys, Device, DeviceCapabilities, Medium};
|
||||
use crate::time::Instant;
|
||||
use crate::Result;
|
||||
|
||||
/// A virtual TUN (IP) or TAP (Ethernet) interface.
|
||||
#[derive(Debug)]
|
||||
|
@ -59,13 +59,13 @@ impl<'a> Device<'a> for TunTapInterface {
|
|||
Ok(size) => {
|
||||
buffer.resize(size, 0);
|
||||
let rx = RxToken { buffer };
|
||||
let tx = TxToken { lower: self.lower.clone() };
|
||||
let tx = TxToken {
|
||||
lower: self.lower.clone(),
|
||||
};
|
||||
Some((rx, tx))
|
||||
}
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
|
||||
None
|
||||
}
|
||||
Err(err) => panic!("{}", err)
|
||||
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None,
|
||||
Err(err) => panic!("{}", err),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,12 +78,13 @@ impl<'a> Device<'a> for TunTapInterface {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub struct RxToken {
|
||||
buffer: Vec<u8>
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl phy::RxToken for RxToken {
|
||||
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
f(&mut self.buffer[..])
|
||||
}
|
||||
|
@ -96,7 +97,8 @@ pub struct TxToken {
|
|||
|
||||
impl phy::TxToken for TxToken {
|
||||
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut [u8]) -> Result<R>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<R>,
|
||||
{
|
||||
let mut lower = self.lower.borrow_mut();
|
||||
let mut buffer = vec![0; len];
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::{Error, Result};
|
||||
use crate::wire::{IpProtocol, IpAddress,
|
||||
Ipv4Cidr, Ipv4Address, Ipv4Repr,
|
||||
UdpRepr, UDP_HEADER_LEN,
|
||||
DhcpPacket, DhcpRepr, DhcpMessageType, DHCP_CLIENT_PORT, DHCP_SERVER_PORT, DHCP_MAX_DNS_SERVER_COUNT};
|
||||
use crate::wire::dhcpv4::{field as dhcpv4_field};
|
||||
use crate::socket::{SocketMeta, Context};
|
||||
use crate::time::{Instant, Duration};
|
||||
use crate::socket::SocketHandle;
|
||||
use crate::socket::{Context, SocketMeta};
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::wire::dhcpv4::field as dhcpv4_field;
|
||||
use crate::wire::{
|
||||
DhcpMessageType, DhcpPacket, DhcpRepr, IpAddress, IpProtocol, Ipv4Address, Ipv4Cidr, Ipv4Repr,
|
||||
UdpRepr, DHCP_CLIENT_PORT, DHCP_MAX_DNS_SERVER_COUNT, DHCP_SERVER_PORT, UDP_HEADER_LEN,
|
||||
};
|
||||
use crate::{Error, Result};
|
||||
|
||||
use super::{PollAt, Socket};
|
||||
|
||||
|
@ -133,7 +133,7 @@ impl Dhcpv4Socket {
|
|||
pub fn new() -> Self {
|
||||
Dhcpv4Socket {
|
||||
meta: SocketMeta::default(),
|
||||
state: ClientState::Discovering(DiscoverState{
|
||||
state: ClientState::Discovering(DiscoverState {
|
||||
retry_at: Instant::from_millis(0),
|
||||
}),
|
||||
config_changed: true,
|
||||
|
@ -159,7 +159,13 @@ impl Dhcpv4Socket {
|
|||
PollAt::Time(t)
|
||||
}
|
||||
|
||||
pub(crate) fn process(&mut self, cx: &Context, ip_repr: &Ipv4Repr, repr: &UdpRepr, payload: &[u8]) -> Result<()> {
|
||||
pub(crate) fn process(
|
||||
&mut self,
|
||||
cx: &Context,
|
||||
ip_repr: &Ipv4Repr,
|
||||
repr: &UdpRepr,
|
||||
payload: &[u8],
|
||||
) -> Result<()> {
|
||||
let src_ip = ip_repr.src_addr;
|
||||
|
||||
// This is enforced in interface.rs.
|
||||
|
@ -179,23 +185,35 @@ impl Dhcpv4Socket {
|
|||
return Ok(());
|
||||
}
|
||||
};
|
||||
if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() { return Ok(()) }
|
||||
if dhcp_repr.transaction_id != self.transaction_id { return Ok(()) }
|
||||
if dhcp_repr.client_hardware_address != cx.ethernet_address.unwrap() {
|
||||
return Ok(());
|
||||
}
|
||||
if dhcp_repr.transaction_id != self.transaction_id {
|
||||
return Ok(());
|
||||
}
|
||||
let server_identifier = match dhcp_repr.server_identifier {
|
||||
Some(server_identifier) => server_identifier,
|
||||
None => {
|
||||
net_debug!("DHCP ignoring {:?} because missing server_identifier", dhcp_repr.message_type);
|
||||
net_debug!(
|
||||
"DHCP ignoring {:?} because missing server_identifier",
|
||||
dhcp_repr.message_type
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier);
|
||||
net_debug!(
|
||||
"DHCP recv {:?} from {} ({})",
|
||||
dhcp_repr.message_type,
|
||||
src_ip,
|
||||
server_identifier
|
||||
);
|
||||
|
||||
match (&mut self.state, dhcp_repr.message_type){
|
||||
match (&mut self.state, dhcp_repr.message_type) {
|
||||
(ClientState::Discovering(_state), DhcpMessageType::Offer) => {
|
||||
if !dhcp_repr.your_ip.is_unicast() {
|
||||
net_debug!("DHCP ignoring OFFER because your_ip is not unicast");
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.state = ClientState::Requesting(RequestState {
|
||||
|
@ -205,13 +223,15 @@ impl Dhcpv4Socket {
|
|||
address: src_ip,
|
||||
identifier: server_identifier,
|
||||
},
|
||||
requested_ip: dhcp_repr.your_ip // use the offered ip
|
||||
requested_ip: dhcp_repr.your_ip, // use the offered ip
|
||||
});
|
||||
}
|
||||
(ClientState::Requesting(state), DhcpMessageType::Ack) => {
|
||||
if let Some((config, renew_at, expires_at)) = Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration) {
|
||||
if let Some((config, renew_at, expires_at)) =
|
||||
Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration)
|
||||
{
|
||||
self.config_changed = true;
|
||||
self.state = ClientState::Renewing(RenewState{
|
||||
self.state = ClientState::Renewing(RenewState {
|
||||
server: state.server,
|
||||
config,
|
||||
renew_at,
|
||||
|
@ -223,7 +243,9 @@ impl Dhcpv4Socket {
|
|||
self.reset();
|
||||
}
|
||||
(ClientState::Renewing(state), DhcpMessageType::Ack) => {
|
||||
if let Some((config, renew_at, expires_at)) = Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration) {
|
||||
if let Some((config, renew_at, expires_at)) =
|
||||
Self::parse_ack(cx.now, &dhcp_repr, self.max_lease_duration)
|
||||
{
|
||||
state.renew_at = renew_at;
|
||||
state.expires_at = expires_at;
|
||||
if state.config != config {
|
||||
|
@ -236,19 +258,26 @@ impl Dhcpv4Socket {
|
|||
self.reset();
|
||||
}
|
||||
_ => {
|
||||
net_debug!("DHCP ignoring {:?}: unexpected in current state", dhcp_repr.message_type);
|
||||
net_debug!(
|
||||
"DHCP ignoring {:?}: unexpected in current state",
|
||||
dhcp_repr.message_type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_ack(now: Instant, dhcp_repr: &DhcpRepr, max_lease_duration: Option<Duration>) -> Option<(Config, Instant, Instant)> {
|
||||
fn parse_ack(
|
||||
now: Instant,
|
||||
dhcp_repr: &DhcpRepr,
|
||||
max_lease_duration: Option<Duration>,
|
||||
) -> Option<(Config, Instant, Instant)> {
|
||||
let subnet_mask = match dhcp_repr.subnet_mask {
|
||||
Some(subnet_mask) => subnet_mask,
|
||||
None => {
|
||||
net_debug!("DHCP ignoring ACK because missing subnet_mask");
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -256,16 +285,19 @@ impl Dhcpv4Socket {
|
|||
Some(prefix_len) => prefix_len,
|
||||
None => {
|
||||
net_debug!("DHCP ignoring ACK because subnet_mask is not a valid mask");
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if !dhcp_repr.your_ip.is_unicast() {
|
||||
net_debug!("DHCP ignoring ACK because your_ip is not unicast");
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut lease_duration = dhcp_repr.lease_duration.map(|d| Duration::from_secs(d as _)).unwrap_or(DEFAULT_LEASE_DURATION);
|
||||
let mut lease_duration = dhcp_repr
|
||||
.lease_duration
|
||||
.map(|d| Duration::from_secs(d as _))
|
||||
.unwrap_or(DEFAULT_LEASE_DURATION);
|
||||
if let Some(max_lease_duration) = max_lease_duration {
|
||||
lease_duration = lease_duration.min(max_lease_duration);
|
||||
}
|
||||
|
@ -275,8 +307,7 @@ impl Dhcpv4Socket {
|
|||
let mut dns_servers = [None; DHCP_MAX_DNS_SERVER_COUNT];
|
||||
if let Some(received) = dhcp_repr.dns_servers {
|
||||
let mut i = 0;
|
||||
for addr in received.iter() {
|
||||
if let Some(addr) = addr{
|
||||
for addr in received.iter().flatten() {
|
||||
if addr.is_unicast() {
|
||||
// This can never be out-of-bounds since both arrays have length DHCP_MAX_DNS_SERVER_COUNT
|
||||
dns_servers[i] = Some(*addr);
|
||||
|
@ -284,11 +315,10 @@ impl Dhcpv4Socket {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let config = Config{
|
||||
let config = Config {
|
||||
address: Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len),
|
||||
router: dhcp_repr.router,
|
||||
dns_servers: dns_servers
|
||||
dns_servers: dns_servers,
|
||||
};
|
||||
|
||||
// RFC 2131 indicates clients should renew a lease halfway through its expiration.
|
||||
|
@ -299,8 +329,9 @@ impl Dhcpv4Socket {
|
|||
}
|
||||
|
||||
pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
|
||||
where F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()> {
|
||||
|
||||
where
|
||||
F: FnOnce((Ipv4Repr, UdpRepr, DhcpRepr)) -> Result<()>,
|
||||
{
|
||||
// note: Dhcpv4Socket is only usable in ethernet mediums, so the
|
||||
// unwrap can never fail.
|
||||
let ethernet_addr = cx.ethernet_address.unwrap();
|
||||
|
@ -349,11 +380,15 @@ impl Dhcpv4Socket {
|
|||
match &mut self.state {
|
||||
ClientState::Discovering(state) => {
|
||||
if cx.now < state.retry_at {
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
|
||||
// send packet
|
||||
net_debug!("DHCP send DISCOVER to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr);
|
||||
net_debug!(
|
||||
"DHCP send DISCOVER to {}: {:?}",
|
||||
ipv4_repr.dst_addr,
|
||||
dhcp_repr
|
||||
);
|
||||
ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
|
||||
emit((ipv4_repr, udp_repr, dhcp_repr))?;
|
||||
|
||||
|
@ -364,14 +399,14 @@ impl Dhcpv4Socket {
|
|||
}
|
||||
ClientState::Requesting(state) => {
|
||||
if cx.now < state.retry_at {
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
|
||||
if state.retry >= REQUEST_RETRIES {
|
||||
net_debug!("DHCP request retries exceeded, restarting discovery");
|
||||
self.reset();
|
||||
// return Ok so we get polled again
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
dhcp_repr.message_type = DhcpMessageType::Request;
|
||||
|
@ -379,7 +414,11 @@ impl Dhcpv4Socket {
|
|||
dhcp_repr.requested_ip = Some(state.requested_ip);
|
||||
dhcp_repr.server_identifier = Some(state.server.identifier);
|
||||
|
||||
net_debug!("DHCP send request to {}: {:?}", ipv4_repr.dst_addr, dhcp_repr);
|
||||
net_debug!(
|
||||
"DHCP send request to {}: {:?}",
|
||||
ipv4_repr.dst_addr,
|
||||
dhcp_repr
|
||||
);
|
||||
ipv4_repr.payload_len = udp_repr.header_len() + dhcp_repr.buffer_len();
|
||||
emit((ipv4_repr, udp_repr, dhcp_repr))?;
|
||||
|
||||
|
@ -395,11 +434,11 @@ impl Dhcpv4Socket {
|
|||
net_debug!("DHCP lease expired");
|
||||
self.reset();
|
||||
// return Ok so we get polled again
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if cx.now < state.renew_at {
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
|
||||
ipv4_repr.src_addr = state.config.address.address();
|
||||
|
@ -440,7 +479,7 @@ impl Dhcpv4Socket {
|
|||
if let ClientState::Renewing(_) = &self.state {
|
||||
self.config_changed = true;
|
||||
}
|
||||
self.state = ClientState::Discovering(DiscoverState{
|
||||
self.state = ClientState::Discovering(DiscoverState {
|
||||
retry_at: Instant::from_millis(0),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,20 +2,20 @@ use core::cmp;
|
|||
#[cfg(feature = "async")]
|
||||
use core::task::Waker;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
|
||||
use crate::storage::{PacketBuffer, PacketMetadata};
|
||||
#[cfg(feature = "async")]
|
||||
use crate::socket::WakerRegistration;
|
||||
use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta};
|
||||
use crate::storage::{PacketBuffer, PacketMetadata};
|
||||
use crate::{Error, Result};
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::{Ipv4Address, Ipv4Repr, Icmpv4Packet, Icmpv4Repr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use crate::wire::{Ipv6Address, Ipv6Repr, Icmpv6Packet, Icmpv6Repr};
|
||||
use crate::wire::IcmpRepr;
|
||||
use crate::wire::{UdpPacket, UdpRepr};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::{Icmpv4Packet, Icmpv4Repr, Ipv4Address, Ipv4Repr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use crate::wire::{Icmpv6Packet, Icmpv6Repr, Ipv6Address, Ipv6Repr};
|
||||
use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr};
|
||||
use crate::wire::{UdpPacket, UdpRepr};
|
||||
|
||||
/// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for
|
||||
/// more details.
|
||||
|
@ -26,7 +26,7 @@ use crate::wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr};
|
|||
pub enum Endpoint {
|
||||
Unspecified,
|
||||
Ident(u16),
|
||||
Udp(IpEndpoint)
|
||||
Udp(IpEndpoint),
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
|
@ -34,13 +34,15 @@ impl Endpoint {
|
|||
match *self {
|
||||
Endpoint::Ident(_) => true,
|
||||
Endpoint::Udp(endpoint) => endpoint.port != 0,
|
||||
Endpoint::Unspecified => false
|
||||
Endpoint::Unspecified => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Endpoint {
|
||||
fn default() -> Endpoint { Endpoint::Unspecified }
|
||||
fn default() -> Endpoint {
|
||||
Endpoint::Unspecified
|
||||
}
|
||||
}
|
||||
|
||||
/// An ICMP packet metadata.
|
||||
|
@ -75,8 +77,7 @@ pub struct IcmpSocket<'a> {
|
|||
|
||||
impl<'a> IcmpSocket<'a> {
|
||||
/// Create an ICMP socket with the given buffers.
|
||||
pub fn new(rx_buffer: IcmpSocketBuffer<'a>,
|
||||
tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> {
|
||||
pub fn new(rx_buffer: IcmpSocketBuffer<'a>, tx_buffer: IcmpSocketBuffer<'a>) -> IcmpSocket<'a> {
|
||||
IcmpSocket {
|
||||
meta: SocketMeta::default(),
|
||||
rx_buffer: rx_buffer,
|
||||
|
@ -219,7 +220,9 @@ impl<'a> IcmpSocket<'a> {
|
|||
return Err(Error::Unaddressable);
|
||||
}
|
||||
|
||||
if self.is_open() { return Err(Error::Illegal) }
|
||||
if self.is_open() {
|
||||
return Err(Error::Illegal);
|
||||
}
|
||||
|
||||
self.endpoint = endpoint;
|
||||
|
||||
|
@ -282,13 +285,17 @@ impl<'a> IcmpSocket<'a> {
|
|||
/// size, and `Err(Error::Unaddressable)` if the remote address is unspecified.
|
||||
pub fn send(&mut self, size: usize, endpoint: IpAddress) -> Result<&mut [u8]> {
|
||||
if endpoint.is_unspecified() {
|
||||
return Err(Error::Unaddressable)
|
||||
return Err(Error::Unaddressable);
|
||||
}
|
||||
|
||||
let packet_buf = self.tx_buffer.enqueue(size, endpoint)?;
|
||||
|
||||
net_trace!("{}:{}: buffer to send {} octets",
|
||||
self.meta.handle, endpoint, size);
|
||||
net_trace!(
|
||||
"{}:{}: buffer to send {} octets",
|
||||
self.meta.handle,
|
||||
endpoint,
|
||||
size
|
||||
);
|
||||
Ok(packet_buf)
|
||||
}
|
||||
|
||||
|
@ -308,8 +315,12 @@ impl<'a> IcmpSocket<'a> {
|
|||
pub fn recv(&mut self) -> Result<(&[u8], IpAddress)> {
|
||||
let (endpoint, packet_buf) = self.rx_buffer.dequeue()?;
|
||||
|
||||
net_trace!("{}:{}: receive {} buffered octets",
|
||||
self.meta.handle, endpoint, packet_buf.len());
|
||||
net_trace!(
|
||||
"{}:{}: receive {} buffered octets",
|
||||
self.meta.handle,
|
||||
endpoint,
|
||||
packet_buf.len()
|
||||
);
|
||||
Ok((packet_buf, endpoint))
|
||||
}
|
||||
|
||||
|
@ -332,19 +343,33 @@ impl<'a> IcmpSocket<'a> {
|
|||
// accept Destination Unreachable messages with the data containing
|
||||
// a UDP packet send from the local port we are bound to.
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
(&Endpoint::Udp(endpoint), &IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. }))
|
||||
if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
|
||||
(
|
||||
&Endpoint::Udp(endpoint),
|
||||
&IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. }),
|
||||
) if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
|
||||
let packet = UdpPacket::new_unchecked(data);
|
||||
match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), &cx.caps.checksum) {
|
||||
match UdpRepr::parse(
|
||||
&packet,
|
||||
&ip_repr.src_addr(),
|
||||
&ip_repr.dst_addr(),
|
||||
&cx.caps.checksum,
|
||||
) {
|
||||
Ok(repr) => endpoint.port == repr.src_port,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Endpoint::Udp(endpoint), &IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. }))
|
||||
if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
|
||||
(
|
||||
&Endpoint::Udp(endpoint),
|
||||
&IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. }),
|
||||
) if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
|
||||
let packet = UdpPacket::new_unchecked(data);
|
||||
match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), &cx.caps.checksum) {
|
||||
match UdpRepr::parse(
|
||||
&packet,
|
||||
&ip_repr.src_addr(),
|
||||
&ip_repr.dst_addr(),
|
||||
&cx.caps.checksum,
|
||||
) {
|
||||
Ok(repr) => endpoint.port == repr.src_port,
|
||||
Err(_) => false,
|
||||
}
|
||||
|
@ -353,44 +378,70 @@ impl<'a> IcmpSocket<'a> {
|
|||
// Echo Request/Reply with the identifier field matching the endpoint
|
||||
// port.
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
(&Endpoint::Ident(bound_ident),
|
||||
&IcmpRepr::Ipv4(Icmpv4Repr::EchoRequest { ident, .. })) |
|
||||
(&Endpoint::Ident(bound_ident),
|
||||
&IcmpRepr::Ipv4(Icmpv4Repr::EchoReply { ident, .. })) =>
|
||||
ident == bound_ident,
|
||||
(
|
||||
&Endpoint::Ident(bound_ident),
|
||||
&IcmpRepr::Ipv4(Icmpv4Repr::EchoRequest { ident, .. }),
|
||||
)
|
||||
| (
|
||||
&Endpoint::Ident(bound_ident),
|
||||
&IcmpRepr::Ipv4(Icmpv4Repr::EchoReply { ident, .. }),
|
||||
) => ident == bound_ident,
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Endpoint::Ident(bound_ident),
|
||||
&IcmpRepr::Ipv6(Icmpv6Repr::EchoRequest { ident, .. })) |
|
||||
(&Endpoint::Ident(bound_ident),
|
||||
&IcmpRepr::Ipv6(Icmpv6Repr::EchoReply { ident, .. })) =>
|
||||
ident == bound_ident,
|
||||
(
|
||||
&Endpoint::Ident(bound_ident),
|
||||
&IcmpRepr::Ipv6(Icmpv6Repr::EchoRequest { ident, .. }),
|
||||
)
|
||||
| (
|
||||
&Endpoint::Ident(bound_ident),
|
||||
&IcmpRepr::Ipv6(Icmpv6Repr::EchoReply { ident, .. }),
|
||||
) => ident == bound_ident,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn process(&mut self, _cx: &Context, ip_repr: &IpRepr, icmp_repr: &IcmpRepr) -> Result<()> {
|
||||
pub(crate) fn process(
|
||||
&mut self,
|
||||
_cx: &Context,
|
||||
ip_repr: &IpRepr,
|
||||
icmp_repr: &IcmpRepr,
|
||||
) -> Result<()> {
|
||||
match *icmp_repr {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
IcmpRepr::Ipv4(ref icmp_repr) => {
|
||||
let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(),
|
||||
ip_repr.src_addr())?;
|
||||
icmp_repr.emit(&mut Icmpv4Packet::new_unchecked(packet_buf),
|
||||
&ChecksumCapabilities::default());
|
||||
let packet_buf = self
|
||||
.rx_buffer
|
||||
.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?;
|
||||
icmp_repr.emit(
|
||||
&mut Icmpv4Packet::new_unchecked(packet_buf),
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
|
||||
net_trace!("{}:{}: receiving {} octets",
|
||||
self.meta.handle, icmp_repr.buffer_len(), packet_buf.len());
|
||||
},
|
||||
net_trace!(
|
||||
"{}:{}: receiving {} octets",
|
||||
self.meta.handle,
|
||||
icmp_repr.buffer_len(),
|
||||
packet_buf.len()
|
||||
);
|
||||
}
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
IcmpRepr::Ipv6(ref icmp_repr) => {
|
||||
let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(),
|
||||
ip_repr.src_addr())?;
|
||||
icmp_repr.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(),
|
||||
let packet_buf = self
|
||||
.rx_buffer
|
||||
.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?;
|
||||
icmp_repr.emit(
|
||||
&ip_repr.src_addr(),
|
||||
&ip_repr.dst_addr(),
|
||||
&mut Icmpv6Packet::new_unchecked(packet_buf),
|
||||
&ChecksumCapabilities::default());
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
|
||||
net_trace!("{}:{}: receiving {} octets",
|
||||
self.meta.handle, icmp_repr.buffer_len(), packet_buf.len());
|
||||
},
|
||||
net_trace!(
|
||||
"{}:{}: receiving {} octets",
|
||||
self.meta.handle,
|
||||
icmp_repr.buffer_len(),
|
||||
packet_buf.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
|
@ -400,13 +451,18 @@ impl<'a> IcmpSocket<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn dispatch<F>(&mut self, _cx: &Context, emit: F) -> Result<()>
|
||||
where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>
|
||||
where
|
||||
F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>,
|
||||
{
|
||||
let handle = self.meta.handle;
|
||||
let hop_limit = self.hop_limit.unwrap_or(64);
|
||||
self.tx_buffer.dequeue_with(|remote_endpoint, packet_buf| {
|
||||
net_trace!("{}:{}: sending {} octets",
|
||||
handle, remote_endpoint, packet_buf.len());
|
||||
net_trace!(
|
||||
"{}:{}: sending {} octets",
|
||||
handle,
|
||||
remote_endpoint,
|
||||
packet_buf.len()
|
||||
);
|
||||
match *remote_endpoint {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
IpAddress::Ipv4(ipv4_addr) => {
|
||||
|
@ -420,12 +476,17 @@ impl<'a> IcmpSocket<'a> {
|
|||
hop_limit: hop_limit,
|
||||
});
|
||||
emit((ip_repr, IcmpRepr::Ipv4(repr)))
|
||||
},
|
||||
}
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
IpAddress::Ipv6(ipv6_addr) => {
|
||||
let packet = Icmpv6Packet::new_unchecked(&*packet_buf);
|
||||
let src_addr = Ipv6Address::default();
|
||||
let repr = Icmpv6Repr::parse(&src_addr.into(), &ipv6_addr.into(), &packet, &ChecksumCapabilities::ignored())?;
|
||||
let repr = Icmpv6Repr::parse(
|
||||
&src_addr.into(),
|
||||
&ipv6_addr.into(),
|
||||
&packet,
|
||||
&ChecksumCapabilities::ignored(),
|
||||
)?;
|
||||
let ip_repr = IpRepr::Ipv6(Ipv6Repr {
|
||||
src_addr: src_addr,
|
||||
dst_addr: ipv6_addr,
|
||||
|
@ -434,8 +495,8 @@ impl<'a> IcmpSocket<'a> {
|
|||
hop_limit: hop_limit,
|
||||
});
|
||||
emit((ip_repr, IcmpRepr::Ipv6(repr)))
|
||||
},
|
||||
_ => Err(Error::Unaddressable)
|
||||
}
|
||||
_ => Err(Error::Unaddressable),
|
||||
}
|
||||
})?;
|
||||
|
||||
|
@ -462,16 +523,21 @@ impl<'a> Into<Socket<'a>> for IcmpSocket<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests_common {
|
||||
pub use super::*;
|
||||
pub use crate::phy::DeviceCapabilities;
|
||||
pub use crate::wire::IpAddress;
|
||||
pub use super::*;
|
||||
|
||||
pub fn buffer(packets: usize) -> IcmpSocketBuffer<'static> {
|
||||
IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY; packets], vec![0; 66 * packets])
|
||||
IcmpSocketBuffer::new(
|
||||
vec![IcmpPacketMetadata::EMPTY; packets],
|
||||
vec![0; 66 * packets],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn socket(rx_buffer: IcmpSocketBuffer<'static>,
|
||||
tx_buffer: IcmpSocketBuffer<'static>) -> IcmpSocket<'static> {
|
||||
pub fn socket(
|
||||
rx_buffer: IcmpSocketBuffer<'static>,
|
||||
tx_buffer: IcmpSocketBuffer<'static>,
|
||||
) -> IcmpSocket<'static> {
|
||||
IcmpSocket::new(rx_buffer, tx_buffer)
|
||||
}
|
||||
|
||||
|
@ -493,12 +559,15 @@ mod test_ipv4 {
|
|||
|
||||
const REMOTE_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x02]);
|
||||
const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]);
|
||||
const LOCAL_END_V4: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv4(LOCAL_IPV4), port: LOCAL_PORT };
|
||||
const LOCAL_END_V4: IpEndpoint = IpEndpoint {
|
||||
addr: IpAddress::Ipv4(LOCAL_IPV4),
|
||||
port: LOCAL_PORT,
|
||||
};
|
||||
|
||||
static ECHOV4_REPR: Icmpv4Repr = Icmpv4Repr::EchoRequest {
|
||||
ident: 0x1234,
|
||||
seq_no: 0x5678,
|
||||
data: &[0xff; 16]
|
||||
data: &[0xff; 16],
|
||||
};
|
||||
|
||||
static LOCAL_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
|
||||
|
@ -506,7 +575,7 @@ mod test_ipv4 {
|
|||
dst_addr: REMOTE_IPV4,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: 24,
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
});
|
||||
|
||||
static REMOTE_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
|
||||
|
@ -514,14 +583,16 @@ mod test_ipv4 {
|
|||
dst_addr: LOCAL_IPV4,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: 24,
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn test_send_unaddressable() {
|
||||
let mut socket = socket(buffer(0), buffer(1));
|
||||
assert_eq!(socket.send_slice(b"abcdef", IpAddress::default()),
|
||||
Err(Error::Unaddressable));
|
||||
assert_eq!(
|
||||
socket.send_slice(b"abcdef", IpAddress::default()),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV4.into()), Ok(()));
|
||||
}
|
||||
|
||||
|
@ -530,34 +601,51 @@ mod test_ipv4 {
|
|||
let mut socket = socket(buffer(0), buffer(1));
|
||||
let checksum = ChecksumCapabilities::default();
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
|
||||
// This buffer is too long
|
||||
assert_eq!(socket.send_slice(&[0xff; 67], REMOTE_IPV4.into()), Err(Error::Truncated));
|
||||
assert_eq!(
|
||||
socket.send_slice(&[0xff; 67], REMOTE_IPV4.into()),
|
||||
Err(Error::Truncated)
|
||||
);
|
||||
assert!(socket.can_send());
|
||||
|
||||
let mut bytes = [0xff; 24];
|
||||
let mut packet = Icmpv4Packet::new_unchecked(&mut bytes);
|
||||
ECHOV4_REPR.emit(&mut packet, &checksum);
|
||||
|
||||
assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(()));
|
||||
assert_eq!(socket.send_slice(b"123456", REMOTE_IPV4.into()), Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
socket.send_slice(b"123456", REMOTE_IPV4.into()),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(ip_repr, LOCAL_IPV4_REPR);
|
||||
assert_eq!(icmp_repr, ECHOV4_REPR.into());
|
||||
Err(Error::Unaddressable)
|
||||
}), Err(Error::Unaddressable));
|
||||
}),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
// buffer is not taken off of the tx queue due to the error
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(ip_repr, LOCAL_IPV4_REPR);
|
||||
assert_eq!(icmp_repr, ECHOV4_REPR.into());
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
}),
|
||||
Ok(())
|
||||
);
|
||||
// buffer is taken off of the queue this time
|
||||
assert!(socket.can_send());
|
||||
}
|
||||
|
@ -573,17 +661,26 @@ mod test_ipv4 {
|
|||
|
||||
s.set_hop_limit(Some(0x2a));
|
||||
|
||||
assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), Ok(()));
|
||||
assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
|
||||
assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr {
|
||||
assert_eq!(
|
||||
s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
|
||||
assert_eq!(
|
||||
ip_repr,
|
||||
IpRepr::Ipv4(Ipv4Repr {
|
||||
src_addr: Ipv4Address::UNSPECIFIED,
|
||||
dst_addr: REMOTE_IPV4,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: ECHOV4_REPR.buffer_len(),
|
||||
hop_limit: 0x2a,
|
||||
}));
|
||||
})
|
||||
);
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
}),
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -602,13 +699,17 @@ mod test_ipv4 {
|
|||
let data = &packet.into_inner()[..];
|
||||
|
||||
assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_recv());
|
||||
|
||||
assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.process(&Context::DUMMY, &REMOTE_IPV4_REPR, &ECHOV4_REPR.into()),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
|
||||
assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV4.into())));
|
||||
assert!(!socket.can_recv());
|
||||
|
@ -625,7 +726,7 @@ mod test_ipv4 {
|
|||
let icmp_repr = Icmpv4Repr::EchoRequest {
|
||||
ident: 0x4321,
|
||||
seq_no: 0x5678,
|
||||
data: &[0xff; 16]
|
||||
data: &[0xff; 16],
|
||||
};
|
||||
icmp_repr.emit(&mut packet, &checksum);
|
||||
|
||||
|
@ -649,7 +750,8 @@ mod test_ipv4 {
|
|||
&LOCAL_IPV4.into(),
|
||||
UDP_PAYLOAD.len(),
|
||||
|buf| buf.copy_from_slice(UDP_PAYLOAD),
|
||||
&checksum);
|
||||
&checksum,
|
||||
);
|
||||
|
||||
let data = &packet.into_inner()[..];
|
||||
|
||||
|
@ -660,16 +762,16 @@ mod test_ipv4 {
|
|||
dst_addr: REMOTE_IPV4,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: 12,
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
},
|
||||
data: data
|
||||
data: data,
|
||||
};
|
||||
let ip_repr = IpRepr::Unspecified {
|
||||
src_addr: REMOTE_IPV4.into(),
|
||||
dst_addr: LOCAL_IPV4.into(),
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: icmp_repr.buffer_len(),
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
};
|
||||
|
||||
assert!(!socket.can_recv());
|
||||
|
@ -677,14 +779,19 @@ mod test_ipv4 {
|
|||
// Ensure we can accept ICMP error response to the bound
|
||||
// UDP port
|
||||
assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into()));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_recv());
|
||||
|
||||
let mut bytes = [0x00; 46];
|
||||
let mut packet = Icmpv4Packet::new_unchecked(&mut bytes[..]);
|
||||
icmp_repr.emit(&mut packet, &checksum);
|
||||
assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV4.into())));
|
||||
assert_eq!(
|
||||
socket.recv(),
|
||||
Ok((&packet.into_inner()[..], REMOTE_IPV4.into()))
|
||||
);
|
||||
assert!(!socket.can_recv());
|
||||
}
|
||||
}
|
||||
|
@ -695,15 +802,18 @@ mod test_ipv6 {
|
|||
|
||||
use crate::wire::Icmpv6DstUnreachable;
|
||||
|
||||
const REMOTE_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
const LOCAL_IPV6: Ipv6Address = Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 2]);
|
||||
const LOCAL_END_V6: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv6(LOCAL_IPV6), port: LOCAL_PORT };
|
||||
const REMOTE_IPV6: Ipv6Address =
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
const LOCAL_IPV6: Ipv6Address =
|
||||
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]);
|
||||
const LOCAL_END_V6: IpEndpoint = IpEndpoint {
|
||||
addr: IpAddress::Ipv6(LOCAL_IPV6),
|
||||
port: LOCAL_PORT,
|
||||
};
|
||||
static ECHOV6_REPR: Icmpv6Repr = Icmpv6Repr::EchoRequest {
|
||||
ident: 0x1234,
|
||||
seq_no: 0x5678,
|
||||
data: &[0xff; 16]
|
||||
data: &[0xff; 16],
|
||||
};
|
||||
|
||||
static LOCAL_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
|
||||
|
@ -711,7 +821,7 @@ mod test_ipv6 {
|
|||
dst_addr: REMOTE_IPV6,
|
||||
next_header: IpProtocol::Icmpv6,
|
||||
payload_len: 24,
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
});
|
||||
|
||||
static REMOTE_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
|
||||
|
@ -719,14 +829,16 @@ mod test_ipv6 {
|
|||
dst_addr: LOCAL_IPV6,
|
||||
next_header: IpProtocol::Icmpv6,
|
||||
payload_len: 24,
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
});
|
||||
|
||||
#[test]
|
||||
fn test_send_unaddressable() {
|
||||
let mut socket = socket(buffer(0), buffer(1));
|
||||
assert_eq!(socket.send_slice(b"abcdef", IpAddress::default()),
|
||||
Err(Error::Unaddressable));
|
||||
assert_eq!(
|
||||
socket.send_slice(b"abcdef", IpAddress::default()),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV6.into()), Ok(()));
|
||||
}
|
||||
|
||||
|
@ -735,34 +847,56 @@ mod test_ipv6 {
|
|||
let mut socket = socket(buffer(0), buffer(1));
|
||||
let checksum = ChecksumCapabilities::default();
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
|
||||
// This buffer is too long
|
||||
assert_eq!(socket.send_slice(&[0xff; 67], REMOTE_IPV6.into()), Err(Error::Truncated));
|
||||
assert_eq!(
|
||||
socket.send_slice(&[0xff; 67], REMOTE_IPV6.into()),
|
||||
Err(Error::Truncated)
|
||||
);
|
||||
assert!(socket.can_send());
|
||||
|
||||
let mut bytes = vec![0xff; 24];
|
||||
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
|
||||
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
|
||||
ECHOV6_REPR.emit(
|
||||
&LOCAL_IPV6.into(),
|
||||
&REMOTE_IPV6.into(),
|
||||
&mut packet,
|
||||
&checksum,
|
||||
);
|
||||
|
||||
assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()));
|
||||
assert_eq!(socket.send_slice(b"123456", REMOTE_IPV6.into()), Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
socket.send_slice(b"123456", REMOTE_IPV6.into()),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(ip_repr, LOCAL_IPV6_REPR);
|
||||
assert_eq!(icmp_repr, ECHOV6_REPR.into());
|
||||
Err(Error::Unaddressable)
|
||||
}), Err(Error::Unaddressable));
|
||||
}),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
// buffer is not taken off of the tx queue due to the error
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(ip_repr, LOCAL_IPV6_REPR);
|
||||
assert_eq!(icmp_repr, ECHOV6_REPR.into());
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
}),
|
||||
Ok(())
|
||||
);
|
||||
// buffer is taken off of the queue this time
|
||||
assert!(socket.can_send());
|
||||
}
|
||||
|
@ -774,21 +908,35 @@ mod test_ipv6 {
|
|||
|
||||
let mut bytes = vec![0xff; 24];
|
||||
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
|
||||
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
|
||||
ECHOV6_REPR.emit(
|
||||
&LOCAL_IPV6.into(),
|
||||
&REMOTE_IPV6.into(),
|
||||
&mut packet,
|
||||
&checksum,
|
||||
);
|
||||
|
||||
s.set_hop_limit(Some(0x2a));
|
||||
|
||||
assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()));
|
||||
assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
|
||||
assert_eq!(ip_repr, IpRepr::Ipv6(Ipv6Repr {
|
||||
assert_eq!(
|
||||
s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(
|
||||
s.dispatch(&Context::DUMMY, |(ip_repr, _)| {
|
||||
assert_eq!(
|
||||
ip_repr,
|
||||
IpRepr::Ipv6(Ipv6Repr {
|
||||
src_addr: Ipv6Address::UNSPECIFIED,
|
||||
dst_addr: REMOTE_IPV6,
|
||||
next_header: IpProtocol::Icmpv6,
|
||||
payload_len: ECHOV6_REPR.buffer_len(),
|
||||
hop_limit: 0x2a,
|
||||
}));
|
||||
})
|
||||
);
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
}),
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -803,17 +951,26 @@ mod test_ipv6 {
|
|||
|
||||
let mut bytes = [0xff; 24];
|
||||
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes);
|
||||
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
|
||||
ECHOV6_REPR.emit(
|
||||
&LOCAL_IPV6.into(),
|
||||
&REMOTE_IPV6.into(),
|
||||
&mut packet,
|
||||
&checksum,
|
||||
);
|
||||
let data = &packet.into_inner()[..];
|
||||
|
||||
assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_recv());
|
||||
|
||||
assert!(socket.accepts(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.process(&Context::DUMMY, &REMOTE_IPV6_REPR, &ECHOV6_REPR.into()),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
|
||||
assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV6.into())));
|
||||
assert!(!socket.can_recv());
|
||||
|
@ -830,9 +987,14 @@ mod test_ipv6 {
|
|||
let icmp_repr = Icmpv6Repr::EchoRequest {
|
||||
ident: 0x4321,
|
||||
seq_no: 0x5678,
|
||||
data: &[0xff; 16]
|
||||
data: &[0xff; 16],
|
||||
};
|
||||
icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
|
||||
icmp_repr.emit(
|
||||
&LOCAL_IPV6.into(),
|
||||
&REMOTE_IPV6.into(),
|
||||
&mut packet,
|
||||
&checksum,
|
||||
);
|
||||
|
||||
// Ensure that a packet with an identifier that isn't the bound
|
||||
// ID is not accepted
|
||||
|
@ -854,7 +1016,8 @@ mod test_ipv6 {
|
|||
&LOCAL_IPV6.into(),
|
||||
UDP_PAYLOAD.len(),
|
||||
|buf| buf.copy_from_slice(UDP_PAYLOAD),
|
||||
&checksum);
|
||||
&checksum,
|
||||
);
|
||||
|
||||
let data = &packet.into_inner()[..];
|
||||
|
||||
|
@ -865,16 +1028,16 @@ mod test_ipv6 {
|
|||
dst_addr: REMOTE_IPV6,
|
||||
next_header: IpProtocol::Icmpv6,
|
||||
payload_len: 12,
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
},
|
||||
data: data
|
||||
data: data,
|
||||
};
|
||||
let ip_repr = IpRepr::Unspecified {
|
||||
src_addr: REMOTE_IPV6.into(),
|
||||
dst_addr: LOCAL_IPV6.into(),
|
||||
protocol: IpProtocol::Icmpv6,
|
||||
payload_len: icmp_repr.buffer_len(),
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
};
|
||||
|
||||
assert!(!socket.can_recv());
|
||||
|
@ -882,14 +1045,24 @@ mod test_ipv6 {
|
|||
// Ensure we can accept ICMP error response to the bound
|
||||
// UDP port
|
||||
assert!(socket.accepts(&Context::DUMMY, &ip_repr, &icmp_repr.into()));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(&Context::DUMMY, &ip_repr, &icmp_repr.into()),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_recv());
|
||||
|
||||
let mut bytes = [0x00; 66];
|
||||
let mut packet = Icmpv6Packet::new_unchecked(&mut bytes[..]);
|
||||
icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &checksum);
|
||||
assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV6.into())));
|
||||
icmp_repr.emit(
|
||||
&LOCAL_IPV6.into(),
|
||||
&REMOTE_IPV6.into(),
|
||||
&mut packet,
|
||||
&checksum,
|
||||
);
|
||||
assert_eq!(
|
||||
socket.recv(),
|
||||
Ok((&packet.into_inner()[..], REMOTE_IPV6.into()))
|
||||
);
|
||||
assert!(!socket.can_recv());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::wire::IpAddress;
|
||||
use crate::socket::{SocketHandle, PollAt};
|
||||
use crate::socket::{PollAt, SocketHandle};
|
||||
use crate::time::{Duration, Instant};
|
||||
use crate::wire::IpAddress;
|
||||
|
||||
/// Neighbor dependency.
|
||||
///
|
||||
|
@ -16,7 +16,7 @@ enum NeighborState {
|
|||
Waiting {
|
||||
neighbor: IpAddress,
|
||||
silent_until: Instant,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for NeighborState {
|
||||
|
@ -47,33 +47,40 @@ impl Meta {
|
|||
pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration { millis: 3_000 };
|
||||
|
||||
pub(crate) fn poll_at<F>(&self, socket_poll_at: PollAt, has_neighbor: F) -> PollAt
|
||||
where F: Fn(IpAddress) -> bool
|
||||
where
|
||||
F: Fn(IpAddress) -> bool,
|
||||
{
|
||||
match self.neighbor_state {
|
||||
NeighborState::Active =>
|
||||
socket_poll_at,
|
||||
NeighborState::Waiting { neighbor, .. }
|
||||
if has_neighbor(neighbor) =>
|
||||
socket_poll_at,
|
||||
NeighborState::Waiting { silent_until, .. } =>
|
||||
PollAt::Time(silent_until)
|
||||
NeighborState::Active => socket_poll_at,
|
||||
NeighborState::Waiting { neighbor, .. } if has_neighbor(neighbor) => socket_poll_at,
|
||||
NeighborState::Waiting { silent_until, .. } => PollAt::Time(silent_until),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn egress_permitted<F>(&mut self, timestamp: Instant, has_neighbor: F) -> bool
|
||||
where F: Fn(IpAddress) -> bool
|
||||
where
|
||||
F: Fn(IpAddress) -> bool,
|
||||
{
|
||||
match self.neighbor_state {
|
||||
NeighborState::Active =>
|
||||
true,
|
||||
NeighborState::Waiting { neighbor, silent_until } => {
|
||||
NeighborState::Active => true,
|
||||
NeighborState::Waiting {
|
||||
neighbor,
|
||||
silent_until,
|
||||
} => {
|
||||
if has_neighbor(neighbor) {
|
||||
net_trace!("{}: neighbor {} discovered, unsilencing",
|
||||
self.handle, neighbor);
|
||||
net_trace!(
|
||||
"{}: neighbor {} discovered, unsilencing",
|
||||
self.handle,
|
||||
neighbor
|
||||
);
|
||||
self.neighbor_state = NeighborState::Active;
|
||||
true
|
||||
} else if timestamp >= silent_until {
|
||||
net_trace!("{}: neighbor {} silence timer expired, rediscovering", self.handle, neighbor);
|
||||
net_trace!(
|
||||
"{}: neighbor {} silence timer expired, rediscovering",
|
||||
self.handle,
|
||||
neighbor
|
||||
);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
@ -83,10 +90,15 @@ impl Meta {
|
|||
}
|
||||
|
||||
pub(crate) fn neighbor_missing(&mut self, timestamp: Instant, neighbor: IpAddress) {
|
||||
net_trace!("{}: neighbor {} missing, silencing until t+{}",
|
||||
self.handle, neighbor, Self::DISCOVERY_SILENT_TIME);
|
||||
net_trace!(
|
||||
"{}: neighbor {} missing, silencing until t+{}",
|
||||
self.handle,
|
||||
neighbor,
|
||||
Self::DISCOVERY_SILENT_TIME
|
||||
);
|
||||
self.neighbor_state = NeighborState::Waiting {
|
||||
neighbor, silent_until: timestamp + Self::DISCOVERY_SILENT_TIME
|
||||
neighbor,
|
||||
silent_until: timestamp + Self::DISCOVERY_SILENT_TIME,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,19 +14,22 @@ size for a buffer, allocate it, and let the networking stack use it.
|
|||
use crate::phy::DeviceCapabilities;
|
||||
use crate::time::Instant;
|
||||
|
||||
#[cfg(feature = "socket-dhcpv4")]
|
||||
mod dhcpv4;
|
||||
#[cfg(all(
|
||||
feature = "socket-icmp",
|
||||
any(feature = "proto-ipv4", feature = "proto-ipv6")
|
||||
))]
|
||||
mod icmp;
|
||||
mod meta;
|
||||
#[cfg(feature = "socket-raw")]
|
||||
mod raw;
|
||||
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
|
||||
mod icmp;
|
||||
#[cfg(feature = "socket-udp")]
|
||||
mod udp;
|
||||
mod ref_;
|
||||
mod set;
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
mod tcp;
|
||||
#[cfg(feature = "socket-dhcpv4")]
|
||||
mod dhcpv4;
|
||||
mod set;
|
||||
mod ref_;
|
||||
#[cfg(feature = "socket-udp")]
|
||||
mod udp;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
mod waker;
|
||||
|
@ -36,30 +39,24 @@ pub(crate) use self::meta::Meta as SocketMeta;
|
|||
pub(crate) use self::waker::WakerRegistration;
|
||||
|
||||
#[cfg(feature = "socket-raw")]
|
||||
pub use self::raw::{RawPacketMetadata,
|
||||
RawSocketBuffer,
|
||||
RawSocket};
|
||||
pub use self::raw::{RawPacketMetadata, RawSocket, RawSocketBuffer};
|
||||
|
||||
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
|
||||
pub use self::icmp::{IcmpPacketMetadata,
|
||||
IcmpSocketBuffer,
|
||||
Endpoint as IcmpEndpoint,
|
||||
IcmpSocket};
|
||||
#[cfg(all(
|
||||
feature = "socket-icmp",
|
||||
any(feature = "proto-ipv4", feature = "proto-ipv6")
|
||||
))]
|
||||
pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer};
|
||||
|
||||
#[cfg(feature = "socket-udp")]
|
||||
pub use self::udp::{UdpPacketMetadata,
|
||||
UdpSocketBuffer,
|
||||
UdpSocket};
|
||||
pub use self::udp::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
|
||||
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
pub use self::tcp::{SocketBuffer as TcpSocketBuffer,
|
||||
State as TcpState,
|
||||
TcpSocket};
|
||||
pub use self::tcp::{SocketBuffer as TcpSocketBuffer, State as TcpState, TcpSocket};
|
||||
|
||||
#[cfg(feature = "socket-dhcpv4")]
|
||||
pub use self::dhcpv4::{Dhcpv4Socket, Config as Dhcpv4Config, Event as Dhcpv4Event};
|
||||
pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event};
|
||||
|
||||
pub use self::set::{Set as SocketSet, Item as SocketSetItem, Handle as SocketHandle};
|
||||
pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet};
|
||||
pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut};
|
||||
|
||||
pub use self::ref_::Ref as SocketRef;
|
||||
|
@ -91,7 +88,10 @@ pub(crate) enum PollAt {
|
|||
pub enum Socket<'a> {
|
||||
#[cfg(feature = "socket-raw")]
|
||||
Raw(RawSocket<'a>),
|
||||
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
|
||||
#[cfg(all(
|
||||
feature = "socket-icmp",
|
||||
any(feature = "proto-ipv4", feature = "proto-ipv6")
|
||||
))]
|
||||
Icmp(IcmpSocket<'a>),
|
||||
#[cfg(feature = "socket-udp")]
|
||||
Udp(UdpSocket<'a>),
|
||||
|
@ -152,15 +152,13 @@ impl<'a> SocketSession for Socket<'a> {
|
|||
|
||||
/// A conversion trait for network sockets.
|
||||
pub trait AnySocket<'a>: SocketSession + Sized {
|
||||
fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) ->
|
||||
Option<SocketRef<'c, Self>>;
|
||||
fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) -> Option<SocketRef<'c, Self>>;
|
||||
}
|
||||
|
||||
macro_rules! from_socket {
|
||||
($socket:ty, $variant:ident) => {
|
||||
impl<'a> AnySocket<'a> for $socket {
|
||||
fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) ->
|
||||
Option<SocketRef<'c, Self>> {
|
||||
fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) -> Option<SocketRef<'c, Self>> {
|
||||
if let Socket::$variant(ref mut socket) = SocketRef::into_inner(ref_) {
|
||||
Some(SocketRef::new(socket))
|
||||
} else {
|
||||
|
@ -168,12 +166,15 @@ macro_rules! from_socket {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "socket-raw")]
|
||||
from_socket!(RawSocket<'a>, Raw);
|
||||
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
|
||||
#[cfg(all(
|
||||
feature = "socket-icmp",
|
||||
any(feature = "proto-ipv4", feature = "proto-ipv6")
|
||||
))]
|
||||
from_socket!(IcmpSocket<'a>, Icmp);
|
||||
#[cfg(feature = "socket-udp")]
|
||||
from_socket!(UdpSocket<'a>, Udp);
|
||||
|
@ -193,14 +194,13 @@ pub(crate) struct Context {
|
|||
|
||||
#[cfg(test)]
|
||||
impl Context {
|
||||
|
||||
pub(crate) const DUMMY: Context = Context {
|
||||
caps: DeviceCapabilities {
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
medium: crate::phy::Medium::Ethernet,
|
||||
#[cfg(not(feature = "medium-ethernet"))]
|
||||
medium: crate::phy::Medium::Ip,
|
||||
checksum: crate::phy::ChecksumCapabilities{
|
||||
checksum: crate::phy::ChecksumCapabilities {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
icmpv4: crate::phy::Checksum::Both,
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
|
@ -216,7 +216,6 @@ impl Context {
|
|||
max_transmission_unit: 1500,
|
||||
},
|
||||
ethernet_address: None,
|
||||
now: Instant{millis: 0},
|
||||
now: Instant { millis: 0 },
|
||||
};
|
||||
|
||||
}
|
|
@ -2,18 +2,18 @@ use core::cmp::min;
|
|||
#[cfg(feature = "async")]
|
||||
use core::task::Waker;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
|
||||
use crate::storage::{PacketBuffer, PacketMetadata};
|
||||
#[cfg(feature = "async")]
|
||||
use crate::socket::WakerRegistration;
|
||||
use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta};
|
||||
use crate::storage::{PacketBuffer, PacketMetadata};
|
||||
use crate::{Error, Result};
|
||||
|
||||
use crate::wire::{IpVersion, IpRepr, IpProtocol};
|
||||
use crate::wire::{IpProtocol, IpRepr, IpVersion};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::{Ipv4Repr, Ipv4Packet};
|
||||
use crate::wire::{Ipv4Packet, Ipv4Repr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use crate::wire::{Ipv6Repr, Ipv6Packet};
|
||||
use crate::wire::{Ipv6Packet, Ipv6Repr};
|
||||
|
||||
/// A UDP packet metadata.
|
||||
pub type RawPacketMetadata = PacketMetadata<()>;
|
||||
|
@ -41,9 +41,12 @@ pub struct RawSocket<'a> {
|
|||
impl<'a> RawSocket<'a> {
|
||||
/// Create a raw IP socket bound to the given IP version and datagram protocol,
|
||||
/// with the given buffers.
|
||||
pub fn new(ip_version: IpVersion, ip_protocol: IpProtocol,
|
||||
pub fn new(
|
||||
ip_version: IpVersion,
|
||||
ip_protocol: IpProtocol,
|
||||
rx_buffer: RawSocketBuffer<'a>,
|
||||
tx_buffer: RawSocketBuffer<'a>) -> RawSocket<'a> {
|
||||
tx_buffer: RawSocketBuffer<'a>,
|
||||
) -> RawSocket<'a> {
|
||||
RawSocket {
|
||||
meta: SocketMeta::default(),
|
||||
ip_version,
|
||||
|
@ -160,9 +163,13 @@ impl<'a> RawSocket<'a> {
|
|||
pub fn send(&mut self, size: usize) -> Result<&mut [u8]> {
|
||||
let packet_buf = self.tx_buffer.enqueue(size, ())?;
|
||||
|
||||
net_trace!("{}:{}:{}: buffer to send {} octets",
|
||||
self.meta.handle, self.ip_version, self.ip_protocol,
|
||||
packet_buf.len());
|
||||
net_trace!(
|
||||
"{}:{}:{}: buffer to send {} octets",
|
||||
self.meta.handle,
|
||||
self.ip_version,
|
||||
self.ip_protocol,
|
||||
packet_buf.len()
|
||||
);
|
||||
Ok(packet_buf)
|
||||
}
|
||||
|
||||
|
@ -183,9 +190,13 @@ impl<'a> RawSocket<'a> {
|
|||
pub fn recv(&mut self) -> Result<&[u8]> {
|
||||
let ((), packet_buf) = self.rx_buffer.dequeue()?;
|
||||
|
||||
net_trace!("{}:{}:{}: receive {} buffered octets",
|
||||
self.meta.handle, self.ip_version, self.ip_protocol,
|
||||
packet_buf.len());
|
||||
net_trace!(
|
||||
"{}:{}:{}: receive {} buffered octets",
|
||||
self.meta.handle,
|
||||
self.ip_version,
|
||||
self.ip_protocol,
|
||||
packet_buf.len()
|
||||
);
|
||||
Ok(packet_buf)
|
||||
}
|
||||
|
||||
|
@ -200,8 +211,12 @@ impl<'a> RawSocket<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn accepts(&self, ip_repr: &IpRepr) -> bool {
|
||||
if ip_repr.version() != self.ip_version { return false }
|
||||
if ip_repr.protocol() != self.ip_protocol { return false }
|
||||
if ip_repr.version() != self.ip_version {
|
||||
return false;
|
||||
}
|
||||
if ip_repr.protocol() != self.ip_protocol {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
@ -215,9 +230,13 @@ impl<'a> RawSocket<'a> {
|
|||
ip_repr.emit(&mut packet_buf[..header_len], &cx.caps.checksum);
|
||||
packet_buf[header_len..].copy_from_slice(payload);
|
||||
|
||||
net_trace!("{}:{}:{}: receiving {} octets",
|
||||
self.meta.handle, self.ip_version, self.ip_protocol,
|
||||
packet_buf.len());
|
||||
net_trace!(
|
||||
"{}:{}:{}: receiving {} octets",
|
||||
self.meta.handle,
|
||||
self.ip_version,
|
||||
self.ip_protocol,
|
||||
packet_buf.len()
|
||||
);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
self.rx_waker.wake();
|
||||
|
@ -226,14 +245,21 @@ impl<'a> RawSocket<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
|
||||
where F: FnOnce((IpRepr, &[u8])) -> Result<()> {
|
||||
fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8],
|
||||
_checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> {
|
||||
where
|
||||
F: FnOnce((IpRepr, &[u8])) -> Result<()>,
|
||||
{
|
||||
fn prepare<'a>(
|
||||
protocol: IpProtocol,
|
||||
buffer: &'a mut [u8],
|
||||
_checksum_caps: &ChecksumCapabilities,
|
||||
) -> Result<(IpRepr, &'a [u8])> {
|
||||
match IpVersion::of_packet(buffer)? {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
IpVersion::Ipv4 => {
|
||||
let mut packet = Ipv4Packet::new_checked(buffer)?;
|
||||
if packet.protocol() != protocol { return Err(Error::Unaddressable) }
|
||||
if packet.protocol() != protocol {
|
||||
return Err(Error::Unaddressable);
|
||||
}
|
||||
if _checksum_caps.ipv4.tx() {
|
||||
packet.fill_checksum();
|
||||
} else {
|
||||
|
@ -249,7 +275,9 @@ impl<'a> RawSocket<'a> {
|
|||
#[cfg(feature = "proto-ipv6")]
|
||||
IpVersion::Ipv6 => {
|
||||
let packet = Ipv6Packet::new_checked(buffer)?;
|
||||
if packet.next_header() != protocol { return Err(Error::Unaddressable) }
|
||||
if packet.next_header() != protocol {
|
||||
return Err(Error::Unaddressable);
|
||||
}
|
||||
let packet = Ipv6Packet::new_unchecked(&*packet.into_inner());
|
||||
let ipv6_repr = Ipv6Repr::parse(&packet)?;
|
||||
Ok((IpRepr::Ipv6(ipv6_repr), packet.payload()))
|
||||
|
@ -264,15 +292,23 @@ impl<'a> RawSocket<'a> {
|
|||
self.tx_buffer.dequeue_with(|&mut (), packet_buf| {
|
||||
match prepare(ip_protocol, packet_buf, &cx.caps.checksum) {
|
||||
Ok((ip_repr, raw_packet)) => {
|
||||
net_trace!("{}:{}:{}: sending {} octets",
|
||||
handle, ip_version, ip_protocol,
|
||||
ip_repr.buffer_len() + raw_packet.len());
|
||||
net_trace!(
|
||||
"{}:{}:{}: sending {} octets",
|
||||
handle,
|
||||
ip_version,
|
||||
ip_protocol,
|
||||
ip_repr.buffer_len() + raw_packet.len()
|
||||
);
|
||||
emit((ip_repr, raw_packet))
|
||||
}
|
||||
Err(error) => {
|
||||
net_debug!("{}:{}:{}: dropping outgoing packet ({})",
|
||||
handle, ip_version, ip_protocol,
|
||||
error);
|
||||
net_debug!(
|
||||
"{}:{}:{}: dropping outgoing packet ({})",
|
||||
handle,
|
||||
ip_version,
|
||||
ip_protocol,
|
||||
error
|
||||
);
|
||||
// Return Ok(()) so the packet is dequeued.
|
||||
Ok(())
|
||||
}
|
||||
|
@ -302,26 +338,34 @@ impl<'a> Into<Socket<'a>> for RawSocket<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::wire::IpRepr;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::{Ipv4Address, Ipv4Repr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use crate::wire::{Ipv6Address, Ipv6Repr};
|
||||
use super::*;
|
||||
|
||||
fn buffer(packets: usize) -> RawSocketBuffer<'static> {
|
||||
RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets])
|
||||
RawSocketBuffer::new(
|
||||
vec![RawPacketMetadata::EMPTY; packets],
|
||||
vec![0; 48 * packets],
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
mod ipv4_locals {
|
||||
use super::*;
|
||||
|
||||
pub fn socket(rx_buffer: RawSocketBuffer<'static>,
|
||||
tx_buffer: RawSocketBuffer<'static>)
|
||||
-> RawSocket<'static> {
|
||||
RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO),
|
||||
rx_buffer, tx_buffer)
|
||||
pub fn socket(
|
||||
rx_buffer: RawSocketBuffer<'static>,
|
||||
tx_buffer: RawSocketBuffer<'static>,
|
||||
) -> RawSocket<'static> {
|
||||
RawSocket::new(
|
||||
IpVersion::Ipv4,
|
||||
IpProtocol::Unknown(IP_PROTO),
|
||||
rx_buffer,
|
||||
tx_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
pub const IP_PROTO: u8 = 63;
|
||||
|
@ -330,60 +374,54 @@ mod test {
|
|||
dst_addr: Ipv4Address([10, 0, 0, 2]),
|
||||
protocol: IpProtocol::Unknown(IP_PROTO),
|
||||
payload_len: 4,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
});
|
||||
pub const PACKET_BYTES: [u8; 24] = [
|
||||
0x45, 0x00, 0x00, 0x18,
|
||||
0x00, 0x00, 0x40, 0x00,
|
||||
0x40, 0x3f, 0x00, 0x00,
|
||||
0x0a, 0x00, 0x00, 0x01,
|
||||
0x0a, 0x00, 0x00, 0x02,
|
||||
0xaa, 0x00, 0x00, 0xff
|
||||
];
|
||||
pub const PACKET_PAYLOAD: [u8; 4] = [
|
||||
0xaa, 0x00, 0x00, 0xff
|
||||
0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x3f, 0x00, 0x00, 0x0a, 0x00,
|
||||
0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ipv6_locals {
|
||||
use super::*;
|
||||
|
||||
pub fn socket(rx_buffer: RawSocketBuffer<'static>,
|
||||
tx_buffer: RawSocketBuffer<'static>)
|
||||
-> RawSocket<'static> {
|
||||
RawSocket::new(IpVersion::Ipv6, IpProtocol::Unknown(IP_PROTO),
|
||||
rx_buffer, tx_buffer)
|
||||
pub fn socket(
|
||||
rx_buffer: RawSocketBuffer<'static>,
|
||||
tx_buffer: RawSocketBuffer<'static>,
|
||||
) -> RawSocket<'static> {
|
||||
RawSocket::new(
|
||||
IpVersion::Ipv6,
|
||||
IpProtocol::Unknown(IP_PROTO),
|
||||
rx_buffer,
|
||||
tx_buffer,
|
||||
)
|
||||
}
|
||||
|
||||
pub const IP_PROTO: u8 = 63;
|
||||
pub const HEADER_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
|
||||
src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]),
|
||||
dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]),
|
||||
src_addr: Ipv6Address([
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
]),
|
||||
dst_addr: Ipv6Address([
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x02,
|
||||
]),
|
||||
next_header: IpProtocol::Unknown(IP_PROTO),
|
||||
payload_len: 4,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
});
|
||||
|
||||
pub const PACKET_BYTES: [u8; 44] = [
|
||||
0x60, 0x00, 0x00, 0x00,
|
||||
0x00, 0x04, 0x3f, 0x40,
|
||||
0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
0xaa, 0x00, 0x00, 0xff
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3f, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xaa, 0x00,
|
||||
0x00, 0xff,
|
||||
];
|
||||
|
||||
pub const PACKET_PAYLOAD: [u8; 4] = [
|
||||
0xaa, 0x00, 0x00, 0xff
|
||||
];
|
||||
pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
|
||||
}
|
||||
|
||||
macro_rules! reusable_ip_specific_tests {
|
||||
|
@ -402,25 +440,33 @@ mod test {
|
|||
let mut socket = $socket(buffer(0), buffer(1));
|
||||
|
||||
assert!(socket.can_send());
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
|
||||
assert_eq!(socket.send_slice(&$packet[..]), Ok(()));
|
||||
assert_eq!(socket.send_slice(b""), Err(Error::Exhausted));
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
|
||||
assert_eq!(ip_repr, $hdr);
|
||||
assert_eq!(ip_payload, &$payload);
|
||||
Err(Error::Unaddressable)
|
||||
}), Err(Error::Unaddressable));
|
||||
}),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
|
||||
assert_eq!(ip_repr, $hdr);
|
||||
assert_eq!(ip_payload, &$payload);
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
}),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_send());
|
||||
}
|
||||
|
||||
|
@ -444,20 +490,32 @@ mod test {
|
|||
buffer[..$packet.len()].copy_from_slice(&$packet[..]);
|
||||
|
||||
assert!(socket.accepts(&$hdr));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &$hdr, &buffer), Err(Error::Truncated));
|
||||
}
|
||||
assert_eq!(
|
||||
socket.process(&Context::DUMMY, &$hdr, &buffer),
|
||||
Err(Error::Truncated)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
reusable_ip_specific_tests!(ipv4, ipv4_locals::socket, ipv4_locals::HEADER_REPR,
|
||||
ipv4_locals::PACKET_BYTES, ipv4_locals::PACKET_PAYLOAD);
|
||||
reusable_ip_specific_tests!(
|
||||
ipv4,
|
||||
ipv4_locals::socket,
|
||||
ipv4_locals::HEADER_REPR,
|
||||
ipv4_locals::PACKET_BYTES,
|
||||
ipv4_locals::PACKET_PAYLOAD
|
||||
);
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
reusable_ip_specific_tests!(ipv6, ipv6_locals::socket, ipv6_locals::HEADER_REPR,
|
||||
ipv6_locals::PACKET_BYTES, ipv6_locals::PACKET_PAYLOAD);
|
||||
|
||||
reusable_ip_specific_tests!(
|
||||
ipv6,
|
||||
ipv6_locals::socket,
|
||||
ipv6_locals::HEADER_REPR,
|
||||
ipv6_locals::PACKET_BYTES,
|
||||
ipv6_locals::PACKET_PAYLOAD
|
||||
);
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
|
@ -470,15 +528,13 @@ mod test {
|
|||
Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6);
|
||||
|
||||
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Ok(()));
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
|
||||
|
||||
let mut wrong_protocol = ipv4_locals::PACKET_BYTES;
|
||||
Ipv4Packet::new_unchecked(&mut wrong_protocol).set_protocol(IpProtocol::Tcp);
|
||||
|
||||
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Ok(()));
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
|
||||
}
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
{
|
||||
|
@ -488,15 +544,13 @@ mod test {
|
|||
Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4);
|
||||
|
||||
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Ok(()));
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
|
||||
|
||||
let mut wrong_protocol = ipv6_locals::PACKET_BYTES;
|
||||
Ipv6Packet::new_unchecked(&mut wrong_protocol[..]).set_next_header(IpProtocol::Tcp);
|
||||
|
||||
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Ok(()));
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -512,13 +566,25 @@ mod test {
|
|||
|
||||
assert_eq!(socket.recv(), Err(Error::Exhausted));
|
||||
assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&ipv4_locals::HEADER_REPR,
|
||||
&ipv4_locals::PACKET_PAYLOAD
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_recv());
|
||||
|
||||
assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&ipv4_locals::HEADER_REPR,
|
||||
&ipv4_locals::PACKET_PAYLOAD
|
||||
),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
assert_eq!(socket.recv(), Ok(&cksumd_packet[..]));
|
||||
assert!(!socket.can_recv());
|
||||
}
|
||||
|
@ -529,13 +595,25 @@ mod test {
|
|||
|
||||
assert_eq!(socket.recv(), Err(Error::Exhausted));
|
||||
assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&ipv6_locals::HEADER_REPR,
|
||||
&ipv6_locals::PACKET_PAYLOAD
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_recv());
|
||||
|
||||
assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&ipv6_locals::HEADER_REPR,
|
||||
&ipv6_locals::PACKET_PAYLOAD
|
||||
),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
assert_eq!(socket.recv(), Ok(&ipv6_locals::PACKET_BYTES[..]));
|
||||
assert!(!socket.can_recv());
|
||||
}
|
||||
|
@ -545,16 +623,24 @@ mod test {
|
|||
fn test_doesnt_accept_wrong_proto() {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
{
|
||||
let socket = RawSocket::new(IpVersion::Ipv4,
|
||||
IpProtocol::Unknown(ipv4_locals::IP_PROTO+1), buffer(1), buffer(1));
|
||||
let socket = RawSocket::new(
|
||||
IpVersion::Ipv4,
|
||||
IpProtocol::Unknown(ipv4_locals::IP_PROTO + 1),
|
||||
buffer(1),
|
||||
buffer(1),
|
||||
);
|
||||
assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
assert!(!socket.accepts(&ipv6_locals::HEADER_REPR));
|
||||
}
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
{
|
||||
let socket = RawSocket::new(IpVersion::Ipv6,
|
||||
IpProtocol::Unknown(ipv6_locals::IP_PROTO+1), buffer(1), buffer(1));
|
||||
let socket = RawSocket::new(
|
||||
IpVersion::Ipv6,
|
||||
IpProtocol::Unknown(ipv6_locals::IP_PROTO + 1),
|
||||
buffer(1),
|
||||
buffer(1),
|
||||
);
|
||||
assert!(!socket.accepts(&ipv6_locals::HEADER_REPR));
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
|
||||
/// A trait for tracking a socket usage session.
|
||||
///
|
||||
/// Allows implementation of custom drop logic that runs only if the socket was changed
|
||||
|
@ -13,7 +12,10 @@ pub trait Session {
|
|||
|
||||
#[cfg(feature = "socket-raw")]
|
||||
impl<'a> Session for crate::socket::RawSocket<'a> {}
|
||||
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
|
||||
#[cfg(all(
|
||||
feature = "socket-icmp",
|
||||
any(feature = "proto-ipv4", feature = "proto-ipv6")
|
||||
))]
|
||||
impl<'a> Session for crate::socket::IcmpSocket<'a> {}
|
||||
#[cfg(feature = "socket-udp")]
|
||||
impl<'a> Session for crate::socket::UdpSocket<'a> {}
|
||||
|
@ -41,7 +43,9 @@ impl<'a, T: Session + 'a> Ref<'a, T> {
|
|||
///
|
||||
/// [into_inner]: #method.into_inner
|
||||
pub fn new(socket: &'a mut T) -> Self {
|
||||
Ref { socket: Some(socket) }
|
||||
Ref {
|
||||
socket: Some(socket),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwrap a smart pointer to a socket.
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use core::{fmt, slice};
|
||||
use managed::ManagedSlice;
|
||||
|
||||
use crate::socket::{Socket, SocketRef, AnySocket};
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
use crate::socket::TcpState;
|
||||
use crate::socket::{AnySocket, Socket, SocketRef};
|
||||
|
||||
/// An item of a socket set.
|
||||
///
|
||||
|
@ -12,7 +12,7 @@ use crate::socket::TcpState;
|
|||
#[derive(Debug)]
|
||||
pub struct Item<'a> {
|
||||
socket: Socket<'a>,
|
||||
refs: usize
|
||||
refs: usize,
|
||||
}
|
||||
|
||||
/// A handle, identifying a socket in a set.
|
||||
|
@ -31,13 +31,15 @@ impl fmt::Display for Handle {
|
|||
/// The lifetime `'a` is used when storing a `Socket<'a>`.
|
||||
#[derive(Debug)]
|
||||
pub struct Set<'a> {
|
||||
sockets: ManagedSlice<'a, Option<Item<'a>>>
|
||||
sockets: ManagedSlice<'a, Option<Item<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Set<'a> {
|
||||
/// Create a socket set using the provided storage.
|
||||
pub fn new<SocketsT>(sockets: SocketsT) -> Set<'a>
|
||||
where SocketsT: Into<ManagedSlice<'a, Option<Item<'a>>>> {
|
||||
where
|
||||
SocketsT: Into<ManagedSlice<'a, Option<Item<'a>>>>,
|
||||
{
|
||||
let sockets = sockets.into();
|
||||
Set { sockets }
|
||||
}
|
||||
|
@ -47,10 +49,10 @@ impl<'a> Set<'a> {
|
|||
/// # Panics
|
||||
/// This function panics if the storage is fixed-size (not a `Vec`) and is full.
|
||||
pub fn add<T>(&mut self, socket: T) -> Handle
|
||||
where T: Into<Socket<'a>>
|
||||
where
|
||||
T: Into<Socket<'a>>,
|
||||
{
|
||||
fn put<'a>(index: usize, slot: &mut Option<Item<'a>>,
|
||||
mut socket: Socket<'a>) -> Handle {
|
||||
fn put<'a>(index: usize, slot: &mut Option<Item<'a>>, mut socket: Socket<'a>) -> Handle {
|
||||
net_trace!("[{}]: adding", index);
|
||||
let handle = Handle(index);
|
||||
socket.meta_mut().handle = handle;
|
||||
|
@ -62,7 +64,7 @@ impl<'a> Set<'a> {
|
|||
|
||||
for (index, slot) in self.sockets.iter_mut().enumerate() {
|
||||
if slot.is_none() {
|
||||
return put(index, slot, socket)
|
||||
return put(index, slot, socket);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,11 +88,9 @@ impl<'a> Set<'a> {
|
|||
/// or the socket has the wrong type.
|
||||
pub fn get<T: AnySocket<'a>>(&mut self, handle: Handle) -> SocketRef<T> {
|
||||
match self.sockets[handle.0].as_mut() {
|
||||
Some(item) => {
|
||||
T::downcast(SocketRef::new(&mut item.socket))
|
||||
.expect("handle refers to a socket of a wrong type")
|
||||
}
|
||||
None => panic!("handle does not refer to a valid socket")
|
||||
Some(item) => T::downcast(SocketRef::new(&mut item.socket))
|
||||
.expect("handle refers to a socket of a wrong type"),
|
||||
None => panic!("handle does not refer to a valid socket"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ impl<'a> Set<'a> {
|
|||
net_trace!("[{}]: removing", handle.0);
|
||||
match self.sockets[handle.0].take() {
|
||||
Some(item) => item.socket,
|
||||
None => panic!("handle does not refer to a valid socket")
|
||||
None => panic!("handle does not refer to a valid socket"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,9 @@ impl<'a> Set<'a> {
|
|||
.as_mut()
|
||||
.expect("handle does not refer to a valid socket")
|
||||
.refs;
|
||||
if *refs == 0 { panic!("decreasing reference count past zero") }
|
||||
if *refs == 0 {
|
||||
panic!("decreasing reference count past zero")
|
||||
}
|
||||
*refs -= 1
|
||||
}
|
||||
|
||||
|
@ -138,27 +140,31 @@ impl<'a> Set<'a> {
|
|||
pub fn prune(&mut self) {
|
||||
for (index, item) in self.sockets.iter_mut().enumerate() {
|
||||
let mut may_remove = false;
|
||||
if let Some(Item { refs: 0, ref mut socket }) = *item {
|
||||
if let Some(Item {
|
||||
refs: 0,
|
||||
ref mut socket,
|
||||
}) = *item
|
||||
{
|
||||
match *socket {
|
||||
#[cfg(feature = "socket-raw")]
|
||||
Socket::Raw(_) =>
|
||||
may_remove = true,
|
||||
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
|
||||
Socket::Icmp(_) =>
|
||||
may_remove = true,
|
||||
Socket::Raw(_) => may_remove = true,
|
||||
#[cfg(all(
|
||||
feature = "socket-icmp",
|
||||
any(feature = "proto-ipv4", feature = "proto-ipv6")
|
||||
))]
|
||||
Socket::Icmp(_) => may_remove = true,
|
||||
#[cfg(feature = "socket-udp")]
|
||||
Socket::Udp(_) =>
|
||||
may_remove = true,
|
||||
Socket::Udp(_) => may_remove = true,
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
Socket::Tcp(ref mut socket) =>
|
||||
Socket::Tcp(ref mut socket) => {
|
||||
if socket.state() == TcpState::Closed {
|
||||
may_remove = true
|
||||
} else {
|
||||
socket.close()
|
||||
},
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "socket-dhcpv4")]
|
||||
Socket::Dhcpv4(_) =>
|
||||
may_remove = true,
|
||||
Socket::Dhcpv4(_) => may_remove = true,
|
||||
}
|
||||
}
|
||||
if may_remove {
|
||||
|
@ -170,12 +176,16 @@ impl<'a> Set<'a> {
|
|||
|
||||
/// Iterate every socket in this set.
|
||||
pub fn iter<'d>(&'d self) -> Iter<'d, 'a> {
|
||||
Iter { lower: self.sockets.iter() }
|
||||
Iter {
|
||||
lower: self.sockets.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate every socket in this set, as SocketRef.
|
||||
pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'a> {
|
||||
IterMut { lower: self.sockets.iter_mut() }
|
||||
IterMut {
|
||||
lower: self.sockets.iter_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,16 +194,16 @@ impl<'a> Set<'a> {
|
|||
/// This struct is created by the [iter](struct.SocketSet.html#method.iter)
|
||||
/// on [socket sets](struct.SocketSet.html).
|
||||
pub struct Iter<'a, 'b: 'a> {
|
||||
lower: slice::Iter<'a, Option<Item<'b>>>
|
||||
lower: slice::Iter<'a, Option<Item<'b>>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> {
|
||||
type Item = &'a Socket<'b>;
|
||||
|
||||
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() {
|
||||
return Some(&item.socket)
|
||||
return Some(&item.socket);
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -212,9 +222,9 @@ impl<'a, 'b: 'a> Iterator for IterMut<'a, 'b> {
|
|||
type Item = SocketRef<'a, Socket<'b>>;
|
||||
|
||||
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() {
|
||||
return Some(SocketRef::new(&mut item.socket))
|
||||
return Some(SocketRef::new(&mut item.socket));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
2622
src/socket/tcp.rs
2622
src/socket/tcp.rs
File diff suppressed because it is too large
Load Diff
|
@ -2,12 +2,12 @@ use core::cmp::min;
|
|||
#[cfg(feature = "async")]
|
||||
use core::task::Waker;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt, Context};
|
||||
use crate::storage::{PacketBuffer, PacketMetadata};
|
||||
use crate::wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr};
|
||||
#[cfg(feature = "async")]
|
||||
use crate::socket::WakerRegistration;
|
||||
use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta};
|
||||
use crate::storage::{PacketBuffer, PacketMetadata};
|
||||
use crate::wire::{IpEndpoint, IpProtocol, IpRepr, UdpRepr};
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// A UDP packet metadata.
|
||||
pub type UdpPacketMetadata = PacketMetadata<IpEndpoint>;
|
||||
|
@ -35,8 +35,7 @@ pub struct UdpSocket<'a> {
|
|||
|
||||
impl<'a> UdpSocket<'a> {
|
||||
/// Create an UDP socket with the given buffers.
|
||||
pub fn new(rx_buffer: UdpSocketBuffer<'a>,
|
||||
tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> {
|
||||
pub fn new(rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> {
|
||||
UdpSocket {
|
||||
meta: SocketMeta::default(),
|
||||
endpoint: IpEndpoint::default(),
|
||||
|
@ -131,9 +130,13 @@ impl<'a> UdpSocket<'a> {
|
|||
/// if the port in the given endpoint is zero.
|
||||
pub fn bind<T: Into<IpEndpoint>>(&mut self, endpoint: T) -> Result<()> {
|
||||
let endpoint = endpoint.into();
|
||||
if endpoint.port == 0 { return Err(Error::Unaddressable) }
|
||||
if endpoint.port == 0 {
|
||||
return Err(Error::Unaddressable);
|
||||
}
|
||||
|
||||
if self.is_open() { return Err(Error::Illegal) }
|
||||
if self.is_open() {
|
||||
return Err(Error::Illegal);
|
||||
}
|
||||
|
||||
self.endpoint = endpoint;
|
||||
|
||||
|
@ -212,13 +215,22 @@ impl<'a> UdpSocket<'a> {
|
|||
/// and `Err(Error::Truncated)` if there is not enough transmit buffer capacity
|
||||
/// to ever send this packet.
|
||||
pub fn send(&mut self, size: usize, endpoint: IpEndpoint) -> Result<&mut [u8]> {
|
||||
if self.endpoint.port == 0 { return Err(Error::Unaddressable) }
|
||||
if !endpoint.is_specified() { return Err(Error::Unaddressable) }
|
||||
if self.endpoint.port == 0 {
|
||||
return Err(Error::Unaddressable);
|
||||
}
|
||||
if !endpoint.is_specified() {
|
||||
return Err(Error::Unaddressable);
|
||||
}
|
||||
|
||||
let payload_buf = self.tx_buffer.enqueue(size, endpoint)?;
|
||||
|
||||
net_trace!("{}:{}:{}: buffer to send {} octets",
|
||||
self.meta.handle, self.endpoint, endpoint, size);
|
||||
net_trace!(
|
||||
"{}:{}:{}: buffer to send {} octets",
|
||||
self.meta.handle,
|
||||
self.endpoint,
|
||||
endpoint,
|
||||
size
|
||||
);
|
||||
Ok(payload_buf)
|
||||
}
|
||||
|
||||
|
@ -237,9 +249,13 @@ impl<'a> UdpSocket<'a> {
|
|||
pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint)> {
|
||||
let (endpoint, payload_buf) = self.rx_buffer.dequeue()?;
|
||||
|
||||
net_trace!("{}:{}:{}: receive {} buffered octets",
|
||||
self.meta.handle, self.endpoint,
|
||||
endpoint, payload_buf.len());
|
||||
net_trace!(
|
||||
"{}:{}:{}: receive {} buffered octets",
|
||||
self.meta.handle,
|
||||
self.endpoint,
|
||||
endpoint,
|
||||
payload_buf.len()
|
||||
);
|
||||
Ok((payload_buf, endpoint))
|
||||
}
|
||||
|
||||
|
@ -263,9 +279,13 @@ impl<'a> UdpSocket<'a> {
|
|||
let handle = self.meta.handle;
|
||||
let endpoint = self.endpoint;
|
||||
self.rx_buffer.peek().map(|(remote_endpoint, payload_buf)| {
|
||||
net_trace!("{}:{}:{}: peek {} buffered octets",
|
||||
handle, endpoint,
|
||||
remote_endpoint, payload_buf.len());
|
||||
net_trace!(
|
||||
"{}:{}:{}: peek {} buffered octets",
|
||||
handle,
|
||||
endpoint,
|
||||
remote_endpoint,
|
||||
payload_buf.len()
|
||||
);
|
||||
(payload_buf, remote_endpoint)
|
||||
})
|
||||
}
|
||||
|
@ -284,26 +304,46 @@ impl<'a> UdpSocket<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &UdpRepr) -> bool {
|
||||
if self.endpoint.port != repr.dst_port { return false }
|
||||
if !self.endpoint.addr.is_unspecified() &&
|
||||
self.endpoint.addr != ip_repr.dst_addr() &&
|
||||
!ip_repr.dst_addr().is_broadcast() &&
|
||||
!ip_repr.dst_addr().is_multicast() { return false }
|
||||
if self.endpoint.port != repr.dst_port {
|
||||
return false;
|
||||
}
|
||||
if !self.endpoint.addr.is_unspecified()
|
||||
&& self.endpoint.addr != ip_repr.dst_addr()
|
||||
&& !ip_repr.dst_addr().is_broadcast()
|
||||
&& !ip_repr.dst_addr().is_multicast()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn process(&mut self, _cx: &Context, ip_repr: &IpRepr, repr: &UdpRepr, payload: &[u8]) -> Result<()> {
|
||||
pub(crate) fn process(
|
||||
&mut self,
|
||||
_cx: &Context,
|
||||
ip_repr: &IpRepr,
|
||||
repr: &UdpRepr,
|
||||
payload: &[u8],
|
||||
) -> Result<()> {
|
||||
debug_assert!(self.accepts(ip_repr, repr));
|
||||
|
||||
let size = payload.len();
|
||||
|
||||
let endpoint = IpEndpoint { addr: ip_repr.src_addr(), port: repr.src_port };
|
||||
self.rx_buffer.enqueue(size, endpoint)?.copy_from_slice(payload);
|
||||
let endpoint = IpEndpoint {
|
||||
addr: ip_repr.src_addr(),
|
||||
port: repr.src_port,
|
||||
};
|
||||
self.rx_buffer
|
||||
.enqueue(size, endpoint)?
|
||||
.copy_from_slice(payload);
|
||||
|
||||
net_trace!("{}:{}:{}: receiving {} octets",
|
||||
self.meta.handle, self.endpoint,
|
||||
endpoint, size);
|
||||
net_trace!(
|
||||
"{}:{}:{}: receiving {} octets",
|
||||
self.meta.handle,
|
||||
self.endpoint,
|
||||
endpoint,
|
||||
size
|
||||
);
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
self.rx_waker.wake();
|
||||
|
@ -312,15 +352,22 @@ impl<'a> UdpSocket<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn dispatch<F>(&mut self, _cx: &Context, emit: F) -> Result<()>
|
||||
where F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()> {
|
||||
where
|
||||
F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()>,
|
||||
{
|
||||
let handle = self.handle();
|
||||
let endpoint = self.endpoint;
|
||||
let hop_limit = self.hop_limit.unwrap_or(64);
|
||||
|
||||
self.tx_buffer.dequeue_with(|remote_endpoint, payload_buf| {
|
||||
net_trace!("{}:{}:{}: sending {} octets",
|
||||
handle, endpoint,
|
||||
endpoint, payload_buf.len());
|
||||
self.tx_buffer
|
||||
.dequeue_with(|remote_endpoint, payload_buf| {
|
||||
net_trace!(
|
||||
"{}:{}:{}: sending {} octets",
|
||||
handle,
|
||||
endpoint,
|
||||
endpoint,
|
||||
payload_buf.len()
|
||||
);
|
||||
|
||||
let repr = UdpRepr {
|
||||
src_port: endpoint.port,
|
||||
|
@ -359,29 +406,39 @@ impl<'a> Into<Socket<'a>> for UdpSocket<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::wire::{IpAddress, IpRepr, UdpRepr};
|
||||
use super::*;
|
||||
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::Ipv4Repr;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use crate::wire::Ipv6Repr;
|
||||
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3};
|
||||
use super::*;
|
||||
use crate::wire::{IpAddress, IpRepr, UdpRepr};
|
||||
|
||||
fn buffer(packets: usize) -> UdpSocketBuffer<'static> {
|
||||
UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; packets], vec![0; 16 * packets])
|
||||
UdpSocketBuffer::new(
|
||||
vec![UdpPacketMetadata::EMPTY; packets],
|
||||
vec![0; 16 * packets],
|
||||
)
|
||||
}
|
||||
|
||||
fn socket(rx_buffer: UdpSocketBuffer<'static>,
|
||||
tx_buffer: UdpSocketBuffer<'static>)
|
||||
-> UdpSocket<'static> {
|
||||
fn socket(
|
||||
rx_buffer: UdpSocketBuffer<'static>,
|
||||
tx_buffer: UdpSocketBuffer<'static>,
|
||||
) -> UdpSocket<'static> {
|
||||
UdpSocket::new(rx_buffer, tx_buffer)
|
||||
}
|
||||
|
||||
const LOCAL_PORT: u16 = 53;
|
||||
const REMOTE_PORT: u16 = 49500;
|
||||
|
||||
pub const LOCAL_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_1, port: LOCAL_PORT };
|
||||
pub const REMOTE_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_2, port: REMOTE_PORT };
|
||||
pub const LOCAL_END: IpEndpoint = IpEndpoint {
|
||||
addr: MOCK_IP_ADDR_1,
|
||||
port: LOCAL_PORT,
|
||||
};
|
||||
pub const REMOTE_END: IpEndpoint = IpEndpoint {
|
||||
addr: MOCK_IP_ADDR_2,
|
||||
port: REMOTE_PORT,
|
||||
};
|
||||
|
||||
pub const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified {
|
||||
src_addr: MOCK_IP_ADDR_1,
|
||||
|
@ -411,7 +468,7 @@ mod test {
|
|||
dst_addr: dst,
|
||||
protocol: IpProtocol::Udp,
|
||||
payload_len: 8 + 6,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
}),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
|
||||
|
@ -419,9 +476,9 @@ mod test {
|
|||
dst_addr: dst,
|
||||
next_header: IpProtocol::Udp,
|
||||
payload_len: 8 + 6,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
}),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,14 +505,31 @@ mod test {
|
|||
#[test]
|
||||
fn test_send_unaddressable() {
|
||||
let mut socket = socket(buffer(0), buffer(1));
|
||||
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Err(Error::Unaddressable));
|
||||
assert_eq!(
|
||||
socket.send_slice(b"abcdef", REMOTE_END),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
|
||||
assert_eq!(socket.send_slice(b"abcdef",
|
||||
IpEndpoint { addr: IpAddress::Unspecified, ..REMOTE_END }),
|
||||
Err(Error::Unaddressable));
|
||||
assert_eq!(socket.send_slice(b"abcdef",
|
||||
IpEndpoint { port: 0, ..REMOTE_END }),
|
||||
Err(Error::Unaddressable));
|
||||
assert_eq!(
|
||||
socket.send_slice(
|
||||
b"abcdef",
|
||||
IpEndpoint {
|
||||
addr: IpAddress::Unspecified,
|
||||
..REMOTE_END
|
||||
}
|
||||
),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
assert_eq!(
|
||||
socket.send_slice(
|
||||
b"abcdef",
|
||||
IpEndpoint {
|
||||
port: 0,
|
||||
..REMOTE_END
|
||||
}
|
||||
),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(()));
|
||||
}
|
||||
|
||||
|
@ -465,27 +539,38 @@ mod test {
|
|||
assert_eq!(socket.bind(LOCAL_END), Ok(()));
|
||||
|
||||
assert!(socket.can_send());
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
|
||||
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(()));
|
||||
assert_eq!(socket.send_slice(b"123456", REMOTE_END), Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.send_slice(b"123456", REMOTE_END),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
|
||||
assert_eq!(ip_repr, LOCAL_IP_REPR);
|
||||
assert_eq!(udp_repr, LOCAL_UDP_REPR);
|
||||
assert_eq!(payload, PAYLOAD);
|
||||
Err(Error::Unaddressable)
|
||||
}), Err(Error::Unaddressable));
|
||||
}),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
|
||||
assert_eq!(
|
||||
socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
|
||||
assert_eq!(ip_repr, LOCAL_IP_REPR);
|
||||
assert_eq!(udp_repr, LOCAL_UDP_REPR);
|
||||
assert_eq!(payload, PAYLOAD);
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
}),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_send());
|
||||
}
|
||||
|
||||
|
@ -498,13 +583,27 @@ mod test {
|
|||
assert_eq!(socket.recv(), Err(Error::Exhausted));
|
||||
|
||||
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&remote_ip_repr(),
|
||||
&REMOTE_UDP_REPR,
|
||||
PAYLOAD
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert!(socket.can_recv());
|
||||
|
||||
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&remote_ip_repr(),
|
||||
&REMOTE_UDP_REPR,
|
||||
PAYLOAD
|
||||
),
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
|
||||
assert!(!socket.can_recv());
|
||||
}
|
||||
|
@ -516,8 +615,15 @@ mod test {
|
|||
|
||||
assert_eq!(socket.peek(), Err(Error::Exhausted));
|
||||
|
||||
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&remote_ip_repr(),
|
||||
&REMOTE_UDP_REPR,
|
||||
PAYLOAD
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END)));
|
||||
assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
|
||||
assert_eq!(socket.peek(), Err(Error::Exhausted));
|
||||
|
@ -529,8 +635,15 @@ mod test {
|
|||
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
|
||||
|
||||
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
|
||||
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&remote_ip_repr(),
|
||||
&REMOTE_UDP_REPR,
|
||||
PAYLOAD
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
let mut slice = [0; 4];
|
||||
assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END)));
|
||||
|
@ -542,8 +655,15 @@ mod test {
|
|||
let mut socket = socket(buffer(1), buffer(0));
|
||||
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
|
||||
|
||||
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &REMOTE_UDP_REPR, PAYLOAD),
|
||||
Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(
|
||||
&Context::DUMMY,
|
||||
&remote_ip_repr(),
|
||||
&REMOTE_UDP_REPR,
|
||||
PAYLOAD
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
let mut slice = [0; 4];
|
||||
assert_eq!(socket.peek_slice(&mut slice[..]), Ok((4, &REMOTE_END)));
|
||||
|
@ -560,16 +680,22 @@ mod test {
|
|||
|
||||
s.set_hop_limit(Some(0x2a));
|
||||
assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
|
||||
assert_eq!(s.dispatch(&Context::DUMMY, |(ip_repr, _, _)| {
|
||||
assert_eq!(ip_repr, IpRepr::Unspecified{
|
||||
assert_eq!(
|
||||
s.dispatch(&Context::DUMMY, |(ip_repr, _, _)| {
|
||||
assert_eq!(
|
||||
ip_repr,
|
||||
IpRepr::Unspecified {
|
||||
src_addr: MOCK_IP_ADDR_1,
|
||||
dst_addr: MOCK_IP_ADDR_2,
|
||||
protocol: IpProtocol::Udp,
|
||||
payload_len: 8 + 6,
|
||||
hop_limit: 0x2a,
|
||||
});
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
}),
|
||||
Ok(())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -593,7 +719,7 @@ mod test {
|
|||
dst_addr: dst,
|
||||
protocol: IpProtocol::Udp,
|
||||
payload_len: 8 + 6,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
}),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
|
||||
|
@ -601,9 +727,9 @@ mod test {
|
|||
dst_addr: dst,
|
||||
next_header: IpProtocol::Udp,
|
||||
payload_len: 8 + 6,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
}),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -623,8 +749,11 @@ mod test {
|
|||
assert_eq!(socket.bind(LOCAL_END), Ok(()));
|
||||
|
||||
let too_large = b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefx";
|
||||
assert_eq!(socket.send_slice(too_large, REMOTE_END), Err(Error::Truncated));
|
||||
assert_eq!(socket.send_slice(&too_large[..16*4], REMOTE_END), Ok(()));
|
||||
assert_eq!(
|
||||
socket.send_slice(too_large, REMOTE_END),
|
||||
Err(Error::Truncated)
|
||||
);
|
||||
assert_eq!(socket.send_slice(&too_large[..16 * 4], REMOTE_END), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -637,7 +766,10 @@ mod test {
|
|||
src_port: REMOTE_PORT,
|
||||
dst_port: LOCAL_PORT,
|
||||
};
|
||||
assert_eq!(socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]), Ok(()));
|
||||
assert_eq!(
|
||||
socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]),
|
||||
Ok(())
|
||||
);
|
||||
assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END)));
|
||||
}
|
||||
|
||||
|
|
|
@ -8,29 +8,44 @@ pub struct TooManyHolesError;
|
|||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
struct Contig {
|
||||
hole_size: usize,
|
||||
data_size: usize
|
||||
data_size: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for Contig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.has_hole() { write!(f, "({})", self.hole_size)?; }
|
||||
if self.has_hole() && self.has_data() { write!(f, " ")?; }
|
||||
if self.has_data() { write!(f, "{}", self.data_size)?; }
|
||||
if self.has_hole() {
|
||||
write!(f, "({})", self.hole_size)?;
|
||||
}
|
||||
if self.has_hole() && self.has_data() {
|
||||
write!(f, " ")?;
|
||||
}
|
||||
if self.has_data() {
|
||||
write!(f, "{}", self.data_size)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Contig {
|
||||
fn empty() -> Contig {
|
||||
Contig { hole_size: 0, data_size: 0 }
|
||||
Contig {
|
||||
hole_size: 0,
|
||||
data_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn hole(size: usize) -> Contig {
|
||||
Contig { hole_size: size, data_size: 0 }
|
||||
Contig {
|
||||
hole_size: size,
|
||||
data_size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn hole_and_data(hole_size: usize, data_size: usize) -> Contig {
|
||||
Contig { hole_size, data_size }
|
||||
Contig {
|
||||
hole_size,
|
||||
data_size,
|
||||
}
|
||||
}
|
||||
|
||||
fn has_hole(&self) -> bool {
|
||||
|
@ -66,10 +81,10 @@ impl Contig {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::boxed::Box;
|
||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "std")]
|
||||
use std::boxed::Box;
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
const CONTIG_COUNT: usize = 32;
|
||||
|
||||
|
@ -93,7 +108,9 @@ impl fmt::Display for Assembler {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "[ ")?;
|
||||
for contig in self.contigs.iter() {
|
||||
if contig.is_empty() { break }
|
||||
if contig.is_empty() {
|
||||
break;
|
||||
}
|
||||
write!(f, "{} ", contig)?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
|
@ -115,10 +132,7 @@ impl Assembler {
|
|||
/// FIXME(whitequark): remove this once I'm certain enough that the assembler works well.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn total_size(&self) -> usize {
|
||||
self.contigs
|
||||
.iter()
|
||||
.map(|contig| contig.total_size())
|
||||
.sum()
|
||||
self.contigs.iter().map(|contig| contig.total_size()).sum()
|
||||
}
|
||||
|
||||
fn front(&self) -> Contig {
|
||||
|
@ -143,7 +157,7 @@ impl Assembler {
|
|||
self.contigs[i] = self.contigs[i + 1];
|
||||
if !self.contigs[i].has_data() {
|
||||
self.contigs[i + 1] = Contig::empty();
|
||||
return &mut self.contigs[i]
|
||||
return &mut self.contigs[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +170,9 @@ impl Assembler {
|
|||
fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, TooManyHolesError> {
|
||||
debug_assert!(!self.contigs[at].is_empty());
|
||||
|
||||
if !self.back().is_empty() { return Err(TooManyHolesError) }
|
||||
if !self.back().is_empty() {
|
||||
return Err(TooManyHolesError);
|
||||
}
|
||||
|
||||
for i in (at + 1..self.contigs.len()).rev() {
|
||||
self.contigs[i] = self.contigs[i - 1];
|
||||
|
@ -205,7 +221,7 @@ impl Assembler {
|
|||
*inserted = Contig::hole_and_data(offset, size);
|
||||
}
|
||||
// Previous contigs[index] got moved to contigs[index+1]
|
||||
self.contigs[index+1].shrink_hole_by(offset + size);
|
||||
self.contigs[index + 1].shrink_hole_by(offset + size);
|
||||
index += 2;
|
||||
} else {
|
||||
unreachable!()
|
||||
|
@ -258,7 +274,7 @@ pub struct AssemblerIter<'a> {
|
|||
offset: usize,
|
||||
index: usize,
|
||||
left: usize,
|
||||
right: usize
|
||||
right: usize,
|
||||
}
|
||||
|
||||
impl<'a> AssemblerIter<'a> {
|
||||
|
@ -268,7 +284,7 @@ impl<'a> AssemblerIter<'a> {
|
|||
offset: offset,
|
||||
index: 0,
|
||||
left: 0,
|
||||
right: 0
|
||||
right: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -297,8 +313,8 @@ impl<'a> Iterator for AssemblerIter<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::vec::Vec;
|
||||
use super::*;
|
||||
use std::vec::Vec;
|
||||
|
||||
impl From<Vec<(usize, usize)>> for Assembler {
|
||||
fn from(vec: Vec<(usize, usize)>) -> Assembler {
|
||||
|
@ -307,7 +323,10 @@ mod test {
|
|||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]);
|
||||
for (i, &(hole_size, data_size)) in vec.iter().enumerate() {
|
||||
contigs[i] = Contig { hole_size, data_size };
|
||||
contigs[i] = Contig {
|
||||
hole_size,
|
||||
data_size,
|
||||
};
|
||||
}
|
||||
Assembler { contigs }
|
||||
}
|
||||
|
@ -412,9 +431,9 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_rejected_add_keeps_state() {
|
||||
let mut assr = Assembler::new(CONTIG_COUNT*20);
|
||||
for c in 1..=CONTIG_COUNT-1 {
|
||||
assert_eq!(assr.add(c*10, 3), Ok(()));
|
||||
let mut assr = Assembler::new(CONTIG_COUNT * 20);
|
||||
for c in 1..=CONTIG_COUNT - 1 {
|
||||
assert_eq!(assr.add(c * 10, 3), Ok(()));
|
||||
}
|
||||
// Maximum of allowed holes is reached
|
||||
let assr_before = assr.clone();
|
||||
|
@ -440,7 +459,6 @@ mod test {
|
|||
let mut assr = contigs![(0, 4), (4, 4)];
|
||||
assert_eq!(assr.remove_front(), Some(4));
|
||||
assert_eq!(assr, contigs![(4, 4), (4, 0)]);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -6,12 +6,12 @@ or `alloc` crates being available, and heap-allocated memory.
|
|||
*/
|
||||
|
||||
mod assembler;
|
||||
mod ring_buffer;
|
||||
mod packet_buffer;
|
||||
mod ring_buffer;
|
||||
|
||||
pub use self::assembler::Assembler;
|
||||
pub use self::ring_buffer::RingBuffer;
|
||||
pub use self::packet_buffer::{PacketBuffer, PacketMetadata};
|
||||
pub use self::ring_buffer::RingBuffer;
|
||||
|
||||
/// A trait for setting a value to a known state.
|
||||
///
|
||||
|
|
|
@ -1,31 +1,34 @@
|
|||
use managed::ManagedSlice;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::storage::RingBuffer;
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// Size and header of a packet.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PacketMetadata<H> {
|
||||
size: usize,
|
||||
header: Option<H>
|
||||
header: Option<H>,
|
||||
}
|
||||
|
||||
impl<H> PacketMetadata<H> {
|
||||
/// Empty packet description.
|
||||
pub const EMPTY: PacketMetadata<H> = PacketMetadata { size: 0, header: None };
|
||||
pub const EMPTY: PacketMetadata<H> = PacketMetadata {
|
||||
size: 0,
|
||||
header: None,
|
||||
};
|
||||
|
||||
fn padding(size: usize) -> PacketMetadata<H> {
|
||||
PacketMetadata {
|
||||
size: size,
|
||||
header: None
|
||||
header: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn packet(size: usize, header: H) -> PacketMetadata<H> {
|
||||
PacketMetadata {
|
||||
size: size,
|
||||
header: Some(header)
|
||||
header: Some(header),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +50,8 @@ impl<'a, H> PacketBuffer<'a, H> {
|
|||
/// Metadata storage limits the maximum _number_ of packets in the buffer and payload
|
||||
/// storage limits the maximum _total size_ of packets.
|
||||
pub fn new<MS, PS>(metadata_storage: MS, payload_storage: PS) -> PacketBuffer<'a, H>
|
||||
where MS: Into<ManagedSlice<'a, PacketMetadata<H>>>,
|
||||
where
|
||||
MS: Into<ManagedSlice<'a, PacketMetadata<H>>>,
|
||||
PS: Into<ManagedSlice<'a, u8>>,
|
||||
{
|
||||
PacketBuffer {
|
||||
|
@ -75,25 +79,25 @@ impl<'a, H> PacketBuffer<'a, H> {
|
|||
/// does not have enough spare payload space.
|
||||
pub fn enqueue(&mut self, size: usize, header: H) -> Result<&mut [u8]> {
|
||||
if self.payload_ring.capacity() < size {
|
||||
return Err(Error::Truncated)
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
|
||||
if self.metadata_ring.is_full() {
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
|
||||
let window = self.payload_ring.window();
|
||||
let contig_window = self.payload_ring.contiguous_window();
|
||||
|
||||
if window < size {
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
} else if contig_window < size {
|
||||
if window - contig_window < size {
|
||||
// The buffer length is larger than the current contiguous window
|
||||
// and is larger than the contiguous window will be after adding
|
||||
// the padding necessary to circle around to the beginning of the
|
||||
// ring buffer.
|
||||
return Err(Error::Exhausted)
|
||||
return Err(Error::Exhausted);
|
||||
} else {
|
||||
// Add padding to the end of the ring buffer so that the
|
||||
// contiguous window is at the beginning of the ring buffer.
|
||||
|
@ -110,7 +114,10 @@ impl<'a, H> PacketBuffer<'a, H> {
|
|||
}
|
||||
|
||||
fn dequeue_padding(&mut self) {
|
||||
let Self { ref mut metadata_ring, ref mut payload_ring } = *self;
|
||||
let Self {
|
||||
ref mut metadata_ring,
|
||||
ref mut payload_ring,
|
||||
} = *self;
|
||||
|
||||
let _ = metadata_ring.dequeue_one_with(|metadata| {
|
||||
if metadata.is_padding() {
|
||||
|
@ -125,22 +132,32 @@ impl<'a, H> PacketBuffer<'a, H> {
|
|||
/// Call `f` with a single packet from the buffer, and dequeue the packet if `f`
|
||||
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty.
|
||||
pub fn dequeue_with<'c, R, F>(&'c mut self, f: F) -> Result<R>
|
||||
where F: FnOnce(&mut H, &'c mut [u8]) -> Result<R> {
|
||||
where
|
||||
F: FnOnce(&mut H, &'c mut [u8]) -> Result<R>,
|
||||
{
|
||||
self.dequeue_padding();
|
||||
|
||||
let Self { ref mut metadata_ring, ref mut payload_ring } = *self;
|
||||
let Self {
|
||||
ref mut metadata_ring,
|
||||
ref mut payload_ring,
|
||||
} = *self;
|
||||
|
||||
metadata_ring.dequeue_one_with(move |metadata| {
|
||||
let PacketMetadata { ref mut header, size } = *metadata;
|
||||
let PacketMetadata {
|
||||
ref mut header,
|
||||
size,
|
||||
} = *metadata;
|
||||
|
||||
payload_ring.dequeue_many_with(|payload_buf| {
|
||||
payload_ring
|
||||
.dequeue_many_with(|payload_buf| {
|
||||
debug_assert!(payload_buf.len() >= size);
|
||||
|
||||
match f(header.as_mut().unwrap(), &mut payload_buf[..size]) {
|
||||
Ok(val) => (size, Ok(val)),
|
||||
Err(err) => (0, Err(err)),
|
||||
}
|
||||
}).1
|
||||
})
|
||||
.1
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -149,7 +166,10 @@ impl<'a, H> PacketBuffer<'a, H> {
|
|||
pub fn dequeue(&mut self) -> Result<(H, &mut [u8])> {
|
||||
self.dequeue_padding();
|
||||
|
||||
let PacketMetadata { ref mut header, size } = *self.metadata_ring.dequeue_one()?;
|
||||
let PacketMetadata {
|
||||
ref mut header,
|
||||
size,
|
||||
} = *self.metadata_ring.dequeue_one()?;
|
||||
|
||||
let payload_buf = self.payload_ring.dequeue_many(size);
|
||||
debug_assert!(payload_buf.len() == size);
|
||||
|
@ -164,7 +184,10 @@ impl<'a, H> PacketBuffer<'a, H> {
|
|||
self.dequeue_padding();
|
||||
|
||||
if let Some(metadata) = self.metadata_ring.get_allocated(0, 1).first() {
|
||||
Ok((metadata.header.as_ref().unwrap(), self.payload_ring.get_allocated(0, metadata.size)))
|
||||
Ok((
|
||||
metadata.header.as_ref().unwrap(),
|
||||
self.payload_ring.get_allocated(0, metadata.size),
|
||||
))
|
||||
} else {
|
||||
Err(Error::Exhausted)
|
||||
}
|
||||
|
@ -193,8 +216,7 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
fn buffer() -> PacketBuffer<'static, ()> {
|
||||
PacketBuffer::new(vec![PacketMetadata::EMPTY; 4],
|
||||
vec![0u8; 16])
|
||||
PacketBuffer::new(vec![PacketMetadata::EMPTY; 4], vec![0u8; 16])
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -237,7 +259,10 @@ mod test {
|
|||
let mut buffer = buffer();
|
||||
assert!(buffer.enqueue(12, ()).is_ok());
|
||||
assert!(buffer.dequeue().is_ok());
|
||||
buffer.enqueue(12, ()).unwrap().copy_from_slice(b"abcdefghijkl");
|
||||
buffer
|
||||
.enqueue(12, ())
|
||||
.unwrap()
|
||||
.copy_from_slice(b"abcdefghijkl");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -250,13 +275,17 @@ mod test {
|
|||
assert_eq!(buffer.metadata_ring.len(), 3);
|
||||
assert!(buffer.dequeue().is_ok());
|
||||
|
||||
assert!(buffer.dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>).is_err());
|
||||
assert!(buffer
|
||||
.dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>)
|
||||
.is_err());
|
||||
assert_eq!(buffer.metadata_ring.len(), 1);
|
||||
|
||||
assert!(buffer.dequeue_with(|&mut (), payload| {
|
||||
assert!(buffer
|
||||
.dequeue_with(|&mut (), payload| {
|
||||
assert_eq!(payload, &b"abcd"[..]);
|
||||
Ok(())
|
||||
}).is_ok());
|
||||
})
|
||||
.is_ok());
|
||||
assert_eq!(buffer.metadata_ring.len(), 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
use core::cmp;
|
||||
use managed::ManagedSlice;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::storage::Resettable;
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// A ring buffer.
|
||||
///
|
||||
|
@ -33,7 +33,8 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
///
|
||||
/// During creation, every element in `storage` is reset.
|
||||
pub fn new<S>(storage: S) -> RingBuffer<'a, T>
|
||||
where S: Into<ManagedSlice<'a, T>>,
|
||||
where
|
||||
S: Into<ManagedSlice<'a, T>>,
|
||||
{
|
||||
RingBuffer {
|
||||
storage: storage.into(),
|
||||
|
@ -55,7 +56,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
|
||||
/// Clear the ring buffer, and reset every element.
|
||||
pub fn reset(&mut self)
|
||||
where T: Resettable {
|
||||
where
|
||||
T: Resettable,
|
||||
{
|
||||
self.clear();
|
||||
for elem in self.storage.iter_mut() {
|
||||
elem.reset();
|
||||
|
@ -112,8 +115,12 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
/// Call `f` with a single buffer element, and enqueue the element if `f`
|
||||
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is full.
|
||||
pub fn enqueue_one_with<'b, R, F>(&'b mut self, f: F) -> Result<R>
|
||||
where F: FnOnce(&'b mut T) -> Result<R> {
|
||||
if self.is_full() { return Err(Error::Exhausted) }
|
||||
where
|
||||
F: FnOnce(&'b mut T) -> Result<R>,
|
||||
{
|
||||
if self.is_full() {
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
|
||||
let index = self.get_idx_unchecked(self.length);
|
||||
match f(&mut self.storage[index]) {
|
||||
|
@ -121,7 +128,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
self.length += 1;
|
||||
Ok(result)
|
||||
}
|
||||
Err(error) => Err(error)
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,8 +143,12 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
/// Call `f` with a single buffer element, and dequeue the element if `f`
|
||||
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty.
|
||||
pub fn dequeue_one_with<'b, R, F>(&'b mut self, f: F) -> Result<R>
|
||||
where F: FnOnce(&'b mut T) -> Result<R> {
|
||||
if self.is_empty() { return Err(Error::Exhausted) }
|
||||
where
|
||||
F: FnOnce(&'b mut T) -> Result<R>,
|
||||
{
|
||||
if self.is_empty() {
|
||||
return Err(Error::Exhausted);
|
||||
}
|
||||
|
||||
let next_at = self.get_idx_unchecked(1);
|
||||
match f(&mut self.storage[self.read_at]) {
|
||||
|
@ -146,7 +157,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
self.read_at = next_at;
|
||||
Ok(result)
|
||||
}
|
||||
Err(error) => Err(error)
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +180,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
/// This function panics if the amount of elements returned by `f` is larger
|
||||
/// than the size of the slice passed into it.
|
||||
pub fn enqueue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R)
|
||||
where F: FnOnce(&'b mut [T]) -> (usize, R) {
|
||||
where
|
||||
F: FnOnce(&'b mut [T]) -> (usize, R),
|
||||
{
|
||||
if self.length == 0 {
|
||||
// Ring is currently empty. Reset `read_at` to optimize
|
||||
// for contiguous space.
|
||||
|
@ -194,14 +207,17 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
self.enqueue_many_with(|buf| {
|
||||
let size = cmp::min(size, buf.len());
|
||||
(size, &mut buf[..size])
|
||||
}).1
|
||||
})
|
||||
.1
|
||||
}
|
||||
|
||||
/// Enqueue as many elements from the given slice into the buffer as possible,
|
||||
/// and return the amount of elements that could fit.
|
||||
// #[must_use]
|
||||
pub fn enqueue_slice(&mut self, data: &[T]) -> usize
|
||||
where T: Copy {
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let (size_1, data) = self.enqueue_many_with(|buf| {
|
||||
let size = cmp::min(buf.len(), data.len());
|
||||
buf[..size].copy_from_slice(&data[..size]);
|
||||
|
@ -222,7 +238,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
/// This function panics if the amount of elements returned by `f` is larger
|
||||
/// than the size of the slice passed into it.
|
||||
pub fn dequeue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R)
|
||||
where F: FnOnce(&'b mut [T]) -> (usize, R) {
|
||||
where
|
||||
F: FnOnce(&'b mut [T]) -> (usize, R),
|
||||
{
|
||||
let capacity = self.capacity();
|
||||
let max_size = cmp::min(self.len(), capacity - self.read_at);
|
||||
let (size, result) = f(&mut self.storage[self.read_at..self.read_at + max_size]);
|
||||
|
@ -246,14 +264,17 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
self.dequeue_many_with(|buf| {
|
||||
let size = cmp::min(size, buf.len());
|
||||
(size, &mut buf[..size])
|
||||
}).1
|
||||
})
|
||||
.1
|
||||
}
|
||||
|
||||
/// Dequeue as many elements from the buffer into the given slice as possible,
|
||||
/// and return the amount of elements that could fit.
|
||||
// #[must_use]
|
||||
pub fn dequeue_slice(&mut self, data: &mut [T]) -> usize
|
||||
where T: Copy {
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let (size_1, data) = self.dequeue_many_with(|buf| {
|
||||
let size = cmp::min(buf.len(), data.len());
|
||||
data[..size].copy_from_slice(&buf[..size]);
|
||||
|
@ -277,13 +298,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
pub fn get_unallocated(&mut self, offset: usize, mut size: usize) -> &mut [T] {
|
||||
let start_at = self.get_idx(self.length + offset);
|
||||
// We can't access past the end of unallocated data.
|
||||
if offset > self.window() { return &mut [] }
|
||||
if offset > self.window() {
|
||||
return &mut [];
|
||||
}
|
||||
// We can't enqueue more than there is free space.
|
||||
let clamped_window = self.window() - offset;
|
||||
if size > clamped_window { size = clamped_window }
|
||||
if size > clamped_window {
|
||||
size = clamped_window
|
||||
}
|
||||
// We can't contiguously enqueue past the end of the storage.
|
||||
let until_end = self.capacity() - start_at;
|
||||
if size > until_end { size = until_end }
|
||||
if size > until_end {
|
||||
size = until_end
|
||||
}
|
||||
|
||||
&mut self.storage[start_at..start_at + size]
|
||||
}
|
||||
|
@ -293,7 +320,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
/// the amount written.
|
||||
// #[must_use]
|
||||
pub fn write_unallocated(&mut self, offset: usize, data: &[T]) -> usize
|
||||
where T: Copy {
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let (size_1, offset, data) = {
|
||||
let slice = self.get_unallocated(offset, data.len());
|
||||
let slice_len = slice.len();
|
||||
|
@ -324,13 +353,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
pub fn get_allocated(&self, offset: usize, mut size: usize) -> &[T] {
|
||||
let start_at = self.get_idx(offset);
|
||||
// We can't read past the end of the allocated data.
|
||||
if offset > self.length { return &mut [] }
|
||||
if offset > self.length {
|
||||
return &mut [];
|
||||
}
|
||||
// We can't read more than we have allocated.
|
||||
let clamped_length = self.length - offset;
|
||||
if size > clamped_length { size = clamped_length }
|
||||
if size > clamped_length {
|
||||
size = clamped_length
|
||||
}
|
||||
// We can't contiguously dequeue past the end of the storage.
|
||||
let until_end = self.capacity() - start_at;
|
||||
if size > until_end { size = until_end }
|
||||
if size > until_end {
|
||||
size = until_end
|
||||
}
|
||||
|
||||
&self.storage[start_at..start_at + size]
|
||||
}
|
||||
|
@ -340,7 +375,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
|
|||
/// the amount read.
|
||||
// #[must_use]
|
||||
pub fn read_allocated(&mut self, offset: usize, data: &mut [T]) -> usize
|
||||
where T: Copy {
|
||||
where
|
||||
T: Copy,
|
||||
{
|
||||
let (size_1, offset, data) = {
|
||||
let slice = self.get_allocated(offset, data.len());
|
||||
data[..slice.len()].copy_from_slice(slice);
|
||||
|
@ -402,8 +439,10 @@ mod test {
|
|||
#[test]
|
||||
fn test_buffer_enqueue_dequeue_one_with() {
|
||||
let mut ring = RingBuffer::new(vec![0; 5]);
|
||||
assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
|
||||
ring.enqueue_one_with(Ok).unwrap();
|
||||
assert!(!ring.is_empty());
|
||||
|
@ -414,15 +453,19 @@ mod test {
|
|||
assert!(!ring.is_empty());
|
||||
}
|
||||
assert!(ring.is_full());
|
||||
assert_eq!(ring.enqueue_one_with(|_| unreachable!()) as Result<()>,
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
ring.enqueue_one_with(|_| unreachable!()) as Result<()>,
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
|
||||
for i in 0..5 {
|
||||
assert_eq!(ring.dequeue_one_with(|e| Ok(*e)).unwrap(), i);
|
||||
assert!(!ring.is_full());
|
||||
}
|
||||
assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(
|
||||
ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
|
||||
Err(Error::Exhausted)
|
||||
);
|
||||
assert!(ring.is_empty());
|
||||
}
|
||||
|
||||
|
@ -454,11 +497,14 @@ mod test {
|
|||
fn test_buffer_enqueue_many_with() {
|
||||
let mut ring = RingBuffer::new(vec![b'.'; 12]);
|
||||
|
||||
assert_eq!(ring.enqueue_many_with(|buf| {
|
||||
assert_eq!(
|
||||
ring.enqueue_many_with(|buf| {
|
||||
assert_eq!(buf.len(), 12);
|
||||
buf[0..2].copy_from_slice(b"ab");
|
||||
(2, true)
|
||||
}), (2, true));
|
||||
}),
|
||||
(2, true)
|
||||
);
|
||||
assert_eq!(ring.len(), 2);
|
||||
assert_eq!(&ring.storage[..], b"ab..........");
|
||||
|
||||
|
@ -545,12 +591,15 @@ mod test {
|
|||
|
||||
assert_eq!(ring.enqueue_slice(b"abcdefghijkl"), 12);
|
||||
|
||||
assert_eq!(ring.dequeue_many_with(|buf| {
|
||||
assert_eq!(
|
||||
ring.dequeue_many_with(|buf| {
|
||||
assert_eq!(buf.len(), 12);
|
||||
assert_eq!(buf, b"abcdefghijkl");
|
||||
buf[..4].copy_from_slice(b"....");
|
||||
(4, true)
|
||||
}), (4, true));
|
||||
}),
|
||||
(4, true)
|
||||
);
|
||||
assert_eq!(ring.len(), 8);
|
||||
assert_eq!(&ring.storage[..], b"....efghijkl");
|
||||
|
||||
|
@ -711,7 +760,6 @@ mod test {
|
|||
let mut data = [0; 6];
|
||||
assert_eq!(ring.read_allocated(6, &mut data[..]), 3);
|
||||
assert_eq!(&data[..], b"mno\x00\x00\x00");
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
67
src/time.rs
67
src/time.rs
|
@ -10,7 +10,7 @@ absolute and relative time.
|
|||
[Duration]: struct.Duration.html
|
||||
*/
|
||||
|
||||
use core::{ops, fmt};
|
||||
use core::{fmt, ops};
|
||||
|
||||
/// A representation of an absolute time value.
|
||||
///
|
||||
|
@ -30,12 +30,16 @@ pub struct Instant {
|
|||
impl Instant {
|
||||
/// Create a new `Instant` from a number of milliseconds.
|
||||
pub fn from_millis<T: Into<i64>>(millis: T) -> Instant {
|
||||
Instant { millis: millis.into() }
|
||||
Instant {
|
||||
millis: millis.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Instant` from a number of seconds.
|
||||
pub fn from_secs<T: Into<i64>>(secs: T) -> Instant {
|
||||
Instant { millis: secs.into() * 1000 }
|
||||
Instant {
|
||||
millis: secs.into() * 1000,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Instant` from the current [std::time::SystemTime].
|
||||
|
@ -79,7 +83,8 @@ impl From<::std::time::Instant> for Instant {
|
|||
#[cfg(feature = "std")]
|
||||
impl From<::std::time::SystemTime> for Instant {
|
||||
fn from(other: ::std::time::SystemTime) -> Instant {
|
||||
let n = other.duration_since(::std::time::UNIX_EPOCH)
|
||||
let n = other
|
||||
.duration_since(::std::time::UNIX_EPOCH)
|
||||
.expect("start time must not be before the unix epoch");
|
||||
Self::from_millis(n.as_secs() as i64 * 1000 + n.subsec_millis() as i64)
|
||||
}
|
||||
|
@ -149,7 +154,9 @@ impl Duration {
|
|||
|
||||
/// Create a new `Instant` from a number of seconds.
|
||||
pub const fn from_secs(secs: u64) -> Duration {
|
||||
Duration { millis: secs * 1000 }
|
||||
Duration {
|
||||
millis: secs * 1000,
|
||||
}
|
||||
}
|
||||
|
||||
/// The fractional number of milliseconds in this `Duration`.
|
||||
|
@ -193,14 +200,19 @@ impl ops::Sub<Duration> for Duration {
|
|||
|
||||
fn sub(self, rhs: Duration) -> Duration {
|
||||
Duration::from_millis(
|
||||
self.millis.checked_sub(rhs.total_millis()).expect("overflow when subtracting durations"))
|
||||
self.millis
|
||||
.checked_sub(rhs.total_millis())
|
||||
.expect("overflow when subtracting durations"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::SubAssign<Duration> for Duration {
|
||||
fn sub_assign(&mut self, rhs: Duration) {
|
||||
self.millis = self.millis.checked_sub(
|
||||
rhs.total_millis()).expect("overflow when subtracting durations");
|
||||
self.millis = self
|
||||
.millis
|
||||
.checked_sub(rhs.total_millis())
|
||||
.expect("overflow when subtracting durations");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,17 +274,13 @@ impl ops::ShrAssign<u32> for Duration {
|
|||
|
||||
impl From<::core::time::Duration> for Duration {
|
||||
fn from(other: ::core::time::Duration) -> Duration {
|
||||
Duration::from_millis(
|
||||
other.as_secs() * 1000 + other.subsec_millis() as u64
|
||||
)
|
||||
Duration::from_millis(other.as_secs() * 1000 + other.subsec_millis() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<::core::time::Duration> for Duration {
|
||||
fn into(self) -> ::core::time::Duration {
|
||||
::core::time::Duration::from_millis(
|
||||
self.total_millis()
|
||||
)
|
||||
::core::time::Duration::from_millis(self.total_millis())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,9 +291,15 @@ mod test {
|
|||
#[test]
|
||||
fn test_instant_ops() {
|
||||
// std::ops::Add
|
||||
assert_eq!(Instant::from_millis(4) + Duration::from_millis(6), Instant::from_millis(10));
|
||||
assert_eq!(
|
||||
Instant::from_millis(4) + Duration::from_millis(6),
|
||||
Instant::from_millis(10)
|
||||
);
|
||||
// std::ops::Sub
|
||||
assert_eq!(Instant::from_millis(7) - Duration::from_millis(5), Instant::from_millis(2));
|
||||
assert_eq!(
|
||||
Instant::from_millis(7) - Duration::from_millis(5),
|
||||
Instant::from_millis(2)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -306,19 +320,30 @@ mod test {
|
|||
#[cfg(feature = "std")]
|
||||
fn test_instant_conversions() {
|
||||
let mut epoc: ::std::time::SystemTime = Instant::from_millis(0).into();
|
||||
assert_eq!(Instant::from(::std::time::UNIX_EPOCH),
|
||||
Instant::from_millis(0));
|
||||
assert_eq!(
|
||||
Instant::from(::std::time::UNIX_EPOCH),
|
||||
Instant::from_millis(0)
|
||||
);
|
||||
assert_eq!(epoc, ::std::time::UNIX_EPOCH);
|
||||
epoc = Instant::from_millis(2085955200i64 * 1000).into();
|
||||
assert_eq!(epoc, ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200));
|
||||
assert_eq!(
|
||||
epoc,
|
||||
::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_duration_ops() {
|
||||
// std::ops::Add
|
||||
assert_eq!(Duration::from_millis(40) + Duration::from_millis(2), Duration::from_millis(42));
|
||||
assert_eq!(
|
||||
Duration::from_millis(40) + Duration::from_millis(2),
|
||||
Duration::from_millis(42)
|
||||
);
|
||||
// std::ops::Sub
|
||||
assert_eq!(Duration::from_millis(555) - Duration::from_millis(42), Duration::from_millis(513));
|
||||
assert_eq!(
|
||||
Duration::from_millis(555) - Duration::from_millis(42),
|
||||
Duration::from_millis(513)
|
||||
);
|
||||
// std::ops::Mul
|
||||
assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286));
|
||||
// std::ops::Div
|
||||
|
|
138
src/wire/arp.rs
138
src/wire/arp.rs
|
@ -1,5 +1,5 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::fmt;
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
|
@ -24,7 +24,7 @@ enum_with_unknown! {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
mod field {
|
||||
|
@ -263,7 +263,7 @@ pub enum Repr {
|
|||
source_hardware_addr: EthernetAddress,
|
||||
source_protocol_addr: Ipv4Address,
|
||||
target_hardware_addr: EthernetAddress,
|
||||
target_protocol_addr: Ipv4Address
|
||||
target_protocol_addr: Ipv4Address,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -271,22 +271,20 @@ impl Repr {
|
|||
/// Parse an Address Resolution Protocol packet and return a high-level representation,
|
||||
/// or return `Err(Error::Unrecognized)` if the packet is not recognized.
|
||||
pub fn parse<T: AsRef<[u8]>>(packet: &Packet<T>) -> Result<Repr> {
|
||||
match (packet.hardware_type(), packet.protocol_type(),
|
||||
packet.hardware_len(), packet.protocol_len()) {
|
||||
(Hardware::Ethernet, Protocol::Ipv4, 6, 4) => {
|
||||
Ok(Repr::EthernetIpv4 {
|
||||
match (
|
||||
packet.hardware_type(),
|
||||
packet.protocol_type(),
|
||||
packet.hardware_len(),
|
||||
packet.protocol_len(),
|
||||
) {
|
||||
(Hardware::Ethernet, Protocol::Ipv4, 6, 4) => Ok(Repr::EthernetIpv4 {
|
||||
operation: packet.operation(),
|
||||
source_hardware_addr:
|
||||
EthernetAddress::from_bytes(packet.source_hardware_addr()),
|
||||
source_protocol_addr:
|
||||
Ipv4Address::from_bytes(packet.source_protocol_addr()),
|
||||
target_hardware_addr:
|
||||
EthernetAddress::from_bytes(packet.target_hardware_addr()),
|
||||
target_protocol_addr:
|
||||
Ipv4Address::from_bytes(packet.target_protocol_addr())
|
||||
})
|
||||
},
|
||||
_ => Err(Error::Unrecognized)
|
||||
source_hardware_addr: EthernetAddress::from_bytes(packet.source_hardware_addr()),
|
||||
source_protocol_addr: Ipv4Address::from_bytes(packet.source_protocol_addr()),
|
||||
target_hardware_addr: EthernetAddress::from_bytes(packet.target_hardware_addr()),
|
||||
target_protocol_addr: Ipv4Address::from_bytes(packet.target_protocol_addr()),
|
||||
}),
|
||||
_ => Err(Error::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,8 +300,10 @@ impl Repr {
|
|||
match *self {
|
||||
Repr::EthernetIpv4 {
|
||||
operation,
|
||||
source_hardware_addr, source_protocol_addr,
|
||||
target_hardware_addr, target_protocol_addr
|
||||
source_hardware_addr,
|
||||
source_protocol_addr,
|
||||
target_hardware_addr,
|
||||
target_protocol_addr,
|
||||
} => {
|
||||
packet.set_hardware_type(Hardware::Ethernet);
|
||||
packet.set_protocol_type(Protocol::Ipv4);
|
||||
|
@ -314,7 +314,7 @@ impl Repr {
|
|||
packet.set_source_protocol_addr(source_protocol_addr.as_bytes());
|
||||
packet.set_target_hardware_addr(target_hardware_addr.as_bytes());
|
||||
packet.set_target_protocol_addr(target_protocol_addr.as_bytes());
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -325,13 +325,23 @@ impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
|
|||
Ok(repr) => write!(f, "{}", repr),
|
||||
_ => {
|
||||
write!(f, "ARP (unrecognized)")?;
|
||||
write!(f, " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
|
||||
self.hardware_type(), self.protocol_type(),
|
||||
self.hardware_len(), self.protocol_len(),
|
||||
self.operation())?;
|
||||
write!(f, " sha={:?} spa={:?} tha={:?} tpa={:?}",
|
||||
self.source_hardware_addr(), self.source_protocol_addr(),
|
||||
self.target_hardware_addr(), self.target_protocol_addr())?;
|
||||
write!(
|
||||
f,
|
||||
" htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
|
||||
self.hardware_type(),
|
||||
self.protocol_type(),
|
||||
self.hardware_len(),
|
||||
self.protocol_len(),
|
||||
self.operation()
|
||||
)?;
|
||||
write!(
|
||||
f,
|
||||
" sha={:?} spa={:?} tha={:?} tpa={:?}",
|
||||
self.source_hardware_addr(),
|
||||
self.source_protocol_addr(),
|
||||
self.target_hardware_addr(),
|
||||
self.target_protocol_addr()
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -343,26 +353,36 @@ impl fmt::Display for Repr {
|
|||
match *self {
|
||||
Repr::EthernetIpv4 {
|
||||
operation,
|
||||
source_hardware_addr, source_protocol_addr,
|
||||
target_hardware_addr, target_protocol_addr
|
||||
source_hardware_addr,
|
||||
source_protocol_addr,
|
||||
target_hardware_addr,
|
||||
target_protocol_addr,
|
||||
} => {
|
||||
write!(f, "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}",
|
||||
source_hardware_addr, source_protocol_addr,
|
||||
target_hardware_addr, target_protocol_addr,
|
||||
operation)
|
||||
},
|
||||
write!(
|
||||
f,
|
||||
"ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}",
|
||||
source_hardware_addr,
|
||||
source_protocol_addr,
|
||||
target_hardware_addr,
|
||||
target_protocol_addr,
|
||||
operation
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
match Packet::new_checked(buffer) {
|
||||
Err(err) => write!(f, "{}({})", indent, err),
|
||||
Ok(packet) => write!(f, "{}{}", indent, packet)
|
||||
Ok(packet) => write!(f, "{}{}", indent, packet),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -371,16 +391,10 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
|||
mod test {
|
||||
use super::*;
|
||||
|
||||
static PACKET_BYTES: [u8; 28] =
|
||||
[0x00, 0x01,
|
||||
0x08, 0x00,
|
||||
0x06,
|
||||
0x04,
|
||||
0x00, 0x01,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x21, 0x22, 0x23, 0x24,
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
||||
0x41, 0x42, 0x43, 0x44];
|
||||
static PACKET_BYTES: [u8; 28] = [
|
||||
0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x21,
|
||||
0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x41, 0x42, 0x43, 0x44,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_deconstruct() {
|
||||
|
@ -390,9 +404,15 @@ mod test {
|
|||
assert_eq!(packet.hardware_len(), 6);
|
||||
assert_eq!(packet.protocol_len(), 4);
|
||||
assert_eq!(packet.operation(), Operation::Request);
|
||||
assert_eq!(packet.source_hardware_addr(), &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
|
||||
assert_eq!(
|
||||
packet.source_hardware_addr(),
|
||||
&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]
|
||||
);
|
||||
assert_eq!(packet.source_protocol_addr(), &[0x21, 0x22, 0x23, 0x24]);
|
||||
assert_eq!(packet.target_hardware_addr(), &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]);
|
||||
assert_eq!(
|
||||
packet.target_hardware_addr(),
|
||||
&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]
|
||||
);
|
||||
assert_eq!(packet.target_protocol_addr(), &[0x41, 0x42, 0x43, 0x44]);
|
||||
}
|
||||
|
||||
|
@ -415,14 +435,14 @@ mod test {
|
|||
fn packet_repr() -> Repr {
|
||||
Repr::EthernetIpv4 {
|
||||
operation: Operation::Request,
|
||||
source_hardware_addr:
|
||||
EthernetAddress::from_bytes(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]),
|
||||
source_protocol_addr:
|
||||
Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
|
||||
target_hardware_addr:
|
||||
EthernetAddress::from_bytes(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]),
|
||||
target_protocol_addr:
|
||||
Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44])
|
||||
source_hardware_addr: EthernetAddress::from_bytes(&[
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
]),
|
||||
source_protocol_addr: Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
|
||||
target_hardware_addr: EthernetAddress::from_bytes(&[
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
||||
]),
|
||||
target_protocol_addr: Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44]),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::wire::{EthernetAddress, Ipv4Address};
|
||||
use crate::wire::arp::Hardware;
|
||||
use crate::wire::{EthernetAddress, Ipv4Address};
|
||||
use crate::{Error, Result};
|
||||
|
||||
pub const SERVER_PORT: u16 = 67;
|
||||
pub const CLIENT_PORT: u16 = 68;
|
||||
|
@ -37,8 +37,11 @@ enum_with_unknown! {
|
|||
impl MessageType {
|
||||
fn opcode(&self) -> OpCode {
|
||||
match *self {
|
||||
MessageType::Discover | MessageType::Inform | MessageType::Request |
|
||||
MessageType::Decline | MessageType::Release => OpCode::Request,
|
||||
MessageType::Discover
|
||||
| MessageType::Inform
|
||||
| MessageType::Request
|
||||
| MessageType::Decline
|
||||
| MessageType::Release => OpCode::Request,
|
||||
MessageType::Offer | MessageType::Ack | MessageType::Nak => OpCode::Reply,
|
||||
MessageType::Unknown(_) => OpCode::Unknown(0),
|
||||
}
|
||||
|
@ -59,7 +62,7 @@ pub enum DhcpOption<'a> {
|
|||
Router(Ipv4Address),
|
||||
SubnetMask(Ipv4Address),
|
||||
MaximumDhcpMessageSize(u16),
|
||||
Other { kind: u8, data: &'a [u8] }
|
||||
Other { kind: u8, data: &'a [u8] },
|
||||
}
|
||||
|
||||
impl<'a> DhcpOption<'a> {
|
||||
|
@ -81,12 +84,10 @@ impl<'a> DhcpOption<'a> {
|
|||
skip_len = length + 2;
|
||||
let data = buffer.get(2..skip_len).ok_or(Error::Truncated)?;
|
||||
match (kind, length) {
|
||||
(field::OPT_END, _) |
|
||||
(field::OPT_PAD, _) =>
|
||||
unreachable!(),
|
||||
(field::OPT_END, _) | (field::OPT_PAD, _) => unreachable!(),
|
||||
(field::OPT_DHCP_MESSAGE_TYPE, 1) => {
|
||||
option = DhcpOption::MessageType(MessageType::from(data[0]));
|
||||
},
|
||||
}
|
||||
(field::OPT_REQUESTED_IP, 4) => {
|
||||
option = DhcpOption::RequestedIp(Ipv4Address::from_bytes(data));
|
||||
}
|
||||
|
@ -95,7 +96,8 @@ impl<'a> DhcpOption<'a> {
|
|||
if hardware_type != Hardware::Ethernet {
|
||||
return Err(Error::Unrecognized);
|
||||
}
|
||||
option = DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..]));
|
||||
option =
|
||||
DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..]));
|
||||
}
|
||||
(field::OPT_SERVER_IDENTIFIER, 4) => {
|
||||
option = DhcpOption::ServerIdentifier(Ipv4Address::from_bytes(data));
|
||||
|
@ -107,13 +109,20 @@ impl<'a> DhcpOption<'a> {
|
|||
option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data));
|
||||
}
|
||||
(field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
|
||||
option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]]));
|
||||
option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([
|
||||
data[0], data[1],
|
||||
]));
|
||||
}
|
||||
(field::OPT_IP_LEASE_TIME, 4) => {
|
||||
option = DhcpOption::IpLeaseTime(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
|
||||
option = DhcpOption::IpLeaseTime(u32::from_be_bytes([
|
||||
data[0], data[1], data[2], data[3],
|
||||
]))
|
||||
}
|
||||
(_, _) => {
|
||||
option = DhcpOption::Other { kind: kind, data: data };
|
||||
option = DhcpOption::Other {
|
||||
kind: kind,
|
||||
data: data,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,20 +135,14 @@ impl<'a> DhcpOption<'a> {
|
|||
&DhcpOption::EndOfList => 1,
|
||||
&DhcpOption::Pad => 1,
|
||||
&DhcpOption::MessageType(_) => 3,
|
||||
&DhcpOption::ClientIdentifier(eth_addr) => {
|
||||
3 + eth_addr.as_bytes().len()
|
||||
}
|
||||
&DhcpOption::RequestedIp(ip) |
|
||||
&DhcpOption::ServerIdentifier(ip) |
|
||||
&DhcpOption::Router(ip) |
|
||||
&DhcpOption::SubnetMask(ip) => {
|
||||
2 + ip.as_bytes().len()
|
||||
},
|
||||
&DhcpOption::MaximumDhcpMessageSize(_) => {
|
||||
4
|
||||
}
|
||||
&DhcpOption::ClientIdentifier(eth_addr) => 3 + eth_addr.as_bytes().len(),
|
||||
&DhcpOption::RequestedIp(ip)
|
||||
| &DhcpOption::ServerIdentifier(ip)
|
||||
| &DhcpOption::Router(ip)
|
||||
| &DhcpOption::SubnetMask(ip) => 2 + ip.as_bytes().len(),
|
||||
&DhcpOption::MaximumDhcpMessageSize(_) => 4,
|
||||
&DhcpOption::IpLeaseTime(_) => 6,
|
||||
&DhcpOption::Other { data, .. } => 2 + data.len()
|
||||
&DhcpOption::Other { data, .. } => 2 + data.len(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,7 +195,10 @@ impl<'a> DhcpOption<'a> {
|
|||
buffer[0] = field::OPT_IP_LEASE_TIME;
|
||||
buffer[2..6].copy_from_slice(&lease_time.to_be_bytes()[..]);
|
||||
}
|
||||
DhcpOption::Other { kind, data: provided } => {
|
||||
DhcpOption::Other {
|
||||
kind,
|
||||
data: provided,
|
||||
} => {
|
||||
buffer[0] = kind;
|
||||
buffer[2..skip_length].copy_from_slice(provided);
|
||||
}
|
||||
|
@ -207,7 +213,7 @@ impl<'a> DhcpOption<'a> {
|
|||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
pub(crate) mod field {
|
||||
|
@ -691,7 +697,7 @@ pub struct Repr<'a> {
|
|||
/// The maximum size dhcp packet the interface can receive
|
||||
pub max_size: Option<u16>,
|
||||
/// The DHCP IP lease duration, specified in seconds.
|
||||
pub lease_duration: Option<u32>
|
||||
pub lease_duration: Option<u32>,
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
|
@ -700,22 +706,39 @@ impl<'a> Repr<'a> {
|
|||
let mut len = field::OPTIONS.start;
|
||||
// message type and end-of-options options
|
||||
len += 3 + 1;
|
||||
if self.requested_ip.is_some() { len += 6; }
|
||||
if self.client_identifier.is_some() { len += 9; }
|
||||
if self.server_identifier.is_some() { len += 6; }
|
||||
if self.max_size.is_some() { len += 4; }
|
||||
if self.router.is_some() { len += 6; }
|
||||
if self.subnet_mask.is_some() { len += 6; }
|
||||
if self.lease_duration.is_some() { len += 6; }
|
||||
if let Some(list) = self.parameter_request_list { len += list.len() + 2; }
|
||||
if self.requested_ip.is_some() {
|
||||
len += 6;
|
||||
}
|
||||
if self.client_identifier.is_some() {
|
||||
len += 9;
|
||||
}
|
||||
if self.server_identifier.is_some() {
|
||||
len += 6;
|
||||
}
|
||||
if self.max_size.is_some() {
|
||||
len += 4;
|
||||
}
|
||||
if self.router.is_some() {
|
||||
len += 6;
|
||||
}
|
||||
if self.subnet_mask.is_some() {
|
||||
len += 6;
|
||||
}
|
||||
if self.lease_duration.is_some() {
|
||||
len += 6;
|
||||
}
|
||||
if let Some(list) = self.parameter_request_list {
|
||||
len += list.len() + 2;
|
||||
}
|
||||
|
||||
len
|
||||
}
|
||||
|
||||
/// Parse a DHCP packet and return a high-level representation.
|
||||
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Self>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
let transaction_id = packet.transaction_id();
|
||||
let client_hardware_address = packet.client_hardware_address();
|
||||
let client_ip = packet.client_ip();
|
||||
|
@ -753,12 +776,12 @@ impl<'a> Repr<'a> {
|
|||
let (next_options, option) = DhcpOption::parse(options)?;
|
||||
match option {
|
||||
DhcpOption::EndOfList => break,
|
||||
DhcpOption::Pad => {},
|
||||
DhcpOption::Pad => {}
|
||||
DhcpOption::MessageType(value) => {
|
||||
if value.opcode() == packet.opcode() {
|
||||
message_type = Ok(value);
|
||||
}
|
||||
},
|
||||
}
|
||||
DhcpOption::RequestedIp(ip) => {
|
||||
requested_ip = Some(ip);
|
||||
}
|
||||
|
@ -773,24 +796,30 @@ impl<'a> Repr<'a> {
|
|||
}
|
||||
DhcpOption::SubnetMask(mask) => {
|
||||
subnet_mask = Some(mask);
|
||||
},
|
||||
}
|
||||
DhcpOption::MaximumDhcpMessageSize(size) => {
|
||||
max_size = Some(size);
|
||||
}
|
||||
DhcpOption::IpLeaseTime(duration) => {
|
||||
lease_duration = Some(duration);
|
||||
}
|
||||
DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => {
|
||||
DhcpOption::Other {
|
||||
kind: field::OPT_PARAMETER_REQUEST_LIST,
|
||||
data,
|
||||
} => {
|
||||
parameter_request_list = Some(data);
|
||||
}
|
||||
DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => {
|
||||
DhcpOption::Other {
|
||||
kind: field::OPT_DOMAIN_NAME_SERVER,
|
||||
data,
|
||||
} => {
|
||||
let mut servers = [None; MAX_DNS_SERVER_COUNT];
|
||||
for (server, chunk) in servers.iter_mut().zip(data.chunks(4)) {
|
||||
*server = Some(Ipv4Address::from_bytes(chunk));
|
||||
}
|
||||
dns_servers = Some(servers);
|
||||
}
|
||||
DhcpOption::Other {..} => {}
|
||||
DhcpOption::Other { .. } => {}
|
||||
}
|
||||
options = next_options;
|
||||
}
|
||||
|
@ -798,9 +827,21 @@ impl<'a> Repr<'a> {
|
|||
let broadcast = packet.broadcast_flag();
|
||||
|
||||
Ok(Repr {
|
||||
transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip,
|
||||
broadcast, requested_ip, server_identifier, router,
|
||||
subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size,
|
||||
transaction_id,
|
||||
client_hardware_address,
|
||||
client_ip,
|
||||
your_ip,
|
||||
server_ip,
|
||||
relay_agent_ip,
|
||||
broadcast,
|
||||
requested_ip,
|
||||
server_identifier,
|
||||
router,
|
||||
subnet_mask,
|
||||
client_identifier,
|
||||
parameter_request_list,
|
||||
dns_servers,
|
||||
max_size,
|
||||
lease_duration,
|
||||
message_type: message_type?,
|
||||
})
|
||||
|
@ -809,7 +850,9 @@ impl<'a> Repr<'a> {
|
|||
/// Emit a high-level representation into a Dynamic Host
|
||||
/// Configuration Protocol packet.
|
||||
pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
|
||||
where
|
||||
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
|
||||
{
|
||||
packet.set_sname_and_boot_file_to_zero();
|
||||
packet.set_opcode(self.message_type.opcode());
|
||||
packet.set_hardware_type(Hardware::Ethernet);
|
||||
|
@ -850,7 +893,11 @@ impl<'a> Repr<'a> {
|
|||
options = DhcpOption::IpLeaseTime(duration).emit(options);
|
||||
}
|
||||
if let Some(list) = self.parameter_request_list {
|
||||
options = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list }.emit(options);
|
||||
options = DhcpOption::Other {
|
||||
kind: field::OPT_PARAMETER_REQUEST_LIST,
|
||||
data: list,
|
||||
}
|
||||
.emit(options);
|
||||
}
|
||||
DhcpOption::EndOfList.emit(options);
|
||||
}
|
||||
|
@ -861,75 +908,80 @@ impl<'a> Repr<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::wire::Ipv4Address;
|
||||
use super::*;
|
||||
use crate::wire::Ipv4Address;
|
||||
|
||||
const MAGIC_COOKIE: u32 = 0x63825363;
|
||||
|
||||
static DISCOVER_BYTES: &[u8] = &[
|
||||
0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x82, 0x01,
|
||||
0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
|
||||
0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00, 0x00,
|
||||
0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
|
||||
0x82, 0x01, 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
|
||||
0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00,
|
||||
0x00, 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
static ACK_DNS_SERVER_BYTES: &[u8] = &[
|
||||
0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06, 0x91,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17, 0xeb, 0xc9,
|
||||
0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
|
||||
0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x2b,
|
||||
0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68, 0x79, 0x73,
|
||||
0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00, 0x03, 0x04, 0x0a,
|
||||
0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0xa3, 0x01, 0x4a,
|
||||
0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0xa3,
|
||||
0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08, 0xff
|
||||
0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06,
|
||||
0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17,
|
||||
0xeb, 0xc9, 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
|
||||
0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00,
|
||||
0x2b, 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68,
|
||||
0x79, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00,
|
||||
0x03, 0x04, 0x0a, 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a,
|
||||
0x07, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03,
|
||||
0xa3, 0x01, 0x4a, 0x04, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08,
|
||||
0xff,
|
||||
];
|
||||
|
||||
static ACK_LEASE_TIME_BYTES: &[u8] = &[
|
||||
0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91, 0x62, 0xd2,
|
||||
0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
|
||||
0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56, 0x01,
|
||||
0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91,
|
||||
0x62, 0xd2, 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
|
||||
0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56,
|
||||
0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
|
||||
const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
|
||||
|
@ -971,9 +1023,13 @@ mod test {
|
|||
assert_eq!(options.len(), 6 + 1 + 7);
|
||||
|
||||
let (options, client_id) = DhcpOption::parse(options).unwrap();
|
||||
assert_eq!(client_id, DhcpOption::Other {
|
||||
kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42]
|
||||
});
|
||||
assert_eq!(
|
||||
client_id,
|
||||
DhcpOption::Other {
|
||||
kind: field::OPT_PARAMETER_REQUEST_LIST,
|
||||
data: &[1, 3, 6, 42]
|
||||
}
|
||||
);
|
||||
assert_eq!(options.len(), 1 + 7);
|
||||
|
||||
let (options, client_id) = DhcpOption::parse(options).unwrap();
|
||||
|
@ -1007,7 +1063,8 @@ mod test {
|
|||
options = DhcpOption::RequestedIp(IP_NULL).emit(options);
|
||||
options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(options);
|
||||
let option = DhcpOption::Other {
|
||||
kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42],
|
||||
kind: field::OPT_PARAMETER_REQUEST_LIST,
|
||||
data: &[1, 3, 6, 42],
|
||||
};
|
||||
options = option.emit(options);
|
||||
DhcpOption::EndOfList.emit(options);
|
||||
|
@ -1106,7 +1163,10 @@ mod test {
|
|||
let rest = dhcp_option.emit(&mut bytes);
|
||||
assert_eq!(rest.len(), 0);
|
||||
}
|
||||
assert_eq!(&bytes[0..2], &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]);
|
||||
assert_eq!(
|
||||
&bytes[0..2],
|
||||
&[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]
|
||||
);
|
||||
assert_eq!(&bytes[2..], DATA);
|
||||
}
|
||||
|
||||
|
@ -1118,10 +1178,14 @@ mod test {
|
|||
// The packet described by ACK_BYTES advertises 4 DNS servers
|
||||
// Here we ensure that we correctly parse the first 3 into our fixed
|
||||
// length-3 array (see issue #305)
|
||||
assert_eq!(repr.dns_servers, Some([
|
||||
assert_eq!(
|
||||
repr.dns_servers,
|
||||
Some([
|
||||
Some(Ipv4Address([163, 1, 74, 6])),
|
||||
Some(Ipv4Address([163, 1, 74, 7])),
|
||||
Some(Ipv4Address([163, 1, 74, 3]))]));
|
||||
Some(Ipv4Address([163, 1, 74, 3]))
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::fmt;
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
|
@ -18,7 +18,7 @@ impl fmt::Display for EtherType {
|
|||
EtherType::Ipv4 => write!(f, "IPv4"),
|
||||
EtherType::Ipv6 => write!(f, "IPv6"),
|
||||
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.
|
||||
pub fn is_unicast(&self) -> bool {
|
||||
!(self.is_broadcast() ||
|
||||
self.is_multicast())
|
||||
!(self.is_broadcast() || self.is_multicast())
|
||||
}
|
||||
|
||||
/// Query whether this address is the broadcast address.
|
||||
|
@ -72,8 +71,11 @@ impl Address {
|
|||
impl fmt::Display for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.0;
|
||||
write!(f, "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
|
||||
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5])
|
||||
write!(
|
||||
f,
|
||||
"{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
|
||||
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +83,7 @@ impl fmt::Display for Address {
|
|||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Frame<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
mod field {
|
||||
|
@ -209,19 +211,27 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Frame<T> {
|
|||
|
||||
impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "EthernetII src={} dst={} type={}",
|
||||
self.src_addr(), self.dst_addr(), self.ethertype())
|
||||
write!(
|
||||
f,
|
||||
"EthernetII src={} dst={} type={}",
|
||||
self.src_addr(),
|
||||
self.dst_addr(),
|
||||
self.ethertype()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
let frame = match Frame::new_checked(buffer) {
|
||||
Err(err) => return write!(f, "{}({})", indent, err),
|
||||
Ok(frame) => frame
|
||||
Ok(frame) => frame,
|
||||
};
|
||||
write!(f, "{}{}", indent, frame)?;
|
||||
|
||||
|
@ -241,7 +251,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
|
|||
indent.increase(f)?;
|
||||
super::Ipv6Packet::<&[u8]>::pretty_print(&frame.payload(), f, indent)
|
||||
}
|
||||
_ => Ok(())
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -300,32 +310,32 @@ mod test_ipv4 {
|
|||
// Tests that are valid only with "proto-ipv4"
|
||||
use super::*;
|
||||
|
||||
static FRAME_BYTES: [u8; 64] =
|
||||
[0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x08, 0x00,
|
||||
0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xff];
|
||||
static FRAME_BYTES: [u8; 64] = [
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x00, 0xaa,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
static PAYLOAD_BYTES: [u8; 50] =
|
||||
[0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xff];
|
||||
static PAYLOAD_BYTES: [u8; 50] = [
|
||||
0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_deconstruct() {
|
||||
let frame = Frame::new_unchecked(&FRAME_BYTES[..]);
|
||||
assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
|
||||
assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
|
||||
assert_eq!(
|
||||
frame.dst_addr(),
|
||||
Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
|
||||
);
|
||||
assert_eq!(
|
||||
frame.src_addr(),
|
||||
Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
|
||||
);
|
||||
assert_eq!(frame.ethertype(), EtherType::Ipv4);
|
||||
assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
|
||||
}
|
||||
|
@ -348,28 +358,30 @@ mod test_ipv6 {
|
|||
// Tests that are valid only with "proto-ipv6"
|
||||
use super::*;
|
||||
|
||||
static FRAME_BYTES: [u8; 54] =
|
||||
[0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
||||
0x86, 0xdd,
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
|
||||
static FRAME_BYTES: [u8; 54] = [
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x86, 0xdd, 0x60,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
];
|
||||
|
||||
static PAYLOAD_BYTES: [u8; 40] =
|
||||
[0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
|
||||
static PAYLOAD_BYTES: [u8; 40] = [
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_deconstruct() {
|
||||
let frame = Frame::new_unchecked(&FRAME_BYTES[..]);
|
||||
assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
|
||||
assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
|
||||
assert_eq!(
|
||||
frame.dst_addr(),
|
||||
Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
|
||||
);
|
||||
assert_eq!(
|
||||
frame.src_addr(),
|
||||
Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
|
||||
);
|
||||
assert_eq!(frame.ethertype(), EtherType::Ipv6);
|
||||
assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use core::{cmp, fmt};
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::{cmp, fmt};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::wire::ip::checksum;
|
||||
use crate::wire::{Ipv4Packet, Ipv4Repr};
|
||||
use crate::{Error, Result};
|
||||
|
||||
enum_with_unknown! {
|
||||
/// Internet protocol control message type.
|
||||
|
@ -45,7 +45,7 @@ impl fmt::Display for Message {
|
|||
Message::ParamProblem => write!(f, "parameter problem"),
|
||||
Message::Timestamp => write!(f, "timestamp"),
|
||||
Message::TimestampReply => write!(f, "timestamp reply"),
|
||||
Message::Unknown(id) => write!(f, "{}", id)
|
||||
Message::Unknown(id) => write!(f, "{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,40 +91,25 @@ enum_with_unknown! {
|
|||
impl fmt::Display for DstUnreachable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
DstUnreachable::NetUnreachable =>
|
||||
write!(f, "destination network unreachable"),
|
||||
DstUnreachable::HostUnreachable =>
|
||||
write!(f, "destination host unreachable"),
|
||||
DstUnreachable::ProtoUnreachable =>
|
||||
write!(f, "destination protocol unreachable"),
|
||||
DstUnreachable::PortUnreachable =>
|
||||
write!(f, "destination port unreachable"),
|
||||
DstUnreachable::FragRequired =>
|
||||
write!(f, "fragmentation required, and DF flag set"),
|
||||
DstUnreachable::SrcRouteFailed =>
|
||||
write!(f, "source route failed"),
|
||||
DstUnreachable::DstNetUnknown =>
|
||||
write!(f, "destination network unknown"),
|
||||
DstUnreachable::DstHostUnknown =>
|
||||
write!(f, "destination host unknown"),
|
||||
DstUnreachable::SrcHostIsolated =>
|
||||
write!(f, "source host isolated"),
|
||||
DstUnreachable::NetProhibited =>
|
||||
write!(f, "network administratively prohibited"),
|
||||
DstUnreachable::HostProhibited =>
|
||||
write!(f, "host administratively prohibited"),
|
||||
DstUnreachable::NetUnreachToS =>
|
||||
write!(f, "network unreachable for ToS"),
|
||||
DstUnreachable::HostUnreachToS =>
|
||||
write!(f, "host unreachable for ToS"),
|
||||
DstUnreachable::CommProhibited =>
|
||||
write!(f, "communication administratively prohibited"),
|
||||
DstUnreachable::HostPrecedViol =>
|
||||
write!(f, "host precedence violation"),
|
||||
DstUnreachable::PrecedCutoff =>
|
||||
write!(f, "precedence cutoff in effect"),
|
||||
DstUnreachable::Unknown(id) =>
|
||||
write!(f, "{}", id)
|
||||
DstUnreachable::NetUnreachable => write!(f, "destination network unreachable"),
|
||||
DstUnreachable::HostUnreachable => write!(f, "destination host unreachable"),
|
||||
DstUnreachable::ProtoUnreachable => write!(f, "destination protocol unreachable"),
|
||||
DstUnreachable::PortUnreachable => write!(f, "destination port unreachable"),
|
||||
DstUnreachable::FragRequired => write!(f, "fragmentation required, and DF flag set"),
|
||||
DstUnreachable::SrcRouteFailed => write!(f, "source route failed"),
|
||||
DstUnreachable::DstNetUnknown => write!(f, "destination network unknown"),
|
||||
DstUnreachable::DstHostUnknown => write!(f, "destination host unknown"),
|
||||
DstUnreachable::SrcHostIsolated => write!(f, "source host isolated"),
|
||||
DstUnreachable::NetProhibited => write!(f, "network administratively prohibited"),
|
||||
DstUnreachable::HostProhibited => write!(f, "host administratively prohibited"),
|
||||
DstUnreachable::NetUnreachToS => write!(f, "network unreachable for ToS"),
|
||||
DstUnreachable::HostUnreachToS => write!(f, "host unreachable for ToS"),
|
||||
DstUnreachable::CommProhibited => {
|
||||
write!(f, "communication administratively prohibited")
|
||||
}
|
||||
DstUnreachable::HostPrecedViol => write!(f, "host precedence violation"),
|
||||
DstUnreachable::PrecedCutoff => write!(f, "precedence cutoff in effect"),
|
||||
DstUnreachable::Unknown(id) => write!(f, "{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +154,7 @@ enum_with_unknown! {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
mod field {
|
||||
|
@ -271,7 +256,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
Message::EchoRequest => field::ECHO_SEQNO.end,
|
||||
Message::EchoReply => field::ECHO_SEQNO.end,
|
||||
Message::DstUnreachable => field::UNUSED.end,
|
||||
_ => field::UNUSED.end // make a conservative assumption
|
||||
_ => field::UNUSED.end, // make a conservative assumption
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -280,7 +265,9 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
/// # Fuzzing
|
||||
/// This function always returns `true` when fuzzing.
|
||||
pub fn verify_checksum(&self) -> bool {
|
||||
if cfg!(fuzzing) { return true }
|
||||
if cfg!(fuzzing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let data = self.buffer.as_ref();
|
||||
checksum::data(data) == !0
|
||||
|
@ -373,45 +360,47 @@ pub enum Repr<'a> {
|
|||
EchoRequest {
|
||||
ident: u16,
|
||||
seq_no: u16,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
EchoReply {
|
||||
ident: u16,
|
||||
seq_no: u16,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
DstUnreachable {
|
||||
reason: DstUnreachable,
|
||||
header: Ipv4Repr,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an Internet Control Message Protocol version 4 packet and return
|
||||
/// a high-level representation.
|
||||
pub fn parse<T>(packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities)
|
||||
-> Result<Repr<'a>>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(
|
||||
packet: &Packet<&'a T>,
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) -> Result<Repr<'a>>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
// Valid checksum is expected.
|
||||
if checksum_caps.icmpv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) }
|
||||
if checksum_caps.icmpv4.rx() && !packet.verify_checksum() {
|
||||
return Err(Error::Checksum);
|
||||
}
|
||||
|
||||
match (packet.msg_type(), packet.msg_code()) {
|
||||
(Message::EchoRequest, 0) => {
|
||||
Ok(Repr::EchoRequest {
|
||||
(Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
|
||||
ident: packet.echo_ident(),
|
||||
seq_no: packet.echo_seq_no(),
|
||||
data: packet.data()
|
||||
})
|
||||
},
|
||||
data: packet.data(),
|
||||
}),
|
||||
|
||||
(Message::EchoReply, 0) => {
|
||||
Ok(Repr::EchoReply {
|
||||
(Message::EchoReply, 0) => Ok(Repr::EchoReply {
|
||||
ident: packet.echo_ident(),
|
||||
seq_no: packet.echo_seq_no(),
|
||||
data: packet.data()
|
||||
})
|
||||
},
|
||||
data: packet.data(),
|
||||
}),
|
||||
|
||||
(Message::DstUnreachable, code) => {
|
||||
let ip_packet = Ipv4Packet::new_checked(packet.data())?;
|
||||
|
@ -419,7 +408,9 @@ impl<'a> Repr<'a> {
|
|||
let payload = &packet.data()[ip_packet.header_len() as usize..];
|
||||
// RFC 792 requires exactly eight bytes to be returned.
|
||||
// We allow more, since there isn't a reason not to, but require at least eight.
|
||||
if payload.len() < 8 { return Err(Error::Truncated) }
|
||||
if payload.len() < 8 {
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
|
||||
Ok(Repr::DstUnreachable {
|
||||
reason: DstUnreachable::from(code),
|
||||
|
@ -428,22 +419,21 @@ impl<'a> Repr<'a> {
|
|||
dst_addr: ip_packet.dst_addr(),
|
||||
protocol: ip_packet.protocol(),
|
||||
payload_len: payload.len(),
|
||||
hop_limit: ip_packet.hop_limit()
|
||||
hop_limit: ip_packet.hop_limit(),
|
||||
},
|
||||
data: payload
|
||||
data: payload,
|
||||
})
|
||||
}
|
||||
_ => Err(Error::Unrecognized)
|
||||
_ => Err(Error::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the length of a packet that will be emitted from this high-level representation.
|
||||
pub fn buffer_len(&self) -> usize {
|
||||
match self {
|
||||
&Repr::EchoRequest { data, .. } |
|
||||
&Repr::EchoReply { data, .. } => {
|
||||
&Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
|
||||
field::ECHO_SEQNO.end + data.len()
|
||||
},
|
||||
}
|
||||
&Repr::DstUnreachable { header, data, .. } => {
|
||||
field::UNUSED.end + header.buffer_len() + data.len()
|
||||
}
|
||||
|
@ -453,35 +443,49 @@ impl<'a> Repr<'a> {
|
|||
/// Emit a high-level representation into an Internet Control Message Protocol version 4
|
||||
/// packet.
|
||||
pub fn emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
|
||||
where
|
||||
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
|
||||
{
|
||||
packet.set_msg_code(0);
|
||||
match *self {
|
||||
Repr::EchoRequest { ident, seq_no, data } => {
|
||||
Repr::EchoRequest {
|
||||
ident,
|
||||
seq_no,
|
||||
data,
|
||||
} => {
|
||||
packet.set_msg_type(Message::EchoRequest);
|
||||
packet.set_msg_code(0);
|
||||
packet.set_echo_ident(ident);
|
||||
packet.set_echo_seq_no(seq_no);
|
||||
let data_len = cmp::min(packet.data_mut().len(), data.len());
|
||||
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
|
||||
},
|
||||
}
|
||||
|
||||
Repr::EchoReply { ident, seq_no, data } => {
|
||||
Repr::EchoReply {
|
||||
ident,
|
||||
seq_no,
|
||||
data,
|
||||
} => {
|
||||
packet.set_msg_type(Message::EchoReply);
|
||||
packet.set_msg_code(0);
|
||||
packet.set_echo_ident(ident);
|
||||
packet.set_echo_seq_no(seq_no);
|
||||
let data_len = cmp::min(packet.data_mut().len(), data.len());
|
||||
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
|
||||
},
|
||||
}
|
||||
|
||||
Repr::DstUnreachable { reason, header, data } => {
|
||||
Repr::DstUnreachable {
|
||||
reason,
|
||||
header,
|
||||
data,
|
||||
} => {
|
||||
packet.set_msg_type(Message::DstUnreachable);
|
||||
packet.set_msg_code(reason.into());
|
||||
|
||||
let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut());
|
||||
header.emit(&mut ip_packet, checksum_caps);
|
||||
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, " type={:?}", self.msg_type())?;
|
||||
match self.msg_type() {
|
||||
Message::DstUnreachable =>
|
||||
write!(f, " code={:?}", DstUnreachable::from(self.msg_code())),
|
||||
_ => write!(f, " code={}", self.msg_code())
|
||||
Message::DstUnreachable => {
|
||||
write!(f, " code={:?}", DstUnreachable::from(self.msg_code()))
|
||||
}
|
||||
_ => write!(f, " code={}", self.msg_code()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -515,27 +520,46 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
|||
impl<'a> fmt::Display for Repr<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Repr::EchoRequest { ident, seq_no, data } =>
|
||||
write!(f, "ICMPv4 echo request id={} seq={} len={}",
|
||||
ident, seq_no, data.len()),
|
||||
Repr::EchoReply { ident, seq_no, data } =>
|
||||
write!(f, "ICMPv4 echo reply id={} seq={} len={}",
|
||||
ident, seq_no, data.len()),
|
||||
Repr::DstUnreachable { reason, .. } =>
|
||||
write!(f, "ICMPv4 destination unreachable ({})",
|
||||
reason),
|
||||
Repr::EchoRequest {
|
||||
ident,
|
||||
seq_no,
|
||||
data,
|
||||
} => write!(
|
||||
f,
|
||||
"ICMPv4 echo request id={} seq={} len={}",
|
||||
ident,
|
||||
seq_no,
|
||||
data.len()
|
||||
),
|
||||
Repr::EchoReply {
|
||||
ident,
|
||||
seq_no,
|
||||
data,
|
||||
} => write!(
|
||||
f,
|
||||
"ICMPv4 echo reply id={} seq={} len={}",
|
||||
ident,
|
||||
seq_no,
|
||||
data.len()
|
||||
),
|
||||
Repr::DstUnreachable { reason, .. } => {
|
||||
write!(f, "ICMPv4 destination unreachable ({})", reason)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
let packet = match Packet::new_checked(buffer) {
|
||||
Err(err) => return write!(f, "{}({})", indent, err),
|
||||
Ok(packet) => packet
|
||||
Ok(packet) => packet,
|
||||
};
|
||||
write!(f, "{}{}", indent, packet)?;
|
||||
|
||||
|
@ -544,7 +568,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
|||
indent.increase(f)?;
|
||||
super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent)
|
||||
}
|
||||
_ => Ok(())
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -553,13 +577,11 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
|||
mod test {
|
||||
use super::*;
|
||||
|
||||
static ECHO_PACKET_BYTES: [u8; 12] =
|
||||
[0x08, 0x00, 0x8e, 0xfe,
|
||||
0x12, 0x34, 0xab, 0xcd,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static ECHO_PACKET_BYTES: [u8; 12] = [
|
||||
0x08, 0x00, 0x8e, 0xfe, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
static ECHO_DATA_BYTES: [u8; 4] =
|
||||
[0xaa, 0x00, 0x00, 0xff];
|
||||
static ECHO_DATA_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
|
||||
|
||||
#[test]
|
||||
fn test_echo_deconstruct() {
|
||||
|
@ -590,7 +612,7 @@ mod test {
|
|||
Repr::EchoRequest {
|
||||
ident: 0x1234,
|
||||
seq_no: 0xabcd,
|
||||
data: &ECHO_DATA_BYTES
|
||||
data: &ECHO_DATA_BYTES,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -612,8 +634,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_check_len() {
|
||||
let bytes = [0x0b, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00];
|
||||
let bytes = [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
assert_eq!(Packet::new_checked(&[]), Err(Error::Truncated));
|
||||
assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error::Truncated));
|
||||
assert!(Packet::new_checked(&bytes[..]).is_ok());
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use core::{cmp, fmt};
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::{cmp, fmt};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::wire::ip::checksum;
|
||||
use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
|
||||
use crate::wire::MldRepr;
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
use crate::wire::NdiscRepr;
|
||||
use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
|
||||
use crate::{Error, Result};
|
||||
|
||||
enum_with_unknown! {
|
||||
/// Internet protocol control message type.
|
||||
|
@ -57,8 +57,11 @@ impl Message {
|
|||
/// [NDISC]: https://tools.ietf.org/html/rfc4861
|
||||
pub fn is_ndisc(&self) -> bool {
|
||||
match *self {
|
||||
Message::RouterSolicit | Message::RouterAdvert | Message::NeighborSolicit |
|
||||
Message::NeighborAdvert | Message::Redirect => true,
|
||||
Message::RouterSolicit
|
||||
| Message::RouterAdvert
|
||||
| Message::NeighborSolicit
|
||||
| Message::NeighborAdvert
|
||||
| Message::Redirect => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +94,7 @@ impl fmt::Display for Message {
|
|||
Message::Redirect => write!(f, "redirect"),
|
||||
Message::MldQuery => write!(f, "multicast listener query"),
|
||||
Message::MldReport => write!(f, "multicast listener report"),
|
||||
Message::Unknown(id) => write!(f, "{}", id)
|
||||
Message::Unknown(id) => write!(f, "{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,22 +122,19 @@ enum_with_unknown! {
|
|||
impl fmt::Display for DstUnreachable {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
DstUnreachable::NoRoute =>
|
||||
write!(f, "no route to destination"),
|
||||
DstUnreachable::AdminProhibit =>
|
||||
write!(f, "communication with destination administratively prohibited"),
|
||||
DstUnreachable::BeyondScope =>
|
||||
write!(f, "beyond scope of source address"),
|
||||
DstUnreachable::AddrUnreachable =>
|
||||
write!(f, "address unreachable"),
|
||||
DstUnreachable::PortUnreachable =>
|
||||
write!(f, "port unreachable"),
|
||||
DstUnreachable::FailedPolicy =>
|
||||
write!(f, "source address failed ingress/egress policy"),
|
||||
DstUnreachable::RejectRoute =>
|
||||
write!(f, "reject route to destination"),
|
||||
DstUnreachable::Unknown(id) =>
|
||||
write!(f, "{}", id)
|
||||
DstUnreachable::NoRoute => write!(f, "no route to destination"),
|
||||
DstUnreachable::AdminProhibit => write!(
|
||||
f,
|
||||
"communication with destination administratively prohibited"
|
||||
),
|
||||
DstUnreachable::BeyondScope => write!(f, "beyond scope of source address"),
|
||||
DstUnreachable::AddrUnreachable => write!(f, "address unreachable"),
|
||||
DstUnreachable::PortUnreachable => write!(f, "port unreachable"),
|
||||
DstUnreachable::FailedPolicy => {
|
||||
write!(f, "source address failed ingress/egress policy")
|
||||
}
|
||||
DstUnreachable::RejectRoute => write!(f, "reject route to destination"),
|
||||
DstUnreachable::Unknown(id) => write!(f, "{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -154,14 +154,10 @@ enum_with_unknown! {
|
|||
impl fmt::Display for ParamProblem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ParamProblem::ErroneousHdrField =>
|
||||
write!(f, "erroneous header field."),
|
||||
ParamProblem::UnrecognizedNxtHdr =>
|
||||
write!(f, "unrecognized next header type."),
|
||||
ParamProblem::UnrecognizedOption =>
|
||||
write!(f, "unrecognized IPv6 option."),
|
||||
ParamProblem::Unknown(id) =>
|
||||
write!(f, "{}", id)
|
||||
ParamProblem::ErroneousHdrField => write!(f, "erroneous header field."),
|
||||
ParamProblem::UnrecognizedNxtHdr => write!(f, "unrecognized next header type."),
|
||||
ParamProblem::UnrecognizedOption => write!(f, "unrecognized IPv6 option."),
|
||||
ParamProblem::Unknown(id) => write!(f, "{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,12 +175,9 @@ enum_with_unknown! {
|
|||
impl fmt::Display for TimeExceeded {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
TimeExceeded::HopLimitExceeded =>
|
||||
write!(f, "hop limit exceeded in transit"),
|
||||
TimeExceeded::FragReassemExceeded =>
|
||||
write!(f, "fragment reassembly time exceeded"),
|
||||
TimeExceeded::Unknown(id) =>
|
||||
write!(f, "{}", id)
|
||||
TimeExceeded::HopLimitExceeded => write!(f, "hop limit exceeded in transit"),
|
||||
TimeExceeded::FragReassemExceeded => write!(f, "fragment reassembly time exceeded"),
|
||||
TimeExceeded::Unknown(id) => write!(f, "{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +186,7 @@ impl fmt::Display for TimeExceeded {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
pub(super) buffer: T
|
||||
pub(super) buffer: T,
|
||||
}
|
||||
|
||||
// Ranges and constants describing key boundaries in the ICMPv6 header.
|
||||
|
@ -333,7 +326,6 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
NetworkEndian::read_u32(&data[field::POINTER])
|
||||
}
|
||||
|
||||
|
||||
/// Return the header length. The result depends on the value of
|
||||
/// the message type field.
|
||||
pub fn header_len(&self) -> usize {
|
||||
|
@ -355,7 +347,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
// include the last 32 bits of the ICMPv6 header in
|
||||
// `header_bytes`. This must be done so that these bytes
|
||||
// can be accessed in the `payload`.
|
||||
_ => field::CHECKSUM.end
|
||||
_ => field::CHECKSUM.end,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -364,13 +356,14 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
/// # Fuzzing
|
||||
/// This function always returns `true` when fuzzing.
|
||||
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
|
||||
if cfg!(fuzzing) { return true }
|
||||
if cfg!(fuzzing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let data = self.buffer.as_ref();
|
||||
checksum::combine(&[
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6,
|
||||
data.len() as u32),
|
||||
checksum::data(data)
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
|
||||
checksum::data(data),
|
||||
]) == !0
|
||||
}
|
||||
}
|
||||
|
@ -409,16 +402,18 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
#[inline]
|
||||
pub fn clear_reserved(&mut self) {
|
||||
match self.msg_type() {
|
||||
Message::RouterSolicit | Message::NeighborSolicit |
|
||||
Message::NeighborAdvert | Message::Redirect => {
|
||||
Message::RouterSolicit
|
||||
| Message::NeighborSolicit
|
||||
| Message::NeighborAdvert
|
||||
| Message::Redirect => {
|
||||
let data = self.buffer.as_mut();
|
||||
NetworkEndian::write_u32(&mut data[field::UNUSED], 0);
|
||||
},
|
||||
}
|
||||
Message::MldQuery => {
|
||||
let data = self.buffer.as_mut();
|
||||
NetworkEndian::write_u16(&mut data[field::QUERY_RESV], 0);
|
||||
data[field::SQRV] &= 0xf;
|
||||
},
|
||||
}
|
||||
Message::MldReport => {
|
||||
let data = self.buffer.as_mut();
|
||||
NetworkEndian::write_u16(&mut data[field::RECORD_RESV], 0);
|
||||
|
@ -479,9 +474,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
let checksum = {
|
||||
let data = self.buffer.as_ref();
|
||||
!checksum::combine(&[
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6,
|
||||
data.len() as u32),
|
||||
checksum::data(data)
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
|
||||
checksum::data(data),
|
||||
])
|
||||
};
|
||||
self.set_checksum(checksum)
|
||||
|
@ -510,33 +504,33 @@ pub enum Repr<'a> {
|
|||
DstUnreachable {
|
||||
reason: DstUnreachable,
|
||||
header: Ipv6Repr,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
PktTooBig {
|
||||
mtu: u32,
|
||||
header: Ipv6Repr,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
TimeExceeded {
|
||||
reason: TimeExceeded,
|
||||
header: Ipv6Repr,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
ParamProblem {
|
||||
reason: ParamProblem,
|
||||
pointer: u32,
|
||||
header: Ipv6Repr,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
EchoRequest {
|
||||
ident: u16,
|
||||
seq_no: u16,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
EchoReply {
|
||||
ident: u16,
|
||||
seq_no: u16,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
Ndisc(NdiscRepr<'a>),
|
||||
|
@ -546,29 +540,37 @@ pub enum Repr<'a> {
|
|||
impl<'a> Repr<'a> {
|
||||
/// Parse an Internet Control Message Protocol version 6 packet and return
|
||||
/// a high-level representation.
|
||||
pub fn parse<T>(src_addr: &IpAddress, dst_addr: &IpAddress,
|
||||
packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities)
|
||||
-> Result<Repr<'a>>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>)
|
||||
-> Result<(&'a [u8], Ipv6Repr)>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(
|
||||
src_addr: &IpAddress,
|
||||
dst_addr: &IpAddress,
|
||||
packet: &Packet<&'a T>,
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) -> Result<Repr<'a>>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>) -> Result<(&'a [u8], Ipv6Repr)>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
let ip_packet = Ipv6Packet::new_checked(packet.payload())?;
|
||||
|
||||
let payload = &packet.payload()[ip_packet.header_len() as usize..];
|
||||
if payload.len() < 8 { return Err(Error::Truncated) }
|
||||
if payload.len() < 8 {
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
let repr = Ipv6Repr {
|
||||
src_addr: ip_packet.src_addr(),
|
||||
dst_addr: ip_packet.dst_addr(),
|
||||
next_header: ip_packet.next_header(),
|
||||
payload_len: payload.len(),
|
||||
hop_limit: ip_packet.hop_limit()
|
||||
hop_limit: ip_packet.hop_limit(),
|
||||
};
|
||||
Ok((payload, repr))
|
||||
}
|
||||
// Valid checksum is expected.
|
||||
if checksum_caps.icmpv6.rx() && !packet.verify_checksum(src_addr, dst_addr) {
|
||||
return Err(Error::Checksum)
|
||||
return Err(Error::Checksum);
|
||||
}
|
||||
|
||||
match (packet.msg_type(), packet.msg_code()) {
|
||||
|
@ -577,149 +579,161 @@ impl<'a> Repr<'a> {
|
|||
Ok(Repr::DstUnreachable {
|
||||
reason: DstUnreachable::from(code),
|
||||
header: repr,
|
||||
data: payload
|
||||
data: payload,
|
||||
})
|
||||
},
|
||||
}
|
||||
(Message::PktTooBig, 0) => {
|
||||
let (payload, repr) = create_packet_from_payload(packet)?;
|
||||
Ok(Repr::PktTooBig {
|
||||
mtu: packet.pkt_too_big_mtu(),
|
||||
header: repr,
|
||||
data: payload
|
||||
data: payload,
|
||||
})
|
||||
},
|
||||
}
|
||||
(Message::TimeExceeded, code) => {
|
||||
let (payload, repr) = create_packet_from_payload(packet)?;
|
||||
Ok(Repr::TimeExceeded {
|
||||
reason: TimeExceeded::from(code),
|
||||
header: repr,
|
||||
data: payload
|
||||
data: payload,
|
||||
})
|
||||
},
|
||||
}
|
||||
(Message::ParamProblem, code) => {
|
||||
let (payload, repr) = create_packet_from_payload(packet)?;
|
||||
Ok(Repr::ParamProblem {
|
||||
reason: ParamProblem::from(code),
|
||||
pointer: packet.param_problem_ptr(),
|
||||
header: repr,
|
||||
data: payload
|
||||
data: payload,
|
||||
})
|
||||
},
|
||||
(Message::EchoRequest, 0) => {
|
||||
Ok(Repr::EchoRequest {
|
||||
}
|
||||
(Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
|
||||
ident: packet.echo_ident(),
|
||||
seq_no: packet.echo_seq_no(),
|
||||
data: packet.payload()
|
||||
})
|
||||
},
|
||||
(Message::EchoReply, 0) => {
|
||||
Ok(Repr::EchoReply {
|
||||
data: packet.payload(),
|
||||
}),
|
||||
(Message::EchoReply, 0) => Ok(Repr::EchoReply {
|
||||
ident: packet.echo_ident(),
|
||||
seq_no: packet.echo_seq_no(),
|
||||
data: packet.payload()
|
||||
})
|
||||
},
|
||||
data: packet.payload(),
|
||||
}),
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
(msg_type, 0) if msg_type.is_ndisc() => {
|
||||
NdiscRepr::parse(packet).map(Repr::Ndisc)
|
||||
},
|
||||
(msg_type, 0) if msg_type.is_mld() => {
|
||||
MldRepr::parse(packet).map(Repr::Mld)
|
||||
},
|
||||
_ => Err(Error::Unrecognized)
|
||||
(msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc),
|
||||
(msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld),
|
||||
_ => Err(Error::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the length of a packet that will be emitted from this high-level representation.
|
||||
pub fn buffer_len(&self) -> usize {
|
||||
match self {
|
||||
&Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } |
|
||||
&Repr::TimeExceeded { header, data, .. } | &Repr::ParamProblem { header, data, .. } => {
|
||||
&Repr::DstUnreachable { header, data, .. }
|
||||
| &Repr::PktTooBig { header, data, .. }
|
||||
| &Repr::TimeExceeded { header, data, .. }
|
||||
| &Repr::ParamProblem { header, data, .. } => {
|
||||
field::UNUSED.end + header.buffer_len() + data.len()
|
||||
}
|
||||
&Repr::EchoRequest { data, .. } |
|
||||
&Repr::EchoReply { data, .. } => {
|
||||
&Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
|
||||
field::ECHO_SEQNO.end + data.len()
|
||||
},
|
||||
}
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
&Repr::Ndisc(ndisc) => {
|
||||
ndisc.buffer_len()
|
||||
},
|
||||
&Repr::Mld(mld) => {
|
||||
mld.buffer_len()
|
||||
},
|
||||
&Repr::Ndisc(ndisc) => ndisc.buffer_len(),
|
||||
&Repr::Mld(mld) => mld.buffer_len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a high-level representation into an Internet Control Message Protocol version 6
|
||||
/// packet.
|
||||
pub fn emit<T>(&self, src_addr: &IpAddress, dst_addr: &IpAddress,
|
||||
packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
|
||||
pub fn emit<T>(
|
||||
&self,
|
||||
src_addr: &IpAddress,
|
||||
dst_addr: &IpAddress,
|
||||
packet: &mut Packet<&mut T>,
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) where
|
||||
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
|
||||
{
|
||||
fn emit_contained_packet(buffer: &mut [u8], header: Ipv6Repr, data: &[u8]) {
|
||||
let mut ip_packet = Ipv6Packet::new_unchecked(buffer);
|
||||
header.emit(&mut ip_packet);
|
||||
let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
|
||||
payload.copy_from_slice(&data[..]);
|
||||
payload.copy_from_slice(data);
|
||||
}
|
||||
|
||||
match *self {
|
||||
Repr::DstUnreachable { reason, header, data } => {
|
||||
Repr::DstUnreachable {
|
||||
reason,
|
||||
header,
|
||||
data,
|
||||
} => {
|
||||
packet.set_msg_type(Message::DstUnreachable);
|
||||
packet.set_msg_code(reason.into());
|
||||
|
||||
emit_contained_packet(packet.payload_mut(), header, &data);
|
||||
},
|
||||
emit_contained_packet(packet.payload_mut(), header, data);
|
||||
}
|
||||
|
||||
Repr::PktTooBig { mtu, header, data } => {
|
||||
packet.set_msg_type(Message::PktTooBig);
|
||||
packet.set_msg_code(0);
|
||||
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_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_code(reason.into());
|
||||
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_code(0);
|
||||
packet.set_echo_ident(ident);
|
||||
packet.set_echo_seq_no(seq_no);
|
||||
let data_len = cmp::min(packet.payload_mut().len(), data.len());
|
||||
packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
|
||||
},
|
||||
}
|
||||
|
||||
Repr::EchoReply { ident, seq_no, data } => {
|
||||
Repr::EchoReply {
|
||||
ident,
|
||||
seq_no,
|
||||
data,
|
||||
} => {
|
||||
packet.set_msg_type(Message::EchoReply);
|
||||
packet.set_msg_code(0);
|
||||
packet.set_echo_ident(ident);
|
||||
packet.set_echo_seq_no(seq_no);
|
||||
let data_len = cmp::min(packet.payload_mut().len(), data.len());
|
||||
packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
Repr::Ndisc(ndisc) => {
|
||||
ndisc.emit(packet)
|
||||
},
|
||||
Repr::Ndisc(ndisc) => ndisc.emit(packet),
|
||||
|
||||
Repr::Mld(mld) => {
|
||||
mld.emit(packet)
|
||||
},
|
||||
Repr::Mld(mld) => mld.emit(packet),
|
||||
}
|
||||
|
||||
if checksum_caps.icmpv6.tx() {
|
||||
|
@ -733,60 +747,39 @@ impl<'a> Repr<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::wire::{Ipv6Address, Ipv6Repr, IpProtocol};
|
||||
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
|
||||
use super::*;
|
||||
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
|
||||
use crate::wire::{IpProtocol, Ipv6Address, Ipv6Repr};
|
||||
|
||||
static ECHO_PACKET_BYTES: [u8; 12] =
|
||||
[0x80, 0x00, 0x19, 0xb3,
|
||||
0x12, 0x34, 0xab, 0xcd,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static ECHO_PACKET_BYTES: [u8; 12] = [
|
||||
0x80, 0x00, 0x19, 0xb3, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
static ECHO_PACKET_PAYLOAD: [u8; 4] =
|
||||
[0xaa, 0x00, 0x00, 0xff];
|
||||
static ECHO_PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
|
||||
|
||||
static PKT_TOO_BIG_BYTES: [u8; 60] =
|
||||
[0x02, 0x00, 0x0f, 0xc9,
|
||||
0x00, 0x00, 0x05, 0xdc,
|
||||
0x60, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x11, 0x40,
|
||||
0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
0xbf, 0x00, 0x00, 0x35,
|
||||
0x00, 0x0c, 0x12, 0x4d,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static PKT_TOO_BIG_BYTES: [u8; 60] = [
|
||||
0x02, 0x00, 0x0f, 0xc9, 0x00, 0x00, 0x05, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11,
|
||||
0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] =
|
||||
[0x60, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x11, 0x40,
|
||||
0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
0xbf, 0x00, 0x00, 0x35,
|
||||
0x00, 0x0c, 0x12, 0x4d,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] = [
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00,
|
||||
0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] =
|
||||
[0xbf, 0x00, 0x00, 0x35,
|
||||
0x00, 0x0c, 0x12, 0x4d,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] = [
|
||||
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
fn echo_packet_repr() -> Repr<'static> {
|
||||
Repr::EchoRequest {
|
||||
ident: 0x1234,
|
||||
seq_no: 0xabcd,
|
||||
data: &ECHO_PACKET_PAYLOAD
|
||||
data: &ECHO_PACKET_PAYLOAD,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -794,17 +787,17 @@ mod test {
|
|||
Repr::PktTooBig {
|
||||
mtu: 1500,
|
||||
header: Ipv6Repr {
|
||||
src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01]),
|
||||
dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02]),
|
||||
src_addr: Ipv6Address([
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01,
|
||||
]),
|
||||
dst_addr: Ipv6Address([
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02,
|
||||
]),
|
||||
next_header: IpProtocol::Udp,
|
||||
payload_len: 12,
|
||||
hop_limit: 0x40
|
||||
hop_limit: 0x40,
|
||||
},
|
||||
data: &PKT_TOO_BIG_UDP_PAYLOAD,
|
||||
}
|
||||
|
@ -819,7 +812,10 @@ mod test {
|
|||
assert_eq!(packet.echo_ident(), 0x1234);
|
||||
assert_eq!(packet.echo_seq_no(), 0xabcd);
|
||||
assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]);
|
||||
assert_eq!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), true);
|
||||
assert_eq!(
|
||||
packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2),
|
||||
true
|
||||
);
|
||||
assert!(!packet.msg_type().is_error());
|
||||
}
|
||||
|
||||
|
@ -831,7 +827,9 @@ mod test {
|
|||
packet.set_msg_code(0);
|
||||
packet.set_echo_ident(0x1234);
|
||||
packet.set_echo_seq_no(0xabcd);
|
||||
packet.payload_mut().copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
|
||||
packet
|
||||
.payload_mut()
|
||||
.copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
|
||||
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
|
||||
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
|
||||
}
|
||||
|
@ -839,8 +837,13 @@ mod test {
|
|||
#[test]
|
||||
fn test_echo_repr_parse() {
|
||||
let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
|
||||
let repr = Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
|
||||
&packet, &ChecksumCapabilities::default()).unwrap();
|
||||
let repr = Repr::parse(
|
||||
&MOCK_IP_ADDR_1,
|
||||
&MOCK_IP_ADDR_2,
|
||||
&packet,
|
||||
&ChecksumCapabilities::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(repr, echo_packet_repr());
|
||||
}
|
||||
|
||||
|
@ -849,8 +852,12 @@ mod test {
|
|||
let repr = echo_packet_repr();
|
||||
let mut bytes = vec![0xa5; repr.buffer_len()];
|
||||
let mut packet = Packet::new_unchecked(&mut bytes);
|
||||
repr.emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
|
||||
&mut packet, &ChecksumCapabilities::default());
|
||||
repr.emit(
|
||||
&MOCK_IP_ADDR_1,
|
||||
&MOCK_IP_ADDR_2,
|
||||
&mut packet,
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
|
||||
}
|
||||
|
||||
|
@ -862,7 +869,10 @@ mod test {
|
|||
assert_eq!(packet.checksum(), 0x0fc9);
|
||||
assert_eq!(packet.pkt_too_big_mtu(), 1500);
|
||||
assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]);
|
||||
assert_eq!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), true);
|
||||
assert_eq!(
|
||||
packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2),
|
||||
true
|
||||
);
|
||||
assert!(packet.msg_type().is_error());
|
||||
}
|
||||
|
||||
|
@ -873,7 +883,9 @@ mod test {
|
|||
packet.set_msg_type(Message::PktTooBig);
|
||||
packet.set_msg_code(0);
|
||||
packet.set_pkt_too_big_mtu(1500);
|
||||
packet.payload_mut().copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
|
||||
packet
|
||||
.payload_mut()
|
||||
.copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
|
||||
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
|
||||
assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
|
||||
}
|
||||
|
@ -881,8 +893,13 @@ mod test {
|
|||
#[test]
|
||||
fn test_too_big_repr_parse() {
|
||||
let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]);
|
||||
let repr = Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
|
||||
&packet, &ChecksumCapabilities::default()).unwrap();
|
||||
let repr = Repr::parse(
|
||||
&MOCK_IP_ADDR_1,
|
||||
&MOCK_IP_ADDR_2,
|
||||
&packet,
|
||||
&ChecksumCapabilities::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(repr, too_big_packet_repr());
|
||||
}
|
||||
|
||||
|
@ -891,8 +908,12 @@ mod test {
|
|||
let repr = too_big_packet_repr();
|
||||
let mut bytes = vec![0xa5; repr.buffer_len()];
|
||||
let mut packet = Packet::new_unchecked(&mut bytes);
|
||||
repr.emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
|
||||
&mut packet, &ChecksumCapabilities::default());
|
||||
repr.emit(
|
||||
&MOCK_IP_ADDR_1,
|
||||
&MOCK_IP_ADDR_2,
|
||||
&mut packet,
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::fmt;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::wire::ip::checksum;
|
||||
use crate::time::Duration;
|
||||
use crate::wire::ip::checksum;
|
||||
use crate::{Error, Result};
|
||||
|
||||
use crate::wire::Ipv4Address;
|
||||
|
||||
|
@ -202,7 +202,8 @@ impl Repr {
|
|||
/// Parse an Internet Group Management Protocol v1/v2 packet and return
|
||||
/// a high-level representation.
|
||||
pub fn parse<T>(packet: &Packet<&T>) -> Result<Repr>
|
||||
where T: AsRef<[u8]> + ?Sized
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
// Check if the address is 0.0.0.0 or multicast
|
||||
let addr = packet.group_addr();
|
||||
|
@ -226,13 +227,13 @@ impl Repr {
|
|||
version,
|
||||
})
|
||||
}
|
||||
Message::MembershipReportV2 => {
|
||||
Ok(Repr::MembershipReport {
|
||||
Message::MembershipReportV2 => Ok(Repr::MembershipReport {
|
||||
group_addr: packet.group_addr(),
|
||||
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 => {
|
||||
// for backwards compatibility with IGMPv1
|
||||
Ok(Repr::MembershipReport {
|
||||
|
@ -252,20 +253,21 @@ impl Repr {
|
|||
|
||||
/// Emit a high-level representation into an Internet Group Management Protocol v2 packet.
|
||||
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized
|
||||
where
|
||||
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
|
||||
{
|
||||
match *self {
|
||||
Repr::MembershipQuery {
|
||||
max_resp_time,
|
||||
group_addr,
|
||||
version
|
||||
version,
|
||||
} => {
|
||||
packet.set_msg_type(Message::MembershipQuery);
|
||||
match version {
|
||||
IgmpVersion::Version1 =>
|
||||
packet.set_max_resp_code(0),
|
||||
IgmpVersion::Version2 =>
|
||||
packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time)),
|
||||
IgmpVersion::Version1 => packet.set_max_resp_code(0),
|
||||
IgmpVersion::Version2 => {
|
||||
packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time))
|
||||
}
|
||||
}
|
||||
packet.set_group_address(group_addr);
|
||||
}
|
||||
|
@ -336,20 +338,21 @@ impl<'a> fmt::Display for Repr {
|
|||
group_addr,
|
||||
version,
|
||||
} => {
|
||||
write!(f,
|
||||
write!(
|
||||
f,
|
||||
"IGMP membership query max_resp_time={} group_addr={} version={:?}",
|
||||
max_resp_time,
|
||||
group_addr,
|
||||
version)
|
||||
max_resp_time, group_addr, version
|
||||
)
|
||||
}
|
||||
Repr::MembershipReport {
|
||||
group_addr,
|
||||
version,
|
||||
} => {
|
||||
write!(f,
|
||||
write!(
|
||||
f,
|
||||
"IGMP membership report group_addr={} version={:?}",
|
||||
group_addr,
|
||||
version)
|
||||
group_addr, version
|
||||
)
|
||||
}
|
||||
Repr::LeaveGroup { group_addr } => {
|
||||
write!(f, "IGMP leave group group_addr={})", group_addr)
|
||||
|
@ -361,10 +364,11 @@ impl<'a> fmt::Display for Repr {
|
|||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>,
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent)
|
||||
-> fmt::Result {
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
match Packet::new_checked(buffer) {
|
||||
Err(err) => writeln!(f, "{}({})", indent, err),
|
||||
Ok(packet) => writeln!(f, "{}{}", indent, packet),
|
||||
|
@ -376,7 +380,6 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
|||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
||||
static LEAVE_PACKET_BYTES: [u8; 8] = [0x17, 0x00, 0x02, 0x69, 0xe0, 0x00, 0x06, 0x96];
|
||||
static REPORT_PACKET_BYTES: [u8; 8] = [0x16, 0x00, 0x08, 0xda, 0xe1, 0x00, 0x00, 0x25];
|
||||
|
||||
|
@ -386,8 +389,10 @@ mod test {
|
|||
assert_eq!(packet.msg_type(), Message::LeaveGroup);
|
||||
assert_eq!(packet.max_resp_code(), 0);
|
||||
assert_eq!(packet.checksum(), 0x269);
|
||||
assert_eq!(packet.group_addr(),
|
||||
Ipv4Address::from_bytes(&[224, 0, 6, 150]));
|
||||
assert_eq!(
|
||||
packet.group_addr(),
|
||||
Ipv4Address::from_bytes(&[224, 0, 6, 150])
|
||||
);
|
||||
assert_eq!(packet.verify_checksum(), true);
|
||||
}
|
||||
|
||||
|
@ -397,8 +402,10 @@ mod test {
|
|||
assert_eq!(packet.msg_type(), Message::MembershipReportV2);
|
||||
assert_eq!(packet.max_resp_code(), 0);
|
||||
assert_eq!(packet.checksum(), 0x08da);
|
||||
assert_eq!(packet.group_addr(),
|
||||
Ipv4Address::from_bytes(&[225, 0, 0, 37]));
|
||||
assert_eq!(
|
||||
packet.group_addr(),
|
||||
Ipv4Address::from_bytes(&[225, 0, 0, 37])
|
||||
);
|
||||
assert_eq!(packet.verify_checksum(), true);
|
||||
}
|
||||
|
||||
|
@ -437,9 +444,7 @@ mod test {
|
|||
#[test]
|
||||
fn duration_to_max_resp_time_max() {
|
||||
for duration in 31744..65536 {
|
||||
let time = duration_to_max_resp_code(
|
||||
Duration::from_millis(duration * 100)
|
||||
);
|
||||
let time = duration_to_max_resp_code(Duration::from_millis(duration * 100));
|
||||
assert_eq!(time, 0xFF);
|
||||
}
|
||||
}
|
||||
|
|
396
src/wire/ip.rs
396
src/wire/ip.rs
|
@ -1,12 +1,12 @@
|
|||
use core::fmt;
|
||||
use core::convert::From;
|
||||
use core::fmt;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr, Ipv4Cidr};
|
||||
use crate::wire::{Ipv4Address, Ipv4Cidr, Ipv4Packet, Ipv4Repr};
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
use crate::wire::{Ipv6Address, Ipv6Cidr, Ipv6Packet, Ipv6Repr};
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// Internet protocol version.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
|
@ -31,7 +31,7 @@ impl Version {
|
|||
4 => Ok(Version::Ipv4),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
6 => Ok(Version::Ipv6),
|
||||
_ => Err(Error::Unrecognized)
|
||||
_ => Err(Error::Unrecognized),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ impl fmt::Display for Protocol {
|
|||
Protocol::Icmpv6 => write!(f, "ICMPv6"),
|
||||
Protocol::Ipv6NoNxt => write!(f, "IPv6-NoNxt"),
|
||||
Protocol::Ipv6Opts => write!(f, "IPv6-Opts"),
|
||||
Protocol::Unknown(id) => write!(f, "0x{:02x}", id)
|
||||
Protocol::Unknown(id) => write!(f, "0x{:02x}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,8 +107,7 @@ impl Address {
|
|||
/// Create an address wrapping an IPv6 address with the given octets.
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16,
|
||||
a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
|
||||
pub fn v6(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
|
||||
Address::Ipv6(Ipv6Address::new(a0, a1, a2, a3, a4, a5, a6, a7))
|
||||
}
|
||||
|
||||
|
@ -196,7 +195,7 @@ impl Address {
|
|||
}
|
||||
} else if one {
|
||||
// 1 where 0 was expected
|
||||
return None
|
||||
return None;
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
|
@ -297,8 +296,9 @@ impl Cidr {
|
|||
Address::Ipv4(addr) => Cidr::Ipv4(Ipv4Cidr::new(addr, prefix_len)),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Address::Ipv6(addr) => Cidr::Ipv6(Ipv6Cidr::new(addr, prefix_len)),
|
||||
Address::Unspecified =>
|
||||
panic!("a CIDR block cannot be based on an unspecified address"),
|
||||
Address::Unspecified => {
|
||||
panic!("a CIDR block cannot be based on an unspecified address")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,18 +327,17 @@ impl Cidr {
|
|||
pub fn contains_addr(&self, addr: &Address) -> bool {
|
||||
match (self, addr) {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
(&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) =>
|
||||
cidr.contains_addr(addr),
|
||||
(&Cidr::Ipv4(ref cidr), &Address::Ipv4(ref addr)) => cidr.contains_addr(addr),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) =>
|
||||
cidr.contains_addr(addr),
|
||||
(&Cidr::Ipv6(ref cidr), &Address::Ipv6(ref addr)) => cidr.contains_addr(addr),
|
||||
#[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
|
||||
(&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) =>
|
||||
false,
|
||||
(&Cidr::Ipv4(_), &Address::Ipv6(_)) | (&Cidr::Ipv6(_), &Address::Ipv4(_)) => false,
|
||||
(_, &Address::Unspecified) =>
|
||||
// a fully unspecified address covers both IPv4 and IPv6,
|
||||
// and no CIDR block can do that.
|
||||
false,
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -347,14 +346,11 @@ impl Cidr {
|
|||
pub fn contains_subnet(&self, subnet: &Cidr) -> bool {
|
||||
match (self, subnet) {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
(&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) =>
|
||||
cidr.contains_subnet(other),
|
||||
(&Cidr::Ipv4(ref cidr), &Cidr::Ipv4(ref other)) => cidr.contains_subnet(other),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) =>
|
||||
cidr.contains_subnet(other),
|
||||
(&Cidr::Ipv6(ref cidr), &Cidr::Ipv6(ref other)) => cidr.contains_subnet(other),
|
||||
#[cfg(all(feature = "proto-ipv6", feature = "proto-ipv4"))]
|
||||
(&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) =>
|
||||
false,
|
||||
(&Cidr::Ipv4(_), &Cidr::Ipv6(_)) | (&Cidr::Ipv6(_), &Cidr::Ipv4(_)) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -402,12 +398,15 @@ impl defmt::Format for Cidr {
|
|||
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
||||
pub struct Endpoint {
|
||||
pub addr: Address,
|
||||
pub port: u16
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
/// An endpoint with unspecified address and port.
|
||||
pub const UNSPECIFIED: Endpoint = Endpoint { addr: Address::Unspecified, port: 0 };
|
||||
pub const UNSPECIFIED: Endpoint = Endpoint {
|
||||
addr: Address::Unspecified,
|
||||
port: 0,
|
||||
};
|
||||
|
||||
/// Create an endpoint address from given address and port.
|
||||
pub fn new(addr: Address, port: u16) -> Endpoint {
|
||||
|
@ -434,7 +433,7 @@ impl From<::std::net::SocketAddr> for Endpoint {
|
|||
impl From<::std::net::SocketAddrV4> for Endpoint {
|
||||
fn from(x: ::std::net::SocketAddrV4) -> Endpoint {
|
||||
Endpoint {
|
||||
addr: x.ip().clone().into(),
|
||||
addr: (*x.ip()).into(),
|
||||
port: x.port(),
|
||||
}
|
||||
}
|
||||
|
@ -444,7 +443,7 @@ impl From<::std::net::SocketAddrV4> for Endpoint {
|
|||
impl From<::std::net::SocketAddrV6> for Endpoint {
|
||||
fn from(x: ::std::net::SocketAddrV6) -> Endpoint {
|
||||
Endpoint {
|
||||
addr: x.ip().clone().into(),
|
||||
addr: (*x.ip()).into(),
|
||||
port: x.port(),
|
||||
}
|
||||
}
|
||||
|
@ -465,13 +464,19 @@ impl defmt::Format for Endpoint {
|
|||
|
||||
impl From<u16> for Endpoint {
|
||||
fn from(port: u16) -> Endpoint {
|
||||
Endpoint { addr: Address::Unspecified, port }
|
||||
Endpoint {
|
||||
addr: Address::Unspecified,
|
||||
port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Address>> From<(T, u16)> for Endpoint {
|
||||
fn from((addr, port): (T, u16)) -> Endpoint {
|
||||
Endpoint { addr: addr.into(), port }
|
||||
Endpoint {
|
||||
addr: addr.into(),
|
||||
port,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -489,7 +494,7 @@ pub enum Repr {
|
|||
dst_addr: Address,
|
||||
protocol: Protocol,
|
||||
payload_len: usize,
|
||||
hop_limit: u8
|
||||
hop_limit: u8,
|
||||
},
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
Ipv4(Ipv4Repr),
|
||||
|
@ -570,14 +575,20 @@ impl Repr {
|
|||
/// Set the payload length.
|
||||
pub fn set_payload_len(&mut self, length: usize) {
|
||||
match *self {
|
||||
Repr::Unspecified { ref mut payload_len, .. } =>
|
||||
*payload_len = length,
|
||||
Repr::Unspecified {
|
||||
ref mut payload_len,
|
||||
..
|
||||
} => *payload_len = length,
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
Repr::Ipv4(Ipv4Repr { ref mut payload_len, .. }) =>
|
||||
*payload_len = length,
|
||||
Repr::Ipv4(Ipv4Repr {
|
||||
ref mut payload_len,
|
||||
..
|
||||
}) => *payload_len = length,
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Repr::Ipv6(Ipv6Repr { ref mut payload_len, .. }) =>
|
||||
*payload_len = length,
|
||||
Repr::Ipv6(Ipv6Repr {
|
||||
ref mut payload_len,
|
||||
..
|
||||
}) => *payload_len = length,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -588,7 +599,7 @@ impl Repr {
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
Repr::Ipv4(Ipv4Repr { hop_limit, .. }) => hop_limit,
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Repr::Ipv6(Ipv6Repr { hop_limit, ..}) => hop_limit,
|
||||
Repr::Ipv6(Ipv6Repr { hop_limit, .. }) => hop_limit,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -607,15 +618,15 @@ impl Repr {
|
|||
$ipty(addr) => {
|
||||
$iprepr.src_addr = addr;
|
||||
return Ok($reprty($iprepr));
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Err(Error::Unaddressable)
|
||||
} else {
|
||||
Ok($reprty($iprepr))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
match self {
|
||||
|
@ -623,12 +634,16 @@ impl Repr {
|
|||
&Repr::Unspecified {
|
||||
src_addr: src_addr @ Address::Unspecified,
|
||||
dst_addr: Address::Ipv4(dst_addr),
|
||||
protocol, payload_len, hop_limit
|
||||
} |
|
||||
&Repr::Unspecified {
|
||||
protocol,
|
||||
payload_len,
|
||||
hop_limit,
|
||||
}
|
||||
| &Repr::Unspecified {
|
||||
src_addr: src_addr @ Address::Ipv4(_),
|
||||
dst_addr: Address::Ipv4(dst_addr),
|
||||
protocol, payload_len, hop_limit
|
||||
protocol,
|
||||
payload_len,
|
||||
hop_limit,
|
||||
} if src_addr.is_unspecified() => {
|
||||
let mut src_addr = if let Address::Ipv4(src_ipv4_addr) = src_addr {
|
||||
Some(src_ipv4_addr)
|
||||
|
@ -643,7 +658,10 @@ impl Repr {
|
|||
}
|
||||
Ok(Repr::Ipv4(Ipv4Repr {
|
||||
src_addr: src_addr.ok_or(Error::Unaddressable)?,
|
||||
dst_addr, protocol, payload_len, hop_limit
|
||||
dst_addr,
|
||||
protocol,
|
||||
payload_len,
|
||||
hop_limit,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -651,12 +669,16 @@ impl Repr {
|
|||
&Repr::Unspecified {
|
||||
src_addr: src_addr @ Address::Unspecified,
|
||||
dst_addr: Address::Ipv6(dst_addr),
|
||||
protocol, payload_len, hop_limit
|
||||
} |
|
||||
&Repr::Unspecified {
|
||||
protocol,
|
||||
payload_len,
|
||||
hop_limit,
|
||||
}
|
||||
| &Repr::Unspecified {
|
||||
src_addr: src_addr @ Address::Ipv6(_),
|
||||
dst_addr: Address::Ipv6(dst_addr),
|
||||
protocol, payload_len, hop_limit
|
||||
protocol,
|
||||
payload_len,
|
||||
hop_limit,
|
||||
} if src_addr.is_unspecified() => {
|
||||
let mut src_addr = if let Address::Ipv6(src_ipv6_addr) = src_addr {
|
||||
Some(src_ipv6_addr)
|
||||
|
@ -672,7 +694,9 @@ impl Repr {
|
|||
Ok(Repr::Ipv6(Ipv6Repr {
|
||||
src_addr: src_addr.ok_or(Error::Unaddressable)?,
|
||||
next_header: protocol,
|
||||
dst_addr, payload_len, hop_limit
|
||||
dst_addr,
|
||||
payload_len,
|
||||
hop_limit,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -680,41 +704,45 @@ impl Repr {
|
|||
&Repr::Unspecified {
|
||||
src_addr: Address::Ipv4(src_addr),
|
||||
dst_addr: Address::Ipv4(dst_addr),
|
||||
protocol, payload_len, hop_limit
|
||||
} => {
|
||||
Ok(Repr::Ipv4(Ipv4Repr {
|
||||
protocol,
|
||||
payload_len,
|
||||
hop_limit,
|
||||
} => Ok(Repr::Ipv4(Ipv4Repr {
|
||||
src_addr: src_addr,
|
||||
dst_addr: dst_addr,
|
||||
protocol: protocol,
|
||||
payload_len: payload_len, hop_limit
|
||||
}))
|
||||
}
|
||||
payload_len: payload_len,
|
||||
hop_limit,
|
||||
})),
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Repr::Unspecified {
|
||||
src_addr: Address::Ipv6(src_addr),
|
||||
dst_addr: Address::Ipv6(dst_addr),
|
||||
protocol, payload_len, hop_limit
|
||||
} => {
|
||||
Ok(Repr::Ipv6(Ipv6Repr {
|
||||
protocol,
|
||||
payload_len,
|
||||
hop_limit,
|
||||
} => Ok(Repr::Ipv6(Ipv6Repr {
|
||||
src_addr: src_addr,
|
||||
dst_addr: dst_addr,
|
||||
next_header: protocol,
|
||||
payload_len: payload_len,
|
||||
hop_limit: hop_limit
|
||||
}))
|
||||
}
|
||||
hop_limit: hop_limit,
|
||||
})),
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
&Repr::Ipv4(mut repr) =>
|
||||
resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs),
|
||||
&Repr::Ipv4(mut repr) => {
|
||||
resolve_unspecified!(Repr::Ipv4, Address::Ipv4, repr, fallback_src_addrs)
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
&Repr::Ipv6(mut repr) =>
|
||||
resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs),
|
||||
&Repr::Ipv6(mut repr) => {
|
||||
resolve_unspecified!(Repr::Ipv6, Address::Ipv6, repr, fallback_src_addrs)
|
||||
}
|
||||
|
||||
&Repr::Unspecified { .. } =>
|
||||
panic!("source and destination IP address families do not match"),
|
||||
&Repr::Unspecified { .. } => {
|
||||
panic!("source and destination IP address families do not match")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -724,14 +752,11 @@ impl Repr {
|
|||
/// This function panics if invoked on an unspecified representation.
|
||||
pub fn buffer_len(&self) -> usize {
|
||||
match *self {
|
||||
Repr::Unspecified { .. } =>
|
||||
panic!("unspecified IP representation"),
|
||||
Repr::Unspecified { .. } => panic!("unspecified IP representation"),
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
Repr::Ipv4(repr) =>
|
||||
repr.buffer_len(),
|
||||
Repr::Ipv4(repr) => repr.buffer_len(),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Repr::Ipv6(repr) =>
|
||||
repr.buffer_len(),
|
||||
Repr::Ipv6(repr) => repr.buffer_len(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -739,16 +764,17 @@ impl Repr {
|
|||
///
|
||||
/// # Panics
|
||||
/// This function panics if invoked on an unspecified representation.
|
||||
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, buffer: T, _checksum_caps: &ChecksumCapabilities) {
|
||||
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
buffer: T,
|
||||
_checksum_caps: &ChecksumCapabilities,
|
||||
) {
|
||||
match *self {
|
||||
Repr::Unspecified { .. } =>
|
||||
panic!("unspecified IP representation"),
|
||||
Repr::Unspecified { .. } => panic!("unspecified IP representation"),
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
Repr::Ipv4(repr) =>
|
||||
repr.emit(&mut Ipv4Packet::new_unchecked(buffer), &_checksum_caps),
|
||||
Repr::Ipv4(repr) => repr.emit(&mut Ipv4Packet::new_unchecked(buffer), _checksum_caps),
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
Repr::Ipv6(repr) =>
|
||||
repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
|
||||
Repr::Ipv6(repr) => repr.emit(&mut Ipv6Packet::new_unchecked(buffer)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -816,8 +842,12 @@ pub mod checksum {
|
|||
}
|
||||
|
||||
/// Compute an IP pseudo header checksum.
|
||||
pub fn pseudo_header(src_addr: &Address, dst_addr: &Address,
|
||||
protocol: Protocol, length: u32) -> u16 {
|
||||
pub fn pseudo_header(
|
||||
src_addr: &Address,
|
||||
dst_addr: &Address,
|
||||
protocol: Protocol,
|
||||
length: u32,
|
||||
) -> u16 {
|
||||
match (src_addr, dst_addr) {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
(&Address::Ipv4(src_addr), &Address::Ipv4(dst_addr)) => {
|
||||
|
@ -828,9 +858,9 @@ pub mod checksum {
|
|||
combine(&[
|
||||
data(src_addr.as_bytes()),
|
||||
data(dst_addr.as_bytes()),
|
||||
data(&proto_len[..])
|
||||
data(&proto_len[..]),
|
||||
])
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
(&Address::Ipv6(src_addr), &Address::Ipv6(dst_addr)) => {
|
||||
|
@ -840,12 +870,14 @@ pub mod checksum {
|
|||
combine(&[
|
||||
data(src_addr.as_bytes()),
|
||||
data(dst_addr.as_bytes()),
|
||||
data(&proto_len[..])
|
||||
data(&proto_len[..]),
|
||||
])
|
||||
}
|
||||
|
||||
_ => panic!("Unexpected pseudo header addresses: {}, {}",
|
||||
src_addr, dst_addr)
|
||||
_ => panic!(
|
||||
"Unexpected pseudo header addresses: {}, {}",
|
||||
src_addr, dst_addr
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -861,14 +893,18 @@ pub mod checksum {
|
|||
|
||||
use crate::wire::pretty_print::PrettyIndent;
|
||||
|
||||
pub fn pretty_print_ip_payload<T: Into<Repr>>(f: &mut fmt::Formatter, indent: &mut PrettyIndent,
|
||||
ip_repr: T, payload: &[u8]) -> fmt::Result {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::Icmpv4Packet;
|
||||
pub fn pretty_print_ip_payload<T: Into<Repr>>(
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
ip_repr: T,
|
||||
payload: &[u8],
|
||||
) -> fmt::Result {
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use super::pretty_print::PrettyPrint;
|
||||
use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
|
||||
use crate::wire::ip::checksum::format_checksum;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::Icmpv4Packet;
|
||||
use crate::wire::{TcpPacket, TcpRepr, UdpPacket, UdpRepr};
|
||||
|
||||
let checksum_caps = ChecksumCapabilities::ignored();
|
||||
let repr = ip_repr.into();
|
||||
|
@ -883,13 +919,23 @@ pub fn pretty_print_ip_payload<T: Into<Repr>>(f: &mut fmt::Formatter, indent: &m
|
|||
match UdpPacket::<&[u8]>::new_checked(payload) {
|
||||
Err(err) => write!(f, "{}({})", indent, err),
|
||||
Ok(udp_packet) => {
|
||||
match UdpRepr::parse(&udp_packet, &repr.src_addr(),
|
||||
&repr.dst_addr(), &checksum_caps) {
|
||||
match UdpRepr::parse(
|
||||
&udp_packet,
|
||||
&repr.src_addr(),
|
||||
&repr.dst_addr(),
|
||||
&checksum_caps,
|
||||
) {
|
||||
Err(err) => write!(f, "{}{} ({})", indent, udp_packet, err),
|
||||
Ok(udp_repr) => {
|
||||
write!(f, "{}{} len={}", indent, udp_repr, udp_packet.payload().len())?;
|
||||
let valid = udp_packet.verify_checksum(&repr.src_addr(),
|
||||
&repr.dst_addr());
|
||||
write!(
|
||||
f,
|
||||
"{}{} len={}",
|
||||
indent,
|
||||
udp_repr,
|
||||
udp_packet.payload().len()
|
||||
)?;
|
||||
let valid =
|
||||
udp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
|
||||
format_checksum(f, valid)
|
||||
}
|
||||
}
|
||||
|
@ -901,20 +947,24 @@ pub fn pretty_print_ip_payload<T: Into<Repr>>(f: &mut fmt::Formatter, indent: &m
|
|||
match TcpPacket::<&[u8]>::new_checked(payload) {
|
||||
Err(err) => write!(f, "{}({})", indent, err),
|
||||
Ok(tcp_packet) => {
|
||||
match TcpRepr::parse(&tcp_packet, &repr.src_addr(),
|
||||
&repr.dst_addr(), &checksum_caps) {
|
||||
match TcpRepr::parse(
|
||||
&tcp_packet,
|
||||
&repr.src_addr(),
|
||||
&repr.dst_addr(),
|
||||
&checksum_caps,
|
||||
) {
|
||||
Err(err) => write!(f, "{}{} ({})", indent, tcp_packet, err),
|
||||
Ok(tcp_repr) => {
|
||||
write!(f, "{}{}", indent, tcp_repr)?;
|
||||
let valid = tcp_packet.verify_checksum(&repr.src_addr(),
|
||||
&repr.dst_addr());
|
||||
let valid =
|
||||
tcp_packet.verify_checksum(&repr.src_addr(), &repr.dst_addr());
|
||||
format_checksum(f, valid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => Ok(())
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -923,17 +973,21 @@ pub(crate) mod test {
|
|||
#![allow(unused)]
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1]));
|
||||
pub(crate) const MOCK_IP_ADDR_1: IpAddress = IpAddress::Ipv6(Ipv6Address([
|
||||
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||
]));
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 2]));
|
||||
pub(crate) const MOCK_IP_ADDR_2: IpAddress = IpAddress::Ipv6(Ipv6Address([
|
||||
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
|
||||
]));
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 3]));
|
||||
pub(crate) const MOCK_IP_ADDR_3: IpAddress = IpAddress::Ipv6(Ipv6Address([
|
||||
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,
|
||||
]));
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 4]));
|
||||
pub(crate) const MOCK_IP_ADDR_4: IpAddress = IpAddress::Ipv6(Ipv6Address([
|
||||
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4,
|
||||
]));
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv6(Ipv6Address::UNSPECIFIED);
|
||||
|
||||
|
@ -948,9 +1002,8 @@ pub(crate) mod test {
|
|||
#[cfg(all(feature = "proto-ipv4", not(feature = "proto-ipv6")))]
|
||||
pub(crate) const MOCK_UNSPECIFIED: IpAddress = IpAddress::Ipv4(Ipv4Address::UNSPECIFIED);
|
||||
|
||||
|
||||
use super::*;
|
||||
use crate::wire::{IpAddress, IpProtocol,IpCidr};
|
||||
use crate::wire::{IpAddress, IpCidr, IpProtocol};
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::{Ipv4Address, Ipv4Repr};
|
||||
|
||||
|
@ -969,14 +1022,15 @@ pub(crate) mod test {
|
|||
let payload_len = 10;
|
||||
|
||||
assert_eq!(
|
||||
Repr::Unspecified{
|
||||
Repr::Unspecified {
|
||||
src_addr: $ip_addr(ip_addr_a),
|
||||
dst_addr: $ip_addr(ip_addr_b),
|
||||
protocol: proto,
|
||||
hop_limit: 0x2a,
|
||||
payload_len,
|
||||
}.lower(&[]),
|
||||
Ok($ip_repr($repr{
|
||||
}
|
||||
.lower(&[]),
|
||||
Ok($ip_repr($repr {
|
||||
src_addr: ip_addr_a,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
|
@ -986,25 +1040,27 @@ pub(crate) mod test {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
Repr::Unspecified{
|
||||
Repr::Unspecified {
|
||||
src_addr: IpAddress::Unspecified,
|
||||
dst_addr: $ip_addr(ip_addr_b),
|
||||
protocol: proto,
|
||||
hop_limit: 64,
|
||||
payload_len
|
||||
}.lower(&[]),
|
||||
}
|
||||
.lower(&[]),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Repr::Unspecified{
|
||||
Repr::Unspecified {
|
||||
src_addr: IpAddress::Unspecified,
|
||||
dst_addr: $ip_addr(ip_addr_b),
|
||||
protocol: proto,
|
||||
hop_limit: 64,
|
||||
payload_len
|
||||
}.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
|
||||
Ok($ip_repr($repr{
|
||||
}
|
||||
.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
|
||||
Ok($ip_repr($repr {
|
||||
src_addr: ip_addr_a,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
|
@ -1014,14 +1070,15 @@ pub(crate) mod test {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
Repr::Unspecified{
|
||||
Repr::Unspecified {
|
||||
src_addr: $ip_addr($unspecified),
|
||||
dst_addr: $ip_addr(ip_addr_b),
|
||||
protocol: proto,
|
||||
hop_limit: 64,
|
||||
payload_len
|
||||
}.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
|
||||
Ok($ip_repr($repr{
|
||||
}
|
||||
.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
|
||||
Ok($ip_repr($repr {
|
||||
src_addr: ip_addr_a,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
|
@ -1031,14 +1088,15 @@ pub(crate) mod test {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
Repr::Unspecified{
|
||||
Repr::Unspecified {
|
||||
src_addr: $ip_addr($unspecified),
|
||||
dst_addr: $ip_addr(ip_addr_b),
|
||||
protocol: proto,
|
||||
hop_limit: 64,
|
||||
payload_len
|
||||
}.lower(&[]),
|
||||
Ok($ip_repr($repr{
|
||||
}
|
||||
.lower(&[]),
|
||||
Ok($ip_repr($repr {
|
||||
src_addr: $unspecified,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
|
@ -1048,14 +1106,15 @@ pub(crate) mod test {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
$ip_repr($repr{
|
||||
$ip_repr($repr {
|
||||
src_addr: ip_addr_a,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
hop_limit: 255,
|
||||
payload_len
|
||||
}).lower(&[]),
|
||||
Ok($ip_repr($repr{
|
||||
})
|
||||
.lower(&[]),
|
||||
Ok($ip_repr($repr {
|
||||
src_addr: ip_addr_a,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
|
@ -1065,25 +1124,27 @@ pub(crate) mod test {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
$ip_repr($repr{
|
||||
$ip_repr($repr {
|
||||
src_addr: $unspecified,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
hop_limit: 255,
|
||||
payload_len
|
||||
}).lower(&[]),
|
||||
})
|
||||
.lower(&[]),
|
||||
Err(Error::Unaddressable)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
$ip_repr($repr{
|
||||
$ip_repr($repr {
|
||||
src_addr: $unspecified,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
hop_limit: 64,
|
||||
payload_len
|
||||
}).lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
|
||||
Ok($ip_repr($repr{
|
||||
})
|
||||
.lower(&[IpCidr::new($ip_addr(ip_addr_a), 24)]),
|
||||
Ok($ip_repr($repr {
|
||||
src_addr: ip_addr_a,
|
||||
dst_addr: ip_addr_b,
|
||||
$nxthdr: proto,
|
||||
|
@ -1095,15 +1156,31 @@ pub(crate) mod test {
|
|||
}
|
||||
};
|
||||
(ipv4 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
|
||||
generate_common_tests!(ipv4, Ipv4Repr, Repr::Ipv4, IpAddress::Ipv4,
|
||||
Ipv4Address::from_bytes, protocol, $addr_bytes_a,
|
||||
$addr_bytes_b, Ipv4Address::UNSPECIFIED);
|
||||
generate_common_tests!(
|
||||
ipv4,
|
||||
Ipv4Repr,
|
||||
Repr::Ipv4,
|
||||
IpAddress::Ipv4,
|
||||
Ipv4Address::from_bytes,
|
||||
protocol,
|
||||
$addr_bytes_a,
|
||||
$addr_bytes_b,
|
||||
Ipv4Address::UNSPECIFIED
|
||||
);
|
||||
};
|
||||
(ipv6 $addr_bytes_a:expr, $addr_bytes_b:expr) => {
|
||||
generate_common_tests!(ipv6, Ipv6Repr, Repr::Ipv6, IpAddress::Ipv6,
|
||||
Ipv6Address::from_bytes, next_header, $addr_bytes_a,
|
||||
$addr_bytes_b, Ipv6Address::UNSPECIFIED);
|
||||
}
|
||||
generate_common_tests!(
|
||||
ipv6,
|
||||
Ipv6Repr,
|
||||
Repr::Ipv6,
|
||||
IpAddress::Ipv6,
|
||||
Ipv6Address::from_bytes,
|
||||
next_header,
|
||||
$addr_bytes_a,
|
||||
$addr_bytes_b,
|
||||
Ipv6Address::UNSPECIFIED
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
|
@ -1125,8 +1202,9 @@ pub(crate) mod test {
|
|||
dst_addr: Address::Ipv4(Ipv4Address::UNSPECIFIED),
|
||||
protocol: IpProtocol::Icmpv6,
|
||||
hop_limit: 0xff,
|
||||
payload_len: 0
|
||||
}.lower(&[]);
|
||||
payload_len: 0,
|
||||
}
|
||||
.lower(&[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1138,10 +1216,7 @@ pub(crate) mod test {
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
fn to_prefix_len_ipv4() {
|
||||
fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
|
||||
assert_eq!(
|
||||
Some(prefix_len),
|
||||
mask.into().to_prefix_len()
|
||||
);
|
||||
assert_eq!(Some(prefix_len), mask.into().to_prefix_len());
|
||||
}
|
||||
|
||||
test_eq(0, Ipv4Address::new(0, 0, 0, 0));
|
||||
|
@ -1181,25 +1256,36 @@ pub(crate) mod test {
|
|||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
fn to_prefix_len_ipv4_error() {
|
||||
assert_eq!(None, IpAddress::from(Ipv4Address::new(255,255,255,1)).to_prefix_len());
|
||||
assert_eq!(
|
||||
None,
|
||||
IpAddress::from(Ipv4Address::new(255, 255, 255, 1)).to_prefix_len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn to_prefix_len_ipv6() {
|
||||
fn test_eq<A: Into<Address>>(prefix_len: u8, mask: A) {
|
||||
assert_eq!(
|
||||
Some(prefix_len),
|
||||
mask.into().to_prefix_len()
|
||||
);
|
||||
assert_eq!(Some(prefix_len), mask.into().to_prefix_len());
|
||||
}
|
||||
|
||||
test_eq(0, Ipv6Address::new(0, 0, 0, 0, 0, 0, 0, 0));
|
||||
test_eq(128, Ipv6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff));
|
||||
test_eq(
|
||||
128,
|
||||
Ipv6Address::new(
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
fn to_prefix_len_ipv6_error() {
|
||||
assert_eq!(None, IpAddress::from(Ipv6Address::new(0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1)).to_prefix_len());
|
||||
assert_eq!(
|
||||
None,
|
||||
IpAddress::from(Ipv6Address::new(
|
||||
0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0, 1
|
||||
))
|
||||
.to_prefix_len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
426
src/wire/ipv4.rs
426
src/wire/ipv4.rs
|
@ -1,9 +1,9 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::fmt;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::wire::ip::{checksum, pretty_print_ip_payload};
|
||||
use crate::{Error, Result};
|
||||
|
||||
pub use super::IpProtocol as Protocol;
|
||||
|
||||
|
@ -60,9 +60,7 @@ impl Address {
|
|||
|
||||
/// Query whether the address is an unicast address.
|
||||
pub fn is_unicast(&self) -> bool {
|
||||
!(self.is_broadcast() ||
|
||||
self.is_multicast() ||
|
||||
self.is_unspecified())
|
||||
!(self.is_broadcast() || self.is_multicast() || self.is_unspecified())
|
||||
}
|
||||
|
||||
/// Query whether the address is the broadcast address.
|
||||
|
@ -115,7 +113,14 @@ impl fmt::Display for Address {
|
|||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for Address {
|
||||
fn format(&self, f: defmt::Formatter) {
|
||||
defmt::write!(f, "{=u8}.{=u8}.{=u8}.{=u8}", self.0[0], self.0[1], self.0[2], self.0[3])
|
||||
defmt::write!(
|
||||
f,
|
||||
"{=u8}.{=u8}.{=u8}.{=u8}",
|
||||
self.0[0],
|
||||
self.0[1],
|
||||
self.0[2],
|
||||
self.0[3]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,14 +142,20 @@ impl Cidr {
|
|||
// Replace with const panic (or assert) when stabilized
|
||||
// see: https://github.com/rust-lang/rust/issues/51999
|
||||
["Prefix length should be <= 32"][(prefix_len > 32) as usize];
|
||||
Cidr { address, prefix_len }
|
||||
Cidr {
|
||||
address,
|
||||
prefix_len,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an IPv4 CIDR block from the given address and network mask.
|
||||
pub fn from_netmask(addr: Address, netmask: Address) -> Result<Cidr> {
|
||||
let netmask = NetworkEndian::read_u32(&netmask.0[..]);
|
||||
if netmask.leading_zeros() == 0 && netmask.trailing_zeros() == netmask.count_zeros() {
|
||||
Ok(Cidr { address: addr, prefix_len: netmask.count_ones() as u8 })
|
||||
Ok(Cidr {
|
||||
address: addr,
|
||||
prefix_len: netmask.count_ones() as u8,
|
||||
})
|
||||
} else {
|
||||
Err(Error::Illegal)
|
||||
}
|
||||
|
@ -206,14 +217,19 @@ impl Cidr {
|
|||
self.address.0[2] & mask[2],
|
||||
self.address.0[3] & mask[3],
|
||||
];
|
||||
Cidr { address: Address(network), prefix_len: self.prefix_len }
|
||||
Cidr {
|
||||
address: Address(network),
|
||||
prefix_len: self.prefix_len,
|
||||
}
|
||||
}
|
||||
|
||||
/// Query whether the subnetwork described by this IPv4 CIDR block contains
|
||||
/// the given address.
|
||||
pub fn contains_addr(&self, addr: &Address) -> bool {
|
||||
// right shift by 32 is not legal
|
||||
if self.prefix_len == 0 { return true }
|
||||
if self.prefix_len == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
let shift = 32 - self.prefix_len;
|
||||
let self_prefix = NetworkEndian::read_u32(self.address.as_bytes()) >> shift;
|
||||
|
@ -245,7 +261,7 @@ impl defmt::Format for Cidr {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
mod field {
|
||||
|
@ -265,7 +281,6 @@ mod field {
|
|||
|
||||
pub const HEADER_LEN: usize = field::DST_ADDR.end;
|
||||
|
||||
|
||||
impl<T: AsRef<[u8]>> Packet<T> {
|
||||
/// Imbue a raw octet buffer with IPv4 packet structure.
|
||||
pub fn new_unchecked(buffer: T) -> Packet<T> {
|
||||
|
@ -414,7 +429,9 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
/// # Fuzzing
|
||||
/// This function always returns `true` when fuzzing.
|
||||
pub fn verify_checksum(&self) -> bool {
|
||||
if cfg!(fuzzing) { return true }
|
||||
if cfg!(fuzzing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let data = self.buffer.as_ref();
|
||||
checksum::data(&data[..self.header_len() as usize]) == !0
|
||||
|
@ -576,22 +593,32 @@ pub struct Repr {
|
|||
pub dst_addr: Address,
|
||||
pub protocol: Protocol,
|
||||
pub payload_len: usize,
|
||||
pub hop_limit: u8
|
||||
pub hop_limit: u8,
|
||||
}
|
||||
|
||||
impl Repr {
|
||||
/// Parse an Internet Protocol version 4 packet and return a high-level representation.
|
||||
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>,
|
||||
checksum_caps: &ChecksumCapabilities) -> Result<Repr> {
|
||||
pub fn parse<T: AsRef<[u8]> + ?Sized>(
|
||||
packet: &Packet<&T>,
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) -> Result<Repr> {
|
||||
// Version 4 is expected.
|
||||
if packet.version() != 4 { return Err(Error::Malformed) }
|
||||
if packet.version() != 4 {
|
||||
return Err(Error::Malformed);
|
||||
}
|
||||
// Valid checksum is expected.
|
||||
if checksum_caps.ipv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) }
|
||||
if checksum_caps.ipv4.rx() && !packet.verify_checksum() {
|
||||
return Err(Error::Checksum);
|
||||
}
|
||||
// We do not support fragmentation.
|
||||
if packet.more_frags() || packet.frag_offset() != 0 { return Err(Error::Fragmented) }
|
||||
if packet.more_frags() || packet.frag_offset() != 0 {
|
||||
return Err(Error::Fragmented);
|
||||
}
|
||||
// Since the packet is not fragmented, it must include the entire payload.
|
||||
let payload_len = packet.total_len() as usize - packet.header_len() as usize;
|
||||
if packet.payload().len() < payload_len { return Err(Error::Truncated) }
|
||||
if packet.payload().len() < payload_len {
|
||||
return Err(Error::Truncated);
|
||||
}
|
||||
|
||||
// All DSCP values are acceptable, since they are of no concern to receiving endpoint.
|
||||
// All ECN values are acceptable, since ECN requires opt-in from both endpoints.
|
||||
|
@ -601,7 +628,7 @@ impl Repr {
|
|||
dst_addr: packet.dst_addr(),
|
||||
protocol: packet.protocol(),
|
||||
payload_len: payload_len,
|
||||
hop_limit: packet.hop_limit()
|
||||
hop_limit: packet.hop_limit(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -612,7 +639,11 @@ impl Repr {
|
|||
}
|
||||
|
||||
/// Emit a high-level representation into an Internet Protocol version 4 packet.
|
||||
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>, checksum_caps: &ChecksumCapabilities) {
|
||||
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
packet: &mut Packet<T>,
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) {
|
||||
packet.set_version(4);
|
||||
packet.set_header_len(field::DST_ADDR.end as u8);
|
||||
packet.set_dscp(0);
|
||||
|
@ -645,8 +676,14 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
|||
Ok(repr) => write!(f, "{}", repr),
|
||||
Err(err) => {
|
||||
write!(f, "IPv4 ({})", err)?;
|
||||
write!(f, " src={} dst={} proto={} hop_limit={}",
|
||||
self.src_addr(), self.dst_addr(), self.protocol(), self.hop_limit())?;
|
||||
write!(
|
||||
f,
|
||||
" src={} dst={} proto={} hop_limit={}",
|
||||
self.src_addr(),
|
||||
self.dst_addr(),
|
||||
self.protocol(),
|
||||
self.hop_limit()
|
||||
)?;
|
||||
if self.version() != 4 {
|
||||
write!(f, " ver={}", self.version())?;
|
||||
}
|
||||
|
@ -680,32 +717,36 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
|||
|
||||
impl fmt::Display for Repr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "IPv4 src={} dst={} proto={}",
|
||||
self.src_addr, self.dst_addr, self.protocol)
|
||||
write!(
|
||||
f,
|
||||
"IPv4 src={} dst={} proto={}",
|
||||
self.src_addr, self.dst_addr, self.protocol
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
use crate::wire::ip::checksum::format_checksum;
|
||||
|
||||
let checksum_caps = ChecksumCapabilities::ignored();
|
||||
|
||||
let (ip_repr, payload) = match Packet::new_checked(buffer) {
|
||||
Err(err) => return write!(f, "{}({})", indent, err),
|
||||
Ok(ip_packet) => {
|
||||
match Repr::parse(&ip_packet, &checksum_caps) {
|
||||
Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) {
|
||||
Err(_) => return Ok(()),
|
||||
Ok(ip_repr) => {
|
||||
write!(f, "{}{}", indent, ip_repr)?;
|
||||
format_checksum(f, ip_packet.verify_checksum())?;
|
||||
(ip_repr, ip_packet.payload())
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
pretty_print_ip_payload(f, indent, ip_repr, payload)
|
||||
|
@ -716,20 +757,12 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
|||
mod test {
|
||||
use super::*;
|
||||
|
||||
static PACKET_BYTES: [u8; 30] =
|
||||
[0x45, 0x00, 0x00, 0x1e,
|
||||
0x01, 0x02, 0x62, 0x03,
|
||||
0x1a, 0x01, 0xd5, 0x6e,
|
||||
0x11, 0x12, 0x13, 0x14,
|
||||
0x21, 0x22, 0x23, 0x24,
|
||||
0xaa, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xff];
|
||||
static PACKET_BYTES: [u8; 30] = [
|
||||
0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12, 0x13,
|
||||
0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
static PAYLOAD_BYTES: [u8; 10] =
|
||||
[0xaa, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xff];
|
||||
static PAYLOAD_BYTES: [u8; 10] = [0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff];
|
||||
|
||||
#[test]
|
||||
fn test_deconstruct() {
|
||||
|
@ -781,10 +814,14 @@ mod test {
|
|||
bytes.extend(&PACKET_BYTES[..]);
|
||||
bytes.push(0);
|
||||
|
||||
assert_eq!(Packet::new_unchecked(&bytes).payload().len(),
|
||||
PAYLOAD_BYTES.len());
|
||||
assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(),
|
||||
PAYLOAD_BYTES.len());
|
||||
assert_eq!(
|
||||
Packet::new_unchecked(&bytes).payload().len(),
|
||||
PAYLOAD_BYTES.len()
|
||||
);
|
||||
assert_eq!(
|
||||
Packet::new_unchecked(&mut bytes).payload_mut().len(),
|
||||
PAYLOAD_BYTES.len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -793,20 +830,15 @@ mod test {
|
|||
bytes.extend(&PACKET_BYTES[..]);
|
||||
Packet::new_unchecked(&mut bytes).set_total_len(128);
|
||||
|
||||
assert_eq!(Packet::new_checked(&bytes).unwrap_err(),
|
||||
Error::Truncated);
|
||||
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated);
|
||||
}
|
||||
|
||||
static REPR_PACKET_BYTES: [u8; 24] =
|
||||
[0x45, 0x00, 0x00, 0x18,
|
||||
0x00, 0x00, 0x40, 0x00,
|
||||
0x40, 0x01, 0xd2, 0x79,
|
||||
0x11, 0x12, 0x13, 0x14,
|
||||
0x21, 0x22, 0x23, 0x24,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static REPR_PACKET_BYTES: [u8; 24] = [
|
||||
0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x79, 0x11, 0x12, 0x13,
|
||||
0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
static REPR_PAYLOAD_BYTES: [u8; 4] =
|
||||
[0xaa, 0x00, 0x00, 0xff];
|
||||
static REPR_PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
|
||||
|
||||
fn packet_repr() -> Repr {
|
||||
Repr {
|
||||
|
@ -814,7 +846,7 @@ mod test {
|
|||
dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
|
||||
protocol: Protocol::Icmp,
|
||||
payload_len: 4,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -833,7 +865,10 @@ mod test {
|
|||
packet.set_version(6);
|
||||
packet.fill_checksum();
|
||||
let packet = Packet::new_unchecked(&*packet.into_inner());
|
||||
assert_eq!(Repr::parse(&packet, &ChecksumCapabilities::default()), Err(Error::Malformed));
|
||||
assert_eq!(
|
||||
Repr::parse(&packet, &ChecksumCapabilities::default()),
|
||||
Err(Error::Malformed)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -876,15 +911,21 @@ mod test {
|
|||
let cidr = Cidr::new(Address::new(192, 168, 1, 10), 24);
|
||||
|
||||
let inside_subnet = [
|
||||
[192, 168, 1, 0], [192, 168, 1, 1],
|
||||
[192, 168, 1, 2], [192, 168, 1, 10],
|
||||
[192, 168, 1, 127], [192, 168, 1, 255],
|
||||
[192, 168, 1, 0],
|
||||
[192, 168, 1, 1],
|
||||
[192, 168, 1, 2],
|
||||
[192, 168, 1, 10],
|
||||
[192, 168, 1, 127],
|
||||
[192, 168, 1, 255],
|
||||
];
|
||||
|
||||
let outside_subnet = [
|
||||
[192, 168, 0, 0], [127, 0, 0, 1],
|
||||
[192, 168, 2, 0], [192, 168, 0, 255],
|
||||
[ 0, 0, 0, 0], [255, 255, 255, 255],
|
||||
[192, 168, 0, 0],
|
||||
[127, 0, 0, 1],
|
||||
[192, 168, 2, 0],
|
||||
[192, 168, 0, 255],
|
||||
[0, 0, 0, 0],
|
||||
[255, 255, 255, 255],
|
||||
];
|
||||
|
||||
let subnets = [
|
||||
|
@ -908,13 +949,17 @@ mod test {
|
|||
assert!(!cidr.contains_addr(&addr));
|
||||
}
|
||||
|
||||
for subnet in subnets.iter().map(
|
||||
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) {
|
||||
for subnet in subnets
|
||||
.iter()
|
||||
.map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p))
|
||||
{
|
||||
assert!(cidr.contains_subnet(&subnet));
|
||||
}
|
||||
|
||||
for subnet in not_subnets.iter().map(
|
||||
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) {
|
||||
for subnet in not_subnets
|
||||
.iter()
|
||||
.map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p))
|
||||
{
|
||||
assert!(!cidr.contains_subnet(&subnet));
|
||||
}
|
||||
|
||||
|
@ -924,94 +969,175 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_cidr_from_netmask() {
|
||||
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(),
|
||||
true);
|
||||
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(),
|
||||
true);
|
||||
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(),
|
||||
Cidr::new(Address([0, 0, 0, 1]), 24));
|
||||
assert_eq!(Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(),
|
||||
Cidr::new(Address([192, 168, 0, 1]), 16));
|
||||
assert_eq!(Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(),
|
||||
Cidr::new(Address([172, 16, 0, 1]), 12));
|
||||
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(),
|
||||
Cidr::new(Address([255, 255, 255, 1]), 24));
|
||||
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255])).unwrap(),
|
||||
Cidr::new(Address([255, 255, 255, 255]), 32));
|
||||
assert_eq!(
|
||||
Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(),
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(),
|
||||
Cidr::new(Address([0, 0, 0, 1]), 24)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(),
|
||||
Cidr::new(Address([192, 168, 0, 1]), 16)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(),
|
||||
Cidr::new(Address([172, 16, 0, 1]), 12)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(),
|
||||
Cidr::new(Address([255, 255, 255, 1]), 24)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255]))
|
||||
.unwrap(),
|
||||
Cidr::new(Address([255, 255, 255, 255]), 32)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr_netmask() {
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).netmask(),
|
||||
Address([0, 0, 0, 0]));
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).netmask(),
|
||||
Address([255, 255, 255, 0]));
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).netmask(),
|
||||
Address([255, 255, 255, 255]));
|
||||
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).netmask(),
|
||||
Address([255, 0, 0, 0]));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).netmask(),
|
||||
Address([255, 255, 0, 0]));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).netmask(),
|
||||
Address([255, 255, 0, 0]));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).netmask(),
|
||||
Address([255, 255, 128, 0]));
|
||||
assert_eq!(Cidr::new(Address([172, 16, 0, 0]), 12).netmask(),
|
||||
Address([255, 240, 0, 0]));
|
||||
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).netmask(),
|
||||
Address([255, 255, 255, 0]));
|
||||
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).netmask(),
|
||||
Address([255, 255, 255, 255]));
|
||||
assert_eq!(
|
||||
Cidr::new(Address([0, 0, 0, 0]), 0).netmask(),
|
||||
Address([0, 0, 0, 0])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([0, 0, 0, 1]), 24).netmask(),
|
||||
Address([255, 255, 255, 0])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([0, 0, 0, 0]), 32).netmask(),
|
||||
Address([255, 255, 255, 255])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([127, 0, 0, 0]), 8).netmask(),
|
||||
Address([255, 0, 0, 0])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 0, 0]), 16).netmask(),
|
||||
Address([255, 255, 0, 0])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 1, 1]), 16).netmask(),
|
||||
Address([255, 255, 0, 0])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 1, 1]), 17).netmask(),
|
||||
Address([255, 255, 128, 0])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([172, 16, 0, 0]), 12).netmask(),
|
||||
Address([255, 240, 0, 0])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([255, 255, 255, 1]), 24).netmask(),
|
||||
Address([255, 255, 255, 0])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([255, 255, 255, 255]), 32).netmask(),
|
||||
Address([255, 255, 255, 255])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr_broadcast() {
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(),
|
||||
Address([255, 255, 255, 255]));
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(),
|
||||
Address([0, 0, 0, 255]));
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(),
|
||||
None);
|
||||
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(),
|
||||
Address([127, 255, 255, 255]));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).broadcast().unwrap(),
|
||||
Address([192, 168, 255, 255]));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).broadcast().unwrap(),
|
||||
Address([192, 168, 255, 255]));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).broadcast().unwrap(),
|
||||
Address([192, 168, 127, 255]));
|
||||
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(),
|
||||
Address([172, 31, 255, 255]));
|
||||
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).broadcast().unwrap(),
|
||||
Address([255, 255, 255, 255]));
|
||||
assert_eq!(Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(),
|
||||
None);
|
||||
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(),
|
||||
None);
|
||||
|
||||
assert_eq!(
|
||||
Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(),
|
||||
Address([255, 255, 255, 255])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(),
|
||||
Address([0, 0, 0, 255])
|
||||
);
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(), None);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(),
|
||||
Address([127, 255, 255, 255])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 0, 0]), 16)
|
||||
.broadcast()
|
||||
.unwrap(),
|
||||
Address([192, 168, 255, 255])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 1, 1]), 16)
|
||||
.broadcast()
|
||||
.unwrap(),
|
||||
Address([192, 168, 255, 255])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 1, 1]), 17)
|
||||
.broadcast()
|
||||
.unwrap(),
|
||||
Address([192, 168, 127, 255])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(),
|
||||
Address([172, 31, 255, 255])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([255, 255, 255, 1]), 24)
|
||||
.broadcast()
|
||||
.unwrap(),
|
||||
Address([255, 255, 255, 255])
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cidr_network() {
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).network(),
|
||||
Cidr::new(Address([0, 0, 0, 0]), 0));
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).network(),
|
||||
Cidr::new(Address([0, 0, 0, 0]), 24));
|
||||
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).network(),
|
||||
Cidr::new(Address([0, 0, 0, 0]), 32));
|
||||
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).network(),
|
||||
Cidr::new(Address([127, 0, 0, 0]), 8));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).network(),
|
||||
Cidr::new(Address([192, 168, 0, 0]), 16));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).network(),
|
||||
Cidr::new(Address([192, 168, 0, 0]), 16));
|
||||
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).network(),
|
||||
Cidr::new(Address([192, 168, 0, 0]), 17));
|
||||
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).network(),
|
||||
Cidr::new(Address([172, 16, 0, 0]), 12));
|
||||
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).network(),
|
||||
Cidr::new(Address([255, 255, 255, 0]), 24));
|
||||
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).network(),
|
||||
Cidr::new(Address([255, 255, 255, 255]), 32));
|
||||
assert_eq!(
|
||||
Cidr::new(Address([0, 0, 0, 0]), 0).network(),
|
||||
Cidr::new(Address([0, 0, 0, 0]), 0)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([0, 0, 0, 1]), 24).network(),
|
||||
Cidr::new(Address([0, 0, 0, 0]), 24)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([0, 0, 0, 0]), 32).network(),
|
||||
Cidr::new(Address([0, 0, 0, 0]), 32)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([127, 0, 0, 0]), 8).network(),
|
||||
Cidr::new(Address([127, 0, 0, 0]), 8)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 0, 0]), 16).network(),
|
||||
Cidr::new(Address([192, 168, 0, 0]), 16)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 1, 1]), 16).network(),
|
||||
Cidr::new(Address([192, 168, 0, 0]), 16)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([192, 168, 1, 1]), 17).network(),
|
||||
Cidr::new(Address([192, 168, 0, 0]), 17)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([172, 16, 0, 1]), 12).network(),
|
||||
Cidr::new(Address([172, 16, 0, 0]), 12)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([255, 255, 255, 1]), 24).network(),
|
||||
Cidr::new(Address([255, 255, 255, 0]), 24)
|
||||
);
|
||||
assert_eq!(
|
||||
Cidr::new(Address([255, 255, 255, 255]), 32).network(),
|
||||
Cidr::new(Address([255, 255, 255, 255]), 32)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
455
src/wire/ipv6.rs
455
src/wire/ipv6.rs
|
@ -1,12 +1,12 @@
|
|||
#![deny(missing_docs)]
|
||||
|
||||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::fmt;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::wire::ip::pretty_print_ip_payload;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::ipv4;
|
||||
use crate::{Error, Result};
|
||||
|
||||
pub use super::IpProtocol as Protocol;
|
||||
|
||||
|
@ -29,28 +29,30 @@ impl Address {
|
|||
/// The link-local [all routers multicast address].
|
||||
///
|
||||
/// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
|
||||
pub const LINK_LOCAL_ALL_NODES: Address =
|
||||
Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||
pub const LINK_LOCAL_ALL_NODES: Address = Address([
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01,
|
||||
]);
|
||||
|
||||
/// The link-local [all nodes multicast address].
|
||||
///
|
||||
/// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
|
||||
pub const LINK_LOCAL_ALL_ROUTERS: Address =
|
||||
Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]);
|
||||
pub const LINK_LOCAL_ALL_ROUTERS: Address = Address([
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02,
|
||||
]);
|
||||
|
||||
/// The [loopback address].
|
||||
///
|
||||
/// [loopback address]: https://tools.ietf.org/html/rfc4291#section-2.5.3
|
||||
pub const LOOPBACK: Address =
|
||||
Address([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
|
||||
pub const LOOPBACK: Address = Address([
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01,
|
||||
]);
|
||||
|
||||
/// Construct an IPv6 address from parts.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16,
|
||||
a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
|
||||
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
|
||||
let mut addr = [0u8; 16];
|
||||
NetworkEndian::write_u16(&mut addr[0..2], a0);
|
||||
NetworkEndian::write_u16(&mut addr[2..4], a1);
|
||||
|
@ -127,8 +129,7 @@ impl Address {
|
|||
///
|
||||
/// [link-local]: https://tools.ietf.org/html/rfc4291#section-2.5.6
|
||||
pub fn is_link_local(&self) -> bool {
|
||||
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00]
|
||||
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
|
||||
}
|
||||
|
||||
/// Query whether the IPv6 address is the [loopback address].
|
||||
|
@ -149,7 +150,9 @@ impl Address {
|
|||
/// Convert an IPv4 mapped IPv6 address to an IPv4 address.
|
||||
pub fn as_ipv4(&self) -> Option<ipv4::Address> {
|
||||
if self.is_ipv4_mapped() {
|
||||
Some(ipv4::Address::new(self.0[12], self.0[13], self.0[14], self.0[15]))
|
||||
Some(ipv4::Address::new(
|
||||
self.0[12], self.0[13], self.0[14], self.0[15],
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -165,7 +168,7 @@ impl Address {
|
|||
let idx = (mask as usize) / 8;
|
||||
let modulus = (mask as usize) % 8;
|
||||
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 {
|
||||
let part = second[0];
|
||||
bytes[idx] = part & (!(0xff >> modulus) as u8);
|
||||
|
@ -180,8 +183,10 @@ impl Address {
|
|||
/// unicast.
|
||||
pub fn solicited_node(&self) -> Address {
|
||||
assert!(self.is_unicast());
|
||||
let mut bytes = [0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
let mut bytes = [
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
];
|
||||
bytes[14..].copy_from_slice(&self.0[14..]);
|
||||
Address(bytes)
|
||||
}
|
||||
|
@ -204,7 +209,11 @@ impl From<Address> for ::std::net::Ipv6Addr {
|
|||
impl fmt::Display for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.is_ipv4_mapped() {
|
||||
return write!(f, "::ffff:{}.{}.{}.{}", self.0[12], self.0[13], self.0[14], self.0[15])
|
||||
return write!(
|
||||
f,
|
||||
"::ffff:{}.{}.{}.{}",
|
||||
self.0[12], self.0[13], self.0[14], self.0[15]
|
||||
);
|
||||
}
|
||||
|
||||
// The string representation of an IPv6 address should
|
||||
|
@ -217,7 +226,7 @@ impl fmt::Display for Address {
|
|||
Head,
|
||||
HeadBody,
|
||||
Tail,
|
||||
TailBody
|
||||
TailBody,
|
||||
}
|
||||
let mut words = [0u16; 8];
|
||||
self.write_parts(&mut words);
|
||||
|
@ -229,7 +238,7 @@ impl fmt::Display for Address {
|
|||
(0, &State::Head) | (0, &State::HeadBody) => {
|
||||
write!(f, "::")?;
|
||||
State::Tail
|
||||
},
|
||||
}
|
||||
// Continue iterating without writing any characters until
|
||||
// we hit anothing non-zero value.
|
||||
(0, &State::Tail) => State::Tail,
|
||||
|
@ -238,11 +247,11 @@ impl fmt::Display for Address {
|
|||
(_, &State::Head) => {
|
||||
write!(f, "{:x}", word)?;
|
||||
State::HeadBody
|
||||
},
|
||||
}
|
||||
(_, &State::Tail) => {
|
||||
write!(f, "{:x}", word)?;
|
||||
State::TailBody
|
||||
},
|
||||
}
|
||||
// Write the u16 with a leading colon when parsing a value
|
||||
// that isn't the first in a section
|
||||
(_, &State::HeadBody) | (_, &State::TailBody) => {
|
||||
|
@ -260,8 +269,9 @@ impl fmt::Display for Address {
|
|||
impl From<ipv4::Address> for Address {
|
||||
fn from(address: ipv4::Address) -> Self {
|
||||
let octets = address.0;
|
||||
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff,
|
||||
octets[0], octets[1], octets[2], octets[3]])
|
||||
Address([
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, octets[0], octets[1], octets[2], octets[3],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,11 +288,12 @@ impl Cidr {
|
|||
/// The [solicited node prefix].
|
||||
///
|
||||
/// [solicited node prefix]: https://tools.ietf.org/html/rfc4291#section-2.7.1
|
||||
pub const SOLICITED_NODE_PREFIX: Cidr =
|
||||
Cidr {
|
||||
address: Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00]),
|
||||
prefix_len: 104
|
||||
pub const SOLICITED_NODE_PREFIX: Cidr = Cidr {
|
||||
address: Address([
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00,
|
||||
0x00, 0x00,
|
||||
]),
|
||||
prefix_len: 104,
|
||||
};
|
||||
|
||||
/// Create an IPv6 CIDR block from the given address and prefix length.
|
||||
|
@ -291,7 +302,10 @@ impl Cidr {
|
|||
/// This function panics if the prefix length is larger than 128.
|
||||
pub fn new(address: Address, prefix_len: u8) -> Cidr {
|
||||
assert!(prefix_len <= 128);
|
||||
Cidr { address, prefix_len }
|
||||
Cidr {
|
||||
address,
|
||||
prefix_len,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the address of this IPv6 CIDR block.
|
||||
|
@ -308,7 +322,9 @@ impl Cidr {
|
|||
/// the given address.
|
||||
pub fn contains_addr(&self, addr: &Address) -> bool {
|
||||
// right shift by 128 is not legal
|
||||
if self.prefix_len == 0 { return true }
|
||||
if self.prefix_len == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
let shift = 128 - self.prefix_len;
|
||||
self.address.mask(shift) == addr.mask(shift)
|
||||
|
@ -332,7 +348,7 @@ impl fmt::Display for Cidr {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
// Ranges and constants describing the IPv6 header
|
||||
|
@ -610,7 +626,7 @@ pub struct Repr {
|
|||
/// Length of the payload including the extension headers.
|
||||
pub payload_len: usize,
|
||||
/// The 8-bit hop limit field.
|
||||
pub hop_limit: u8
|
||||
pub hop_limit: u8,
|
||||
}
|
||||
|
||||
impl Repr {
|
||||
|
@ -618,13 +634,15 @@ impl Repr {
|
|||
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> {
|
||||
// Ensure basic accessors will work
|
||||
packet.check_len()?;
|
||||
if packet.version() != 6 { return Err(Error::Malformed); }
|
||||
if packet.version() != 6 {
|
||||
return Err(Error::Malformed);
|
||||
}
|
||||
Ok(Repr {
|
||||
src_addr: packet.src_addr(),
|
||||
dst_addr: packet.dst_addr(),
|
||||
next_header: packet.next_header(),
|
||||
payload_len: packet.payload_len() as usize,
|
||||
hop_limit: packet.hop_limit()
|
||||
hop_limit: packet.hop_limit(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -651,29 +669,33 @@ impl Repr {
|
|||
|
||||
impl fmt::Display for Repr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
|
||||
self.src_addr, self.dst_addr, self.next_header, self.hop_limit)
|
||||
write!(
|
||||
f,
|
||||
"IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
|
||||
self.src_addr, self.dst_addr, self.next_header, self.hop_limit
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
// TODO: This is very similar to the implementation for IPv4. Make
|
||||
// a way to have less copy and pasted code here.
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
let (ip_repr, payload) = match Packet::new_checked(buffer) {
|
||||
Err(err) => return write!(f, "{}({})", indent, err),
|
||||
Ok(ip_packet) => {
|
||||
match Repr::parse(&ip_packet) {
|
||||
Ok(ip_packet) => match Repr::parse(&ip_packet) {
|
||||
Err(_) => return Ok(()),
|
||||
Ok(ip_repr) => {
|
||||
write!(f, "{}{}", indent, ip_repr)?;
|
||||
(ip_repr, ip_packet.payload())
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
pretty_print_ip_payload(f, indent, ip_repr, payload)
|
||||
|
@ -682,18 +704,18 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::Error;
|
||||
use super::{Address, Cidr};
|
||||
use super::{Packet, Protocol, Repr};
|
||||
use crate::wire::pretty_print::{PrettyPrinter};
|
||||
use crate::wire::pretty_print::PrettyPrinter;
|
||||
use crate::Error;
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::ipv4::Address as Ipv4Address;
|
||||
|
||||
static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01]);
|
||||
static LINK_LOCAL_ADDR: Address = Address([
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01,
|
||||
]);
|
||||
#[test]
|
||||
fn test_basic_multicast() {
|
||||
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unspecified());
|
||||
|
@ -724,48 +746,62 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_address_format() {
|
||||
assert_eq!("ff02::1",
|
||||
format!("{}", Address::LINK_LOCAL_ALL_NODES));
|
||||
assert_eq!("fe80::1",
|
||||
format!("{}", LINK_LOCAL_ADDR));
|
||||
assert_eq!("fe80::7f00:0:1",
|
||||
format!("{}", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)));
|
||||
assert_eq!("::",
|
||||
format!("{}", Address::UNSPECIFIED));
|
||||
assert_eq!("::1",
|
||||
format!("{}", Address::LOOPBACK));
|
||||
assert_eq!("ff02::1", format!("{}", Address::LINK_LOCAL_ALL_NODES));
|
||||
assert_eq!("fe80::1", format!("{}", LINK_LOCAL_ADDR));
|
||||
assert_eq!(
|
||||
"fe80::7f00:0:1",
|
||||
format!(
|
||||
"{}",
|
||||
Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)
|
||||
)
|
||||
);
|
||||
assert_eq!("::", format!("{}", Address::UNSPECIFIED));
|
||||
assert_eq!("::1", format!("{}", Address::LOOPBACK));
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
assert_eq!("::ffff:192.168.1.1",
|
||||
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1))));
|
||||
assert_eq!(
|
||||
"::ffff:192.168.1.1",
|
||||
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
|
||||
Address::LINK_LOCAL_ALL_NODES);
|
||||
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
|
||||
Address::LINK_LOCAL_ALL_ROUTERS);
|
||||
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1),
|
||||
Address::LOOPBACK);
|
||||
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0),
|
||||
Address::UNSPECIFIED);
|
||||
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
|
||||
LINK_LOCAL_ADDR);
|
||||
assert_eq!(
|
||||
Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
|
||||
Address::LINK_LOCAL_ALL_NODES
|
||||
);
|
||||
assert_eq!(
|
||||
Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
|
||||
Address::LINK_LOCAL_ALL_ROUTERS
|
||||
);
|
||||
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1), Address::LOOPBACK);
|
||||
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0), Address::UNSPECIFIED);
|
||||
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), LINK_LOCAL_ADDR);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_parts() {
|
||||
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
|
||||
Address::LINK_LOCAL_ALL_NODES);
|
||||
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
|
||||
Address::LINK_LOCAL_ALL_ROUTERS);
|
||||
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]),
|
||||
Address::LOOPBACK);
|
||||
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
Address::UNSPECIFIED);
|
||||
assert_eq!(Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]),
|
||||
LINK_LOCAL_ADDR);
|
||||
assert_eq!(
|
||||
Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
|
||||
Address::LINK_LOCAL_ALL_NODES
|
||||
);
|
||||
assert_eq!(
|
||||
Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
|
||||
Address::LINK_LOCAL_ALL_ROUTERS
|
||||
);
|
||||
assert_eq!(
|
||||
Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]),
|
||||
Address::LOOPBACK
|
||||
);
|
||||
assert_eq!(
|
||||
Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
Address::UNSPECIFIED
|
||||
);
|
||||
assert_eq!(
|
||||
Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]),
|
||||
LINK_LOCAL_ADDR
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -788,18 +824,36 @@ mod test {
|
|||
#[test]
|
||||
fn test_mask() {
|
||||
let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1);
|
||||
assert_eq!(addr.mask(11), [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(addr.mask(15), [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(addr.mask(26), [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(addr.mask(128), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
|
||||
assert_eq!(addr.mask(127), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
assert_eq!(
|
||||
addr.mask(11),
|
||||
[0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
);
|
||||
assert_eq!(
|
||||
addr.mask(15),
|
||||
[0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
);
|
||||
assert_eq!(
|
||||
addr.mask(26),
|
||||
[0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
);
|
||||
assert_eq!(
|
||||
addr.mask(128),
|
||||
[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
|
||||
);
|
||||
assert_eq!(
|
||||
addr.mask(127),
|
||||
[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
#[test]
|
||||
fn test_is_ipv4_mapped() {
|
||||
assert_eq!(false, Address::UNSPECIFIED.is_ipv4_mapped());
|
||||
assert_eq!(true, Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped());
|
||||
assert_eq!(
|
||||
true,
|
||||
Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped()
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
|
@ -814,10 +868,14 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
#[test]
|
||||
fn test_from_ipv4_address() {
|
||||
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]),
|
||||
Address::from(Ipv4Address::new(192, 168, 1, 1)));
|
||||
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]),
|
||||
Address::from(Ipv4Address::new(222, 1, 41, 90)));
|
||||
assert_eq!(
|
||||
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]),
|
||||
Address::from(Ipv4Address::new(192, 168, 1, 1))
|
||||
);
|
||||
assert_eq!(
|
||||
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]),
|
||||
Address::from(Ipv4Address::new(222, 1, 41, 90))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -825,52 +883,96 @@ mod test {
|
|||
let cidr = Cidr::new(LINK_LOCAL_ADDR, 64);
|
||||
|
||||
let inside_subnet = [
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x02,
|
||||
],
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
|
||||
0x77, 0x88,
|
||||
],
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
],
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xff,
|
||||
],
|
||||
];
|
||||
|
||||
let outside_subnet = [
|
||||
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
],
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
],
|
||||
[
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
],
|
||||
[
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x02,
|
||||
],
|
||||
];
|
||||
|
||||
let subnets = [
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||
65),
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
128),
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78],
|
||||
96)
|
||||
(
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff,
|
||||
],
|
||||
65,
|
||||
),
|
||||
(
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01,
|
||||
],
|
||||
128,
|
||||
),
|
||||
(
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
|
||||
0x34, 0x56, 0x78,
|
||||
],
|
||||
96,
|
||||
),
|
||||
];
|
||||
|
||||
let not_subnets = [
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||
63),
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||
64),
|
||||
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
|
||||
65),
|
||||
([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
|
||||
128)
|
||||
(
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff,
|
||||
],
|
||||
63,
|
||||
),
|
||||
(
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff,
|
||||
],
|
||||
64,
|
||||
),
|
||||
(
|
||||
[
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff,
|
||||
],
|
||||
65,
|
||||
),
|
||||
(
|
||||
[
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01,
|
||||
],
|
||||
128,
|
||||
),
|
||||
];
|
||||
|
||||
for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
|
||||
|
@ -881,13 +983,11 @@ mod test {
|
|||
assert!(!cidr.contains_addr(&addr));
|
||||
}
|
||||
|
||||
for subnet in subnets.iter().map(
|
||||
|&(a, p)| Cidr::new(Address(a), p)) {
|
||||
for subnet in subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) {
|
||||
assert!(cidr.contains_subnet(&subnet));
|
||||
}
|
||||
|
||||
for subnet in not_subnets.iter().map(
|
||||
|&(a, p)| Cidr::new(Address(a), p)) {
|
||||
for subnet in not_subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) {
|
||||
assert!(!cidr.contains_subnet(&subnet));
|
||||
}
|
||||
|
||||
|
@ -907,33 +1007,26 @@ mod test {
|
|||
let _ = Address::from_parts(&[0u16; 7]);
|
||||
}
|
||||
|
||||
static REPR_PACKET_BYTES: [u8; 52] = [0x60, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x11, 0x40,
|
||||
0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x01, 0x00, 0x02,
|
||||
0x00, 0x0c, 0x02, 0x4e,
|
||||
0xff, 0xff, 0xff, 0xff];
|
||||
static REPR_PAYLOAD_BYTES: [u8; 12] = [0x00, 0x01, 0x00, 0x02,
|
||||
0x00, 0x0c, 0x02, 0x4e,
|
||||
0xff, 0xff, 0xff, 0xff];
|
||||
static REPR_PACKET_BYTES: [u8; 52] = [
|
||||
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00,
|
||||
0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
|
||||
];
|
||||
static REPR_PAYLOAD_BYTES: [u8; 12] = [
|
||||
0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
|
||||
];
|
||||
|
||||
fn packet_repr() -> Repr {
|
||||
Repr {
|
||||
src_addr: Address([0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01]),
|
||||
src_addr: Address([
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
]),
|
||||
dst_addr: Address::LINK_LOCAL_ALL_NODES,
|
||||
next_header: Protocol::Udp,
|
||||
payload_len: 12,
|
||||
hop_limit: 64
|
||||
hop_limit: 64,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -948,10 +1041,13 @@ mod test {
|
|||
assert_eq!(packet.payload_len() as usize, REPR_PAYLOAD_BYTES.len());
|
||||
assert_eq!(packet.next_header(), Protocol::Udp);
|
||||
assert_eq!(packet.hop_limit(), 0x40);
|
||||
assert_eq!(packet.src_addr(), Address([0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01]));
|
||||
assert_eq!(
|
||||
packet.src_addr(),
|
||||
Address([
|
||||
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01
|
||||
])
|
||||
);
|
||||
assert_eq!(packet.dst_addr(), Address::LINK_LOCAL_ALL_NODES);
|
||||
assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]);
|
||||
}
|
||||
|
@ -976,15 +1072,14 @@ mod test {
|
|||
packet.set_hop_limit(0xfe);
|
||||
packet.set_src_addr(Address::LINK_LOCAL_ALL_ROUTERS);
|
||||
packet.set_dst_addr(Address::LINK_LOCAL_ALL_NODES);
|
||||
packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
|
||||
packet
|
||||
.payload_mut()
|
||||
.copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
|
||||
let mut expected_bytes = [
|
||||
0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe,
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
];
|
||||
let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len();
|
||||
expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
|
||||
|
@ -998,10 +1093,14 @@ mod test {
|
|||
bytes.extend(&REPR_PACKET_BYTES[..]);
|
||||
bytes.push(0);
|
||||
|
||||
assert_eq!(Packet::new_unchecked(&bytes).payload().len(),
|
||||
REPR_PAYLOAD_BYTES.len());
|
||||
assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(),
|
||||
REPR_PAYLOAD_BYTES.len());
|
||||
assert_eq!(
|
||||
Packet::new_unchecked(&bytes).payload().len(),
|
||||
REPR_PAYLOAD_BYTES.len()
|
||||
);
|
||||
assert_eq!(
|
||||
Packet::new_unchecked(&mut bytes).payload_mut().len(),
|
||||
REPR_PAYLOAD_BYTES.len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1010,8 +1109,7 @@ mod test {
|
|||
bytes.extend(&REPR_PACKET_BYTES[..]);
|
||||
Packet::new_unchecked(&mut bytes).set_payload_len(0x80);
|
||||
|
||||
assert_eq!(Packet::new_checked(&bytes).unwrap_err(),
|
||||
Error::Truncated);
|
||||
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1063,7 +1161,12 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_pretty_print() {
|
||||
assert_eq!(format!("{}", PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])),
|
||||
"\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4");
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{}",
|
||||
PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])
|
||||
),
|
||||
"\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use core::fmt;
|
||||
use crate::{Error, Result};
|
||||
use core::fmt;
|
||||
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
|
@ -9,7 +9,7 @@ pub use super::IpProtocol as Protocol;
|
|||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Header<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
// Format of the Fragment Header
|
||||
|
@ -170,17 +170,19 @@ pub struct Repr {
|
|||
pub more_frags: bool,
|
||||
/// The identification for every packet that is fragmented.
|
||||
pub ident: u32,
|
||||
|
||||
}
|
||||
|
||||
impl Repr {
|
||||
/// Parse an IPv6 Fragment Header and return a high-level representation.
|
||||
pub fn parse<T>(header: &Header<&T>) -> Result<Repr> where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(header: &Header<&T>) -> Result<Repr>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
Ok(Repr {
|
||||
next_header: header.next_header(),
|
||||
frag_offset: header.frag_offset(),
|
||||
more_frags: header.more_frags(),
|
||||
ident: header.ident()
|
||||
ident: header.ident(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -202,8 +204,11 @@ impl Repr {
|
|||
|
||||
impl<'a> fmt::Display for Repr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "IPv6 Fragment next_hdr={} offset={} more={} ident={}",
|
||||
self.next_header, self.frag_offset, self.more_frags, self.ident)
|
||||
write!(
|
||||
f,
|
||||
"IPv6 Fragment next_hdr={} offset={} more={} ident={}",
|
||||
self.next_header, self.frag_offset, self.more_frags, self.ident
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -212,21 +217,23 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
// A Fragment Header with more fragments remaining
|
||||
static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1,
|
||||
0x0, 0x0, 0x30, 0x39];
|
||||
static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x39];
|
||||
|
||||
// A Fragment Header with no more fragments remaining
|
||||
static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0,
|
||||
0x0, 0x1, 0x9, 0x32];
|
||||
static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0, 0x0, 0x1, 0x9, 0x32];
|
||||
|
||||
#[test]
|
||||
fn test_check_len() {
|
||||
// less than 8 bytes
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len()
|
||||
);
|
||||
// valid
|
||||
assert_eq!(Ok(()),
|
||||
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len());
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -248,24 +255,48 @@ mod test {
|
|||
fn test_repr_parse_valid() {
|
||||
let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
|
||||
let repr = Repr::parse(&header).unwrap();
|
||||
assert_eq!(repr,
|
||||
Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 });
|
||||
assert_eq!(
|
||||
repr,
|
||||
Repr {
|
||||
next_header: Protocol::Tcp,
|
||||
frag_offset: 0,
|
||||
more_frags: true,
|
||||
ident: 12345
|
||||
}
|
||||
);
|
||||
|
||||
let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
|
||||
let repr = Repr::parse(&header).unwrap();
|
||||
assert_eq!(repr,
|
||||
Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 });
|
||||
assert_eq!(
|
||||
repr,
|
||||
Repr {
|
||||
next_header: Protocol::Tcp,
|
||||
frag_offset: 320,
|
||||
more_frags: false,
|
||||
ident: 67890
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_repr_emit() {
|
||||
let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 };
|
||||
let repr = Repr {
|
||||
next_header: Protocol::Tcp,
|
||||
frag_offset: 0,
|
||||
more_frags: true,
|
||||
ident: 12345,
|
||||
};
|
||||
let mut bytes = [0u8; 8];
|
||||
let mut header = Header::new_unchecked(&mut bytes);
|
||||
repr.emit(&mut header);
|
||||
assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..8]);
|
||||
|
||||
let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 };
|
||||
let repr = Repr {
|
||||
next_header: Protocol::Tcp,
|
||||
frag_offset: 320,
|
||||
more_frags: false,
|
||||
ident: 67890,
|
||||
};
|
||||
let mut bytes = [0u8; 8];
|
||||
let mut header = Header::new_unchecked(&mut bytes);
|
||||
repr.emit(&mut header);
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use core::fmt;
|
||||
use crate::{Error, Result};
|
||||
use core::fmt;
|
||||
|
||||
use crate::wire::ipv6option::Ipv6OptionsIterator;
|
||||
pub use super::IpProtocol as Protocol;
|
||||
use crate::wire::ipv6option::Ipv6OptionsIterator;
|
||||
|
||||
/// A read/write wrapper around an IPv6 Hop-by-Hop Options Header.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Header<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
// Format of the Hop-by-Hop Options Header
|
||||
|
@ -111,7 +111,7 @@ impl<T: AsRef<[u8]>> Header<T> {
|
|||
impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> {
|
||||
/// Return the option data.
|
||||
#[inline]
|
||||
pub fn options(&self) -> &'a[u8] {
|
||||
pub fn options(&self) -> &'a [u8] {
|
||||
let data = self.buffer.as_ref();
|
||||
&data[field::OPTIONS(data[field::LENGTH])]
|
||||
}
|
||||
|
@ -165,16 +165,19 @@ pub struct Repr<'a> {
|
|||
/// Length of the Hop-by-Hop Options header in 8-octet units, not including the first 8 octets.
|
||||
pub length: u8,
|
||||
/// The options contained in the Hop-by-Hop Options header.
|
||||
pub options: &'a [u8]
|
||||
pub options: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an IPv6 Hop-by-Hop Options Header and return a high-level representation.
|
||||
pub fn parse<T>(header: &Header<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(header: &Header<&'a T>) -> Result<Repr<'a>>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
Ok(Repr {
|
||||
next_header: header.next_header(),
|
||||
length: header.header_len(),
|
||||
options: header.options()
|
||||
options: header.options(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -199,7 +202,11 @@ impl<'a> Repr<'a> {
|
|||
|
||||
impl<'a> fmt::Display for Repr<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "IPv6 Hop-by-Hop Options next_hdr={} length={} ", self.next_header, self.length)
|
||||
write!(
|
||||
f,
|
||||
"IPv6 Hop-by-Hop Options next_hdr={} length={} ",
|
||||
self.next_header, self.length
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,36 +215,43 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
// A Hop-by-Hop Option header with a PadN option of option data length 4.
|
||||
static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4,
|
||||
0x0, 0x0, 0x0, 0x0];
|
||||
static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4, 0x0, 0x0, 0x0, 0x0];
|
||||
|
||||
// A Hop-by-Hop Option header with a PadN option of option data length 12.
|
||||
static REPR_PACKET_PAD12: [u8; 16] = [0x06, 0x1, 0x1, 0x12,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0];
|
||||
static REPR_PACKET_PAD12: [u8; 16] = [
|
||||
0x06, 0x1, 0x1, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_check_len() {
|
||||
// zero byte buffer
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len()
|
||||
);
|
||||
// no length field
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len()
|
||||
);
|
||||
// less than 8 bytes
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len()
|
||||
);
|
||||
// valid
|
||||
assert_eq!(Ok(()),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
|
||||
assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
|
||||
// valid
|
||||
assert_eq!(Ok(()),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD12).check_len());
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
Header::new_unchecked(&REPR_PACKET_PAD12).check_len()
|
||||
);
|
||||
// length field value greater than number of bytes
|
||||
let header: [u8; 8] = [0x06, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Header::new_unchecked(&header).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new_unchecked(&header).check_len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -259,19 +273,27 @@ mod test {
|
|||
bytes.extend(&REPR_PACKET_PAD4[..]);
|
||||
bytes.push(0);
|
||||
|
||||
assert_eq!(Header::new_unchecked(&bytes).options().len(),
|
||||
REPR_PACKET_PAD4[2..].len());
|
||||
assert_eq!(Header::new_unchecked(&mut bytes).options_mut().len(),
|
||||
REPR_PACKET_PAD4[2..].len());
|
||||
assert_eq!(
|
||||
Header::new_unchecked(&bytes).options().len(),
|
||||
REPR_PACKET_PAD4[2..].len()
|
||||
);
|
||||
assert_eq!(
|
||||
Header::new_unchecked(&mut bytes).options_mut().len(),
|
||||
REPR_PACKET_PAD4[2..].len()
|
||||
);
|
||||
|
||||
let mut bytes = vec![];
|
||||
bytes.extend(&REPR_PACKET_PAD12[..]);
|
||||
bytes.push(0);
|
||||
|
||||
assert_eq!(Header::new_unchecked(&bytes).options().len(),
|
||||
REPR_PACKET_PAD12[2..].len());
|
||||
assert_eq!(Header::new_unchecked(&mut bytes).options_mut().len(),
|
||||
REPR_PACKET_PAD12[2..].len());
|
||||
assert_eq!(
|
||||
Header::new_unchecked(&bytes).options().len(),
|
||||
REPR_PACKET_PAD12[2..].len()
|
||||
);
|
||||
assert_eq!(
|
||||
Header::new_unchecked(&mut bytes).options_mut().len(),
|
||||
REPR_PACKET_PAD12[2..].len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -295,26 +317,44 @@ mod test {
|
|||
fn test_repr_parse_valid() {
|
||||
let header = Header::new_unchecked(&REPR_PACKET_PAD4);
|
||||
let repr = Repr::parse(&header).unwrap();
|
||||
assert_eq!(repr, Repr {
|
||||
next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..]
|
||||
});
|
||||
assert_eq!(
|
||||
repr,
|
||||
Repr {
|
||||
next_header: Protocol::Tcp,
|
||||
length: 0,
|
||||
options: &REPR_PACKET_PAD4[2..]
|
||||
}
|
||||
);
|
||||
|
||||
let header = Header::new_unchecked(&REPR_PACKET_PAD12);
|
||||
let repr = Repr::parse(&header).unwrap();
|
||||
assert_eq!(repr, Repr {
|
||||
next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..]
|
||||
});
|
||||
assert_eq!(
|
||||
repr,
|
||||
Repr {
|
||||
next_header: Protocol::Tcp,
|
||||
length: 1,
|
||||
options: &REPR_PACKET_PAD12[2..]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_repr_emit() {
|
||||
let repr = Repr{ next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..] };
|
||||
let repr = Repr {
|
||||
next_header: Protocol::Tcp,
|
||||
length: 0,
|
||||
options: &REPR_PACKET_PAD4[2..],
|
||||
};
|
||||
let mut bytes = [0u8; 8];
|
||||
let mut header = Header::new_unchecked(&mut bytes);
|
||||
repr.emit(&mut header);
|
||||
assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]);
|
||||
|
||||
let repr = Repr{ next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..] };
|
||||
let repr = Repr {
|
||||
next_header: Protocol::Tcp,
|
||||
length: 1,
|
||||
options: &REPR_PACKET_PAD12[2..],
|
||||
};
|
||||
let mut bytes = [0u8; 16];
|
||||
let mut header = Header::new_unchecked(&mut bytes);
|
||||
repr.emit(&mut header);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use core::fmt;
|
||||
use crate::{Error, Result};
|
||||
use core::fmt;
|
||||
|
||||
enum_with_unknown! {
|
||||
/// IPv6 Extension Header Option Type
|
||||
|
@ -16,7 +16,7 @@ impl fmt::Display for Type {
|
|||
match *self {
|
||||
Type::Pad1 => write!(f, "Pad1"),
|
||||
Type::PadN => write!(f, "PadN"),
|
||||
Type::Unknown(id) => write!(f, "{}", id)
|
||||
Type::Unknown(id) => write!(f, "{}", id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ impl From<Type> for FailureType {
|
|||
#[derive(Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Ipv6Option<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
// Format of Option
|
||||
|
@ -223,25 +223,24 @@ pub enum Repr<'a> {
|
|||
Unknown {
|
||||
type_: Type,
|
||||
length: u8,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an IPv6 Extension Header Option and return a high-level representation.
|
||||
pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
match opt.option_type() {
|
||||
Type::Pad1 =>
|
||||
Ok(Repr::Pad1),
|
||||
Type::PadN =>
|
||||
Ok(Repr::PadN(opt.data_len())),
|
||||
unknown_type @ Type::Unknown(_) => {
|
||||
Ok(Repr::Unknown {
|
||||
Type::Pad1 => Ok(Repr::Pad1),
|
||||
Type::PadN => Ok(Repr::PadN(opt.data_len())),
|
||||
unknown_type @ Type::Unknown(_) => Ok(Repr::Unknown {
|
||||
type_: unknown_type,
|
||||
length: opt.data_len(),
|
||||
data: opt.data(),
|
||||
})
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,18 +248,15 @@ impl<'a> Repr<'a> {
|
|||
pub fn buffer_len(&self) -> usize {
|
||||
match *self {
|
||||
Repr::Pad1 => 1,
|
||||
Repr::PadN(length) =>
|
||||
field::DATA(length).end,
|
||||
Repr::Unknown{ length, .. } =>
|
||||
field::DATA(length).end,
|
||||
Repr::PadN(length) => field::DATA(length).end,
|
||||
Repr::Unknown { length, .. } => field::DATA(length).end,
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a high-level representation into an IPv6 Extension Header Option.
|
||||
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, opt: &mut Ipv6Option<&'a mut T>) {
|
||||
match *self {
|
||||
Repr::Pad1 =>
|
||||
opt.set_option_type(Type::Pad1),
|
||||
Repr::Pad1 => opt.set_option_type(Type::Pad1),
|
||||
Repr::PadN(len) => {
|
||||
opt.set_option_type(Type::PadN);
|
||||
opt.set_data_len(len);
|
||||
|
@ -269,7 +265,11 @@ impl<'a> Repr<'a> {
|
|||
*x = 0
|
||||
}
|
||||
}
|
||||
Repr::Unknown{ type_, length, data } => {
|
||||
Repr::Unknown {
|
||||
type_,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
opt.set_option_type(type_);
|
||||
opt.set_data_len(length);
|
||||
opt.data_mut().copy_from_slice(&data[..length as usize]);
|
||||
|
@ -285,7 +285,7 @@ pub struct Ipv6OptionsIterator<'a> {
|
|||
pos: usize,
|
||||
length: usize,
|
||||
data: &'a [u8],
|
||||
hit_error: bool
|
||||
hit_error: bool,
|
||||
}
|
||||
|
||||
impl<'a> Ipv6OptionsIterator<'a> {
|
||||
|
@ -301,7 +301,8 @@ impl<'a> Ipv6OptionsIterator<'a> {
|
|||
Ipv6OptionsIterator {
|
||||
pos: 0,
|
||||
hit_error: false,
|
||||
length, data
|
||||
length,
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,8 +315,7 @@ impl<'a> Iterator for Ipv6OptionsIterator<'a> {
|
|||
// If we still have data to parse and we have not previously
|
||||
// hit an error, attempt to parse the next option.
|
||||
match Ipv6Option::new_checked(&self.data[self.pos..]) {
|
||||
Ok(hdr) => {
|
||||
match Repr::parse(&hdr) {
|
||||
Ok(hdr) => match Repr::parse(&hdr) {
|
||||
Ok(repr) => {
|
||||
self.pos += repr.buffer_len();
|
||||
Some(Ok(repr))
|
||||
|
@ -324,8 +324,7 @@ impl<'a> Iterator for Ipv6OptionsIterator<'a> {
|
|||
self.hit_error = true;
|
||||
Some(Err(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
self.hit_error = true;
|
||||
Some(Err(e))
|
||||
|
@ -343,12 +342,9 @@ impl<'a> fmt::Display for Repr<'a> {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "IPv6 Option ")?;
|
||||
match *self {
|
||||
Repr::Pad1 =>
|
||||
write!(f, "{} ", Type::Pad1),
|
||||
Repr::PadN(len) =>
|
||||
write!(f, "{} length={} ", Type::PadN, len),
|
||||
Repr::Unknown{ type_, length, .. } =>
|
||||
write!(f, "{} length={} ", type_, length),
|
||||
Repr::Pad1 => write!(f, "{} ", Type::Pad1),
|
||||
Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len),
|
||||
Repr::Unknown { type_, length, .. } => write!(f, "{} length={} ", type_, length),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -365,27 +361,41 @@ mod test {
|
|||
fn test_check_len() {
|
||||
let bytes = [0u8];
|
||||
// zero byte buffer
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Ipv6Option::new_unchecked(&bytes[..0]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Ipv6Option::new_unchecked(&bytes[..0]).check_len()
|
||||
);
|
||||
// pad1
|
||||
assert_eq!(Ok(()),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len());
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len()
|
||||
);
|
||||
|
||||
// padn with truncated data
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len()
|
||||
);
|
||||
// padn
|
||||
assert_eq!(Ok(()),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len());
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len()
|
||||
);
|
||||
|
||||
// unknown option type with truncated data
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len());
|
||||
assert_eq!(Err(Error::Truncated),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len()
|
||||
);
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len()
|
||||
);
|
||||
// unknown type
|
||||
assert_eq!(Ok(()),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len());
|
||||
assert_eq!(
|
||||
Ok(()),
|
||||
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -447,7 +457,14 @@ mod test {
|
|||
let data = [0u8; 3];
|
||||
let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN);
|
||||
let unknown = Repr::parse(&opt).unwrap();
|
||||
assert_eq!(unknown, Repr::Unknown { type_: Type::Unknown(255), length: 3, data: &data });
|
||||
assert_eq!(
|
||||
unknown,
|
||||
Repr::Unknown {
|
||||
type_: Type::Unknown(255),
|
||||
length: 3,
|
||||
data: &data
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -465,7 +482,11 @@ mod test {
|
|||
assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PADN);
|
||||
|
||||
let data = [0u8; 3];
|
||||
let repr = Repr::Unknown { type_: Type::Unknown(255), length: 3, data: &data };
|
||||
let repr = Repr::Unknown {
|
||||
type_: Type::Unknown(255),
|
||||
length: 3,
|
||||
data: &data,
|
||||
};
|
||||
let mut bytes = [254u8; 5]; // don't assume bytes are initialized to zero
|
||||
let mut opt = Ipv6Option::new_unchecked(&mut bytes);
|
||||
repr.emit(&mut opt);
|
||||
|
@ -488,10 +509,10 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_options_iter() {
|
||||
let options = [0x00, 0x01, 0x01, 0x00,
|
||||
0x01, 0x02, 0x00, 0x00,
|
||||
0x01, 0x00, 0x00, 0x11,
|
||||
0x00, 0x01, 0x08, 0x00];
|
||||
let options = [
|
||||
0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x01,
|
||||
0x08, 0x00,
|
||||
];
|
||||
|
||||
let mut iterator = Ipv6OptionsIterator::new(&options, 0);
|
||||
assert_eq!(iterator.next(), None);
|
||||
|
@ -504,8 +525,14 @@ mod test {
|
|||
(2, Ok(Repr::PadN(2))) => continue,
|
||||
(3, Ok(Repr::PadN(0))) => continue,
|
||||
(4, Ok(Repr::Pad1)) => continue,
|
||||
(5, Ok(Repr::Unknown { type_: Type::Unknown(0x11), length: 0, .. })) =>
|
||||
continue,
|
||||
(
|
||||
5,
|
||||
Ok(Repr::Unknown {
|
||||
type_: Type::Unknown(0x11),
|
||||
length: 0,
|
||||
..
|
||||
}),
|
||||
) => continue,
|
||||
(6, Err(Error::Truncated)) => continue,
|
||||
(i, res) => panic!("Unexpected option `{:?}` at index {}", res, i),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use core::fmt;
|
||||
use crate::{Error, Result};
|
||||
use core::fmt;
|
||||
|
||||
use crate::wire::IpProtocol as Protocol;
|
||||
use crate::wire::Ipv6Address as Address;
|
||||
|
@ -44,7 +44,7 @@ impl fmt::Display for Type {
|
|||
Type::Experiment1 => write!(f, "Experiment1"),
|
||||
Type::Experiment2 => write!(f, "Experiment2"),
|
||||
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)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Header<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
// Format of the Routing Header
|
||||
|
@ -234,7 +234,6 @@ impl<T: AsRef<[u8]>> Header<T> {
|
|||
data[field::CMPR] >> 4
|
||||
}
|
||||
|
||||
|
||||
/// Return the number of prefix octects elided from the last address (`addresses[n]`).
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -316,7 +315,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
|
|||
data[7] = 0;
|
||||
}
|
||||
|
||||
_ => panic!("Unrecognized routing type when clearing reserved fields.")
|
||||
_ => panic!("Unrecognized routing type when clearing reserved fields."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -418,25 +417,24 @@ pub enum Repr<'a> {
|
|||
/// RPL Source Route Header.
|
||||
pad: u8,
|
||||
/// Vector of addresses, numbered 1 to `n`.
|
||||
addresses: &'a[u8],
|
||||
addresses: &'a [u8],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an IPv6 Routing Header and return a high-level representation.
|
||||
pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
match header.routing_type() {
|
||||
Type::Type2 => {
|
||||
Ok(Repr::Type2 {
|
||||
Type::Type2 => Ok(Repr::Type2 {
|
||||
next_header: header.next_header(),
|
||||
length: header.header_len(),
|
||||
segments_left: header.segments_left(),
|
||||
home_address: header.home_address(),
|
||||
})
|
||||
}
|
||||
Type::Rpl => {
|
||||
Ok(Repr::Rpl {
|
||||
}),
|
||||
Type::Rpl => Ok(Repr::Rpl {
|
||||
next_header: header.next_header(),
|
||||
length: header.header_len(),
|
||||
segments_left: header.segments_left(),
|
||||
|
@ -444,10 +442,9 @@ impl<'a> Repr<'a> {
|
|||
cmpr_e: header.cmpr_e(),
|
||||
pad: header.pad(),
|
||||
addresses: header.addresses(),
|
||||
})
|
||||
}
|
||||
}),
|
||||
|
||||
_ => Err(Error::Unrecognized)
|
||||
_ => Err(Error::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -455,16 +452,19 @@ impl<'a> Repr<'a> {
|
|||
/// representation.
|
||||
pub fn buffer_len(&self) -> usize {
|
||||
match self {
|
||||
&Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => {
|
||||
field::DATA(length).end
|
||||
}
|
||||
&Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => field::DATA(length).end,
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a high-level representation into an IPv6 Routing Header.
|
||||
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
|
||||
match *self {
|
||||
Repr::Type2 { next_header, length, segments_left, home_address } => {
|
||||
Repr::Type2 {
|
||||
next_header,
|
||||
length,
|
||||
segments_left,
|
||||
home_address,
|
||||
} => {
|
||||
header.set_next_header(next_header);
|
||||
header.set_header_len(length);
|
||||
header.set_routing_type(Type::Type2);
|
||||
|
@ -472,7 +472,15 @@ impl<'a> Repr<'a> {
|
|||
header.clear_reserved();
|
||||
header.set_home_address(home_address);
|
||||
}
|
||||
Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, addresses } => {
|
||||
Repr::Rpl {
|
||||
next_header,
|
||||
length,
|
||||
segments_left,
|
||||
cmpr_i,
|
||||
cmpr_e,
|
||||
pad,
|
||||
addresses,
|
||||
} => {
|
||||
header.set_next_header(next_header);
|
||||
header.set_header_len(length);
|
||||
header.set_routing_type(Type::Rpl);
|
||||
|
@ -490,11 +498,31 @@ impl<'a> Repr<'a> {
|
|||
impl<'a> fmt::Display for Repr<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Repr::Type2 { next_header, length, segments_left, home_address } => {
|
||||
write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}",
|
||||
next_header, length, Type::Type2, segments_left, home_address)
|
||||
Repr::Type2 {
|
||||
next_header,
|
||||
length,
|
||||
segments_left,
|
||||
home_address,
|
||||
} => {
|
||||
write!(
|
||||
f,
|
||||
"IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}",
|
||||
next_header,
|
||||
length,
|
||||
Type::Type2,
|
||||
segments_left,
|
||||
home_address
|
||||
)
|
||||
}
|
||||
Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, .. } => {
|
||||
Repr::Rpl {
|
||||
next_header,
|
||||
length,
|
||||
segments_left,
|
||||
cmpr_i,
|
||||
cmpr_e,
|
||||
pad,
|
||||
..
|
||||
} => {
|
||||
write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}",
|
||||
next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad)
|
||||
}
|
||||
|
@ -507,12 +535,10 @@ mod test {
|
|||
use super::*;
|
||||
|
||||
// A Type 2 Routing Header
|
||||
static BYTES_TYPE2: [u8; 24] = [0x6, 0x2, 0x2, 0x1,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x1];
|
||||
static BYTES_TYPE2: [u8; 24] = [
|
||||
0x6, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
|
||||
];
|
||||
|
||||
// A representation of a Type 2 Routing header
|
||||
static REPR_TYPE2: Repr = Repr::Type2 {
|
||||
|
@ -523,16 +549,11 @@ mod test {
|
|||
};
|
||||
|
||||
// A Source Routing Header with full IPv6 addresses in bytes
|
||||
static BYTES_SRH_FULL: [u8; 40] = [0x6, 0x4, 0x3, 0x2,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0xfd, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x2,
|
||||
0xfd, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x3, 0x1];
|
||||
static BYTES_SRH_FULL: [u8; 40] = [
|
||||
0x6, 0x4, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x3, 0x1,
|
||||
];
|
||||
|
||||
// A representation of a Source Routing Header with full IPv6 addresses
|
||||
static REPR_SRH_FULL: Repr = Repr::Rpl {
|
||||
|
@ -542,21 +563,16 @@ mod test {
|
|||
cmpr_i: 0,
|
||||
cmpr_e: 0,
|
||||
pad: 0,
|
||||
addresses: &[0xfd, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x2,
|
||||
0xfd, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0,
|
||||
0x0, 0x0, 0x3, 0x1]
|
||||
addresses: &[
|
||||
0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd,
|
||||
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1,
|
||||
],
|
||||
};
|
||||
|
||||
// A Source Routing Header with elided IPv6 addresses in bytes
|
||||
static BYTES_SRH_ELIDED: [u8; 16] = [0x6, 0x1, 0x3, 0x2,
|
||||
0xfe, 0x50, 0x0, 0x0,
|
||||
0x2, 0x3, 0x1, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0];
|
||||
static BYTES_SRH_ELIDED: [u8; 16] = [
|
||||
0x6, 0x1, 0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||
];
|
||||
|
||||
// A representation of a Source Routing Header with elided IPv6 addresses
|
||||
static REPR_SRH_ELIDED: Repr = Repr::Rpl {
|
||||
|
@ -566,20 +582,37 @@ mod test {
|
|||
cmpr_i: 15,
|
||||
cmpr_e: 14,
|
||||
pad: 5,
|
||||
addresses: &[0x2, 0x3, 0x1, 0x0,
|
||||
0x0, 0x0, 0x0, 0x0]
|
||||
addresses: &[0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0],
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_check_len() {
|
||||
// less than min header size
|
||||
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_TYPE2[..3]).check_len());
|
||||
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_FULL[..3]).check_len());
|
||||
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..3]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new(&BYTES_TYPE2[..3]).check_len()
|
||||
);
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new(&BYTES_SRH_FULL[..3]).check_len()
|
||||
);
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new(&BYTES_SRH_ELIDED[..3]).check_len()
|
||||
);
|
||||
// less than specfied length field
|
||||
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_TYPE2[..23]).check_len());
|
||||
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_FULL[..39]).check_len());
|
||||
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..11]).check_len());
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new(&BYTES_TYPE2[..23]).check_len()
|
||||
);
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new(&BYTES_SRH_FULL[..39]).check_len()
|
||||
);
|
||||
assert_eq!(
|
||||
Err(Error::Truncated),
|
||||
Header::new(&BYTES_SRH_ELIDED[..11]).check_len()
|
||||
);
|
||||
// valid
|
||||
assert_eq!(Ok(()), Header::new(&BYTES_TYPE2[..]).check_len());
|
||||
assert_eq!(Ok(()), Header::new(&BYTES_SRH_FULL[..]).check_len());
|
||||
|
|
198
src/wire/mld.rs
198
src/wire/mld.rs
|
@ -6,9 +6,9 @@
|
|||
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::wire::icmpv6::{field, Message, Packet};
|
||||
use crate::wire::Ipv6Address;
|
||||
use crate::{Error, Result};
|
||||
|
||||
enum_with_unknown! {
|
||||
/// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for
|
||||
|
@ -168,7 +168,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct AddressRecord<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> AddressRecord<T> {
|
||||
|
@ -305,58 +305,61 @@ pub enum Repr<'a> {
|
|||
qrv: u8,
|
||||
qqic: u8,
|
||||
num_srcs: u16,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
Report {
|
||||
nr_mcast_addr_rcrds: u16,
|
||||
data: &'a [u8]
|
||||
}
|
||||
data: &'a [u8],
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an MLDv2 packet and return a high-level representation.
|
||||
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
match packet.msg_type() {
|
||||
Message::MldQuery => {
|
||||
Ok(Repr::Query {
|
||||
Message::MldQuery => Ok(Repr::Query {
|
||||
max_resp_code: packet.max_resp_code(),
|
||||
mcast_addr: packet.mcast_addr(),
|
||||
s_flag: packet.s_flag(),
|
||||
qrv: packet.qrv(),
|
||||
qqic: packet.qqic(),
|
||||
num_srcs: packet.num_srcs(),
|
||||
data: packet.payload()
|
||||
})
|
||||
},
|
||||
Message::MldReport => {
|
||||
Ok(Repr::Report {
|
||||
data: packet.payload(),
|
||||
}),
|
||||
Message::MldReport => Ok(Repr::Report {
|
||||
nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(),
|
||||
data: packet.payload()
|
||||
})
|
||||
},
|
||||
_ => Err(Error::Unrecognized)
|
||||
data: packet.payload(),
|
||||
}),
|
||||
_ => Err(Error::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the length of a packet that will be emitted from this high-level representation.
|
||||
pub fn buffer_len(&self) -> usize {
|
||||
match self {
|
||||
Repr::Query { .. } => {
|
||||
field::QUERY_NUM_SRCS.end
|
||||
}
|
||||
Repr::Report { .. } => {
|
||||
field::NR_MCAST_RCRDS.end
|
||||
}
|
||||
Repr::Query { .. } => field::QUERY_NUM_SRCS.end,
|
||||
Repr::Report { .. } => field::NR_MCAST_RCRDS.end,
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a high-level representation into an MLDv2 packet.
|
||||
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
|
||||
where
|
||||
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
|
||||
{
|
||||
match self {
|
||||
Repr::Query { max_resp_code, mcast_addr, s_flag,
|
||||
qrv, qqic, num_srcs, data } => {
|
||||
Repr::Query {
|
||||
max_resp_code,
|
||||
mcast_addr,
|
||||
s_flag,
|
||||
qrv,
|
||||
qqic,
|
||||
num_srcs,
|
||||
data,
|
||||
} => {
|
||||
packet.set_msg_type(Message::MldQuery);
|
||||
packet.set_msg_code(0);
|
||||
packet.clear_reserved();
|
||||
|
@ -371,8 +374,11 @@ impl<'a> Repr<'a> {
|
|||
packet.set_qqic(*qqic);
|
||||
packet.set_num_srcs(*num_srcs);
|
||||
packet.payload_mut().copy_from_slice(&data[..]);
|
||||
},
|
||||
Repr::Report { nr_mcast_addr_rcrds, data } => {
|
||||
}
|
||||
Repr::Report {
|
||||
nr_mcast_addr_rcrds,
|
||||
data,
|
||||
} => {
|
||||
packet.set_msg_type(Message::MldReport);
|
||||
packet.set_msg_code(0);
|
||||
packet.clear_reserved();
|
||||
|
@ -385,74 +391,49 @@ impl<'a> Repr<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::wire::Icmpv6Repr;
|
||||
use crate::wire::icmpv6::Message;
|
||||
use super::*;
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::wire::icmpv6::Message;
|
||||
use crate::wire::Icmpv6Repr;
|
||||
|
||||
static QUERY_PACKET_BYTES: [u8; 44] =
|
||||
[0x82, 0x00, 0x73, 0x74,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x0a, 0x12, 0x00, 0x01,
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02];
|
||||
static QUERY_PACKET_BYTES: [u8; 44] = [
|
||||
0x82, 0x00, 0x73, 0x74, 0x04, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x12, 0x00, 0x01, 0xff, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||
];
|
||||
|
||||
static QUERY_PACKET_PAYLOAD: [u8; 16] =
|
||||
[0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02];
|
||||
static QUERY_PACKET_PAYLOAD: [u8; 16] = [
|
||||
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x02,
|
||||
];
|
||||
|
||||
static REPORT_PACKET_BYTES: [u8; 44] =
|
||||
[0x8f, 0x00, 0x73, 0x85,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x01, 0x00, 0x00, 0x01,
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02];
|
||||
|
||||
static REPORT_PACKET_PAYLOAD: [u8; 36] =
|
||||
[0x01, 0x00, 0x00, 0x01,
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0xff, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02];
|
||||
static REPORT_PACKET_BYTES: [u8; 44] = [
|
||||
0x8f, 0x00, 0x73, 0x85, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||
];
|
||||
|
||||
static REPORT_PACKET_PAYLOAD: [u8; 36] = [
|
||||
0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||
];
|
||||
|
||||
fn create_repr<'a>(ty: Message) -> Icmpv6Repr<'a> {
|
||||
match ty {
|
||||
Message::MldQuery => {
|
||||
Icmpv6Repr::Mld(Repr::Query {
|
||||
Message::MldQuery => Icmpv6Repr::Mld(Repr::Query {
|
||||
max_resp_code: 0x400,
|
||||
mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES,
|
||||
s_flag: true,
|
||||
qrv: 0x02,
|
||||
qqic: 0x12,
|
||||
num_srcs: 0x01,
|
||||
data: &QUERY_PACKET_PAYLOAD
|
||||
})
|
||||
},
|
||||
Message::MldReport => {
|
||||
Icmpv6Repr::Mld(Repr::Report {
|
||||
data: &QUERY_PACKET_PAYLOAD,
|
||||
}),
|
||||
Message::MldReport => Icmpv6Repr::Mld(Repr::Report {
|
||||
nr_mcast_addr_rcrds: 1,
|
||||
data: &REPORT_PACKET_PAYLOAD
|
||||
})
|
||||
},
|
||||
data: &REPORT_PACKET_PAYLOAD,
|
||||
}),
|
||||
_ => {
|
||||
panic!("Message type must be a MLDv2 message type");
|
||||
}
|
||||
|
@ -471,8 +452,10 @@ mod test {
|
|||
assert_eq!(packet.qrv(), 0x02);
|
||||
assert_eq!(packet.qqic(), 0x12);
|
||||
assert_eq!(packet.num_srcs(), 0x01);
|
||||
assert_eq!(Ipv6Address::from_bytes(packet.payload()),
|
||||
Ipv6Address::LINK_LOCAL_ALL_ROUTERS);
|
||||
assert_eq!(
|
||||
Ipv6Address::from_bytes(packet.payload()),
|
||||
Ipv6Address::LINK_LOCAL_ALL_ROUTERS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -487,10 +470,14 @@ mod test {
|
|||
packet.set_qrv(0x02);
|
||||
packet.set_qqic(0x12);
|
||||
packet.set_num_srcs(0x01);
|
||||
packet.payload_mut().copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
|
||||
packet
|
||||
.payload_mut()
|
||||
.copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
|
||||
packet.clear_reserved();
|
||||
packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into());
|
||||
packet.fill_checksum(
|
||||
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
|
||||
}
|
||||
|
||||
|
@ -506,8 +493,10 @@ mod test {
|
|||
assert_eq!(addr_rcrd.aux_data_len(), 0x00);
|
||||
assert_eq!(addr_rcrd.num_srcs(), 0x01);
|
||||
assert_eq!(addr_rcrd.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES);
|
||||
assert_eq!(Ipv6Address::from_bytes(addr_rcrd.payload()),
|
||||
Ipv6Address::LINK_LOCAL_ALL_ROUTERS);
|
||||
assert_eq!(
|
||||
Ipv6Address::from_bytes(addr_rcrd.payload()),
|
||||
Ipv6Address::LINK_LOCAL_ALL_ROUTERS
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -524,31 +513,38 @@ mod test {
|
|||
addr_rcrd.set_aux_data_len(0);
|
||||
addr_rcrd.set_num_srcs(1);
|
||||
addr_rcrd.set_mcast_addr(Ipv6Address::LINK_LOCAL_ALL_NODES);
|
||||
addr_rcrd.payload_mut()
|
||||
addr_rcrd
|
||||
.payload_mut()
|
||||
.copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
|
||||
}
|
||||
packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into());
|
||||
packet.fill_checksum(
|
||||
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_repr_parse() {
|
||||
let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]);
|
||||
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
let repr = Icmpv6Repr::parse(
|
||||
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
|
||||
&packet,
|
||||
&ChecksumCapabilities::default());
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(repr, Ok(create_repr(Message::MldQuery)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_report_repr_parse() {
|
||||
let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]);
|
||||
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
let repr = Icmpv6Repr::parse(
|
||||
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
|
||||
&packet,
|
||||
&ChecksumCapabilities::default());
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(repr, Ok(create_repr(Message::MldReport)));
|
||||
}
|
||||
|
||||
|
@ -557,10 +553,12 @@ mod test {
|
|||
let mut bytes = [0x2a; 44];
|
||||
let mut packet = Packet::new_unchecked(&mut bytes[..]);
|
||||
let repr = create_repr(Message::MldQuery);
|
||||
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
repr.emit(
|
||||
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
|
||||
&mut packet,
|
||||
&ChecksumCapabilities::default());
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
|
||||
}
|
||||
|
||||
|
@ -569,10 +567,12 @@ mod test {
|
|||
let mut bytes = [0x2a; 44];
|
||||
let mut packet = Packet::new_unchecked(&mut bytes[..]);
|
||||
let repr = create_repr(Message::MldReport);
|
||||
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
repr.emit(
|
||||
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
|
||||
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
|
||||
&mut packet,
|
||||
&ChecksumCapabilities::default());
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
|
||||
}
|
||||
}
|
||||
|
|
163
src/wire/mod.rs
163
src/wire/mod.rs
|
@ -77,156 +77,131 @@ mod field {
|
|||
|
||||
pub mod pretty_print;
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
mod ethernet;
|
||||
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
|
||||
mod arp;
|
||||
#[cfg(feature = "proto-dhcpv4")]
|
||||
pub(crate) mod dhcpv4;
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
mod ethernet;
|
||||
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
|
||||
mod icmp;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
mod icmpv4;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod icmpv6;
|
||||
#[cfg(feature = "proto-igmp")]
|
||||
mod igmp;
|
||||
pub(crate) mod ip;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
mod ipv4;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ipv6;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ipv6option;
|
||||
mod ipv6fragment;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ipv6hopbyhop;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ipv6fragment;
|
||||
mod ipv6option;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod ipv6routing;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
mod icmpv4;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod icmpv6;
|
||||
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
|
||||
mod icmp;
|
||||
#[cfg(feature = "proto-igmp")]
|
||||
mod igmp;
|
||||
mod mld;
|
||||
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
|
||||
mod ndisc;
|
||||
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
|
||||
mod ndiscoption;
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
mod mld;
|
||||
mod udp;
|
||||
mod tcp;
|
||||
#[cfg(feature = "proto-dhcpv4")]
|
||||
pub(crate) mod dhcpv4;
|
||||
mod udp;
|
||||
|
||||
pub use self::pretty_print::PrettyPrinter;
|
||||
|
||||
#[cfg(feature = "medium-ethernet")]
|
||||
pub use self::ethernet::{EtherType as EthernetProtocol,
|
||||
Address as EthernetAddress,
|
||||
Frame as EthernetFrame,
|
||||
HEADER_LEN as ETHERNET_HEADER_LEN,
|
||||
Repr as EthernetRepr};
|
||||
pub use self::ethernet::{
|
||||
Address as EthernetAddress, EtherType as EthernetProtocol, Frame as EthernetFrame,
|
||||
Repr as EthernetRepr, HEADER_LEN as ETHERNET_HEADER_LEN,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
|
||||
pub use self::arp::{Hardware as ArpHardware,
|
||||
Operation as ArpOperation,
|
||||
Packet as ArpPacket,
|
||||
Repr as ArpRepr};
|
||||
pub use self::arp::{
|
||||
Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr,
|
||||
};
|
||||
|
||||
pub use self::ip::{Version as IpVersion,
|
||||
Protocol as IpProtocol,
|
||||
Address as IpAddress,
|
||||
Endpoint as IpEndpoint,
|
||||
Repr as IpRepr,
|
||||
Cidr as IpCidr};
|
||||
pub use self::ip::{
|
||||
Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol,
|
||||
Repr as IpRepr, Version as IpVersion,
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
pub use self::ipv4::{Address as Ipv4Address,
|
||||
Packet as Ipv4Packet,
|
||||
Repr as Ipv4Repr,
|
||||
Cidr as Ipv4Cidr,
|
||||
HEADER_LEN as IPV4_HEADER_LEN,
|
||||
MIN_MTU as IPV4_MIN_MTU};
|
||||
pub use self::ipv4::{
|
||||
Address as Ipv4Address, Cidr as Ipv4Cidr, Packet as Ipv4Packet, Repr as Ipv4Repr,
|
||||
HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU,
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::ipv6::{Address as Ipv6Address,
|
||||
Packet as Ipv6Packet,
|
||||
Repr as Ipv6Repr,
|
||||
Cidr as Ipv6Cidr,
|
||||
HEADER_LEN as IPV6_HEADER_LEN,
|
||||
MIN_MTU as IPV6_MIN_MTU};
|
||||
pub use self::ipv6::{
|
||||
Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr,
|
||||
HEADER_LEN as IPV6_HEADER_LEN, MIN_MTU as IPV6_MIN_MTU,
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::ipv6option::{Ipv6Option,
|
||||
Repr as Ipv6OptionRepr,
|
||||
pub use self::ipv6option::{
|
||||
FailureType as Ipv6OptionFailureType, Ipv6Option, Repr as Ipv6OptionRepr,
|
||||
Type as Ipv6OptionType,
|
||||
FailureType as Ipv6OptionFailureType};
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader,
|
||||
Repr as Ipv6HopByHopRepr};
|
||||
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader,
|
||||
Repr as Ipv6FragmentRepr};
|
||||
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::ipv6routing::{Header as Ipv6RoutingHeader,
|
||||
Repr as Ipv6RoutingRepr};
|
||||
pub use self::ipv6routing::{Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr};
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
pub use self::icmpv4::{Message as Icmpv4Message,
|
||||
DstUnreachable as Icmpv4DstUnreachable,
|
||||
Redirect as Icmpv4Redirect,
|
||||
pub use self::icmpv4::{
|
||||
DstUnreachable as Icmpv4DstUnreachable, Message as Icmpv4Message, Packet as Icmpv4Packet,
|
||||
ParamProblem as Icmpv4ParamProblem, Redirect as Icmpv4Redirect, Repr as Icmpv4Repr,
|
||||
TimeExceeded as Icmpv4TimeExceeded,
|
||||
ParamProblem as Icmpv4ParamProblem,
|
||||
Packet as Icmpv4Packet,
|
||||
Repr as Icmpv4Repr};
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-igmp")]
|
||||
pub use self::igmp::{Packet as IgmpPacket,
|
||||
Repr as IgmpRepr,
|
||||
IgmpVersion};
|
||||
pub use self::igmp::{IgmpVersion, Packet as IgmpPacket, Repr as IgmpRepr};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::icmpv6::{Message as Icmpv6Message,
|
||||
DstUnreachable as Icmpv6DstUnreachable,
|
||||
TimeExceeded as Icmpv6TimeExceeded,
|
||||
ParamProblem as Icmpv6ParamProblem,
|
||||
Packet as Icmpv6Packet,
|
||||
Repr as Icmpv6Repr};
|
||||
pub use self::icmpv6::{
|
||||
DstUnreachable as Icmpv6DstUnreachable, Message as Icmpv6Message, Packet as Icmpv6Packet,
|
||||
ParamProblem as Icmpv6ParamProblem, Repr as Icmpv6Repr, TimeExceeded as Icmpv6TimeExceeded,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
|
||||
pub use self::icmp::Repr as IcmpRepr;
|
||||
|
||||
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
|
||||
pub use self::ndisc::{
|
||||
NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags,
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
|
||||
pub use self::ndisc::{Repr as NdiscRepr,
|
||||
RouterFlags as NdiscRouterFlags,
|
||||
NeighborFlags as NdiscNeighborFlags};
|
||||
|
||||
#[cfg(all(feature = "proto-ipv6", feature = "medium-ethernet"))]
|
||||
pub use self::ndiscoption::{NdiscOption,
|
||||
Repr as NdiscOptionRepr,
|
||||
Type as NdiscOptionType,
|
||||
PrefixInformation as NdiscPrefixInformation,
|
||||
RedirectedHeader as NdiscRedirectedHeader,
|
||||
PrefixInfoFlags as NdiscPrefixInfoFlags};
|
||||
pub use self::ndiscoption::{
|
||||
NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
|
||||
PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
|
||||
Repr as NdiscOptionRepr, Type as NdiscOptionType,
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-ipv6")]
|
||||
pub use self::mld::{AddressRecord as MldAddressRecord,
|
||||
Repr as MldRepr};
|
||||
pub use self::mld::{AddressRecord as MldAddressRecord, Repr as MldRepr};
|
||||
|
||||
pub use self::udp::{Packet as UdpPacket,
|
||||
Repr as UdpRepr,
|
||||
HEADER_LEN as UDP_HEADER_LEN};
|
||||
pub use self::udp::{Packet as UdpPacket, Repr as UdpRepr, HEADER_LEN as UDP_HEADER_LEN};
|
||||
|
||||
pub use self::tcp::{SeqNumber as TcpSeqNumber,
|
||||
Packet as TcpPacket,
|
||||
TcpOption,
|
||||
Repr as TcpRepr,
|
||||
Control as TcpControl,
|
||||
HEADER_LEN as TCP_HEADER_LEN};
|
||||
pub use self::tcp::{
|
||||
Control as TcpControl, Packet as TcpPacket, Repr as TcpRepr, SeqNumber as TcpSeqNumber,
|
||||
TcpOption, HEADER_LEN as TCP_HEADER_LEN,
|
||||
};
|
||||
|
||||
#[cfg(feature = "proto-dhcpv4")]
|
||||
pub use self::dhcpv4::{Packet as DhcpPacket,
|
||||
Repr as DhcpRepr,
|
||||
MessageType as DhcpMessageType,
|
||||
CLIENT_PORT as DHCP_CLIENT_PORT,
|
||||
pub use self::dhcpv4::{
|
||||
MessageType as DhcpMessageType, Packet as DhcpPacket, Repr as DhcpRepr,
|
||||
CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
|
||||
SERVER_PORT as DHCP_SERVER_PORT,
|
||||
MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT};
|
||||
};
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use bitflags::bitflags;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::time::Duration;
|
||||
use crate::wire::icmpv6::{field, Message, Packet};
|
||||
use crate::wire::{EthernetAddress, Ipv6Repr, Ipv6Packet};
|
||||
use crate::wire::Ipv6Address;
|
||||
use crate::wire::{EthernetAddress, Ipv6Packet, Ipv6Repr};
|
||||
use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType};
|
||||
use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader};
|
||||
use crate::time::Duration;
|
||||
use crate::wire::Ipv6Address;
|
||||
use crate::{Error, Result};
|
||||
|
||||
bitflags! {
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
|
@ -82,7 +82,6 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Getters for the Neighbor Solicitation message header.
|
||||
/// See [RFC 4861 § 4.3].
|
||||
///
|
||||
|
@ -194,7 +193,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub enum Repr<'a> {
|
||||
RouterSolicit {
|
||||
lladdr: Option<EthernetAddress>
|
||||
lladdr: Option<EthernetAddress>,
|
||||
},
|
||||
RouterAdvert {
|
||||
hop_limit: u8,
|
||||
|
@ -204,44 +203,47 @@ pub enum Repr<'a> {
|
|||
retrans_time: Duration,
|
||||
lladdr: Option<EthernetAddress>,
|
||||
mtu: Option<u32>,
|
||||
prefix_info: Option<NdiscPrefixInformation>
|
||||
prefix_info: Option<NdiscPrefixInformation>,
|
||||
},
|
||||
NeighborSolicit {
|
||||
target_addr: Ipv6Address,
|
||||
lladdr: Option<EthernetAddress>
|
||||
lladdr: Option<EthernetAddress>,
|
||||
},
|
||||
NeighborAdvert {
|
||||
flags: NeighborFlags,
|
||||
target_addr: Ipv6Address,
|
||||
lladdr: Option<EthernetAddress>
|
||||
lladdr: Option<EthernetAddress>,
|
||||
},
|
||||
Redirect {
|
||||
target_addr: Ipv6Address,
|
||||
dest_addr: Ipv6Address,
|
||||
lladdr: Option<EthernetAddress>,
|
||||
redirected_hdr: Option<NdiscRedirectedHeader<'a>>
|
||||
}
|
||||
redirected_hdr: Option<NdiscRedirectedHeader<'a>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an NDISC packet and return a high-level representation of the
|
||||
/// packet.
|
||||
pub fn parse<T>(packet: &Packet<&'a T>)
|
||||
-> Result<Repr<'a>>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
match packet.msg_type() {
|
||||
Message::RouterSolicit => {
|
||||
let lladdr = if !packet.payload().is_empty() {
|
||||
let opt = NdiscOption::new_checked(packet.payload())?;
|
||||
match opt.option_type() {
|
||||
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
|
||||
_ => { return Err(Error::Unrecognized); }
|
||||
_ => {
|
||||
return Err(Error::Unrecognized);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Repr::RouterSolicit { lladdr })
|
||||
},
|
||||
}
|
||||
Message::RouterAdvert => {
|
||||
let mut offset = 0;
|
||||
let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None);
|
||||
|
@ -252,7 +254,9 @@ impl<'a> Repr<'a> {
|
|||
NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
|
||||
NdiscOptionRepr::Mtu(val) => mtu = Some(val),
|
||||
NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info),
|
||||
_ => { return Err(Error::Unrecognized); }
|
||||
_ => {
|
||||
return Err(Error::Unrecognized);
|
||||
}
|
||||
}
|
||||
offset += opt.buffer_len();
|
||||
}
|
||||
|
@ -262,29 +266,36 @@ impl<'a> Repr<'a> {
|
|||
router_lifetime: packet.router_lifetime(),
|
||||
reachable_time: packet.reachable_time(),
|
||||
retrans_time: packet.retrans_time(),
|
||||
lladdr, mtu, prefix_info
|
||||
lladdr,
|
||||
mtu,
|
||||
prefix_info,
|
||||
})
|
||||
},
|
||||
}
|
||||
Message::NeighborSolicit => {
|
||||
let lladdr = if !packet.payload().is_empty() {
|
||||
let opt = NdiscOption::new_checked(packet.payload())?;
|
||||
match opt.option_type() {
|
||||
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
|
||||
_ => { return Err(Error::Unrecognized); }
|
||||
_ => {
|
||||
return Err(Error::Unrecognized);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Repr::NeighborSolicit {
|
||||
target_addr: packet.target_addr(), lladdr
|
||||
target_addr: packet.target_addr(),
|
||||
lladdr,
|
||||
})
|
||||
},
|
||||
}
|
||||
Message::NeighborAdvert => {
|
||||
let lladdr = if !packet.payload().is_empty() {
|
||||
let opt = NdiscOption::new_checked(packet.payload())?;
|
||||
match opt.option_type() {
|
||||
NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()),
|
||||
_ => { return Err(Error::Unrecognized); }
|
||||
_ => {
|
||||
return Err(Error::Unrecognized);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -292,9 +303,9 @@ impl<'a> Repr<'a> {
|
|||
Ok(Repr::NeighborAdvert {
|
||||
flags: packet.neighbor_flags(),
|
||||
target_addr: packet.target_addr(),
|
||||
lladdr
|
||||
lladdr,
|
||||
})
|
||||
},
|
||||
}
|
||||
Message::Redirect => {
|
||||
let mut offset = 0;
|
||||
let (mut lladdr, mut redirected_hdr) = (None, None);
|
||||
|
@ -304,43 +315,50 @@ impl<'a> Repr<'a> {
|
|||
NdiscOptionType::SourceLinkLayerAddr => {
|
||||
lladdr = Some(opt.link_layer_addr());
|
||||
offset += 8;
|
||||
},
|
||||
}
|
||||
NdiscOptionType::RedirectedHeader => {
|
||||
if opt.data_len() < 6 {
|
||||
return Err(Error::Truncated)
|
||||
return Err(Error::Truncated);
|
||||
} else {
|
||||
let ip_packet =
|
||||
Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]);
|
||||
let ip_repr = Ipv6Repr::parse(&ip_packet)?;
|
||||
let data = &opt.data()[offset + 8 + ip_repr.buffer_len()..];
|
||||
redirected_hdr = Some(NdiscRedirectedHeader {
|
||||
header: ip_repr, data
|
||||
header: ip_repr,
|
||||
data,
|
||||
});
|
||||
offset += 8 + ip_repr.buffer_len() + data.len();
|
||||
}
|
||||
}
|
||||
_ => { return Err(Error::Unrecognized); }
|
||||
_ => {
|
||||
return Err(Error::Unrecognized);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Repr::Redirect {
|
||||
target_addr: packet.target_addr(),
|
||||
dest_addr: packet.dest_addr(),
|
||||
lladdr, redirected_hdr
|
||||
lladdr,
|
||||
redirected_hdr,
|
||||
})
|
||||
},
|
||||
_ => Err(Error::Unrecognized)
|
||||
}
|
||||
_ => Err(Error::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn buffer_len(&self) -> usize {
|
||||
match self {
|
||||
&Repr::RouterSolicit { lladdr } => {
|
||||
match lladdr {
|
||||
&Repr::RouterSolicit { lladdr } => match lladdr {
|
||||
Some(_) => field::UNUSED.end + 8,
|
||||
None => field::UNUSED.end,
|
||||
}
|
||||
},
|
||||
&Repr::RouterAdvert { lladdr, mtu, prefix_info, .. } => {
|
||||
&Repr::RouterAdvert {
|
||||
lladdr,
|
||||
mtu,
|
||||
prefix_info,
|
||||
..
|
||||
} => {
|
||||
let mut offset = 0;
|
||||
if lladdr.is_some() {
|
||||
offset += 8;
|
||||
|
@ -352,14 +370,18 @@ impl<'a> Repr<'a> {
|
|||
offset += 32;
|
||||
}
|
||||
field::RETRANS_TM.end + offset
|
||||
},
|
||||
}
|
||||
&Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => {
|
||||
match lladdr {
|
||||
Some(_) => field::TARGET_ADDR.end + 8,
|
||||
None => field::TARGET_ADDR.end,
|
||||
}
|
||||
},
|
||||
&Repr::Redirect { lladdr, redirected_hdr, .. } => {
|
||||
}
|
||||
&Repr::Redirect {
|
||||
lladdr,
|
||||
redirected_hdr,
|
||||
..
|
||||
} => {
|
||||
let mut offset = 0;
|
||||
if lladdr.is_some() {
|
||||
offset += 8;
|
||||
|
@ -373,7 +395,9 @@ impl<'a> Repr<'a> {
|
|||
}
|
||||
|
||||
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
|
||||
where
|
||||
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
|
||||
{
|
||||
match *self {
|
||||
Repr::RouterSolicit { lladdr } => {
|
||||
packet.set_msg_type(Message::RouterSolicit);
|
||||
|
@ -383,10 +407,18 @@ impl<'a> Repr<'a> {
|
|||
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
|
||||
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Repr::RouterAdvert { hop_limit, flags, router_lifetime, reachable_time,
|
||||
retrans_time, lladdr, mtu, prefix_info } => {
|
||||
Repr::RouterAdvert {
|
||||
hop_limit,
|
||||
flags,
|
||||
router_lifetime,
|
||||
reachable_time,
|
||||
retrans_time,
|
||||
lladdr,
|
||||
mtu,
|
||||
prefix_info,
|
||||
} => {
|
||||
packet.set_msg_type(Message::RouterAdvert);
|
||||
packet.set_msg_code(0);
|
||||
packet.set_current_hop_limit(hop_limit);
|
||||
|
@ -396,8 +428,7 @@ impl<'a> Repr<'a> {
|
|||
packet.set_retrans_time(retrans_time);
|
||||
let mut offset = 0;
|
||||
if let Some(lladdr) = lladdr {
|
||||
let mut opt_pkt =
|
||||
NdiscOption::new_unchecked(packet.payload_mut());
|
||||
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
|
||||
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
|
||||
offset += 8;
|
||||
}
|
||||
|
@ -412,34 +443,44 @@ impl<'a> Repr<'a> {
|
|||
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
|
||||
NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Repr::NeighborSolicit { target_addr, lladdr } => {
|
||||
Repr::NeighborSolicit {
|
||||
target_addr,
|
||||
lladdr,
|
||||
} => {
|
||||
packet.set_msg_type(Message::NeighborSolicit);
|
||||
packet.set_msg_code(0);
|
||||
packet.clear_reserved();
|
||||
packet.set_target_addr(target_addr);
|
||||
if let Some(lladdr) = lladdr {
|
||||
let mut opt_pkt =
|
||||
NdiscOption::new_unchecked(packet.payload_mut());
|
||||
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
|
||||
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Repr::NeighborAdvert { flags, target_addr, lladdr } => {
|
||||
Repr::NeighborAdvert {
|
||||
flags,
|
||||
target_addr,
|
||||
lladdr,
|
||||
} => {
|
||||
packet.set_msg_type(Message::NeighborAdvert);
|
||||
packet.set_msg_code(0);
|
||||
packet.clear_reserved();
|
||||
packet.set_neighbor_flags(flags);
|
||||
packet.set_target_addr(target_addr);
|
||||
if let Some(lladdr) = lladdr {
|
||||
let mut opt_pkt =
|
||||
NdiscOption::new_unchecked(packet.payload_mut());
|
||||
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
|
||||
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Repr::Redirect { target_addr, dest_addr, lladdr, redirected_hdr } => {
|
||||
Repr::Redirect {
|
||||
target_addr,
|
||||
dest_addr,
|
||||
lladdr,
|
||||
redirected_hdr,
|
||||
} => {
|
||||
packet.set_msg_type(Message::Redirect);
|
||||
packet.set_msg_code(0);
|
||||
packet.clear_reserved();
|
||||
|
@ -447,11 +488,10 @@ impl<'a> Repr<'a> {
|
|||
packet.set_dest_addr(dest_addr);
|
||||
let offset = match lladdr {
|
||||
Some(lladdr) => {
|
||||
let mut opt_pkt =
|
||||
NdiscOption::new_unchecked(packet.payload_mut());
|
||||
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
|
||||
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
|
||||
8
|
||||
},
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
if let Some(redirected_hdr) = redirected_hdr {
|
||||
|
@ -459,28 +499,23 @@ impl<'a> Repr<'a> {
|
|||
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
|
||||
NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use super::*;
|
||||
use crate::wire::Icmpv6Repr;
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
|
||||
use crate::wire::Icmpv6Repr;
|
||||
|
||||
static ROUTER_ADVERT_BYTES: [u8; 24] =
|
||||
[0x86, 0x00, 0xa9, 0xde,
|
||||
0x40, 0x80, 0x03, 0x84,
|
||||
0x00, 0x00, 0x03, 0x84,
|
||||
0x00, 0x00, 0x03, 0x84,
|
||||
0x01, 0x01, 0x52, 0x54,
|
||||
0x00, 0x12, 0x34, 0x56];
|
||||
static SOURCE_LINK_LAYER_OPT: [u8; 8] =
|
||||
[0x01, 0x01, 0x52, 0x54,
|
||||
0x00, 0x12, 0x34, 0x56];
|
||||
static ROUTER_ADVERT_BYTES: [u8; 24] = [
|
||||
0x86, 0x00, 0xa9, 0xde, 0x40, 0x80, 0x03, 0x84, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03,
|
||||
0x84, 0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56,
|
||||
];
|
||||
static SOURCE_LINK_LAYER_OPT: [u8; 8] = [0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56];
|
||||
|
||||
fn create_repr<'a>() -> Icmpv6Repr<'a> {
|
||||
Icmpv6Repr::Ndisc(Repr::RouterAdvert {
|
||||
|
@ -491,7 +526,7 @@ mod test {
|
|||
retrans_time: Duration::from_millis(900),
|
||||
lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56])),
|
||||
mtu: None,
|
||||
prefix_info: None
|
||||
prefix_info: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -519,7 +554,9 @@ mod test {
|
|||
packet.set_router_lifetime(Duration::from_secs(900));
|
||||
packet.set_reachable_time(Duration::from_millis(900));
|
||||
packet.set_retrans_time(Duration::from_millis(900));
|
||||
packet.payload_mut().copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
|
||||
packet
|
||||
.payload_mut()
|
||||
.copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
|
||||
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
|
||||
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
|
||||
}
|
||||
|
@ -527,17 +564,28 @@ mod test {
|
|||
#[test]
|
||||
fn test_router_advert_repr_parse() {
|
||||
let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]);
|
||||
assert_eq!(Icmpv6Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
|
||||
&packet, &ChecksumCapabilities::default()).unwrap(),
|
||||
create_repr());
|
||||
assert_eq!(
|
||||
Icmpv6Repr::parse(
|
||||
&MOCK_IP_ADDR_1,
|
||||
&MOCK_IP_ADDR_2,
|
||||
&packet,
|
||||
&ChecksumCapabilities::default()
|
||||
)
|
||||
.unwrap(),
|
||||
create_repr()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_router_advert_repr_emit() {
|
||||
let mut bytes = vec![0x2a; 24];
|
||||
let mut packet = Packet::new_unchecked(&mut bytes[..]);
|
||||
create_repr().emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
|
||||
&mut packet, &ChecksumCapabilities::default());
|
||||
create_repr().emit(
|
||||
&MOCK_IP_ADDR_1,
|
||||
&MOCK_IP_ADDR_2,
|
||||
&mut packet,
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use core::fmt;
|
||||
use byteorder::{NetworkEndian, ByteOrder};
|
||||
use bitflags::bitflags;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::fmt;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::time::Duration;
|
||||
use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr};
|
||||
use crate::{Error, Result};
|
||||
|
||||
enum_with_unknown! {
|
||||
/// NDISC Option Type
|
||||
|
@ -30,7 +30,7 @@ impl fmt::Display for Type {
|
|||
Type::PrefixInformation => write!(f, "prefix information"),
|
||||
Type::RedirectedHeader => write!(f, "redirected header"),
|
||||
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)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct NdiscOption<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
// Format of an NDISC Option
|
||||
|
@ -180,16 +180,11 @@ impl<T: AsRef<[u8]>> NdiscOption<T> {
|
|||
Err(Error::Truncated)
|
||||
} else {
|
||||
match self.option_type() {
|
||||
Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu =>
|
||||
Ok(()),
|
||||
Type::PrefixInformation if data_range.end >= field::PREFIX.end =>
|
||||
Ok(()),
|
||||
Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ =>
|
||||
Ok(()),
|
||||
Type::Unknown(_) =>
|
||||
Ok(()),
|
||||
_ =>
|
||||
Err(Error::Truncated),
|
||||
Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Ok(()),
|
||||
Type::PrefixInformation if data_range.end >= field::PREFIX.end => Ok(()),
|
||||
Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => Ok(()),
|
||||
Type::Unknown(_) => Ok(()),
|
||||
_ => Err(Error::Truncated),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -371,7 +366,6 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> {
|
||||
/// Return a mutable pointer to the option data.
|
||||
#[inline]
|
||||
|
@ -401,14 +395,14 @@ pub struct PrefixInformation {
|
|||
pub flags: PrefixInfoFlags,
|
||||
pub valid_lifetime: Duration,
|
||||
pub preferred_lifetime: Duration,
|
||||
pub prefix: Ipv6Address
|
||||
pub prefix: Ipv6Address,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct RedirectedHeader<'a> {
|
||||
pub header: Ipv6Repr,
|
||||
pub data: &'a [u8]
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
/// A high-level representation of an NDISC Option.
|
||||
|
@ -423,14 +417,16 @@ pub enum Repr<'a> {
|
|||
Unknown {
|
||||
type_: u8,
|
||||
length: u8,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse an NDISC Option and return a high-level representation.
|
||||
pub fn parse<T>(opt: &'a NdiscOption<&'a T>) -> Result<Repr<'a>>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
match opt.option_type() {
|
||||
Type::SourceLinkLayerAddr => {
|
||||
if opt.data_len() == 1 {
|
||||
|
@ -438,14 +434,14 @@ impl<'a> Repr<'a> {
|
|||
} else {
|
||||
Err(Error::Malformed)
|
||||
}
|
||||
},
|
||||
}
|
||||
Type::TargetLinkLayerAddr => {
|
||||
if opt.data_len() == 1 {
|
||||
Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr()))
|
||||
} else {
|
||||
Err(Error::Malformed)
|
||||
}
|
||||
},
|
||||
}
|
||||
Type::PrefixInformation => {
|
||||
if opt.data_len() == 4 {
|
||||
Ok(Repr::PrefixInformation(PrefixInformation {
|
||||
|
@ -453,12 +449,12 @@ impl<'a> Repr<'a> {
|
|||
flags: opt.prefix_flags(),
|
||||
valid_lifetime: opt.valid_lifetime(),
|
||||
preferred_lifetime: opt.preferred_lifetime(),
|
||||
prefix: opt.prefix()
|
||||
prefix: opt.prefix(),
|
||||
}))
|
||||
} else {
|
||||
Err(Error::Malformed)
|
||||
}
|
||||
},
|
||||
}
|
||||
Type::RedirectedHeader => {
|
||||
// If the options data length is less than 6, the option
|
||||
// does not have enough data to fill out the IP header
|
||||
|
@ -470,60 +466,60 @@ impl<'a> Repr<'a> {
|
|||
let ip_repr = Ipv6Repr::parse(&ip_packet)?;
|
||||
Ok(Repr::RedirectedHeader(RedirectedHeader {
|
||||
header: ip_repr,
|
||||
data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..]
|
||||
data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..],
|
||||
}))
|
||||
}
|
||||
},
|
||||
}
|
||||
Type::Mtu => {
|
||||
if opt.data_len() == 1 {
|
||||
Ok(Repr::Mtu(opt.mtu()))
|
||||
} else {
|
||||
Err(Error::Malformed)
|
||||
}
|
||||
},
|
||||
Type::Unknown(id) => {
|
||||
Ok(Repr::Unknown {
|
||||
}
|
||||
Type::Unknown(id) => Ok(Repr::Unknown {
|
||||
type_: id,
|
||||
length: opt.data_len(),
|
||||
data: opt.data()
|
||||
})
|
||||
}
|
||||
data: opt.data(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the length of a header that will be emitted from this high-level representation.
|
||||
pub fn buffer_len(&self) -> usize {
|
||||
match self {
|
||||
&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) =>
|
||||
field::LL_ADDR.end,
|
||||
&Repr::PrefixInformation(_) =>
|
||||
field::PREFIX.end,
|
||||
&Repr::RedirectedHeader(RedirectedHeader { header, data }) =>
|
||||
field::IP_DATA + header.buffer_len() + data.len(),
|
||||
&Repr::Mtu(_) =>
|
||||
field::MTU.end,
|
||||
&Repr::Unknown { length, .. } =>
|
||||
field::DATA(length).end
|
||||
&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) => field::LL_ADDR.end,
|
||||
&Repr::PrefixInformation(_) => field::PREFIX.end,
|
||||
&Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
|
||||
field::IP_DATA + header.buffer_len() + data.len()
|
||||
}
|
||||
&Repr::Mtu(_) => field::MTU.end,
|
||||
&Repr::Unknown { length, .. } => field::DATA(length).end,
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a high-level representation into an NDISC Option.
|
||||
pub fn emit<T>(&self, opt: &mut NdiscOption<&'a mut T>)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
|
||||
where
|
||||
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
|
||||
{
|
||||
match *self {
|
||||
Repr::SourceLinkLayerAddr(addr) => {
|
||||
opt.set_option_type(Type::SourceLinkLayerAddr);
|
||||
opt.set_data_len(1);
|
||||
opt.set_link_layer_addr(addr);
|
||||
},
|
||||
}
|
||||
Repr::TargetLinkLayerAddr(addr) => {
|
||||
opt.set_option_type(Type::TargetLinkLayerAddr);
|
||||
opt.set_data_len(1);
|
||||
opt.set_link_layer_addr(addr);
|
||||
},
|
||||
}
|
||||
Repr::PrefixInformation(PrefixInformation {
|
||||
prefix_len, flags, valid_lifetime,
|
||||
preferred_lifetime, prefix
|
||||
prefix_len,
|
||||
flags,
|
||||
valid_lifetime,
|
||||
preferred_lifetime,
|
||||
prefix,
|
||||
}) => {
|
||||
opt.clear_prefix_reserved();
|
||||
opt.set_option_type(Type::PrefixInformation);
|
||||
|
@ -533,10 +529,8 @@ impl<'a> Repr<'a> {
|
|||
opt.set_valid_lifetime(valid_lifetime);
|
||||
opt.set_preferred_lifetime(preferred_lifetime);
|
||||
opt.set_prefix(prefix);
|
||||
},
|
||||
Repr::RedirectedHeader(RedirectedHeader {
|
||||
header, data
|
||||
}) => {
|
||||
}
|
||||
Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
|
||||
let data_len = data.len() / 8;
|
||||
opt.clear_redirected_reserved();
|
||||
opt.set_option_type(Type::RedirectedHeader);
|
||||
|
@ -552,7 +546,11 @@ impl<'a> Repr<'a> {
|
|||
opt.set_data_len(1);
|
||||
opt.set_mtu(mtu);
|
||||
}
|
||||
Repr::Unknown { type_: id, length, data } => {
|
||||
Repr::Unknown {
|
||||
type_: id,
|
||||
length,
|
||||
data,
|
||||
} => {
|
||||
opt.set_option_type(Type::Unknown(id));
|
||||
opt.set_data_len(length);
|
||||
opt.data_mut().copy_from_slice(data);
|
||||
|
@ -567,67 +565,61 @@ impl<'a> fmt::Display for Repr<'a> {
|
|||
match *self {
|
||||
Repr::SourceLinkLayerAddr(addr) => {
|
||||
write!(f, "SourceLinkLayer addr={}", addr)
|
||||
},
|
||||
}
|
||||
Repr::TargetLinkLayerAddr(addr) => {
|
||||
write!(f, "TargetLinkLayer addr={}", addr)
|
||||
},
|
||||
}
|
||||
Repr::PrefixInformation(PrefixInformation {
|
||||
prefix, prefix_len,
|
||||
..
|
||||
prefix, prefix_len, ..
|
||||
}) => {
|
||||
write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len)
|
||||
},
|
||||
Repr::RedirectedHeader(RedirectedHeader {
|
||||
header,
|
||||
..
|
||||
}) => {
|
||||
}
|
||||
Repr::RedirectedHeader(RedirectedHeader { header, .. }) => {
|
||||
write!(f, "RedirectedHeader header={}", header)
|
||||
},
|
||||
}
|
||||
Repr::Mtu(mtu) => {
|
||||
write!(f, "MTU mtu={}", mtu)
|
||||
},
|
||||
Repr::Unknown { type_: id, length, .. } => {
|
||||
}
|
||||
Repr::Unknown {
|
||||
type_: id, length, ..
|
||||
} => {
|
||||
write!(f, "Unknown({}) length={}", id, length)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
match NdiscOption::new_checked(buffer) {
|
||||
Err(err) => return write!(f, "{}({})", indent, err),
|
||||
Ok(ndisc) => {
|
||||
match Repr::parse(&ndisc) {
|
||||
Ok(ndisc) => match Repr::parse(&ndisc) {
|
||||
Err(_) => Ok(()),
|
||||
Ok(repr) => {
|
||||
write!(f, "{}{}", indent, repr)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::Error;
|
||||
use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type};
|
||||
use crate::time::Duration;
|
||||
use crate::wire::{EthernetAddress, Ipv6Address};
|
||||
use super::{NdiscOption, Type, PrefixInfoFlags, PrefixInformation, Repr};
|
||||
use crate::Error;
|
||||
|
||||
static PREFIX_OPT_BYTES: [u8; 32] = [
|
||||
0x03, 0x04, 0x40, 0xc0,
|
||||
0x00, 0x00, 0x03, 0x84,
|
||||
0x00, 0x00, 0x03, 0xe8,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0xfe, 0x80, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01
|
||||
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00,
|
||||
0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01,
|
||||
];
|
||||
|
||||
#[test]
|
||||
|
@ -636,7 +628,10 @@ mod test {
|
|||
assert_eq!(opt.option_type(), Type::PrefixInformation);
|
||||
assert_eq!(opt.data_len(), 4);
|
||||
assert_eq!(opt.prefix_len(), 64);
|
||||
assert_eq!(opt.prefix_flags(), PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF);
|
||||
assert_eq!(
|
||||
opt.prefix_flags(),
|
||||
PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF
|
||||
);
|
||||
assert_eq!(opt.valid_lifetime(), Duration::from_secs(900));
|
||||
assert_eq!(opt.preferred_lifetime(), Duration::from_secs(1000));
|
||||
assert_eq!(opt.prefix(), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
|
||||
|
@ -658,11 +653,11 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_short_packet() {
|
||||
assert_eq!(NdiscOption::new_checked(&[0x00, 0x00]), Err(Error::Truncated));
|
||||
let bytes = [
|
||||
0x03, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
];
|
||||
assert_eq!(
|
||||
NdiscOption::new_checked(&[0x00, 0x00]),
|
||||
Err(Error::Truncated)
|
||||
);
|
||||
let bytes = [0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
|
||||
assert_eq!(NdiscOption::new_checked(&bytes), Err(Error::Truncated));
|
||||
}
|
||||
|
||||
|
@ -671,13 +666,17 @@ mod test {
|
|||
let mut bytes = [0x01, 0x01, 0x54, 0x52, 0x00, 0x12, 0x23, 0x34];
|
||||
let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]);
|
||||
{
|
||||
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)),
|
||||
Ok(Repr::SourceLinkLayerAddr(addr)));
|
||||
assert_eq!(
|
||||
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
|
||||
Ok(Repr::SourceLinkLayerAddr(addr))
|
||||
);
|
||||
}
|
||||
bytes[0] = 0x02;
|
||||
{
|
||||
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)),
|
||||
Ok(Repr::TargetLinkLayerAddr(addr)));
|
||||
assert_eq!(
|
||||
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
|
||||
Ok(Repr::TargetLinkLayerAddr(addr))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -688,9 +687,12 @@ mod test {
|
|||
flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
|
||||
valid_lifetime: Duration::from_secs(900),
|
||||
preferred_lifetime: Duration::from_secs(1000),
|
||||
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)
|
||||
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
|
||||
});
|
||||
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)), Ok(repr));
|
||||
assert_eq!(
|
||||
Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)),
|
||||
Ok(repr)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -701,7 +703,7 @@ mod test {
|
|||
flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
|
||||
valid_lifetime: Duration::from_secs(900),
|
||||
preferred_lifetime: Duration::from_secs(1000),
|
||||
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)
|
||||
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
|
||||
});
|
||||
let mut opt = NdiscOption::new_unchecked(&mut bytes);
|
||||
repr.emit(&mut opt);
|
||||
|
@ -711,6 +713,9 @@ mod test {
|
|||
#[test]
|
||||
fn test_repr_parse_mtu() {
|
||||
let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc];
|
||||
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)), Ok(Repr::Mtu(1500)));
|
||||
assert_eq!(
|
||||
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
|
||||
Ok(Repr::Mtu(1500))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ use core::marker::PhantomData;
|
|||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct PrettyIndent {
|
||||
prefix: &'static str,
|
||||
level: usize
|
||||
level: usize,
|
||||
}
|
||||
|
||||
impl PrettyIndent {
|
||||
|
@ -72,15 +72,18 @@ pub trait PrettyPrint {
|
|||
///
|
||||
/// `pretty_print` accepts a buffer and not a packet wrapper because the packet might
|
||||
/// be truncated, and so it might not be possible to create the packet wrapper.
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, fmt: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result;
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
fmt: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result;
|
||||
}
|
||||
|
||||
/// Wrapper for using a `PrettyPrint` where a `Display` is expected.
|
||||
pub struct PrettyPrinter<'a, T: PrettyPrint> {
|
||||
prefix: &'static str,
|
||||
buffer: &'a dyn AsRef<[u8]>,
|
||||
phantom: PhantomData<T>
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> {
|
||||
|
@ -89,7 +92,7 @@ impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> {
|
|||
PrettyPrinter {
|
||||
prefix: prefix,
|
||||
buffer: buffer,
|
||||
phantom: PhantomData
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
446
src/wire/tcp.rs
446
src/wire/tcp.rs
|
@ -1,10 +1,10 @@
|
|||
use core::{i32, ops, cmp, fmt};
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::{cmp, fmt, i32, ops};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::wire::{IpProtocol, IpAddress};
|
||||
use crate::wire::ip::checksum;
|
||||
use crate::wire::{IpAddress, IpProtocol};
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// A TCP sequence number.
|
||||
///
|
||||
|
@ -70,7 +70,7 @@ impl cmp::PartialOrd for SeqNumber {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
mod field {
|
||||
|
@ -289,8 +289,12 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
pub fn segment_len(&self) -> usize {
|
||||
let data = self.buffer.as_ref();
|
||||
let mut length = data.len() - self.header_len() as usize;
|
||||
if self.syn() { length += 1 }
|
||||
if self.fin() { length += 1 }
|
||||
if self.syn() {
|
||||
length += 1
|
||||
}
|
||||
if self.fin() {
|
||||
length += 1
|
||||
}
|
||||
length
|
||||
}
|
||||
|
||||
|
@ -333,13 +337,14 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
/// # Fuzzing
|
||||
/// This function always returns `true` when fuzzing.
|
||||
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
|
||||
if cfg!(fuzzing) { return true }
|
||||
if cfg!(fuzzing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let data = self.buffer.as_ref();
|
||||
checksum::combine(&[
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp,
|
||||
data.len() as u32),
|
||||
checksum::data(data)
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32),
|
||||
checksum::data(data),
|
||||
]) == !0
|
||||
}
|
||||
}
|
||||
|
@ -405,7 +410,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_fin(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_FIN } else { raw & !field::FLG_FIN };
|
||||
let raw = if value {
|
||||
raw | field::FLG_FIN
|
||||
} else {
|
||||
raw & !field::FLG_FIN
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -414,7 +423,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_syn(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_SYN } else { raw & !field::FLG_SYN };
|
||||
let raw = if value {
|
||||
raw | field::FLG_SYN
|
||||
} else {
|
||||
raw & !field::FLG_SYN
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -423,7 +436,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_rst(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_RST } else { raw & !field::FLG_RST };
|
||||
let raw = if value {
|
||||
raw | field::FLG_RST
|
||||
} else {
|
||||
raw & !field::FLG_RST
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -432,7 +449,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_psh(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_PSH } else { raw & !field::FLG_PSH };
|
||||
let raw = if value {
|
||||
raw | field::FLG_PSH
|
||||
} else {
|
||||
raw & !field::FLG_PSH
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -441,7 +462,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_ack(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_ACK } else { raw & !field::FLG_ACK };
|
||||
let raw = if value {
|
||||
raw | field::FLG_ACK
|
||||
} else {
|
||||
raw & !field::FLG_ACK
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -450,7 +475,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_urg(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_URG } else { raw & !field::FLG_URG };
|
||||
let raw = if value {
|
||||
raw | field::FLG_URG
|
||||
} else {
|
||||
raw & !field::FLG_URG
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -459,7 +488,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_ece(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_ECE } else { raw & !field::FLG_ECE };
|
||||
let raw = if value {
|
||||
raw | field::FLG_ECE
|
||||
} else {
|
||||
raw & !field::FLG_ECE
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -468,7 +501,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_cwr(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_CWR } else { raw & !field::FLG_CWR };
|
||||
let raw = if value {
|
||||
raw | field::FLG_CWR
|
||||
} else {
|
||||
raw & !field::FLG_CWR
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -477,7 +514,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
pub fn set_ns(&mut self, value: bool) {
|
||||
let data = self.buffer.as_mut();
|
||||
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
|
||||
let raw = if value { raw | field::FLG_NS } else { raw & !field::FLG_NS };
|
||||
let raw = if value {
|
||||
raw | field::FLG_NS
|
||||
} else {
|
||||
raw & !field::FLG_NS
|
||||
};
|
||||
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
|
||||
}
|
||||
|
||||
|
@ -521,9 +562,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
let checksum = {
|
||||
let data = self.buffer.as_ref();
|
||||
!checksum::combine(&[
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp,
|
||||
data.len() as u32),
|
||||
checksum::data(data)
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32),
|
||||
checksum::data(data),
|
||||
])
|
||||
};
|
||||
self.set_checksum(checksum)
|
||||
|
@ -562,7 +602,7 @@ pub enum TcpOption<'a> {
|
|||
WindowScale(u8),
|
||||
SackPermitted,
|
||||
SackRange([Option<(u32, u32)>; 3]),
|
||||
Unknown { kind: u8, data: &'a [u8] }
|
||||
Unknown { kind: u8, data: &'a [u8] },
|
||||
}
|
||||
|
||||
impl<'a> TcpOption<'a> {
|
||||
|
@ -581,24 +621,18 @@ impl<'a> TcpOption<'a> {
|
|||
length = *buffer.get(1).ok_or(Error::Truncated)? as usize;
|
||||
let data = buffer.get(2..length).ok_or(Error::Truncated)?;
|
||||
match (kind, length) {
|
||||
(field::OPT_END, _) |
|
||||
(field::OPT_NOP, _) =>
|
||||
unreachable!(),
|
||||
(field::OPT_MSS, 4) =>
|
||||
option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data)),
|
||||
(field::OPT_MSS, _) =>
|
||||
return Err(Error::Malformed),
|
||||
(field::OPT_WS, 3) =>
|
||||
option = TcpOption::WindowScale(data[0]),
|
||||
(field::OPT_WS, _) =>
|
||||
return Err(Error::Malformed),
|
||||
(field::OPT_SACKPERM, 2) =>
|
||||
option = TcpOption::SackPermitted,
|
||||
(field::OPT_SACKPERM, _) =>
|
||||
return Err(Error::Malformed),
|
||||
(field::OPT_END, _) | (field::OPT_NOP, _) => unreachable!(),
|
||||
(field::OPT_MSS, 4) => {
|
||||
option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data))
|
||||
}
|
||||
(field::OPT_MSS, _) => return Err(Error::Malformed),
|
||||
(field::OPT_WS, 3) => option = TcpOption::WindowScale(data[0]),
|
||||
(field::OPT_WS, _) => return Err(Error::Malformed),
|
||||
(field::OPT_SACKPERM, 2) => option = TcpOption::SackPermitted,
|
||||
(field::OPT_SACKPERM, _) => return Err(Error::Malformed),
|
||||
(field::OPT_SACKRNG, n) => {
|
||||
if n < 10 || (n-2) % 8 != 0 {
|
||||
return Err(Error::Malformed)
|
||||
if n < 10 || (n - 2) % 8 != 0 {
|
||||
return Err(Error::Malformed);
|
||||
}
|
||||
if n > 26 {
|
||||
// It's possible for a remote to send 4 SACK blocks, but extremely rare.
|
||||
|
@ -622,19 +656,16 @@ impl<'a> TcpOption<'a> {
|
|||
*nmut = if left < data.len() {
|
||||
let mid = left + 4;
|
||||
let right = mid + 4;
|
||||
let range_left = NetworkEndian::read_u32(
|
||||
&data[left..mid]);
|
||||
let range_right = NetworkEndian::read_u32(
|
||||
&data[mid..right]);
|
||||
let range_left = NetworkEndian::read_u32(&data[left..mid]);
|
||||
let range_right = NetworkEndian::read_u32(&data[mid..right]);
|
||||
Some((range_left, range_right))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
});
|
||||
option = TcpOption::SackRange(sack_ranges);
|
||||
},
|
||||
(_, _) =>
|
||||
option = TcpOption::Unknown { kind, data }
|
||||
}
|
||||
(_, _) => option = TcpOption::Unknown { kind, data },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -649,7 +680,7 @@ impl<'a> TcpOption<'a> {
|
|||
TcpOption::WindowScale(_) => 3,
|
||||
TcpOption::SackPermitted => 2,
|
||||
TcpOption::SackRange(s) => s.iter().filter(|s| s.is_some()).count() * 8 + 2,
|
||||
TcpOption::Unknown { data, .. } => 2 + data.len()
|
||||
TcpOption::Unknown { data, .. } => 2 + data.len(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -671,9 +702,7 @@ impl<'a> TcpOption<'a> {
|
|||
length = self.buffer_len();
|
||||
buffer[1] = length as u8;
|
||||
match self {
|
||||
&TcpOption::EndOfList |
|
||||
&TcpOption::NoOperation =>
|
||||
unreachable!(),
|
||||
&TcpOption::EndOfList | &TcpOption::NoOperation => unreachable!(),
|
||||
&TcpOption::MaxSegmentSize(value) => {
|
||||
buffer[0] = field::OPT_MSS;
|
||||
NetworkEndian::write_u16(&mut buffer[2..], value)
|
||||
|
@ -687,14 +716,21 @@ impl<'a> TcpOption<'a> {
|
|||
}
|
||||
&TcpOption::SackRange(slice) => {
|
||||
buffer[0] = field::OPT_SACKRNG;
|
||||
slice.iter().filter(|s| s.is_some()).enumerate().for_each(|(i, s)| {
|
||||
slice
|
||||
.iter()
|
||||
.filter(|s| s.is_some())
|
||||
.enumerate()
|
||||
.for_each(|(i, s)| {
|
||||
let (first, second) = *s.as_ref().unwrap();
|
||||
let pos = i * 8 + 2;
|
||||
NetworkEndian::write_u32(&mut buffer[pos..], first);
|
||||
NetworkEndian::write_u32(&mut buffer[pos+4..], second);
|
||||
NetworkEndian::write_u32(&mut buffer[pos + 4..], second);
|
||||
});
|
||||
}
|
||||
&TcpOption::Unknown { kind, data: provided } => {
|
||||
&TcpOption::Unknown {
|
||||
kind,
|
||||
data: provided,
|
||||
} => {
|
||||
buffer[0] = kind;
|
||||
buffer[2..].copy_from_slice(provided)
|
||||
}
|
||||
|
@ -713,7 +749,7 @@ pub enum Control {
|
|||
Psh,
|
||||
Syn,
|
||||
Fin,
|
||||
Rst
|
||||
Rst,
|
||||
}
|
||||
|
||||
#[allow(clippy::len_without_is_empty)]
|
||||
|
@ -722,7 +758,7 @@ impl Control {
|
|||
pub fn len(self) -> usize {
|
||||
match self {
|
||||
Control::Syn | Control::Fin => 1,
|
||||
_ => 0
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -730,7 +766,7 @@ impl Control {
|
|||
pub fn quash_psh(self) -> Control {
|
||||
match self {
|
||||
Control::Psh => Control::None,
|
||||
_ => self
|
||||
_ => self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -749,35 +785,43 @@ pub struct Repr<'a> {
|
|||
pub max_seg_size: Option<u16>,
|
||||
pub sack_permitted: bool,
|
||||
pub sack_ranges: [Option<(u32, u32)>; 3],
|
||||
pub payload: &'a [u8]
|
||||
pub payload: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> Repr<'a> {
|
||||
/// Parse a Transmission Control Protocol packet and return a high-level representation.
|
||||
pub fn parse<T>(packet: &Packet<&'a T>, src_addr: &IpAddress, dst_addr: &IpAddress,
|
||||
checksum_caps: &ChecksumCapabilities) -> Result<Repr<'a>>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(
|
||||
packet: &Packet<&'a T>,
|
||||
src_addr: &IpAddress,
|
||||
dst_addr: &IpAddress,
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) -> Result<Repr<'a>>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
// Source and destination ports must be present.
|
||||
if packet.src_port() == 0 { return Err(Error::Malformed) }
|
||||
if packet.dst_port() == 0 { return Err(Error::Malformed) }
|
||||
if packet.src_port() == 0 {
|
||||
return Err(Error::Malformed);
|
||||
}
|
||||
if packet.dst_port() == 0 {
|
||||
return Err(Error::Malformed);
|
||||
}
|
||||
// Valid checksum is expected.
|
||||
if checksum_caps.tcp.rx() && !packet.verify_checksum(src_addr, dst_addr) {
|
||||
return Err(Error::Checksum)
|
||||
return Err(Error::Checksum);
|
||||
}
|
||||
|
||||
let control =
|
||||
match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) {
|
||||
let control = match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) {
|
||||
(false, false, false, false) => Control::None,
|
||||
(false, false, false, true) => Control::Psh,
|
||||
(true, false, false, _) => Control::Syn,
|
||||
(false, true, false, _) => Control::Fin,
|
||||
(false, false, true , _) => Control::Rst,
|
||||
_ => return Err(Error::Malformed)
|
||||
(false, false, true, _) => Control::Rst,
|
||||
_ => return Err(Error::Malformed),
|
||||
};
|
||||
let ack_number =
|
||||
match packet.ack() {
|
||||
let ack_number = match packet.ack() {
|
||||
true => Some(packet.ack_number()),
|
||||
false => None
|
||||
false => None,
|
||||
};
|
||||
// The PSH flag is ignored.
|
||||
// The URG flag and the urgent field is ignored. This behavior is standards-compliant,
|
||||
|
@ -794,24 +838,27 @@ impl<'a> Repr<'a> {
|
|||
match option {
|
||||
TcpOption::EndOfList => break,
|
||||
TcpOption::NoOperation => (),
|
||||
TcpOption::MaxSegmentSize(value) =>
|
||||
max_seg_size = Some(value),
|
||||
TcpOption::MaxSegmentSize(value) => max_seg_size = Some(value),
|
||||
TcpOption::WindowScale(value) => {
|
||||
// RFC 1323: Thus, the shift count must be limited to 14 (which allows windows
|
||||
// of 2**30 = 1 Gbyte). If a Window Scale option is received with a shift.cnt
|
||||
// value exceeding 14, the TCP should log the error but use 14 instead of the
|
||||
// specified value.
|
||||
window_scale = if value > 14 {
|
||||
net_debug!("{}:{}:{}:{}: parsed window scaling factor >14, setting to 14", src_addr, packet.src_port(), dst_addr, packet.dst_port());
|
||||
net_debug!(
|
||||
"{}:{}:{}:{}: parsed window scaling factor >14, setting to 14",
|
||||
src_addr,
|
||||
packet.src_port(),
|
||||
dst_addr,
|
||||
packet.dst_port()
|
||||
);
|
||||
Some(14)
|
||||
} else {
|
||||
Some(value)
|
||||
};
|
||||
},
|
||||
TcpOption::SackPermitted =>
|
||||
sack_permitted = true,
|
||||
TcpOption::SackRange(slice) =>
|
||||
sack_ranges = slice,
|
||||
}
|
||||
TcpOption::SackPermitted => sack_permitted = true,
|
||||
TcpOption::SackRange(slice) => sack_ranges = slice,
|
||||
_ => (),
|
||||
}
|
||||
options = next_options;
|
||||
|
@ -828,7 +875,7 @@ impl<'a> Repr<'a> {
|
|||
max_seg_size: max_seg_size,
|
||||
sack_permitted: sack_permitted,
|
||||
sack_ranges: sack_ranges,
|
||||
payload: packet.payload()
|
||||
payload: packet.payload(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -847,9 +894,11 @@ impl<'a> Repr<'a> {
|
|||
if self.sack_permitted {
|
||||
length += 2;
|
||||
}
|
||||
let sack_range_len: usize = self.sack_ranges.iter().map(
|
||||
|o| o.map(|_| 8).unwrap_or(0)
|
||||
).sum();
|
||||
let sack_range_len: usize = self
|
||||
.sack_ranges
|
||||
.iter()
|
||||
.map(|o| o.map(|_| 8).unwrap_or(0))
|
||||
.sum();
|
||||
if sack_range_len > 0 {
|
||||
length += sack_range_len + 2;
|
||||
}
|
||||
|
@ -865,9 +914,15 @@ impl<'a> Repr<'a> {
|
|||
}
|
||||
|
||||
/// Emit a high-level representation into a Transmission Control Protocol packet.
|
||||
pub fn emit<T>(&self, packet: &mut Packet<&mut T>, src_addr: &IpAddress, dst_addr: &IpAddress,
|
||||
checksum_caps: &ChecksumCapabilities)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
|
||||
pub fn emit<T>(
|
||||
&self,
|
||||
packet: &mut Packet<&mut T>,
|
||||
src_addr: &IpAddress,
|
||||
dst_addr: &IpAddress,
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) where
|
||||
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
|
||||
{
|
||||
packet.set_src_port(self.src_port);
|
||||
packet.set_dst_port(self.dst_port);
|
||||
packet.set_seq_number(self.seq_number);
|
||||
|
@ -880,21 +935,25 @@ impl<'a> Repr<'a> {
|
|||
Control::Psh => packet.set_psh(true),
|
||||
Control::Syn => packet.set_syn(true),
|
||||
Control::Fin => packet.set_fin(true),
|
||||
Control::Rst => packet.set_rst(true)
|
||||
Control::Rst => packet.set_rst(true),
|
||||
}
|
||||
packet.set_ack(self.ack_number.is_some());
|
||||
{
|
||||
let mut options = packet.options_mut();
|
||||
if let Some(value) = self.max_seg_size {
|
||||
let tmp = options; options = TcpOption::MaxSegmentSize(value).emit(tmp);
|
||||
let tmp = options;
|
||||
options = TcpOption::MaxSegmentSize(value).emit(tmp);
|
||||
}
|
||||
if let Some(value) = self.window_scale {
|
||||
let tmp = options; options = TcpOption::WindowScale(value).emit(tmp);
|
||||
let tmp = options;
|
||||
options = TcpOption::WindowScale(value).emit(tmp);
|
||||
}
|
||||
if self.sack_permitted {
|
||||
let tmp = options; options = TcpOption::SackPermitted.emit(tmp);
|
||||
let tmp = options;
|
||||
options = TcpOption::SackPermitted.emit(tmp);
|
||||
} else if self.ack_number.is_some() && self.sack_ranges.iter().any(|s| s.is_some()) {
|
||||
let tmp = options; options = TcpOption::SackRange(self.sack_ranges).emit(tmp);
|
||||
let tmp = options;
|
||||
options = TcpOption::SackRange(self.sack_ranges).emit(tmp);
|
||||
}
|
||||
|
||||
if !options.is_empty() {
|
||||
|
@ -923,7 +982,7 @@ impl<'a> Repr<'a> {
|
|||
match self.control {
|
||||
_ if !self.payload.is_empty() => 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> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Cannot use Repr::parse because we don't have the IP addresses.
|
||||
write!(f, "TCP src={} dst={}",
|
||||
self.src_port(), self.dst_port())?;
|
||||
if self.syn() { write!(f, " syn")? }
|
||||
if self.fin() { write!(f, " fin")? }
|
||||
if self.rst() { write!(f, " rst")? }
|
||||
if self.psh() { write!(f, " psh")? }
|
||||
if self.ece() { write!(f, " ece")? }
|
||||
if self.cwr() { write!(f, " cwr")? }
|
||||
if self.ns() { write!(f, " ns" )? }
|
||||
write!(f, "TCP src={} dst={}", self.src_port(), self.dst_port())?;
|
||||
if self.syn() {
|
||||
write!(f, " syn")?
|
||||
}
|
||||
if self.fin() {
|
||||
write!(f, " fin")?
|
||||
}
|
||||
if self.rst() {
|
||||
write!(f, " rst")?
|
||||
}
|
||||
if self.psh() {
|
||||
write!(f, " psh")?
|
||||
}
|
||||
if self.ece() {
|
||||
write!(f, " ece")?
|
||||
}
|
||||
if self.cwr() {
|
||||
write!(f, " cwr")?
|
||||
}
|
||||
if self.ns() {
|
||||
write!(f, " ns")?
|
||||
}
|
||||
write!(f, " seq={}", self.seq_number())?;
|
||||
if self.ack() {
|
||||
write!(f, " ack={}", self.ack_number())?;
|
||||
|
@ -952,24 +1024,18 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
|||
|
||||
let mut options = self.options();
|
||||
while !options.is_empty() {
|
||||
let (next_options, option) =
|
||||
match TcpOption::parse(options) {
|
||||
let (next_options, option) = match TcpOption::parse(options) {
|
||||
Ok(res) => res,
|
||||
Err(err) => return write!(f, " ({})", err)
|
||||
Err(err) => return write!(f, " ({})", err),
|
||||
};
|
||||
match option {
|
||||
TcpOption::EndOfList => break,
|
||||
TcpOption::NoOperation => (),
|
||||
TcpOption::MaxSegmentSize(value) =>
|
||||
write!(f, " mss={}", value)?,
|
||||
TcpOption::WindowScale(value) =>
|
||||
write!(f, " ws={}", value)?,
|
||||
TcpOption::SackPermitted =>
|
||||
write!(f, " sACK")?,
|
||||
TcpOption::SackRange(slice) =>
|
||||
write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s
|
||||
TcpOption::Unknown { kind, .. } =>
|
||||
write!(f, " opt({})", kind)?,
|
||||
TcpOption::MaxSegmentSize(value) => write!(f, " mss={}", value)?,
|
||||
TcpOption::WindowScale(value) => write!(f, " ws={}", value)?,
|
||||
TcpOption::SackPermitted => write!(f, " sACK")?,
|
||||
TcpOption::SackRange(slice) => write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s
|
||||
TcpOption::Unknown { kind, .. } => write!(f, " opt({})", kind)?,
|
||||
}
|
||||
options = next_options;
|
||||
}
|
||||
|
@ -979,14 +1045,13 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
|||
|
||||
impl<'a> fmt::Display for Repr<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "TCP src={} dst={}",
|
||||
self.src_port, self.dst_port)?;
|
||||
write!(f, "TCP src={} dst={}", self.src_port, self.dst_port)?;
|
||||
match self.control {
|
||||
Control::Syn => write!(f, " syn")?,
|
||||
Control::Fin => write!(f, " fin")?,
|
||||
Control::Rst => write!(f, " rst")?,
|
||||
Control::Psh => write!(f, " psh")?,
|
||||
Control::None => ()
|
||||
Control::None => (),
|
||||
}
|
||||
write!(f, " seq={}", self.seq_number)?;
|
||||
if let Some(ack_number) = self.ack_number {
|
||||
|
@ -1001,23 +1066,26 @@ impl<'a> fmt::Display for Repr<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
match Packet::new_checked(buffer) {
|
||||
Err(err) => write!(f, "{}({})", indent, err),
|
||||
Ok(packet) => write!(f, "{}{}", indent, packet)
|
||||
Ok(packet) => write!(f, "{}{}", indent, packet),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::Ipv4Address;
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
|
||||
|
@ -1025,22 +1093,16 @@ mod test {
|
|||
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
static PACKET_BYTES: [u8; 28] =
|
||||
[0xbf, 0x00, 0x00, 0x50,
|
||||
0x01, 0x23, 0x45, 0x67,
|
||||
0x89, 0xab, 0xcd, 0xef,
|
||||
0x60, 0x35, 0x01, 0x23,
|
||||
0x01, 0xb6, 0x02, 0x01,
|
||||
0x03, 0x03, 0x0c, 0x01,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static PACKET_BYTES: [u8; 28] = [
|
||||
0xbf, 0x00, 0x00, 0x50, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x60, 0x35, 0x01,
|
||||
0x23, 0x01, 0xb6, 0x02, 0x01, 0x03, 0x03, 0x0c, 0x01, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
static OPTION_BYTES: [u8; 4] =
|
||||
[0x03, 0x03, 0x0c, 0x01];
|
||||
static OPTION_BYTES: [u8; 4] = [0x03, 0x03, 0x0c, 0x01];
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
static PAYLOAD_BYTES: [u8; 4] =
|
||||
[0xaa, 0x00, 0x00, 0xff];
|
||||
static PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
|
@ -1062,7 +1124,10 @@ mod test {
|
|||
assert_eq!(packet.checksum(), 0x01b6);
|
||||
assert_eq!(packet.options(), &OPTION_BYTES[..]);
|
||||
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
|
||||
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
|
||||
assert_eq!(
|
||||
packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1107,13 +1172,10 @@ mod test {
|
|||
}
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
static SYN_PACKET_BYTES: [u8; 24] =
|
||||
[0xbf, 0x00, 0x00, 0x50,
|
||||
0x01, 0x23, 0x45, 0x67,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x50, 0x02, 0x01, 0x23,
|
||||
0x7a, 0x8d, 0x00, 0x00,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static SYN_PACKET_BYTES: [u8; 24] = [
|
||||
0xbf, 0x00, 0x00, 0x50, 0x01, 0x23, 0x45, 0x67, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x01,
|
||||
0x23, 0x7a, 0x8d, 0x00, 0x00, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
fn packet_repr() -> Repr<'static> {
|
||||
|
@ -1128,7 +1190,7 @@ mod test {
|
|||
max_seg_size: None,
|
||||
sack_permitted: false,
|
||||
sack_ranges: [None, None, None],
|
||||
payload: &PAYLOAD_BYTES
|
||||
payload: &PAYLOAD_BYTES,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1136,7 +1198,13 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
fn test_parse() {
|
||||
let packet = Packet::new_unchecked(&SYN_PACKET_BYTES[..]);
|
||||
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()).unwrap();
|
||||
let repr = Repr::parse(
|
||||
&packet,
|
||||
&SRC_ADDR.into(),
|
||||
&DST_ADDR.into(),
|
||||
&ChecksumCapabilities::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(repr, packet_repr());
|
||||
}
|
||||
|
||||
|
@ -1146,7 +1214,12 @@ mod test {
|
|||
let repr = packet_repr();
|
||||
let mut bytes = vec![0xa5; repr.buffer_len()];
|
||||
let mut packet = Packet::new_unchecked(&mut bytes);
|
||||
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default());
|
||||
repr.emit(
|
||||
&mut packet,
|
||||
&SRC_ADDR.into(),
|
||||
&DST_ADDR.into(),
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]);
|
||||
}
|
||||
|
||||
|
@ -1159,57 +1232,62 @@ mod test {
|
|||
}
|
||||
|
||||
macro_rules! assert_option_parses {
|
||||
($opt:expr, $data:expr) => ({
|
||||
($opt:expr, $data:expr) => {{
|
||||
assert_eq!(TcpOption::parse($data), Ok((&[][..], $opt)));
|
||||
let buffer = &mut [0; 40][..$opt.buffer_len()];
|
||||
assert_eq!($opt.emit(buffer), &mut []);
|
||||
assert_eq!(&*buffer, $data);
|
||||
})
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tcp_options() {
|
||||
assert_option_parses!(TcpOption::EndOfList,
|
||||
&[0x00]);
|
||||
assert_option_parses!(TcpOption::NoOperation,
|
||||
&[0x01]);
|
||||
assert_option_parses!(TcpOption::MaxSegmentSize(1500),
|
||||
&[0x02, 0x04, 0x05, 0xdc]);
|
||||
assert_option_parses!(TcpOption::WindowScale(12),
|
||||
&[0x03, 0x03, 0x0c]);
|
||||
assert_option_parses!(TcpOption::SackPermitted,
|
||||
&[0x4, 0x02]);
|
||||
assert_option_parses!(TcpOption::SackRange([Some((500, 1500)), None, None]),
|
||||
&[0x05, 0x0a,
|
||||
0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x05, 0xdc]);
|
||||
assert_option_parses!(TcpOption::SackRange([Some((875, 1225)), Some((1500, 2500)), None]),
|
||||
&[0x05, 0x12,
|
||||
0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x04, 0xc9,
|
||||
0x00, 0x00, 0x05, 0xdc, 0x00, 0x00, 0x09, 0xc4]);
|
||||
assert_option_parses!(TcpOption::SackRange([Some((875000, 1225000)),
|
||||
assert_option_parses!(TcpOption::EndOfList, &[0x00]);
|
||||
assert_option_parses!(TcpOption::NoOperation, &[0x01]);
|
||||
assert_option_parses!(TcpOption::MaxSegmentSize(1500), &[0x02, 0x04, 0x05, 0xdc]);
|
||||
assert_option_parses!(TcpOption::WindowScale(12), &[0x03, 0x03, 0x0c]);
|
||||
assert_option_parses!(TcpOption::SackPermitted, &[0x4, 0x02]);
|
||||
assert_option_parses!(
|
||||
TcpOption::SackRange([Some((500, 1500)), None, None]),
|
||||
&[0x05, 0x0a, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x05, 0xdc]
|
||||
);
|
||||
assert_option_parses!(
|
||||
TcpOption::SackRange([Some((875, 1225)), Some((1500, 2500)), None]),
|
||||
&[
|
||||
0x05, 0x12, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x04, 0xc9, 0x00, 0x00, 0x05, 0xdc,
|
||||
0x00, 0x00, 0x09, 0xc4
|
||||
]
|
||||
);
|
||||
assert_option_parses!(
|
||||
TcpOption::SackRange([
|
||||
Some((875000, 1225000)),
|
||||
Some((1500000, 2500000)),
|
||||
Some((876543210, 876654320))]),
|
||||
&[0x05, 0x1a,
|
||||
0x00, 0x0d, 0x59, 0xf8, 0x00, 0x12, 0xb1, 0x28,
|
||||
0x00, 0x16, 0xe3, 0x60, 0x00, 0x26, 0x25, 0xa0,
|
||||
0x34, 0x3e, 0xfc, 0xea, 0x34, 0x40, 0xae, 0xf0]);
|
||||
assert_option_parses!(TcpOption::Unknown { kind: 12, data: &[1, 2, 3][..] },
|
||||
&[0x0c, 0x05, 0x01, 0x02, 0x03])
|
||||
Some((876543210, 876654320))
|
||||
]),
|
||||
&[
|
||||
0x05, 0x1a, 0x00, 0x0d, 0x59, 0xf8, 0x00, 0x12, 0xb1, 0x28, 0x00, 0x16, 0xe3, 0x60,
|
||||
0x00, 0x26, 0x25, 0xa0, 0x34, 0x3e, 0xfc, 0xea, 0x34, 0x40, 0xae, 0xf0
|
||||
]
|
||||
);
|
||||
assert_option_parses!(
|
||||
TcpOption::Unknown {
|
||||
kind: 12,
|
||||
data: &[1, 2, 3][..]
|
||||
},
|
||||
&[0x0c, 0x05, 0x01, 0x02, 0x03]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_malformed_tcp_options() {
|
||||
assert_eq!(TcpOption::parse(&[]),
|
||||
Err(Error::Truncated));
|
||||
assert_eq!(TcpOption::parse(&[0xc]),
|
||||
Err(Error::Truncated));
|
||||
assert_eq!(TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]),
|
||||
Err(Error::Truncated));
|
||||
assert_eq!(TcpOption::parse(&[0xc, 0x01]),
|
||||
Err(Error::Truncated));
|
||||
assert_eq!(TcpOption::parse(&[0x2, 0x02]),
|
||||
Err(Error::Malformed));
|
||||
assert_eq!(TcpOption::parse(&[0x3, 0x02]),
|
||||
Err(Error::Malformed));
|
||||
assert_eq!(TcpOption::parse(&[]), Err(Error::Truncated));
|
||||
assert_eq!(TcpOption::parse(&[0xc]), Err(Error::Truncated));
|
||||
assert_eq!(
|
||||
TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]),
|
||||
Err(Error::Truncated)
|
||||
);
|
||||
assert_eq!(TcpOption::parse(&[0xc, 0x01]), Err(Error::Truncated));
|
||||
assert_eq!(TcpOption::parse(&[0x2, 0x02]), Err(Error::Malformed));
|
||||
assert_eq!(TcpOption::parse(&[0x3, 0x02]), Err(Error::Malformed));
|
||||
}
|
||||
}
|
||||
|
|
125
src/wire/udp.rs
125
src/wire/udp.rs
|
@ -1,16 +1,16 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
use core::fmt;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::phy::ChecksumCapabilities;
|
||||
use crate::wire::{IpProtocol, IpAddress};
|
||||
use crate::wire::ip::checksum;
|
||||
use crate::wire::{IpAddress, IpProtocol};
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// A read/write wrapper around an User Datagram Protocol packet buffer.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct Packet<T: AsRef<[u8]>> {
|
||||
buffer: T
|
||||
buffer: T,
|
||||
}
|
||||
|
||||
mod field {
|
||||
|
@ -113,13 +113,14 @@ impl<T: AsRef<[u8]>> Packet<T> {
|
|||
/// # Fuzzing
|
||||
/// This function always returns `true` when fuzzing.
|
||||
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
|
||||
if cfg!(fuzzing) { return true }
|
||||
if cfg!(fuzzing) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let data = self.buffer.as_ref();
|
||||
checksum::combine(&[
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp,
|
||||
self.len() as u32),
|
||||
checksum::data(&data[..self.len() as usize])
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32),
|
||||
checksum::data(&data[..self.len() as usize]),
|
||||
]) == !0
|
||||
}
|
||||
}
|
||||
|
@ -173,9 +174,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
|
|||
let checksum = {
|
||||
let data = self.buffer.as_ref();
|
||||
!checksum::combine(&[
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp,
|
||||
self.len() as u32),
|
||||
checksum::data(&data[..self.len() as usize])
|
||||
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32),
|
||||
checksum::data(&data[..self.len() as usize]),
|
||||
])
|
||||
};
|
||||
// UDP checksum value of 0 means no checksum; if the checksum really is zero,
|
||||
|
@ -210,21 +210,26 @@ pub struct Repr {
|
|||
|
||||
impl Repr {
|
||||
/// Parse an User Datagram Protocol packet and return a high-level representation.
|
||||
pub fn parse<T>(packet: &Packet<&T>, src_addr: &IpAddress, dst_addr: &IpAddress,
|
||||
checksum_caps: &ChecksumCapabilities) -> Result<Repr>
|
||||
where T: AsRef<[u8]> + ?Sized {
|
||||
pub fn parse<T>(
|
||||
packet: &Packet<&T>,
|
||||
src_addr: &IpAddress,
|
||||
dst_addr: &IpAddress,
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) -> Result<Repr>
|
||||
where
|
||||
T: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
// Destination port cannot be omitted (but source port can be).
|
||||
if packet.dst_port() == 0 { return Err(Error::Malformed) }
|
||||
if packet.dst_port() == 0 {
|
||||
return Err(Error::Malformed);
|
||||
}
|
||||
// Valid checksum is expected...
|
||||
if checksum_caps.udp.rx() && !packet.verify_checksum(src_addr, dst_addr) {
|
||||
match (src_addr, dst_addr) {
|
||||
// ... except on UDP-over-IPv4, where it can be omitted.
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
(&IpAddress::Ipv4(_), &IpAddress::Ipv4(_))
|
||||
if packet.checksum() == 0 => (),
|
||||
_ => {
|
||||
return Err(Error::Checksum)
|
||||
}
|
||||
(&IpAddress::Ipv4(_), &IpAddress::Ipv4(_)) if packet.checksum() == 0 => (),
|
||||
_ => return Err(Error::Checksum),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,13 +245,17 @@ impl Repr {
|
|||
}
|
||||
|
||||
/// Emit a high-level representation into an User Datagram Protocol packet.
|
||||
pub fn emit<T: ?Sized>(&self, packet: &mut Packet<&mut T>,
|
||||
pub fn emit<T: ?Sized>(
|
||||
&self,
|
||||
packet: &mut Packet<&mut T>,
|
||||
src_addr: &IpAddress,
|
||||
dst_addr: &IpAddress,
|
||||
payload_len: usize,
|
||||
emit_payload: impl FnOnce(&mut [u8]),
|
||||
checksum_caps: &ChecksumCapabilities)
|
||||
where T: AsRef<[u8]> + AsMut<[u8]> {
|
||||
checksum_caps: &ChecksumCapabilities,
|
||||
) where
|
||||
T: AsRef<[u8]> + AsMut<[u8]>,
|
||||
{
|
||||
packet.set_src_port(self.src_port);
|
||||
packet.set_dst_port(self.dst_port);
|
||||
packet.set_len((HEADER_LEN + payload_len) as u16);
|
||||
|
@ -265,8 +274,13 @@ impl Repr {
|
|||
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// Cannot use Repr::parse because we don't have the IP addresses.
|
||||
write!(f, "UDP src={} dst={} len={}",
|
||||
self.src_port(), self.dst_port(), self.payload().len())
|
||||
write!(
|
||||
f,
|
||||
"UDP src={} dst={} len={}",
|
||||
self.src_port(),
|
||||
self.dst_port(),
|
||||
self.payload().len()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,23 +290,26 @@ impl fmt::Display for Repr {
|
|||
}
|
||||
}
|
||||
|
||||
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
|
||||
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
|
||||
|
||||
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
|
||||
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent) -> fmt::Result {
|
||||
fn pretty_print(
|
||||
buffer: &dyn AsRef<[u8]>,
|
||||
f: &mut fmt::Formatter,
|
||||
indent: &mut PrettyIndent,
|
||||
) -> fmt::Result {
|
||||
match Packet::new_checked(buffer) {
|
||||
Err(err) => write!(f, "{}({})", indent, err),
|
||||
Ok(packet) => write!(f, "{}{}", indent, packet)
|
||||
Ok(packet) => write!(f, "{}{}", indent, packet),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
use crate::wire::Ipv4Address;
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
|
||||
|
@ -300,20 +317,17 @@ mod test {
|
|||
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
static PACKET_BYTES: [u8; 12] =
|
||||
[0xbf, 0x00, 0x00, 0x35,
|
||||
0x00, 0x0c, 0x12, 0x4d,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static PACKET_BYTES: [u8; 12] = [
|
||||
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
static NO_CHECKSUM_PACKET: [u8; 12] =
|
||||
[0xbf, 0x00, 0x00, 0x35,
|
||||
0x00, 0x0c, 0x00, 0x00,
|
||||
0xaa, 0x00, 0x00, 0xff];
|
||||
static NO_CHECKSUM_PACKET: [u8; 12] = [
|
||||
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x00, 0x00, 0xaa, 0x00, 0x00, 0xff,
|
||||
];
|
||||
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
static PAYLOAD_BYTES: [u8; 4] =
|
||||
[0xaa, 0x00, 0x00, 0xff];
|
||||
static PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "proto-ipv4")]
|
||||
|
@ -324,7 +338,10 @@ mod test {
|
|||
assert_eq!(packet.len(), 12);
|
||||
assert_eq!(packet.checksum(), 0x124d);
|
||||
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
|
||||
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
|
||||
assert_eq!(
|
||||
packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -373,8 +390,13 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
fn test_parse() {
|
||||
let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
|
||||
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(),
|
||||
&ChecksumCapabilities::default()).unwrap();
|
||||
let repr = Repr::parse(
|
||||
&packet,
|
||||
&SRC_ADDR.into(),
|
||||
&DST_ADDR.into(),
|
||||
&ChecksumCapabilities::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(repr, packet_repr());
|
||||
}
|
||||
|
||||
|
@ -384,10 +406,14 @@ mod test {
|
|||
let repr = packet_repr();
|
||||
let mut bytes = vec![0xa5; repr.header_len() + PAYLOAD_BYTES.len()];
|
||||
let mut packet = Packet::new_unchecked(&mut bytes);
|
||||
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(),
|
||||
repr.emit(
|
||||
&mut packet,
|
||||
&SRC_ADDR.into(),
|
||||
&DST_ADDR.into(),
|
||||
PAYLOAD_BYTES.len(),
|
||||
|payload| payload.copy_from_slice(&PAYLOAD_BYTES),
|
||||
&ChecksumCapabilities::default());
|
||||
&ChecksumCapabilities::default(),
|
||||
);
|
||||
assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
|
||||
}
|
||||
|
||||
|
@ -395,8 +421,13 @@ mod test {
|
|||
#[cfg(feature = "proto-ipv4")]
|
||||
fn test_checksum_omitted() {
|
||||
let packet = Packet::new_unchecked(&NO_CHECKSUM_PACKET[..]);
|
||||
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(),
|
||||
&ChecksumCapabilities::default()).unwrap();
|
||||
let repr = Repr::parse(
|
||||
&packet,
|
||||
&SRC_ADDR.into(),
|
||||
&DST_ADDR.into(),
|
||||
&ChecksumCapabilities::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(repr, packet_repr());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,18 @@
|
|||
use std::cell::RefCell;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::env;
|
||||
use std::process::exit;
|
||||
use getopts::Options;
|
||||
use smoltcp::phy::{PcapLinkType, PcapSink};
|
||||
use smoltcp::time::Instant;
|
||||
use getopts::Options;
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
|
||||
fn convert(packet_filename: &Path, pcap_filename: &Path, link_type: PcapLinkType)
|
||||
-> io::Result<()> {
|
||||
fn convert(
|
||||
packet_filename: &Path,
|
||||
pcap_filename: &Path,
|
||||
link_type: PcapLinkType,
|
||||
) -> io::Result<()> {
|
||||
let mut packet_file = File::open(packet_filename)?;
|
||||
let mut packet = Vec::new();
|
||||
packet_file.read_to_end(&mut packet)?;
|
||||
|
@ -35,31 +38,37 @@ fn main() {
|
|||
|
||||
let mut opts = Options::new();
|
||||
opts.optflag("h", "help", "print this help menu");
|
||||
opts.optopt("t", "link-type", "set link type (one of: ethernet ip)", "TYPE");
|
||||
opts.optopt(
|
||||
"t",
|
||||
"link-type",
|
||||
"set link type (one of: ethernet ip)",
|
||||
"TYPE",
|
||||
);
|
||||
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
return
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let link_type =
|
||||
match matches.opt_str("t").as_ref().map(|s| &s[..]) {
|
||||
let link_type = match matches.opt_str("t").as_ref().map(|s| &s[..]) {
|
||||
Some("ethernet") => Some(PcapLinkType::Ethernet),
|
||||
Some("ip") => Some(PcapLinkType::Ip),
|
||||
_ => None
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if matches.opt_present("h") || matches.free.len() != 2 || link_type.is_none() {
|
||||
print_usage(&program, opts);
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
match convert(Path::new(&matches.free[0]),
|
||||
match convert(
|
||||
Path::new(&matches.free[0]),
|
||||
Path::new(&matches.free[1]),
|
||||
link_type.unwrap()) {
|
||||
link_type.unwrap(),
|
||||
) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
eprintln!("Cannot convert packet to pcap: {}", e);
|
||||
|
|
Loading…
Reference in New Issue