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