1 use core::slice::from_raw_parts;
2 #[cfg(feature = "std")]
3 use std::net::Ipv4Addr;
4 
5 use crate::*;
6 
7 /// A slice containing an ipv4 header of a network package.
8 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
9 pub struct Ipv4HeaderSlice<'a> {
10     slice: &'a [u8],
11 }
12 
13 impl<'a> Ipv4HeaderSlice<'a> {
14     /// Creates a slice containing an ipv4 header (including header options).
15     ///
16     /// If you also want to have the payload & ip extension headers correctly
17     /// seperated you can use
18     ///
19     /// * [`crate::Ipv4Slice::from_slice`] (just identifies slice ranges)
20     /// * [`crate::IpHeaders::from_ipv4_slice`] (unpacks all fields)
21     ///
22     /// or
23     ///
24     /// * [`crate::IpHeaders::from_ipv4_slice_lax`]
25     /// * [`crate::LaxIpv4Slice::from_slice`]
26     ///
27     /// for a laxer version which falls back to slice length only when the total_length
28     /// field in the header is inconsistent.
from_slice(slice: &'a [u8]) -> Result<Ipv4HeaderSlice<'a>, err::ipv4::HeaderSliceError>29     pub fn from_slice(slice: &'a [u8]) -> Result<Ipv4HeaderSlice<'a>, err::ipv4::HeaderSliceError> {
30         use err::ipv4::HeaderError::*;
31         use err::ipv4::HeaderSliceError::*;
32 
33         // check length
34         if slice.len() < Ipv4Header::MIN_LEN {
35             return Err(Len(err::LenError {
36                 required_len: Ipv4Header::MIN_LEN,
37                 len: slice.len(),
38                 len_source: LenSource::Slice,
39                 layer: err::Layer::Ipv4Header,
40                 layer_start_offset: 0,
41             }));
42         }
43 
44         // read version & ihl
45         let (version_number, ihl) = unsafe {
46             let value = slice.get_unchecked(0);
47             (value >> 4, value & 0xf)
48         };
49 
50         // check version
51         if 4 != version_number {
52             return Err(Content(UnexpectedVersion { version_number }));
53         }
54 
55         // check that the ihl is correct
56         if ihl < 5 {
57             return Err(Content(HeaderLengthSmallerThanHeader { ihl }));
58         }
59 
60         // check that the slice contains enough data for the entire header + options
61         let header_length = (usize::from(ihl)) * 4;
62         if slice.len() < header_length {
63             return Err(Len(err::LenError {
64                 required_len: header_length,
65                 len: slice.len(),
66                 len_source: LenSource::Slice,
67                 layer: err::Layer::Ipv4Header,
68                 layer_start_offset: 0,
69             }));
70         }
71 
72         //all good
73         Ok(Ipv4HeaderSlice {
74             // SAFETY:
75             // Safe as the slice length is checked to be at least
76             // header_length or greater above.
77             slice: unsafe { from_raw_parts(slice.as_ptr(), header_length) },
78         })
79     }
80 
81     /// Converts the given slice into a ipv4 header slice WITHOUT any
82     /// checks to ensure that the data present is an ipv4 header or that the
83     /// slice length is matching the header length.
84     ///
85     /// If you are not sure what this means, use [`Ipv4HeaderSlice::from_slice`]
86     /// instead.
87     ///
88     /// # Safety
89     ///
90     /// It must ensured that the slice exactly contains the IPv4 header
91     /// and the ihl (intra header length) & total length must be consistent.
92     #[inline]
from_slice_unchecked(slice: &[u8]) -> Ipv4HeaderSlice93     pub(crate) unsafe fn from_slice_unchecked(slice: &[u8]) -> Ipv4HeaderSlice {
94         Ipv4HeaderSlice { slice }
95     }
96 
97     /// Returns the slice containing the ipv4 header
98     #[inline]
slice(&self) -> &'a [u8]99     pub fn slice(&self) -> &'a [u8] {
100         self.slice
101     }
102 
103     /// Read the "version" field of the IPv4 header (should be 4).
104     #[inline]
version(&self) -> u8105     pub fn version(&self) -> u8 {
106         // SAFETY:
107         // Safe as the slice length is checked to be at least
108         // Ipv4Header::MIN_LEN (20) in the constructor.
109         unsafe { *self.slice.get_unchecked(0) >> 4 }
110     }
111 
112     /// Read the "ip header length" (length of the ipv4 header + options in multiples of 4 bytes).
113     #[inline]
ihl(&self) -> u8114     pub fn ihl(&self) -> u8 {
115         // SAFETY:
116         // Safe as the slice length is checked to be at least
117         // Ipv4Header::MIN_LEN (20) in the constructor.
118         unsafe { *self.slice.get_unchecked(0) & 0xf }
119     }
120 
121     /// Read the "differentiated_services_code_point" from the slice.
122     #[inline]
dcp(&self) -> Ipv4Dscp123     pub fn dcp(&self) -> Ipv4Dscp {
124         // SAFETY:
125         // get_unchecked: Safe as the slice length is checked to be at least
126         // Ipv4Header::MIN_LEN (20) in the constructor.
127         // new_unchecked: Safe as the bitshift by 2 guarantees that the passed
128         // value is not bigger then 6 bits.
129         unsafe { Ipv4Dscp::new_unchecked(*self.slice.get_unchecked(1) >> 2) }
130     }
131 
132     /// Read the "explicit_congestion_notification" from the slice.
133     #[inline]
ecn(&self) -> Ipv4Ecn134     pub fn ecn(&self) -> Ipv4Ecn {
135         // SAFETY:
136         // get_unchecked: Safe as the slice length is checked to be at least
137         // Ipv4Header::MIN_LEN (20) in the constructor.
138         // new_unchecked: Safe as value has been bitmasked to two bits.
139         unsafe { Ipv4Ecn::new_unchecked(*self.slice.get_unchecked(1) & 0b0000_0011) }
140     }
141 
142     /// Read the "total length" from the slice (total length of ip header + payload).
143     #[inline]
total_len(&self) -> u16144     pub fn total_len(&self) -> u16 {
145         // SAFETY:
146         // Safe as the slice length is checked to be at least
147         // Ipv4Header::MIN_LEN (20) in the constructor.
148         unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) }
149     }
150 
151     /// Determine the payload length based on the ihl & total_length
152     /// field of the header.
153     ///
154     /// # Example Usage
155     ///
156     /// ```
157     /// use etherparse::{Ipv4Header, Ipv4HeaderSlice};
158     ///
159     /// let bytes = Ipv4Header{
160     ///     // the payload len will be calculated by subtracting the
161     ///     // header length from the total length
162     ///     total_len: Ipv4Header::MIN_LEN as u16 + 100,
163     ///     ..Default::default()
164     /// }.to_bytes();
165     ///
166     /// let slice = Ipv4HeaderSlice::from_slice(&bytes).unwrap();
167     /// assert_eq!(Ok(100), slice.payload_len());
168     ///
169     /// // error case
170     /// let bad_bytes = Ipv4Header {
171     ///     // total len should also include the header, in case it does
172     ///     // not it is not possible to calculate the payload length
173     ///     total_len: Ipv4Header::MIN_LEN as u16 - 1,
174     ///     ..Default::default()
175     /// }.to_bytes();
176     ///
177     /// let bad_slice = Ipv4HeaderSlice::from_slice(&bad_bytes).unwrap();
178     /// // in case the total_len is smaller then the header itself an
179     /// // error is returned
180     /// use etherparse::{err::{LenError, Layer}, LenSource};
181     /// assert_eq!(
182     ///     bad_slice.payload_len(),
183     ///     Err(LenError {
184     ///         required_len: Ipv4Header::MIN_LEN,
185     ///         len: Ipv4Header::MIN_LEN - 1,
186     ///         len_source: LenSource::Ipv4HeaderTotalLen,
187     ///         layer: Layer::Ipv4Packet,
188     ///         layer_start_offset: 0,
189     ///     })
190     /// );
191     /// ```
192     #[inline]
payload_len(&self) -> Result<u16, err::LenError>193     pub fn payload_len(&self) -> Result<u16, err::LenError> {
194         let total_len = self.total_len();
195         // SAFETY: slice.len() can be at most be 60 (verified in from_slice) so a
196         // cast to u16 is safe.
197         let header_len = self.slice.len() as u16;
198         if header_len <= total_len {
199             Ok(total_len - header_len)
200         } else {
201             use err::{Layer, LenError};
202             Err(LenError {
203                 required_len: header_len.into(),
204                 len: total_len.into(),
205                 len_source: LenSource::Ipv4HeaderTotalLen,
206                 layer: Layer::Ipv4Packet,
207                 layer_start_offset: 0,
208             })
209         }
210     }
211 
212     /// Read the "identification" field from the slice.
213     #[inline]
identification(&self) -> u16214     pub fn identification(&self) -> u16 {
215         // SAFETY:
216         // Safe as the slice length is checked to be at least
217         // Ipv4Header::MIN_LEN (20) in the constructor.
218         unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) }
219     }
220 
221     /// Read the "dont fragment" flag from the slice.
222     #[inline]
dont_fragment(&self) -> bool223     pub fn dont_fragment(&self) -> bool {
224         // SAFETY:
225         // Safe as the slice length is checked to be at least
226         // Ipv4Header::MIN_LEN (20) in the constructor.
227         unsafe { 0 != (*self.slice.get_unchecked(6) & 0x40) }
228     }
229 
230     /// Read the "more fragments" flag from the slice.
231     #[inline]
more_fragments(&self) -> bool232     pub fn more_fragments(&self) -> bool {
233         // SAFETY:
234         // Safe as the slice length is checked to be at least
235         // Ipv4Header::MIN_LEN (20) in the constructor.
236         unsafe { 0 != (*self.slice.get_unchecked(6) & 0x20) }
237     }
238 
239     /// Read the "fragment_offset" field from the slice.
240     #[inline]
fragments_offset(&self) -> IpFragOffset241     pub fn fragments_offset(&self) -> IpFragOffset {
242         unsafe {
243             // SAFETY:
244             // Safe as the value is limited to be 13 bits long bellow.
245             IpFragOffset::new_unchecked(u16::from_be_bytes([
246                 // SAFETY:
247                 // Safe as the slice length is checked to be at least
248                 // Ipv4Header::MIN_LEN (20) in the constructor.
249                 *self.slice.get_unchecked(6) & 0x1f,
250                 *self.slice.get_unchecked(7),
251             ]))
252         }
253     }
254 
255     /// Read the "time_to_live" field from the slice.
256     #[inline]
ttl(&self) -> u8257     pub fn ttl(&self) -> u8 {
258         // SAFETY:
259         // Safe as the slice length is checked to be at least
260         // Ipv4Header::MIN_LEN (20) in the constructor.
261         unsafe { *self.slice.get_unchecked(8) }
262     }
263 
264     /// Read the "protocol" field from the slice.
265     #[inline]
protocol(&self) -> IpNumber266     pub fn protocol(&self) -> IpNumber {
267         // SAFETY:
268         // Safe as the slice length is checked to be at least
269         // Ipv4Header::MIN_LEN (20) in the constructor.
270         IpNumber(unsafe { *self.slice.get_unchecked(9) })
271     }
272 
273     /// Read the "header checksum" field from the slice.
274     #[inline]
header_checksum(&self) -> u16275     pub fn header_checksum(&self) -> u16 {
276         // SAFETY:
277         // Safe as the slice length is checked to be at least
278         // Ipv4Header::MIN_LEN (20) in the constructor.
279         unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(10)) }
280     }
281 
282     /// Returns a slice containing the ipv4 source address.
283     #[inline]
source(&self) -> [u8; 4]284     pub fn source(&self) -> [u8; 4] {
285         // SAFETY:
286         // Safe as the slice length is checked to be at least
287         // Ipv4Header::MIN_LEN (20) in the constructor.
288         unsafe { get_unchecked_4_byte_array(self.slice.as_ptr().add(12)) }
289     }
290 
291     /// Return the ipv4 source address as an std::net::Ipv4Addr
292     #[cfg(feature = "std")]
293     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
294     #[inline]
source_addr(&self) -> Ipv4Addr295     pub fn source_addr(&self) -> Ipv4Addr {
296         Ipv4Addr::from(self.source())
297     }
298 
299     /// Returns a slice containing the ipv4 source address.
300     #[inline]
destination(&self) -> [u8; 4]301     pub fn destination(&self) -> [u8; 4] {
302         // SAFETY:
303         // Safe as the slice length is checked to be at least
304         // Ipv4Header::MIN_LEN (20) in the constructor.
305         unsafe { get_unchecked_4_byte_array(self.slice.as_ptr().add(16)) }
306     }
307 
308     /// Return the ipv4 destination address as an std::net::Ipv4Addr
309     #[cfg(feature = "std")]
310     #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
311     #[inline]
destination_addr(&self) -> Ipv4Addr312     pub fn destination_addr(&self) -> Ipv4Addr {
313         Ipv4Addr::from(self.destination())
314     }
315 
316     /// Returns a slice containing the ipv4 header options (empty when there are no options).
317     #[inline]
options(&self) -> &'a [u8]318     pub fn options(&self) -> &'a [u8] {
319         // SAFETY:
320         // Safe as the slice length is checked to be at least
321         // Ipv4Header::MIN_LEN (20) in the constructor.
322         unsafe { from_raw_parts(self.slice.as_ptr().add(20), self.slice.len() - 20) }
323     }
324 
325     /// Returns true if the payload is fragmented.
326     ///
327     /// Either data is missing (more_fragments set) or there is
328     /// an fragment offset.
329     #[inline]
is_fragmenting_payload(&self) -> bool330     pub fn is_fragmenting_payload(&self) -> bool {
331         self.more_fragments() || (0 != self.fragments_offset().value())
332     }
333 
334     /// Decode all the fields and copy the results to a Ipv4Header struct
335     #[inline]
to_header(&self) -> Ipv4Header336     pub fn to_header(&self) -> Ipv4Header {
337         Ipv4Header {
338             dscp: self.dcp(),
339             ecn: self.ecn(),
340             total_len: self.total_len(),
341             identification: self.identification(),
342             dont_fragment: self.dont_fragment(),
343             more_fragments: self.more_fragments(),
344             fragment_offset: self.fragments_offset(),
345             time_to_live: self.ttl(),
346             protocol: self.protocol(),
347             header_checksum: self.header_checksum(),
348             source: self.source(),
349             destination: self.destination(),
350             options: {
351                 let options_slice = self.options();
352                 let mut options = Ipv4Options::new();
353                 options.len = options_slice.len() as u8;
354                 let target_slice: &mut [u8] = options.as_mut();
355                 target_slice.copy_from_slice(options_slice);
356                 options
357             },
358         }
359     }
360 }
361 
362 #[cfg(test)]
363 mod test {
364     use crate::{test_gens::*, *};
365     use alloc::{format, vec::Vec};
366     use arrayvec::ArrayVec;
367     use proptest::prelude::*;
368 
369     #[test]
debug()370     fn debug() {
371         let buffer = {
372             let header: Ipv4Header = Default::default();
373             header.to_bytes()
374         };
375         let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
376         assert_eq!(
377             format!("{:?}", slice),
378             format!("Ipv4HeaderSlice {{ slice: {:?} }}", slice.slice())
379         );
380     }
381 
382     proptest! {
383         #[test]
384         fn clone_eq(header in ipv4_any()) {
385             let buffer = header.to_bytes();
386             let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
387             assert_eq!(slice.clone(), slice);
388         }
389     }
390 
391     proptest! {
392         #[test]
393         fn from_slice(header in ipv4_any()) {
394             use err::ipv4::HeaderError::*;
395             use err::ipv4::HeaderSliceError::*;
396 
397             // ok
398             {
399                 let mut buffer = ArrayVec::<u8, { Ipv4Header::MAX_LEN + 1 }>::new();
400                 buffer.try_extend_from_slice(&header.to_bytes()).unwrap();
401                 buffer.try_extend_from_slice(&[1]).unwrap();
402 
403                 let actual_slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
404                 assert_eq!(actual_slice.to_header(), header);
405                 assert_eq!(actual_slice.slice(), &buffer[..header.header_len()]);
406             }
407 
408             // unexpected end of slice
409             {
410                 let buffer = header.to_bytes();
411                 for len in 0..header.header_len() {
412                     assert_eq!(
413                         Ipv4HeaderSlice::from_slice(&buffer[..len]),
414                         Err(Len(err::LenError{
415                             required_len: if len < Ipv4Header::MIN_LEN {
416                                 Ipv4Header::MIN_LEN
417                             } else {
418                                 header.header_len()
419                             },
420                             len: len,
421                             len_source: LenSource::Slice,
422                             layer: err::Layer::Ipv4Header,
423                             layer_start_offset: 0,
424                         }))
425                     );
426                 }
427             }
428 
429             // version error
430             for version_number in 0u8..0b1111u8 {
431                 if 4 != version_number {
432                     let mut buffer = header.to_bytes();
433                     // inject the bad ihl
434                     buffer[0] = (version_number << 4) | (buffer[0] & 0b1111);
435                     // expect an error
436                     assert_eq!(
437                         Ipv4HeaderSlice::from_slice(&buffer).unwrap_err(),
438                         Content(UnexpectedVersion{
439                             version_number,
440                         })
441                     );
442                 }
443             }
444 
445             // ihl too small error
446             for ihl in 0u8..5u8 {
447                 let mut buffer = header.to_bytes();
448                 // inject the bad ihl
449                 buffer[0] = (4 << 4) | ihl;
450                 // expect an error
451                 assert_eq!(
452                     Ipv4HeaderSlice::from_slice(&buffer).unwrap_err(),
453                     Content(HeaderLengthSmallerThanHeader{
454                         ihl,
455                     })
456                 );
457             }
458         }
459     }
460 
461     #[test]
from_slice_unchecked()462     fn from_slice_unchecked() {
463         let buffer = [0u8; 4];
464         let slice = unsafe { Ipv4HeaderSlice::from_slice_unchecked(&buffer) };
465         assert_eq!(slice.slice(), &buffer);
466     }
467 
468     proptest! {
469         #[test]
470         fn getters(header in ipv4_any()) {
471             let buffer = header.to_bytes();
472             let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
473 
474             assert_eq!(slice.slice(), &buffer[..]);
475             assert_eq!(slice.version(), 4);
476             assert_eq!(slice.ihl(), header.ihl());
477             assert_eq!(slice.dcp(), header.dscp);
478             assert_eq!(slice.ecn(), header.ecn);
479             assert_eq!(slice.total_len(), header.total_len);
480             assert_eq!(slice.payload_len(), header.payload_len());
481             assert_eq!(slice.identification(), header.identification);
482             assert_eq!(slice.dont_fragment(), header.dont_fragment);
483             assert_eq!(slice.more_fragments(), header.more_fragments);
484             assert_eq!(slice.fragments_offset(), header.fragment_offset);
485             assert_eq!(slice.ttl(), header.time_to_live);
486             assert_eq!(slice.protocol(), header.protocol);
487             assert_eq!(slice.header_checksum(), header.header_checksum);
488             assert_eq!(slice.source(), header.source);
489             assert_eq!(slice.destination(), header.destination);
490             assert_eq!(slice.options(), &header.options[..]);
491         }
492     }
493 
494     #[cfg(feature = "std")]
495     proptest! {
496         #[test]
497         fn getters_std(header in ipv4_any()) {
498             use std::net::Ipv4Addr;
499 
500             let buffer = header.to_bytes();
501             let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
502 
503             assert_eq!(slice.source_addr(), Ipv4Addr::from(header.source));
504             assert_eq!(slice.destination_addr(), Ipv4Addr::from(header.destination));
505         }
506     }
507 
508     #[test]
is_fragmenting_payload()509     fn is_fragmenting_payload() {
510         // not fragmenting
511         {
512             let buffer = {
513                 let mut header: Ipv4Header = Default::default();
514                 header.fragment_offset = 0.try_into().unwrap();
515                 header.more_fragments = false;
516                 let mut buffer = Vec::with_capacity(header.header_len());
517                 header.write(&mut buffer).unwrap();
518                 buffer
519             };
520             let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
521             assert_eq!(false, slice.is_fragmenting_payload());
522         }
523 
524         // fragmenting based on offset
525         {
526             let buffer = {
527                 let mut header: Ipv4Header = Default::default();
528                 header.fragment_offset = 1.try_into().unwrap();
529                 header.more_fragments = false;
530                 let mut buffer = Vec::with_capacity(header.header_len());
531                 header.write(&mut buffer).unwrap();
532                 buffer
533             };
534             let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
535             assert!(slice.is_fragmenting_payload());
536         }
537 
538         // fragmenting based on more_fragments
539         {
540             let buffer = {
541                 let mut header: Ipv4Header = Default::default();
542                 header.fragment_offset = 0.try_into().unwrap();
543                 header.more_fragments = true;
544                 let mut buffer = Vec::with_capacity(header.header_len());
545                 header.write(&mut buffer).unwrap();
546                 buffer
547             };
548             let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
549             assert!(slice.is_fragmenting_payload());
550         }
551     }
552 
553     proptest! {
554         #[test]
555         fn to_header(header in ipv4_any()) {
556             let buffer = header.to_bytes();
557             let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap();
558             assert_eq!(slice.to_header(), header);
559         }
560     }
561 }
562