Skip to content

Commit efc7bfb

Browse files
committedDec 20, 2016
Reply with ICMP dest. unreachable or TCP RST from unused ports.
1 parent b0f7ac1 commit efc7bfb

File tree

8 files changed

+219
-55
lines changed

8 files changed

+219
-55
lines changed
 

‎README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ The only supported internetworking protocol is IPv4.
3131
* IPv4 options are **not** supported.
3232
* ICMPv4 header checksum is supported.
3333
* ICMPv4 echo requests and replies are supported.
34-
* ICMPv4 destination unreachable message is **not** supported.
34+
* ICMPv4 destination unreachable message is supported.
3535
* ICMPv4 parameter problem message is **not** supported.
3636

3737
### UDP layer

‎examples/smoltcpserver.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,16 @@ fn main() {
1515
let device = Tracer::<_, EthernetFrame<&[u8]>>::new(device);
1616
let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
1717

18-
let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
19-
let mut protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
20-
21-
let listen_address = IpAddress::v4(0, 0, 0, 0);
22-
let endpoint = IpEndpoint::new(listen_address, 6969);
23-
2418
let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 2048])]);
2519
let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 2048])]);
20+
let endpoint = IpEndpoint::new(IpAddress::default(), 6969);
2621
let udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer);
2722

28-
let mut sockets = [udp_socket];
23+
let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
24+
let protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
25+
let sockets = [udp_socket];
2926
let mut iface = EthernetInterface::new(device, arp_cache,
30-
hardware_addr, &mut protocol_addrs[..], &mut sockets[..]);
27+
hardware_addr, protocol_addrs, sockets);
3128

3229
loop {
3330
match iface.poll() {

‎src/iface/ethernet.rs

+88-22
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use wire::{EthernetAddress, EthernetProtocol, EthernetFrame};
77
use wire::{ArpPacket, ArpRepr, ArpOperation};
88
use wire::{IpAddress, IpProtocol};
99
use wire::{Ipv4Packet, Ipv4Repr};
10-
use wire::{Icmpv4Packet, Icmpv4Repr};
10+
use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable};
11+
use wire::{TcpPacket, TcpRepr, TcpControl};
1112
use socket::Socket;
1213
use super::{ArpCache};
1314

@@ -115,7 +116,8 @@ impl<'a, 'b: 'a,
115116
enum Response<'a> {
116117
Nop,
117118
Arp(ArpRepr),
118-
Icmpv4(Ipv4Repr, Icmpv4Repr<'a>)
119+
Icmpv4(Ipv4Repr, Icmpv4Repr<'a>),
120+
Tcpv4(Ipv4Repr, TcpRepr<'a>)
119121
}
120122

121123
// First, transmit any outgoing packets.
@@ -168,7 +170,8 @@ impl<'a, 'b: 'a,
168170
// Handle IP packets directed at us.
169171
EthernetProtocol::Ipv4 => {
170172
let ip_packet = try!(Ipv4Packet::new(eth_frame.payload()));
171-
match try!(Ipv4Repr::parse(&ip_packet)) {
173+
let ip_repr = try!(Ipv4Repr::parse(&ip_packet));
174+
match ip_repr {
172175
// Ignore IP packets not directed at us.
173176
Ipv4Repr { dst_addr, .. } if !self.has_protocol_addr(dst_addr) => (),
174177

@@ -204,16 +207,58 @@ impl<'a, 'b: 'a,
204207

205208
// Try dispatching a packet to a socket.
206209
Ipv4Repr { src_addr, dst_addr, protocol } => {
210+
let mut handled = false;
207211
for socket in self.sockets.borrow_mut() {
208212
match socket.collect(&src_addr.into(), &dst_addr.into(),
209213
protocol, ip_packet.payload()) {
210-
Ok(()) => break,
214+
Ok(()) => { handled = true; break }
211215
Err(Error::Rejected) => continue,
212216
Err(e) => return Err(e)
213217
}
214218
}
215219

216-
// FIXME: respond with ICMP destination unreachable here?
220+
if !handled && protocol == IpProtocol::Tcp {
221+
let tcp_packet = try!(TcpPacket::new(ip_packet.payload()));
222+
223+
let ip_reply_repr = Ipv4Repr {
224+
src_addr: dst_addr,
225+
dst_addr: src_addr,
226+
protocol: IpProtocol::Tcp
227+
};
228+
let tcp_reply_repr = TcpRepr {
229+
src_port: tcp_packet.dst_port(),
230+
dst_port: tcp_packet.src_port(),
231+
control: TcpControl::Rst,
232+
seq_number: 0,
233+
ack_number: Some(tcp_packet.seq_number() + 1),
234+
window_len: 0,
235+
payload: &[]
236+
};
237+
response = Response::Tcpv4(ip_reply_repr, tcp_reply_repr);
238+
} else if !handled {
239+
let reason;
240+
if protocol == IpProtocol::Udp {
241+
reason = Icmpv4DstUnreachable::PortUnreachable
242+
} else {
243+
reason = Icmpv4DstUnreachable::ProtoUnreachable
244+
}
245+
246+
let mut data = [0; 8];
247+
data.copy_from_slice(&ip_packet.payload()[0..8]);
248+
249+
let ip_reply_repr = Ipv4Repr {
250+
src_addr: dst_addr,
251+
dst_addr: src_addr,
252+
protocol: IpProtocol::Icmp
253+
};
254+
let icmp_reply_repr = Icmpv4Repr::DstUnreachable {
255+
reason: reason,
256+
header: ip_repr,
257+
length: ip_packet.payload().len(),
258+
data: data
259+
};
260+
response = Response::Icmpv4(ip_reply_repr, icmp_reply_repr)
261+
}
217262
},
218263
}
219264
}
@@ -222,6 +267,29 @@ impl<'a, 'b: 'a,
222267
_ => return Err(Error::Unrecognized)
223268
}
224269

270+
macro_rules! ip_response {
271+
($tx_buffer:ident, $frame:ident, $ip_repr:ident, $length:expr) => ({
272+
let dst_hardware_addr =
273+
match self.arp_cache.lookup($ip_repr.dst_addr.into()) {
274+
None => return Err(Error::Unaddressable),
275+
Some(hardware_addr) => hardware_addr
276+
};
277+
278+
let payload_len = $length;
279+
let frame_len = EthernetFrame::<&[u8]>::buffer_len($ip_repr.buffer_len() +
280+
payload_len);
281+
$tx_buffer = try!(self.device.transmit(frame_len));
282+
$frame = try!(EthernetFrame::new(&mut $tx_buffer));
283+
$frame.set_src_addr(self.hardware_addr);
284+
$frame.set_dst_addr(dst_hardware_addr);
285+
$frame.set_ethertype(EthernetProtocol::Ipv4);
286+
287+
let mut ip_packet = try!(Ipv4Packet::new($frame.payload_mut()));
288+
$ip_repr.emit(&mut ip_packet, payload_len);
289+
ip_packet
290+
})
291+
}
292+
225293
match response {
226294
Response::Arp(repr) => {
227295
let tx_len = EthernetFrame::<&[u8]>::buffer_len(repr.buffer_len());
@@ -241,26 +309,24 @@ impl<'a, 'b: 'a,
241309
},
242310

243311
Response::Icmpv4(ip_repr, icmp_repr) => {
244-
let dst_hardware_addr =
245-
match self.arp_cache.lookup(ip_repr.dst_addr.into()) {
246-
None => return Err(Error::Unaddressable),
247-
Some(hardware_addr) => hardware_addr
248-
};
249-
250-
let tx_len = EthernetFrame::<&[u8]>::buffer_len(ip_repr.buffer_len() +
251-
icmp_repr.buffer_len());
252-
let mut tx_buffer = try!(self.device.transmit(tx_len));
253-
let mut frame = try!(EthernetFrame::new(&mut tx_buffer));
254-
frame.set_src_addr(self.hardware_addr);
255-
frame.set_dst_addr(dst_hardware_addr);
256-
frame.set_ethertype(EthernetProtocol::Ipv4);
257-
258-
let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut()));
259-
ip_repr.emit(&mut ip_packet, icmp_repr.buffer_len());
260-
312+
let mut tx_buffer;
313+
let mut frame;
314+
let mut ip_packet = ip_response!(tx_buffer, frame, ip_repr,
315+
icmp_repr.buffer_len());
261316
let mut icmp_packet = try!(Icmpv4Packet::new(ip_packet.payload_mut()));
262317
icmp_repr.emit(&mut icmp_packet);
318+
Ok(())
319+
}
263320

321+
Response::Tcpv4(ip_repr, tcp_repr) => {
322+
let mut tx_buffer;
323+
let mut frame;
324+
let mut ip_packet = ip_response!(tx_buffer, frame, ip_repr,
325+
tcp_repr.buffer_len());
326+
let mut tcp_packet = try!(TcpPacket::new(ip_packet.payload_mut()));
327+
tcp_repr.emit(&mut tcp_packet,
328+
&IpAddress::Ipv4(ip_repr.src_addr),
329+
&IpAddress::Ipv4(ip_repr.dst_addr));
264330
Ok(())
265331
}
266332

‎src/wire/ethernet.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use core::cmp;
21
use core::fmt;
32
use byteorder::{ByteOrder, NetworkEndian};
43

@@ -100,9 +99,7 @@ impl<T: AsRef<[u8]>> Frame<T> {
10099
/// Return the length of a buffer required to hold a packet with the payload
101100
/// of a given length.
102101
pub fn buffer_len(payload_len: usize) -> usize {
103-
// Minimal frame size is 64, but that includes FCS, which the network device
104-
// is taking care of for us.
105-
cmp::max(field::PAYLOAD.start + payload_len, 60)
102+
field::PAYLOAD.start + payload_len
106103
}
107104

108105
/// Return the destination address field.

‎src/wire/icmpv4.rs

+116-13
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ use byteorder::{ByteOrder, NetworkEndian};
33

44
use Error;
55
use super::ip::checksum;
6+
use super::{Ipv4Packet, Ipv4Repr};
67

78
enum_with_unknown! {
89
/// Internet protocol control message type.
910
pub doc enum Message(u8) {
1011
/// Echo reply
1112
EchoReply = 0,
1213
/// Destination unreachable
13-
DstUnreachable = 1,
14+
DstUnreachable = 3,
1415
/// Message redirect
1516
Redirect = 5,
1617
/// Echo request
@@ -86,6 +87,47 @@ enum_with_unknown! {
8687
}
8788
}
8889

90+
impl fmt::Display for DstUnreachable {
91+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
92+
match self {
93+
&DstUnreachable::NetUnreachable =>
94+
write!(f, "destination network unreachable"),
95+
&DstUnreachable::HostUnreachable =>
96+
write!(f, "destination host unreachable"),
97+
&DstUnreachable::ProtoUnreachable =>
98+
write!(f, "destination protocol unreachable"),
99+
&DstUnreachable::PortUnreachable =>
100+
write!(f, "destination port unreachable"),
101+
&DstUnreachable::FragRequired =>
102+
write!(f, "fragmentation required, and DF flag set"),
103+
&DstUnreachable::SrcRouteFailed =>
104+
write!(f, "source route failed"),
105+
&DstUnreachable::DstNetUnknown =>
106+
write!(f, "destination network unknown"),
107+
&DstUnreachable::DstHostUnknown =>
108+
write!(f, "destination host unknown"),
109+
&DstUnreachable::SrcHostIsolated =>
110+
write!(f, "source host isolated"),
111+
&DstUnreachable::NetProhibited =>
112+
write!(f, "network administratively prohibited"),
113+
&DstUnreachable::HostProhibited =>
114+
write!(f, "host administratively prohibited"),
115+
&DstUnreachable::NetUnreachToS =>
116+
write!(f, "network unreachable for ToS"),
117+
&DstUnreachable::HostUnreachToS =>
118+
write!(f, "host unreachable for ToS"),
119+
&DstUnreachable::CommProhibited =>
120+
write!(f, "communication administratively prohibited"),
121+
&DstUnreachable::HostPrecedViol =>
122+
write!(f, "host precedence violation"),
123+
&DstUnreachable::PrecedCutoff =>
124+
write!(f, "precedence cutoff in effect"),
125+
&DstUnreachable::Unknown(id) =>
126+
write!(f, "{}", id)
127+
}
128+
}
129+
}
130+
89131
enum_with_unknown! {
90132
/// Internet protocol control message subtype for type "Redirect Message".
91133
pub doc enum Redirect(u8) {
@@ -135,6 +177,8 @@ mod field {
135177
pub const CODE: usize = 1;
136178
pub const CHECKSUM: Field = 2..4;
137179

180+
pub const UNUSED: Field = 4..8;
181+
138182
pub const ECHO_IDENT: Field = 4..6;
139183
pub const ECHO_SEQNO: Field = 6..8;
140184
}
@@ -206,8 +250,9 @@ impl<T: AsRef<[u8]>> Packet<T> {
206250
/// The result depends on the value of the message type field.
207251
pub fn header_len(&self) -> usize {
208252
match self.msg_type() {
209-
Message::EchoRequest => field::ECHO_SEQNO.end,
210-
Message::EchoReply => field::ECHO_SEQNO.end,
253+
Message::EchoRequest => field::ECHO_SEQNO.end,
254+
Message::EchoReply => field::ECHO_SEQNO.end,
255+
Message::DstUnreachable => field::UNUSED.end,
211256
_ => field::CHECKSUM.end // make a conservative assumption
212257
}
213258
}
@@ -304,6 +349,12 @@ pub enum Repr<'a> {
304349
seq_no: u16,
305350
data: &'a [u8]
306351
},
352+
DstUnreachable {
353+
reason: DstUnreachable,
354+
header: Ipv4Repr,
355+
length: usize,
356+
data: [u8; 8]
357+
},
307358
#[doc(hidden)]
308359
__Nonexhaustive
309360
}
@@ -320,13 +371,33 @@ impl<'a> Repr<'a> {
320371
data: packet.data()
321372
})
322373
},
374+
323375
(Message::EchoReply, 0) => {
324376
Ok(Repr::EchoReply {
325377
ident: packet.echo_ident(),
326378
seq_no: packet.echo_seq_no(),
327379
data: packet.data()
328380
})
329381
},
382+
383+
(Message::DstUnreachable, code) => {
384+
let ip_packet = try!(Ipv4Packet::new(packet.data()));
385+
let ip_repr = try!(Ipv4Repr::parse(&ip_packet));
386+
387+
let mut data = [0; 8];
388+
let payload = &packet.data()[ip_packet.header_len() as usize..];
389+
if payload.len() < data.len() { return Err(Error::Truncated) }
390+
data.copy_from_slice(&payload[0..8]);
391+
392+
let length = ip_packet.total_len() as usize - ip_packet.header_len() as usize;
393+
394+
Ok(Repr::DstUnreachable {
395+
reason: DstUnreachable::from(code),
396+
header: ip_repr,
397+
length: length,
398+
data: data
399+
})
400+
}
330401
_ => Err(Error::Unrecognized)
331402
}
332403
}
@@ -338,6 +409,9 @@ impl<'a> Repr<'a> {
338409
&Repr::EchoReply { data, .. } => {
339410
field::ECHO_SEQNO.end + data.len()
340411
},
412+
&Repr::DstUnreachable { header, data, .. } => {
413+
field::UNUSED.end + header.buffer_len() + data.len()
414+
}
341415
&Repr::__Nonexhaustive => unreachable!()
342416
}
343417
}
@@ -349,18 +423,33 @@ impl<'a> Repr<'a> {
349423
match self {
350424
&Repr::EchoRequest { ident, seq_no, data } => {
351425
packet.set_msg_type(Message::EchoRequest);
426+
packet.set_msg_code(0);
352427
packet.set_echo_ident(ident);
353428
packet.set_echo_seq_no(seq_no);
354429
let data_len = cmp::min(packet.data_mut().len(), data.len());
355430
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
356431
},
432+
357433
&Repr::EchoReply { ident, seq_no, data } => {
358434
packet.set_msg_type(Message::EchoReply);
435+
packet.set_msg_code(0);
359436
packet.set_echo_ident(ident);
360437
packet.set_echo_seq_no(seq_no);
361438
let data_len = cmp::min(packet.data_mut().len(), data.len());
362439
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
363440
},
441+
442+
&Repr::DstUnreachable { reason, header, length, data } => {
443+
packet.set_msg_type(Message::DstUnreachable);
444+
packet.set_msg_code(reason.into());
445+
446+
let mut ip_packet = Ipv4Packet::new(packet.data_mut())
447+
.expect("undersized data");
448+
header.emit(&mut ip_packet, length);
449+
let mut payload = &mut ip_packet.into_inner()[header.buffer_len()..];
450+
payload.copy_from_slice(&data[..])
451+
}
452+
364453
&Repr::__Nonexhaustive => unreachable!()
365454
}
366455
packet.fill_checksum()
@@ -371,10 +460,14 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
371460
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
372461
match Repr::parse(self) {
373462
Ok(repr) => write!(f, "{}", repr),
374-
_ => {
375-
try!(write!(f, "ICMPv4 (unrecognized)"));
376-
try!(write!(f, " type={} code={} cksum={:#04x}",
377-
self.msg_type(), self.msg_code(), self.checksum()));
463+
Err(err) => {
464+
try!(write!(f, "ICMPv4 ({})", err));
465+
try!(write!(f, " type={:?}", self.msg_type()));
466+
match self.msg_type() {
467+
Message::DstUnreachable =>
468+
try!(write!(f, " code={:?}", DstUnreachable::from(self.msg_code()))),
469+
_ => try!(write!(f, " code={}", self.msg_code()))
470+
}
378471
Ok(())
379472
}
380473
}
@@ -385,11 +478,14 @@ impl<'a> fmt::Display for Repr<'a> {
385478
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
386479
match self {
387480
&Repr::EchoRequest { ident, seq_no, data } =>
388-
write!(f, "ICMPv4 Echo Request id={} seq={} len={}",
481+
write!(f, "ICMPv4 echo request id={} seq={} len={}",
389482
ident, seq_no, data.len()),
390483
&Repr::EchoReply { ident, seq_no, data } =>
391-
write!(f, "ICMPv4 Echo Reply id={} seq={} len={}",
484+
write!(f, "ICMPv4 echo reply id={} seq={} len={}",
392485
ident, seq_no, data.len()),
486+
&Repr::DstUnreachable { reason, .. } =>
487+
write!(f, "ICMPv4 destination unreachable ({})",
488+
reason),
393489
&Repr::__Nonexhaustive => unreachable!()
394490
}
395491
}
@@ -400,14 +496,21 @@ use super::pretty_print::{PrettyPrint, PrettyIndent};
400496
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
401497
fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
402498
indent: &mut PrettyIndent) -> fmt::Result {
403-
match Packet::new(buffer) {
404-
Err(err) => write!(f, "{}({})\n", indent, err),
405-
Ok(packet) => write!(f, "{}{}\n", indent, packet)
499+
let packet = match Packet::new(buffer) {
500+
Err(err) => return write!(f, "{}({})\n", indent, err),
501+
Ok(packet) => packet
502+
};
503+
try!(write!(f, "{}{}\n", indent, packet));
504+
505+
indent.increase();
506+
match packet.msg_type() {
507+
Message::DstUnreachable =>
508+
super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent),
509+
_ => Ok(())
406510
}
407511
}
408512
}
409513

410-
411514
#[cfg(test)]
412515
mod test {
413516
use super::*;

‎src/wire/ip.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ pub struct Endpoint {
8686
impl Endpoint {
8787
pub const INVALID: Endpoint = Endpoint { addr: Address::Invalid, port: 0 };
8888

89-
/// Create an internet endpoint address.
89+
/// Create an endpoint address from given address and port.
9090
pub fn new(addr: Address, port: u16) -> Endpoint {
9191
Endpoint { addr: addr, port: port }
9292
}

‎src/wire/ipv4.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ impl Repr {
401401
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>,
402402
payload_len: usize) {
403403
packet.set_version(4);
404-
packet.set_header_len(20);
404+
packet.set_header_len(field::DST_ADDR.end as u8);
405405
packet.set_dscp(0);
406406
packet.set_ecn(0);
407407
let total_len = packet.header_len() as u16 + payload_len as u16;
@@ -423,8 +423,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
423423
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424424
match Repr::parse(self) {
425425
Ok(repr) => write!(f, "{}", repr),
426-
_ => {
427-
try!(write!(f, "IPv4 (unrecognized)"));
426+
Err(err) => {
427+
try!(write!(f, "IPv4 ({})", err));
428428
try!(write!(f, " src={} dst={} proto={} ttl={}",
429429
self.src_addr(), self.dst_addr(), self.protocol(), self.ttl()));
430430
if self.version() != 4 {
@@ -471,12 +471,12 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
471471
fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
472472
indent: &mut PrettyIndent) -> fmt::Result {
473473
let packet = match Packet::new(buffer) {
474-
Err(err) => return write!(f, "{}({})\n", indent, err),
474+
Err(err) => return write!(f, "{}({})\n", indent, err),
475475
Ok(packet) => packet
476476
};
477477
try!(write!(f, "{}{}\n", indent, packet));
478-
indent.increase();
479478

479+
indent.increase();
480480
match packet.protocol() {
481481
Protocol::Icmp =>
482482
super::Icmpv4Packet::<&[u8]>::pretty_print(&packet.payload(), f, indent),

‎src/wire/tcp.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -462,14 +462,15 @@ impl<'a> Repr<'a> {
462462
packet.set_seq_number(self.seq_number);
463463
packet.set_ack_number(self.ack_number.unwrap_or(0));
464464
packet.set_window_len(self.window_len);
465-
packet.set_header_len(20);
465+
packet.set_header_len(field::URGENT.end as u8);
466466
packet.clear_flags();
467467
match self.control {
468468
Control::None => (),
469469
Control::Syn => packet.set_syn(true),
470470
Control::Fin => packet.set_fin(true),
471471
Control::Rst => packet.set_rst(true)
472472
}
473+
packet.set_ack(self.ack_number.is_some());
473474
packet.payload_mut().copy_from_slice(self.payload);
474475
packet.fill_checksum(src_addr, dst_addr)
475476
}

0 commit comments

Comments
 (0)
Please sign in to comment.