Skip to main content

netstack3_ip/
gso.rs

1// Copyright 2026 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//! Generic Segmentation Offload (GSO) support.
6
7use core::fmt::Debug;
8use core::marker::PhantomData;
9
10use net_types::ip::{IpAddress, Ipv4, Ipv6};
11use netstack3_base::{
12    ChecksumRxOffloading, Counter, GsoInfo, Ipv4IdMode, MAX_GSO_PAYLOAD_LEN, NetworkParsingContext,
13    NetworkSerializationContext, Payload,
14};
15use netstack3_filter::{DynTransportSerializer, FilterIpExt, ForwardedPacket, RawIpBody};
16use packet::{
17    BufferMut, EmptyBuf, FragmentedBytesMut, InnerPacketBuilder, InnerSerializer,
18    NestablePacketBuilder, NestableSerializer as _, Nested, PacketBuilder, PacketConstraints,
19    ParsablePacket as _, ParseBuffer, SerializationContext, SerializeTarget,
20};
21use packet_formats::icmp::{IcmpIpExt, IcmpMessage, IcmpPacketBuilder};
22use packet_formats::ip::{FragmentOffset, IpExt, IpPacket as _, IpProto};
23use packet_formats::ipv4::{
24    HDR_PREFIX_LEN, Ipv4Header, Ipv4Packet, Ipv4PacketBuilder, Ipv4PacketBuilderWithOptions,
25};
26use packet_formats::ipv6::{Ipv6Packet, Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
27use packet_formats::tcp::options::TcpOptionsRef;
28use packet_formats::tcp::{TcpParseArgs, TcpSegment, TcpSegmentBuilderWithOptions};
29use packet_formats::udp::UdpPacketBuilder;
30
31use crate::internal::fragmentation::{
32    AsFragmentableIpPacketBuilder, FragmentationError, FragmentationIpExt,
33};
34
35/// Reasons a packet can't be segmented in software.
36#[derive(Debug, Eq, PartialEq)]
37pub enum GsoError {
38    /// The packet's headers couldn't be parsed.
39    Parse,
40    /// The packet doesn't carry a protocol that can be segmented in software.
41    NotSegmentable,
42    /// The packet carries headers that can't be faithfully replicated into
43    /// each segment, e.g. IPv4 options or IPv6 extension headers.
44    UnsupportedHeaders,
45    /// The packet's payload is longer than [`MAX_GSO_PAYLOAD_LEN`].
46    PayloadTooLong,
47}
48
49/// An IP version extension trait providing the version-specific parts of
50/// software segmentation.
51pub trait GsoIpExt:
52    IpExt<PacketBuilder<NetworkSerializationContext>: AsSegmentableIpPacketBuilder<Self>>
53    + FragmentationIpExt
54{
55    /// The IP header template for the segments of a forwarded packet.
56    type ForwardedSegmentBuilder: SegmentableIpPacketBuilder<Self>;
57
58    /// Returns a template IP header builder for the segments of `packet`.
59    ///
60    /// Returns an error if `packet` carries headers that can't be replicated
61    /// into each segment.
62    fn try_segmented_packet_builder<'a>(
63        packet: &Self::Packet<&'a [u8]>,
64    ) -> Result<Self::ForwardedSegmentBuilder, GsoError>;
65}
66
67impl GsoIpExt for Ipv4 {
68    type ForwardedSegmentBuilder = Ipv4PacketBuilder;
69
70    fn try_segmented_packet_builder<'a>(
71        packet: &Ipv4Packet<&'a [u8]>,
72    ) -> Result<Ipv4PacketBuilder, GsoError> {
73        // Options would have to be copied into every segment. Packets only
74        // reach this path with `GsoInfo` attached by GRO, which refuses to
75        // coalesce packets carrying options, so this is a defensive check.
76        if packet.header_len() != HDR_PREFIX_LEN {
77            return Err(GsoError::UnsupportedHeaders);
78        }
79        // A fragment doesn't carry a complete transport payload, so it can't
80        // be segmented.
81        if packet.fragment_offset() != FragmentOffset::ZERO || packet.mf_flag() {
82            return Err(GsoError::UnsupportedHeaders);
83        }
84        Ok(Ipv4Header::builder(packet))
85    }
86}
87
88impl GsoIpExt for Ipv6 {
89    type ForwardedSegmentBuilder = ForwardedIpv6SegmentBuilder;
90
91    fn try_segmented_packet_builder<'a>(
92        packet: &Ipv6Packet<&'a [u8]>,
93    ) -> Result<ForwardedIpv6SegmentBuilder, GsoError> {
94        // Extension headers would have to be copied into every segment. Packets
95        // only reach this path with `GsoInfo` attached by GRO, which refuses to
96        // coalesce packets carrying extension headers, so this is a defensive
97        // check.
98        if packet.iter_extension_hdrs().next().is_some() {
99            return Err(GsoError::UnsupportedHeaders);
100        }
101        Ok(ForwardedIpv6SegmentBuilder(packet.builder()))
102    }
103}
104
105/// The IP header of a segment of a forwarded IPv6 packet.
106///
107/// Per RFC 8200 Section 4.5, routers don't fragment IPv6 packets, so unlike a
108/// bare [`Ipv6PacketBuilder`], segments built with this type refuse to be
109/// fragmented. Segments that don't fit the egress MTU instead fail to send,
110/// causing the forwarding path to generate an ICMPv6 Packet Too Big error.
111#[derive(Clone, Debug)]
112pub struct ForwardedIpv6SegmentBuilder(Ipv6PacketBuilder);
113
114impl SegmentableIpPacketBuilder<Ipv6> for ForwardedIpv6SegmentBuilder {
115    type SegmentBuilder = Self;
116
117    fn segment_builder(&self, _index: u16, _gso_info: GsoInfo) -> Self {
118        // No header fields need to be updated.
119        self.clone()
120    }
121}
122
123impl AsFragmentableIpPacketBuilder<Ipv6> for ForwardedIpv6SegmentBuilder {
124    type Builder<'a>
125        = <Ipv6PacketBuilder as AsFragmentableIpPacketBuilder<Ipv6>>::Builder<'a>
126    where
127        Self: 'a;
128
129    fn try_as_fragmentable(&self) -> Result<Self::Builder<'_>, FragmentationError> {
130        // Forwarded packets that were reassembled on ingress must be
131        // re-fragmented to their original size on egress (see the
132        // `FragmentableIpSerializer` impl for `ForwardedPacket`). That never
133        // applies here: segments only come from packets coalesced by GRO, and
134        // GRO doesn't coalesce fragmented packets (it rejects IPv6 packets with
135        // any extension headers, including the Fragment header), so a segmented
136        // packet can't have been reassembled.
137        Err(FragmentationError::NotAllowed)
138    }
139}
140
141impl NestablePacketBuilder for ForwardedIpv6SegmentBuilder {
142    fn constraints(&self) -> PacketConstraints {
143        let Self(builder) = self;
144        builder.constraints()
145    }
146}
147
148impl PacketBuilder<NetworkSerializationContext> for ForwardedIpv6SegmentBuilder {
149    fn context_state(&self) -> <NetworkSerializationContext as SerializationContext>::ContextState {
150        let Self(builder) = self;
151        PacketBuilder::<NetworkSerializationContext>::context_state(builder)
152    }
153
154    fn serialize(
155        &self,
156        context: &mut NetworkSerializationContext,
157        target: &mut SerializeTarget<'_>,
158        body: FragmentedBytesMut<'_, '_>,
159    ) {
160        let Self(builder) = self;
161        builder.serialize(context, target, body)
162    }
163}
164
165/// A [`packet::Serializer`] the IP egress path may be able to split into
166/// segments.
167///
168/// This is the transport-layer segmentation counterpart of
169/// [`FragmentableIpSerializer`]: rather than serializing the packet and parsing
170/// it back, implementations hand out the header builders and payload they
171/// already hold, which a [`GsoSegmenter`] stamps onto each segment.
172pub trait MaybeSegmentableIpSerializer<I: GsoIpExt> {
173    /// Where packets of this type requiring segmentation come from.
174    const SOURCE: GsoSource;
175
176    /// The IP header template for every segment.
177    type IpBuilder<'a>: SegmentableIpPacketBuilder<I>
178    where
179        Self: 'a;
180
181    /// The transport header template for every segment.
182    type TransportBuilder<'a>: SegmentableTransportBuilder
183    where
184        Self: 'a;
185
186    /// The transport payload split across the segments.
187    type Payload<'a>: Payload + InnerPacketBuilder + Copy
188    where
189        Self: 'a;
190
191    /// Returns a segmenter splitting this packet into `gso_info`-sized
192    /// segments.
193    ///
194    /// Returns an error if the packet can't be segmented in software.
195    fn try_segmenter(
196        &self,
197        gso_info: GsoInfo,
198    ) -> Result<
199        GsoSegmenter<I, Self::IpBuilder<'_>, Self::TransportBuilder<'_>, Self::Payload<'_>>,
200        GsoError,
201    >;
202}
203
204impl<I, B> MaybeSegmentableIpSerializer<I> for ForwardedPacket<I, B>
205where
206    I: GsoIpExt,
207    B: BufferMut,
208{
209    const SOURCE: GsoSource = GsoSource::Forwarded;
210
211    type IpBuilder<'a>
212        = I::ForwardedSegmentBuilder
213    where
214        Self: 'a;
215    type TransportBuilder<'a>
216        = ParsedForwardedTransport<'a, I::Addr>
217    where
218        Self: 'a;
219    type Payload<'a>
220        = &'a [u8]
221    where
222        Self: 'a;
223
224    fn try_segmenter(
225        &self,
226        gso_info: GsoInfo,
227    ) -> Result<GsoSegmenter<I, Self::IpBuilder<'_>, Self::TransportBuilder<'_>, &[u8]>, GsoError>
228    {
229        segmenter_from_packet_bytes(self.buffer().as_ref(), gso_info)
230    }
231}
232
233impl<I, S, B> MaybeSegmentableIpSerializer<I> for Nested<S, B>
234where
235    I: GsoIpExt,
236    S: MaybeSegmentableTransportSerializer,
237    B: AsSegmentableIpPacketBuilder<I>,
238{
239    // Packets the stack hasn't serialized yet were generated locally.
240    const SOURCE: GsoSource = GsoSource::Local;
241
242    type IpBuilder<'a>
243        = B::Builder<'a>
244    where
245        Self: 'a;
246    type TransportBuilder<'a>
247        = S::Builder<'a>
248    where
249        Self: 'a;
250    type Payload<'a>
251        = S::Payload<'a>
252    where
253        Self: 'a;
254
255    fn try_segmenter(
256        &self,
257        gso_info: GsoInfo,
258    ) -> Result<GsoSegmenter<I, B::Builder<'_>, S::Builder<'_>, S::Payload<'_>>, GsoError> {
259        let ip_builder = self.outer().try_as_segmentable()?;
260        let (transport_builder, payload) = self.inner().try_as_segmentable()?;
261        GsoSegmenter::new(ip_builder, transport_builder, payload, gso_info)
262    }
263}
264
265/// A type that may be viewed as the IP header template for a packet's
266/// segments.
267pub trait AsSegmentableIpPacketBuilder<I: GsoIpExt> {
268    /// The segment header template borrowed from this type.
269    type Builder<'a>: SegmentableIpPacketBuilder<I>
270    where
271        Self: 'a;
272
273    /// Attempts to borrow a segment header template, returning an error if
274    /// this builder's headers can't be replicated into every segment.
275    fn try_as_segmentable(&self) -> Result<Self::Builder<'_>, GsoError>;
276}
277
278impl AsSegmentableIpPacketBuilder<Ipv4> for Ipv4PacketBuilder {
279    type Builder<'a>
280        = &'a Ipv4PacketBuilder
281    where
282        Self: 'a;
283
284    fn try_as_segmentable(&self) -> Result<&Ipv4PacketBuilder, GsoError> {
285        Ok(self)
286    }
287}
288
289impl AsSegmentableIpPacketBuilder<Ipv6> for Ipv6PacketBuilder {
290    type Builder<'a>
291        = &'a Ipv6PacketBuilder
292    where
293        Self: 'a;
294
295    fn try_as_segmentable(&self) -> Result<&Ipv6PacketBuilder, GsoError> {
296        Ok(self)
297    }
298}
299
300impl<O> AsSegmentableIpPacketBuilder<Ipv4> for Ipv4PacketBuilderWithOptions<'_, O> {
301    type Builder<'a>
302        = !
303    where
304        Self: 'a;
305
306    fn try_as_segmentable(&self) -> Result<!, GsoError> {
307        // GSO doesn't currently support IPv4 options.
308        Err(GsoError::UnsupportedHeaders)
309    }
310}
311
312impl<O> AsSegmentableIpPacketBuilder<Ipv6> for Ipv6PacketBuilderWithHbhOptions<'_, O> {
313    type Builder<'a>
314        = !
315    where
316        Self: 'a;
317
318    fn try_as_segmentable(&self) -> Result<!, GsoError> {
319        // GSO doesn't currently support IPv6 extension headers.
320        Err(GsoError::UnsupportedHeaders)
321    }
322}
323
324/// A template for the IP header of each of the segments a packet is split into.
325///
326/// This is IP-layer counterpart of [`SegmentableTransportBuilder`].
327pub trait SegmentableIpPacketBuilder<I: GsoIpExt> {
328    /// The builder for a single segment's IP header.
329    type SegmentBuilder: PacketBuilder<NetworkSerializationContext>
330        + NestablePacketBuilder
331        + AsFragmentableIpPacketBuilder<I>;
332
333    /// Returns the header builder for the Nth segment, where N is a zero-based
334    /// ordinal given by `index`.
335    fn segment_builder(&self, index: u16, gso_info: GsoInfo) -> Self::SegmentBuilder;
336}
337
338impl SegmentableIpPacketBuilder<Ipv4> for Ipv4PacketBuilder {
339    type SegmentBuilder = Self;
340
341    fn segment_builder(&self, index: u16, gso_info: GsoInfo) -> Self {
342        let GsoInfo { ipv4_id_mode, gso_size: _, gso_segs: _ } = gso_info;
343        let mut builder = self.clone();
344        match ipv4_id_mode {
345            Some(Ipv4IdMode::Fixed) => {}
346            Some(Ipv4IdMode::Incrementing) | None => {
347                builder.id(self.read_id().wrapping_add(index));
348            }
349        }
350        builder
351    }
352}
353
354impl SegmentableIpPacketBuilder<Ipv6> for Ipv6PacketBuilder {
355    type SegmentBuilder = Self;
356
357    fn segment_builder(&self, _index: u16, _gso_info: GsoInfo) -> Self {
358        // No header fields need to be updated.
359        self.clone()
360    }
361}
362
363impl SegmentableIpPacketBuilder<Ipv4> for ! {
364    type SegmentBuilder = Ipv4PacketBuilder;
365
366    fn segment_builder(&self, _index: u16, _gso_info: GsoInfo) -> Ipv4PacketBuilder {
367        match *self {}
368    }
369}
370
371impl SegmentableIpPacketBuilder<Ipv6> for ! {
372    type SegmentBuilder = Ipv6PacketBuilder;
373
374    fn segment_builder(&self, _index: u16, _gso_info: GsoInfo) -> Ipv6PacketBuilder {
375        match *self {}
376    }
377}
378
379// Allow both owned builders and builder references to be used as segment header
380// templates.
381impl<I: GsoIpExt, T: SegmentableIpPacketBuilder<I>> SegmentableIpPacketBuilder<I> for &T {
382    type SegmentBuilder = T::SegmentBuilder;
383
384    fn segment_builder(&self, index: u16, gso_info: GsoInfo) -> T::SegmentBuilder {
385        T::segment_builder(*self, index, gso_info)
386    }
387}
388
389/// A transport-layer [`packet::Serializer`] that may carry a payload splittable
390/// in software.
391pub trait MaybeSegmentableTransportSerializer {
392    /// The transport header stamped onto every segment.
393    type Builder<'a>: SegmentableTransportBuilder
394    where
395        Self: 'a;
396
397    /// The transport payload split across the segments.
398    type Payload<'a>: Payload + InnerPacketBuilder + Copy
399    where
400        Self: 'a;
401
402    /// Attempts to extract the transport header template and the payload to
403    /// split, returning an error if this serializer can't be segmented.
404    fn try_as_segmentable(&self) -> Result<(Self::Builder<'_>, Self::Payload<'_>), GsoError>;
405}
406
407impl<A, P, B, O> MaybeSegmentableTransportSerializer
408    for Nested<InnerSerializer<P, B>, TcpSegmentBuilderWithOptions<A, O>>
409where
410    A: IpAddress,
411    P: Payload + InnerPacketBuilder + Copy,
412    O: InnerPacketBuilder + Clone,
413{
414    type Builder<'a>
415        = &'a TcpSegmentBuilderWithOptions<A, O>
416    where
417        Self: 'a;
418    type Payload<'a>
419        = P
420    where
421        Self: 'a;
422
423    fn try_as_segmentable(&self) -> Result<(&TcpSegmentBuilderWithOptions<A, O>, P), GsoError> {
424        Ok((self.outer(), *self.inner().inner()))
425    }
426}
427
428/// Implements [`MaybeSegmentableTransportSerializer`] for serializers that do
429/// not support segmentation.
430macro_rules! impl_not_segmentable {
431    ([$($generics:tt)*] $ty:ty) => {
432        impl<$($generics)*> MaybeSegmentableTransportSerializer for $ty {
433            type Builder<'x> = ! where Self: 'x;
434            type Payload<'x> = &'x [u8] where Self: 'x;
435
436            fn try_as_segmentable(
437                &self,
438            ) -> Result<(!, &[u8]), GsoError> {
439                Err(GsoError::NotSegmentable)
440            }
441        }
442    };
443}
444
445// TODO(https://fxbug.dev/566314871): Support UDP GSO.
446impl_not_segmentable!([S, A: IpAddress] Nested<S, UdpPacketBuilder<A>>);
447
448// Anything behind `DynTransportSerializer` has already acknowledged it can't be
449// segmented via `NotSegmentableTransportSerializer`.
450// TODO(https://fxbug.dev/578576724): Support GSO behind
451// `DynTransportSerializer`.
452impl_not_segmentable!(['a, I: FilterIpExt] DynTransportSerializer<'a, I>);
453impl_not_segmentable!([I: IpExt, B: ParseBuffer] RawIpBody<I, B>);
454impl_not_segmentable!([S, I: IcmpIpExt, M: IcmpMessage<I>] Nested<S, IcmpPacketBuilder<I, M>>);
455impl_not_segmentable!([S] Nested<S, ()>);
456impl_not_segmentable!([P, B] InnerSerializer<P, B>);
457
458/// A template for the transport header of each of the segments a packet is
459/// split into.
460///
461/// This is the transport-layer counterpart of [`SegmentableIpPacketBuilder`].
462/// Implemented both for owned builders and for references to them, so a
463/// template the stack already holds can be used without copying it up front.
464pub trait SegmentableTransportBuilder {
465    /// The builder for a single segment's transport header.
466    type SegmentBuilder: PacketBuilder<NetworkSerializationContext> + NestablePacketBuilder;
467
468    /// Returns the header builder for the segment carrying the payload bytes
469    /// starting at `offset`.
470    ///
471    /// `has_more` indicates that further segments follow this one.
472    fn segment_builder(&self, offset: u16, has_more: bool) -> Self::SegmentBuilder;
473}
474
475impl<A: IpAddress, O: InnerPacketBuilder + Clone> SegmentableTransportBuilder
476    for TcpSegmentBuilderWithOptions<A, O>
477{
478    type SegmentBuilder = Self;
479
480    fn segment_builder(&self, offset: u16, has_more: bool) -> Self {
481        let mut builder = self.clone();
482        let prefix_builder = builder.prefix_builder_mut();
483        let seq_num = prefix_builder.seq_num().wrapping_add(u32::from(offset));
484        prefix_builder.set_seq_num(seq_num);
485        if has_more {
486            // PSH and FIN carry end-of-data semantics, so they belong only on
487            // the segment carrying the last byte of the payload.
488            prefix_builder.psh(false);
489            prefix_builder.fin(false);
490        }
491        builder
492    }
493}
494
495/// The transport header template recovered by parsing a [`ForwardedPacket`].
496#[derive(Clone, Debug)]
497pub enum ParsedForwardedTransport<'a, A: IpAddress> {
498    /// A TCP segment header.
499    Tcp(TcpSegmentBuilderWithOptions<A, TcpOptionsRef<&'a [u8]>>),
500}
501
502impl<A: IpAddress> SegmentableTransportBuilder for ParsedForwardedTransport<'_, A> {
503    type SegmentBuilder = Self;
504
505    fn segment_builder(&self, offset: u16, has_more: bool) -> Self {
506        match self {
507            Self::Tcp(builder) => Self::Tcp(builder.segment_builder(offset, has_more)),
508        }
509    }
510}
511
512impl<A: IpAddress> NestablePacketBuilder for ParsedForwardedTransport<'_, A> {
513    fn constraints(&self) -> PacketConstraints {
514        match self {
515            Self::Tcp(builder) => builder.constraints(),
516        }
517    }
518}
519
520impl<A: IpAddress> PacketBuilder<NetworkSerializationContext> for ParsedForwardedTransport<'_, A> {
521    fn context_state(&self) -> <NetworkSerializationContext as SerializationContext>::ContextState {
522        match self {
523            Self::Tcp(builder) => {
524                PacketBuilder::<NetworkSerializationContext>::context_state(builder)
525            }
526        }
527    }
528
529    fn serialize(
530        &self,
531        context: &mut NetworkSerializationContext,
532        target: &mut SerializeTarget<'_>,
533        body: FragmentedBytesMut<'_, '_>,
534    ) {
535        match self {
536            Self::Tcp(builder) => builder.serialize(context, target, body),
537        }
538    }
539}
540
541impl<T: SegmentableTransportBuilder> SegmentableTransportBuilder for &T {
542    type SegmentBuilder = T::SegmentBuilder;
543
544    fn segment_builder(&self, offset: u16, has_more: bool) -> T::SegmentBuilder {
545        T::segment_builder(*self, offset, has_more)
546    }
547}
548
549impl SegmentableTransportBuilder for ! {
550    type SegmentBuilder = !;
551
552    fn segment_builder(&self, _offset: u16, _has_more: bool) -> ! {
553        match *self {}
554    }
555}
556
557/// Splits a single oversized packet into discrete segments.
558///
559/// Created via [`MaybeSegmentableIpSerializer::try_segmenter`], which fails if
560/// the packet doesn't require (or doesn't support) software segmentation.
561///
562/// The header templates may be owned or borrowed (see
563/// [`SegmentableIpPacketBuilder`] and [`SegmentableTransportBuilder`]); either
564/// way, they are only copied once per emitted segment.
565pub struct GsoSegmenter<I: GsoIpExt, IB, B, P> {
566    /// The template for each segment's IP header.
567    ip_builder: IB,
568    /// The template for each segment's transport header.
569    transport_builder: B,
570    /// The transport payload being split.
571    payload: P,
572    /// The length of `payload`.
573    payload_len: u16,
574    /// The number of payload bytes already emitted.
575    consumed: u16,
576    /// The index of the next segment to be emitted.
577    index: u16,
578    gso_info: GsoInfo,
579    _marker: PhantomData<I>,
580}
581
582impl<I, IB, B, P> GsoSegmenter<I, IB, B, P>
583where
584    I: GsoIpExt,
585    IB: SegmentableIpPacketBuilder<I>,
586    B: SegmentableTransportBuilder,
587    P: Payload + InnerPacketBuilder + Copy,
588{
589    fn new(
590        ip_builder: IB,
591        transport_builder: B,
592        payload: P,
593        gso_info: GsoInfo,
594    ) -> Result<Self, GsoError> {
595        if payload.len() > usize::from(MAX_GSO_PAYLOAD_LEN) {
596            return Err(GsoError::PayloadTooLong);
597        }
598        let payload_len =
599            u16::try_from(payload.len()).expect("payload length is at most MAX_GSO_PAYLOAD_LEN");
600        Ok(Self {
601            ip_builder,
602            transport_builder,
603            payload,
604            payload_len,
605            consumed: 0,
606            index: 0,
607            gso_info,
608            _marker: PhantomData,
609        })
610    }
611}
612
613/// The serializer for a single segment produced by a [`GsoSegmenter`].
614type SegmentSerializer<P, TB, IB> = Nested<Nested<InnerSerializer<P, EmptyBuf>, TB>, IB>;
615
616impl<I, IB, B, P> Iterator for GsoSegmenter<I, IB, B, P>
617where
618    I: GsoIpExt,
619    IB: SegmentableIpPacketBuilder<I>,
620    B: SegmentableTransportBuilder,
621    P: Payload + InnerPacketBuilder + Copy,
622{
623    /// The serializer for the next segment and whether more segments are
624    /// pending.
625    type Item = (SegmentSerializer<P, B::SegmentBuilder, IB::SegmentBuilder>, bool);
626
627    fn next(&mut self) -> Option<Self::Item> {
628        let Self {
629            ip_builder,
630            transport_builder,
631            payload,
632            payload_len,
633            consumed,
634            index,
635            gso_info,
636            _marker: _,
637        } = self;
638        let GsoInfo { gso_size: max_segment_body, gso_segs: _, ipv4_id_mode: _ } = gso_info;
639
640        let remaining = *payload_len - *consumed;
641        if remaining == 0 {
642            return None;
643        }
644        let take = remaining.min(max_segment_body.get());
645        let has_more = take < remaining;
646        let segment_body = payload.slice(u32::from(*consumed)..u32::from(*consumed + take));
647
648        let transport_builder = transport_builder.segment_builder(*consumed, has_more);
649        let ip_builder = ip_builder.segment_builder(*index, *gso_info);
650
651        *consumed += take;
652        *index = index.checked_add(1).expect("number of segments cannot exceed u16::MAX");
653
654        Some((
655            segment_body.into_serializer().wrap_in(transport_builder).wrap_in(ip_builder),
656            has_more,
657        ))
658    }
659}
660
661/// Creates a segmenter for the IP packet serialized in `packet_bytes`.
662///
663/// Recovers the builders by parsing the packet, which is only necessary when
664/// the stack doesn't hold them anymore; see
665/// [`MaybeSegmentableIpSerializer::try_segmenter`].
666fn segmenter_from_packet_bytes<I: GsoIpExt>(
667    packet_bytes: &[u8],
668    gso_info: GsoInfo,
669) -> Result<
670    GsoSegmenter<I, I::ForwardedSegmentBuilder, ParsedForwardedTransport<'_, I::Addr>, &[u8]>,
671    GsoError,
672> {
673    let mut buffer = packet_bytes;
674    let packet = <I as IpExt>::Packet::parse(&mut buffer, ()).map_err(|_| GsoError::Parse)?;
675    if packet.proto() != IpProto::Tcp.into() {
676        return Err(GsoError::NotSegmentable);
677    }
678    let ip_builder = I::try_segmented_packet_builder(&packet)?;
679
680    let (src_ip, dst_ip) = (packet.src_ip(), packet.dst_ip());
681    // Re-slice the IP bytes from `packet_bytes` for TCP parsing because
682    // `packet` is dropped at the end of the function.
683    let ip_body =
684        &packet_bytes[packet.parse_metadata().header_len()..][..packet.parse_metadata().body_len()];
685    let mut tcp_bytes = ip_body;
686    // GRO produced this packet after verifying the transport-layer checksums of
687    // the source packets, so there's no need to verify the checksum of the
688    // coalesced packet here.
689    let mut parsing_context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
690    let tcp = TcpSegment::parse(
691        &mut tcp_bytes,
692        TcpParseArgs::with_context(src_ip, dst_ip, &mut parsing_context),
693    )
694    .map_err(|_| GsoError::Parse)?;
695    // Just as above, we re-slice the payload bytes here so that we can drop
696    // `tcp` at the end of the function.
697    let payload = &ip_body[tcp.header_len()..];
698    let tcp_builder = ParsedForwardedTransport::Tcp(tcp.into_builder(src_ip, dst_ip));
699
700    GsoSegmenter::new(ip_builder, tcp_builder, payload, gso_info)
701}
702
703/// Where a packet requiring software segmentation came from.
704#[derive(Clone, Copy, Debug, Eq, PartialEq)]
705pub enum GsoSource {
706    /// The packet is being forwarded.
707    ///
708    /// Only GRO attaches GSO metadata to received packets, so forwarded packets
709    /// requiring segmentation must have been coalesced by GRO.
710    Forwarded,
711    /// The packet was generated by the local stack as a large segment.
712    Local,
713}
714
715/// Counters kept by the IP stack pertaining to software segmentation, kept
716/// separately for each [`GsoSource`].
717#[derive(Default, Debug)]
718#[cfg_attr(
719    any(test, feature = "testutils"),
720    derive(PartialEq, netstack3_macros::CounterCollection)
721)]
722pub struct GsoCounters<C = Counter> {
723    /// Counters for [`GsoSource::Forwarded`] frames.
724    pub forwarded: GsoSourceCounters<C>,
725    /// Counters for [`GsoSource::Local`] frames.
726    pub local: GsoSourceCounters<C>,
727}
728
729impl GsoCounters {
730    pub(crate) fn for_source(&self, source: GsoSource) -> &GsoSourceCounters {
731        let Self { forwarded, local } = self;
732        match source {
733            GsoSource::Forwarded => forwarded,
734            GsoSource::Local => local,
735        }
736    }
737}
738
739/// Software segmentation counters for frames from a single [`GsoSource`].
740#[derive(Default, Debug)]
741#[cfg_attr(
742    any(test, feature = "testutils"),
743    derive(PartialEq, netstack3_macros::CounterCollection)
744)]
745pub struct GsoSourceCounters<C = Counter> {
746    /// The number of IP frames requiring segmentation on egress.
747    pub segmentation_required: C,
748    /// The total number of segments produced by segmentation.
749    pub segments: C,
750    /// The number of [`GsoError::Parse`] errors encountered.
751    pub error_parse: C,
752    /// The number of [`GsoError::NotSegmentable`] errors encountered.
753    pub error_not_segmentable: C,
754    /// The number of [`GsoError::UnsupportedHeaders`] errors encountered.
755    pub error_unsupported_headers: C,
756    /// The number of [`GsoError::PayloadTooLong`] errors encountered.
757    pub error_payload_too_long: C,
758}
759
760impl GsoSourceCounters {
761    pub(crate) fn error_counter(&self, error: &GsoError) -> &Counter {
762        match error {
763            GsoError::Parse => &self.error_parse,
764            GsoError::NotSegmentable => &self.error_not_segmentable,
765            GsoError::UnsupportedHeaders => &self.error_unsupported_headers,
766            GsoError::PayloadTooLong => &self.error_payload_too_long,
767        }
768    }
769}
770
771#[cfg(test)]
772mod tests {
773    use super::*;
774
775    use alloc::vec::Vec;
776    use core::num::NonZeroU16;
777
778    use ip_test_macro::ip_test;
779    use net_types::Witness as _;
780    use net_types::ip::IpVersion;
781    use netstack3_base::testutil::{TEST_ADDRS_V4, TEST_ADDRS_V6, TestIpExt};
782    use packet::{Buf, Serializer};
783    use packet_formats::ip::{IpPacketBuilder as _, IpProto};
784    use packet_formats::ipv4::options::Ipv4Option;
785    use packet_formats::tcp::TcpSegmentBuilder;
786    use packet_formats::tcp::options::{
787        TcpOptions as _, TcpOptionsBuilder, TcpSackBlock, TimestampOption,
788    };
789    use test_case::{test_case, test_matrix};
790
791    const SRC_PORT: NonZeroU16 = NonZeroU16::new(1234).unwrap();
792    const DST_PORT: NonZeroU16 = NonZeroU16::new(80).unwrap();
793    const SEQ_NUM: u32 = 100;
794    const TTL: u8 = 64;
795    /// The payload length in the split segments (aside from a potentially
796    /// shorter last segment).
797    const GSO_SIZE: NonZeroU16 = NonZeroU16::new(100).unwrap();
798    /// The number of segments a [`PAYLOAD_LEN`]-long payload splits into.
799    const NUM_SEGMENTS: usize = 3;
800    /// The payload length of the final, partially filled segment of
801    /// [`PAYLOAD_LEN`].
802    const LAST_SEGMENT_LEN: usize = GSO_SIZE.get() as usize / 2;
803    /// A payload length that splits into [`NUM_SEGMENTS`] segments, the last
804    /// one partially filled.
805    const PAYLOAD_LEN: usize = (NUM_SEGMENTS - 1) * GSO_SIZE.get() as usize + LAST_SEGMENT_LEN;
806    /// The TCP Timestamp option carried by the packets under test.
807    const TIMESTAMP: TimestampOption = TimestampOption::new(1, 2);
808    /// The TCP SACK blocks carried by the packets under test.
809    static SACK_BLOCKS: [TcpSackBlock; 2] = [TcpSackBlock::new(10, 20), TcpSackBlock::new(30, 40)];
810
811    /// Where a [`GsoSegmenter`] under test gets its header templates from.
812    #[derive(Clone, Copy, Debug)]
813    enum TcpPacketSource {
814        /// Parsed from the bytes of a serialized packet, as for packets
815        /// coalesced by GRO.
816        Bytes,
817        /// Borrowed from the builders of a packet that hasn't been serialized,
818        /// as for packets generated by the TCP socket layer.
819        Serializer,
820    }
821
822    fn gso_info<I: net_types::ip::Ip>(id_mode: Ipv4IdMode) -> GsoInfo {
823        let ipv4_id_mode = match I::VERSION {
824            IpVersion::V4 => Some(id_mode),
825            IpVersion::V6 => None,
826        };
827        GsoInfo {
828            gso_size: GSO_SIZE,
829            gso_segs: NonZeroU16::new(NUM_SEGMENTS as u16).unwrap(),
830            ipv4_id_mode,
831        }
832    }
833
834    fn test_payload(len: usize) -> Vec<u8> {
835        // Cycle bytes until 251, the largest prime that fits in a u8, so that
836        // misaligned segmentation is unlikely to go unnoticed.
837        (0u8..251).cycle().take(len).collect()
838    }
839
840    /// Returns a serializer for a TCP packet carrying `payload`, shaped like
841    /// the one the TCP socket layer hands to the IP layer.
842    fn new_tcp_serializer<'a, I: TestIpExt + GsoIpExt>(
843        payload: &'a [u8],
844        fin: bool,
845        psh: bool,
846    ) -> impl MaybeSegmentableIpSerializer<I> + Serializer<NetworkSerializationContext> + 'a {
847        let src_ip = I::TEST_ADDRS.local_ip.get();
848        let dst_ip = I::TEST_ADDRS.remote_ip.get();
849        let mut prefix_builder =
850            TcpSegmentBuilder::new(src_ip, dst_ip, SRC_PORT, DST_PORT, SEQ_NUM, None, u16::MAX);
851        prefix_builder.fin(fin);
852        prefix_builder.psh(psh);
853        // Only options that may appear on data segments are included: options
854        // that are only valid during the handshake (e.g. MSS or Window Scale)
855        // are never seen by GSO.
856        let options = TcpOptionsBuilder {
857            timestamp: Some(TIMESTAMP),
858            sack_blocks: Some(&SACK_BLOCKS),
859            ..Default::default()
860        };
861        payload
862            .into_serializer()
863            .wrap_in(
864                TcpSegmentBuilderWithOptions::new(prefix_builder, options).expect("create builder"),
865            )
866            .wrap_in(I::PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Tcp.into()))
867    }
868
869    /// Serializes a TCP packet with `payload` for IP version `I`.
870    fn new_tcp_packet<I: TestIpExt + GsoIpExt>(payload: &[u8], fin: bool, psh: bool) -> Vec<u8> {
871        new_tcp_serializer::<I>(payload, fin, psh)
872            .serialize_vec_outer_no_reuse(&mut NetworkSerializationContext::default())
873            .expect("serialize packet")
874            .into_inner()
875    }
876
877    /// Creates a TCP packet carrying `payload` and splits it into
878    /// `gso_info`-sized segments, returning each segment serialized.
879    fn create_and_segment_tcp_packet<I: TestIpExt + GsoIpExt>(
880        source: TcpPacketSource,
881        payload: &[u8],
882        fin: bool,
883        psh: bool,
884        gso_info: GsoInfo,
885    ) -> Vec<Buf<Vec<u8>>> {
886        match source {
887            TcpPacketSource::Bytes => {
888                let packet = new_tcp_packet::<I>(payload, fin, psh);
889                collect_segments(
890                    segmenter_from_packet_bytes::<I>(&packet, gso_info).expect("create segmenter"),
891                )
892            }
893            TcpPacketSource::Serializer => {
894                let serializer = new_tcp_serializer::<I>(payload, fin, psh);
895                collect_segments(serializer.try_segmenter(gso_info).expect("create segmenter"))
896            }
897        }
898    }
899
900    /// Collects all the segments produced by `segmenter`.
901    fn collect_segments<I, IB, B, P>(mut segmenter: GsoSegmenter<I, IB, B, P>) -> Vec<Buf<Vec<u8>>>
902    where
903        I: GsoIpExt,
904        IB: SegmentableIpPacketBuilder<I>,
905        B: SegmentableTransportBuilder,
906        P: Payload + InnerPacketBuilder + Copy,
907    {
908        let mut segments = Vec::new();
909        let mut expect_more = true;
910        while let Some((segment, has_more)) = segmenter.next() {
911            // `has_more` must agree with the segmenter running out of segments.
912            assert!(expect_more);
913            expect_more = has_more;
914            let segment = segment
915                .serialize_vec_outer(&mut NetworkSerializationContext::default())
916                .map_err(|(err, _serializer)| err)
917                .expect("serialize segment")
918                .unwrap_b();
919            segments.push(segment);
920        }
921        assert!(!expect_more);
922        segments
923    }
924
925    /// Parses `buf` and returns its TCP payload, asserting that the
926    /// headers match what's expected of the segment at `index`.
927    fn check_tcp_ip_packet<I: TestIpExt>(
928        buf: &mut Buf<Vec<u8>>,
929        index: u16,
930        id_mode: Ipv4IdMode,
931        fin: bool,
932        psh: bool,
933    ) -> Vec<u8> {
934        let src_ip = I::TEST_ADDRS.local_ip.get();
935        let dst_ip = I::TEST_ADDRS.remote_ip.get();
936        {
937            let packet = buf.parse::<<I as IpExt>::Packet<_>>().expect("parse IP packet");
938            assert_eq!(packet.src_ip(), src_ip);
939            assert_eq!(packet.dst_ip(), dst_ip);
940            assert_eq!(packet.proto(), IpProto::Tcp.into());
941            assert_eq!(packet.ttl(), TTL);
942            I::map_ip_in(
943                &packet,
944                |packet| {
945                    let expected_id = match id_mode {
946                        Ipv4IdMode::Fixed => 0,
947                        Ipv4IdMode::Incrementing => index,
948                    };
949                    assert_eq!(packet.id(), expected_id);
950                },
951                |_packet| {},
952            );
953        }
954
955        let tcp = buf
956            .parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(src_ip, dst_ip))
957            .expect("parse TCP segment");
958        assert_eq!(tcp.src_port(), SRC_PORT);
959        assert_eq!(tcp.dst_port(), DST_PORT);
960        assert_eq!(tcp.fin(), fin);
961        assert_eq!(tcp.psh(), psh);
962        assert_eq!(tcp.options().timestamp(), Some(&TIMESTAMP));
963        assert_eq!(tcp.options().sack_blocks(), Some(&SACK_BLOCKS[..]));
964        tcp.body().to_vec()
965    }
966
967    #[ip_test(I)]
968    #[test_matrix(
969        [TcpPacketSource::Bytes, TcpPacketSource::Serializer],
970        [Ipv4IdMode::Incrementing, Ipv4IdMode::Fixed],
971        [LAST_SEGMENT_LEN, usize::from(GSO_SIZE.get())],
972        [true, false],
973        [true, false]
974    )]
975    fn segments_tcp_packet<I: TestIpExt + GsoIpExt>(
976        source: TcpPacketSource,
977        id_mode: Ipv4IdMode,
978        last_segment_len: usize,
979        fin: bool,
980        psh: bool,
981    ) {
982        let payload =
983            test_payload((NUM_SEGMENTS - 1) * usize::from(GSO_SIZE.get()) + last_segment_len);
984        let mut segments =
985            create_and_segment_tcp_packet::<I>(source, &payload, fin, psh, gso_info::<I>(id_mode));
986        let num_segments = segments.len();
987        assert_eq!(num_segments, NUM_SEGMENTS);
988
989        let mut collected = Vec::new();
990        for (index, segment) in segments.iter_mut().enumerate() {
991            let last = index == num_segments - 1;
992            // FIN and PSH are only kept on the last segment.
993            let expect_fin = last && fin;
994            let expect_psh = last && psh;
995            let body = check_tcp_ip_packet::<I>(
996                segment,
997                u16::try_from(index).unwrap(),
998                id_mode,
999                expect_fin,
1000                expect_psh,
1001            );
1002            assert_eq!(
1003                body.len(),
1004                if last { last_segment_len } else { usize::from(GSO_SIZE.get()) }
1005            );
1006            collected.extend_from_slice(&body);
1007        }
1008        assert_eq!(collected, payload);
1009    }
1010
1011    #[ip_test(I)]
1012    fn rejects_non_tcp_serializer<I: TestIpExt + GsoIpExt>() {
1013        let payload = test_payload(PAYLOAD_LEN);
1014        let src_ip = I::TEST_ADDRS.local_ip.get();
1015        let dst_ip = I::TEST_ADDRS.remote_ip.get();
1016        let serializer = Buf::new(payload, ..)
1017            .wrap_in(UdpPacketBuilder::new(src_ip, dst_ip, Some(SRC_PORT), DST_PORT))
1018            .wrap_in(I::PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Udp.into()));
1019        assert_eq!(
1020            serializer.try_segmenter(gso_info::<I>(Ipv4IdMode::Incrementing)).err(),
1021            Some(GsoError::NotSegmentable)
1022        );
1023    }
1024
1025    #[test_case(TcpPacketSource::Bytes; "bytes")]
1026    #[test_case(TcpPacketSource::Serializer; "serializer")]
1027    fn segments_advance_sequence_numbers(source: TcpPacketSource) {
1028        let payload = test_payload(PAYLOAD_LEN);
1029        let fin = false;
1030        let psh = true;
1031        let seq_nums: Vec<u32> = create_and_segment_tcp_packet::<Ipv4>(
1032            source,
1033            &payload,
1034            fin,
1035            psh,
1036            gso_info::<Ipv4>(Ipv4IdMode::Incrementing),
1037        )
1038        .into_iter()
1039        .map(|mut segment| {
1040            let _ = segment.parse::<Ipv4Packet<_>>().expect("parse IPv4 packet");
1041            segment
1042                .parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(
1043                    TEST_ADDRS_V4.local_ip.get(),
1044                    TEST_ADDRS_V4.remote_ip.get(),
1045                ))
1046                .expect("parse TCP segment")
1047                .seq_num()
1048        })
1049        .collect();
1050        let gso_size = u32::from(GSO_SIZE.get());
1051        let expected: Vec<u32> =
1052            (0..NUM_SEGMENTS).map(|i| SEQ_NUM + u32::try_from(i).unwrap() * gso_size).collect();
1053        assert_eq!(seq_nums, expected);
1054    }
1055
1056    #[ip_test(I)]
1057    fn rejects_non_tcp<I: TestIpExt + GsoIpExt>() {
1058        let src_ip = I::TEST_ADDRS.local_ip.get();
1059        let dst_ip = I::TEST_ADDRS.remote_ip.get();
1060        let packet = Buf::new(test_payload(PAYLOAD_LEN), ..)
1061            .wrap_in(UdpPacketBuilder::new(src_ip, dst_ip, Some(SRC_PORT), DST_PORT))
1062            .wrap_in(I::PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Udp.into()))
1063            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1064            .map_err(|(err, _serializer)| err)
1065            .expect("serialize packet")
1066            .unwrap_b()
1067            .into_inner();
1068        assert_eq!(
1069            segmenter_from_packet_bytes::<I>(&packet, gso_info::<I>(Ipv4IdMode::Incrementing))
1070                .err(),
1071            Some(GsoError::NotSegmentable)
1072        );
1073    }
1074
1075    #[test]
1076    fn rejects_unparseable_packet() {
1077        let packet = [0u8; 8];
1078        assert_eq!(
1079            segmenter_from_packet_bytes::<Ipv6>(
1080                &packet[..],
1081                gso_info::<Ipv6>(Ipv4IdMode::Incrementing)
1082            )
1083            .err(),
1084            Some(GsoError::Parse)
1085        );
1086    }
1087
1088    #[test]
1089    fn rejects_ipv4_packet_with_options() {
1090        let src_ip = TEST_ADDRS_V4.local_ip.get();
1091        let dst_ip = TEST_ADDRS_V4.remote_ip.get();
1092        let packet = Buf::new(test_payload(PAYLOAD_LEN), ..)
1093            .wrap_in(TcpSegmentBuilder::new(
1094                src_ip,
1095                dst_ip,
1096                SRC_PORT,
1097                DST_PORT,
1098                SEQ_NUM,
1099                None,
1100                u16::MAX,
1101            ))
1102            .wrap_in(
1103                Ipv4PacketBuilderWithOptions::new(
1104                    Ipv4PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Tcp.into()),
1105                    [Ipv4Option::RouterAlert { data: 0 }],
1106                )
1107                .expect("create builder"),
1108            )
1109            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1110            .map_err(|(err, _serializer)| err)
1111            .expect("serialize packet")
1112            .unwrap_b()
1113            .into_inner();
1114        assert_eq!(
1115            segmenter_from_packet_bytes::<Ipv4>(
1116                &packet,
1117                gso_info::<Ipv4>(Ipv4IdMode::Incrementing)
1118            )
1119            .err(),
1120            Some(GsoError::UnsupportedHeaders)
1121        );
1122    }
1123
1124    #[test]
1125    fn rejects_ipv6_packet_with_extension_headers() {
1126        use packet_formats::ipv6::Ipv6PacketBuilderWithHbhOptions;
1127        use packet_formats::ipv6::ext_hdrs::{
1128            ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData,
1129        };
1130
1131        let src_ip = TEST_ADDRS_V6.local_ip.get();
1132        let dst_ip = TEST_ADDRS_V6.remote_ip.get();
1133        let packet = Buf::new(test_payload(PAYLOAD_LEN), ..)
1134            .wrap_in(TcpSegmentBuilder::new(
1135                src_ip,
1136                dst_ip,
1137                SRC_PORT,
1138                DST_PORT,
1139                SEQ_NUM,
1140                None,
1141                u16::MAX,
1142            ))
1143            .wrap_in(
1144                Ipv6PacketBuilderWithHbhOptions::new(
1145                    Ipv6PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Tcp.into()),
1146                    &[HopByHopOption {
1147                        action: ExtensionHeaderOptionAction::SkipAndContinue,
1148                        mutable: false,
1149                        data: HopByHopOptionData::RouterAlert { data: 0 },
1150                    }],
1151                )
1152                .expect("create builder"),
1153            )
1154            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1155            .expect("serialize packet")
1156            .unwrap_b()
1157            .into_inner();
1158        assert_eq!(
1159            segmenter_from_packet_bytes::<Ipv6>(
1160                &packet,
1161                gso_info::<Ipv6>(Ipv4IdMode::Incrementing)
1162            )
1163            .err(),
1164            Some(GsoError::UnsupportedHeaders)
1165        );
1166    }
1167}