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