Prechádzať zdrojové kódy

Reply with ICMP dest. unreachable or TCP RST from unused ports.

whitequark 8 rokov pred
rodič
commit
efc7bfbc38

+ 1 - 1
README.md

@@ -31,7 +31,7 @@ The only supported internetworking protocol is IPv4.
   * IPv4 options are **not** supported.
   * ICMPv4 header checksum is supported.
   * ICMPv4 echo requests and replies are supported.
-  * ICMPv4 destination unreachable message is **not** supported.
+  * ICMPv4 destination unreachable message is supported.
   * ICMPv4 parameter problem message is **not** supported.
 
 ### UDP layer

+ 5 - 8
examples/smoltcpserver.rs

@@ -15,19 +15,16 @@ fn main() {
     let device = Tracer::<_, EthernetFrame<&[u8]>>::new(device);
     let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);
 
-    let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
-    let mut protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
-
-    let listen_address = IpAddress::v4(0, 0, 0, 0);
-    let endpoint = IpEndpoint::new(listen_address, 6969);
-
     let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 2048])]);
     let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketBuffer::new(vec![0; 2048])]);
+    let endpoint = IpEndpoint::new(IpAddress::default(), 6969);
     let udp_socket = UdpSocket::new(endpoint, udp_rx_buffer, udp_tx_buffer);
 
