Browse Source

Fix Redirected Header for Ndisc Options

Thibaut Vandervelden 2 years ago
parent
commit
7ee06df264
2 changed files with 36 additions and 32 deletions
  1. 23 19
      src/wire/ndisc.rs
  2. 13 13
      src/wire/ndiscoption.rs

+ 23 - 19
src/wire/ndisc.rs

@@ -319,19 +319,21 @@ impl<'a> Repr<'a> {
                             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);
@@ -367,11 +369,11 @@ impl<'a> Repr<'a> {
                 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
             }
@@ -387,14 +389,16 @@ impl<'a> Repr<'a> {
                 redirected_hdr,
                 ..
             } => {
-                let mut offset = 0;
+                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
             }
         }
     }

+ 13 - 13
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
@@ -368,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);
     }
 }
 
@@ -468,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()..],
                     }))
                 }
             }
@@ -508,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,
@@ -550,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);