Skip to main content

netstack3_ip/
reassembly.rs

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