Add support for IPv6 to ICMP sockets.

Closes: #205
Approved by: whitequark
This commit is contained in:
Valentin Lorentz 2018-05-09 15:27:51 +02:00 committed by Homu
parent 435292cc35
commit 2afc538fd9
7 changed files with 384 additions and 75 deletions

View File

@ -20,6 +20,8 @@ use wire::{ArpPacket, ArpRepr, ArpOperation};
use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable};
#[cfg(feature = "proto-ipv6")]
use wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem};
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
use wire::IcmpRepr;
#[cfg(feature = "proto-ipv6")]
use wire::{NdiscNeighborFlags, NdiscRepr};
#[cfg(all(feature = "proto-ipv6", feature = "socket-udp"))]
@ -32,7 +34,7 @@ use wire::{TcpPacket, TcpRepr, TcpControl};
use socket::{Socket, SocketSet, AnySocket};
#[cfg(feature = "socket-raw")]
use socket::RawSocket;
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
use socket::IcmpSocket;
#[cfg(feature = "socket-udp")]
use socket::UdpSocket;
@ -424,13 +426,16 @@ impl<'b, 'c, DeviceT> Interface<'b, 'c, DeviceT>
Socket::Raw(ref mut socket) =>
socket.dispatch(&caps.checksum, |response|
respond!(Packet::Raw(response))),
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
Socket::Icmp(ref mut socket) =>
socket.dispatch(&caps, |response| {
match response {
#[cfg(feature = "proto-ipv4")]
(IpRepr::Ipv4(ipv4_repr), icmpv4_repr) =>
(IpRepr::Ipv4(ipv4_repr), IcmpRepr::Ipv4(icmpv4_repr)) =>
respond!(Packet::Icmpv4((ipv4_repr, icmpv4_repr))),
#[cfg(feature = "proto-ipv6")]
(IpRepr::Ipv6(ipv6_repr), IcmpRepr::Ipv6(icmpv6_repr)) =>
respond!(Packet::Icmpv6((ipv6_repr, icmpv6_repr))),
_ => Err(Error::Unaddressable)
}
}),
@ -746,6 +751,23 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
let icmp_repr = Icmpv6Repr::parse(&ip_repr.src_addr(), &ip_repr.dst_addr(),
&icmp_packet, &checksum_caps)?;
#[cfg(feature = "socket-icmp")]
let mut handled_by_icmp_socket = false;
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv6"))]
for mut icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) {
if !icmp_socket.accepts(&ip_repr, &icmp_repr.into(), &checksum_caps) { continue }
match icmp_socket.process(&ip_repr, &icmp_repr.into(), &checksum_caps) {
// The packet is valid and handled by socket.
Ok(()) => handled_by_icmp_socket = true,
// The socket buffer is full.
Err(Error::Exhausted) => (),
// ICMP sockets don't validate the packets in any way.
Err(_) => unreachable!(),
}
}
match icmp_repr {
// Respond to echo requests.
Icmpv6Repr::EchoRequest { ident, seq_no, data } => {
@ -771,6 +793,11 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
_ => Ok(Packet::None)
},
// Don't report an error if a packet with unknown type
// has been handled by an ICMP socket
#[cfg(feature = "socket-icmp")]
_ if handled_by_icmp_socket => Ok(Packet::None),
// FIXME: do something correct here?
_ => Err(Error::Unrecognized),
}
@ -839,9 +866,9 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
for mut icmp_socket in _sockets.iter_mut().filter_map(IcmpSocket::downcast) {
if !icmp_socket.accepts(&ip_repr, &icmp_repr, &checksum_caps) { continue }
if !icmp_socket.accepts(&ip_repr, &icmp_repr.into(), &checksum_caps) { continue }
match icmp_socket.process(&ip_repr, &icmp_repr, &checksum_caps) {
match icmp_socket.process(&ip_repr, &icmp_repr.into(), &checksum_caps) {
// The packet is valid and handled by socket.
Ok(()) => handled_by_icmp_socket = true,
// The socket buffer is full.
@ -853,6 +880,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
match icmp_repr {
// Respond to echo requests.
#[cfg(feature = "proto-ipv4")]
Icmpv4Repr::EchoRequest { ident, seq_no, data } => {
let icmp_reply_repr = Icmpv4Repr::EchoReply {
ident: ident,
@ -863,7 +891,7 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
IpRepr::Ipv4(ipv4_repr) => Ok(self.icmpv4_reply(ipv4_repr, icmp_reply_repr)),
_ => Err(Error::Unrecognized),
}
}
},
// Ignore any echo replies.
Icmpv4Repr::EchoReply { .. } => Ok(Packet::None),

View File

@ -6,8 +6,12 @@ use socket::{Socket, SocketMeta, SocketHandle};
use storage::{PacketBuffer, PacketMetadata};
use time::Instant;
use wire::{IpAddress, IpEndpoint, IpProtocol, IpRepr};
use wire::{Ipv4Address, Ipv4Repr};
use wire::{Icmpv4Packet, Icmpv4Repr};
#[cfg(feature = "proto-ipv4")]
use wire::{Ipv4Address, Ipv4Repr, Icmpv4Packet, Icmpv4Repr};
#[cfg(feature = "proto-ipv6")]
use wire::{Ipv6Address, Ipv6Repr, Icmpv6Packet, Icmpv6Repr};
use wire::IcmpRepr;
use wire::{UdpPacket, UdpRepr};
/// Type of endpoint to bind the ICMP socket to. See [IcmpSocket::bind] for
@ -244,13 +248,23 @@ impl<'a, 'b> IcmpSocket<'a, 'b> {
/// 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,
pub(crate) fn accepts(&self, ip_repr: &IpRepr, icmp_repr: &IcmpRepr,
cksum: &ChecksumCapabilities) -> bool {
match (&self.endpoint, icmp_repr) {
// 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), &Icmpv4Repr::DstUnreachable { data, .. })
#[cfg(feature = "proto-ipv4")]
(&Endpoint::Udp(endpoint), &IcmpRepr::Ipv4(Icmpv4Repr::DstUnreachable { data, .. }))
if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
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,
}
}
#[cfg(feature = "proto-ipv6")]
(&Endpoint::Udp(endpoint), &IcmpRepr::Ipv6(Icmpv6Repr::DstUnreachable { data, .. }))
if endpoint.addr.is_unspecified() || endpoint.addr == ip_repr.dst_addr() => {
let packet = UdpPacket::new(data);
match UdpRepr::parse(&packet, &ip_repr.src_addr(), &ip_repr.dst_addr(), cksum) {
@ -261,25 +275,44 @@ impl<'a, 'b> IcmpSocket<'a, 'b> {
// 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(bound_ident), &Icmpv4Repr::EchoRequest { ident, .. }) |
(&Endpoint::Ident(bound_ident), &Icmpv4Repr::EchoReply { ident, .. }) =>
#[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,
#[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,
_ => false,
}
}
pub(crate) fn process(&mut self, ip_repr: &IpRepr, icmp_repr: &Icmpv4Repr,
pub(crate) fn process(&mut self, ip_repr: &IpRepr, icmp_repr: &IcmpRepr,
_cksum: &ChecksumCapabilities) -> Result<()> {
let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?;
icmp_repr.emit(&mut Icmpv4Packet::new(packet_buf), &ChecksumCapabilities::default());
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(packet_buf), &ChecksumCapabilities::default());
net_trace!("{}:{}: receiving {} octets",
self.meta.handle, icmp_repr.buffer_len(), packet_buf.len());
net_trace!("{}:{}: receiving {} octets",
self.meta.handle, icmp_repr.buffer_len(), packet_buf.len());
},
#[cfg(feature = "proto-ipv6")]
&IcmpRepr::Ipv6(ref icmp_repr) => {
let packet_buf = self.rx_buffer.enqueue(icmp_repr.buffer_len(), ip_repr.src_addr())?;
icmp_repr.emit(&ip_repr.src_addr(), &ip_repr.dst_addr(),
&mut Icmpv6Packet::new(packet_buf), &ChecksumCapabilities::default());
net_trace!("{}:{}: receiving {} octets",
self.meta.handle, icmp_repr.buffer_len(), packet_buf.len());
},
}
Ok(())
}
pub(crate) fn dispatch<F>(&mut self, _caps: &DeviceCapabilities, emit: F) -> Result<()>
where F: FnOnce((IpRepr, Icmpv4Repr)) -> Result<()>
where F: FnOnce((IpRepr, IcmpRepr)) -> Result<()>
{
let handle = self.meta.handle;
let hop_limit = self.hop_limit.unwrap_or(64);
@ -287,9 +320,10 @@ impl<'a, 'b> IcmpSocket<'a, 'b> {
net_trace!("{}:{}: sending {} octets",
handle, remote_endpoint, packet_buf.len());
match *remote_endpoint {
#[cfg(feature = "proto-ipv4")]
IpAddress::Ipv4(ipv4_addr) => {
let packet = Icmpv4Packet::new(&*packet_buf);
let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::default())?;
let repr = Icmpv4Repr::parse(&packet, &ChecksumCapabilities::ignored())?;
let ip_repr = IpRepr::Ipv4(Ipv4Repr {
src_addr: Ipv4Address::default(),
dst_addr: ipv4_addr,
@ -297,7 +331,21 @@ impl<'a, 'b> IcmpSocket<'a, 'b> {
payload_len: repr.buffer_len(),
hop_limit: hop_limit,
});
emit((ip_repr, repr))
emit((ip_repr, IcmpRepr::Ipv4(repr)))
},
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(ipv6_addr) => {
let packet = Icmpv6Packet::new(&*packet_buf);
let src_addr = Ipv6Address::default();
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,
next_header: IpProtocol::Icmp,
payload_len: repr.buffer_len(),
hop_limit: hop_limit,
});
emit((ip_repr, IcmpRepr::Ipv6(repr)))
},
_ => Err(Error::Unaddressable)
}
@ -320,40 +368,46 @@ impl<'a, 'b> Into<Socket<'a, 'b>> for IcmpSocket<'a, 'b> {
}
#[cfg(test)]
mod test {
use phy::DeviceCapabilities;
use wire::{IpAddress, Icmpv4DstUnreachable};
use super::*;
mod tests_common {
pub use phy::DeviceCapabilities;
pub use wire::IpAddress;
pub use super::*;
fn buffer(packets: usize) -> IcmpSocketBuffer<'static, 'static> {
IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY; packets], vec![0; 46 * packets])
pub fn buffer(packets: usize) -> IcmpSocketBuffer<'static, 'static> {
IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY; packets], vec![0; 66 * packets])
}
fn socket(rx_buffer: IcmpSocketBuffer<'static, 'static>,
pub fn socket(rx_buffer: IcmpSocketBuffer<'static, 'static>,
tx_buffer: IcmpSocketBuffer<'static, 'static>) -> IcmpSocket<'static, 'static> {
IcmpSocket::new(rx_buffer, tx_buffer)
}
pub const LOCAL_PORT: u16 = 53;
pub static UDP_REPR: UdpRepr = UdpRepr {
src_port: 53,
dst_port: 9090,
payload: &[0xff; 10]
};
}
#[cfg(all(test, feature = "proto-ipv4"))]
mod test_ipv4 {
use super::tests_common::*;
use wire::Icmpv4DstUnreachable;
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 };
const LOCAL_END_V4: IpEndpoint = IpEndpoint { addr: IpAddress::Ipv4(LOCAL_IPV4), port: LOCAL_PORT };
static ECHO_REPR: Icmpv4Repr = Icmpv4Repr::EchoRequest {
static ECHOV4_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 {
static LOCAL_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
src_addr: Ipv4Address::UNSPECIFIED,
dst_addr: REMOTE_IPV4,
protocol: IpProtocol::Icmp,
@ -361,7 +415,7 @@ mod test {
hop_limit: 0x40
});
static REMOTE_IP_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
static REMOTE_IPV4_REPR: IpRepr = IpRepr::Ipv4(Ipv4Repr {
src_addr: REMOTE_IPV4,
dst_addr: LOCAL_IPV4,
protocol: IpProtocol::Icmp,
@ -374,7 +428,7 @@ mod test {
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(()));
assert_eq!(socket.send_slice(b"abcdef", REMOTE_IPV4.into()), Ok(()));
}
#[test]
@ -386,28 +440,28 @@ mod test {
Err(Error::Exhausted));
// This buffer is too long
assert_eq!(socket.send_slice(&[0xff; 47], REMOTE_IP), 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(&mut bytes);
ECHO_REPR.emit(&mut packet, &caps.checksum);
ECHOV4_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_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(&caps, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(icmp_repr, ECHO_REPR);
assert_eq!(ip_repr, LOCAL_IPV4_REPR);
assert_eq!(icmp_repr, ECHOV4_REPR.into());
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);
assert_eq!(ip_repr, LOCAL_IPV4_REPR);
assert_eq!(icmp_repr, ECHOV4_REPR.into());
Ok(())
}), Ok(()));
// buffer is taken off of the queue this time
@ -415,23 +469,23 @@ mod test {
}
#[test]
fn test_set_hop_limit() {
fn test_set_hop_limit_v4() {
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);
ECHOV4_REPR.emit(&mut packet, &caps.checksum);
s.set_hop_limit(Some(0x2a));
assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IP), Ok(()));
assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV4.into()), 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(),
payload_len: ECHOV4_REPR.buffer_len(),
hop_limit: 0x2a,
}));
Ok(())
@ -450,19 +504,19 @@ mod test {
let mut bytes = [0xff; 24];
let mut packet = Icmpv4Packet::new(&mut bytes);
ECHO_REPR.emit(&mut packet, &caps.checksum);
ECHOV4_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, &ECHO_REPR, &caps.checksum),
assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum));
assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum),
Ok(()));
assert!(socket.can_recv());
assert!(socket.accepts(&REMOTE_IP_REPR, &ECHO_REPR, &caps.checksum));
assert_eq!(socket.process(&REMOTE_IP_REPR, &ECHO_REPR, &caps.checksum),
assert!(socket.accepts(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum));
assert_eq!(socket.process(&REMOTE_IPV4_REPR, &ECHOV4_REPR.into(), &caps.checksum),
Err(Error::Exhausted));
assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IP)));
assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV4.into())));
assert!(!socket.can_recv());
}
@ -483,19 +537,19 @@ mod test {
// 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));
assert!(!socket.accepts(&REMOTE_IPV4_REPR, &icmp_repr.into(), &caps.checksum));
}
#[test]
fn test_accepts_udp() {
let mut socket = socket(buffer(1), buffer(1));
assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END)), Ok(()));
assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V4)), 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);
UDP_REPR.emit(&mut packet, &REMOTE_IPV4.into(), &LOCAL_IPV4.into(), &caps.checksum);
let data = &packet.into_inner()[..];
@ -511,8 +565,8 @@ mod test {
data: data
};
let ip_repr = IpRepr::Unspecified {
src_addr: REMOTE_IP,
dst_addr: LOCAL_IP,
src_addr: REMOTE_IPV4.into(),
dst_addr: LOCAL_IPV4.into(),
protocol: IpProtocol::Icmp,
payload_len: icmp_repr.buffer_len(),
hop_limit: 0x40
@ -522,15 +576,214 @@ mod test {
// 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, &icmp_repr, &caps.checksum),
assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &caps.checksum));
assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &caps.checksum),
Ok(()));
assert!(socket.can_recv());
let mut bytes = [0x00; 46];
let mut packet = Icmpv4Packet::new(&mut bytes[..]);
icmp_repr.emit(&mut packet, &caps.checksum);
assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IP)));
assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV4.into())));
assert!(!socket.can_recv());
}
}
#[cfg(all(test, feature = "proto-ipv6"))]
mod test_ipv6 {
use super::tests_common::*;
use 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 };
static ECHOV6_REPR: Icmpv6Repr = Icmpv6Repr::EchoRequest {
ident: 0x1234,
seq_no: 0x5678,
data: &[0xff; 16]
};
static LOCAL_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
src_addr: Ipv6Address::UNSPECIFIED,
dst_addr: REMOTE_IPV6,
next_header: IpProtocol::Icmp,
payload_len: 24,
hop_limit: 0x40
});
static REMOTE_IPV6_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
src_addr: REMOTE_IPV6,
dst_addr: LOCAL_IPV6,
next_header: IpProtocol::Icmp,
payload_len: 24,
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", REMOTE_IPV6.into()), 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; 67], REMOTE_IPV6.into()), Err(Error::Truncated));
assert!(socket.can_send());
let mut bytes = vec![0xff; 24];
let mut packet = Icmpv6Packet::new(&mut bytes);
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.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!(!socket.can_send());
assert_eq!(socket.dispatch(&caps, |(ip_repr, icmp_repr)| {
assert_eq!(ip_repr, LOCAL_IPV6_REPR);
assert_eq!(icmp_repr, ECHOV6_REPR.into());
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_IPV6_REPR);
assert_eq!(icmp_repr, ECHOV6_REPR.into());
Ok(())
}), Ok(()));
// buffer is taken off of the queue this time
assert!(socket.can_send());
}
#[test]
fn test_set_hop_limit() {
let mut s = socket(buffer(0), buffer(1));
let caps = DeviceCapabilities::default();
let mut bytes = vec![0xff; 24];
let mut packet = Icmpv6Packet::new(&mut bytes);
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
s.set_hop_limit(Some(0x2a));
assert_eq!(s.send_slice(&packet.into_inner()[..], REMOTE_IPV6.into()), Ok(()));
assert_eq!(s.dispatch(&caps, |(ip_repr, _)| {
assert_eq!(ip_repr, IpRepr::Ipv6(Ipv6Repr {
src_addr: Ipv6Address::UNSPECIFIED,
dst_addr: REMOTE_IPV6,
next_header: IpProtocol::Icmp,
payload_len: ECHOV6_REPR.buffer_len(),
hop_limit: 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; 24];
let mut packet = Icmpv6Packet::new(&mut bytes);
ECHOV6_REPR.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
let data = &packet.into_inner()[..];
assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum));
assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum),
Ok(()));
assert!(socket.can_recv());
assert!(socket.accepts(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum));
assert_eq!(socket.process(&REMOTE_IPV6_REPR, &ECHOV6_REPR.into(), &caps.checksum),
Err(Error::Exhausted));
assert_eq!(socket.recv(), Ok((&data[..], REMOTE_IPV6.into())));
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 = Icmpv6Packet::new(&mut bytes);
let icmp_repr = Icmpv6Repr::EchoRequest {
ident: 0x4321,
seq_no: 0x5678,
data: &[0xff; 16]
};
icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
// Ensure that a packet with an identifier that isn't the bound
// ID is not accepted
assert!(!socket.accepts(&REMOTE_IPV6_REPR, &icmp_repr.into(), &caps.checksum));
}
#[test]
fn test_accepts_udp() {
let mut socket = socket(buffer(1), buffer(1));
assert_eq!(socket.bind(Endpoint::Udp(LOCAL_END_V6)), Ok(()));
let caps = DeviceCapabilities::default();
let mut bytes = [0xff; 18];
let mut packet = UdpPacket::new(&mut bytes);
UDP_REPR.emit(&mut packet, &REMOTE_IPV6.into(), &LOCAL_IPV6.into(), &caps.checksum);
let data = &packet.into_inner()[..];
let icmp_repr = Icmpv6Repr::DstUnreachable {
reason: Icmpv6DstUnreachable::PortUnreachable,
header: Ipv6Repr {
src_addr: LOCAL_IPV6,
dst_addr: REMOTE_IPV6,
next_header: IpProtocol::Icmp,
payload_len: 12,
hop_limit: 0x40
},
data: data
};
let ip_repr = IpRepr::Unspecified {
src_addr: REMOTE_IPV6.into(),
dst_addr: LOCAL_IPV6.into(),
protocol: IpProtocol::Icmp,
payload_len: icmp_repr.buffer_len(),
hop_limit: 0x40
};
assert!(!socket.can_recv());
// Ensure we can accept ICMP error response to the bound
// UDP port
assert!(socket.accepts(&ip_repr, &icmp_repr.into(), &caps.checksum));
assert_eq!(socket.process(&ip_repr, &icmp_repr.into(), &caps.checksum),
Ok(()));
assert!(socket.can_recv());
let mut bytes = [0x00; 66];
let mut packet = Icmpv6Packet::new(&mut bytes[..]);
icmp_repr.emit(&LOCAL_IPV6.into(), &REMOTE_IPV6.into(), &mut packet, &caps.checksum);
assert_eq!(socket.recv(), Ok((&packet.into_inner()[..], REMOTE_IPV6.into())));
assert!(!socket.can_recv());
}
}

View File

@ -17,7 +17,7 @@ use time::Instant;
mod meta;
#[cfg(feature = "socket-raw")]
mod raw;
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
mod icmp;
#[cfg(feature = "socket-udp")]
mod udp;
@ -33,7 +33,7 @@ pub use self::raw::{RawPacketMetadata,
RawSocketBuffer,
RawSocket};
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
pub use self::icmp::{IcmpPacketMetadata,
IcmpSocketBuffer,
Endpoint as IcmpEndpoint,
@ -69,7 +69,7 @@ pub(crate) use self::ref_::Session as SocketSession;
pub enum Socket<'a, 'b: 'a> {
#[cfg(feature = "socket-raw")]
Raw(RawSocket<'a, 'b>),
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
Icmp(IcmpSocket<'a, 'b>),
#[cfg(feature = "socket-udp")]
Udp(UdpSocket<'a, 'b>),
@ -90,7 +90,7 @@ macro_rules! dispatch_socket {
match $self_ {
#[cfg(feature = "socket-raw")]
&$( $mut_ )* Socket::Raw(ref $( $mut_ )* $socket) => $code,
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
&$( $mut_ )* Socket::Icmp(ref $( $mut_ )* $socket) => $code,
#[cfg(feature = "socket-udp")]
&$( $mut_ )* Socket::Udp(ref $( $mut_ )* $socket) => $code,
@ -149,7 +149,7 @@ macro_rules! from_socket {
#[cfg(feature = "socket-raw")]
from_socket!(RawSocket<'a, 'b>, Raw);
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
from_socket!(IcmpSocket<'a, 'b>, Icmp);
#[cfg(feature = "socket-udp")]
from_socket!(UdpSocket<'a, 'b>, Udp);

View File

@ -2,7 +2,7 @@ use core::ops::{Deref, DerefMut};
#[cfg(feature = "socket-raw")]
use socket::RawSocket;
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
use socket::IcmpSocket;
#[cfg(feature = "socket-udp")]
use socket::UdpSocket;
@ -21,7 +21,7 @@ pub trait Session {
#[cfg(feature = "socket-raw")]
impl<'a, 'b> Session for RawSocket<'a, 'b> {}
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
impl<'a, 'b> Session for IcmpSocket<'a, 'b> {}
#[cfg(feature = "socket-udp")]
impl<'a, 'b> Session for UdpSocket<'a, 'b> {}

View File

@ -144,7 +144,7 @@ impl<'a, 'b: 'a, 'c: 'a + 'b> Set<'a, 'b, 'c> {
#[cfg(feature = "socket-raw")]
&mut Socket::Raw(_) =>
may_remove = true,
#[cfg(all(feature = "socket-icmp", feature = "proto-ipv4"))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
&mut Socket::Icmp(_) =>
may_remove = true,
#[cfg(feature = "socket-udp")]

24
src/wire/icmp.rs Normal file
View File

@ -0,0 +1,24 @@
#[cfg(feature = "proto-ipv4")]
use super::icmpv4;
#[cfg(feature = "proto-ipv6")]
use super::icmpv6;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Repr<'a> {
#[cfg(feature = "proto-ipv4")]
Ipv4(icmpv4::Repr<'a>),
#[cfg(feature = "proto-ipv6")]
Ipv6(icmpv6::Repr<'a>),
}
#[cfg(feature = "proto-ipv4")]
impl<'a> From<icmpv4::Repr<'a>> for Repr<'a> {
fn from(s: icmpv4::Repr<'a>) -> Self {
Repr::Ipv4(s)
}
}
#[cfg(feature = "proto-ipv6")]
impl<'a> From<icmpv6::Repr<'a>> for Repr<'a> {
fn from(s: icmpv6::Repr<'a>) -> Self {
Repr::Ipv6(s)
}
}

View File

@ -95,6 +95,8 @@ mod ipv6fragment;
mod icmpv4;
#[cfg(feature = "proto-ipv6")]
mod icmpv6;
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
mod icmp;
#[cfg(feature = "proto-ipv4")]
mod igmp;
#[cfg(feature = "proto-ipv6")]
@ -174,6 +176,8 @@ pub use self::icmpv6::{Message as Icmpv6Message,
ParamProblem as Icmpv6ParamProblem,
Packet as Icmpv6Packet,
Repr as Icmpv6Repr};
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
pub use self::icmp::Repr as IcmpRepr;
#[cfg(feature = "proto-ipv6")]
pub use self::icmpv6::{RouterFlags as NdiscRouterFlags,