Skip to main content

netstack3_ip/
reassembly.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Module for IP fragmented packet reassembly support.
6//!
7//! `reassembly` is a utility to support reassembly of fragmented IP packets.
8//! Fragmented packets are associated by a combination of the packets' source
9//! address, destination address and identification value. When a potentially
10//! fragmented packet is received, this utility will check to see if the packet
11//! is in fact fragmented or not. If it isn't fragmented, it will be returned as
12//! is without any modification. If it is fragmented, this utility will capture
13//! its body and store it in a cache while waiting for all the fragments for a
14//! packet to arrive. The header information from a fragment with offset set to
15//! 0 will also be kept to add to the final, reassembled packet. Once this
16//! utility has received all the fragments for a combination of source address,
17//! destination address and identification value, the implementer will need to
18//! allocate a buffer of sufficient size to reassemble the final packet into and
19//! pass it to this utility. This utility will then attempt to reassemble and
20//! parse the packet, which will be returned to the caller. The caller should
21//! then handle the returned packet as a normal IP packet. Note, there is a
22//! timer from receipt of the first fragment to reassembly of the final packet.
23//! See [`REASSEMBLY_TIMEOUT_SECONDS`].
24//!
25//! Note, this utility does not support reassembly of jumbogram packets.
26//! According to the IPv6 Jumbogram RFC (RFC 2675), the jumbogram payload option
27//! is relevant only for nodes that may be attached to links with a link MTU
28//! greater than 65575 bytes. Note, the maximum size of a non-jumbogram IPv6
29//! packet is also 65575 (as the payload length field for IP packets is 16 bits
30//! + 40 byte IPv6 header). If a link supports an MTU greater than the maximum
31//! size of a non-jumbogram packet, the packet should not be fragmented.
32
33use alloc::collections::{BTreeSet, BinaryHeap};
34use alloc::vec::Vec;
35use core::cmp::Ordering;
36use core::fmt::Debug;
37use core::hash::Hash;
38use core::time::Duration;
39
40use assert_matches::assert_matches;
41use log::debug;
42use net_types::ip::{GenericOverIp, Ip, IpAddr, IpVersionMarker, Ipv4, Ipv6};
43use netstack3_base::{
44    CoreTimerContext, HandleableTimer, InstantBindingsTypes, IpExt, LocalTimerHeap,
45    TimerBindingsTypes, TimerContext,
46};
47use netstack3_hashmap::hash_map::{Entry, HashMap};
48use packet::BufferViewMut;
49use packet_formats::ip::{IpPacket, Ipv4Proto};
50use packet_formats::ipv4::{Ipv4Header, Ipv4Packet};
51use packet_formats::ipv6::Ipv6Packet;
52use packet_formats::ipv6::ext_hdrs::Ipv6ExtensionHeaderData;
53use zerocopy::{SplitByteSlice, SplitByteSliceMut};
54
55/// An IP extension trait supporting reassembly of fragments.
56pub trait ReassemblyIpExt: IpExt {
57    /// The maximum amount of time from receipt of the first fragment to
58    /// reassembly of a packet. Note, "first fragment" does not mean a fragment
59    /// with offset 0; it means the first fragment packet we receive with a new
60    /// combination of source address, destination address and fragment
61    /// identification value.
62    const REASSEMBLY_TIMEOUT: Duration;
63
64    /// An IP specific field that should be considered part of the
65    /// [`FragmentCacheKey`].
66    type FragmentCacheKeyPart: Copy + Clone + Debug + Hash + PartialEq + Eq;
67
68    /// Returns the IP specific portion of the [`FragmentCacheKey`] from the
69    /// packet.
70    fn ip_specific_key_part<B: SplitByteSlice>(
71        packet: &Self::Packet<B>,
72    ) -> Self::FragmentCacheKeyPart;
73}
74
75impl ReassemblyIpExt for Ipv4 {
76    /// This value is specified in RFC 729, section 3.1:
77    ///   The current recommendation for the initial timer setting is 15
78    ///   seconds.
79    const REASSEMBLY_TIMEOUT: Duration = Duration::from_secs(15);
80
81    /// IPv4 considers the inner protocol to be part of the fragmentation key.
82    /// From RFC 791, section 2.3:
83    ///   To assemble the fragments of an internet datagram, an internet
84    ///   protocol module (for example at a destination host) combines
85    ///   internet datagrams that all have the same value for the four fields:
86    ///   identification, source, destination, and protocol.
87    type FragmentCacheKeyPart = Ipv4Proto;
88
89    fn ip_specific_key_part<B: SplitByteSlice>(
90        packet: &Self::Packet<B>,
91    ) -> Self::FragmentCacheKeyPart {
92        IpPacket::proto(packet)
93    }
94}
95
96impl ReassemblyIpExt for Ipv6 {
97    /// This value is specified in RFC 8200, section 4.5:
98    ///   If insufficient fragments are received to complete reassembly
99    ///   of a packet within 60 seconds of the reception of the first-
100    ///   arriving fragment of that packet, reassembly of that packet
101    ///   must be abandoned and all the fragments that have been received
102    ///   for that packet must be discarded.
103    const REASSEMBLY_TIMEOUT: Duration = Duration::from_secs(60);
104
105    /// Unlike IPv4, IPv6 allows reassembling fragments that have different
106    /// inner protocols. From RFC 8200, section 4.5:
107    ///   The Next Header values in the Fragment headers of different
108    ///   fragments of the same original packet may differ.  Only the value
109    ///   from the Offset zero fragment packet is used for reassembly.
110    type FragmentCacheKeyPart = ();
111
112    fn ip_specific_key_part<B: SplitByteSlice>(
113        _packet: &Self::Packet<B>,
114    ) -> Self::FragmentCacheKeyPart {
115        ()
116    }
117}
118
119/// Number of bytes per fragment block for IPv4 and IPv6.
120///
121/// IPv4 outlines the fragment block size in RFC 791 section 3.1, under the
122/// fragment offset field's description: "The fragment offset is measured in
123/// units of 8 octets (64 bits)".
124///
125/// IPv6 outlines the fragment block size in RFC 8200 section 4.5, under the
126/// fragment offset field's description: "The offset, in 8-octet units, of the
127/// data following this header".
128const FRAGMENT_BLOCK_SIZE: u8 = 8;
129
130/// Maximum number of fragment blocks an IPv4 or IPv6 packet can have.
131///
132/// We use this value because both IPv4 fixed header's fragment offset field and
133/// IPv6 fragment extension header's fragment offset field are 13 bits wide.
134const MAX_FRAGMENT_BLOCKS: u16 = 8191;
135
136/// Maximum number of bytes of all currently cached fragments per IP protocol.
137///
138/// If the current cache size is less than this number, a new fragment can be
139/// cached (even if this will result in the total cache size exceeding this
140/// threshold). If the current cache size >= this number, the incoming fragment
141/// will be dropped.
142const MAX_FRAGMENT_CACHE_SIZE: usize = 4 * 1024 * 1024;
143
144/// The state context for the fragment cache.
145pub trait FragmentContext<I: Ip, BT: FragmentBindingsTypes> {
146    /// Returns a mutable reference to the fragment cache.
147    fn with_state_mut<O, F: FnOnce(&mut IpPacketFragmentCache<I, BT>) -> O>(&mut self, cb: F) -> O;
148}
149
150/// The bindings types for IP packet fragment reassembly.
151pub trait FragmentBindingsTypes: TimerBindingsTypes + InstantBindingsTypes {}
152impl<BT> FragmentBindingsTypes for BT where BT: TimerBindingsTypes + InstantBindingsTypes {}
153
154/// The bindings execution context for IP packet fragment reassembly.
155pub trait FragmentBindingsContext: TimerContext + FragmentBindingsTypes {}
156impl<BC> FragmentBindingsContext for BC where BC: TimerContext + FragmentBindingsTypes {}
157
158/// The timer ID for the fragment cache.
159#[derive(Hash, Eq, PartialEq, Default, Clone, Debug, GenericOverIp)]
160#[generic_over_ip(I, Ip)]
161pub struct FragmentTimerId<I: Ip>(IpVersionMarker<I>);
162
163/// An implementation of a fragment cache.
164pub trait FragmentHandler<I: ReassemblyIpExt, BC> {
165    /// Attempts to process a packet fragment.
166    ///
167    /// # Panics
168    ///
169    /// Panics if the packet has no fragment data.
170    fn process_fragment<B: SplitByteSlice>(
171        &mut self,
172        bindings_ctx: &mut BC,
173        packet: I::Packet<B>,
174    ) -> FragmentProcessingState<I, B>
175    where
176        I::Packet<B>: FragmentablePacket;
177
178    /// Attempts to reassemble a packet.
179    ///
180    /// Attempts to reassemble a packet associated with a given
181    /// `FragmentCacheKey`, `key`, and cancels the timer to reset reassembly
182    /// data. The caller is expected to allocate a buffer of sufficient size
183    /// (available from `process_fragment` when it returns a
184    /// `FragmentProcessingState::Ready` value) and provide it to
185    /// `reassemble_packet` as `buffer` where the packet will be reassembled
186    /// into.
187    ///
188    /// # Panics
189    ///
190    /// Panics if the provided `buffer` does not have enough capacity for the
191    /// reassembled packet. Also panics if a different `ctx` is passed to
192    /// `reassemble_packet` from the one passed to `process_fragment` when
193    /// processing a packet with a given `key` as `reassemble_packet` will fail
194    /// to cancel the reassembly timer.
195    fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
196        &mut self,
197        bindings_ctx: &mut BC,
198        key: &FragmentCacheKey<I>,
199        buffer: BV,
200    ) -> Result<(), FragmentReassemblyError>;
201}
202
203impl<I: IpExt + ReassemblyIpExt, BC: FragmentBindingsContext, CC: FragmentContext<I, BC>>
204    FragmentHandler<I, BC> for CC
205{
206    fn process_fragment<B: SplitByteSlice>(
207        &mut self,
208        bindings_ctx: &mut BC,
209        packet: I::Packet<B>,
210    ) -> FragmentProcessingState<I, B>
211    where
212        I::Packet<B>: FragmentablePacket,
213    {
214        self.with_state_mut(|cache| {
215            let (res, timer_action) = cache.process_fragment(packet);
216
217            if let Some(timer_action) = timer_action {
218                match timer_action {
219                    // TODO(https://fxbug.dev/414413500): for IPv4, use the
220                    // fragment's TTL to determine the timeout.
221                    CacheTimerAction::CreateNewTimer(key) => {
222                        assert_eq!(
223                            cache.timers.schedule_after(
224                                bindings_ctx,
225                                key,
226                                (),
227                                I::REASSEMBLY_TIMEOUT,
228                            ),
229                            None
230                        )
231                    }
232                    CacheTimerAction::CancelExistingTimer(key) => {
233                        assert_ne!(cache.timers.cancel(bindings_ctx, &key), None)
234                    }
235                }
236            }
237
238            res
239        })
240    }
241
242    fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
243        &mut self,
244        bindings_ctx: &mut BC,
245        key: &FragmentCacheKey<I>,
246        buffer: BV,
247    ) -> Result<(), FragmentReassemblyError> {
248        self.with_state_mut(|cache| {
249            let res = cache.reassemble_packet(key, buffer);
250
251            match res {
252                Ok(_) | Err(FragmentReassemblyError::PacketParsingError) => {
253                    // Cancel the reassembly timer as we attempt reassembly which
254                    // means we had all the fragments for the final packet, even
255                    // if parsing the reassembled packet failed.
256                    assert_matches!(cache.timers.cancel(bindings_ctx, key), Some(_));
257                }
258                Err(FragmentReassemblyError::InvalidKey)
259                | Err(FragmentReassemblyError::MissingFragments) => {}
260            }
261
262            res
263        })
264    }
265}
266
267impl<I: ReassemblyIpExt, BC: FragmentBindingsContext, CC: FragmentContext<I, BC>>
268    HandleableTimer<CC, BC> for FragmentTimerId<I>
269{
270    fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
271        let Self(IpVersionMarker { .. }) = self;
272        core_ctx.with_state_mut(|cache| {
273            let Some((key, ())) = cache.timers.pop(bindings_ctx) else {
274                return;
275            };
276
277            // If a timer fired, the `key` must still exist in our fragment cache.
278            let FragmentCacheData { missing_blocks: _, body_fragments, header: _, total_size } =
279                assert_matches!(cache.remove_data(&key), Some(c) => c);
280            debug!(
281                "reassembly for {key:?} \
282                timed out with {} fragments and {total_size} bytes",
283                body_fragments.len(),
284            );
285        });
286    }
287}
288
289/// Trait that must be implemented by any packet type that is fragmentable.
290pub trait FragmentablePacket {
291    /// Return fragment identifier data.
292    ///
293    /// Returns the fragment identification, offset and more flag as `(a, b, c)`
294    /// where `a` is the fragment identification value, `b` is the fragment
295    /// offset and `c` is the more flag.
296    ///
297    /// # Panics
298    ///
299    /// Panics if the packet has no fragment data.
300    fn fragment_data(&self) -> (u32, u16, bool);
301}
302
303impl<B: SplitByteSlice> FragmentablePacket for Ipv4Packet<B> {
304    fn fragment_data(&self) -> (u32, u16, bool) {
305        (u32::from(self.id()), self.fragment_offset().into_raw(), self.mf_flag())
306    }
307}
308
309impl<B: SplitByteSlice> FragmentablePacket for Ipv6Packet<B> {
310    fn fragment_data(&self) -> (u32, u16, bool) {
311        for ext_hdr in self.iter_extension_hdrs() {
312            if let Ipv6ExtensionHeaderData::Fragment { fragment_data } = ext_hdr.data() {
313                return (
314                    fragment_data.identification(),
315                    fragment_data.fragment_offset().into_raw(),
316                    fragment_data.m_flag(),
317                );
318            }
319        }
320
321        unreachable!(
322            "Should never call this function if the packet does not have a fragment header"
323        );
324    }
325}
326
327/// Possible return values for [`IpPacketFragmentCache::process_fragment`].
328#[derive(Debug)]
329pub enum FragmentProcessingState<I: ReassemblyIpExt, B: SplitByteSlice> {
330    /// The provided packet is not fragmented so no processing is required.
331    /// The packet is returned with this value without any modification.
332    NotNeeded(I::Packet<B>),
333
334    /// The provided packet is fragmented but it is malformed.
335    ///
336    /// Possible reasons for being malformed are:
337    ///  1) Body is not a multiple of `FRAGMENT_BLOCK_SIZE` and  it is not the
338    ///     last fragment (last fragment of a packet, not last fragment received
339    ///     for a packet).
340    ///  2) Overlaps with an existing fragment. This is explicitly not allowed
341    ///     for IPv6 as per RFC 8200 section 4.5 (more details in RFC 5722). We
342    ///     choose the same behaviour for IPv4 for the same reasons.
343    ///  3) Packet's fragment offset + # of fragment blocks >
344    ///     `MAX_FRAGMENT_BLOCKS`.
345    InvalidFragment,
346
347    /// Successfully processed the provided fragment. We are still waiting on
348    /// more fragments for a packet to arrive before being ready to reassemble
349    /// the packet.
350    NeedMoreFragments,
351
352    /// Cannot process the fragment because `MAX_FRAGMENT_CACHE_SIZE` is
353    /// reached.
354    OutOfMemory,
355
356    /// Successfully processed the provided fragment. We now have all the
357    /// fragments we need to reassemble the packet. The caller must create a
358    /// buffer with capacity for at least `packet_len` bytes and provide the
359    /// buffer and `key` to `reassemble_packet`.
360    Ready { key: FragmentCacheKey<I>, packet_len: usize },
361}
362
363/// Possible errors when attempting to reassemble a packet.
364#[derive(Debug, PartialEq, Eq)]
365pub enum FragmentReassemblyError {
366    /// At least one fragment for a packet has not arrived.
367    MissingFragments,
368
369    /// A `FragmentCacheKey` is not associated with any packet. This could be
370    /// because either no fragment has yet arrived for a packet associated with
371    /// a `FragmentCacheKey` or some fragments did arrive, but the reassembly
372    /// timer expired and got discarded.
373    InvalidKey,
374
375    /// Packet parsing error.
376    PacketParsingError,
377}
378
379/// Fragment Cache Key.
380///
381/// Composed of the original packet's source address, destination address,
382/// and fragment id.
383#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
384pub struct FragmentCacheKey<I: ReassemblyIpExt> {
385    src_ip: I::Addr,
386    dst_ip: I::Addr,
387    fragment_id: u32,
388    ip_specific_fields: I::FragmentCacheKeyPart,
389}
390
391/// An inclusive-inclusive range of bytes within a reassembled packet.
392// NOTE: We use this instead of `std::ops::RangeInclusive` because the latter
393// provides getter methods which return references, and it adds a lot of
394// unnecessary dereferences.
395#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
396struct BlockRange {
397    start: u16,
398    end: u16,
399}
400
401/// Data required for fragmented packet reassembly.
402#[derive(Debug)]
403struct FragmentCacheData {
404    /// List of non-overlapping inclusive ranges of fragment blocks required
405    /// before being ready to reassemble a packet.
406    ///
407    /// When creating a new instance of `FragmentCacheData`, we will set
408    /// `missing_blocks` to a list with a single element representing all
409    /// blocks, (0, MAX_VALUE). In this case, MAX_VALUE will be set to
410    /// `core::u16::MAX`.
411    missing_blocks: BTreeSet<BlockRange>,
412
413    /// Received fragment blocks.
414    ///
415    /// We use a binary heap for help when reassembling packets. When we
416    /// reassemble packets, we will want to fill up a new buffer with all the
417    /// body fragments. The easiest way to do this is in order, from the
418    /// fragment with offset 0 to the fragment with the highest offset. Since we
419    /// only need to enforce the order when reassembling, we use a min-heap so
420    /// we have a defined order (increasing fragment offset values) when
421    /// popping. `BinaryHeap` is technically a max-heap, but we use the negative
422    /// of the offset values as the key for the heap. See
423    /// [`PacketBodyFragment::new`].
424    body_fragments: BinaryHeap<PacketBodyFragment>,
425
426    /// The header data for the reassembled packet.
427    ///
428    /// The header of the fragment packet with offset 0 will be used as the
429    /// header for the final, reassembled packet.
430    header: Option<Vec<u8>>,
431
432    /// Total number of bytes in the reassembled packet.
433    ///
434    /// This is used so that we don't have to iterated through `body_fragments`
435    /// and sum the partial body sizes to calculate the reassembled packet's
436    /// size.
437    total_size: usize,
438}
439
440impl Default for FragmentCacheData {
441    fn default() -> FragmentCacheData {
442        FragmentCacheData {
443            missing_blocks: core::iter::once(BlockRange { start: 0, end: u16::MAX }).collect(),
444            body_fragments: BinaryHeap::new(),
445            header: None,
446            total_size: 0,
447        }
448    }
449}
450
451impl FragmentCacheData {
452    /// Attempts to find a gap where the provided `BlockRange` will fit in.
453    fn find_gap(&self, BlockRange { start, end }: BlockRange) -> FindGapResult {
454        let result = self.missing_blocks.iter().find_map(|gap| {
455            // This gap completely contains the provided range.
456            if gap.start <= start && gap.end >= end {
457                return Some(FindGapResult::Found { gap: *gap });
458            }
459
460            // This gap is completely disjoint from the provided range.
461            // Ignore it.
462            if gap.start > end || gap.end < start {
463                return None;
464            }
465
466            // If neither of the above are true, this gap must overlap with
467            // the provided range.
468            return Some(FindGapResult::Overlap);
469        });
470
471        match result {
472            Some(result) => result,
473            None => {
474                // Searching the missing blocks didn't find a suitable gap nor
475                // an overlap. Check for an out-of-bounds range before
476                // concluding that this range must be a duplicate.
477
478                // Note: `last` *must* exist and *must* represent the final
479                // fragment. If we had not yet received the final fragment, the
480                // search through the `missing_blocks` would be guaranteed to
481                // return `Some` (because it would contain a range with an end
482                // equal to u16::Max).
483                let last = self.body_fragments.peek().unwrap();
484                if last.offset < start {
485                    FindGapResult::OutOfBounds
486                } else {
487                    FindGapResult::Duplicate
488                }
489            }
490        }
491    }
492}
493
494/// The result of calling [`FragmentCacheData::find_gap`].
495enum FindGapResult {
496    // The provided `BlockRange` fits inside of an existing gap. The gap may be
497    // completely or partially filled by the provided `BlockRange`.
498    Found {
499        gap: BlockRange,
500    },
501    // The provided `BlockRange` overlaps with data we've already received.
502    // Specifically, an overlap occurs if the provided `BlockRange` is partially
503    // contained within a gap.
504    Overlap,
505    /// The provided `BlockRange` has an end beyond the known end of the packet.
506    OutOfBounds,
507    // The provided `BlockRange` has already been received. Specifically, a
508    // duplicate occurs if the provided `BlockRange` is completely disjoint from
509    // all known gaps.
510    //
511    // RFC 8200, Section 4.5 states:
512    //   It should be noted that fragments may be duplicated in the
513    //   network.  Instead of treating these exact duplicate fragments
514    //   as overlapping fragments, an implementation may choose to
515    //   detect this case and drop exact duplicate fragments while
516    //   keeping the other fragments belonging to the same packet.
517    //
518    // Here we take a loose interpretation of "exact" and choose not to verify
519    // that the *data* contained within the fragment matches the previously
520    // received data. This is in the spirit of reducing the work performed by
521    // the assembler, and is in line with the behavior of other platforms.
522    Duplicate,
523}
524
525/// A cache of inbound IP packet fragments.
526#[derive(Debug)]
527pub struct IpPacketFragmentCache<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
528    cache: HashMap<FragmentCacheKey<I>, FragmentCacheData>,
529    size: usize,
530    threshold: usize,
531    timers: LocalTimerHeap<FragmentCacheKey<I>, (), BT>,
532}
533
534impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> IpPacketFragmentCache<I, BC> {
535    /// Creates a new `IpFragmentCache`.
536    pub fn new<CC: CoreTimerContext<FragmentTimerId<I>, BC>>(
537        bindings_ctx: &mut BC,
538    ) -> IpPacketFragmentCache<I, BC> {
539        IpPacketFragmentCache {
540            cache: HashMap::new(),
541            size: 0,
542            threshold: MAX_FRAGMENT_CACHE_SIZE,
543            timers: LocalTimerHeap::new(bindings_ctx, CC::convert_timer(Default::default())),
544        }
545    }
546}
547
548enum CacheTimerAction<I: ReassemblyIpExt> {
549    CreateNewTimer(FragmentCacheKey<I>),
550    CancelExistingTimer(FragmentCacheKey<I>),
551}
552
553impl<I: ReassemblyIpExt, BT: FragmentBindingsTypes> IpPacketFragmentCache<I, BT> {
554    /// Attempts to process a packet fragment.
555    ///
556    /// # Panics
557    ///
558    /// Panics if the packet has no fragment data.
559    fn process_fragment<B: SplitByteSlice>(
560        &mut self,
561        packet: I::Packet<B>,
562    ) -> (FragmentProcessingState<I, B>, Option<CacheTimerAction<I>>)
563    where
564        I::Packet<B>: FragmentablePacket,
565    {
566        if self.above_size_threshold() {
567            return (FragmentProcessingState::OutOfMemory, None);
568        }
569
570        // Get the fragment data.
571        let (id, offset, m_flag) = packet.fragment_data();
572
573        // Check if `packet` is actually fragmented. We know it is not
574        // fragmented if the fragment offset is 0 (contains first fragment) and
575        // we have no more fragments. This means the first fragment is the only
576        // fragment, implying we have a full packet.
577        if offset == 0 && !m_flag {
578            return (FragmentProcessingState::NotNeeded(packet), None);
579        }
580
581        // Make sure packet's body isn't empty. Since at this point we know that
582        // the packet is definitely fragmented (`offset` is not 0 or `m_flag` is
583        // `true`), we simply let the caller know we need more fragments. This
584        // should never happen, but just in case :).
585        if packet.body().is_empty() {
586            return (FragmentProcessingState::NeedMoreFragments, None);
587        }
588
589        // Make sure body is a multiple of `FRAGMENT_BLOCK_SIZE` bytes, or
590        // `packet` contains the last fragment block which is allowed to be less
591        // than `FRAGMENT_BLOCK_SIZE` bytes.
592        if m_flag && (packet.body().len() % (FRAGMENT_BLOCK_SIZE as usize) != 0) {
593            return (FragmentProcessingState::InvalidFragment, None);
594        }
595
596        // Key used to find this connection's fragment cache data.
597        let key = FragmentCacheKey {
598            src_ip: packet.src_ip(),
599            dst_ip: packet.dst_ip(),
600            fragment_id: id,
601            ip_specific_fields: I::ip_specific_key_part(&packet),
602        };
603
604        // The number of fragment blocks `packet` contains.
605        //
606        // Note, we are calculating the ceiling of an integer division.
607        // Essentially:
608        //     ceil(packet.body.len() / FRAGMENT_BLOCK_SIZE)
609        //
610        // We need to calculate the ceiling of the division because the final
611        // fragment block for a reassembled packet is allowed to contain less
612        // than `FRAGMENT_BLOCK_SIZE` bytes.
613        //
614        // We know `packet.body().len() - 1` will never be less than 0 because
615        // we already made sure that `packet`'s body is not empty, and it is
616        // impossible to have a negative body size.
617        let num_fragment_blocks = 1 + ((packet.body().len() - 1) / (FRAGMENT_BLOCK_SIZE as usize));
618        assert!(num_fragment_blocks > 0);
619
620        // The range of fragment blocks `packet` contains.
621        //
622        // The maximum number of fragment blocks a reassembled packet is allowed
623        // to contain is `MAX_FRAGMENT_BLOCKS` so we make sure that the fragment
624        // we received does not violate this.
625        let fragment_blocks_range =
626            if let Ok(offset_end) = u16::try_from((offset as usize) + num_fragment_blocks - 1) {
627                if offset_end <= MAX_FRAGMENT_BLOCKS {
628                    BlockRange { start: offset, end: offset_end }
629                } else {
630                    return (FragmentProcessingState::InvalidFragment, None);
631                }
632            } else {
633                return (FragmentProcessingState::InvalidFragment, None);
634            };
635
636        // Get (or create) the fragment cache data.
637        let (fragment_data, timer_not_yet_scheduled) = self.get_or_create(key);
638
639        // Find the gap where `packet` belongs.
640        let found_gap = match fragment_data.find_gap(fragment_blocks_range) {
641            FindGapResult::Overlap | FindGapResult::OutOfBounds => {
642                // Drop all reassembly data as per RFC 8200 section 4.5 (IPv6).
643                // See RFC 5722 for more information.
644                //
645                // IPv4 (RFC 791) does not specify what to do for overlapped
646                // fragments. RFC 1858 section 4.2 outlines a way to prevent an
647                // overlapping fragment attack for IPv4, but this is primarily
648                // for IP filtering since "no standard requires that an
649                // overlap-safe reassemble algorithm be used" on hosts. In
650                // practice, non-malicious nodes should not intentionally send
651                // data for the same fragment block multiple times, so we will
652                // do the same thing as IPv6 in this case.
653                assert_matches!(self.remove_data(&key), Some(_));
654
655                return (
656                    FragmentProcessingState::InvalidFragment,
657                    (!timer_not_yet_scheduled)
658                        .then_some(CacheTimerAction::CancelExistingTimer(key)),
659                );
660            }
661            FindGapResult::Duplicate => {
662                // Ignore duplicate fragments as per RFC 8200 section 4.5
663                // (IPv6):
664                //   It should be noted that fragments may be duplicated in the
665                //   network.  Instead of treating these exact duplicate fragments
666                //   as overlapping fragments, an implementation may choose to
667                //   detect this case and drop exact duplicate fragments while
668                //   keeping the other fragments belonging to the same packet.
669                //
670                // Ipv4 (RFC 791) does not specify what to do for duplicate
671                // fragments. As such we choose to do the same as IPv6 in this
672                // case.
673                return (FragmentProcessingState::NeedMoreFragments, None);
674            }
675            FindGapResult::Found { gap } => gap,
676        };
677
678        let timer_id = timer_not_yet_scheduled.then_some(CacheTimerAction::CreateNewTimer(key));
679
680        // Remove `found_gap` since the gap as it exists will no longer be
681        // valid.
682        assert!(fragment_data.missing_blocks.remove(&found_gap));
683
684        // If the received fragment blocks start after the beginning of
685        // `found_gap`, create a new gap between the beginning of `found_gap`
686        // and the first fragment block contained in `packet`.
687        //
688        // Example:
689        //   `packet` w/ fragments [4, 7]
690        //                 |-----|-----|-----|-----|
691        //                    4     5     6     7
692        //
693        //   `found_gap` w/ fragments [X, 7] where 0 <= X < 4
694        //     |-----| ... |-----|-----|-----|-----|
695        //        X    ...    4     5     6     7
696        //
697        //   Here we can see that with a `found_gap` of [2, 7], `packet` covers
698        //   [4, 7] but we are still missing [X, 3] so we create a new gap of
699        //   [X, 3].
700        if found_gap.start < fragment_blocks_range.start {
701            assert!(fragment_data.missing_blocks.insert(BlockRange {
702                start: found_gap.start,
703                end: fragment_blocks_range.start - 1
704            }));
705        }
706
707        // If the received fragment blocks end before the end of `found_gap` and
708        // we expect more fragments, create a new gap between the last fragment
709        // block contained in `packet` and the end of `found_gap`.
710        //
711        // Example 1:
712        //   `packet` w/ fragments [4, 7] & m_flag = true
713        //     |-----|-----|-----|-----|
714        //        4     5     6     7
715        //
716        //   `found_gap` w/ fragments [4, Y] where 7 < Y <= `MAX_FRAGMENT_BLOCKS`.
717        //     |-----|-----|-----|-----| ... |-----|
718        //        4     5     6     7    ...    Y
719        //
720        //   Here we can see that with a `found_gap` of [4, Y], `packet` covers
721        //   [4, 7] but we still expect more fragment blocks after the blocks in
722        //   `packet` (as noted by `m_flag`) so we are still missing [8, Y] so
723        //   we create a new gap of [8, Y].
724        //
725        // Example 2:
726        //   `packet` w/ fragments [4, 7] & m_flag = false
727        //     |-----|-----|-----|-----|
728        //        4     5     6     7
729        //
730        //   `found_gap` w/ fragments [4, Y] where MAX = `MAX_FRAGMENT_BLOCKS`.
731        //     |-----|-----|-----|-----| ... |-----|
732        //        4     5     6     7    ...   MAX
733        //
734        //   Here we can see that with a `found_gap` of [4, MAX], `packet`
735        //   covers [4, 7] and we don't expect more fragment blocks after the
736        //   blocks in `packet` (as noted by `m_flag`) so we don't create a new
737        //   gap. Note, if we encounter a `packet` where `m_flag` is false,
738        //   `found_gap`'s end value must be MAX because we should only ever not
739        //   create a new gap where the end is MAX when we are processing a
740        //   packet with the last fragment block.
741        if found_gap.end > fragment_blocks_range.end && m_flag {
742            assert!(
743                fragment_data.missing_blocks.insert(BlockRange {
744                    start: fragment_blocks_range.end + 1,
745                    end: found_gap.end
746                })
747            );
748        } else if found_gap.end > fragment_blocks_range.end && !m_flag && found_gap.end < u16::MAX {
749            // There is another fragment after this one that is already present
750            // in the cache. That means that this fragment can't be the last
751            // one (must have `m_flag` set).
752            return (FragmentProcessingState::InvalidFragment, timer_id);
753        } else {
754            // Make sure that if we are not adding a fragment after the packet,
755            // it is because `packet` goes up to the `found_gap`'s end boundary,
756            // or this is the last fragment. If it is the last fragment for a
757            // packet, we make sure that `found_gap`'s end value is
758            // `core::u16::MAX`.
759            assert!(
760                found_gap.end == fragment_blocks_range.end
761                    || (!m_flag && found_gap.end == u16::MAX),
762                "found_gap: {:?}, fragment_blocks_range: {:?} offset: {:?}, m_flag: {:?}",
763                found_gap,
764                fragment_blocks_range,
765                offset,
766                m_flag
767            );
768        }
769
770        let mut added_bytes = 0;
771        // Get header buffer from `packet` if its fragment offset equals to 0.
772        if offset == 0 {
773            assert_eq!(fragment_data.header, None);
774            let header = get_header::<B, I>(&packet);
775            added_bytes = header.len();
776            fragment_data.header = Some(header);
777        }
778
779        // Add our `packet`'s body to the store of body fragments.
780        let mut body = Vec::with_capacity(packet.body().len());
781        body.extend_from_slice(packet.body());
782        added_bytes += body.len();
783        fragment_data.total_size += added_bytes;
784        fragment_data.body_fragments.push(PacketBodyFragment::new(offset, body));
785
786        // If we still have missing fragments, let the caller know that we are
787        // still waiting on some fragments. Otherwise, we let them know we are
788        // ready to reassemble and give them a key and the final packet length
789        // so they can allocate a sufficient buffer and call
790        // `reassemble_packet`.
791        let result = if fragment_data.missing_blocks.is_empty() {
792            FragmentProcessingState::Ready { key, packet_len: fragment_data.total_size }
793        } else {
794            FragmentProcessingState::NeedMoreFragments
795        };
796
797        self.increment_size(added_bytes);
798        (result, timer_id)
799    }
800
801    /// Attempts to reassemble a packet.
802    ///
803    /// Attempts to reassemble a packet associated with a given
804    /// `FragmentCacheKey`, `key`, and cancels the timer to reset reassembly
805    /// data. The caller is expected to allocate a buffer of sufficient size
806    /// (available from `process_fragment` when it returns a
807    /// `FragmentProcessingState::Ready` value) and provide it to
808    /// `reassemble_packet` as `buffer` where the packet will be reassembled
809    /// into.
810    ///
811    /// # Panics
812    ///
813    /// Panics if the provided `buffer` does not have enough capacity for the
814    /// reassembled packet. Also panics if a different `ctx` is passed to
815    /// `reassemble_packet` from the one passed to `process_fragment` when
816    /// processing a packet with a given `key` as `reassemble_packet` will fail
817    /// to cancel the reassembly timer.
818    fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
819        &mut self,
820        key: &FragmentCacheKey<I>,
821        buffer: BV,
822    ) -> Result<(), FragmentReassemblyError> {
823        let entry = match self.cache.entry(*key) {
824            Entry::Occupied(entry) => entry,
825            Entry::Vacant(_) => return Err(FragmentReassemblyError::InvalidKey),
826        };
827
828        // Make sure we are not missing fragments.
829        if !entry.get().missing_blocks.is_empty() {
830            return Err(FragmentReassemblyError::MissingFragments);
831        }
832        // Remove the entry from the cache now that we've validated that we will
833        // be able to reassemble it.
834        let (_key, data) = entry.remove_entry();
835        self.size -= data.total_size;
836
837        // If we are not missing fragments, we must have header data.
838        assert_matches!(data.header, Some(_));
839
840        // TODO(https://github.com/rust-lang/rust/issues/59278): Use
841        // `BinaryHeap::into_iter_sorted`.
842        let body_fragments = data.body_fragments.into_sorted_vec().into_iter().map(|x| x.data);
843        I::Packet::reassemble_fragmented_packet(buffer, data.header.unwrap(), body_fragments)
844            .map_err(|_| FragmentReassemblyError::PacketParsingError)
845    }
846
847    /// Gets or creates a new entry in the cache for a given `key`.
848    ///
849    /// Returns a tuple whose second component indicates whether a reassembly
850    /// timer needs to be scheduled.
851    fn get_or_create(&mut self, key: FragmentCacheKey<I>) -> (&mut FragmentCacheData, bool) {
852        match self.cache.entry(key) {
853            Entry::Occupied(e) => (e.into_mut(), false),
854            Entry::Vacant(e) => {
855                // We have no reassembly data yet so this fragment is the first
856                // one associated with the given `key`. Create a new entry in
857                // the hash table and let the caller know to schedule a timer to
858                // reset the entry.
859                (e.insert(FragmentCacheData::default()), true)
860            }
861        }
862    }
863
864    fn above_size_threshold(&self) -> bool {
865        self.size >= self.threshold
866    }
867
868    fn increment_size(&mut self, sz: usize) {
869        assert!(!self.above_size_threshold());
870        self.size += sz;
871    }
872
873    fn remove_data(&mut self, key: &FragmentCacheKey<I>) -> Option<FragmentCacheData> {
874        let data = self.cache.remove(key)?;
875        self.size -= data.total_size;
876        Some(data)
877    }
878}
879
880/// Gets the header bytes for a packet.
881fn get_header<B: SplitByteSlice, I: IpExt>(packet: &I::Packet<B>) -> Vec<u8> {
882    match packet.as_ip_addr_ref() {
883        IpAddr::V4(packet) => packet.copy_header_bytes_for_fragment(),
884        IpAddr::V6(packet) => {
885            // We are guaranteed not to panic here because we will only panic if
886            // `packet` does not have a fragment extension header. We can only get
887            // here if `packet` is a fragment packet, so we know that `packet` has a
888            // fragment extension header.
889            packet.copy_header_bytes_for_fragment()
890        }
891    }
892}
893
894/// A fragment of a packet's body.
895#[derive(Debug, PartialEq, Eq)]
896struct PacketBodyFragment {
897    offset: u16,
898    data: Vec<u8>,
899}
900
901impl PacketBodyFragment {
902    /// Constructs a new `PacketBodyFragment` to be stored in a `BinaryHeap`.
903    fn new(offset: u16, data: Vec<u8>) -> Self {
904        PacketBodyFragment { offset, data }
905    }
906}
907
908// The ordering of a `PacketBodyFragment` is only dependant on the fragment
909// offset.
910impl PartialOrd for PacketBodyFragment {
911    fn partial_cmp(&self, other: &PacketBodyFragment) -> Option<Ordering> {
912        Some(self.cmp(other))
913    }
914}
915
916impl Ord for PacketBodyFragment {
917    fn cmp(&self, other: &Self) -> Ordering {
918        self.offset.cmp(&other.offset)
919    }
920}
921
922#[cfg(test)]
923mod tests {
924    use alloc::vec;
925
926    use assert_matches::assert_matches;
927    use ip_test_macro::ip_test;
928    use net_declare::{net_ip_v4, net_ip_v6};
929    use net_types::Witness;
930    use net_types::ip::{Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
931    use netstack3_base::testutil::{
932        FakeBindingsCtx, FakeCoreCtx, FakeInstant, FakeTimerCtxExt, TEST_ADDRS_V4, TEST_ADDRS_V6,
933        assert_empty,
934    };
935    use netstack3_base::{CtxPair, IntoCoreTimerCtx, NetworkSerializationContext};
936    use packet::{Buf, NestablePacketBuilder as _, ParsablePacket, ParseBuffer, Serializer};
937    use packet_formats::ip::{FragmentOffset, IpProto, Ipv6Proto};
938    use packet_formats::ipv4::Ipv4PacketBuilder;
939    use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithFragmentHeader};
940    use test_case::test_case;
941
942    use super::*;
943
944    struct FakeFragmentContext<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
945        cache: IpPacketFragmentCache<I, BT>,
946    }
947
948    impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> FakeFragmentContext<I, BC>
949    where
950        BC::DispatchId: From<FragmentTimerId<I>>,
951    {
952        fn new(bindings_ctx: &mut BC) -> Self {
953            Self { cache: IpPacketFragmentCache::new::<IntoCoreTimerCtx>(bindings_ctx) }
954        }
955    }
956
957    type FakeCtxImpl<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
958    type FakeBindingsCtxImpl<I> = FakeBindingsCtx<FragmentTimerId<I>, (), (), ()>;
959    type FakeCoreCtxImpl<I> = FakeCoreCtx<FakeFragmentContext<I, FakeBindingsCtxImpl<I>>, (), ()>;
960
961    impl<I: ReassemblyIpExt> FragmentContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
962        fn with_state_mut<
963            O,
964            F: FnOnce(&mut IpPacketFragmentCache<I, FakeBindingsCtxImpl<I>>) -> O,
965        >(
966            &mut self,
967            cb: F,
968        ) -> O {
969            cb(&mut self.state.cache)
970        }
971    }
972
973    /// The result `process_ipv4_fragment` or `process_ipv6_fragment` should
974    /// expect after processing a fragment.
975    #[derive(PartialEq)]
976    enum ExpectedResult<I: ReassemblyIpExt> {
977        /// After processing a packet fragment, we should be ready to reassemble
978        /// the packet.
979        ///
980        /// `body_fragment_blocks` is in units of `FRAGMENT_BLOCK_SIZE`.
981        Ready { body_fragment_blocks: u16, key: FragmentCacheKey<I> },
982
983        /// After processing a packet fragment, we need more packet fragments
984        /// before being ready to reassemble the packet.
985        NeedMore,
986
987        /// The packet fragment is invalid.
988        Invalid,
989
990        /// The Cache is full.
991        OutOfMemory,
992    }
993
994    /// Get an IPv4 packet builder.
995    fn get_ipv4_builder() -> Ipv4PacketBuilder {
996        Ipv4PacketBuilder::new(
997            TEST_ADDRS_V4.remote_ip,
998            TEST_ADDRS_V4.local_ip,
999            10,
1000            <Ipv4 as TestIpExt>::PROTOCOL,
1001        )
1002    }
1003
1004    /// Get an IPv6 packet builder.
1005    fn get_ipv6_builder() -> Ipv6PacketBuilder {
1006        Ipv6PacketBuilder::new(
1007            TEST_ADDRS_V6.remote_ip,
1008            TEST_ADDRS_V6.local_ip,
1009            10,
1010            <Ipv6 as TestIpExt>::PROTOCOL,
1011        )
1012    }
1013
1014    /// Validate that IpPacketFragmentCache has correct size.
1015    fn validate_size<I: ReassemblyIpExt, BT: FragmentBindingsTypes>(
1016        cache: &IpPacketFragmentCache<I, BT>,
1017    ) {
1018        let mut sz: usize = 0;
1019
1020        for v in cache.cache.values() {
1021            sz += v.total_size;
1022        }
1023
1024        assert_eq!(sz, cache.size);
1025    }
1026
1027    struct FragmentSpec {
1028        /// The ID of the fragment.
1029        id: u16,
1030        /// The offset of the fragment, in units of `FRAGMENT_BLOCK_SIZE`.
1031        offset: u16,
1032        /// The size of the fragment, in units of `FRAGMENT_BLOCK_SIZE`.
1033        size: u16,
1034        /// The value of the M flag. "True" indicates more fragments.
1035        m_flag: bool,
1036    }
1037
1038    fn expected_packet_size<I: TestIpExt>(num_fragment_blocks: u16) -> usize {
1039        usize::from(num_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE) + I::HEADER_LENGTH
1040    }
1041
1042    /// Generates and processes an IPv4 fragment packet.
1043    fn process_ipv4_fragment<CC: FragmentContext<Ipv4, BC>, BC: FragmentBindingsContext>(
1044        core_ctx: &mut CC,
1045        bindings_ctx: &mut BC,
1046        FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1047        mut builder: Ipv4PacketBuilder,
1048        expected_result: ExpectedResult<Ipv4>,
1049    ) {
1050        builder.id(id);
1051        builder.fragment_offset(FragmentOffset::new(offset).unwrap());
1052        builder.mf_flag(m_flag);
1053        let body = generate_body_fragment(
1054            id,
1055            offset,
1056            usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1057        );
1058
1059        let mut buffer = builder
1060            .wrap_body(Buf::new(body, ..))
1061            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1062            .unwrap();
1063        let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1064
1065        let actual_result =
1066            FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1067        match expected_result {
1068            ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1069                let (key, packet_len) = assert_matches!(
1070                    actual_result,
1071                    FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1072                );
1073                assert_eq!(key, expected_key);
1074                assert_eq!(packet_len, expected_packet_size::<Ipv4>(body_fragment_blocks));
1075            }
1076            ExpectedResult::NeedMore => {
1077                assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1078            }
1079            ExpectedResult::Invalid => {
1080                assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1081            }
1082            ExpectedResult::OutOfMemory => {
1083                assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1084            }
1085        }
1086    }
1087
1088    /// Generates and processes an IPv6 fragment packet.
1089    ///
1090    /// `fragment_offset` and `size` are both in units of `FRAGMENT_BLOCK_SIZE`.
1091    fn process_ipv6_fragment<CC: FragmentContext<Ipv6, BC>, BC: FragmentBindingsContext>(
1092        core_ctx: &mut CC,
1093        bindings_ctx: &mut BC,
1094        FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1095        builder: Ipv6PacketBuilder,
1096        expected_result: ExpectedResult<Ipv6>,
1097    ) {
1098        let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1099            builder,
1100            FragmentOffset::new(offset).unwrap(),
1101            m_flag,
1102            id.into(),
1103        );
1104
1105        let body = generate_body_fragment(
1106            id,
1107            offset,
1108            usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1109        );
1110
1111        let mut buffer = builder
1112            .wrap_body(Buf::new(body, ..))
1113            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1114            .unwrap();
1115        let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1116
1117        let actual_result =
1118            FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1119        match expected_result {
1120            ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1121                let (key, packet_len) = assert_matches!(
1122                    actual_result,
1123                    FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1124                );
1125                assert_eq!(key, expected_key);
1126                assert_eq!(packet_len, expected_packet_size::<Ipv6>(body_fragment_blocks));
1127            }
1128            ExpectedResult::NeedMore => {
1129                assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1130            }
1131            ExpectedResult::Invalid => {
1132                assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1133            }
1134            ExpectedResult::OutOfMemory => {
1135                assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1136            }
1137        }
1138    }
1139
1140    trait TestIpExt: IpExt + netstack3_base::testutil::TestIpExt + ReassemblyIpExt {
1141        const HEADER_LENGTH: usize;
1142
1143        const PROTOCOL: Self::Proto;
1144
1145        fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1146            core_ctx: &mut CC,
1147            bindings_ctx: &mut BC,
1148            spec: FragmentSpec,
1149            expected_result: ExpectedResult<Self>,
1150        );
1151    }
1152
1153    impl TestIpExt for Ipv4 {
1154        const HEADER_LENGTH: usize = packet_formats::ipv4::HDR_PREFIX_LEN;
1155
1156        const PROTOCOL: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1157
1158        fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1159            core_ctx: &mut CC,
1160            bindings_ctx: &mut BC,
1161            spec: FragmentSpec,
1162            expected_result: ExpectedResult<Ipv4>,
1163        ) {
1164            process_ipv4_fragment(core_ctx, bindings_ctx, spec, get_ipv4_builder(), expected_result)
1165        }
1166    }
1167    impl TestIpExt for Ipv6 {
1168        const HEADER_LENGTH: usize = packet_formats::ipv6::IPV6_FIXED_HDR_LEN;
1169
1170        const PROTOCOL: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1171
1172        fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1173            core_ctx: &mut CC,
1174            bindings_ctx: &mut BC,
1175            spec: FragmentSpec,
1176            expected_result: ExpectedResult<Ipv6>,
1177        ) {
1178            process_ipv6_fragment(core_ctx, bindings_ctx, spec, get_ipv6_builder(), expected_result)
1179        }
1180    }
1181
1182    /// Tries to reassemble the packet with the given fragment ID.
1183    ///
1184    /// `body_fragment_blocks` is in units of `FRAGMENT_BLOCK_SIZE`.
1185    fn try_reassemble_ip_packet<
1186        I: TestIpExt + netstack3_base::IpExt,
1187        CC: FragmentContext<I, BC>,
1188        BC: FragmentBindingsContext,
1189    >(
1190        core_ctx: &mut CC,
1191        bindings_ctx: &mut BC,
1192        fragment_id: u16,
1193        body_fragment_blocks: u16,
1194    ) {
1195        let mut buffer: Vec<u8> = vec![
1196            0;
1197            usize::from(body_fragment_blocks)
1198                * usize::from(FRAGMENT_BLOCK_SIZE)
1199                + I::HEADER_LENGTH
1200        ];
1201        let mut buffer = &mut buffer[..];
1202        let key = test_key(fragment_id);
1203
1204        FragmentHandler::reassemble_packet(core_ctx, bindings_ctx, &key, &mut buffer).unwrap();
1205        let packet = I::Packet::parse_mut(&mut buffer, ()).unwrap();
1206
1207        let expected_body = generate_body_fragment(
1208            fragment_id,
1209            0,
1210            usize::from(body_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE),
1211        );
1212        assert_eq!(packet.body(), &expected_body[..]);
1213    }
1214
1215    /// Generates the body of a packet with the given fragment ID, offset, and
1216    /// length.
1217    ///
1218    /// Overlapping body bytes from different calls to `generate_body_fragment`
1219    /// are guaranteed to have the same values.
1220    fn generate_body_fragment(fragment_id: u16, fragment_offset: u16, len: usize) -> Vec<u8> {
1221        // The body contains increasing byte values which start at `fragment_id`
1222        // at byte 0. This ensures that different packets with different
1223        // fragment IDs contain bodies with different byte values.
1224        let start = usize::from(fragment_id)
1225            + usize::from(fragment_offset) * usize::from(FRAGMENT_BLOCK_SIZE);
1226        (start..start + len).map(|byte| byte as u8).collect()
1227    }
1228
1229    /// Gets a `FragmentCacheKey` with hard coded test values.
1230    fn test_key<I: TestIpExt>(id: u16) -> FragmentCacheKey<I> {
1231        #[derive(GenericOverIp)]
1232        #[generic_over_ip(I, Ip)]
1233        struct Wrapper<I: ReassemblyIpExt>(I::FragmentCacheKeyPart);
1234
1235        let Wrapper(ip_specific_fields) =
1236            I::map_ip_out((), |()| Wrapper(Ipv4::PROTOCOL), |()| Wrapper(()));
1237
1238        FragmentCacheKey {
1239            src_ip: I::TEST_ADDRS.remote_ip.get(),
1240            dst_ip: I::TEST_ADDRS.local_ip.get(),
1241            fragment_id: id.into(),
1242            ip_specific_fields,
1243        }
1244    }
1245
1246    fn new_context<I: ReassemblyIpExt>() -> FakeCtxImpl<I> {
1247        FakeCtxImpl::<I>::with_default_bindings_ctx(|bindings_ctx| {
1248            FakeCoreCtxImpl::with_state(FakeFragmentContext::new(bindings_ctx))
1249        })
1250    }
1251
1252    #[test]
1253    fn test_ipv4_reassembly_not_needed() {
1254        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1255
1256        // Test that we don't attempt reassembly if the packet is not
1257        // fragmented.
1258
1259        let builder = get_ipv4_builder();
1260        let body = [1, 2, 3, 4, 5];
1261        let mut buffer = builder
1262            .wrap_body(Buf::new(body.to_vec(), ..))
1263            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1264            .unwrap();
1265        let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1266        assert_matches!(
1267            FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1268            FragmentProcessingState::NotNeeded(unfragmented) if unfragmented.body() == body
1269        );
1270    }
1271
1272    #[test]
1273    #[should_panic(
1274        expected = "internal error: entered unreachable code: Should never call this function if the packet does not have a fragment header"
1275    )]
1276    fn test_ipv6_reassembly_not_needed() {
1277        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1278
1279        // Test that we panic if we call `fragment_data` on a packet that has no
1280        // fragment data.
1281
1282        let builder = get_ipv6_builder();
1283        let mut buffer = builder
1284            .wrap_body(Buf::new(vec![1, 2, 3, 4, 5], ..))
1285            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1286            .unwrap();
1287        let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1288        assert_matches!(
1289            FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1290            FragmentProcessingState::InvalidFragment
1291        );
1292    }
1293
1294    #[ip_test(I)]
1295    #[test_case(1)]
1296    #[test_case(10)]
1297    #[test_case(100)]
1298    fn test_ip_reassembly<I: TestIpExt>(size: u16) {
1299        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1300        let id = 5;
1301
1302        // Test that we properly reassemble fragmented packets.
1303
1304        // Process fragment #0
1305        I::process_ip_fragment(
1306            &mut core_ctx,
1307            &mut bindings_ctx,
1308            FragmentSpec { id, offset: 0, size, m_flag: true },
1309            ExpectedResult::NeedMore,
1310        );
1311
1312        // Process fragment #1
1313        I::process_ip_fragment(
1314            &mut core_ctx,
1315            &mut bindings_ctx,
1316            FragmentSpec { id, offset: size, size, m_flag: true },
1317            ExpectedResult::NeedMore,
1318        );
1319
1320        // Process fragment #2
1321        I::process_ip_fragment(
1322            &mut core_ctx,
1323            &mut bindings_ctx,
1324            FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1325            ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1326        );
1327
1328        try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id, 3 * size);
1329    }
1330
1331    #[test]
1332    fn test_ipv4_key_uniqueness() {
1333        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1334
1335        const RIGHT_SRC: Ipv4Addr = net_ip_v4!("192.0.2.1");
1336        const WRONG_SRC: Ipv4Addr = net_ip_v4!("192.0.2.2");
1337
1338        const RIGHT_DST: Ipv4Addr = net_ip_v4!("192.0.2.3");
1339        const WRONG_DST: Ipv4Addr = net_ip_v4!("192.0.2.4");
1340
1341        const RIGHT_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1342        const WRONG_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Udp);
1343
1344        const RIGHT_ID: u16 = 1;
1345        const WRONG_ID: u16 = 2;
1346
1347        const TTL: u8 = 1;
1348
1349        // Process fragment #0.
1350        process_ipv4_fragment(
1351            &mut core_ctx,
1352            &mut bindings_ctx,
1353            FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1354            Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1355            ExpectedResult::NeedMore,
1356        );
1357
1358        // Process fragment #1 under a different key, and verify it doesn't
1359        // complete the packet.
1360        for (id, src, dst, proto) in [
1361            (RIGHT_ID, RIGHT_SRC, RIGHT_DST, WRONG_PROTO),
1362            (RIGHT_ID, RIGHT_SRC, WRONG_DST, RIGHT_PROTO),
1363            (RIGHT_ID, WRONG_SRC, RIGHT_DST, RIGHT_PROTO),
1364            (WRONG_ID, RIGHT_SRC, RIGHT_DST, RIGHT_PROTO),
1365        ] {
1366            process_ipv4_fragment(
1367                &mut core_ctx,
1368                &mut bindings_ctx,
1369                FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1370                Ipv4PacketBuilder::new(src, dst, TTL, proto),
1371                ExpectedResult::NeedMore,
1372            );
1373        }
1374
1375        // Finally, process fragment #1 under the correct key, and verify the
1376        // packet is completed.
1377        const KEY: FragmentCacheKey<Ipv4> = FragmentCacheKey {
1378            src_ip: RIGHT_SRC,
1379            dst_ip: RIGHT_DST,
1380            fragment_id: RIGHT_ID as u32,
1381            ip_specific_fields: RIGHT_PROTO,
1382        };
1383        process_ipv4_fragment(
1384            &mut core_ctx,
1385            &mut bindings_ctx,
1386            FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1387            Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1388            ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1389        );
1390        let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv4>(2)];
1391        let mut buffer = &mut buffer[..];
1392        FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1393            .expect("reassembly should succeed");
1394        let _packet = Ipv4Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1395    }
1396
1397    #[test]
1398    fn test_ipv6_key_uniqueness() {
1399        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1400
1401        const RIGHT_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1402        const WRONG_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1403
1404        const RIGHT_DST: Ipv6Addr = net_ip_v6!("2001:0db8::3");
1405        const WRONG_DST: Ipv6Addr = net_ip_v6!("2001:0db8::4");
1406
1407        const RIGHT_ID: u16 = 1;
1408        const WRONG_ID: u16 = 2;
1409
1410        const TTL: u8 = 1;
1411
1412        // Process fragment #0.
1413        process_ipv6_fragment(
1414            &mut core_ctx,
1415            &mut bindings_ctx,
1416            FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1417            Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1418            ExpectedResult::NeedMore,
1419        );
1420
1421        // Process fragment #1 under a different key, and verify it doesn't
1422        // complete the packet.
1423        for (id, src, dst) in [
1424            (RIGHT_ID, RIGHT_SRC, WRONG_DST),
1425            (RIGHT_ID, WRONG_SRC, RIGHT_DST),
1426            (WRONG_ID, RIGHT_SRC, RIGHT_DST),
1427        ] {
1428            process_ipv6_fragment(
1429                &mut core_ctx,
1430                &mut bindings_ctx,
1431                FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1432                Ipv6PacketBuilder::new(src, dst, TTL, Ipv6::PROTOCOL),
1433                ExpectedResult::NeedMore,
1434            );
1435        }
1436
1437        // Finally, process fragment #1 under the correct key, and verify the
1438        // packet is completed.
1439        const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1440            src_ip: RIGHT_SRC,
1441            dst_ip: RIGHT_DST,
1442            fragment_id: RIGHT_ID as u32,
1443            ip_specific_fields: (),
1444        };
1445        process_ipv6_fragment(
1446            &mut core_ctx,
1447            &mut bindings_ctx,
1448            FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1449            Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1450            ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1451        );
1452        let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1453        let mut buffer = &mut buffer[..];
1454        FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1455            .expect("reassembly should succeed");
1456        let _packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1457    }
1458
1459    #[test]
1460    fn test_ipv6_reassemble_different_protocols() {
1461        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1462
1463        const SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1464        const DST: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1465        const ID: u16 = 1;
1466        const TTL: u8 = 1;
1467
1468        const PROTO1: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1469        const PROTO2: Ipv6Proto = Ipv6Proto::Proto(IpProto::Udp);
1470
1471        // Process fragment #0 (uses `PROTO1`).
1472        process_ipv6_fragment(
1473            &mut core_ctx,
1474            &mut bindings_ctx,
1475            FragmentSpec { id: ID, offset: 0, size: 1, m_flag: true },
1476            Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO1),
1477            ExpectedResult::NeedMore,
1478        );
1479
1480        // Process fragment #1 (uses `PROTO2`).
1481        // The packet should successfully reassemble, using the protocol from
1482        // fragment #0 (i.e. `PROTO1`).
1483        const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1484            src_ip: SRC,
1485            dst_ip: DST,
1486            fragment_id: ID as u32,
1487            ip_specific_fields: (),
1488        };
1489        process_ipv6_fragment(
1490            &mut core_ctx,
1491            &mut bindings_ctx,
1492            FragmentSpec { id: ID, offset: 1, size: 1, m_flag: false },
1493            Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO2),
1494            ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1495        );
1496        let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1497        let mut buffer = &mut buffer[..];
1498        FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1499            .expect("reassembly should succeed");
1500        let packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1501        assert_eq!(packet.proto(), PROTO1);
1502    }
1503
1504    #[ip_test(I)]
1505    #[test_case(1)]
1506    #[test_case(10)]
1507    #[test_case(100)]
1508    fn test_ip_reassemble_with_missing_blocks<I: TestIpExt>(size: u16) {
1509        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1510        let id = 5;
1511
1512        // Test the error we get when we attempt to reassemble with missing
1513        // fragments.
1514
1515        // Process fragment #0
1516        I::process_ip_fragment(
1517            &mut core_ctx,
1518            &mut bindings_ctx,
1519            FragmentSpec { id, offset: 0, size, m_flag: true },
1520            ExpectedResult::NeedMore,
1521        );
1522
1523        // Process fragment #2
1524        I::process_ip_fragment(
1525            &mut core_ctx,
1526            &mut bindings_ctx,
1527            FragmentSpec { id, offset: size, size, m_flag: true },
1528            ExpectedResult::NeedMore,
1529        );
1530
1531        let mut buffer: Vec<u8> = vec![0; 1];
1532        let mut buffer = &mut buffer[..];
1533        let key = test_key(id);
1534        assert_eq!(
1535            FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1536                .unwrap_err(),
1537            FragmentReassemblyError::MissingFragments,
1538        );
1539    }
1540
1541    #[ip_test(I)]
1542    fn test_ip_reassemble_after_timer<I: TestIpExt>() {
1543        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1544        let id = 5;
1545        let key = test_key::<I>(id);
1546
1547        // Make sure no timers in the dispatcher yet.
1548        bindings_ctx.timers.assert_no_timers_installed();
1549        assert_eq!(core_ctx.state.cache.size, 0);
1550
1551        // Test that we properly reset fragment cache on timer.
1552
1553        // Process fragment #0
1554        I::process_ip_fragment(
1555            &mut core_ctx,
1556            &mut bindings_ctx,
1557            FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1558            ExpectedResult::NeedMore,
1559        );
1560
1561        // Make sure a timer got added.
1562        core_ctx.state.cache.timers.assert_timers([(
1563            key,
1564            (),
1565            FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1566        )]);
1567        validate_size(&core_ctx.state.cache);
1568
1569        // Process fragment #1
1570        I::process_ip_fragment(
1571            &mut core_ctx,
1572            &mut bindings_ctx,
1573            FragmentSpec { id, offset: 1, size: 1, m_flag: true },
1574            ExpectedResult::NeedMore,
1575        );
1576        // Make sure no new timers got added or fired.
1577        core_ctx.state.cache.timers.assert_timers([(
1578            key,
1579            (),
1580            FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1581        )]);
1582        validate_size(&core_ctx.state.cache);
1583
1584        // Process fragment #2
1585        I::process_ip_fragment(
1586            &mut core_ctx,
1587            &mut bindings_ctx,
1588            FragmentSpec { id, offset: 2, size: 1, m_flag: false },
1589            ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id) },
1590        );
1591        // Make sure no new timers got added or fired.
1592        core_ctx.state.cache.timers.assert_timers([(
1593            key,
1594            (),
1595            FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1596        )]);
1597        validate_size(&core_ctx.state.cache);
1598
1599        // Trigger the timer (simulate a timer for the fragmented packet).
1600        assert_eq!(
1601            bindings_ctx.trigger_next_timer(&mut core_ctx),
1602            Some(FragmentTimerId::<I>::default())
1603        );
1604
1605        // Make sure no other times exist..
1606        bindings_ctx.timers.assert_no_timers_installed();
1607        assert_eq!(core_ctx.state.cache.size, 0);
1608
1609        // Attempt to reassemble the packet but get an error since the fragment
1610        // data would have been reset/cleared.
1611        let key = test_key(id);
1612        let packet_len = 44;
1613        let mut buffer: Vec<u8> = vec![0; packet_len];
1614        let mut buffer = &mut buffer[..];
1615        assert_eq!(
1616            FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1617                .unwrap_err(),
1618            FragmentReassemblyError::InvalidKey,
1619        );
1620    }
1621
1622    #[ip_test(I)]
1623    #[test_case(1)]
1624    #[test_case(10)]
1625    #[test_case(100)]
1626    fn test_ip_fragment_cache_oom<I: TestIpExt>(size: u16) {
1627        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1628        let mut id = 0;
1629        const THRESHOLD: usize = 8196usize;
1630
1631        assert_eq!(core_ctx.state.cache.size, 0);
1632        core_ctx.state.cache.threshold = THRESHOLD;
1633
1634        // Test that when cache size exceeds the threshold, process_fragment
1635        // returns OOM.
1636        while core_ctx.state.cache.size + usize::from(size) <= THRESHOLD {
1637            I::process_ip_fragment(
1638                &mut core_ctx,
1639                &mut bindings_ctx,
1640                FragmentSpec { id, offset: 0, size, m_flag: true },
1641                ExpectedResult::NeedMore,
1642            );
1643            validate_size(&core_ctx.state.cache);
1644            id += 1;
1645        }
1646
1647        // Now that the cache is at or above the threshold, observe OOM.
1648        I::process_ip_fragment(
1649            &mut core_ctx,
1650            &mut bindings_ctx,
1651            FragmentSpec { id, offset: 0, size, m_flag: true },
1652            ExpectedResult::OutOfMemory,
1653        );
1654        validate_size(&core_ctx.state.cache);
1655
1656        // Trigger the timers, which will clear the cache.
1657        let _timers = bindings_ctx
1658            .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1659        assert_eq!(core_ctx.state.cache.size, 0);
1660        validate_size(&core_ctx.state.cache);
1661
1662        // Can process fragments again.
1663        I::process_ip_fragment(
1664            &mut core_ctx,
1665            &mut bindings_ctx,
1666            FragmentSpec { id, offset: 0, size, m_flag: true },
1667            ExpectedResult::NeedMore,
1668        );
1669    }
1670
1671    #[ip_test(I)]
1672    #[test_case(1)]
1673    #[test_case(10)]
1674    #[test_case(100)]
1675    fn test_unordered_fragments<I: TestIpExt>(size: u16) {
1676        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1677        let id = 5;
1678
1679        // Process fragment #0
1680        I::process_ip_fragment(
1681            &mut core_ctx,
1682            &mut bindings_ctx,
1683            FragmentSpec { id, offset: 0, size, m_flag: true },
1684            ExpectedResult::NeedMore,
1685        );
1686
1687        // Process fragment #2
1688        I::process_ip_fragment(
1689            &mut core_ctx,
1690            &mut bindings_ctx,
1691            FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1692            ExpectedResult::NeedMore,
1693        );
1694
1695        // Process fragment #1
1696        I::process_ip_fragment(
1697            &mut core_ctx,
1698            &mut bindings_ctx,
1699            FragmentSpec { id, offset: size, size, m_flag: true },
1700            ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1701        );
1702    }
1703
1704    #[ip_test(I)]
1705    #[test_case(1)]
1706    #[test_case(10)]
1707    #[test_case(100)]
1708    fn test_ip_duplicate_fragment<I: TestIpExt>(size: u16) {
1709        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1710        let id = 5;
1711
1712        // Process fragment #0
1713        I::process_ip_fragment(
1714            &mut core_ctx,
1715            &mut bindings_ctx,
1716            FragmentSpec { id, offset: 0, size, m_flag: true },
1717            ExpectedResult::NeedMore,
1718        );
1719
1720        // Process the exact same fragment over again. It should be ignored.
1721        I::process_ip_fragment(
1722            &mut core_ctx,
1723            &mut bindings_ctx,
1724            FragmentSpec { id, offset: 0, size, m_flag: true },
1725            ExpectedResult::NeedMore,
1726        );
1727
1728        // Verify that the fragment's cache is intact by sending the remaining
1729        // fragment.
1730        I::process_ip_fragment(
1731            &mut core_ctx,
1732            &mut bindings_ctx,
1733            FragmentSpec { id, offset: size, size, m_flag: false },
1734            ExpectedResult::Ready { body_fragment_blocks: 2 * size, key: test_key(id) },
1735        );
1736
1737        try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id, 2 * size);
1738    }
1739
1740    #[ip_test(I)]
1741    #[test_case(1)]
1742    #[test_case(10)]
1743    #[test_case(100)]
1744    fn test_ip_out_of_bounds_fragment<I: TestIpExt>(size: u16) {
1745        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1746        let id = 5;
1747
1748        // Process fragment #1
1749        I::process_ip_fragment(
1750            &mut core_ctx,
1751            &mut bindings_ctx,
1752            FragmentSpec { id, offset: size, size, m_flag: false },
1753            ExpectedResult::NeedMore,
1754        );
1755
1756        // Process a fragment after fragment #1. It should be deemed invalid
1757        // because fragment #1 was the end.
1758        I::process_ip_fragment(
1759            &mut core_ctx,
1760            &mut bindings_ctx,
1761            FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1762            ExpectedResult::Invalid,
1763        );
1764    }
1765
1766    #[ip_test(I)]
1767    #[test_case(50, 100; "overlaps_front")]
1768    #[test_case(150, 100; "overlaps_back")]
1769    #[test_case(50, 200; "overlaps_both")]
1770    fn test_ip_overlapping_fragment<I: TestIpExt>(offset: u16, size: u16) {
1771        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1772        let id = 5;
1773
1774        // Process fragment #0
1775        I::process_ip_fragment(
1776            &mut core_ctx,
1777            &mut bindings_ctx,
1778            FragmentSpec { id, offset: 100, size: 100, m_flag: true },
1779            ExpectedResult::NeedMore,
1780        );
1781
1782        // Process a fragment that overlaps with fragment 0. It should be deemed
1783        // invalid.
1784        I::process_ip_fragment(
1785            &mut core_ctx,
1786            &mut bindings_ctx,
1787            FragmentSpec { id, offset, size, m_flag: true },
1788            ExpectedResult::Invalid,
1789        );
1790    }
1791
1792    #[test]
1793    fn test_ipv4_fragment_not_multiple_of_offset_unit() {
1794        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1795        let id = 0;
1796
1797        assert_eq!(core_ctx.state.cache.size, 0);
1798        // Test that fragment bodies must be a multiple of
1799        // `FRAGMENT_BLOCK_SIZE`, except for the last fragment.
1800
1801        // Process fragment #0
1802        process_ipv4_fragment(
1803            &mut core_ctx,
1804            &mut bindings_ctx,
1805            FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1806            get_ipv4_builder(),
1807            ExpectedResult::NeedMore,
1808        );
1809
1810        // Process fragment #1 (body size is not a multiple of
1811        // `FRAGMENT_BLOCK_SIZE` and more flag is `true`).
1812        let mut builder = get_ipv4_builder();
1813        builder.id(id);
1814        builder.fragment_offset(FragmentOffset::new(1).unwrap());
1815        builder.mf_flag(true);
1816        // Body with 1 byte less than `FRAGMENT_BLOCK_SIZE` so it is not a
1817        // multiple of `FRAGMENT_BLOCK_SIZE`.
1818        let mut body: Vec<u8> = Vec::new();
1819        body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
1820        let mut buffer = builder
1821            .wrap_body(Buf::new(body, ..))
1822            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1823            .unwrap();
1824        let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1825        assert_matches!(
1826            FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1827            FragmentProcessingState::InvalidFragment
1828        );
1829
1830        // Process fragment #1 (body size is not a multiple of
1831        // `FRAGMENT_BLOCK_SIZE` but more flag is `false`). The last fragment is
1832        // allowed to not be a multiple of `FRAGMENT_BLOCK_SIZE`.
1833        let mut builder = get_ipv4_builder();
1834        builder.id(id);
1835        builder.fragment_offset(FragmentOffset::new(1).unwrap());
1836        builder.mf_flag(false);
1837        // Body with 1 byte less than `FRAGMENT_BLOCK_SIZE` so it is not a
1838        // multiple of `FRAGMENT_BLOCK_SIZE`.
1839        let mut body: Vec<u8> = Vec::new();
1840        body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
1841        let mut buffer = builder
1842            .wrap_body(Buf::new(body, ..))
1843            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1844            .unwrap();
1845        let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1846        let (key, packet_len) = assert_matches!(
1847            FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1848            FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1849        );
1850        assert_eq!(key, test_key(id));
1851        assert_eq!(packet_len, 35);
1852        validate_size(&core_ctx.state.cache);
1853        let mut buffer: Vec<u8> = vec![0; packet_len];
1854        let mut buffer = &mut buffer[..];
1855        FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1856            .unwrap();
1857        let packet = Ipv4Packet::parse_mut(&mut buffer, ()).unwrap();
1858        let mut expected_body: Vec<u8> = Vec::new();
1859        expected_body.extend(0..15);
1860        assert_eq!(packet.body(), &expected_body[..]);
1861        assert_eq!(core_ctx.state.cache.size, 0);
1862    }
1863
1864    #[test]
1865    fn test_ipv6_fragment_not_multiple_of_offset_unit() {
1866        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1867        let id = 0;
1868
1869        assert_eq!(core_ctx.state.cache.size, 0);
1870        // Test that fragment bodies must be a multiple of
1871        // `FRAGMENT_BLOCK_SIZE`, except for the last fragment.
1872
1873        // Process fragment #0
1874        process_ipv6_fragment(
1875            &mut core_ctx,
1876            &mut bindings_ctx,
1877            FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1878            get_ipv6_builder(),
1879            ExpectedResult::NeedMore,
1880        );
1881
1882        // Process fragment #1 (body size is not a multiple of
1883        // `FRAGMENT_BLOCK_SIZE` and more flag is `true`).
1884        let offset = 1;
1885        let body_size: usize = (FRAGMENT_BLOCK_SIZE - 1).into();
1886        let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1887            get_ipv6_builder(),
1888            FragmentOffset::new(offset).unwrap(),
1889            true,
1890            id.into(),
1891        );
1892        let body = generate_body_fragment(id, offset, body_size);
1893        let mut buffer = builder
1894            .wrap_body(Buf::new(body, ..))
1895            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1896            .unwrap();
1897        let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1898        assert_matches!(
1899            FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1900            FragmentProcessingState::InvalidFragment
1901        );
1902
1903        // Process fragment #1 (body size is not a multiple of
1904        // `FRAGMENT_BLOCK_SIZE` but more flag is `false`). The last fragment is
1905        // allowed to not be a multiple of `FRAGMENT_BLOCK_SIZE`.
1906        let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1907            get_ipv6_builder(),
1908            FragmentOffset::new(offset).unwrap(),
1909            false,
1910            id.into(),
1911        );
1912        let body = generate_body_fragment(id, offset, body_size);
1913        let mut buffer = builder
1914            .wrap_body(Buf::new(body, ..))
1915            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1916            .unwrap();
1917        let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1918        let (key, packet_len) = assert_matches!(
1919            FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1920            FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1921        );
1922        assert_eq!(key, test_key(id));
1923        assert_eq!(packet_len, 55);
1924
1925        validate_size(&core_ctx.state.cache);
1926        let mut buffer: Vec<u8> = vec![0; packet_len];
1927        let mut buffer = &mut buffer[..];
1928        FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1929            .unwrap();
1930        let packet = Ipv6Packet::parse_mut(&mut buffer, ()).unwrap();
1931        let mut expected_body: Vec<u8> = Vec::new();
1932        expected_body.extend(0..15);
1933        assert_eq!(packet.body(), &expected_body[..]);
1934        assert_eq!(core_ctx.state.cache.size, 0);
1935    }
1936
1937    #[ip_test(I)]
1938    fn test_ip_reassembly_with_multiple_intertwined_packets<I: TestIpExt>() {
1939        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1940        const SIZE: u16 = 1;
1941        let id_0 = 5;
1942        let id_1 = 10;
1943
1944        // Test that we properly reassemble fragmented packets when they arrive
1945        // intertwined with other packets' fragments.
1946
1947        // Process fragment #0 for packet #0
1948        I::process_ip_fragment(
1949            &mut core_ctx,
1950            &mut bindings_ctx,
1951            FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
1952            ExpectedResult::NeedMore,
1953        );
1954
1955        // Process fragment #0 for packet #1
1956        I::process_ip_fragment(
1957            &mut core_ctx,
1958            &mut bindings_ctx,
1959            FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
1960            ExpectedResult::NeedMore,
1961        );
1962
1963        // Process fragment #1 for packet #0
1964        I::process_ip_fragment(
1965            &mut core_ctx,
1966            &mut bindings_ctx,
1967            FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
1968            ExpectedResult::NeedMore,
1969        );
1970
1971        // Process fragment #1 for packet #0
1972        I::process_ip_fragment(
1973            &mut core_ctx,
1974            &mut bindings_ctx,
1975            FragmentSpec { id: id_1, offset: 1, size: SIZE, m_flag: true },
1976            ExpectedResult::NeedMore,
1977        );
1978
1979        // Process fragment #2 for packet #0
1980        I::process_ip_fragment(
1981            &mut core_ctx,
1982            &mut bindings_ctx,
1983            FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
1984            ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
1985        );
1986
1987        try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_0, 3);
1988
1989        // Process fragment #2 for packet #1
1990        I::process_ip_fragment(
1991            &mut core_ctx,
1992            &mut bindings_ctx,
1993            FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
1994            ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_1) },
1995        );
1996
1997        try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_1, 3);
1998    }
1999
2000    #[ip_test(I)]
2001    fn test_ip_reassembly_timer_with_multiple_intertwined_packets<I: TestIpExt>() {
2002        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2003        const SIZE: u16 = 1;
2004        let id_0 = 5;
2005        let id_1 = 10;
2006        let id_2 = 15;
2007
2008        // Test that we properly timer with multiple intertwined packets that
2009        // all arrive out of order. We expect packet 1 and 3 to succeed, and
2010        // packet 1 to fail due to the reassembly timer.
2011        //
2012        // The flow of events:
2013        //   T=0:
2014        //     - Packet #0, Fragment #0 arrives (timer scheduled for T=60s).
2015        //     - Packet #1, Fragment #2 arrives (timer scheduled for T=60s).
2016        //     - Packet #2, Fragment #2 arrives (timer scheduled for T=60s).
2017        //   T=BEFORE_TIMEOUT1:
2018        //     - Packet #0, Fragment #2 arrives.
2019        //   T=BEFORE_TIMEOUT2:
2020        //     - Packet #2, Fragment #1 arrives.
2021        //     - Packet #0, Fragment #1 arrives (timer cancelled since all
2022        //       fragments arrived).
2023        //   T=BEFORE_TIMEOUT3:
2024        //     - Packet #1, Fragment #0 arrives.
2025        //     - Packet #2, Fragment #0 arrives (timer cancelled since all
2026        //       fragments arrived).
2027        //   T=TIMEOUT:
2028        //     - Timeout for reassembly of Packet #1.
2029        //     - Packet #1, Fragment #1 arrives (final fragment but timer
2030        //       already triggered so fragment not complete).
2031
2032        const BEFORE_TIMEOUT1: Duration = Duration::from_secs(1);
2033        const BEFORE_TIMEOUT2: Duration = Duration::from_secs(2);
2034        const BEFORE_TIMEOUT3: Duration = Duration::from_secs(3);
2035        assert!(BEFORE_TIMEOUT1 < I::REASSEMBLY_TIMEOUT);
2036        assert!(BEFORE_TIMEOUT2 < I::REASSEMBLY_TIMEOUT);
2037        assert!(BEFORE_TIMEOUT3 < I::REASSEMBLY_TIMEOUT);
2038
2039        // Process fragment #0 for packet #0
2040        I::process_ip_fragment(
2041            &mut core_ctx,
2042            &mut bindings_ctx,
2043            FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
2044            ExpectedResult::NeedMore,
2045        );
2046
2047        // Process fragment #1 for packet #1
2048        I::process_ip_fragment(
2049            &mut core_ctx,
2050            &mut bindings_ctx,
2051            FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
2052            ExpectedResult::NeedMore,
2053        );
2054
2055        // Process fragment #2 for packet #2
2056        I::process_ip_fragment(
2057            &mut core_ctx,
2058            &mut bindings_ctx,
2059            FragmentSpec { id: id_2, offset: 2, size: SIZE, m_flag: false },
2060            ExpectedResult::NeedMore,
2061        );
2062
2063        // Advance time.
2064        assert_empty(
2065            bindings_ctx
2066                .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT1), &mut core_ctx),
2067        );
2068
2069        // Process fragment #2 for packet #0
2070        I::process_ip_fragment(
2071            &mut core_ctx,
2072            &mut bindings_ctx,
2073            FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
2074            ExpectedResult::NeedMore,
2075        );
2076
2077        // Advance time.
2078        assert_empty(
2079            bindings_ctx
2080                .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT2), &mut core_ctx),
2081        );
2082
2083        // Process fragment #1 for packet #2
2084        I::process_ip_fragment(
2085            &mut core_ctx,
2086            &mut bindings_ctx,
2087            FragmentSpec { id: id_2, offset: 1, size: SIZE, m_flag: true },
2088            ExpectedResult::NeedMore,
2089        );
2090
2091        // Process fragment #1 for packet #0
2092        I::process_ip_fragment(
2093            &mut core_ctx,
2094            &mut bindings_ctx,
2095            FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
2096            ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
2097        );
2098
2099        try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_0, 3);
2100
2101        // Advance time.
2102        assert_empty(
2103            bindings_ctx
2104                .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT3), &mut core_ctx),
2105        );
2106
2107        // Process fragment #0 for packet #1
2108        I::process_ip_fragment(
2109            &mut core_ctx,
2110            &mut bindings_ctx,
2111            FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
2112            ExpectedResult::NeedMore,
2113        );
2114
2115        // Process fragment #0 for packet #2
2116        I::process_ip_fragment(
2117            &mut core_ctx,
2118            &mut bindings_ctx,
2119            FragmentSpec { id: id_2, offset: 0, size: SIZE, m_flag: true },
2120            ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_2) },
2121        );
2122
2123        try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_2, 3);
2124
2125        // Advance time to the timeout, triggering the timer for the reassembly
2126        // of packet #1
2127        bindings_ctx.trigger_timers_until_and_expect_unordered(
2128            FakeInstant::from(I::REASSEMBLY_TIMEOUT),
2129            [FragmentTimerId::<I>::default()],
2130            &mut core_ctx,
2131        );
2132
2133        // Make sure no other times exist.
2134        bindings_ctx.timers.assert_no_timers_installed();
2135
2136        // Process fragment #2 for packet #1 Should get a need more return value
2137        // since even though we technically received all the fragments, the last
2138        // fragment didn't arrive until after the reassembly timer.
2139        I::process_ip_fragment(
2140            &mut core_ctx,
2141            &mut bindings_ctx,
2142            FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: true },
2143            ExpectedResult::NeedMore,
2144        );
2145    }
2146
2147    #[test]
2148    fn test_no_more_fragments_in_middle_of_block() {
2149        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2150        process_ipv4_fragment(
2151            &mut core_ctx,
2152            &mut bindings_ctx,
2153            FragmentSpec { id: 0, offset: 100, size: 1, m_flag: false },
2154            get_ipv4_builder(),
2155            ExpectedResult::NeedMore,
2156        );
2157
2158        process_ipv4_fragment(
2159            &mut core_ctx,
2160            &mut bindings_ctx,
2161            FragmentSpec { id: 0, offset: 50, size: 1, m_flag: false },
2162            get_ipv4_builder(),
2163            ExpectedResult::Invalid,
2164        );
2165    }
2166
2167    #[ip_test(I)]
2168    fn test_cancel_timer_on_overlap<I: TestIpExt>() {
2169        const FRAGMENT_ID: u16 = 1;
2170
2171        let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2172
2173        let key = test_key(FRAGMENT_ID);
2174
2175        // Do this a couple times to make sure that new packets matching the
2176        // invalid packet's fragment cache key create a new entry.
2177        for _ in 0..=2 {
2178            I::process_ip_fragment(
2179                &mut core_ctx,
2180                &mut bindings_ctx,
2181                FragmentSpec { id: FRAGMENT_ID, offset: 0, size: 10, m_flag: true },
2182                ExpectedResult::NeedMore,
2183            );
2184            core_ctx
2185                .state
2186                .cache
2187                .timers
2188                .assert_timers_after(&mut bindings_ctx, [(key, (), I::REASSEMBLY_TIMEOUT)]);
2189
2190            I::process_ip_fragment(
2191                &mut core_ctx,
2192                &mut bindings_ctx,
2193                FragmentSpec { id: FRAGMENT_ID, offset: 5, size: 10, m_flag: true },
2194                ExpectedResult::Invalid,
2195            );
2196            assert_eq!(bindings_ctx.timers.timers(), [],);
2197        }
2198    }
2199}