Forráskód Böngészése

Add Repr support for NDISC packets

Add Representation layer support for NDISC packets.
Dan Robertson 7 éve
szülő
commit
7b911267ef
3 módosított fájl, 210 hozzáadás és 5 törlés
  1. 24 0
      src/wire/icmpv6.rs
  2. 3 0
      src/wire/mod.rs
  3. 183 5
      src/wire/ndisc.rs

+ 24 - 0
src/wire/icmpv6.rs

@@ -5,6 +5,7 @@ use {Error, Result};
 use phy::ChecksumCapabilities;
 use super::ip::checksum;
 use super::{Ipv6Packet, Ipv6Repr};
+use super::NdiscRepr;
 
 enum_with_unknown! {
     /// Internet protocol control message type.
@@ -43,6 +44,18 @@ impl Message {
     pub fn is_error(&self) -> bool {
         (u8::from(*self) & 0x80) != 0x80
     }
+
+    /// Return a boolean value indicating if the given message type
+    /// is an [NDISC] message type.
+    ///
+    /// [NDISC]: https://tools.ietf.org/html/rfc4861
+    pub fn is_ndisc(&self) -> bool {
+        match *self {
+            Message::RouterSolicit | Message::RouterAdvert | Message::NeighborSolicit |
+            Message::NeighborAdvert | Message::Redirect => true,
+            _ => false,
+        }
+    }
 }
 
 impl fmt::Display for Message {
@@ -462,6 +475,7 @@ pub enum Repr<'a> {
         seq_no: u16,
         data:   &'a [u8]
     },
+    Ndisc(NdiscRepr<'a>),
     #[doc(hidden)]
     __Nonexhaustive
 }
@@ -539,6 +553,9 @@ impl<'a> Repr<'a> {
                     data:   packet.payload()
                 })
             },
+            (msg_type, 0) if msg_type.is_ndisc() => {
+                NdiscRepr::parse(packet).map(|repr| Repr::Ndisc(repr))
+            },
             _ => Err(Error::Unrecognized)
         }
     }
@@ -554,6 +571,9 @@ impl<'a> Repr<'a> {
             &Repr::EchoReply { data, .. } => {
                 field::ECHO_SEQNO.end + data.len()
             },
+            &Repr::Ndisc(ndisc) => {
+                ndisc.buffer_len()
+            },
             &Repr::__Nonexhaustive => unreachable!()
         }
     }
@@ -618,6 +638,10 @@ impl<'a> Repr<'a> {
                 packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
             },
 
+            &Repr::Ndisc(ndisc) => {
+                ndisc.emit(packet)
+            },
+
             &Repr::__Nonexhaustive => unreachable!(),
         }
 

+ 3 - 0
src/wire/mod.rs

@@ -179,6 +179,9 @@ pub use self::icmpv6::{Message as Icmpv6Message,
 pub use self::icmpv6::{RouterFlags as NdiscRouterFlags,
                        NeighborFlags as NdiscNeighborFlags};
 
+#[cfg(feature = "proto-ipv6")]
+pub use self::ndisc::Repr as NdiscRepr;
+
 #[cfg(feature = "proto-ipv6")]
 pub use self::ndiscoption::{NdiscOption,
                             Repr as NdiscOptionRepr,

+ 183 - 5
src/wire/ndisc.rs

@@ -1,6 +1,7 @@
 use byteorder::{ByteOrder, NetworkEndian};
 
-use super::icmpv6::*;
+use {Error, Result};
+use super::icmpv6::{field, Message, NeighborFlags, Packet, RouterFlags};
 use time::Duration;
 use super::Ipv6Address;
 
@@ -180,16 +181,176 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
     }
 }
 
+/// A high-level representation of an Neighbor Discovery packet header.
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Repr<'a> {
+    RouterSolicit {
+        options:   &'a [u8]
+    },
+    RouterAdvert {
+        hop_limit: u8,
+        flags: RouterFlags,
+        router_lifetime: Duration,
+        reachable_time: Duration,
+        retrans_time: Duration,
+        options:   &'a [u8]
+    },
+    NeighborSolicit {
+        target_addr: Ipv6Address,
+        options:   &'a [u8]
+    },
+    NeighborAdvert {
+        flags: NeighborFlags,
+        target_addr: Ipv6Address,
+        options:   &'a [u8]
+    },
+    Redirect {
+        target_addr: Ipv6Address,
+        dest_addr: Ipv6Address,
+        options:   &'a [u8]
+    }
+}
+
+impl<'a> Repr<'a> {
+    /// Parse an NDISC packet and return a high-level representation of the
+    /// packet.
+    pub fn parse<T>(packet: &Packet<&'a T>)
+                   -> Result<Repr<'a>>
+                where T: AsRef<[u8]> + ?Sized {
+        match packet.msg_type() {
+            Message::RouterSolicit => {
+                Ok(Repr::RouterSolicit {
+                    options: packet.payload()
+                })
+            },
+            Message::RouterAdvert => {
+                Ok(Repr::RouterAdvert {
+                    hop_limit: packet.current_hop_limit(),
+                    flags: packet.router_flags(),
+                    router_lifetime: packet.router_lifetime(),
+                    reachable_time: packet.reachable_time(),
+                    retrans_time: packet.retrans_time(),
+                    options: packet.payload()
+                })
+            },
+            Message::NeighborSolicit => {
+                Ok(Repr::NeighborSolicit {
+                    target_addr: packet.target_addr(),
+                    options: packet.payload()
+                })
+            },
+            Message::NeighborAdvert => {
+                Ok(Repr::NeighborAdvert {
+                    flags: packet.neighbor_flags(),
+                    target_addr: packet.target_addr(),
+                    options: packet.payload()
+                })
+            },
+            Message::Redirect => {
+                Ok(Repr::Redirect {
+                    target_addr: packet.target_addr(),
+                    dest_addr: packet.dest_addr(),
+                    options: packet.payload()
+                })
+            },
+            _ => Err(Error::Unrecognized)
+        }
+    }
+
+    pub fn buffer_len(&self) -> usize {
+        match self {
+            &Repr::RouterSolicit { options, .. } => {
+                field::UNUSED.end + options.len()
+            },
+            &Repr::RouterAdvert { options, .. } => {
+                field::RETRANS_TM.end + options.len()
+            },
+            &Repr::NeighborSolicit { options, .. } | &Repr::NeighborAdvert { options, .. } => {
+                field::TARGET_ADDR.end + options.len()
+            },
+            &Repr::Redirect { options, .. } => {
+                field::DEST_ADDR.end + options.len()
+            }
+        }
+    }
+
+    pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
+            where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
+        match self {
+            &Repr::RouterSolicit { options } => {
+                packet.set_msg_type(Message::RouterSolicit);
+                packet.set_msg_code(0);
+                packet.clear_reserved();
+                packet.payload_mut().copy_from_slice(&options[..]);
+            },
+
+            &Repr::RouterAdvert { hop_limit, flags, router_lifetime, reachable_time, retrans_time, options } => {
+                packet.set_msg_type(Message::RouterAdvert);
+                packet.set_msg_code(0);
+                packet.set_current_hop_limit(hop_limit);
+                packet.set_router_flags(flags);
+                packet.set_router_lifetime(router_lifetime);
+                packet.set_reachable_time(reachable_time);
+                packet.set_retrans_time(retrans_time);
+                packet.payload_mut().copy_from_slice(&options[..]);
+            },
+
+            &Repr::NeighborSolicit { target_addr, options } => {
+                packet.set_msg_type(Message::NeighborSolicit);
+                packet.set_msg_code(0);
+                packet.clear_reserved();
+                packet.set_target_addr(target_addr);
+                packet.payload_mut().copy_from_slice(&options[..]);
+            },
+
+            &Repr::NeighborAdvert { flags, target_addr, options } => {
+                packet.set_msg_type(Message::NeighborAdvert);
+                packet.set_msg_code(0);
+                packet.clear_reserved();
+                packet.set_neighbor_flags(flags);
+                packet.set_target_addr(target_addr);
+                packet.payload_mut().copy_from_slice(&options[..]);
+            },
+
+            &Repr::Redirect { target_addr, dest_addr, options } => {
+                packet.set_msg_type(Message::Redirect);
+                packet.set_msg_code(0);
+                packet.clear_reserved();
+                packet.set_target_addr(target_addr);
+                packet.set_dest_addr(dest_addr);
+                packet.payload_mut().copy_from_slice(&options[..]);
+            },
+        }
+    }
+}
 
 #[cfg(test)]
 mod test {
+    use phy::ChecksumCapabilities;
     use super::*;
+    use wire::Icmpv6Repr;
 
-    static ROUTER_ADVERT_BYTES: [u8; 16] =
-        [0x86, 0x00, 0x2e, 0xf3,
+    static ROUTER_ADVERT_BYTES: [u8; 24] =
+        [0x86, 0x00, 0xa7, 0x35,
          0x40, 0x80, 0x03, 0x84,
          0x00, 0x00, 0x03, 0x84,
-         0x00, 0x00, 0x03, 0x84];
+         0x00, 0x00, 0x03, 0x84,
+         0x01, 0x01, 0x52, 0x54,
+         0x00, 0x12, 0x34, 0x56];
+    static SOURCE_LINK_LAYER_OPT: [u8; 8] =
+        [0x01, 0x01, 0x52, 0x54,
+         0x00, 0x12, 0x34, 0x56];
+
+    fn create_repr<'a>() -> Icmpv6Repr<'a> {
+        Icmpv6Repr::Ndisc(Repr::RouterAdvert {
+            hop_limit: 64,
+            flags: RouterFlags::MANAGED,
+            router_lifetime: Duration::from_secs(900),
+            reachable_time: Duration::from_millis(900),
+            retrans_time: Duration::from_millis(900),
+            options: &SOURCE_LINK_LAYER_OPT[..]
+        })
+    }
 
     #[test]
     fn test_router_advert_deconstruct() {
@@ -201,11 +362,12 @@ mod test {
         assert_eq!(packet.router_lifetime(), Duration::from_secs(900));
         assert_eq!(packet.reachable_time(), Duration::from_millis(900));
         assert_eq!(packet.retrans_time(), Duration::from_millis(900));
+        assert_eq!(packet.payload(), &SOURCE_LINK_LAYER_OPT[..]);
     }
 
     #[test]
     fn test_router_advert_construct() {
-        let mut bytes = vec![0x0; 16];
+        let mut bytes = vec![0x0; 24];
         let mut packet = Packet::new(&mut bytes);
         packet.set_msg_type(Message::RouterAdvert);
         packet.set_msg_code(0);
@@ -214,7 +376,23 @@ mod test {
         packet.set_router_lifetime(Duration::from_secs(900));
         packet.set_reachable_time(Duration::from_millis(900));
         packet.set_retrans_time(Duration::from_millis(900));
+        packet.payload_mut().copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
         packet.fill_checksum();
         assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
     }
+
+    #[test]
+    fn test_router_advert_repr_parse() {
+        let packet = Packet::new(&ROUTER_ADVERT_BYTES[..]);
+        assert_eq!(Icmpv6Repr::parse(&packet, &ChecksumCapabilities::default()).unwrap(),
+                   create_repr());
+    }
+
+    #[test]
+    fn test_router_advert_repr_emit() {
+        let mut bytes = vec![0x2a; 24];
+        let mut packet = Packet::new(&mut bytes[..]);
+        create_repr().emit(&mut packet, &ChecksumCapabilities::default());
+        assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
+    }
 }