Browse Source

iface: Begin parsing Hop-By-Hop extension headers

Begin add support for parsing extension headers.

Closes: #212
Approved by: whitequark
Dan Robertson 7 years ago
parent
commit
2363f97834
2 changed files with 83 additions and 13 deletions
  1. 81 12
      src/iface/ethernet.rs
  2. 2 1
      src/wire/mod.rs

+ 81 - 12
src/iface/ethernet.rs

@@ -1,5 +1,6 @@
 // Heads up! Before working on this file you should read the parts
-// of RFC 1122 that discuss Ethernet, ARP and IP.
+// of RFC 1122 that discuss Ethernet, ARP and IP for any IPv4 work
+// and RFCs 8200 and 4861 for any IPv6 and NDISC work.
 
 use core::cmp;
 use managed::ManagedSlice;
@@ -23,6 +24,10 @@ 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::{Ipv6HopByHopHeader, Ipv6HopByHopRepr};
+#[cfg(feature = "proto-ipv6")]
+use wire::{Ipv6OptionRepr, Ipv6OptionFailureType};
+#[cfg(feature = "proto-ipv6")]
 use wire::{NdiscNeighborFlags, NdiscRepr};
 #[cfg(all(feature = "proto-ipv6", feature = "socket-udp"))]
 use wire::Icmpv6DstUnreachable;
@@ -690,23 +695,39 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
             }
         }
 
-        let ip_repr = IpRepr::Ipv6(ipv6_repr);
         let ip_payload = ipv6_packet.payload();
 
         #[cfg(feature = "socket-raw")]
-        let handled_by_raw_socket = self.raw_socket_filter(sockets, &ip_repr, ip_payload);
+        let handled_by_raw_socket = self.raw_socket_filter(sockets, &ipv6_repr.into(), ip_payload);
+        #[cfg(not(feature = "socket-raw"))]
+        let handled_by_raw_socket = false;
 
