Skip to main content

netstack3_ip/
fragmentation.rs

1// Copyright 2024 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//! IP fragmentation support.
6
7use core::borrow::Borrow;
8use core::fmt::Debug;
9
10use alloc::vec::Vec;
11
12use net_types::ip::{GenericOverIp, Ip, IpInvariant, Ipv4, Ipv6, Mtu};
13use netstack3_base::{Counter, NetworkSerializationContext, NetworkSerializer, RngContext};
14use netstack3_filter::ForwardedPacket;
15use packet::{
16    Buf, BufferMut, EmptyBuf, FragmentedBuffer as _, InnerPacketBuilder as _,
17    NestablePacketBuilder, Nested, PacketBuilder, PacketConstraints, ParsablePacket,
18    SerializeError,
19};
20use packet_formats::ip::FragmentOffset;
21use packet_formats::ipv4::options::Ipv4Option;
22use packet_formats::ipv4::{
23    Ipv4Header as _, Ipv4PacketBuilder, Ipv4PacketBuilderWithOptions, Ipv4PacketRaw,
24};
25use packet_formats::ipv6::ext_hdrs::IPV6_FRAGMENT_EXT_HDR_LEN;
26use packet_formats::ipv6::{
27    Ipv6Packet, Ipv6PacketBuilderBeforeFragment, Ipv6PacketBuilderWithFragmentHeader,
28    Ipv6PerFragmentHeaderBuilder,
29};
30use rand::RngExt as _;
31
32/// The maximum fragment offset that can be expressed in both IPv4 and IPv6
33/// headers. The maximum transmissible body is this value plus the maximum bytes
34/// transmitted in the last fragment.
35// We have 13 bits to express an 8-byte multiple offset.
36const MAX_FRAGMENT_OFFSET: usize = ((1 << 13) - 1) * 8;
37
38pub trait FragmentationIpExt:
39    packet_formats::ip::IpExt<
40        PacketBuilder<NetworkSerializationContext>: AsFragmentableIpPacketBuilder<Self>,
41    >
42{
43    /// The IP packet builder for a forwarded packet.
44    type ForwardedFragmentBuilder: FragmentableIpPacketBuilder<Self>;
45    /// An identifier generated at fragmentation time.
46    type FragmentationId: Copy + Debug;
47}
48
49impl FragmentationIpExt for Ipv4 {
50    type ForwardedFragmentBuilder = ForwardedIpv4PacketBuilder;
51    type FragmentationId = ();
52}
53
54impl FragmentationIpExt for Ipv6 {
55    type ForwardedFragmentBuilder = ForwardedIpv6PacketBuilder;
56    type FragmentationId = u32;
57}
58
59/// Fragmentation errors
60#[derive(Debug, Eq, PartialEq, GenericOverIp)]
61#[generic_over_ip()]
62pub enum FragmentationError {
63    /// Fragmentation not allowed.
64    NotAllowed,
65    /// MTU is too small, headers don't fit.
66    MtuTooSmall,
67    /// Body is too long to be fragmented.
68    BodyTooLong,
69    /// Inner serializer reported a size limited exceeded.
70    SizeLimitExceeded,
71}
72
73/// A [`Serializer`] capable of splitting itself into a packet builder and a
74/// pre-serialized body for fragmentation.
75// TODO(https://fxbug.dev/42148826): Ideally we'd be able to generate fragments
76// without requiring the IP body to be dumped into a Vec first. Update this when
77// that support is available in packet and packet_formats.
78pub trait FragmentableIpSerializer<I: FragmentationIpExt>: NetworkSerializer {
79    /// The builder for each fragment.
80    type Builder<'a>: FragmentableIpPacketBuilder<I>
81    where
82        Self: 'a;
83    /// The body to be fragmented.
84    ///
85    /// Note that this API is not attempting to reuse buffers in any way. There
86    /// are improvements that can be made here to perhaps avoid allocations and
87    /// yield out reusable bodies, but we're constrained to taking references to
88    /// the serializers here to avoid changing the body which could interfere
89    /// with the higher layers on errors.
90    type Body<'a>: AsRef<[u8]>
91    where
92        Self: 'a;
93
94    /// Returns the inner packet builder for this IP version and a serialized
95    /// body.
96    fn builder_and_body(&self) -> Result<(Self::Builder<'_>, Self::Body<'_>), FragmentationError>;
97}
98
99impl<I, S, B> FragmentableIpSerializer<I> for Nested<S, B>
100where
101    I: FragmentationIpExt,
102    S: NetworkSerializer,
103    B: AsFragmentableIpPacketBuilder<I> + PacketBuilder<NetworkSerializationContext>,
104{
105    type Builder<'a>
106        = B::Builder<'a>
107    where
108        Self: 'a;
109
110    type Body<'a>
111        = Buf<Vec<u8>>
112    where
113        Self: 'a;
114
115    fn builder_and_body(&self) -> Result<(Self::Builder<'_>, Self::Body<'_>), FragmentationError> {
116        let builder = self.outer().try_as_fragmentable()?;
117        let body = self
118            .inner()
119            .serialize_new_buf(
120                &mut NetworkSerializationContext::default(),
121                PacketConstraints::UNCONSTRAINED,
122                packet::new_buf_vec,
123            )
124            .map_err(|e| match e {
125                SerializeError::SizeLimitExceeded => FragmentationError::SizeLimitExceeded,
126            })?;
127        Ok((builder, body))
128    }
129}
130
131#[derive(Debug, Eq, PartialEq, Copy, Clone)]
132pub enum FragmentPosition {
133    First,
134    Middle,
135    Last,
136}
137
138/// The header size constraints for `FragmentableIpPacketBuilder`
139/// implementations.
140pub struct HeaderSizes {
141    first: usize,
142    remaining: usize,
143}
144
145/// A type that may be transformed into a fragmentable ip packet builder.
146pub trait AsFragmentableIpPacketBuilder<I: FragmentationIpExt> {
147    /// The fragmentable packet builder that can be constructed from this type.
148    type Builder<'a>: FragmentableIpPacketBuilder<I>
149    where
150        Self: 'a;
151
152    /// Attempts to extract a `FragmentableIpPacketBuilder` implementation from
153    /// this type, returning an error if it can't be fragmented.
154    fn try_as_fragmentable(&self) -> Result<Self::Builder<'_>, FragmentationError>;
155}
156
157/// An IP packet builder that can create IP fragments.
158pub trait FragmentableIpPacketBuilder<I: FragmentationIpExt> {
159    /// Returns the portion of the MTU occupied by IP headers.
160    fn header_sizes(&self) -> HeaderSizes;
161
162    /// Returns a builder for fragment at offset `offset`.
163    ///
164    /// `position` carries information if this is the first or last segment, which
165    /// require special logic.
166    fn builder_at(
167        &self,
168        offset: FragmentOffset,
169        position: FragmentPosition,
170        identifier: I::FragmentationId,
171    ) -> impl PacketBuilder<NetworkSerializationContext> + '_;
172}
173
174/// Blanket impl for everything that has a shape to fit in `Ipv4FragmentBuilder`
175/// as a provider for fragmentation.
176impl<B> AsFragmentableIpPacketBuilder<Ipv4> for B
177where
178    B: InnerIpv4FragmentBuilder,
179{
180    type Builder<'a>
181        = Ipv4FragmentBuilder<'a, Self>
182    where
183        Self: 'a;
184
185    fn try_as_fragmentable(&self) -> Result<Self::Builder<'_>, FragmentationError> {
186        can_fragment_ipv4(self.prefix())?;
187        Ok(Ipv4FragmentBuilder { builder: self })
188    }
189}
190
191/// A trait marking all the IPv4 builder types that can be fragmented with
192/// [`Ipv4FragmentBuilder`].
193trait InnerIpv4FragmentBuilder: PacketBuilder<NetworkSerializationContext> {
194    fn prefix(&self) -> &Ipv4PacketBuilder;
195    fn prefix_mut(&mut self) -> &mut Ipv4PacketBuilder;
196    fn clone_for_fragment(&self, position: FragmentPosition) -> impl InnerIpv4FragmentBuilder;
197    fn header_sizes(&self) -> HeaderSizes;
198}
199
200impl InnerIpv4FragmentBuilder for Ipv4PacketBuilder {
201    fn prefix(&self) -> &Ipv4PacketBuilder {
202        self
203    }
204
205    fn prefix_mut(&mut self) -> &mut Ipv4PacketBuilder {
206        self
207    }
208
209    fn clone_for_fragment(&self, _position: FragmentPosition) -> impl InnerIpv4FragmentBuilder {
210        self.clone()
211    }
212
213    fn header_sizes(&self) -> HeaderSizes {
214        let size = self.constraints().header_len();
215        HeaderSizes { first: size, remaining: size }
216    }
217}
218
219impl<'a, I> InnerIpv4FragmentBuilder for Ipv4PacketBuilderWithOptions<'a, I>
220where
221    I: Iterator<Item: Borrow<Ipv4Option<'a>>> + Clone,
222{
223    fn prefix(&self) -> &Ipv4PacketBuilder {
224        self.prefix_builder()
225    }
226
227    fn prefix_mut(&mut self) -> &mut Ipv4PacketBuilder {
228        self.prefix_builder_mut()
229    }
230
231    fn clone_for_fragment(&self, position: FragmentPosition) -> impl InnerIpv4FragmentBuilder {
232        self.clone().with_fragment_options(position == FragmentPosition::First)
233    }
234
235    fn header_sizes(&self) -> HeaderSizes {
236        let first = self.constraints().header_len();
237        let remaining = self.clone().with_fragment_options(false).constraints().header_len();
238        HeaderSizes { first, remaining }
239    }
240}
241
242pub struct Ipv4FragmentBuilder<'a, B> {
243    builder: &'a B,
244}
245
246impl<'a, B> FragmentableIpPacketBuilder<Ipv4> for Ipv4FragmentBuilder<'a, B>
247where
248    B: InnerIpv4FragmentBuilder,
249{
250    fn header_sizes(&self) -> HeaderSizes {
251        self.builder.header_sizes()
252    }
253
254    fn builder_at(
255        &self,
256        offset: FragmentOffset,
257        position: FragmentPosition,
258        (): (),
259    ) -> impl PacketBuilder<NetworkSerializationContext> + '_ {
260        let mut builder = self.builder.clone_for_fragment(position);
261        set_ipv4_fragment(builder.prefix_mut(), offset, position);
262        builder
263    }
264}
265
266impl<B> AsFragmentableIpPacketBuilder<Ipv6> for B
267where
268    for<'a> &'a B: Ipv6PacketBuilderBeforeFragment,
269{
270    type Builder<'a>
271        = Ipv6FragmentBuilder<'a, Self>
272    where
273        Self: 'a;
274
275    fn try_as_fragmentable(&self) -> Result<Self::Builder<'_>, FragmentationError> {
276        Ok(Ipv6FragmentBuilder { builder: self })
277    }
278}
279
280pub struct Ipv6FragmentBuilder<'a, B> {
281    builder: &'a B,
282}
283
284impl<'a, B> FragmentableIpPacketBuilder<Ipv6> for Ipv6FragmentBuilder<'a, B>
285where
286    &'a B: Ipv6PacketBuilderBeforeFragment,
287{
288    fn header_sizes(&self) -> HeaderSizes {
289        // NB: We currently only support headers that need to be in all
290        // fragments, so we only need to calculate once. We might need to change
291        // the trait shape if that changes.
292        let header_len =
293            Ipv6PacketBuilderWithFragmentHeader::new(self.builder, FragmentOffset::ZERO, false, 0)
294                .constraints()
295                .header_len();
296        HeaderSizes { first: header_len, remaining: header_len }
297    }
298
299    fn builder_at(
300        &self,
301        offset: FragmentOffset,
302        position: FragmentPosition,
303        identifier: u32,
304    ) -> impl PacketBuilder<NetworkSerializationContext> + '_ {
305        Ipv6PacketBuilderWithFragmentHeader::new(
306            self.builder,
307            offset,
308            position != FragmentPosition::Last,
309            identifier,
310        )
311    }
312}
313
314impl<I, B> FragmentableIpSerializer<I> for ForwardedPacket<I, B>
315where
316    I: FragmentationIpExt,
317    B: BufferMut + NetworkSerializer,
318{
319    type Builder<'a>
320        = I::ForwardedFragmentBuilder
321    where
322        Self: 'a;
323    type Body<'a>
324        = Buf<&'a [u8]>
325    where
326        Self: 'a;
327
328    fn builder_and_body(&self) -> Result<(Self::Builder<'_>, Self::Body<'_>), FragmentationError> {
329        #[derive(GenericOverIp)]
330        #[generic_over_ip(I, Ip)]
331        struct Out<I: FragmentationIpExt>(I::ForwardedFragmentBuilder);
332        I::map_ip::<_, Result<(Out<I>, IpInvariant<Buf<&[u8]>>), FragmentationError>>(
333            self,
334            |forwarded| {
335                // Parse an IPv4 packet from the forwarded packet. We can assert
336                // strongly on all of the parsing here because ForwardedPacket
337                // is guaranteed to have been parsed by the IP stack already.
338                let mut buffer = forwarded.buffer().as_ref();
339                let packet = Ipv4PacketRaw::parse(&mut buffer, ())
340                    .expect("ForwardedPacket must be parseable");
341                let builder = packet.builder();
342                can_fragment_ipv4(&builder)?;
343                let raw_options_bytes = packet
344                    .options()
345                    .as_ref()
346                    .complete()
347                    .expect("unexpected incomplete IP header")
348                    .bytes();
349
350                let mut raw_options = Buf::new(
351                    [0u8; packet_formats::ipv4::MAX_OPTIONS_LEN],
352                    ..raw_options_bytes.len(),
353                );
354                raw_options.as_mut().copy_from_slice(raw_options_bytes);
355                let body = Buf::new(
356                    packet.into_body().complete().expect("unexpected incomplete IP body"),
357                    ..,
358                );
359                Ok((Out(ForwardedIpv4PacketBuilder { builder, raw_options }), IpInvariant(body)))
360            },
361            |forwarded| {
362                // Per RFC 8200 Section 4.5:
363                //   fragmentation in IPv6 is performed only by source nodes,
364                //   not by routers along a packet's delivery path.
365                // Therefore, in general, we should not fragment forwarded
366                // packets. However, during ingress we reassemble IP fragments
367                // prior to making a routing decision. In such cases, we have
368                // an obligation to re-fragment the packet back to the original
369                // MTU.
370                if !forwarded.reassembled() {
371                    return Err(FragmentationError::NotAllowed);
372                }
373                let mut buffer = forwarded.buffer().as_ref();
374                // NB: `Ipv6Packet::parse` must succeed, because
375                // `ForwardedPacket` has already been parsed by the IP stack.
376                let packet =
377                    Ipv6Packet::parse(&mut buffer, ()).expect("ForwardedPacket must be parseable");
378                let builder = packet.per_fragment_builder();
379                let body = Buf::new(&forwarded.buffer().as_ref()[builder.header_len()..], ..);
380                Ok((Out(ForwardedIpv6PacketBuilder(builder)), IpInvariant(body)))
381            },
382        )
383        .map(|(Out(builder), IpInvariant(body))| (builder, body))
384    }
385}
386
387pub struct ForwardedIpv4PacketBuilder {
388    builder: Ipv4PacketBuilder,
389    raw_options: Buf<[u8; packet_formats::ipv4::MAX_OPTIONS_LEN]>,
390}
391
392impl FragmentableIpPacketBuilder<Ipv4> for ForwardedIpv4PacketBuilder {
393    fn header_sizes(&self) -> HeaderSizes {
394        let Self { builder, raw_options } = self;
395        if raw_options.is_empty() {
396            builder.header_sizes()
397        } else {
398            let options = packet_formats::ipv4::Options::parse(raw_options.as_ref())
399                .expect("must hold valid options");
400            Ipv4PacketBuilderWithOptions::new_with_records_iter(builder.clone(), options.iter())
401                .header_sizes()
402        }
403    }
404
405    fn builder_at(
406        &self,
407        offset: FragmentOffset,
408        position: FragmentPosition,
409        (): (),
410    ) -> impl PacketBuilder<NetworkSerializationContext> + '_ {
411        let Self { builder, raw_options } = self;
412        let mut builder = builder.clone();
413        set_ipv4_fragment(&mut builder, offset, position);
414        let options = packet_formats::ipv4::Options::parse(raw_options.as_ref())
415            .expect("must hold valid options");
416        Ipv4PacketBuilderWithOptions::new_with_records_iter(builder.clone(), options.into_iter())
417            .with_fragment_options(position == FragmentPosition::First)
418    }
419}
420
421pub struct ForwardedIpv6PacketBuilder(Ipv6PerFragmentHeaderBuilder<Vec<u8>>);
422
423impl FragmentableIpPacketBuilder<Ipv6> for ForwardedIpv6PacketBuilder {
424    fn header_sizes(&self) -> HeaderSizes {
425        let size = self.0.header_len() + IPV6_FRAGMENT_EXT_HDR_LEN;
426        HeaderSizes { first: size, remaining: size }
427    }
428
429    fn builder_at(
430        &self,
431        offset: FragmentOffset,
432        position: FragmentPosition,
433        identifier: u32,
434    ) -> impl PacketBuilder<NetworkSerializationContext> + '_ {
435        Ipv6PacketBuilderWithFragmentHeader::new(
436            self.0.as_ref(),
437            offset,
438            position != FragmentPosition::Last,
439            identifier,
440        )
441    }
442}
443
444/// Abstracts fragment ID generation for [`IpFragmenter`].
445///
446/// A blanket impl is provided for [`RngContext`] implementers, so the bindings
447/// context can be used to generate random IDs for IPv6.
448pub(crate) trait FragmentationIdGenContext {
449    fn generate_id<I: FragmentationIpExt>(&mut self) -> I::FragmentationId;
450}
451
452#[derive(GenericOverIp)]
453#[generic_over_ip(I, Ip)]
454struct WrapFragmentationId<I: FragmentationIpExt>(I::FragmentationId);
455
456impl<BC> FragmentationIdGenContext for BC
457where
458    BC: RngContext,
459{
460    fn generate_id<I: FragmentationIpExt>(&mut self) -> I::FragmentationId {
461        let WrapFragmentationId(identifier) = I::map_ip_out(
462            self,
463            |_| WrapFragmentationId(()),
464            |rng| {
465                // TODO(https://fxbug.dev/373428005): Perhaps we can do better
466                // than a simple RNG. This is currently copying what netstack2
467                // does. RFC 7739 calls out different strategies for fragment
468                // IDs in IPv6. We currently pick an option that is not doing a
469                // best effort to avoid collisions, but it guarantees that
470                // fragment IDs can't be tracked as an attack vector.
471                // We avoid a zero fragment ID like netstack2 does.
472                WrapFragmentationId(rng.rng().random_range(1..=u32::MAX))
473            },
474        );
475        identifier
476    }
477}
478
479pub(crate) struct IpFragmenter<'a, I: FragmentationIpExt, S: FragmentableIpSerializer<I> + 'a> {
480    builder: S::Builder<'a>,
481    body: S::Body<'a>,
482    consumed: usize,
483    max_fragment_body_first: usize,
484    max_fragment_body_remaining: usize,
485    identifier: I::FragmentationId,
486}
487
488/// Returns the biggest fragment body that can fit in `mtu` with a given IP
489/// `header` size.
490///
491/// The returned body size is rounded down to the nearest multiple of 8 to fit
492/// the IP header representation of fragment offsets.
493fn maximum_fragment_body_with_header_and_mtu(
494    mtu: Mtu,
495    header: usize,
496) -> Result<usize, FragmentationError> {
497    let v = usize::from(mtu).checked_sub(header).ok_or(FragmentationError::MtuTooSmall)?;
498    // Mask the final 3 bits since fragment offset is expressed in units of 8
499    // octets for both IP versions.
500    let v = v & !0x07usize;
501
502    if v == 0 {
503        // Can't fragment if we don't have at least a single 8 octet
504        // of space.
505        return Err(FragmentationError::MtuTooSmall);
506    }
507    Ok(v)
508}
509
510impl<'a, I: FragmentationIpExt, S: FragmentableIpSerializer<I>> IpFragmenter<'a, I, S> {
511    /// Creates a new `IpFragmenter` with some `serializer` respecting a maximum
512    /// IP layer `mtu`.
513    pub(crate) fn new<C: FragmentationIdGenContext>(
514        id_ctx: &mut C,
515        serializer: &'a S,
516        mtu: Mtu,
517    ) -> Result<Self, FragmentationError> {
518        let (builder, body) = serializer.builder_and_body()?;
519        let HeaderSizes { first, remaining } = builder.header_sizes();
520        let max_fragment_body_first = maximum_fragment_body_with_header_and_mtu(mtu, first)?;
521        let max_fragment_body_remaining =
522            maximum_fragment_body_with_header_and_mtu(mtu, remaining)?;
523
524        if body.as_ref().len() > MAX_FRAGMENT_OFFSET + max_fragment_body_remaining {
525            return Err(FragmentationError::BodyTooLong);
526        }
527
528        let identifier = id_ctx.generate_id::<I>();
529
530        Ok(Self {
531            builder,
532            body,
533            consumed: 0,
534            max_fragment_body_first,
535            max_fragment_body_remaining,
536            identifier,
537        })
538    }
539
540    /// Returns the serializer for the next segment and a boolean indicating
541    /// whether more fragments are pending, or `None` if all segments have been
542    /// produced.
543    ///
544    /// # Panics
545    ///
546    /// Panics if fragmentation is not necessary for the `serializer` that
547    /// created this `IpFragmenter`.
548    pub(crate) fn next(&mut self) -> Option<(impl NetworkSerializer<Buffer = EmptyBuf>, bool)> {
549        let Self {
550            builder,
551            body,
552            consumed,
553            max_fragment_body_first,
554            max_fragment_body_remaining,
555            identifier,
556        } = self;
557        let body = &AsRef::as_ref(body)[*consumed..];
558        if body.is_empty() {
559            return None;
560        }
561        let first = *consumed == 0;
562        let max_fragment_body =
563            if first { max_fragment_body_first } else { max_fragment_body_remaining };
564        let take = body.len().min(*max_fragment_body);
565        let last = take == body.len();
566        let position = match (first, last) {
567            (true, true) => {
568                panic!("unnecessary fragmentation");
569            }
570            (true, false) => FragmentPosition::First,
571            (false, false) => FragmentPosition::Middle,
572            (false, true) => FragmentPosition::Last,
573        };
574        // Upon construction IpFragmenter verifies that we won't go over the
575        // maximum offset since the body length is known.
576        let fragment_offset = u16::try_from(*consumed).expect("fragment offset too large");
577        // Care is taken above to always take 8-byte multiples to be added to
578        // consumed, so we should always have a good representation for
579        // FragmentOffset.
580        let fragment_offset =
581            FragmentOffset::new_with_bytes(fragment_offset).expect("invalid offset");
582        let fragment_builder = builder.builder_at(fragment_offset, position, *identifier);
583        let end = *consumed + take;
584        let has_more = body.len() > take;
585        let fragment_body = &body[..take];
586        *consumed = end;
587        Some((fragment_builder.wrap_body(fragment_body.into_serializer()), has_more))
588    }
589}
590
591fn can_fragment_ipv4(builder: &Ipv4PacketBuilder) -> Result<(), FragmentationError> {
592    if builder.read_df_flag() {
593        return Err(FragmentationError::NotAllowed);
594    }
595    Ok(())
596}
597
598fn set_ipv4_fragment(
599    builder: &mut Ipv4PacketBuilder,
600    offset: FragmentOffset,
601    position: FragmentPosition,
602) {
603    builder.mf_flag(position != FragmentPosition::Last);
604    builder.fragment_offset(offset);
605}
606
607/// Counters kept by the IP stack pertaining to fragmentation.
608#[derive(Default, Debug)]
609#[cfg_attr(
610    any(test, feature = "testutils"),
611    derive(PartialEq, netstack3_macros::CounterCollection)
612)]
613pub struct FragmentationCounters<C = Counter> {
614    /// The number of IP frames requiring fragmentation on egress.
615    pub fragmentation_required: C,
616    /// The total number of fragments sent.
617    pub fragments: C,
618    /// The number of `NotAllowed` errors encountered.
619    pub error_not_allowed: C,
620    /// The number of `MtuTooSmall` errors encountered.
621    pub error_mtu_too_small: C,
622    /// The number of `BodyTooLong` errors encountered.
623    pub error_body_too_long: C,
624    /// The number of `SizeLimitExceeded` errors encountered.
625    pub error_inner_size_limit_exceeded: C,
626    /// Counts the number of times fragmentation was short-circuited due to a
627    /// fragment serialization error.
628    pub error_fragmented_serializer: C,
629}
630
631impl FragmentationCounters {
632    pub(crate) fn error_counter(&self, error: &FragmentationError) -> &Counter {
633        match error {
634            FragmentationError::NotAllowed => &self.error_not_allowed,
635            FragmentationError::MtuTooSmall => &self.error_mtu_too_small,
636            FragmentationError::BodyTooLong => &self.error_body_too_long,
637            FragmentationError::SizeLimitExceeded => &self.error_inner_size_limit_exceeded,
638        }
639    }
640}
641
642#[cfg(test)]
643mod tests {
644    use super::*;
645
646    use assert_matches::assert_matches;
647    use net_types::Witness as _;
648    use netstack3_base::testutil::{TEST_ADDRS_V4, TEST_ADDRS_V6};
649    use netstack3_filter::FilterIpExt;
650    use packet::{Buffer, BufferView, GrowBuffer, Serializer};
651    use packet_formats::ip::IpProto;
652    use packet_formats::ipv4::Ipv4Packet;
653    use packet_formats::ipv6::ext_hdrs::{
654        ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData, Ipv6ExtensionHeader,
655    };
656    use packet_formats::ipv6::{Ipv6Header, Ipv6Packet, Ipv6PacketBuilder};
657    use test_case::test_case;
658
659    const TEST_MTU: Mtu = Ipv6::MINIMUM_LINK_MTU;
660
661    fn gen_body(len: usize) -> Vec<u8> {
662        // Cycle bytes until 251 which is the largest prime that can fit in a
663        // u8. Unlikely this aligns poorly and hides fragmentation bugs.
664        (0u8..=251).cycle().take(len).collect::<Vec<u8>>()
665    }
666
667    impl<'a, I: FragmentationIpExt, S: FragmentableIpSerializer<I>> IpFragmenter<'a, I, S> {
668        fn next_serialized(&mut self) -> Buf<Vec<u8>> {
669            self.next()
670                .expect("no more fragments")
671                .0
672                .serialize_vec_outer(&mut NetworkSerializationContext::default())
673                .map_err(|(err, _serializer)| err)
674                .unwrap()
675                .unwrap_b()
676        }
677    }
678
679    trait FragmentationTestEnv<I: FragmentationIpExt> {
680        fn new_serializer<'a>(
681            &self,
682            body: &'a [u8],
683        ) -> impl FragmentableIpSerializer<I, Buffer: Buffer> + 'a;
684        fn check_fragment(
685            &self,
686            fragment: &mut Buf<Vec<u8>>,
687            position: FragmentPosition,
688            offset: usize,
689        );
690    }
691
692    #[derive(Default)]
693    struct Ipv4TestEnv {
694        dont_frag: bool,
695    }
696
697    impl Ipv4TestEnv {
698        const fn dont_frag() -> Self {
699            Self { dont_frag: true }
700        }
701    }
702
703    const IPV4_ID: u16 = 0x1234;
704    fn new_ipv4_packet_builder(dont_frag: bool) -> Ipv4PacketBuilder {
705        let mut builder = Ipv4PacketBuilder::new(
706            TEST_ADDRS_V4.local_ip,
707            TEST_ADDRS_V4.remote_ip,
708            1,
709            IpProto::Udp.into(),
710        );
711        builder.id(IPV4_ID);
712        builder.df_flag(dont_frag);
713        builder
714    }
715
716    fn parse_and_check_ipv4_packet(
717        fragment: &mut Buf<Vec<u8>>,
718        position: FragmentPosition,
719        offset: usize,
720    ) -> Ipv4Packet<&[u8]> {
721        let packet = Ipv4Packet::parse(fragment.buffer_view(), ()).expect("parse fragment");
722        assert_eq!(packet.src_ip(), TEST_ADDRS_V4.local_ip.get());
723        assert_eq!(packet.dst_ip(), TEST_ADDRS_V4.remote_ip.get());
724        assert_eq!(packet.ttl(), 1);
725        assert_eq!(packet.id(), IPV4_ID);
726        assert_eq!(packet.proto(), IpProto::Udp.into());
727        assert_eq!(packet.mf_flag(), position != FragmentPosition::Last);
728        assert_eq!(usize::from(packet.fragment_offset().into_bytes()), offset);
729        packet
730    }
731
732    impl FragmentationTestEnv<Ipv4> for Ipv4TestEnv {
733        fn new_serializer<'a>(
734            &self,
735            body: &'a [u8],
736        ) -> impl FragmentableIpSerializer<Ipv4, Buffer: Buffer> + 'a {
737            let Self { dont_frag } = self;
738            new_ipv4_packet_builder(*dont_frag).wrap_body(body.into_serializer())
739        }
740
741        fn check_fragment(
742            &self,
743            fragment: &mut Buf<Vec<u8>>,
744            position: FragmentPosition,
745            offset: usize,
746        ) {
747            let _ = parse_and_check_ipv4_packet(fragment, position, offset);
748        }
749    }
750
751    #[derive(Default)]
752    struct Ipv4WithOptionsTestEnv(Ipv4TestEnv);
753
754    // The MSB of an option kind determines if it should be copied.
755    const FAKE_OPTION_COPIED_KIND: u8 = 255;
756    const FAKE_OPTION_COPIED: [u8; 1] = [255];
757    const FAKE_OPTION_NOT_COPIED_KIND: u8 = 127;
758    const FAKE_OPTION_NOT_COPIED: [u8; 1] = [127];
759
760    impl FragmentationTestEnv<Ipv4> for Ipv4WithOptionsTestEnv {
761        fn new_serializer<'a>(
762            &self,
763            body: &'a [u8],
764        ) -> impl FragmentableIpSerializer<Ipv4, Buffer: Buffer> + 'a {
765            let Self(Ipv4TestEnv { dont_frag }) = self;
766
767            Ipv4PacketBuilderWithOptions::new(
768                new_ipv4_packet_builder(*dont_frag),
769                [
770                    Ipv4Option::Unrecognized {
771                        kind: FAKE_OPTION_COPIED_KIND,
772                        data: &FAKE_OPTION_COPIED[..],
773                    },
774                    Ipv4Option::Unrecognized {
775                        kind: FAKE_OPTION_NOT_COPIED_KIND,
776                        data: &FAKE_OPTION_NOT_COPIED[..],
777                    },
778                ],
779            )
780            .unwrap()
781            .wrap_body(body.into_serializer())
782        }
783
784        fn check_fragment(
785            &self,
786            fragment: &mut Buf<Vec<u8>>,
787            position: FragmentPosition,
788            offset: usize,
789        ) {
790            let packet = parse_and_check_ipv4_packet(fragment, position, offset);
791            let (copied, not_copied) = packet.iter_options().fold(
792                (false, false),
793                |(mut copied, mut not_copied), option| {
794                    let (kind, data) = assert_matches!(option,
795                        Ipv4Option::Unrecognized{ kind, data } => (kind, data)
796                    );
797                    assert_eq!(data.len(), 1);
798                    assert_eq!(data[0], kind);
799                    let seen = match kind {
800                        FAKE_OPTION_COPIED_KIND => &mut copied,
801                        FAKE_OPTION_NOT_COPIED_KIND => &mut not_copied,
802                        k => panic!("unexpected option {k}"),
803                    };
804                    assert_eq!(core::mem::replace(seen, true), false);
805                    (copied, not_copied)
806                },
807            );
808            assert_eq!(copied, true, "must be copied on all fragments {position:?}");
809            assert_eq!(
810                not_copied,
811                position == FragmentPosition::First,
812                "must only be in first fragment {position:?}"
813            );
814        }
815    }
816
817    struct ForwardingTestEnv<E> {
818        inner: E,
819        reassembled: bool,
820    }
821
822    impl<E> ForwardingTestEnv<E> {
823        fn new(inner: E) -> Self {
824            Self { inner, reassembled: false }
825        }
826
827        fn new_reassembled(inner: E) -> Self {
828            Self { inner, reassembled: true }
829        }
830    }
831
832    impl<I: FragmentationIpExt + FilterIpExt, E: FragmentationTestEnv<I>> FragmentationTestEnv<I>
833        for ForwardingTestEnv<E>
834    {
835        fn new_serializer<'a>(
836            &self,
837            body: &'a [u8],
838        ) -> impl FragmentableIpSerializer<I, Buffer: Buffer> + 'a {
839            use packet_formats::ip::IpPacket as _;
840            let Self { inner, reassembled } = self;
841            let mut buffer = inner
842                .new_serializer(body)
843                .serialize_outer(
844                    &mut NetworkSerializationContext::default(),
845                    packet::NoReuseBufferProvider(packet::new_buf_vec),
846                )
847                .map_err(|(err, _)| err)
848                .unwrap();
849            let packet =
850                <I::Packet<_> as ParsablePacket<_, _>>::parse(buffer.buffer_view(), ()).unwrap();
851            let src_addr = packet.src_ip();
852            let dst_addr = packet.dst_ip();
853            let proto = packet.proto();
854            let meta = packet.parse_metadata();
855            drop(packet);
856            ForwardedPacket::new(src_addr, dst_addr, proto, meta, buffer, *reassembled)
857        }
858        fn check_fragment(
859            &self,
860            fragment: &mut Buf<Vec<u8>>,
861            position: FragmentPosition,
862            offset: usize,
863        ) {
864            let Self { inner, reassembled: _ } = self;
865            inner.check_fragment(fragment, position, offset)
866        }
867    }
868
869    struct Ipv6TestEnv;
870
871    const IPV6_ID: u32 = 0x1234ABCD;
872
873    impl FragmentationTestEnv<Ipv6> for Ipv6TestEnv {
874        fn new_serializer<'a>(
875            &self,
876            body: &'a [u8],
877        ) -> impl FragmentableIpSerializer<Ipv6, Buffer: Buffer> + 'a {
878            Ipv6PacketBuilder::new(
879                TEST_ADDRS_V6.local_ip,
880                TEST_ADDRS_V6.remote_ip,
881                1,
882                IpProto::Udp.into(),
883            )
884            .wrap_body(body.into_serializer())
885        }
886
887        fn check_fragment(
888            &self,
889            fragment: &mut Buf<Vec<u8>>,
890            position: FragmentPosition,
891            offset: usize,
892        ) {
893            let packet = Ipv6Packet::parse(fragment.buffer_view(), ()).unwrap();
894            assert_eq!(packet.src_ip(), TEST_ADDRS_V6.local_ip.get());
895            assert_eq!(packet.dst_ip(), TEST_ADDRS_V6.remote_ip.get());
896            assert_eq!(packet.hop_limit(), 1);
897            assert_eq!(packet.proto(), IpProto::Udp.into());
898            let fragment = packet
899                .iter_extension_hdrs()
900                .find_map(|h| match h {
901                    Ipv6ExtensionHeader::Fragment { fragment_data } => Some(fragment_data),
902                    _ => None,
903                })
904                .expect("no fragment header");
905            assert_eq!(fragment.identification(), IPV6_ID);
906            assert_eq!(usize::from(fragment.fragment_offset().into_bytes()), offset);
907            assert_eq!(fragment.m_flag(), position != FragmentPosition::Last);
908        }
909    }
910
911    #[derive(Default)]
912    struct Ipv6WithOptionsTestEnv;
913
914    impl FragmentationTestEnv<Ipv6> for Ipv6WithOptionsTestEnv {
915        fn new_serializer<'a>(
916            &self,
917            body: &'a [u8],
918        ) -> impl FragmentableIpSerializer<Ipv6, Buffer: Buffer> + 'a {
919            packet_formats::ipv6::Ipv6PacketBuilderWithHbhOptions::new(
920                Ipv6PacketBuilder::new(
921                    TEST_ADDRS_V6.local_ip,
922                    TEST_ADDRS_V6.remote_ip,
923                    1,
924                    IpProto::Udp.into(),
925                ),
926                // Add an arbitrary extension header to setup so that this
927                // environment tests fragmentation with extension headers.
928                &[HopByHopOption {
929                    action: ExtensionHeaderOptionAction::SkipAndContinue,
930                    mutable: false,
931                    data: HopByHopOptionData::RouterAlert { data: 0 },
932                }],
933            )
934            .unwrap()
935            .wrap_body(body.into_serializer())
936        }
937
938        fn check_fragment(
939            &self,
940            fragment: &mut Buf<Vec<u8>>,
941            position: FragmentPosition,
942            offset: usize,
943        ) {
944            let packet = Ipv6Packet::parse(fragment.buffer_view(), ()).unwrap();
945            assert_eq!(packet.src_ip(), TEST_ADDRS_V6.local_ip.get());
946            assert_eq!(packet.dst_ip(), TEST_ADDRS_V6.remote_ip.get());
947            assert_eq!(packet.hop_limit(), 1);
948            assert_eq!(packet.proto(), IpProto::Udp.into());
949            let ext_hdrs = packet.iter_extension_hdrs().collect::<Vec<_>>();
950            let (hbh, frag) = assert_matches!(&ext_hdrs[..], [hbh, frag] => (hbh, frag));
951            assert_matches!(hbh, Ipv6ExtensionHeader::HopByHopOptions { .. });
952            let fragment = assert_matches!(
953                frag, Ipv6ExtensionHeader::Fragment { fragment_data } => fragment_data
954            );
955            assert_eq!(fragment.identification(), IPV6_ID);
956            assert_eq!(usize::from(fragment.fragment_offset().into_bytes()), offset);
957            assert_eq!(fragment.m_flag(), position != FragmentPosition::Last);
958        }
959    }
960
961    struct FixedIdContext;
962    impl FragmentationIdGenContext for FixedIdContext {
963        fn generate_id<I: FragmentationIpExt>(&mut self) -> I::FragmentationId {
964            let WrapFragmentationId(id) =
965                I::map_ip_out((), |()| WrapFragmentationId(()), |()| WrapFragmentationId(IPV6_ID));
966            id
967        }
968    }
969
970    #[test_case::test_matrix(
971        [
972            Ipv4TestEnv::default(),
973            Ipv4WithOptionsTestEnv::default(),
974            ForwardingTestEnv::new(Ipv4TestEnv::default()),
975            ForwardingTestEnv::new(Ipv4WithOptionsTestEnv::default()),
976            ForwardingTestEnv::new_reassembled(Ipv6TestEnv),
977            ForwardingTestEnv::new_reassembled(Ipv6WithOptionsTestEnv),
978            Ipv6TestEnv,
979            Ipv6WithOptionsTestEnv,
980        ],
981        0..=2
982    )]
983    fn fragment<I: FragmentationIpExt, E: FragmentationTestEnv<I>>(
984        env: E,
985        middle_fragments: usize,
986    ) {
987        // NB: We're using the fact that MTU is larger than the header sizes
988        // here to end up obtaining the right number of middle fragments as
989        // expected. This makes this test sensitive to the relation between the
990        // picked MTU and the header sizes for the multiple serializers.
991        let full_body = gen_body(usize::from(TEST_MTU) * (1 + middle_fragments));
992        let mut body_view = Buf::new(&full_body[..], ..);
993        let serializer = env.new_serializer(&full_body[..]);
994        let mut fragmenter = IpFragmenter::new(&mut FixedIdContext, &serializer, TEST_MTU)
995            .expect("create fragmenter");
996
997        let mut frag = fragmenter.next_serialized();
998        env.check_fragment(&mut frag, FragmentPosition::First, body_view.prefix_len());
999        assert_eq!(
1000            frag.as_ref(),
1001            body_view.buffer_view().take_front(fragmenter.max_fragment_body_first).unwrap()
1002        );
1003
1004        for _ in 0..middle_fragments {
1005            let mut frag = fragmenter.next_serialized();
1006            env.check_fragment(&mut frag, FragmentPosition::Middle, body_view.prefix_len());
1007            assert_eq!(
1008                frag.as_ref(),
1009                body_view.buffer_view().take_front(fragmenter.max_fragment_body_remaining).unwrap()
1010            );
1011        }
1012
1013        let mut frag = fragmenter.next_serialized();
1014        env.check_fragment(&mut frag, FragmentPosition::Last, body_view.prefix_len());
1015        assert_eq!(frag.as_ref(), body_view.buffer_view().into_rest());
1016
1017        // No more fragments.
1018        assert!(fragmenter.next().is_none());
1019    }
1020
1021    #[test_case(Ipv4TestEnv::dont_frag())]
1022    #[test_case(Ipv4WithOptionsTestEnv(Ipv4TestEnv::dont_frag()))]
1023    #[test_case(ForwardingTestEnv::new(Ipv4TestEnv::dont_frag()))]
1024    #[test_case(ForwardingTestEnv::new(Ipv6TestEnv))]
1025    #[test_case(ForwardingTestEnv::new(Ipv6WithOptionsTestEnv))]
1026    fn not_allowed<I: FragmentationIpExt, E: FragmentationTestEnv<I>>(env: E) {
1027        let body = gen_body(usize::from(TEST_MTU));
1028        let serializer = env.new_serializer(&body[..]);
1029        let result = IpFragmenter::new(&mut FixedIdContext, &serializer, TEST_MTU).map(|_| ());
1030        assert_eq!(result, Err(FragmentationError::NotAllowed))
1031    }
1032
1033    #[test_case(Ipv4TestEnv::default())]
1034    #[test_case(Ipv4WithOptionsTestEnv::default())]
1035    #[test_case(ForwardingTestEnv::new(Ipv4TestEnv::default()))]
1036    #[test_case(ForwardingTestEnv::new_reassembled(Ipv6TestEnv))]
1037    #[test_case(ForwardingTestEnv::new_reassembled(Ipv6WithOptionsTestEnv))]
1038    #[test_case(Ipv6TestEnv)]
1039    #[test_case(Ipv6WithOptionsTestEnv)]
1040    fn mtu_too_small<I: FragmentationIpExt, E: FragmentationTestEnv<I>>(env: E) {
1041        let body = gen_body(usize::from(TEST_MTU));
1042        let serializer = env.new_serializer(&body[..]);
1043        let result = IpFragmenter::new(&mut FixedIdContext, &serializer, Mtu::new(10)).map(|_| ());
1044        assert_eq!(result, Err(FragmentationError::MtuTooSmall));
1045    }
1046
1047    #[test_case(Ipv4TestEnv::default())]
1048    #[test_case(Ipv4WithOptionsTestEnv::default())]
1049    #[test_case(Ipv6TestEnv)]
1050    #[test_case(Ipv6WithOptionsTestEnv)]
1051    fn body_too_long<I: FragmentationIpExt, E: FragmentationTestEnv<I>>(env: E) {
1052        let body = gen_body(MAX_FRAGMENT_OFFSET + usize::from(TEST_MTU));
1053        let serializer = env.new_serializer(&body[..]);
1054        let result = IpFragmenter::new(&mut FixedIdContext, &serializer, TEST_MTU).map(|_| ());
1055        assert_eq!(result, Err(FragmentationError::BodyTooLong));
1056    }
1057}