Add ICMP sockets
- Add support for ICMP sockets - Add tests for ICMP sockets - Rename proto-<type> features to socket-<type> - Update documentationv0.7.x
parent
c3e07dad9a
commit
adb5014780
|
@ -20,11 +20,13 @@ matrix:
|
|||
- rust: nightly
|
||||
env: FEATURES='socket-tcp' MODE='build'
|
||||
- rust: nightly
|
||||
env: FEATURES='socket-raw socket-udp socket-tcp' MODE='build'
|
||||
env: FEATURES='socket-icmp' MODE='build'
|
||||
- rust: nightly
|
||||
env: FEATURES='socket-raw socket-udp socket-tcp std' MODE='build'
|
||||
env: FEATURES='socket-raw socket-udp socket-tcp socket-icmp' MODE='build'
|
||||
- rust: nightly
|
||||
env: FEATURES='socket-raw socket-udp socket-tcp alloc' MODE='build'
|
||||
env: FEATURES='socket-raw socket-udp socket-tcp socket-icmp std' MODE='build'
|
||||
- rust: nightly
|
||||
env: FEATURES='socket-raw socket-udp socket-tcp socket-icmp alloc' MODE='build'
|
||||
script:
|
||||
- cargo "$MODE" --no-default-features --features "$FEATURES"
|
||||
notifications:
|
||||
|
|
|
@ -31,9 +31,11 @@ verbose = []
|
|||
"socket-raw" = []
|
||||
"socket-udp" = []
|
||||
"socket-tcp" = []
|
||||
"socket-icmp" = []
|
||||
default = ["std", "log",
|
||||
"phy-raw_socket", "phy-tap_interface",
|
||||
"socket-raw", "socket-udp", "socket-tcp"]
|
||||
"socket-raw", "socket-udp", "socket-tcp",
|
||||
"socket-icmp"]
|
||||
|
||||
[[example]]
|
||||
name = "tcpdump"
|
||||
|
|
|
@ -12,11 +12,10 @@ use std::time::Instant;
|
|||
use std::os::unix::io::AsRawFd;
|
||||
use smoltcp::phy::Device;
|
||||
use smoltcp::phy::wait as phy_wait;
|
||||
use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr,
|
||||
Ipv4Address, Ipv4Packet, Ipv4Repr,
|
||||
Icmpv4Repr, Icmpv4Packet};
|
||||
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr,
|
||||
Ipv4Address, Icmpv4Repr, Icmpv4Packet};
|
||||
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
|
||||
use smoltcp::socket::{SocketSet, RawSocket, RawSocketBuffer, RawPacketBuffer};
|
||||
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketBuffer, IcmpEndpoint};
|
||||
use std::collections::HashMap;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
|
@ -51,10 +50,9 @@ fn main() {
|
|||
let remote_addr = address;
|
||||
let local_addr = Ipv4Address::new(192, 168, 69, 1);
|
||||
|
||||
let raw_rx_buffer = RawSocketBuffer::new(vec![RawPacketBuffer::new(vec![0; 256])]);
|
||||
let raw_tx_buffer = RawSocketBuffer::new(vec![RawPacketBuffer::new(vec![0; 256])]);
|
||||
let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Icmp,
|
||||
raw_rx_buffer, raw_tx_buffer);
|
||||
let icmp_rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 256])]);
|
||||
let icmp_tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 256])]);
|
||||
let icmp_socket = IcmpSocket::new(icmp_rx_buffer, icmp_tx_buffer);
|
||||
|
||||
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
|
||||
let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
|
||||
|
@ -64,17 +62,22 @@ fn main() {
|
|||
ethernet_addr, [ip_addr], Some(default_v4_gw));
|
||||
|
||||
let mut sockets = SocketSet::new(vec![]);
|
||||
let raw_handle = sockets.add(raw_socket);
|
||||
let icmp_handle = sockets.add(icmp_socket);
|
||||
|
||||
let mut send_at = 0;
|
||||
let mut seq_no = 0;
|
||||
let mut received = 0;
|
||||
let mut echo_payload = [0xffu8; 40];
|
||||
let mut waiting_queue = HashMap::new();
|
||||
let ident = 0x22b;
|
||||
let endpoint = IpAddress::Ipv4(remote_addr);
|
||||
|
||||
loop {
|
||||
{
|
||||
let mut socket = sockets.get::<RawSocket>(raw_handle);
|
||||
let mut socket = sockets.get::<IcmpSocket>(icmp_handle);
|
||||
if !socket.is_open() {
|
||||
socket.bind(IcmpEndpoint::Ident(ident)).unwrap()
|
||||
}
|
||||
|
||||
let timestamp = Instant::now().duration_since(startup_time);
|
||||
let timestamp_us = (timestamp.as_secs() * 1000000) +
|
||||
|
@ -84,26 +87,16 @@ fn main() {
|
|||
send_at <= utils::millis_since(startup_time) {
|
||||
NetworkEndian::write_u64(&mut echo_payload, timestamp_us);
|
||||
let icmp_repr = Icmpv4Repr::EchoRequest {
|
||||
ident: 1,
|
||||
ident: ident,
|
||||
seq_no,
|
||||
data: &echo_payload,
|
||||
};
|
||||
let ipv4_repr = Ipv4Repr {
|
||||
/*src_addr: Ipv4Address::UNSPECIFIED,*/
|
||||
src_addr: Ipv4Address::new(0, 0, 0, 0),
|
||||
dst_addr: remote_addr,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: icmp_repr.buffer_len(),
|
||||
ttl: 64
|
||||
};
|
||||
|
||||
let raw_payload = socket
|
||||
.send(ipv4_repr.buffer_len() + icmp_repr.buffer_len())
|
||||
let icmp_payload = socket
|
||||
.send(icmp_repr.buffer_len(), endpoint)
|
||||
.unwrap();
|
||||
|
||||
let mut ipv4_packet = Ipv4Packet::new(raw_payload);
|
||||
ipv4_repr.emit(&mut ipv4_packet, &device_caps.checksum);
|
||||
let mut icmp_packet = Icmpv4Packet::new(ipv4_packet.payload_mut());
|
||||
let mut icmp_packet = Icmpv4Packet::new(icmp_payload);
|
||||
icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);
|
||||
|
||||
waiting_queue.insert(seq_no, timestamp);
|
||||
|
@ -112,23 +105,18 @@ fn main() {
|
|||
}
|
||||
|
||||
if socket.can_recv() {
|
||||
let payload = socket.recv().unwrap();
|
||||
let ipv4_packet = Ipv4Packet::new(payload);
|
||||
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &device_caps.checksum).unwrap();
|
||||
let (payload, _) = socket.recv().unwrap();
|
||||
let icmp_packet = Icmpv4Packet::new(&payload);
|
||||
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();
|
||||
|
||||
if ipv4_repr.src_addr == remote_addr && ipv4_repr.dst_addr == local_addr {
|
||||
let icmp_packet = Icmpv4Packet::new(ipv4_packet.payload());
|
||||
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum);
|
||||
|
||||
if let Ok(Icmpv4Repr::EchoReply { seq_no, data, .. }) = icmp_repr {
|
||||
if let Some(_) = waiting_queue.get(&seq_no) {
|
||||
let packet_timestamp_us = NetworkEndian::read_u64(data);
|
||||
println!("{} bytes from {}: icmp_seq={}, time={:.3}ms",
|
||||
data.len(), remote_addr, seq_no,
|
||||
(timestamp_us - packet_timestamp_us) as f64 / 1000.0);
|
||||
waiting_queue.remove(&seq_no);
|
||||
received += 1;
|
||||
}
|
||||
if let Icmpv4Repr::EchoReply { seq_no, data, .. } = icmp_repr {
|
||||
if let Some(_) = waiting_queue.get(&seq_no) {
|
||||
let packet_timestamp_us = NetworkEndian::read_u64(data);
|
||||
println!("{} bytes from {}: icmp_seq={}, time={:.3}ms",
|
||||
data.len(), remote_addr, seq_no,
|
||||
(timestamp_us - packet_timestamp_us) as f64 / 1000.0);
|
||||
waiting_queue.remove(&seq_no);
|
||||
received += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ use socket::RawSocket;
|
|||
use socket::UdpSocket;
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
use socket::TcpSocket;
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
use socket::IcmpSocket;
|
||||
use super::ArpCache;
|
||||
|
||||
/// An Ethernet network interface.
|
||||
|
@ -55,7 +57,7 @@ struct InterfaceInner<'b, 'c> {
|
|||
enum Packet<'a> {
|
||||
None,
|
||||
Arp(ArpRepr),
|
||||
Icmpv4(Ipv4Repr, Icmpv4Repr<'a>),
|
||||
Icmpv4((Ipv4Repr, Icmpv4Repr<'a>)),
|
||||
#[cfg(feature = "socket-raw")]
|
||||
Raw((IpRepr, &'a [u8])),
|
||||
#[cfg(feature = "socket-udp")]
|
||||
|
@ -235,6 +237,16 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
|
|||
device_result = inner.dispatch(tx_token, timestamp, Packet::Tcp(response));
|
||||
device_result
|
||||
}),
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
Socket::Icmp(ref mut socket) =>
|
||||
socket.dispatch(&caps, |response| {
|
||||
let tx_token = device.transmit().ok_or(Error::Exhausted)?;
|
||||
match response {
|
||||
(IpRepr::Ipv4(repr), icmp_repr) =>
|
||||
inner.dispatch(tx_token, timestamp, Packet::Icmpv4((repr, icmp_repr))),
|
||||
_ => Err(Error::Unaddressable),
|
||||
}
|
||||
}),
|
||||
Socket::__Nonexhaustive(_) => unreachable!()
|
||||
};
|
||||
match (device_result, socket_result) {
|
||||
|
@ -384,7 +396,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
|
||||
match ipv4_repr.protocol {
|
||||
IpProtocol::Icmp =>
|
||||
self.process_icmpv4(ipv4_repr, ip_payload),
|
||||
self.process_icmpv4(sockets, ip_repr, ip_payload),
|
||||
|
||||
#[cfg(feature = "socket-udp")]
|
||||
IpProtocol::Udp =>
|
||||
|
@ -412,13 +424,25 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
}
|
||||
}
|
||||
|
||||
fn process_icmpv4<'frame>(&self, ipv4_repr: Ipv4Repr, ip_payload: &'frame [u8]) ->
|
||||
Result<Packet<'frame>>
|
||||
fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr,
|
||||
ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
|
||||
{
|
||||
let icmp_packet = Icmpv4Packet::new_checked(ip_payload)?;
|
||||
let checksum_caps = self.device_capabilities.checksum.clone();
|
||||
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &checksum_caps)?;
|
||||
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
for mut icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) {
|
||||
if !icmp_socket.accepts(&ip_repr, &icmp_repr, &checksum_caps) { continue }
|
||||
|
||||
match icmp_socket.process(&ip_repr, ip_payload) {
|
||||
// The packet is valid and handled by socket.
|
||||
Ok(()) => return Ok(Packet::None),
|
||||
// The packet is malformed, or the socket buffer is full.
|
||||
Err(e) => return Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
match icmp_repr {
|
||||
// Respond to echo requests.
|
||||
Icmpv4Repr::EchoRequest { ident, seq_no, data } => {
|
||||
|
@ -427,7 +451,10 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
seq_no: seq_no,
|
||||
data: data
|
||||
};
|
||||
Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr))
|
||||
match ip_repr {
|
||||
IpRepr::Ipv4(ipv4_repr) => Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)),
|
||||
_ => Err(Error::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore any echo replies.
|
||||
|
@ -450,7 +477,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
payload_len: icmp_repr.buffer_len(),
|
||||
ttl: 64
|
||||
};
|
||||
Packet::Icmpv4(ipv4_reply_repr, icmp_repr)
|
||||
Packet::Icmpv4((ipv4_reply_repr, icmp_repr))
|
||||
} else {
|
||||
// Do not send any ICMP replies to a broadcast destination address.
|
||||
Packet::None
|
||||
|
@ -549,7 +576,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
arp_repr.emit(&mut packet);
|
||||
})
|
||||
},
|
||||
Packet::Icmpv4(ipv4_repr, icmpv4_repr) => {
|
||||
Packet::Icmpv4((ipv4_repr, icmpv4_repr)) => {
|
||||
self.dispatch_ip(tx_token, timestamp, IpRepr::Ipv4(ipv4_repr), |_ip_repr, payload| {
|
||||
icmpv4_repr.emit(&mut Icmpv4Packet::new(payload), &checksum_caps);
|
||||
})
|
||||
|
@ -702,17 +729,19 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::boxed::Box;
|
||||
use super::Packet;
|
||||
use {Result, Error};
|
||||
|
||||
use iface::{ArpCache, SliceArpCache, EthernetInterface};
|
||||
use phy::{self, Loopback, ChecksumCapabilities};
|
||||
use socket::SocketSet;
|
||||
use wire::{ArpOperation, ArpPacket, ArpRepr};
|
||||
use wire::{EthernetAddress, EthernetFrame, EthernetProtocol};
|
||||
use wire::{IpAddress, IpCidr, IpProtocol, IpRepr};
|
||||
use wire::{Ipv4Address, Ipv4Repr};
|
||||
use wire::{Icmpv4Repr, Icmpv4DstUnreachable};
|
||||
use wire::{UdpPacket, UdpRepr};
|
||||
use iface::{ArpCache, SliceArpCache, EthernetInterface};
|
||||
use socket::SocketSet;
|
||||
use {Result, Error};
|
||||
|
||||
use super::Packet;
|
||||
|
||||
fn create_loopback<'a, 'b>() ->
|
||||
(EthernetInterface<'static, 'b, Loopback>, SocketSet<'static, 'a, 'b>) {
|
||||
|
@ -812,7 +841,7 @@ mod test {
|
|||
data: &NO_BYTES
|
||||
};
|
||||
|
||||
let expected_repr = Packet::Icmpv4(
|
||||
let expected_repr = Packet::Icmpv4((
|
||||
Ipv4Repr {
|
||||
src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
|
||||
dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
|
||||
|
@ -821,7 +850,7 @@ mod test {
|
|||
ttl: 64
|
||||
},
|
||||
icmp_repr
|
||||
);
|
||||
));
|
||||
|
||||
// Ensure that the unknown protocol triggers an error response.
|
||||
// And we correctly handle no payload.
|
||||
|
@ -877,7 +906,7 @@ mod test {
|
|||
},
|
||||
data: &data
|
||||
};
|
||||
let expected_repr = Packet::Icmpv4(
|
||||
let expected_repr = Packet::Icmpv4((
|
||||
Ipv4Repr {
|
||||
src_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x01]),
|
||||
dst_addr: Ipv4Address([0x7f, 0x00, 0x00, 0x02]),
|
||||
|
@ -886,7 +915,7 @@ mod test {
|
|||
ttl: 64
|
||||
},
|
||||
icmp_repr
|
||||
);
|
||||
));
|
||||
|
||||
// Ensure that the unknown protocol triggers an error response.
|
||||
// And we correctly handle no payload.
|
||||
|
@ -994,4 +1023,60 @@ mod test {
|
|||
&IpAddress::Ipv4(remote_ip_addr)),
|
||||
Ok((remote_hw_addr, MockTxToken)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
fn test_icmpv4_socket() {
|
||||
use socket::{IcmpPacketBuffer, IcmpSocket, IcmpSocketBuffer, IcmpEndpoint};
|
||||
use wire::Icmpv4Packet;
|
||||
|
||||
let (iface, mut socket_set) = create_loopback();
|
||||
|
||||
let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 24])]);
|
||||
let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 24])]);
|
||||
|
||||
let icmpv4_socket = IcmpSocket::new(rx_buffer, tx_buffer);
|
||||
|
||||
let socket_handle = socket_set.add(icmpv4_socket);
|
||||
|
||||
let ident = 0x1234;
|
||||
{
|
||||
let mut socket = socket_set.get::<IcmpSocket>(socket_handle);
|
||||
// Bind to the ID 0x1234
|
||||
assert_eq!(socket.bind(IcmpEndpoint::Ident(ident)), Ok(()));
|
||||
}
|
||||
|
||||
// Ensure the ident we bound to and the ident of the packet are the same.
|
||||
let mut bytes = [0xff; 24];
|
||||
let mut packet = Icmpv4Packet::new(&mut bytes);
|
||||
let echo_repr = Icmpv4Repr::EchoReply {
|
||||
ident: ident,
|
||||
seq_no: 0x5432,
|
||||
data: &[0xff; 16],
|
||||
};
|
||||
echo_repr.emit(&mut packet, &ChecksumCapabilities::default());
|
||||
let data = &packet.into_inner()[..];
|
||||
|
||||
let ip_repr = IpRepr::Ipv4(Ipv4Repr {
|
||||
src_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x02),
|
||||
dst_addr: Ipv4Address::new(0x7f, 0x00, 0x00, 0x01),
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: 24,
|
||||
ttl: 64
|
||||
});
|
||||
|
||||
// Open a socket and ensure the packet is handled due to the listening
|
||||
// socket.
|
||||
{
|
||||
assert!(!socket_set.get::<IcmpSocket>(socket_handle).can_recv());
|
||||
}
|
||||
assert_eq!(iface.inner.process_icmpv4(&mut socket_set, ip_repr, data),
|
||||
Ok(Packet::None));
|
||||
{
|
||||
let mut socket = socket_set.get::<IcmpSocket>(socket_handle);
|
||||
assert!(socket.can_recv());
|
||||
assert_eq!(socket.recv(),
|
||||
Ok((&data[..], IpAddress::Ipv4(Ipv4Address::new(0x7f, 0x00, 0x00, 0x02)))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
//!
|
||||
//! # The socket layer
|
||||
//! The socket layer APIs are provided in the module [socket](socket/index.html); currently,
|
||||
//! TCP and UDP sockets are provided. The socket API provides the usual primitives, but
|
||||
//! necessarily differs in many from the [Berkeley socket API][berk], as the latter was not
|
||||
//! designed to be used without heap allocation.
|
||||
//! TCP, UDP, ICMP, and Raw sockets are provided. The socket API provides the usual primitives,
|
||||
//! but necessarily differs in many from the [Berkeley socket API][berk], as the latter was
|
||||
//! not designed to be used without heap allocation.
|
||||
//! [berk]: https://en.wikipedia.org/wiki/Berkeley_sockets
|
||||
//!
|
||||
//! The socket layer provides the buffering, packet construction and validation, and (for
|
||||
|
|
|
@ -0,0 +1,583 @@
|
|||
use core::cmp;
|
||||
use managed::Managed;
|
||||
|
||||
use {Error, Result};
|
||||
use phy::{ChecksumCapabilities, DeviceCapabilities};
|
||||
use socket::{Socket, SocketHandle};
|
||||
use storage::{Resettable, RingBuffer};
|
||||
use wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr};
|
||||
use wire::{Ipv4Address, Ipv4Repr};
|
||||
use wire::{Icmpv4Packet, Icmpv4Repr};
|
||||
use wire::{UdpPacket, UdpRepr};
|
||||
|
||||
/// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for
|
||||
/// more details.
|
||||
///
|
||||
/// [IcmpSocket::bind]: struct.IcmpSocket.html#method.bind
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub enum Endpoint {
|
||||
Unspecified,
|
||||
Ident(u16),
|
||||
Udp(IpEndpoint)
|
||||
}
|
||||
|
||||
impl Endpoint {
|
||||
pub fn is_specified(&self) -> bool {
|
||||
match *self {
|
||||
Endpoint::Ident(_) => true,
|
||||
Endpoint::Udp(endpoint) => endpoint.port != 0,
|
||||
Endpoint::Unspecified => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Endpoint {
|
||||
fn default() -> Endpoint { Endpoint::Unspecified }
|
||||
}
|
||||
|
||||
/// A buffered ICMPv4 packet.
|
||||
#[derive(Debug)]
|
||||
pub struct PacketBuffer<'a> {
|
||||
endpoint: IpAddress,
|
||||
size: usize,
|
||||
payload: Managed<'a, [u8]>
|
||||
}
|
||||
|
||||
impl<'a> PacketBuffer<'a> {
|
||||
/// Create a buffered packet.
|
||||
pub fn new<T>(payload: T) -> PacketBuffer<'a>
|
||||
where T: Into<Managed<'a, [u8]>> {
|
||||
PacketBuffer {
|
||||
endpoint: IpAddress::default(),
|
||||
size: 0,
|
||||
payload: payload.into()
|
||||
}
|
||||
}
|
||||
|
||||
fn as_ref<'b>(&'b self) -> &'b [u8] {
|
||||
&self.payload[..self.size]
|
||||
}
|
||||
|
||||
fn as_mut<'b>(&'b mut self) -> &'b mut [u8] {
|
||||
&mut self.payload[..self.size]
|
||||
}
|
||||
|
||||
fn resize<'b>(&'b mut self, size: usize) -> Result<&'b mut Self> {
|
||||
if self.payload.len() >= size {
|
||||
self.size = size;
|
||||
Ok(self)
|
||||
} else {
|
||||
Err(Error::Truncated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Resettable for PacketBuffer<'a> {
|
||||
fn reset(&mut self) {
|
||||
self.size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// An ICMPv4 packet ring buffer.
|
||||
pub type SocketBuffer<'a, 'b: 'a> = RingBuffer<'a, PacketBuffer<'b>>;
|
||||
|
||||
/// An ICMPv4 socket
|
||||
///
|
||||
/// An ICMPv4 socket is bound to a specific [IcmpEndpoint] which may
|
||||
/// be a sepecific UDP port to listen for ICMP error messages related
|
||||
/// to the port or a specific ICMP identifier value. See [bind] for
|
||||
/// more details.
|
||||
///
|
||||
/// [IcmpEndpoint]: enum.IcmpEndpoint.html
|
||||
/// [bind]: #method.bind
|
||||
#[derive(Debug)]
|
||||
pub struct IcmpSocket<'a, 'b: 'a> {
|
||||
handle: SocketHandle,
|
||||
rx_buffer: SocketBuffer<'a, 'b>,
|
||||
tx_buffer: SocketBuffer<'a, 'b>,
|
||||
/// The endpoint this socket is communicating with
|
||||
endpoint: Endpoint,
|
||||
/// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
|
||||
ttl: Option<u8>
|
||||
}
|
||||
|
||||
impl<'a, 'b> IcmpSocket<'a, 'b> {
|
||||
/// Create an ICMPv4 socket with the given buffers.
|
||||
pub fn new(rx_buffer: SocketBuffer<'a, 'b>, tx_buffer: SocketBuffer<'a, 'b>) -> Socket<'a, 'b> {
|
||||
Socket::Icmp(IcmpSocket {
|
||||
handle: SocketHandle::EMPTY,
|
||||
rx_buffer: rx_buffer,
|
||||
tx_buffer: tx_buffer,
|
||||
endpoint: Endpoint::default(),
|
||||
ttl: None
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the socket handle.
|
||||
#[inline]
|
||||
pub fn handle(&self) -> SocketHandle {
|
||||
self.handle
|
||||
}
|
||||
|
||||
/// Set the socket handle.
|
||||
pub(in super) fn set_handle(&mut self, handle: SocketHandle) {
|
||||
self.handle = handle;
|
||||
}
|
||||
|
||||
/// Return the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
|
||||
///
|
||||
/// See also the [set_ttl](#method.set_ttl) method
|
||||
pub fn ttl(&self) -> Option<u8> {
|
||||
self.ttl
|
||||
}
|
||||
|
||||
/// Set the time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
|
||||
///
|
||||
/// A socket without an explicitly set TTL value uses the default [IANA recommended]
|
||||
/// value (64).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if a TTL value of 0 is given. See [RFC 1122 § 3.2.1.7].
|
||||
///
|
||||
/// [IANA recommended]: https://www.iana.org/assignments/ip-parameters/ip-parameters.xhtml
|
||||
/// [RFC 1122 § 3.2.1.7]: https://tools.ietf.org/html/rfc1122#section-3.2.1.7
|
||||
pub fn set_ttl(&mut self, ttl: Option<u8>) {
|
||||
// A host MUST NOT send a datagram with a Time-to-Live (TTL) value of 0
|
||||
if let Some(0) = ttl {
|
||||
panic!("the time-to-live value of a packet must not be zero")
|
||||
}
|
||||
|
||||
self.ttl = ttl
|
||||
}
|
||||
|
||||
/// Bind the socket to the given endpoint.
|
||||
///
|
||||
/// This function returns `Err(Error::Illegal)` if the socket was open
|
||||
/// (see [is_open](#method.is_open)), and `Err(Error::Unaddressable)`
|
||||
/// if `endpoint` is unspecified (see [is_specified]).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Bind to ICMP Error messages associated with a specific UDP port:
|
||||
///
|
||||
/// To [recv] ICMP error messages that are associated with a specific local
|
||||
/// UDP port, the socket may be bound to a given port using [IcmpEndpoint::Udp].
|
||||
/// This may be useful for applications using UDP attempting to detect and/or
|
||||
/// diagnose connection problems.
|
||||
///
|
||||
/// ```
|
||||
/// # use smoltcp::socket::{IcmpPacketBuffer, IcmpSocketBuffer};
|
||||
/// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 20])]);
|
||||
/// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 20])]);
|
||||
/// use smoltcp::wire::IpEndpoint;
|
||||
/// use smoltcp::socket::{Socket, IcmpSocket, IcmpEndpoint};
|
||||
/// let mut icmp_socket = match IcmpSocket::new(rx_buffer, tx_buffer) {
|
||||
/// Socket::Icmp(socket) => socket,
|
||||
/// _ => unreachable!()
|
||||
/// };
|
||||
/// // Bind to ICMP error responses for UDP packets sent from port 53.
|
||||
/// let endpoint = IpEndpoint::from(53);
|
||||
/// icmp_socket.bind(IcmpEndpoint::Udp(endpoint)).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// ## Bind to a specific ICMP identifier:
|
||||
///
|
||||
/// To [send] and [recv] ICMP packets that are not associated with a specific UDP
|
||||
/// port, the socket may be bound to a specific ICMP identifier using
|
||||
/// [IcmpEndpoint::Ident]. This is useful for sending and receiving Echo Request/Reply
|
||||
/// messages.
|
||||
///
|
||||
/// ```
|
||||
/// # use smoltcp::socket::{IcmpPacketBuffer, IcmpSocketBuffer};
|
||||
/// # let rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 20])]);
|
||||
/// # let tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketBuffer::new(vec![0; 20])]);
|
||||
/// use smoltcp::socket::{Socket, IcmpSocket, IcmpEndpoint};
|
||||
/// let mut icmp_socket = match IcmpSocket::new(rx_buffer, tx_buffer) {
|
||||
/// Socket::Icmp(socket) => socket,
|
||||
/// _ => unreachable!()
|
||||
/// };
|
||||
/// // Bind to ICMP messages with the identifier 0x1234
|
||||
/// icmp_socket.bind(IcmpEndpoint::Ident(0x1234)).unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// [is_specified]: enum.IcmpEndpoint.html#method.is_specified
|
||||
/// [IcmpEndpoint::Ident]: enum.IcmpEndpoint#variant.Ident
|
||||
/// [IcmpEndpoint::Udp]: enum.IcmpEndpoint#variant.Udp
|
||||
/// [send]: #method.send
|
||||
/// [recv]: #method.recv
|
||||
pub fn bind<T: Into<Endpoint>>(&mut self, endpoint: T) -> Result<()> {
|
||||
let endpoint = endpoint.into();
|
||||
if !endpoint.is_specified() {
|
||||
return Err(Error::Unaddressable);
|
||||
}
|
||||
|
||||
if self.is_open() { return Err(Error::Illegal) }
|
||||
|
||||
self.endpoint = endpoint;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check whether the transmit buffer is full.
|
||||
#[inline]
|
||||
pub fn can_send(&self) -> bool {
|
||||
!self.tx_buffer.is_full()
|
||||
}
|
||||
|
||||
/// Check whether the receive buffer is not empty.
|
||||
#[inline]
|
||||
pub fn can_recv(&self) -> bool {
|
||||
!self.rx_buffer.is_empty()
|
||||
}
|
||||
|
||||
/// Check whether the socket is open.
|
||||
#[inline]
|
||||
pub fn is_open(&self) -> bool {
|
||||
self.endpoint != Endpoint::Unspecified
|
||||
}
|
||||
|
||||
/// Enqueue a packet to be sent to a given remote address, and return a pointer
|
||||
/// to its payload.
|
||||
///
|
||||
/// This function returns `Err(Error::Exhausted)` if the transmit buffer is full,
|
||||
/// `Err(Error::Truncated)` if the requested size is larger than the packet buffer
|
||||
/// size, and `Err(Error::Unaddressable)` if the or remote address, is unspecified.
|
||||
pub fn send(&mut self, size: usize, endpoint: IpAddress) -> Result<&mut [u8]> {
|
||||
if endpoint.is_unspecified() {
|
||||
return Err(Error::Unaddressable)
|
||||
}
|
||||
|
||||
let packet_buf = self.tx_buffer.enqueue_one_with(|buf| buf.resize(size))?;
|
||||
packet_buf.endpoint = endpoint;
|
||||
net_trace!("{}:{}: buffer to send {} octets",
|
||||
self.handle, packet_buf.endpoint, size);
|
||||
Ok(&mut packet_buf.as_mut()[..size])
|
||||
}
|
||||
|
||||
/// Enqueue a packet to be sent to a given remote address, and fill it from a slice.
|
||||
///
|
||||
/// See also [send](#method.send).
|
||||
pub fn send_slice(&mut self, data: &[u8], endpoint: IpAddress) -> Result<()> {
|
||||
let packet_buf = self.send(data.len(), endpoint)?;
|
||||
packet_buf.copy_from_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Dequeue a packet received from a remote endpoint, and return the `IpAddress` as well
|
||||
/// as a pointer to the payload.
|
||||
///
|
||||
/// This function returns `Err(Error::Exhausted)` if the receive buffer is empty.
|
||||
pub fn recv(&mut self) -> Result<(&[u8], IpAddress)> {
|
||||
let packet_buf = self.rx_buffer.dequeue_one()?;
|
||||
net_trace!("{}:{}: receive {} buffered octets",
|
||||
self.handle, packet_buf.endpoint, packet_buf.size);
|
||||
Ok((&packet_buf.as_ref(), packet_buf.endpoint))
|
||||
}
|
||||
|
||||
/// Dequeue a packet received from a remote endpoint, copy the payload into the given slice,
|
||||
/// and return the amount of octets copied as well as the `IpAddress`
|
||||
///
|
||||
/// See also [recv](#method.recv).
|
||||
pub fn recv_slice(&mut self, data: &mut [u8]) -> Result<(usize, IpAddress)> {
|
||||
let (buffer, endpoint) = self.recv()?;
|
||||
let length = cmp::min(data.len(), buffer.len());
|
||||
data[..length].copy_from_slice(&buffer[..length]);
|
||||
Ok((length, endpoint))
|
||||
}
|
||||
|
||||
/// Filter determining which packets received by the interface are appended to
|
||||
/// the given sockets received buffer.
|
||||
pub(crate) fn accepts(&self, ip_repr: &IpRepr, icmp_repr: &Icmpv4Repr,
|
||||
cksum: &ChecksumCapabilities) -> bool {
|
||||
match self.endpoint {
|
||||
// If we are bound to ICMP errors associated to a UDP port, only
|
||||
// accept Destination Unreachable messages with the data containing
|
||||
// a UDP packet send from the local port we are bound to.
|
||||
Endpoint::Udp(endpoint) =>
|
||||
if !endpoint.addr.is_unspecified() && endpoint.addr != ip_repr.dst_addr() {
|
||||
false
|
||||
} else {
|
||||
match icmp_repr {
|
||||
&Icmpv4Repr::DstUnreachable { data, .. } => {
|
||||
let packet = UdpPacket::new(data);
|
||||
match UdpRepr::parse(&packet, &ip_repr.src_addr(),
|
||||
&ip_repr.dst_addr(), cksum) {
|
||||
Ok(repr) => endpoint.port == repr.src_port,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
// If we are bound to a specific ICMP identifier value, only accept an
|
||||
// Echo Request/Reply with the identifier field matching the endpoint
|
||||
// port.
|
||||
Endpoint::Ident(id) => match icmp_repr {
|
||||
&Icmpv4Repr::EchoRequest { ident, .. } | &Icmpv4Repr::EchoReply { ident, .. } =>
|
||||
ident == id,
|
||||
_ => false,
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn process(&mut self, ip_repr: &IpRepr, ip_payload: &[u8]) -> Result<()> {
|
||||
let packet_buf = self.rx_buffer.enqueue_one_with(|buf| buf.resize(ip_payload.len()))?;
|
||||
packet_buf.as_mut().copy_from_slice(ip_payload);
|
||||
packet_buf.endpoint = ip_repr.src_addr();
|
||||
net_trace!("{}:{}: receiving {} octets",
|
||||
self.handle, packet_buf.endpoint, packet_buf.size);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch<F>(&mut self, caps: &DeviceCapabilities,
|
||||
emit: F) -> Result<()>
|
||||
where F: FnOnce((IpRepr, Icmpv4Repr)) -> Result<()> {
|
||||
let handle = self.handle;
|
||||
let ttl = self.ttl.unwrap_or(64);
|
||||
let checksum = &caps.checksum;
|
||||
self.tx_buffer.dequeue_one_with(|packet_buf| {
|
||||
net_trace!("{}:{}: sending {} octets",
|
||||
handle, packet_buf.endpoint, packet_buf.size);
|
||||
match packet_buf.endpoint {
|
||||
IpAddress::Ipv4(ipv4_addr) => {
|
||||
let packet = Icmpv4Packet::new(packet_buf.as_ref());
|
||||
let repr = Icmpv4Repr::parse(&packet, checksum)?;
|
||||
let ip_repr = IpRepr::Ipv4(Ipv4Repr {
|
||||
src_addr: Ipv4Address::default(),
|
||||
dst_addr: ipv4_addr,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: repr.buffer_len(),
|
||||
ttl: ttl,
|
||||
});
|
||||
emit((ip_repr, repr))
|
||||
},
|
||||
_ => Err(Error::Unaddressable)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn poll_at(&self) -> Option<u64> {
|
||||
if self.tx_buffer.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use phy::DeviceCapabilities;
|
||||
use wire::{IpAddress, Icmpv4DstUnreachable};
|
||||
use super::*;
|
||||
|
||||
fn buffer(packets: usize) -> SocketBuffer<'static, 'static> {
|
||||
let mut storage = vec![];
|
||||
for _ in 0..packets {
|
||||
storage.push(PacketBuffer::new(vec![0; 24]))
|
||||
}
|
||||
SocketBuffer::new(storage)
|
||||
}
|
||||
|
||||
fn socket(rx_buffer: SocketBuffer<'static, 'static>,
|
||||
tx_buffer: SocketBuffer<'static, 'static>) -> IcmpSocket<'static, 'static> {
|
||||
match IcmpSocket::new(rx_buffer, tx_buffer) {
|
||||
Socket::Icmp(socket) => socket,
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
const REMOTE_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x02]);
|
||||
const LOCAL_IPV4: Ipv4Address = Ipv4Address([0x7f, 0x00, 0x00, 0x01]);
|
||||
const REMOTE_IP: IpAddress = IpAddress::Ipv4(REMOTE_IPV4);
|
||||
const LOCAL_IP: IpAddress = IpAddress::Ipv4(LOCAL_IPV4);
|
||||
const LOCAL_PORT: u16 = 53;
|
||||
const LOCAL_END: IpEndpoint = IpEndpoint { addr: LOCAL_IP, port: LOCAL_PORT };
|
||||
|
||||
static ECHO_REPR: Icmpv4Repr = Icmpv4Repr::EchoRequest {
|
||||
ident: 0x1234,
|
||||
seq_no: 0x5678,
|
||||
data: &[0xff; 16]
|
||||
};
|
||||
|
||||
static UDP_REPR: UdpRepr = UdpRepr {
|
||||
src_port: 53,
|
||||
dst_port: 9090,
|
||||
payload: &[0xff; 10]
|
||||
};
|
||||
|
||||
static LOCAL_IP_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
|
||||
src_addr: Ipv4Address::UNSPECIFIED,
|
||||
dst_addr: REMOTE_IPV4,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: 24,
|
||||
ttl: 0x40
|
||||
});
|
||||
|
||||
static REMOTE_IP_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
|
||||
src_addr: REMOTE_IPV4,
|
||||
dst_addr: LOCAL_IPV4,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: 24,
|
||||
ttl: 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", REMOTE_IP), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_dispatch() {
|
||||
let mut socket = socket(buffer(0), buffer(1));
|
||||
let caps = DeviceCapabilities::default();
|
||||
|
||||
assert_eq!(socket.dispatch(&caps, |_| unreachable!()),
|
||||
Err(Error::Exhausted));
|
||||
|
||||
// This buffer is too long
|
||||
assert_eq!(socket.send_slice(&[0xff; 25], REMOTE_IP), Err(Error::Truncated));
|
||||
assert!(socket.can_send());
|
||||
|
||||
let mut bytes = [0xff; 24];
|
||||
let mut packet = Icmpv4Packet::new(&mut bytes);
|
||||
ECHO_REPR.emit(&mut packet, &caps.checksum);
|
||||
|
||||
assert_eq!(socket.send_slice(&packet.into_inner()[..], REMOTE_IP), Ok(()));
|
||||
assert_eq!(socket.send_slice(b"123456", REMOTE_IP), Err(Error::Exhausted));
|
||||
assert!(!socket.can_send());
|
||||
|
||||
assert_eq!(socket.dispatch(&caps, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(ip_repr, LOCAL_IP_REPR);
|
||||
assert_eq!(icmp_repr, ECHO_REPR);
|
||||
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(&caps, |(ip_repr, icmp_repr)| {
|
||||
assert_eq!(ip_repr, LOCAL_IP_REPR);
|
||||
assert_eq!(icmp_repr, ECHO_REPR);
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
// buffer is taken off of the queue this time
|
||||
assert!(socket.can_send());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_ttl() {
|
||||
let mut s = socket(buffer(0), buffer(1));
|
||||
let caps = DeviceCapabilities::default();
|
||||
|
||||
let mut bytes = [0xff; 24];
|
||||
let mut packet = Icmpv4Packet::new(&mut bytes);
|
||||
ECHO_REPR.emit(&mut packet, &caps.checksum);
|
||||
|
||||
s.set_ttl(Some(0x2a));
|
||||
|
||||
assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IP), Ok(()));
|
||||
assert_eq!(s.dispatch(&caps, |(ip_repr, _)| {
|
||||
assert_eq!(ip_repr, IpRepr::Ipv4(Ipv4Repr {
|
||||
src_addr: Ipv4Address::UNSPECIFIED,
|
||||
dst_addr: REMOTE_IPV4,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: ECHO_REPR.buffer_len(),
|
||||
ttl: 0x2a,
|
||||
}));
|
||||
Ok(())
|
||||
}), Ok(()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recv_process() {
|
||||
let mut socket = socket(buffer(1), buffer(1));
|
||||
assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(()));
|
||||
|
||||
assert!(!socket.can_recv());
|
||||
assert_eq!(socket.recv(), Err(Error::Exhausted));
|
||||
|
||||
let caps = DeviceCapabilities::default();
|
||||
|
||||
let mut bytes = [0xff; 20];
|
||||
let mut packet = Icmpv4Packet::new(&mut bytes);
|
||||
ECHO_REPR.emit(&mut packet, &caps.checksum);
|
||||
let data = &packet.into_inner()[..];
|
||||
|
||||
assert!(socket.accepts(&REMOTE_IP_REPR, &ECHO_REPR, &caps.checksum));
|
||||
assert_eq!(socket.process(&REMOTE_IP_REPR, &data[..]),
|
||||
Ok(()));
|
||||
assert!(socket.can_recv());
|
||||
|
||||
assert!(socket.accepts(&REMOTE_IP_REPR, &ECHO_REPR, &caps.checksum));
|
||||
assert_eq!(socket.process(&REMOTE_IP_REPR, &data[..]),
|
||||
Err(Error::Exhausted));
|
||||
assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IP)));
|
||||
assert!(!socket.can_recv());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_accept_bad_id() {
|
||||
let mut socket = socket(buffer(1), buffer(1));
|
||||
assert_eq!(socket.bind(Endpoint::Ident(0x1234)), Ok(()));
|
||||
|
||||
let caps = DeviceCapabilities::default();
|
||||
let mut bytes = [0xff; 20];
|
||||
let mut packet = Icmpv4Packet::new(&mut bytes);
|
||||
let icmp_repr = Icmpv4Repr::EchoRequest {
|
||||
ident: 0x4321,
|
||||
seq_no: 0x5678,
|
||||
data: &[0xff; 16]
|
||||
};
|
||||
icmp_repr.emit(&mut packet, &caps.checksum);
|
||||
|
||||
// Ensure that a packet with an identifier that isn't the bound
|
||||
// ID is not accepted
|
||||
assert!(!socket.accepts(&REMOTE_IP_REPR, &icmp_repr, &caps.checksum));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_accepts_udp() {
|
||||
let mut socket = socket(buffer(1), buffer(1));
|
||||
assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END)), Ok(()));
|
||||
|
||||
let caps = DeviceCapabilities::default();
|
||||
|
||||
let mut bytes = [0xff; 18];
|
||||
let mut packet = UdpPacket::new(&mut bytes);
|
||||
UDP_REPR.emit(&mut packet, &REMOTE_IP, &LOCAL_IP, &caps.checksum);
|
||||
|
||||
let data = &packet.into_inner()[..];
|
||||
|
||||
let icmp_repr = Icmpv4Repr::DstUnreachable {
|
||||
reason: Icmpv4DstUnreachable::PortUnreachable,
|
||||
header: Ipv4Repr {
|
||||
src_addr: LOCAL_IPV4,
|
||||
dst_addr: REMOTE_IPV4,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: 12,
|
||||
ttl: 0x40
|
||||
},
|
||||
data: data
|
||||
};
|
||||
let ip_repr = IpRepr::Unspecified {
|
||||
src_addr: REMOTE_IP,
|
||||
dst_addr: LOCAL_IP,
|
||||
protocol: IpProtocol::Icmp,
|
||||
payload_len: icmp_repr.buffer_len(),
|
||||
ttl: 0x40
|
||||
};
|
||||
|
||||
assert!(!socket.can_recv());
|
||||
|
||||
// Ensure we can accept ICMP error response to the bound
|
||||
// UDP port
|
||||
assert!(socket.accepts(&ip_repr, &icmp_repr, &caps.checksum));
|
||||
assert_eq!(socket.process(&ip_repr, &data[..]),
|
||||
Ok(()));
|
||||
assert!(socket.can_recv());
|
||||
}
|
||||
}
|
|
@ -11,10 +11,11 @@
|
|||
//! size for a buffer, allocate it, and let the networking stack use it.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use wire::IpRepr;
|
||||
|
||||
#[cfg(feature = "socket-raw")]
|
||||
mod raw;
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
mod icmp;
|
||||
#[cfg(feature = "socket-udp")]
|
||||
mod udp;
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
|
@ -27,6 +28,12 @@ pub use self::raw::{PacketBuffer as RawPacketBuffer,
|
|||
SocketBuffer as RawSocketBuffer,
|
||||
RawSocket};
|
||||
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
pub use self::icmp::{PacketBuffer as IcmpPacketBuffer,
|
||||
SocketBuffer as IcmpSocketBuffer,
|
||||
Endpoint as IcmpEndpoint,
|
||||
IcmpSocket};
|
||||
|
||||
#[cfg(feature = "socket-udp")]
|
||||
pub use self::udp::{PacketBuffer as UdpPacketBuffer,
|
||||
SocketBuffer as UdpSocketBuffer,
|
||||
|
@ -57,6 +64,8 @@ pub(crate) use self::ref_::Session as SocketSession;
|
|||
pub enum Socket<'a, 'b: 'a> {
|
||||
#[cfg(feature = "socket-raw")]
|
||||
Raw(RawSocket<'a, 'b>),
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
Icmp(IcmpSocket<'a, 'b>),
|
||||
#[cfg(feature = "socket-udp")]
|
||||
Udp(UdpSocket<'a, 'b>),
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
|
@ -70,6 +79,8 @@ macro_rules! dispatch_socket {
|
|||
match $self_ {
|
||||
#[cfg(feature = "socket-raw")]
|
||||
&$( $mut_ )* Socket::Raw(ref $( $mut_ )* $socket) => $code,
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
&$( $mut_ )* Socket::Icmp(ref $( $mut_ )* $socket) => $code,
|
||||
#[cfg(feature = "socket-udp")]
|
||||
&$( $mut_ )* Socket::Udp(ref $( $mut_ )* $socket) => $code,
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
|
@ -122,6 +133,8 @@ macro_rules! from_socket {
|
|||
|
||||
#[cfg(feature = "socket-raw")]
|
||||
from_socket!(RawSocket<'a, 'b>, Raw);
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
from_socket!(IcmpSocket<'a, 'b>, Icmp);
|
||||
#[cfg(feature = "socket-udp")]
|
||||
from_socket!(UdpSocket<'a, 'b>, Udp);
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
|
|
|
@ -3,8 +3,8 @@ use managed::Managed;
|
|||
|
||||
use {Error, Result};
|
||||
use phy::ChecksumCapabilities;
|
||||
use wire::{IpVersion, IpProtocol, Ipv4Repr, Ipv4Packet};
|
||||
use socket::{IpRepr, Socket, SocketHandle};
|
||||
use wire::{IpVersion, IpRepr, IpProtocol, Ipv4Repr, Ipv4Packet};
|
||||
use socket::{Socket, SocketHandle};
|
||||
use storage::{Resettable, RingBuffer};
|
||||
|
||||
/// A buffered raw IP packet.
|
||||
|
|
|
@ -2,6 +2,8 @@ use core::ops::{Deref, DerefMut};
|
|||
|
||||
#[cfg(feature = "socket-raw")]
|
||||
use socket::RawSocket;
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
use socket::IcmpSocket;
|
||||
#[cfg(feature = "socket-udp")]
|
||||
use socket::UdpSocket;
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
|
@ -19,6 +21,8 @@ pub trait Session {
|
|||
|
||||
#[cfg(feature = "socket-raw")]
|
||||
impl<'a, 'b> Session for RawSocket<'a, 'b> {}
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
impl<'a, 'b> Session for IcmpSocket<'a, 'b> {}
|
||||
#[cfg(feature = "socket-udp")]
|
||||
impl<'a, 'b> Session for UdpSocket<'a, 'b> {}
|
||||
#[cfg(feature = "socket-tcp")]
|
||||
|
|
|
@ -144,6 +144,9 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> {
|
|||
#[cfg(feature = "socket-raw")]
|
||||
&mut Socket::Raw(_) =>
|
||||
may_remove = true,
|
||||
#[cfg(feature = "socket-icmp")]
|
||||
&mut Socket::Icmp(_) =>
|
||||
may_remove = true,
|
||||
#[cfg(feature = "socket-udp")]
|
||||
&mut Socket::Udp(_) =>
|
||||
may_remove = true,
|
||||
|
|
|
@ -6,8 +6,8 @@ use core::{cmp, fmt};
|
|||
|
||||
use {Error, Result};
|
||||
use phy::DeviceCapabilities;
|
||||
use wire::{IpProtocol, IpAddress, IpEndpoint, TcpSeqNumber, TcpRepr, TcpControl};
|
||||
use socket::{Socket, SocketHandle, IpRepr};
|
||||
use wire::{IpProtocol, IpRepr, IpAddress, IpEndpoint, TcpSeqNumber, TcpRepr, TcpControl};
|
||||
use socket::{Socket, SocketHandle};
|
||||
use storage::{Assembler, RingBuffer};
|
||||
|
||||
pub type SocketBuffer<'a> = RingBuffer<'a, u8>;
|
||||
|
|
|
@ -2,8 +2,8 @@ use core::cmp::min;
|
|||
use managed::Managed;
|
||||
|
||||
use {Error, Result};
|
||||
use wire::{IpProtocol, IpEndpoint, UdpRepr};
|
||||
use socket::{Socket, SocketHandle, IpRepr};
|
||||
use wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr};
|
||||
use socket::{Socket, SocketHandle};
|
||||
use storage::{Resettable, RingBuffer};
|
||||
|
||||
/// A buffered UDP packet.
|
||||
|
|
Loading…
Reference in New Issue