-    let mut sockets = [udp_socket];
+    let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
+    let protocol_addrs = [IpAddress::v4(192, 168, 69, 1)];
+    let sockets = [udp_socket];
     let mut iface = EthernetInterface::new(device, arp_cache,
-        hardware_addr, &mut protocol_addrs[..], &mut sockets[..]);
+        hardware_addr, protocol_addrs, sockets);
 
     loop {
         match iface.poll() {

+ 88 - 22
src/iface/ethernet.rs

@@ -7,7 +7,8 @@ use wire::{EthernetAddress, EthernetProtocol, EthernetFrame};
 use wire::{ArpPacket, ArpRepr, ArpOperation};
 use wire::{IpAddress, IpProtocol};
 use wire::{Ipv4Packet, Ipv4Repr};
-use wire::{Icmpv4Packet, Icmpv4Repr};
+use wire::{Icmpv4Packet, Icmpv4Repr, Icmpv4DstUnreachable};
+use wire::{TcpPacket, TcpRepr, TcpControl};
 use socket::Socket;
 use super::{ArpCache};
 
@@ -115,7 +116,8 @@ impl<'a, 'b: 'a,
         enum Response<'a> {
             Nop,
             Arp(ArpRepr),
-            Icmpv4(Ipv4Repr, Icmpv4Repr<'a>)
+            Icmpv4(Ipv4Repr, Icmpv4Repr<'a>),
+            Tcpv4(Ipv4Repr, TcpRepr<'a>)
         }
 
         // First, transmit any outgoing packets.
@@ -168,7 +170,8 @@ impl<'a, 'b: 'a,
             // Handle IP packets directed at us.
             EthernetProtocol::Ipv4 => {
                 let ip_packet = try!(Ipv4Packet::new(eth_frame.payload()));
-                match try!(Ipv4Repr::parse(&ip_packet)) {
+                let ip_repr = try!(Ipv4Repr::parse(&ip_packet));
+                match ip_repr {
                     // Ignore IP packets not directed at us.
                     Ipv4Repr { dst_addr, .. } if !self.has_protocol_addr(dst_addr) => (),
 
@@ -204,16 +207,58 @@ impl<'a, 'b: 'a,
 
                     // Try dispatching a packet to a socket.
                     Ipv4Repr { src_addr, dst_addr, protocol } => {
+                        let mut handled = false;
                         for socket in self.sockets.borrow_mut() {
                             match socket.collect(&src_addr.into(), &dst_addr.into(),
                                                  protocol, ip_packet.payload()) {
-                                Ok(()) => break,
+                                Ok(()) => { handled = true; break }
                                 Err(Error::Rejected) => continue,
                                 Err(e) => return Err(e)
                             }
                         }
 
-                        // FIXME: respond with ICMP destination unreachable here?
+                        if !handled && protocol == IpProtocol::Tcp {
+                            let tcp_packet = try!(TcpPacket::new(ip_packet.payload()));
+
+                            let ip_reply_repr = Ipv4Repr {
+                                src_addr: dst_addr,
+                                dst_addr: src_addr,
+                                protocol: IpProtocol::Tcp
+                            };
+                            let tcp_reply_repr = TcpRepr {
+                                src_port:   tcp_packet.dst_port(),
+                                dst_port:   tcp_packet.src_port(),
+                                control:    TcpControl::Rst,
+                                seq_number: 0,
+                                ack_number: Some(tcp_packet.seq_number() + 1),
+                                window_len: 0,
+                                payload:    &[]
+                            };
+                            response = Response::Tcpv4(ip_reply_repr, tcp_reply_repr);
+                        } else if !handled {
+                            let reason;
+                            if protocol == IpProtocol::Udp {
+                                reason = Icmpv4DstUnreachable::PortUnreachable
+                            } else {
+                                reason = Icmpv4DstUnreachable::ProtoUnreachable
+                            }
+
+                            let mut data = [0; 8];
+                            data.copy_from_slice(&ip_packet.payload()[0..8]);
+
+                            let ip_reply_repr = Ipv4Repr {
+                                src_addr: dst_addr,
+                                dst_addr: src_addr,
+                                protocol: IpProtocol::Icmp
+                            };
+                            let icmp_reply_repr = Icmpv4Repr::DstUnreachable {
+                                reason:   reason,
+                                header:   ip_repr,
+                                length:   ip_packet.payload().len(),
+                                data:     data
+                            };
+                            response = Response::Icmpv4(ip_reply_repr, icmp_reply_repr)
+                        }
                     },
                 }
             }
@@ -222,6 +267,29 @@ impl<'a, 'b: 'a,
             _ => return Err(Error::Unrecognized)
         }
 
+        macro_rules! ip_response {
+            ($tx_buffer:ident, $frame:ident, $ip_repr:ident, $length:expr) => ({
+                let dst_hardware_addr =
+                    match self.arp_cache.lookup($ip_repr.dst_addr.into()) {
+                        None => return Err(Error::Unaddressable),
+                        Some(hardware_addr) => hardware_addr
+                    };
+
+                let payload_len = $length;
+                let frame_len = EthernetFrame::<&[u8]>::buffer_len($ip_repr.buffer_len() +
+                                                                   payload_len);
+                $tx_buffer = try!(self.device.transmit(frame_len));
+                $frame = try!(EthernetFrame::new(&mut $tx_buffer));
+                $frame.set_src_addr(self.hardware_addr);
+                $frame.set_dst_addr(dst_hardware_addr);
+                $frame.set_ethertype(EthernetProtocol::Ipv4);
+
+                let mut ip_packet = try!(Ipv4Packet::new($frame.payload_mut()));
+                $ip_repr.emit(&mut ip_packet, payload_len);
+                ip_packet
+            })
+        }
+
         match response {
             Response::Arp(repr) => {
                 let tx_len = EthernetFrame::<&[u8]>::buffer_len(repr.buffer_len());
@@ -241,26 +309,24 @@ impl<'a, 'b: 'a,
             },
 
             Response::Icmpv4(ip_repr, icmp_repr) => {
-                let dst_hardware_addr =
-                    match self.arp_cache.lookup(ip_repr.dst_addr.into()) {
-                        None => return Err(Error::Unaddressable),
-                        Some(hardware_addr) => hardware_addr
-                    };
-
-                let tx_len = EthernetFrame::<&[u8]>::buffer_len(ip_repr.buffer_len() +
-                                                                icmp_repr.buffer_len());
-                let mut tx_buffer = try!(self.device.transmit(tx_len));
-                let mut frame = try!(EthernetFrame::new(&mut tx_buffer));
-                frame.set_src_addr(self.hardware_addr);
-                frame.set_dst_addr(dst_hardware_addr);
-                frame.set_ethertype(EthernetProtocol::Ipv4);
-
-                let mut ip_packet = try!(Ipv4Packet::new(frame.payload_mut()));
-                ip_repr.emit(&mut ip_packet, icmp_repr.buffer_len());
-
+                let mut tx_buffer;
+                let mut frame;
+                let mut ip_packet = ip_response!(tx_buffer, frame, ip_repr,
+                                                 icmp_repr.buffer_len());
                 let mut icmp_packet = try!(Icmpv4Packet::new(ip_packet.payload_mut()));
                 icmp_repr.emit(&mut icmp_packet);
+                Ok(())
+            }
 
+            Response::Tcpv4(ip_repr, tcp_repr) => {
+                let mut tx_buffer;
+                let mut frame;
+                let mut ip_packet = ip_response!(tx_buffer, frame, ip_repr,
+                                                 tcp_repr.buffer_len());
+                let mut tcp_packet = try!(TcpPacket::new(ip_packet.payload_mut()));
+                tcp_repr.emit(&mut tcp_packet,
+                              &IpAddress::Ipv4(ip_repr.src_addr),
+                              &IpAddress::Ipv4(ip_repr.dst_addr));
                 Ok(())
             }
 

+ 1 - 4
src/wire/ethernet.rs

@@ -1,4 +1,3 @@
-use core::cmp;
 use core::fmt;
 use byteorder::{ByteOrder, NetworkEndian};
 
@@ -100,9 +99,7 @@ impl<T: AsRef<[u8]>> Frame<T> {
     /// Return the length of a buffer required to hold a packet with the payload
     /// of a given length.
     pub fn buffer_len(payload_len: usize) -> usize {
-        // Minimal frame size is 64, but that includes FCS, which the network device
-        // is taking care of for us.
-        cmp::max(field::PAYLOAD.start + payload_len, 60)
+        field::PAYLOAD.start + payload_len
     }
 
     /// Return the destination address field.

+ 116 - 13
src/wire/icmpv4.rs

@@ -3,6 +3,7 @@ use byteorder::{ByteOrder, NetworkEndian};
 
 use Error;
 use super::ip::checksum;
+use super::{Ipv4Packet, Ipv4Repr};
 
 enum_with_unknown! {
     /// Internet protocol control message type.
@@ -10,7 +11,7 @@ enum_with_unknown! {
         /// Echo reply
         EchoReply      =  0,
         /// Destination unreachable
-        DstUnreachable =  1,
+        DstUnreachable =  3,
         /// Message redirect
         Redirect       =  5,
         /// Echo request
@@ -86,6 +87,47 @@ enum_with_unknown! {
     }
 }
 
+impl fmt::Display for DstUnreachable {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            &DstUnreachable::NetUnreachable =>
+                write!(f, "destination network unreachable"),
+            &DstUnreachable::HostUnreachable =>
+                write!(f, "destination host unreachable"),
+            &DstUnreachable::ProtoUnreachable =>
+                write!(f, "destination protocol unreachable"),
+            &DstUnreachable::PortUnreachable =>
+                write!(f, "destination port unreachable"),
+            &DstUnreachable::FragRequired =>
+                write!(f, "fragmentation required, and DF flag set"),
+            &DstUnreachable::SrcRouteFailed =>
+                write!(f, "source route failed"),
+            &DstUnreachable::DstNetUnknown =>
+                write!(f, "destination network unknown"),
+            &DstUnreachable::DstHostUnknown =>
+                write!(f, "destination host unknown"),
+            &DstUnreachable::SrcHostIsolated =>
+                write!(f, "source host isolated"),
+            &DstUnreachable::NetProhibited =>
+                write!(f, "network administratively prohibited"),
+            &DstUnreachable::HostProhibited =>
+                write!(f, "host administratively prohibited"),
+            &DstUnreachable::NetUnreachToS =>
+                write!(f, "network unreachable for ToS"),
+            &DstUnreachable::HostUnreachToS =>
+                write!(f, "host unreachable for ToS"),
+            &DstUnreachable::CommProhibited =>
+                write!(f, "communication administratively prohibited"),
+            &DstUnreachable::HostPrecedViol =>
+                write!(f, "host precedence violation"),
+            &DstUnreachable::PrecedCutoff =>
+                write!(f, "precedence cutoff in effect"),
+            &DstUnreachable::Unknown(id) =>
+                write!(f, "{}", id)
+        }
+    }
+}
+
 enum_with_unknown! {
     /// Internet protocol control message subtype for type "Redirect Message".
     pub doc enum Redirect(u8) {
@@ -135,6 +177,8 @@ mod field {
     pub const CODE:       usize = 1;
     pub const CHECKSUM:   Field = 2..4;
 
+    pub const UNUSED:     Field = 4..8;
+
     pub const ECHO_IDENT: Field = 4..6;
     pub const ECHO_SEQNO: Field = 6..8;
 }
@@ -206,8 +250,9 @@ impl<T: AsRef<[u8]>> Packet<T> {
     /// The result depends on the value of the message type field.
     pub fn header_len(&self) -> usize {
         match self.msg_type() {
-            Message::EchoRequest => field::ECHO_SEQNO.end,
-            Message::EchoReply   => field::ECHO_SEQNO.end,
+            Message::EchoRequest    => field::ECHO_SEQNO.end,
+            Message::EchoReply      => field::ECHO_SEQNO.end,
+            Message::DstUnreachable => field::UNUSED.end,
             _ => field::CHECKSUM.end // make a conservative assumption
         }
     }
@@ -304,6 +349,12 @@ pub enum Repr<'a> {
         seq_no: u16,
         data:   &'a [u8]
     },
+    DstUnreachable {
+        reason: DstUnreachable,
+        header: Ipv4Repr,
+        length: usize,
+        data:   [u8; 8]
+    },
     #[doc(hidden)]
     __Nonexhaustive
 }
@@ -320,6 +371,7 @@ impl<'a> Repr<'a> {
                     data:   packet.data()
                 })
             },
+
             (Message::EchoReply, 0) => {
                 Ok(Repr::EchoReply {
                     ident:  packet.echo_ident(),
@@ -327,6 +379,25 @@ impl<'a> Repr<'a> {
                     data:   packet.data()
                 })
             },
+
+            (Message::DstUnreachable, code) => {
+                let ip_packet = try!(Ipv4Packet::new(packet.data()));
+                let ip_repr = try!(Ipv4Repr::parse(&ip_packet));
+
+                let mut data = [0; 8];
+                let payload = &packet.data()[ip_packet.header_len() as usize..];
+                if payload.len() < data.len() { return Err(Error::Truncated) }
+                data.copy_from_slice(&payload[0..8]);
+
+                let length = ip_packet.total_len() as usize - ip_packet.header_len() as usize;
+
+                Ok(Repr::DstUnreachable {
+                    reason: DstUnreachable::from(code),
+                    header: ip_repr,
+                    length: length,
+                    data:   data
+                })
+            }
             _ => Err(Error::Unrecognized)
         }
     }
@@ -338,6 +409,9 @@ impl<'a> Repr<'a> {
             &Repr::EchoReply { data, .. } => {
                 field::ECHO_SEQNO.end + data.len()
             },
+            &Repr::DstUnreachable { header, data, .. } => {
+                field::UNUSED.end + header.buffer_len() + data.len()
+            }
             &Repr::__Nonexhaustive => unreachable!()
         }
     }
@@ -349,18 +423,33 @@ impl<'a> Repr<'a> {
         match self {
             &Repr::EchoRequest { ident, seq_no, data } => {
                 packet.set_msg_type(Message::EchoRequest);
+                packet.set_msg_code(0);
                 packet.set_echo_ident(ident);
                 packet.set_echo_seq_no(seq_no);
                 let data_len = cmp::min(packet.data_mut().len(), data.len());
                 packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
             },
+
             &Repr::EchoReply { ident, seq_no, data } => {
                 packet.set_msg_type(Message::EchoReply);
+                packet.set_msg_code(0);
                 packet.set_echo_ident(ident);
                 packet.set_echo_seq_no(seq_no);
                 let data_len = cmp::min(packet.data_mut().len(), data.len());
                 packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
             },
+
+            &Repr::DstUnreachable { reason, header, length, data } => {
+                packet.set_msg_type(Message::DstUnreachable);
+                packet.set_msg_code(reason.into());
+
+                let mut ip_packet = Ipv4Packet::new(packet.data_mut())
+                                               .expect("undersized data");
+                header.emit(&mut ip_packet, length);
+                let mut payload = &mut ip_packet.into_inner()[header.buffer_len()..];
+                payload.copy_from_slice(&data[..])
+            }
+
             &Repr::__Nonexhaustive => unreachable!()
         }
         packet.fill_checksum()
@@ -371,10 +460,14 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match Repr::parse(self) {
             Ok(repr) => write!(f, "{}", repr),
-            _ => {
-                try!(write!(f, "ICMPv4 (unrecognized)"));
-                try!(write!(f, " type={} code={} cksum={:#04x}",
-                            self.msg_type(), self.msg_code(), self.checksum()));
+            Err(err) => {
+                try!(write!(f, "ICMPv4 ({})", err));
+                try!(write!(f, " type={:?}", self.msg_type()));
+                match self.msg_type() {
+                    Message::DstUnreachable =>
+                        try!(write!(f, " code={:?}", DstUnreachable::from(self.msg_code()))),
+                    _ => try!(write!(f, " code={}", self.msg_code()))
+                }
                 Ok(())
             }
         }
@@ -385,11 +478,14 @@ impl<'a> fmt::Display for Repr<'a> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             &Repr::EchoRequest { ident, seq_no, data } =>
-                write!(f, "ICMPv4 Echo Request id={} seq={} len={}",
+                write!(f, "ICMPv4 echo request id={} seq={} len={}",
                        ident, seq_no, data.len()),
             &Repr::EchoReply { ident, seq_no, data } =>
-                write!(f, "ICMPv4 Echo Reply id={} seq={} len={}",
+                write!(f, "ICMPv4 echo reply id={} seq={} len={}",
                        ident, seq_no, data.len()),
+            &Repr::DstUnreachable { reason, .. } =>
+                write!(f, "ICMPv4 destination unreachable ({})",
+                       reason),
             &Repr::__Nonexhaustive => unreachable!()
         }
     }
@@ -400,14 +496,21 @@ use super::pretty_print::{PrettyPrint, PrettyIndent};
 impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
     fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
                     indent: &mut PrettyIndent) -> fmt::Result {
-        match Packet::new(buffer) {
-            Err(err)   => write!(f, "{}({})\n", indent, err),
-            Ok(packet) => write!(f, "{}{}\n", indent, packet)
+        let packet = match Packet::new(buffer) {
+            Err(err)   => return write!(f, "{}({})\n", indent, err),
+            Ok(packet) => packet
+        };
+        try!(write!(f, "{}{}\n", indent, packet));
+
+        indent.increase();
+        match packet.msg_type() {
+            Message::DstUnreachable =>
+                super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent),
+            _ => Ok(())
         }
     }
 }
 
-
 #[cfg(test)]
 mod test {
     use super::*;

+ 1 - 1
src/wire/ip.rs

@@ -86,7 +86,7 @@ pub struct Endpoint {
 impl Endpoint {
     pub const INVALID: Endpoint = Endpoint { addr: Address::Invalid, port: 0 };
 
-    /// Create an internet endpoint address.
+    /// Create an endpoint address from given address and port.
     pub fn new(addr: Address, port: u16) -> Endpoint {
         Endpoint { addr: addr, port: port }
     }

+ 5 - 5
src/wire/ipv4.rs

@@ -401,7 +401,7 @@ impl Repr {
     pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>,
                                               payload_len: usize) {
         packet.set_version(4);
-        packet.set_header_len(20);
+        packet.set_header_len(field::DST_ADDR.end as u8);
         packet.set_dscp(0);
         packet.set_ecn(0);
         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> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match Repr::parse(self) {
             Ok(repr) => write!(f, "{}", repr),
-            _ => {
-                try!(write!(f, "IPv4 (unrecognized)"));
+            Err(err) => {
+                try!(write!(f, "IPv4 ({})", err));
                 try!(write!(f, " src={} dst={} proto={} ttl={}",
                             self.src_addr(), self.dst_addr(), self.protocol(), self.ttl()));
                 if self.version() != 4 {
@@ -471,12 +471,12 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
     fn pretty_print(buffer: &AsRef<[u8]>, f: &mut fmt::Formatter,
                     indent: &mut PrettyIndent) -> fmt::Result {
         let packet = match Packet::new(buffer) {
-            Err(err)  => return write!(f, "{}({})\n", indent, err),
+            Err(err)   => return write!(f, "{}({})\n", indent, err),
             Ok(packet) => packet
         };
         try!(write!(f, "{}{}\n", indent, packet));
-        indent.increase();
 
+        indent.increase();
         match packet.protocol() {
             Protocol::Icmp =>
                 super::Icmpv4Packet::<&[u8]>::pretty_print(&packet.payload(), f, indent),

+ 2 - 1
src/wire/tcp.rs

@@ -462,7 +462,7 @@ impl<'a> Repr<'a> {
         packet.set_seq_number(self.seq_number);
         packet.set_ack_number(self.ack_number.unwrap_or(0));
         packet.set_window_len(self.window_len);
-        packet.set_header_len(20);
+        packet.set_header_len(field::URGENT.end as u8);
         packet.clear_flags();
         match self.control {
             Control::None => (),
@@ -470,6 +470,7 @@ impl<'a> Repr<'a> {
             Control::Fin  => packet.set_fin(true),
             Control::Rst  => packet.set_rst(true)
         }
+        packet.set_ack(self.ack_number.is_some());
         packet.payload_mut().copy_from_slice(self.payload);
         packet.fill_checksum(src_addr, dst_addr)
     }