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// TODO(https://fxbug.dev/452980285): Require serializers behind
449// `DynTransportSerializer` to be GSO-aware.
450impl_not_segmentable!(['a, I: FilterIpExt] DynTransportSerializer<'a, I>);
451impl_not_segmentable!([I: IpExt, B: ParseBuffer] RawIpBody<I, B>);
452impl_not_segmentable!([S, I: IcmpIpExt, M: IcmpMessage<I>] Nested<S, IcmpPacketBuilder<I, M>>);
453impl_not_segmentable!([S] Nested<S, ()>);
454impl_not_segmentable!([P, B] InnerSerializer<P, B>);
455
456/// A template for the transport header of each of the segments a packet is
457/// split into.
458///
459/// This is the transport-layer counterpart of [`SegmentableIpPacketBuilder`].
460/// Implemented both for owned builders and for references to them, so a
461/// template the stack already holds can be used without copying it up front.
462pub trait SegmentableTransportBuilder {
463    /// The builder for a single segment's transport header.
464    type SegmentBuilder: PacketBuilder<NetworkSerializationContext> + NestablePacketBuilder;
465
466    /// Returns the header builder for the segment carrying the payload bytes
467    /// starting at `offset`.
468    ///
469    /// `has_more` indicates that further segments follow this one.
470    fn segment_builder(&self, offset: u16, has_more: bool) -> Self::SegmentBuilder;
471}
472
473impl<A: IpAddress, O: InnerPacketBuilder + Clone> SegmentableTransportBuilder
474    for TcpSegmentBuilderWithOptions<A, O>
475{
476    type SegmentBuilder = Self;
477
478    fn segment_builder(&self, offset: u16, has_more: bool) -> Self {
479        let mut builder = self.clone();
480        let prefix_builder = builder.prefix_builder_mut();
481        let seq_num = prefix_builder.seq_num().wrapping_add(u32::from(offset));
482        prefix_builder.set_seq_num(seq_num);
483        if has_more {
484            // PSH and FIN carry end-of-data semantics, so they belong only on
485            // the segment carrying the last byte of the payload.
486            prefix_builder.psh(false);
487            prefix_builder.fin(false);
488        }
489        builder
490    }
491}
492
493/// The transport header template recovered by parsing a [`ForwardedPacket`].
494#[derive(Clone, Debug)]
495pub enum ParsedForwardedTransport<'a, A: IpAddress> {
496    /// A TCP segment header.
497    Tcp(TcpSegmentBuilderWithOptions<A, TcpOptionsRef<&'a [u8]>>),
498}
499
500impl<A: IpAddress> SegmentableTransportBuilder for ParsedForwardedTransport<'_, A> {
501    type SegmentBuilder = Self;
502
503    fn segment_builder(&self, offset: u16, has_more: bool) -> Self {
504        match self {
505            Self::Tcp(builder) => Self::Tcp(builder.segment_builder(offset, has_more)),
506        }
507    }
508}
509
510impl<A: IpAddress> NestablePacketBuilder for ParsedForwardedTransport<'_, A> {
511    fn constraints(&self) -> PacketConstraints {
512        match self {
513            Self::Tcp(builder) => builder.constraints(),
514        }
515    }
516}
517
518impl<A: IpAddress> PacketBuilder<NetworkSerializationContext> for ParsedForwardedTransport<'_, A> {
519    fn context_state(&self) -> <NetworkSerializationContext as SerializationContext>::ContextState {
520        match self {
521            Self::Tcp(builder) => {
522                PacketBuilder::<NetworkSerializationContext>::context_state(builder)
523            }
524        }
525    }
526
527    fn serialize(
528        &self,
529        context: &mut NetworkSerializationContext,
530        target: &mut SerializeTarget<'_>,
531        body: FragmentedBytesMut<'_, '_>,
532    ) {
533        match self {
534            Self::Tcp(builder) => builder.serialize(context, target, body),
535        }
536    }
537}
538
539impl<T: SegmentableTransportBuilder> SegmentableTransportBuilder for &T {
540    type SegmentBuilder = T::SegmentBuilder;
541
542    fn segment_builder(&self, offset: u16, has_more: bool) -> T::SegmentBuilder {
543        T::segment_builder(*self, offset, has_more)
544    }
545}
546
547impl SegmentableTransportBuilder for ! {
548    type SegmentBuilder = !;
549
550    fn segment_builder(&self, _offset: u16, _has_more: bool) -> ! {
551        match *self {}
552    }
553}
554
555/// Splits a single oversized packet into discrete segments.
556///
557/// Created via [`MaybeSegmentableIpSerializer::try_segmenter`], which fails if
558/// the packet doesn't require (or doesn't support) software segmentation.
559///
560/// The header templates may be owned or borrowed (see
561/// [`SegmentableIpPacketBuilder`] and [`SegmentableTransportBuilder`]); either
562/// way, they are only copied once per emitted segment.
563pub struct GsoSegmenter<I: GsoIpExt, IB, B, P> {
564    /// The template for each segment's IP header.
565    ip_builder: IB,
566    /// The template for each segment's transport header.
567    transport_builder: B,
568    /// The transport payload being split.
569    payload: P,
570    /// The length of `payload`.
571    payload_len: u16,
572    /// The number of payload bytes already emitted.
573    consumed: u16,
574    /// The index of the next segment to be emitted.
575    index: u16,
576    gso_info: GsoInfo,
577    _marker: PhantomData<I>,
578}
579
580impl<I, IB, B, P> GsoSegmenter<I, IB, B, P>
581where
582    I: GsoIpExt,
583    IB: SegmentableIpPacketBuilder<I>,
584    B: SegmentableTransportBuilder,
585    P: Payload + InnerPacketBuilder + Copy,
586{
587    fn new(
588        ip_builder: IB,
589        transport_builder: B,
590        payload: P,
591        gso_info: GsoInfo,
592    ) -> Result<Self, GsoError> {
593        if payload.len() > usize::from(MAX_GSO_PAYLOAD_LEN) {
594            return Err(GsoError::PayloadTooLong);
595        }
596        let payload_len =
597            u16::try_from(payload.len()).expect("payload length is at most MAX_GSO_PAYLOAD_LEN");
598        Ok(Self {
599            ip_builder,
600            transport_builder,
601            payload,
602            payload_len,
603            consumed: 0,
604            index: 0,
605            gso_info,
606            _marker: PhantomData,
607        })
608    }
609}
610
611/// The serializer for a single segment produced by a [`GsoSegmenter`].
612type SegmentSerializer<P, TB, IB> = Nested<Nested<InnerSerializer<P, EmptyBuf>, TB>, IB>;
613
614impl<I, IB, B, P> Iterator for GsoSegmenter<I, IB, B, P>
615where
616    I: GsoIpExt,
617    IB: SegmentableIpPacketBuilder<I>,
618    B: SegmentableTransportBuilder,
619    P: Payload + InnerPacketBuilder + Copy,
620{
621    /// The serializer for the next segment and whether more segments are
622    /// pending.
623    type Item = (SegmentSerializer<P, B::SegmentBuilder, IB::SegmentBuilder>, bool);
624
625    fn next(&mut self) -> Option<Self::Item> {
626        let Self {
627            ip_builder,
628            transport_builder,
629            payload,
630            payload_len,
631            consumed,
632            index,
633            gso_info,
634            _marker: _,
635        } = self;
636        let GsoInfo { gso_size: max_segment_body, gso_segs: _, ipv4_id_mode: _ } = gso_info;
637
638        let remaining = *payload_len - *consumed;
639        if remaining == 0 {
640            return None;
641        }
642        let take = remaining.min(max_segment_body.get());
643        let has_more = take < remaining;
644        let segment_body = payload.slice(u32::from(*consumed)..u32::from(*consumed + take));
645
646        let transport_builder = transport_builder.segment_builder(*consumed, has_more);
647        let ip_builder = ip_builder.segment_builder(*index, *gso_info);
648
649        *consumed += take;
650        *index = index.checked_add(1).expect("number of segments cannot exceed u16::MAX");
651
652        Some((
653            segment_body.into_serializer().wrap_in(transport_builder).wrap_in(ip_builder),
654            has_more,
655        ))
656    }
657}
658
659/// Creates a segmenter for the IP packet serialized in `packet_bytes`.
660///
661/// Recovers the builders by parsing the packet, which is only necessary when
662/// the stack doesn't hold them anymore; see
663/// [`MaybeSegmentableIpSerializer::try_segmenter`].
664fn segmenter_from_packet_bytes<I: GsoIpExt>(
665    packet_bytes: &[u8],
666    gso_info: GsoInfo,
667) -> Result<
668    GsoSegmenter<I, I::ForwardedSegmentBuilder, ParsedForwardedTransport<'_, I::Addr>, &[u8]>,
669    GsoError,
670> {
671    let mut buffer = packet_bytes;
672    let packet = <I as IpExt>::Packet::parse(&mut buffer, ()).map_err(|_| GsoError::Parse)?;
673    if packet.proto() != IpProto::Tcp.into() {
674        return Err(GsoError::NotSegmentable);
675    }
676    let ip_builder = I::try_segmented_packet_builder(&packet)?;
677
678    let (src_ip, dst_ip) = (packet.src_ip(), packet.dst_ip());
679    // Re-slice the IP bytes from `packet_bytes` for TCP parsing because
680    // `packet` is dropped at the end of the function.
681    let ip_body =
682        &packet_bytes[packet.parse_metadata().header_len()..][..packet.parse_metadata().body_len()];
683    let mut tcp_bytes = ip_body;
684    // GRO produced this packet after verifying the transport-layer checksums of
685    // the source packets, so there's no need to verify the checksum of the
686    // coalesced packet here.
687    let mut parsing_context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
688    let tcp = TcpSegment::parse(
689        &mut tcp_bytes,
690        TcpParseArgs::with_context(src_ip, dst_ip, &mut parsing_context),
691    )
692    .map_err(|_| GsoError::Parse)?;
693    // Just as above, we re-slice the payload bytes here so that we can drop
694    // `tcp` at the end of the function.
695    let payload = &ip_body[tcp.header_len()..];
696    let tcp_builder = ParsedForwardedTransport::Tcp(tcp.into_builder(src_ip, dst_ip));
697
698    GsoSegmenter::new(ip_builder, tcp_builder, payload, gso_info)
699}
700
701/// Where a packet requiring software segmentation came from.
702#[derive(Clone, Copy, Debug, Eq, PartialEq)]
703pub enum GsoSource {
704    /// The packet is being forwarded.
705    ///
706    /// Only GRO attaches GSO metadata to received packets, so forwarded packets
707    /// requiring segmentation must have been coalesced by GRO.
708    Forwarded,
709    /// The packet was generated by the local stack as a large segment.
710    Local,
711}
712
713/// Counters kept by the IP stack pertaining to software segmentation, kept
714/// separately for each [`GsoSource`].
715#[derive(Default, Debug)]
716#[cfg_attr(
717    any(test, feature = "testutils"),
718    derive(PartialEq, netstack3_macros::CounterCollection)
719)]
720pub struct GsoCounters<C = Counter> {
721    /// Counters for [`GsoSource::Forwarded`] frames.
722    pub forwarded: GsoSourceCounters<C>,
723    /// Counters for [`GsoSource::Local`] frames.
724    pub local: GsoSourceCounters<C>,
725}
726
727impl GsoCounters {
728    pub(crate) fn for_source(&self, source: GsoSource) -> &GsoSourceCounters {
729        let Self { forwarded, local } = self;
730        match source {
731            GsoSource::Forwarded => forwarded,
732            GsoSource::Local => local,
733        }
734    }
735}
736
737/// Software segmentation counters for frames from a single [`GsoSource`].
738#[derive(Default, Debug)]
739#[cfg_attr(
740    any(test, feature = "testutils"),
741    derive(PartialEq, netstack3_macros::CounterCollection)
742)]
743pub struct GsoSourceCounters<C = Counter> {
744    /// The number of IP frames requiring segmentation on egress.
745    pub segmentation_required: C,
746    /// The total number of segments produced by segmentation.
747    pub segments: C,
748    /// The number of [`GsoError::Parse`] errors encountered.
749    pub error_parse: C,
750    /// The number of [`GsoError::NotSegmentable`] errors encountered.
751    pub error_not_segmentable: C,
752    /// The number of [`GsoError::UnsupportedHeaders`] errors encountered.
753    pub error_unsupported_headers: C,
754    /// The number of [`GsoError::PayloadTooLong`] errors encountered.
755    pub error_payload_too_long: C,
756}
757
758impl GsoSourceCounters {
759    pub(crate) fn error_counter(&self, error: &GsoError) -> &Counter {
760        match error {
761            GsoError::Parse => &self.error_parse,
762            GsoError::NotSegmentable => &self.error_not_segmentable,
763            GsoError::UnsupportedHeaders => &self.error_unsupported_headers,
764            GsoError::PayloadTooLong => &self.error_payload_too_long,
765        }
766    }
767}
768
769#[cfg(test)]
770mod tests {
771    use super::*;
772
773    use alloc::vec::Vec;
774    use core::num::NonZeroU16;
775
776    use ip_test_macro::ip_test;
777    use net_types::Witness as _;
778    use net_types::ip::IpVersion;
779    use netstack3_base::testutil::{TEST_ADDRS_V4, TEST_ADDRS_V6, TestIpExt};
780    use packet::{Buf, Serializer};
781    use packet_formats::ip::{IpPacketBuilder as _, IpProto};
782    use packet_formats::ipv4::options::Ipv4Option;
783    use packet_formats::tcp::TcpSegmentBuilder;
784    use packet_formats::tcp::options::{
785        TcpOptions as _, TcpOptionsBuilder, TcpSackBlock, TimestampOption,
786    };
787    use test_case::{test_case, test_matrix};
788
789    const SRC_PORT: NonZeroU16 = NonZeroU16::new(1234).unwrap();
790    const DST_PORT: NonZeroU16 = NonZeroU16::new(80).unwrap();
791    const SEQ_NUM: u32 = 100;
792    const TTL: u8 = 64;
793    /// The payload length in the split segments (aside from a potentially
794    /// shorter last segment).
795    const GSO_SIZE: NonZeroU16 = NonZeroU16::new(100).unwrap();
796    /// The number of segments a [`PAYLOAD_LEN`]-long payload splits into.
797    const NUM_SEGMENTS: usize = 3;
798    /// The payload length of the final, partially filled segment of
799    /// [`PAYLOAD_LEN`].
800    const LAST_SEGMENT_LEN: usize = GSO_SIZE.get() as usize / 2;
801    /// A payload length that splits into [`NUM_SEGMENTS`] segments, the last
802    /// one partially filled.
803    const PAYLOAD_LEN: usize = (NUM_SEGMENTS - 1) * GSO_SIZE.get() as usize + LAST_SEGMENT_LEN;
804    /// The TCP Timestamp option carried by the packets under test.
805    const TIMESTAMP: TimestampOption = TimestampOption::new(1, 2);
806    /// The TCP SACK blocks carried by the packets under test.
807    static SACK_BLOCKS: [TcpSackBlock; 2] = [TcpSackBlock::new(10, 20), TcpSackBlock::new(30, 40)];
808
809    /// Where a [`GsoSegmenter`] under test gets its header templates from.
810    #[derive(Clone, Copy, Debug)]
811    enum TcpPacketSource {
812        /// Parsed from the bytes of a serialized packet, as for packets
813        /// coalesced by GRO.
814        Bytes,
815        /// Borrowed from the builders of a packet that hasn't been serialized,
816        /// as for packets generated by the TCP socket layer.
817        Serializer,
818    }
819
820    fn gso_info<I: net_types::ip::Ip>(id_mode: Ipv4IdMode) -> GsoInfo {
821        let ipv4_id_mode = match I::VERSION {
822            IpVersion::V4 => Some(id_mode),
823            IpVersion::V6 => None,
824        };
825        GsoInfo {
826            gso_size: GSO_SIZE,
827            gso_segs: NonZeroU16::new(NUM_SEGMENTS as u16).unwrap(),
828            ipv4_id_mode,
829        }
830    }
831
832    fn test_payload(len: usize) -> Vec<u8> {
833        // Cycle bytes until 251, the largest prime that fits in a u8, so that
834        // misaligned segmentation is unlikely to go unnoticed.
835        (0u8..251).cycle().take(len).collect()
836    }
837
838    /// Returns a serializer for a TCP packet carrying `payload`, shaped like
839    /// the one the TCP socket layer hands to the IP layer.
840    fn new_tcp_serializer<'a, I: TestIpExt + GsoIpExt>(
841        payload: &'a [u8],
842        fin: bool,
843        psh: bool,
844    ) -> impl MaybeSegmentableIpSerializer<I> + Serializer<NetworkSerializationContext> + 'a {
845        let src_ip = I::TEST_ADDRS.local_ip.get();
846        let dst_ip = I::TEST_ADDRS.remote_ip.get();
847        let mut prefix_builder =
848            TcpSegmentBuilder::new(src_ip, dst_ip, SRC_PORT, DST_PORT, SEQ_NUM, None, u16::MAX);
849        prefix_builder.fin(fin);
850        prefix_builder.psh(psh);
851        // Only options that may appear on data segments are included: options
852        // that are only valid during the handshake (e.g. MSS or Window Scale)
853        // are never seen by GSO.
854        let options = TcpOptionsBuilder {
855            timestamp: Some(TIMESTAMP),
856            sack_blocks: Some(&SACK_BLOCKS),
857            ..Default::default()
858        };
859        payload
860            .into_serializer()
861            .wrap_in(
862                TcpSegmentBuilderWithOptions::new(prefix_builder, options).expect("create builder"),
863            )
864            .wrap_in(I::PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Tcp.into()))
865    }
866
867    /// Serializes a TCP packet with `payload` for IP version `I`.
868    fn new_tcp_packet<I: TestIpExt + GsoIpExt>(payload: &[u8], fin: bool, psh: bool) -> Vec<u8> {
869        new_tcp_serializer::<I>(payload, fin, psh)
870            .serialize_vec_outer_no_reuse(&mut NetworkSerializationContext::default())
871            .expect("serialize packet")
872            .into_inner()
873    }
874
875    /// Creates a TCP packet carrying `payload` and splits it into
876    /// `gso_info`-sized segments, returning each segment serialized.
877    fn create_and_segment_tcp_packet<I: TestIpExt + GsoIpExt>(
878        source: TcpPacketSource,
879        payload: &[u8],
880        fin: bool,
881        psh: bool,
882        gso_info: GsoInfo,
883    ) -> Vec<Buf<Vec<u8>>> {
884        match source {
885            TcpPacketSource::Bytes => {
886                let packet = new_tcp_packet::<I>(payload, fin, psh);
887                collect_segments(
888                    segmenter_from_packet_bytes::<I>(&packet, gso_info).expect("create segmenter"),
889                )
890            }
891            TcpPacketSource::Serializer => {
892                let serializer = new_tcp_serializer::<I>(payload, fin, psh);
893                collect_segments(serializer.try_segmenter(gso_info).expect("create segmenter"))
894            }
895        }
896    }
897
898    /// Collects all the segments produced by `segmenter`.
899    fn collect_segments<I, IB, B, P>(mut segmenter: GsoSegmenter<I, IB, B, P>) -> Vec<Buf<Vec<u8>>>
900    where
901        I: GsoIpExt,
902        IB: SegmentableIpPacketBuilder<I>,
903        B: SegmentableTransportBuilder,
904        P: Payload + InnerPacketBuilder + Copy,
905    {
906        let mut segments = Vec::new();
907        let mut expect_more = true;
908        while let Some((segment, has_more)) = segmenter.next() {
909            // `has_more` must agree with the segmenter running out of segments.
910            assert!(expect_more);
911            expect_more = has_more;
912            let segment = segment
913                .serialize_vec_outer(&mut NetworkSerializationContext::default())
914                .map_err(|(err, _serializer)| err)
915                .expect("serialize segment")
916                .unwrap_b();
917            segments.push(segment);
918        }
919        assert!(!expect_more);
920        segments
921    }
922
923    /// Parses `buf` and returns its TCP payload, asserting that the
924    /// headers match what's expected of the segment at `index`.
925    fn check_tcp_ip_packet<I: TestIpExt>(
926        buf: &mut Buf<Vec<u8>>,
927        index: u16,
928        id_mode: Ipv4IdMode,
929        fin: bool,
930        psh: bool,
931    ) -> Vec<u8> {
932        let src_ip = I::TEST_ADDRS.local_ip.get();
933        let dst_ip = I::TEST_ADDRS.remote_ip.get();
934        {
935            let packet = buf.parse::<<I as IpExt>::Packet<_>>().expect("parse IP packet");
936            assert_eq!(packet.src_ip(), src_ip);
937            assert_eq!(packet.dst_ip(), dst_ip);
938            assert_eq!(packet.proto(), IpProto::Tcp.into());
939            assert_eq!(packet.ttl(), TTL);
940            I::map_ip_in(
941                &packet,
942                |packet| {
943                    let expected_id = match id_mode {
944                        Ipv4IdMode::Fixed => 0,
945                        Ipv4IdMode::Incrementing => index,
946                    };
947                    assert_eq!(packet.id(), expected_id);
948                },
949                |_packet| {},
950            );
951        }
952
953        let tcp = buf
954            .parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(src_ip, dst_ip))
955            .expect("parse TCP segment");
956        assert_eq!(tcp.src_port(), SRC_PORT);
957        assert_eq!(tcp.dst_port(), DST_PORT);
958        assert_eq!(tcp.fin(), fin);
959        assert_eq!(tcp.psh(), psh);
960        assert_eq!(tcp.options().timestamp(), Some(&TIMESTAMP));
961        assert_eq!(tcp.options().sack_blocks(), Some(&SACK_BLOCKS[..]));
962        tcp.body().to_vec()
963    }
964
965    #[ip_test(I)]
966    #[test_matrix(
967        [TcpPacketSource::Bytes, TcpPacketSource::Serializer],
968        [Ipv4IdMode::Incrementing, Ipv4IdMode::Fixed],
969        [LAST_SEGMENT_LEN, usize::from(GSO_SIZE.get())],
970        [true, false],
971        [true, false]
972    )]
973    fn segments_tcp_packet<I: TestIpExt + GsoIpExt>(
974        source: TcpPacketSource,
975        id_mode: Ipv4IdMode,
976        last_segment_len: usize,
977        fin: bool,
978        psh: bool,
979    ) {
980        let payload =
981            test_payload((NUM_SEGMENTS - 1) * usize::from(GSO_SIZE.get()) + last_segment_len);
982        let mut segments =
983            create_and_segment_tcp_packet::<I>(source, &payload, fin, psh, gso_info::<I>(id_mode));
984        let num_segments = segments.len();
985        assert_eq!(num_segments, NUM_SEGMENTS);
986
987        let mut collected = Vec::new();
988        for (index, segment) in segments.iter_mut().enumerate() {
989            let last = index == num_segments - 1;
990            // FIN and PSH are only kept on the last segment.
991            let expect_fin = last && fin;
992            let expect_psh = last && psh;
993            let body = check_tcp_ip_packet::<I>(
994                segment,
995                u16::try_from(index).unwrap(),
996                id_mode,
997                expect_fin,
998                expect_psh,
999            );
1000            assert_eq!(
1001                body.len(),
1002                if last { last_segment_len } else { usize::from(GSO_SIZE.get()) }
1003            );
1004            collected.extend_from_slice(&body);
1005        }
1006        assert_eq!(collected, payload);
1007    }
1008
1009    #[ip_test(I)]
1010    fn rejects_non_tcp_serializer<I: TestIpExt + GsoIpExt>() {
1011        let payload = test_payload(PAYLOAD_LEN);
1012        let src_ip = I::TEST_ADDRS.local_ip.get();
1013        let dst_ip = I::TEST_ADDRS.remote_ip.get();
1014        let serializer = Buf::new(payload, ..)
1015            .wrap_in(UdpPacketBuilder::new(src_ip, dst_ip, Some(SRC_PORT), DST_PORT))
1016            .wrap_in(I::PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Udp.into()));
1017        assert_eq!(
1018            serializer.try_segmenter(gso_info::<I>(Ipv4IdMode::Incrementing)).err(),
1019            Some(GsoError::NotSegmentable)
1020        );
1021    }
1022
1023    #[test_case(TcpPacketSource::Bytes; "bytes")]
1024    #[test_case(TcpPacketSource::Serializer; "serializer")]
1025    fn segments_advance_sequence_numbers(source: TcpPacketSource) {
1026        let payload = test_payload(PAYLOAD_LEN);
1027        let fin = false;
1028        let psh = true;
1029        let seq_nums: Vec<u32> = create_and_segment_tcp_packet::<Ipv4>(
1030            source,
1031            &payload,
1032            fin,
1033            psh,
1034            gso_info::<Ipv4>(Ipv4IdMode::Incrementing),
1035        )
1036        .into_iter()
1037        .map(|mut segment| {
1038            let _ = segment.parse::<Ipv4Packet<_>>().expect("parse IPv4 packet");
1039            segment
1040                .parse_with::<_, TcpSegment<_>>(TcpParseArgs::new(
1041                    TEST_ADDRS_V4.local_ip.get(),
1042                    TEST_ADDRS_V4.remote_ip.get(),
1043                ))
1044                .expect("parse TCP segment")
1045                .seq_num()
1046        })
1047        .collect();
1048        let gso_size = u32::from(GSO_SIZE.get());
1049        let expected: Vec<u32> =
1050            (0..NUM_SEGMENTS).map(|i| SEQ_NUM + u32::try_from(i).unwrap() * gso_size).collect();
1051        assert_eq!(seq_nums, expected);
1052    }
1053
1054    #[ip_test(I)]
1055    fn rejects_non_tcp<I: TestIpExt + GsoIpExt>() {
1056        let src_ip = I::TEST_ADDRS.local_ip.get();
1057        let dst_ip = I::TEST_ADDRS.remote_ip.get();
1058        let packet = Buf::new(test_payload(PAYLOAD_LEN), ..)
1059            .wrap_in(UdpPacketBuilder::new(src_ip, dst_ip, Some(SRC_PORT), DST_PORT))
1060            .wrap_in(I::PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Udp.into()))
1061            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1062            .map_err(|(err, _serializer)| err)
1063            .expect("serialize packet")
1064            .unwrap_b()
1065            .into_inner();
1066        assert_eq!(
1067            segmenter_from_packet_bytes::<I>(&packet, gso_info::<I>(Ipv4IdMode::Incrementing))
1068                .err(),
1069            Some(GsoError::NotSegmentable)
1070        );
1071    }
1072
1073    #[test]
1074    fn rejects_unparseable_packet() {
1075        let packet = [0u8; 8];
1076        assert_eq!(
1077            segmenter_from_packet_bytes::<Ipv6>(
1078                &packet[..],
1079                gso_info::<Ipv6>(Ipv4IdMode::Incrementing)
1080            )
1081            .err(),
1082            Some(GsoError::Parse)
1083        );
1084    }
1085
1086    #[test]
1087    fn rejects_ipv4_packet_with_options() {
1088        let src_ip = TEST_ADDRS_V4.local_ip.get();
1089        let dst_ip = TEST_ADDRS_V4.remote_ip.get();
1090        let packet = Buf::new(test_payload(PAYLOAD_LEN), ..)
1091            .wrap_in(TcpSegmentBuilder::new(
1092                src_ip,
1093                dst_ip,
1094                SRC_PORT,
1095                DST_PORT,
1096                SEQ_NUM,
1097                None,
1098                u16::MAX,
1099            ))
1100            .wrap_in(
1101                Ipv4PacketBuilderWithOptions::new(
1102                    Ipv4PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Tcp.into()),
1103                    [Ipv4Option::RouterAlert { data: 0 }],
1104                )
1105                .expect("create builder"),
1106            )
1107            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1108            .map_err(|(err, _serializer)| err)
1109            .expect("serialize packet")
1110            .unwrap_b()
1111            .into_inner();
1112        assert_eq!(
1113            segmenter_from_packet_bytes::<Ipv4>(
1114                &packet,
1115                gso_info::<Ipv4>(Ipv4IdMode::Incrementing)
1116            )
1117            .err(),
1118            Some(GsoError::UnsupportedHeaders)
1119        );
1120    }
1121
1122    #[test]
1123    fn rejects_ipv6_packet_with_extension_headers() {
1124        use packet_formats::ipv6::Ipv6PacketBuilderWithHbhOptions;
1125        use packet_formats::ipv6::ext_hdrs::{
1126            ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData,
1127        };
1128
1129        let src_ip = TEST_ADDRS_V6.local_ip.get();
1130        let dst_ip = TEST_ADDRS_V6.remote_ip.get();
1131        let packet = Buf::new(test_payload(PAYLOAD_LEN), ..)
1132            .wrap_in(TcpSegmentBuilder::new(
1133                src_ip,
1134                dst_ip,
1135                SRC_PORT,
1136                DST_PORT,
1137                SEQ_NUM,
1138                None,
1139                u16::MAX,
1140            ))
1141            .wrap_in(
1142                Ipv6PacketBuilderWithHbhOptions::new(
1143                    Ipv6PacketBuilder::new(src_ip, dst_ip, TTL, IpProto::Tcp.into()),
1144                    &[HopByHopOption {
1145                        action: ExtensionHeaderOptionAction::SkipAndContinue,
1146                        mutable: false,
1147                        data: HopByHopOptionData::RouterAlert { data: 0 },
1148                    }],
1149                )
1150                .expect("create builder"),
1151            )
1152            .serialize_vec_outer(&mut NetworkSerializationContext::default())
1153            .expect("serialize packet")
1154            .unwrap_b()
1155            .into_inner();
1156        assert_eq!(
1157            segmenter_from_packet_bytes::<Ipv6>(
1158                &packet,
1159                gso_info::<Ipv6>(Ipv4IdMode::Incrementing)
1160            )
1161            .err(),
1162            Some(GsoError::UnsupportedHeaders)
1163        );
1164    }
1165}