-        match ipv6_repr.next_header {
+        self.process_nxt_hdr(sockets, timestamp, ipv6_repr, ipv6_repr.next_header,
+                             handled_by_raw_socket, ip_payload)
+    }
+
+    /// Given the next header value forward the payload onto the correct process
+    /// function.
+    #[cfg(feature = "proto-ipv6")]
+    fn process_nxt_hdr<'frame>
+                   (&mut self, sockets: &mut SocketSet, timestamp: Instant, ipv6_repr: Ipv6Repr,
+                    nxt_hdr: IpProtocol, handled_by_raw_socket: bool, ip_payload: &'frame [u8])
+                   -> Result<Packet<'frame>>
+    {
+        match nxt_hdr {
             IpProtocol::Icmpv6 =>
-                self.process_icmpv6(sockets, timestamp, ip_repr, ip_payload),
+                self.process_icmpv6(sockets, timestamp, ipv6_repr.into(), ip_payload),
 
             #[cfg(feature = "socket-udp")]
             IpProtocol::Udp =>
-                self.process_udp(sockets, ip_repr, ip_payload),
+                self.process_udp(sockets, ipv6_repr.into(), ip_payload),
 
             #[cfg(feature = "socket-tcp")]
             IpProtocol::Tcp =>
-                self.process_tcp(sockets, timestamp, ip_repr, ip_payload),
+                self.process_tcp(sockets, timestamp, ipv6_repr.into(), ip_payload),
+
+            IpProtocol::HopByHop =>
+                self.process_hopbyhop(sockets, timestamp, ipv6_repr, handled_by_raw_socket, ip_payload),
 
             #[cfg(feature = "socket-raw")]
             _ if handled_by_raw_socket =>
@@ -906,6 +927,37 @@ impl<'b, 'c> InterfaceInner<'b, 'c> {
         packet
     }
 
+    #[cfg(feature = "proto-ipv6")]
+    fn process_hopbyhop<'frame>(&mut self, sockets: &mut SocketSet, timestamp: Instant,
+                                ipv6_repr: Ipv6Repr, handled_by_raw_socket: bool,
+                                ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
+    {
+        let hbh_pkt = Ipv6HopByHopHeader::new_checked(ip_payload)?;
+        let hbh_repr = Ipv6HopByHopRepr::parse(&hbh_pkt)?;
+        for result in hbh_repr.options() {
+            let opt_repr = result?;
+            match opt_repr {
+                Ipv6OptionRepr::Pad1 | Ipv6OptionRepr::PadN(_) => (),
+                Ipv6OptionRepr::Unknown { type_, .. } => {
+                    match Ipv6OptionFailureType::from(type_) {
+                        Ipv6OptionFailureType::Skip => (),
+                        Ipv6OptionFailureType::Discard => {
+                            return Ok(Packet::None);
+                        },
+                        _ => {
+                            // FIXME(dlrobertson): Send an ICMPv6 parameter problem message
+                            // here.
+                            return Err(Error::Unrecognized);
+                        }
+                    }
+                }
+                _ => return Err(Error::Unrecognized),
+            }
+        }
+        self.process_nxt_hdr(sockets, timestamp, ipv6_repr, hbh_repr.next_header,
+                             handled_by_raw_socket, &ip_payload[hbh_repr.buffer_len()..])
+    }
+
     #[cfg(feature = "proto-ipv4")]
     fn process_icmpv4<'frame>(&self, _sockets: &mut SocketSet, ip_repr: IpRepr,
                               ip_payload: &'frame [u8]) -> Result<Packet<'frame>>
@@ -1378,6 +1430,8 @@ mod test {
     use wire::{Icmpv6Packet, Icmpv6Repr, Icmpv6ParamProblem};
     #[cfg(feature = "proto-ipv6")]
     use wire::{NdiscNeighborFlags, NdiscRepr};
+    #[cfg(feature = "proto-ipv6")]
+    use wire::{Ipv6HopByHopHeader, Ipv6Option, Ipv6OptionRepr};
 
     use super::Packet;
 
@@ -1944,25 +1998,40 @@ mod test {
         let remote_ip_addr = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1);
         let remote_hw_addr = EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x01]);
 
-        let mut eth_bytes = vec![0; 58];
+        let mut eth_bytes = vec![0; 66];
         let payload = [0x12, 0x34, 0x56, 0x78];
 
         let ipv6_repr = Ipv6Repr {
             src_addr:    remote_ip_addr,
             dst_addr:    Ipv6Address::LOOPBACK,
-            next_header: IpProtocol::Unknown(0x0c),
-            payload_len: 4,
+            next_header: IpProtocol::HopByHop,
+            payload_len: 12,
             hop_limit:   0x40,
         };
-        let ip_repr = IpRepr::Ipv6(ipv6_repr);
 
         let frame = {
             let mut frame = EthernetFrame::new(&mut eth_bytes);
+            let ip_repr = IpRepr::Ipv6(ipv6_repr);
             frame.set_dst_addr(EthernetAddress([0x52, 0x54, 0x00, 0x00, 0x00, 0x00]));
             frame.set_src_addr(remote_hw_addr);
             frame.set_ethertype(EthernetProtocol::Ipv6);
             ip_repr.emit(frame.payload_mut(), &ChecksumCapabilities::default());
-            frame.payload_mut()[ip_repr.buffer_len()..].copy_from_slice(&payload);
+            let mut offset = ipv6_repr.buffer_len();
+            {
+                let mut hbh_pkt = Ipv6HopByHopHeader::new(&mut frame.payload_mut()[offset..]);
+                hbh_pkt.set_next_header(IpProtocol::Unknown(0x0c));
+                hbh_pkt.set_header_len(0);
+                offset += 8;
+                {
+                    let mut pad_pkt = Ipv6Option::new(&mut hbh_pkt.options_mut()[..]);
+                    Ipv6OptionRepr::PadN(3).emit(&mut pad_pkt);
+                }
+                {
+                    let mut pad_pkt = Ipv6Option::new(&mut hbh_pkt.options_mut()[5..]);
+                    Ipv6OptionRepr::Pad1.emit(&mut pad_pkt);
+                }
+            }
+            frame.payload_mut()[offset..].copy_from_slice(&payload);
             EthernetFrame::new(&*frame.into_inner())
         };
 

+ 2 - 1
src/wire/mod.rs

@@ -147,7 +147,8 @@ pub use self::ipv6::{Address as Ipv6Address,
 #[cfg(feature = "proto-ipv6")]
 pub use self::ipv6option::{Ipv6Option,
                            Repr as Ipv6OptionRepr,
-                           Type as Ipv6OptionType};
+                           Type as Ipv6OptionType,
+                           FailureType as Ipv6OptionFailureType};
 
 #[cfg(feature = "proto-ipv6")]
 pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader,