瀏覽代碼

Merge #636

636: Update 6LoWPAN fuzzer and apply fixes r=Dirbaio a=thvdveld

This PR updates the fuzzer for 6LoWPAN. Now, every packet that smoltcp knows and can be used by 6LoWPAN is fuzzed. I ran it for 60 minutes and nothing failed after applying all the fixes.

## Fixes

- 14d52219347f642bdd3a6545e148ec5cf8acd738 Index was out of bounds for the `0b01` case.
- b0e4050659f63e7537b6b774f56286b362a416fc Address resolving for 6LoWPAN still contained some `unwraps`.
- c02559121c04efb457226c702f21803cd6521092 Fix multiplication overflow in IPv6 HopByHop and IPv6 Routing headers.
- 3057ee5b30825396d19689721723b17ff36a0631 The length field did not actually match the length of the packet. The length field is used for calculating offsets for the getters/setters, and thus resulting in incorrect offsets when the length field is wrong.
- 14d5d03e557e4e9c5ee3e77fcf5ca7f54003b559 and 7f73afa6416f9a32e0038547729e8e55579f1dfb Length fields of 0 are invalid in Ndisc options.
- 054ebd8f1bbb2aadf83335d306ef8dd660ca534f The offsets for `SourceLinkLayerAddr` and `TargetLinkLayerAddr` Ndisc Options only considered Ethernet addresses.
- eb3846da17b4ce37448f7c88159f3eb68bdb689d Fix `check_length` function for 6LoWPAN compressed extension headers.
- 7ee06df264affab79a9c7208d2293c98ca1c0150 The `RedirectHeader` emit/parse functions were wrong.
- 9ddb63a6e532529f3f8bf35f2f6818a875bfa345 The MLD header `buffer_length` function did not consider the length of the data.

Co-authored-by: Thibaut Vandervelden <thvdveld@vub.be>
bors[bot] 2 年之前
父節點
當前提交
a6f6a6142f

+ 2 - 8
fuzz/Cargo.toml

@@ -43,13 +43,7 @@ test = false
 doc = false
 
 [[bin]]
-name = "sixlowpan_udp_header"
-path = "fuzz_targets/sixlowpan_udp_header.rs"
-test = false
-doc = false
-
-[[bin]]
-name = "sixlowpan_iphc_header"
-path = "fuzz_targets/sixlowpan_iphc_header.rs"
+name = "sixlowpan_packet"
+path = "fuzz_targets/sixlowpan_packet.rs"
 test = false
 doc = false

+ 0 - 42
fuzz/fuzz_targets/sixlowpan_iphc_header.rs

@@ -1,42 +0,0 @@
-#![no_main]
-use libfuzzer_sys::fuzz_target;
-use smoltcp::wire::{Ieee802154Address, SixlowpanIphcPacket, SixlowpanIphcRepr};
-
-#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)]
-pub enum AddressFuzzer {
-    Absent,
-    Short([u8; 2]),
-    Extended([u8; 8]),
-}
-
-impl From<AddressFuzzer> for Ieee802154Address {
-    fn from(val: AddressFuzzer) -> Self {
-        match val {
-            AddressFuzzer::Absent => Ieee802154Address::Absent,
-            AddressFuzzer::Short(b) => Ieee802154Address::Short(b),
-            AddressFuzzer::Extended(b) => Ieee802154Address::Extended(b),
-        }
-    }
-}
-
-#[derive(Debug, arbitrary::Arbitrary)]
-struct SixlowpanIphcPacketFuzzer<'a> {
-    data: &'a [u8],
-    ll_src_addr: Option<AddressFuzzer>,
-    ll_dst_addr: Option<AddressFuzzer>,
-}
-
-fuzz_target!(|fuzz: SixlowpanIphcPacketFuzzer| {
-    if let Ok(ref frame) = SixlowpanIphcPacket::new_checked(fuzz.data) {
-        if let Ok(repr) = SixlowpanIphcRepr::parse(
-            frame,
-            fuzz.ll_src_addr.map(Into::into),
-            fuzz.ll_dst_addr.map(Into::into),
-        ) {
-            let mut buffer = vec![0; repr.buffer_len()];
-
-            let mut frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]);
-            repr.emit(&mut frame);
-        }
-    };
-});

+ 240 - 0
fuzz/fuzz_targets/sixlowpan_packet.rs

@@ -0,0 +1,240 @@
+#![no_main]
+use libfuzzer_sys::fuzz_target;
+use smoltcp::{phy::ChecksumCapabilities, wire::*};
+
+#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)]
+pub enum AddressFuzzer {
+    Absent,
+    Short([u8; 2]),
+    Extended([u8; 8]),
+}
+
+impl From<AddressFuzzer> for Ieee802154Address {
+    fn from(val: AddressFuzzer) -> Self {
+        match val {
+            AddressFuzzer::Absent => Ieee802154Address::Absent,
+            AddressFuzzer::Short(b) => Ieee802154Address::Short(b),
+            AddressFuzzer::Extended(b) => Ieee802154Address::Extended(b),
+        }
+    }
+}
+
+#[derive(Debug, arbitrary::Arbitrary)]
+struct SixlowpanPacketFuzzer<'a> {
+    data: &'a [u8],
+    ll_src_addr: Option<AddressFuzzer>,
+    ll_dst_addr: Option<AddressFuzzer>,
+}
+
+fuzz_target!(|fuzz: SixlowpanPacketFuzzer| {
+    match SixlowpanPacket::dispatch(fuzz.data) {
+        Ok(SixlowpanPacket::FragmentHeader) => {
+            if let Ok(frame) = SixlowpanFragPacket::new_checked(fuzz.data) {
+                if let Ok(repr) = SixlowpanFragRepr::parse(&frame) {
+                    let mut buffer = vec![0; repr.buffer_len()];
+                    let mut frame = SixlowpanFragPacket::new_unchecked(&mut buffer[..]);
+                    repr.emit(&mut frame);
+                }
+            }
+        }
+        Ok(SixlowpanPacket::IphcHeader) => {
+            if let Ok(frame) = SixlowpanIphcPacket::new_checked(fuzz.data) {
+                if let Ok(iphc_repr) = SixlowpanIphcRepr::parse(
+                    &frame,
+                    fuzz.ll_src_addr.map(Into::into),
+                    fuzz.ll_dst_addr.map(Into::into),
+                ) {
+                    let mut buffer = vec![0; iphc_repr.buffer_len()];
+                    let mut iphc_frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]);
+                    iphc_repr.emit(&mut iphc_frame);
+
+                    let payload = frame.payload();
+                    match iphc_repr.next_header {
+                        SixlowpanNextHeader::Compressed => {
+                            if let Ok(p) = SixlowpanNhcPacket::dispatch(payload) {
+                                match p {
+                                    SixlowpanNhcPacket::ExtHeader => {
+                                        if let Ok(frame) =
+                                            SixlowpanExtHeaderPacket::new_checked(payload)
+                                        {
+                                            if let Ok(repr) = SixlowpanExtHeaderRepr::parse(&frame)
+                                            {
+                                                let mut buffer = vec![0; repr.buffer_len()];
+                                                let mut ext_header_frame =
+                                                    SixlowpanExtHeaderPacket::new_unchecked(
+                                                        &mut buffer[..],
+                                                    );
+                                                repr.emit(&mut ext_header_frame);
+                                            }
+                                        }
+                                    }
+                                    SixlowpanNhcPacket::UdpHeader => {
+                                        if let Ok(frame) =
+                                            SixlowpanUdpNhcPacket::new_checked(payload)
+                                        {
+                                            if let Ok(repr) = SixlowpanUdpNhcRepr::parse(
+                                                &frame,
+                                                &iphc_repr.src_addr,
+                                                &iphc_repr.dst_addr,
+                                            ) {
+                                                let mut buffer = vec![
+                                                    0;
+                                                    repr.header_len()
+                                                        + frame.payload().len()
+                                                ];
+                                                let mut udp_packet =
+                                                    SixlowpanUdpNhcPacket::new_unchecked(
+                                                        &mut buffer[..],
+                                                    );
+                                                repr.emit(
+                                                    &mut udp_packet,
+                                                    &iphc_repr.src_addr,
+                                                    &iphc_repr.dst_addr,
+                                                    frame.payload().len(),
+                                                    |b| b.copy_from_slice(frame.payload()),
+                                                );
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        SixlowpanNextHeader::Uncompressed(proto) => match proto {
+                            IpProtocol::HopByHop => {
+                                if let Ok(frame) = Ipv6HopByHopHeader::new_checked(payload) {
+                                    if let Ok(repr) = Ipv6HopByHopRepr::parse(&frame) {
+                                        let mut buffer = vec![0; repr.buffer_len()];
+                                        let mut hop_by_hop_frame =
+                                            Ipv6HopByHopHeader::new_unchecked(&mut buffer[..]);
+                                        repr.emit(&mut hop_by_hop_frame);
+                                    }
+                                }
+                            }
+                            IpProtocol::Icmp => {
+                                if let Ok(frame) = Icmpv4Packet::new_checked(payload) {
+                                    if let Ok(repr) =
+                                        Icmpv4Repr::parse(&frame, &ChecksumCapabilities::default())
+                                    {
+                                        let mut buffer = vec![0; repr.buffer_len()];
+                                        let mut icmpv4_packet =
+                                            Icmpv4Packet::new_unchecked(&mut buffer[..]);
+                                        repr.emit(
+                                            &mut icmpv4_packet,
+                                            &ChecksumCapabilities::default(),
+                                        );
+                                    }
+                                }
+                            }
+                            IpProtocol::Igmp => {
+                                if let Ok(frame) = IgmpPacket::new_checked(payload) {
+                                    if let Ok(repr) = IgmpRepr::parse(&frame) {
+                                        let mut buffer = vec![0; repr.buffer_len()];
+                                        let mut frame = IgmpPacket::new_unchecked(&mut buffer[..]);
+                                        repr.emit(&mut frame);
+                                    }
+                                }
+                            }
+                            IpProtocol::Tcp => {
+                                if let Ok(frame) = TcpPacket::new_checked(payload) {
+                                    if let Ok(repr) = TcpRepr::parse(
+                                        &frame,
+                                        &iphc_repr.src_addr.into_address(),
+                                        &iphc_repr.dst_addr.into_address(),
+                                        &ChecksumCapabilities::default(),
+                                    ) {
+                                        let mut buffer = vec![0; repr.buffer_len()];
+                                        let mut frame = TcpPacket::new_unchecked(&mut buffer[..]);
+                                        repr.emit(
+                                            &mut frame,
+                                            &iphc_repr.src_addr.into_address(),
+                                            &iphc_repr.dst_addr.into_address(),
+                                            &ChecksumCapabilities::default(),
+                                        );
+                                    }
+                                }
+                            }
+                            IpProtocol::Udp => {
+                                if let Ok(frame) = UdpPacket::new_checked(payload) {
+                                    if let Ok(repr) = UdpRepr::parse(
+                                        &frame,
+                                        &iphc_repr.src_addr.into_address(),
+                                        &iphc_repr.dst_addr.into_address(),
+                                        &ChecksumCapabilities::default(),
+                                    ) {
+                                        let mut buffer =
+                                            vec![0; repr.header_len() + frame.payload().len()];
+                                        let mut packet = UdpPacket::new_unchecked(&mut buffer[..]);
+                                        repr.emit(
+                                            &mut packet,
+                                            &iphc_repr.src_addr.into_address(),
+                                            &iphc_repr.dst_addr.into_address(),
+                                            frame.payload().len(),
+                                            |b| b.copy_from_slice(frame.payload()),
+                                            &ChecksumCapabilities::default(),
+                                        );
+                                    }
+                                }
+                            }
+                            IpProtocol::Ipv6Route => {
+                                if let Ok(frame) = Ipv6RoutingHeader::new_checked(payload) {
+                                    if let Ok(repr) = Ipv6RoutingRepr::parse(&frame) {
+                                        let mut buffer = vec![0; repr.buffer_len()];
+                                        let mut packet = Ipv6RoutingHeader::new(&mut buffer[..]);
+                                        repr.emit(&mut packet);
+                                    }
+                                }
+                            }
+                            IpProtocol::Ipv6Frag => {
+                                if let Ok(frame) = Ipv6FragmentHeader::new_checked(payload) {
+                                    if let Ok(repr) = Ipv6FragmentRepr::parse(&frame) {
+                                        let mut buffer = vec![0; repr.buffer_len()];
+                                        let mut frame =
+                                            Ipv6FragmentHeader::new_unchecked(&mut buffer[..]);
+                                        repr.emit(&mut frame);
+                                    }
+                                }
+                            }
+                            IpProtocol::Icmpv6 => {
+                                if let Ok(packet) = Icmpv6Packet::new_checked(payload) {
+                                    if let Ok(repr) = Icmpv6Repr::parse(
+                                        &iphc_repr.src_addr.into_address(),
+                                        &iphc_repr.dst_addr.into_address(),
+                                        &packet,
+                                        &ChecksumCapabilities::default(),
+                                    ) {
+                                        let mut buffer = vec![0; repr.buffer_len()];
+                                        let mut packet =
+                                            Icmpv6Packet::new_unchecked(&mut buffer[..]);
+                                        repr.emit(
+                                            &iphc_repr.src_addr.into_address(),
+                                            &iphc_repr.dst_addr.into_address(),
+                                            &mut packet,
+                                            &ChecksumCapabilities::default(),
+                                        );
+                                    }
+                                }
+                            }
+                            IpProtocol::Ipv6NoNxt => (),
+                            IpProtocol::Ipv6Opts => {
+                                if let Ok(packet) = Ipv6Option::new_checked(payload) {
+                                    if let Ok(repr) = Ipv6OptionRepr::parse(&packet) {
+                                        let mut buffer = vec![0; repr.buffer_len()];
+                                        let mut packet = Ipv6Option::new_unchecked(&mut buffer[..]);
+                                        repr.emit(&mut packet);
+                                    }
+                                }
+                            }
+                            IpProtocol::Unknown(_) => (),
+                        },
+                    };
+
+                    let mut buffer = vec![0; iphc_repr.buffer_len()];
+
+                    let mut frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]);
+                    iphc_repr.emit(&mut frame);
+                }
+            };
+        }
+        Err(_) => (),
+    }
+});

+ 0 - 41
fuzz/fuzz_targets/sixlowpan_udp_header.rs

@@ -1,41 +0,0 @@
-#![no_main]
-use libfuzzer_sys::fuzz_target;
-use smoltcp::wire::{Ipv6Address, SixlowpanUdpNhcPacket, SixlowpanUdpNhcRepr};
-
-#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)]
-pub struct AddressFuzzer(pub [u8; 16]);
-
-impl From<AddressFuzzer> for Ipv6Address {
-    fn from(val: AddressFuzzer) -> Self {
-        Ipv6Address(val.0)
-    }
-}
-
-#[derive(Debug, arbitrary::Arbitrary)]
-struct SixlowpanUdpPacketFuzzer<'a> {
-    data: &'a [u8],
-    src_addr: AddressFuzzer,
-    dst_addr: AddressFuzzer,
-}
-
-fuzz_target!(|fuzz: SixlowpanUdpPacketFuzzer| {
-    if let Ok(ref frame) = SixlowpanUdpNhcPacket::new_checked(fuzz.data) {
-        if let Ok(repr) = SixlowpanUdpNhcRepr::parse(
-            frame,
-            &fuzz.src_addr.into(),
-            &fuzz.dst_addr.into(),
-        ) {
-            let payload = frame.payload();
-            let mut buffer = vec![0; repr.header_len() + payload.len()];
-
-            let mut frame = SixlowpanUdpNhcPacket::new_unchecked(&mut buffer[..]);
-            repr.emit(
-                &mut frame,
-                &fuzz.src_addr.into(),
-                &fuzz.dst_addr.into(),
-                payload.len(),
-                |b| b.copy_from_slice(payload),
-            );
-        }
-    };
-});

+ 2 - 2
src/wire/ipv6hopbyhop.rs

@@ -43,8 +43,8 @@ mod field {
     // Length of the header is in 8-octet units, not including the first 8 octets. The first two
     // octets are the next header type and the header length.
     pub fn OPTIONS(length_field: u8) -> Field {
-        let bytes = length_field * 8 + 8;
-        2..bytes as usize
+        let bytes = length_field as usize * 8 + 8;
+        2..bytes
     }
 }
 

+ 9 - 2
src/wire/ipv6routing.rs

@@ -92,8 +92,8 @@ mod field {
     // Length of the header is in 8-octet units, not including the first 8 octets. The first four
     // octets are the next header type, the header length, routing type and segments left.
     pub fn DATA(length_field: u8) -> Field {
-        let bytes = length_field * 8 + 8;
-        4..bytes as usize
+        let bytes = length_field as usize * 8 + 8;
+        4..bytes
     }
 
     // The Type 2 Routing Header has the following format:
@@ -173,6 +173,13 @@ impl<T: AsRef<[u8]>> Header<T> {
             return Err(Error);
         }
 
+        // The header lenght field could be wrong and thus we need to check this as well:
+        if matches!(self.routing_type(), Type::Type2)
+            && field::DATA(self.header_len()).end != field::HOME_ADDRESS.end
+        {
+            return Err(Error);
+        }
+
         Ok(())
     }
 

+ 2 - 2
src/wire/mld.rs

@@ -340,8 +340,8 @@ impl<'a> Repr<'a> {
     /// Return the length of a packet that will be emitted from this high-level representation.
     pub fn buffer_len(&self) -> usize {
         match self {
-            Repr::Query { .. } => field::QUERY_NUM_SRCS.end,
-            Repr::Report { .. } => field::NR_MCAST_RCRDS.end,
+            Repr::Query { data, .. } => field::QUERY_NUM_SRCS.end + data.len(),
+            Repr::Report { data, .. } => field::NR_MCAST_RCRDS.end + data.len(),
         }
     }
 

+ 35 - 28
src/wire/ndisc.rs

@@ -314,23 +314,26 @@ impl<'a> Repr<'a> {
                     let opt = NdiscOption::new_checked(&packet.payload()[offset..])?;
                     match opt.option_type() {
                         NdiscOptionType::SourceLinkLayerAddr => {
-                            lladdr = Some(opt.link_layer_addr());
-                            offset += 8;
+                            let addr = opt.link_layer_addr();
+                            offset += NdiscOptionRepr::SourceLinkLayerAddr(addr).buffer_len();
+                            lladdr = Some(addr);
                         }
                         NdiscOptionType::RedirectedHeader => {
-                            if opt.data_len() < 6 {
+                            let opt_data = opt.data();
+
+                            if opt.data_len() < 6 || opt_data.len() < offset + 8 {
                                 return Err(Error);
-                            } else {
-                                let ip_packet =
-                                    Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]);
-                                let ip_repr = Ipv6Repr::parse(&ip_packet)?;
-                                let data = &opt.data()[offset + 8 + ip_repr.buffer_len()..];
-                                redirected_hdr = Some(NdiscRedirectedHeader {
-                                    header: ip_repr,
-                                    data,
-                                });
-                                offset += 8 + ip_repr.buffer_len() + data.len();
-                            }
+                            };
+
+                            let ip_packet = Ipv6Packet::new_checked(&opt_data[offset + 8..])?;
+                            let ip_repr = Ipv6Repr::parse(&ip_packet)?;
+                            let data = ip_packet.payload();
+                            let redirected = NdiscRedirectedHeader {
+                                header: ip_repr,
+                                data,
+                            };
+                            offset += NdiscOptionRepr::RedirectedHeader(redirected).buffer_len();
+                            redirected_hdr = Some(redirected);
                         }
                         _ => {
                             return Err(Error);
@@ -351,7 +354,9 @@ impl<'a> Repr<'a> {
     pub fn buffer_len(&self) -> usize {
         match self {
             &Repr::RouterSolicit { lladdr } => match lladdr {
-                Some(_) => field::UNUSED.end + 8,
+                Some(addr) => {
+                    field::UNUSED.end + { NdiscOptionRepr::SourceLinkLayerAddr(addr).buffer_len() }
+                }
                 None => field::UNUSED.end,
             },
             &Repr::RouterAdvert {
@@ -361,14 +366,14 @@ impl<'a> Repr<'a> {
                 ..
             } => {
                 let mut offset = 0;
-                if lladdr.is_some() {
-                    offset += 8;
+                if let Some(lladdr) = lladdr {
+                    offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len();
                 }
-                if mtu.is_some() {
-                    offset += 8;
+                if let Some(mtu) = mtu {
+                    offset += NdiscOptionRepr::Mtu(mtu).buffer_len();
                 }
-                if prefix_info.is_some() {
-                    offset += 32;
+                if let Some(prefix_info) = prefix_info {
+                    offset += NdiscOptionRepr::PrefixInformation(prefix_info).buffer_len();
                 }
                 field::RETRANS_TM.end + offset
             }
@@ -384,14 +389,16 @@ impl<'a> Repr<'a> {
                 redirected_hdr,
                 ..
             } => {
-                let mut offset = 0;
-                if lladdr.is_some() {
-                    offset += 8;
+                let mut offset = field::DEST_ADDR.end;
+                if let Some(lladdr) = lladdr {
+                    offset += NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len();
                 }
                 if let Some(NdiscRedirectedHeader { header, data }) = redirected_hdr {
-                    offset += 8 + header.buffer_len() + data.len();
+                    offset +=
+                        NdiscOptionRepr::RedirectedHeader(NdiscRedirectedHeader { header, data })
+                            .buffer_len();
                 }
-                field::DEST_ADDR.end + offset
+                offset
             }
         }
     }
@@ -439,7 +446,7 @@ impl<'a> Repr<'a> {
                     let mut opt_pkt =
                         NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
                     NdiscOptionRepr::Mtu(mtu).emit(&mut opt_pkt);
-                    offset += 8;
+                    offset += NdiscOptionRepr::Mtu(mtu).buffer_len();
                 }
                 if let Some(prefix_info) = prefix_info {
                     let mut opt_pkt =
@@ -493,7 +500,7 @@ impl<'a> Repr<'a> {
                     Some(lladdr) => {
                         let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
                         NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
-                        8
+                        NdiscOptionRepr::TargetLinkLayerAddr(lladdr).buffer_len()
                     }
                     None => 0,
                 };

+ 31 - 18
src/wire/ndiscoption.rs

@@ -128,9 +128,7 @@ mod field {
     //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
     // Reserved bits.
-    pub const IP_RESERVED: Field = 4..8;
-    // Redirected header IP header + data.
-    pub const IP_DATA: usize = 8;
+    pub const REDIRECTED_RESERVED: Field = 2..8;
     pub const REDIR_MIN_SZ: usize = 48;
 
     // MTU Option fields
@@ -158,6 +156,12 @@ impl<T: AsRef<[u8]>> NdiscOption<T> {
     pub fn new_checked(buffer: T) -> Result<NdiscOption<T>> {
         let opt = Self::new_unchecked(buffer);
         opt.check_len()?;
+
+        // A data length field of 0 is invalid.
+        if opt.data_len() == 0 {
+            return Err(Error);
+        }
+
         Ok(opt)
     }
 
@@ -362,7 +366,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
     #[inline]
     pub fn clear_redirected_reserved(&mut self) {
         let data = self.buffer.as_mut();
-        NetworkEndian::write_u32(&mut data[field::IP_RESERVED], 0);
+        data[field::REDIRECTED_RESERVED].fill_with(|| 0);
     }
 }
 
@@ -462,11 +466,13 @@ impl<'a> Repr<'a> {
                 if opt.data_len() < 6 {
                     Err(Error)
                 } else {
-                    let ip_packet = Ipv6Packet::new_unchecked(&opt.data()[field::IP_DATA..]);
+                    let ip_packet =
+                        Ipv6Packet::new_unchecked(&opt.data()[field::REDIRECTED_RESERVED.len()..]);
                     let ip_repr = Ipv6Repr::parse(&ip_packet)?;
                     Ok(Repr::RedirectedHeader(RedirectedHeader {
                         header: ip_repr,
-                        data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..],
+                        data: &opt.data()
+                            [field::REDIRECTED_RESERVED.len() + ip_repr.buffer_len()..],
                     }))
                 }
             }
@@ -477,11 +483,18 @@ impl<'a> Repr<'a> {
                     Err(Error)
                 }
             }
-            Type::Unknown(id) => Ok(Repr::Unknown {
-                type_: id,
-                length: opt.data_len(),
-                data: opt.data(),
-            }),
+            Type::Unknown(id) => {
+                // A length of 0 is invalid.
+                if opt.data_len() != 0 {
+                    Ok(Repr::Unknown {
+                        type_: id,
+                        length: opt.data_len(),
+                        data: opt.data(),
+                    })
+                } else {
+                    Err(Error)
+                }
+            }
         }
     }
 
@@ -495,7 +508,7 @@ impl<'a> Repr<'a> {
             }
             &Repr::PrefixInformation(_) => field::PREFIX.end,
             &Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
-                field::IP_DATA + header.buffer_len() + data.len()
+                (8 + header.buffer_len() + data.len() + 7) / 8 * 8
             }
             &Repr::Mtu(_) => field::MTU.end,
             &Repr::Unknown { length, .. } => field::DATA(length).end,
@@ -537,15 +550,15 @@ impl<'a> Repr<'a> {
                 opt.set_prefix(prefix);
             }
             Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
-                let data_len = data.len() / 8;
+                // TODO(thvdveld): I think we need to check if the data we are sending is not
+                // exceeding the MTU.
                 opt.clear_redirected_reserved();
                 opt.set_option_type(Type::RedirectedHeader);
-                opt.set_data_len((header.buffer_len() + 1 + data_len) as u8);
-                let mut ip_packet =
-                    Ipv6Packet::new_unchecked(&mut opt.data_mut()[field::IP_DATA..]);
+                opt.set_data_len((((8 + header.buffer_len() + data.len()) + 7) / 8) as u8);
+                let mut packet = &mut opt.data_mut()[field::REDIRECTED_RESERVED.end - 2..];
+                let mut ip_packet = Ipv6Packet::new_unchecked(&mut packet);
                 header.emit(&mut ip_packet);
-                let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
-                payload.copy_from_slice(&data[..data_len]);
+                ip_packet.payload_mut().copy_from_slice(data);
             }
             Repr::Mtu(mtu) => {
                 opt.set_option_type(Type::Mtu);

+ 42 - 27
src/wire/sixlowpan.rs

@@ -69,15 +69,17 @@ impl<'a> UnresolvedAddress<'a> {
                 }
                 AddressMode::FullyElided => {
                     bytes[0..2].copy_from_slice(&LINK_LOCAL_PREFIX[..]);
-                    match ll_address.unwrap() {
-                        LlAddress::Short(ll) => {
+                    match ll_address {
+                        Some(LlAddress::Short(ll)) => {
                             bytes[11..13].copy_from_slice(&EUI64_MIDDLE_VALUE[..]);
                             bytes[14..].copy_from_slice(&ll);
                         }
-                        LlAddress::Extended(_) => {
-                            bytes[8..].copy_from_slice(&ll_address.unwrap().as_eui_64().unwrap());
-                        }
-                        LlAddress::Absent => return Err(Error),
+                        Some(addr @ LlAddress::Extended(_)) => match addr.as_eui_64() {
+                            Some(addr) => bytes[8..].copy_from_slice(&addr),
+                            None => return Err(Error),
+                        },
+                        Some(LlAddress::Absent) => return Err(Error),
+                        None => return Err(Error),
                     }
                     Ok(ipv6::Address::from_bytes(&bytes[..]))
                 }
@@ -134,6 +136,10 @@ impl SixlowpanPacket {
     pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result<Self> {
         let raw = buffer.as_ref();
 
+        if raw.is_empty() {
+            return Err(Error);
+        }
+
         if raw[0] >> 3 == DISPATCH_FIRST_FRAGMENT_HEADER || raw[0] >> 3 == DISPATCH_FRAGMENT_HEADER
         {
             Ok(Self::FragmentHeader)
@@ -616,17 +622,19 @@ pub mod iphc {
         }
 
         /// Return the flow label field (when it is inlined).
-        pub fn flow_label_field(&self) -> Option<u32> {
+        pub fn flow_label_field(&self) -> Option<u16> {
             match self.tf_field() {
                 0b00 => {
                     let start = self.ip_fields_start() as usize;
-                    let raw = NetworkEndian::read_u32(&self.buffer.as_ref()[start..][..4]);
-                    Some(raw & 0xfffff)
+                    Some(NetworkEndian::read_u16(
+                        &self.buffer.as_ref()[start..][2..4],
+                    ))
                 }
                 0b01 => {
                     let start = self.ip_fields_start() as usize;
-                    let raw = NetworkEndian::read_u32(&self.buffer.as_ref()[start..][..4]) >> 8;
-                    Some(raw & 0xfffff)
+                    Some(NetworkEndian::read_u16(
+                        &self.buffer.as_ref()[start..][1..3],
+                    ))
                 }
                 0b10 | 0b11 => None,
                 _ => unreachable!(),
@@ -1074,7 +1082,7 @@ pub mod iphc {
         // TODO(thvdveld): refactor the following fields into something else
         pub ecn: Option<u8>,
         pub dscp: Option<u8>,
-        pub flow_label: Option<u32>,
+        pub flow_label: Option<u16>,
     }
 
     impl Repr {
@@ -1370,6 +1378,9 @@ pub mod nhc {
         /// dispatch is recognized.
         pub fn dispatch(buffer: impl AsRef<[u8]>) -> Result<Self> {
             let raw = buffer.as_ref();
+            if raw.is_empty() {
+                return Err(Error);
+            }
 
             if raw[0] >> 4 == DISPATCH_EXT_HEADER {
                 // We have a compressed IPv6 Extension Header.
@@ -1429,6 +1440,11 @@ pub mod nhc {
         pub fn new_checked(buffer: T) -> Result<Self> {
             let packet = Self::new_unchecked(buffer);
             packet.check_len()?;
+
+            if packet.eid_field() > 7 {
+                return Err(Error);
+            }
+
             Ok(packet)
         }
 
@@ -1436,10 +1452,18 @@ pub mod nhc {
         /// Returns `Err(Error)` if the buffer is too short.
         pub fn check_len(&self) -> Result<()> {
             let buffer = self.buffer.as_ref();
+
             if buffer.is_empty() {
-                Err(Error)
-            } else {
+                return Err(Error);
+            }
+
+            let mut len = 1;
+            len += self.next_header_size();
+
+            if len <= buffer.len() {
                 Ok(())
+            } else {
+                Err(Error)
             }
         }
 
@@ -1466,14 +1490,6 @@ pub mod nhc {
             }
         }
 
-        /// Return the length field.
-        pub fn length_field(&self) -> u8 {
-            let start = 1 + self.next_header_size();
-
-            let data = self.buffer.as_ref();
-            data[start]
-        }
-
         /// Parse the next header field.
         pub fn next_header(&self) -> NextHeader {
             if self.nh_field() == 1 {
@@ -1502,7 +1518,7 @@ pub mod nhc {
     impl<'a, T: AsRef<[u8]> + ?Sized> ExtHeaderPacket<&'a T> {
         /// Return a pointer to the payload.
         pub fn payload(&self) -> &'a [u8] {
-            let start = 2 + self.next_header_size();
+            let start = 1 + self.next_header_size();
             &self.buffer.as_ref()[start..]
         }
     }
@@ -1531,8 +1547,8 @@ pub mod nhc {
                 ExtHeaderId::FragmentHeader => 2,
                 ExtHeaderId::DestinationOptionsHeader => 3,
                 ExtHeaderId::MobilityHeader => 4,
+                ExtHeaderId::Reserved => 5,
                 ExtHeaderId::Header => 7,
-                _ => unreachable!(),
             };
 
             self.set_eid_field(id);
@@ -1958,11 +1974,11 @@ pub mod nhc {
             let bytes = [0xe3, 0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00];
 
             let packet = ExtHeaderPacket::new_checked(&bytes[..]).unwrap();
+            assert_eq!(packet.next_header_size(), 0);
             assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER);
-            assert_eq!(packet.length_field(), 6);
             assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader);
 
-            assert_eq!(packet.payload(), [0x03, 0x00, 0xff, 0x00, 0x00, 0x00]);
+            assert_eq!(packet.payload(), [0x06, 0x03, 0x00, 0xff, 0x00, 0x00, 0x00]);
         }
 
         #[test]
@@ -1980,7 +1996,6 @@ pub mod nhc {
 
             assert_eq!(packet.dispatch_field(), DISPATCH_EXT_HEADER);
             assert_eq!(packet.next_header(), NextHeader::Compressed);
-            assert_eq!(packet.length_field(), 6);
             assert_eq!(packet.extension_header_id(), ExtHeaderId::RoutingHeader);
         }