Implement ICMPv4 echo replies.
parent
c18d6bf04d
commit
d587981ef5
10
README.md
10
README.md
|
@ -18,11 +18,10 @@ features are listed.
|
|||
The only supported medium is Ethernet.
|
||||
|
||||
* Regular 802.3 frames are supported.
|
||||
* ARP packets (including gratuitous requests and replies) are supported.
|
||||
* 802.1Q is **not** supported.
|
||||
* Jumbo frames are **not** supported.
|
||||
* CRC calculation is **not** supported.
|
||||
* ARP packets are supported.
|
||||
* ARP probes or announcements are **not** supported.
|
||||
* Frame check sequence calculation is **not** supported.
|
||||
|
||||
### IP layer
|
||||
|
||||
|
@ -31,6 +30,9 @@ The only supported internetworking protocol is IPv4.
|
|||
* IPv4 header checksum is supported.
|
||||
* IPv4 fragmentation is **not** supported.
|
||||
* IPv4 options are **not** supported.
|
||||
* ICMPv4 echo requests and replies are supported.
|
||||
* ICMPv4 destination unreachable message is **not** supported.
|
||||
* ICMPv4 parameter problem message is **not** supported.
|
||||
|
||||
### UDP layer
|
||||
|
||||
|
@ -91,7 +93,7 @@ cargo run --example smoltcpserver -- tap0
|
|||
|
||||
It responds to:
|
||||
|
||||
* pings (`ping 192.168.69.1`) (actually not yet).
|
||||
* pings (`ping 192.168.69.1`).
|
||||
|
||||
License
|
||||
-------
|
||||
|
|
|
@ -2,6 +2,9 @@ use Error;
|
|||
use phy::Device;
|
||||
use wire::{EthernetAddress, EthernetProtocolType, EthernetFrame};
|
||||
use wire::{ArpPacket, ArpRepr, ArpOperation};
|
||||
use wire::InternetProtocolType;
|
||||
use wire::{Ipv4Packet, Ipv4Repr};
|
||||
use wire::{Icmpv4Packet, Icmpv4Repr};
|
||||
use super::{ProtocolAddress, ArpCache};
|
||||
|
||||
/// An Ethernet network interface.
|
||||
|
@ -71,19 +74,20 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
|
|||
|
||||
/// Receive and process a packet, if available.
|
||||
pub fn poll(&mut self) -> Result<(), Error> {
|
||||
enum Response {
|
||||
enum Response<'a> {
|
||||
Nop,
|
||||
Arp(ArpRepr)
|
||||
Arp(ArpRepr),
|
||||
Icmpv4(Ipv4Repr, Icmpv4Repr<'a>)
|
||||
}
|
||||
let mut response = Response::Nop;
|
||||
|
||||
let rx_buffer = try!(self.device.receive());
|
||||
let frame = try!(EthernetFrame::new(rx_buffer));
|
||||
match frame.ethertype() {
|
||||
let eth_frame = try!(EthernetFrame::new(rx_buffer));
|
||||
match eth_frame.ethertype() {
|
||||
// Snoop all ARP traffic, and respond to ARP packets directed at us.
|
||||
EthernetProtocolType::Arp => {
|
||||
let packet = try!(ArpPacket::new(frame.payload()));
|
||||
let repr = try!(ArpRepr::parse(&packet));
|
||||
match repr {
|
||||
let arp_packet = try!(ArpPacket::new(eth_frame.payload()));
|
||||
match try!(ArpRepr::parse(&arp_packet)) {
|
||||
// Respond to ARP requests aimed at us, and fill the ARP cache
|
||||
// from all ARP requests, including gratuitous.
|
||||
ArpRepr::EthernetIpv4 {
|
||||
|
@ -115,17 +119,61 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
|
|||
_ => return Err(Error::Unrecognized)
|
||||
}
|
||||
},
|
||||
|
||||
// Respond to IP packets directed at us.
|
||||
EthernetProtocolType::Ipv4 => {
|
||||
let ip_packet = try!(Ipv4Packet::new(eth_frame.payload()));
|
||||
match try!(Ipv4Repr::parse(&ip_packet)) {
|
||||
// Ignore IP packets not directed at us.
|
||||
Ipv4Repr { dst_addr, .. } if !self.has_protocol_addr(dst_addr) => (),
|
||||
|
||||
// Respond to ICMP packets.
|
||||
Ipv4Repr { protocol: InternetProtocolType::Icmp, src_addr, dst_addr } => {
|
||||
let icmp_packet = try!(Icmpv4Packet::new(ip_packet.payload()));
|
||||
let icmp_repr = try!(Icmpv4Repr::parse(&icmp_packet));
|
||||
match icmp_repr {
|
||||
// Respond to echo requests.
|
||||
Icmpv4Repr::EchoRequest {
|
||||
ident, seq_no, data
|
||||
} => {
|
||||
let ip_reply_repr = Ipv4Repr {
|
||||
src_addr: dst_addr,
|
||||
dst_addr: src_addr,
|
||||
protocol: InternetProtocolType::Icmp
|
||||
};
|
||||
let icmp_reply_repr = Icmpv4Repr::EchoReply {
|
||||
ident: ident,
|
||||
seq_no: seq_no,
|
||||
data: &[]
|
||||
};
|
||||
response = Response::Icmpv4(ip_reply_repr, icmp_reply_repr)
|
||||
}
|
||||
|
||||
// Ignore any echo replies.
|
||||
Icmpv4Repr::EchoReply { .. } => (),
|
||||
|
||||
// FIXME: do something correct here?
|
||||
_ => return Err(Error::Unrecognized)
|
||||
}
|
||||
},
|
||||
|
||||
// FIXME: respond with ICMP unknown protocol here?
|
||||
_ => return Err(Error::Unrecognized)
|
||||
}
|
||||
}
|
||||
|
||||
// Drop all other traffic.
|
||||
_ => return Err(Error::Unrecognized)
|
||||
}
|
||||
if let Response::Nop = response { return Ok(()) }
|
||||
|
||||
let tx_size = self.device.mtu();
|
||||
let tx_buffer = try!(self.device.transmit(tx_size));
|
||||
let mut frame = try!(EthernetFrame::new(tx_buffer));
|
||||
frame.set_src_addr(self.hardware_addr);
|
||||
|
||||
match response {
|
||||
Response::Nop => Ok(()),
|
||||
|
||||
Response::Arp(repr) => {
|
||||
let tx_size = self.device.mtu();
|
||||
let tx_buffer = try!(self.device.transmit(tx_size));
|
||||
let mut frame = try!(EthernetFrame::new(tx_buffer));
|
||||
frame.set_src_addr(self.hardware_addr);
|
||||
frame.set_dst_addr(match repr {
|
||||
ArpRepr::EthernetIpv4 { target_hardware_addr, .. } => target_hardware_addr,
|
||||
_ => unreachable!()
|
||||
|
@ -133,10 +181,26 @@ impl<'a, DeviceT: Device, ArpCacheT: ArpCache> Interface<'a, DeviceT, ArpCacheT>
|
|||
frame.set_ethertype(EthernetProtocolType::Arp);
|
||||
|
||||
let mut packet = try!(ArpPacket::new(frame.payload_mut()));
|
||||
repr.emit(&mut packet);
|
||||
repr.emit(&mut packet)
|
||||
},
|
||||
|
||||
Ok(())
|
||||
Response::Icmpv4(ip_repr, icmp_repr) => {
|
||||
match self.arp_cache.lookup(ip_repr.dst_addr.into()) {
|
||||
None => return Err(Error::Unaddressable),
|
||||
Some(hardware_addr) => frame.set_dst_addr(hardware_addr)
|
||||
}
|
||||
frame.set_ethertype(EthernetProtocolType::Ipv4);
|
||||
|
||||
let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut()));
|
||||
ip_repr.emit(&mut ip_packet, icmp_repr.len());
|
||||
|
||||
let mut icmp_packet = try!(Icmpv4Packet::new(ip_packet.payload_mut()));
|
||||
icmp_repr.emit(&mut icmp_packet);
|
||||
}
|
||||
|
||||
Response::Nop => unreachable!()
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -18,8 +18,8 @@ pub mod iface;
|
|||
/// The error type for the networking stack.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// A packet could not be parsed or emitted because a field was out of bounds
|
||||
/// for the underlying buffer.
|
||||
/// An incoming packet could not be parsed, or an outgoing packet could not be emitted
|
||||
/// because a field was out of bounds for the underlying buffer.
|
||||
Truncated,
|
||||
/// An incoming packet could not be recognized and was dropped.
|
||||
/// E.g. a packet with an unknown EtherType.
|
||||
|
@ -32,6 +32,10 @@ pub enum Error {
|
|||
Checksum,
|
||||
/// An incoming packet has been fragmented and was dropped.
|
||||
Fragmented,
|
||||
/// An outgoing packet could not be sent because a protocol address could not be mapped
|
||||
/// to hardware address. E.g. an IPv4 packet did not have an Ethernet address
|
||||
/// corresponding to its IPv4 destination address.
|
||||
Unaddressable,
|
||||
|
||||
#[doc(hidden)]
|
||||
__Nonexhaustive
|
||||
|
@ -40,11 +44,12 @@ pub enum Error {
|
|||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&Error::Truncated => write!(f, "truncated packet"),
|
||||
&Error::Unrecognized => write!(f, "unrecognized packet"),
|
||||
&Error::Malformed => write!(f, "malformed packet"),
|
||||
&Error::Checksum => write!(f, "checksum error"),
|
||||
&Error::Fragmented => write!(f, "fragmented packet"),
|
||||
&Error::Truncated => write!(f, "truncated packet"),
|
||||
&Error::Unrecognized => write!(f, "unrecognized packet"),
|
||||
&Error::Malformed => write!(f, "malformed packet"),
|
||||
&Error::Checksum => write!(f, "checksum error"),
|
||||
&Error::Fragmented => write!(f, "fragmented packet"),
|
||||
&Error::Unaddressable => write!(f, "unaddressable destination"),
|
||||
&Error::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use core::fmt;
|
||||
use core::{cmp, fmt};
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
use Error;
|
||||
|
@ -332,6 +332,17 @@ impl<'a> Repr<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the length of a packet that will be emitted from this high-level representation.
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
&Repr::EchoRequest { data, .. } |
|
||||
&Repr::EchoReply { data, .. } => {
|
||||
field::ECHO_SEQNO.end + data.len()
|
||||
},
|
||||
&Repr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>) {
|
||||
packet.set_msg_code(0);
|
||||
|
@ -340,13 +351,15 @@ impl<'a> Repr<'a> {
|
|||
packet.set_msg_type(Type::EchoRequest);
|
||||
packet.set_echo_ident(ident);
|
||||
packet.set_echo_seq_no(seq_no);
|
||||
packet.data_mut().copy_from_slice(data)
|
||||
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 } => {
|
||||
packet.set_msg_type(Type::EchoReply);
|
||||
packet.set_echo_ident(ident);
|
||||
packet.set_echo_seq_no(seq_no);
|
||||
packet.data_mut().copy_from_slice(data)
|
||||
let data_len = cmp::min(packet.data_mut().len(), data.len());
|
||||
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
|
||||
},
|
||||
&Repr::__Nonexhaustive => unreachable!()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue