1use alloc::vec::Vec;
8use core::num::NonZeroU16;
9
10use assert_matches::assert_matches;
11use derivative::Derivative;
12use net_types::ethernet::Mac;
13use net_types::for_any_ip_version;
14use net_types::ip::{IpAddress, IpVersion, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
15use packet::ParsablePacket;
16use packet_formats::ethernet::{EtherType, EthernetFrame, EthernetFrameLengthCheck};
17use packet_formats::ip::{DscpAndEcn, IpExt, IpProto, Ipv4Proto, Ipv6Proto};
18use packet_formats::ipv4::{Ipv4Header, Ipv4Packet, Ipv4PacketRaw};
19use packet_formats::ipv6::{IPV6_FIXED_HDR_LEN, Ipv6Header, Ipv6Packet, Ipv6PacketRaw};
20use packet_formats::tcp::{MAX_OPTIONS_LEN, TcpParseArgs, TcpSegment, TcpSegmentRaw};
21use static_assertions::const_assert;
22
23use netstack3_base::{
24 ChecksumRxOffloading, Counter, GsoInfo, Inspectable, Inspector, InspectorExt as _, Ipv4IdMode,
25 MAX_GSO_PAYLOAD_LEN, NetworkParsingContext,
26};
27
28const MAX_GRO_FRAME_LEN: u16 = u16::MAX;
43const_assert!(MAX_GRO_FRAME_LEN as usize <= MAX_GSO_PAYLOAD_LEN as usize);
46
47fn gro_headroom(frame_len: usize) -> usize {
50 usize::from(MAX_GRO_FRAME_LEN).saturating_sub(frame_len)
51}
52
53#[derive(Debug)]
56pub enum BufferSlice<'a, 'b> {
57 Contiguous(&'a mut [u8]),
59 Linearized(&'b mut [u8]),
62}
63
64impl BufferSlice<'_, '_> {
65 pub fn as_slice(&self) -> &[u8] {
67 match self {
68 Self::Contiguous(s) => s,
69 Self::Linearized(s) => s,
70 }
71 }
72
73 pub fn as_slice_mut(&mut self) -> &mut [u8] {
75 match self {
76 Self::Contiguous(s) => s,
77 Self::Linearized(s) => s,
78 }
79 }
80}
81
82pub trait MaybeContiguousBuffer {
84 fn linearized<'a, 'b>(
88 &'a mut self,
89 storage: Option<&'b mut Vec<u8>>,
90 ) -> Option<BufferSlice<'a, 'b>>;
91
92 fn unwrap_contiguous<'a>(&'a mut self) -> &'a mut [u8] {
95 assert_matches!(
96 self.linearized(None).expect("must be `Some` if contiguous"),
97 BufferSlice::Contiguous(slice) => slice
98 )
99 }
100}
101
102#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub enum GroFrameType {
105 Ethernet,
107 PureIp(IpVersion),
109}
110
111pub trait GroBufferDestination: Eq {
114 fn frame_type(&self) -> GroFrameType;
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120struct EthernetFlowId {
121 src_mac: Mac,
122 dst_mac: Mac,
123 tag: Option<u32>,
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128enum LinkLayerFlowId {
129 Ethernet(EthernetFlowId),
130 PureIp,
131}
132
133enum LinkLayerFrame {
135 Ethernet { flow_id: EthernetFlowId, ethertype: Option<EtherType> },
136 PureIp(IpVersion),
137}
138
139impl LinkLayerFrame {
140 fn parse<T: GroBufferDestination>(slice: &mut &[u8], target: &T) -> Option<LinkLayerFrame> {
141 match target.frame_type() {
142 GroFrameType::Ethernet => {
143 let frame = EthernetFrame::parse(slice, EthernetFrameLengthCheck::NoCheck).ok()?;
144 let flow_id = EthernetFlowId {
145 src_mac: frame.src_mac(),
146 dst_mac: frame.dst_mac(),
147 tag: frame.tag(),
148 };
149 Some(LinkLayerFrame::Ethernet { flow_id, ethertype: frame.ethertype() })
150 }
151 GroFrameType::PureIp(ip_version) => Some(LinkLayerFrame::PureIp(ip_version)),
152 }
153 }
154
155 fn is_eligible_for_gro(&self) -> bool {
156 match self {
157 Self::Ethernet { .. } => true,
158 Self::PureIp(_) => true,
159 }
160 }
161
162 fn ethertype(&self) -> Option<EtherType> {
163 match self {
164 Self::Ethernet { ethertype, .. } => *ethertype,
165 Self::PureIp(ip_version) => Some(EtherType::from_ip_version(*ip_version)),
166 }
167 }
168
169 fn flow_id(&self) -> LinkLayerFlowId {
170 match self {
171 Self::Ethernet { flow_id, .. } => LinkLayerFlowId::Ethernet(*flow_id),
172 Self::PureIp(_) => LinkLayerFlowId::PureIp,
173 }
174 }
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
179struct Ipv4FlowId {
180 src_ip: Ipv4Addr,
181 dst_ip: Ipv4Addr,
182}
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186struct Ipv6FlowId {
187 src_ip: Ipv6Addr,
188 dst_ip: Ipv6Addr,
189 flowlabel: u32,
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194enum IpFlowId {
195 Ipv4(Ipv4FlowId),
196 Ipv6(Ipv6FlowId),
197}
198
199impl IpFlowId {
200 fn recover_payload_partial_sum(&self, segment: &TcpSegment<&'_ [u8]>) -> [u8; 2] {
203 fn recover<A: IpAddress>(src_ip: A, dst_ip: A, segment: &TcpSegment<&'_ [u8]>) -> [u8; 2] {
204 segment
205 .recover_payload_partial_sum::<A::Version>(src_ip, dst_ip)
206 .expect("transport len fits in IP total length")
207 }
208
209 match self {
210 IpFlowId::Ipv4(Ipv4FlowId { src_ip, dst_ip }) => recover(*src_ip, *dst_ip, segment),
211 IpFlowId::Ipv6(Ipv6FlowId { src_ip, dst_ip, flowlabel: _ }) => {
212 recover(*src_ip, *dst_ip, segment)
213 }
214 }
215 }
216}
217
218enum IpPacket<'a> {
219 V4(Ipv4Packet<&'a [u8]>),
220 V6(Ipv6Packet<&'a [u8]>),
221}
222
223impl<'a> From<Ipv4Packet<&'a [u8]>> for IpPacket<'a> {
224 fn from(packet: Ipv4Packet<&'a [u8]>) -> IpPacket<'a> {
225 IpPacket::V4(packet)
226 }
227}
228
229impl<'a> From<Ipv6Packet<&'a [u8]>> for IpPacket<'a> {
230 fn from(packet: Ipv6Packet<&'a [u8]>) -> IpPacket<'a> {
231 IpPacket::V6(packet)
232 }
233}
234
235impl IpPacket<'_> {
236 fn total_len(&self) -> usize {
237 match self {
238 IpPacket::V4(packet) => packet.header_len() + packet.body().len(),
239 IpPacket::V6(packet) => packet.header_len() + packet.body().len(),
240 }
241 }
242}
243
244trait GroIpPacket<I: IpExt> {
245 fn is_eligible_for_gro(&self) -> bool;
246 fn ip_proto(&self) -> Option<IpProto>;
247 fn flow_id(&self) -> IpFlowId;
248}
249
250impl GroIpPacket<Ipv4> for Ipv4Packet<&[u8]> {
251 fn is_eligible_for_gro(&self) -> bool {
252 self.fragment_offset() == packet_formats::ip::FragmentOffset::ZERO
255 && !self.mf_flag()
256 && self.header_len() == packet_formats::ipv4::HDR_PREFIX_LEN
260 }
261
262 fn ip_proto(&self) -> Option<IpProto> {
263 match self.proto() {
264 Ipv4Proto::Proto(proto) => Some(proto),
265 Ipv4Proto::Icmp | Ipv4Proto::Igmp | Ipv4Proto::Other(_) => None,
266 }
267 }
268
269 fn flow_id(&self) -> IpFlowId {
270 IpFlowId::Ipv4(Ipv4FlowId { src_ip: self.src_ip(), dst_ip: self.dst_ip() })
271 }
272}
273
274impl GroIpPacket<Ipv6> for Ipv6Packet<&[u8]> {
275 fn is_eligible_for_gro(&self) -> bool {
276 self.iter_extension_hdrs().next().is_none()
282 }
283
284 fn ip_proto(&self) -> Option<IpProto> {
285 match self.proto() {
286 Ipv6Proto::Proto(proto) => Some(proto),
287 Ipv6Proto::Icmpv6 | Ipv6Proto::NoNextHeader | Ipv6Proto::Other(_) => None,
288 }
289 }
290
291 fn flow_id(&self) -> IpFlowId {
292 IpFlowId::Ipv6(Ipv6FlowId {
293 src_ip: self.src_ip(),
294 dst_ip: self.dst_ip(),
295 flowlabel: self.flowlabel(),
296 })
297 }
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq)]
302struct TcpFlowId {
303 src_port: NonZeroU16,
304 dst_port: NonZeroU16,
305}
306
307#[derive(Debug, Clone, Copy, PartialEq, Eq)]
309enum TransportFlowId {
310 Tcp(TcpFlowId),
311}
312
313fn flag_requires_flush(segment: &TcpSegment<&'_ [u8]>) -> bool {
314 segment.syn() || segment.fin() || segment.rst() || segment.urg() || segment.psh()
315}
316
317enum TransportPacket<'a> {
318 Tcp(TcpSegment<&'a [u8]>),
319}
320
321impl<'a> TransportPacket<'a> {
322 fn parse<A: IpAddress>(
323 transport_view: &mut &'a [u8],
324 proto: IpProto,
325 src_ip: A,
326 dst_ip: A,
327 context: &mut NetworkParsingContext,
328 ) -> Option<TransportPacket<'a>> {
329 match proto {
330 IpProto::Tcp => TcpSegment::parse(
331 transport_view,
332 TcpParseArgs::with_context(src_ip, dst_ip, context),
333 )
334 .ok()
335 .map(TransportPacket::Tcp),
336 IpProto::Udp | IpProto::Reserved => None,
338 }
339 }
340
341 fn is_eligible_for_gro(&self) -> bool {
342 match self {
343 Self::Tcp(_) => true,
344 }
345 }
346
347 fn flow_id(&self) -> TransportFlowId {
348 match self {
349 Self::Tcp(tcp) => TransportFlowId::Tcp(TcpFlowId {
350 src_port: tcp.src_port(),
351 dst_port: tcp.dst_port(),
352 }),
353 }
354 }
355
356 fn flush_if_not_merged(&self) -> bool {
362 match self {
363 Self::Tcp(tcp) => {
364 let is_payload_segment = !tcp.body().is_empty();
365 !is_payload_segment || flag_requires_flush(tcp)
366 }
367 }
368 }
369}
370
371#[derive(Debug, Clone, Copy, PartialEq, Eq)]
377struct GroFlowId {
378 link_layer: LinkLayerFlowId,
379 ip: IpFlowId,
380 transport: TransportFlowId,
381}
382
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384struct HeaderOffsets {
385 pub ip_offset: usize,
386 pub transport_offset: usize,
387}
388
389fn build_csum_offload_output(
393 input: ChecksumRxOffloading,
394 context_post_parse: &NetworkParsingContext,
395) -> ChecksumRxOffloading {
396 let verified = context_post_parse.verified_checksum_count();
397 match input {
398 ChecksumRxOffloading::FullyOffloaded => ChecksumRxOffloading::FullyOffloaded,
399 ChecksumRxOffloading::Offloaded(Some(n)) => {
400 ChecksumRxOffloading::Offloaded(Some(n.saturating_add(verified)))
401 }
402 ChecksumRxOffloading::Offloaded(None) => {
403 ChecksumRxOffloading::Offloaded(NonZeroU16::new(verified))
404 }
405 }
406}
407
408struct GroPacket<'a> {
411 pub flow_id: GroFlowId,
412 pub offsets: HeaderOffsets,
413 pub ip: IpPacket<'a>,
414 pub transport: TransportPacket<'a>,
415}
416
417impl<'a> GroPacket<'a> {
418 fn parse<T: GroBufferDestination>(
419 slice: &'a [u8],
420 target: &T,
421 context: &mut NetworkParsingContext,
422 ) -> Option<GroPacket<'a>> {
423 let total_len = slice.len();
424 let mut view = slice;
425
426 let ll_frame =
427 LinkLayerFrame::parse(&mut view, target).filter(|f| f.is_eligible_for_gro())?;
428 let ethertype = ll_frame.ethertype()?;
429
430 let ip_offset = total_len - view.len();
431 let ip_version = ethertype.to_ip_version()?;
432 let (ip, ip_flow_id, transport_offset, transport) = for_any_ip_version!(ip_version, I, {
433 let ip = <I as IpExt>::Packet::parse(&mut view, ())
434 .ok()
435 .filter(|p| p.is_eligible_for_gro())?;
436 let flow_id = ip.flow_id();
437 let proto = ip.ip_proto()?;
438 let src = ip.src_ip();
439 let dst = ip.dst_ip();
440
441 let transport_offset = ip_offset + ip.header_len();
447 let transport = TransportPacket::parse(&mut view, proto, src, dst, context)
448 .filter(|p| p.is_eligible_for_gro())?;
449
450 (ip.into(), flow_id, transport_offset, transport)
451 });
452
453 let offsets = HeaderOffsets { ip_offset, transport_offset };
454 let flow_id = GroFlowId {
455 link_layer: ll_frame.flow_id(),
456 ip: ip_flow_id,
457 transport: transport.flow_id(),
458 };
459
460 Some(GroPacket { flow_id, offsets, ip, transport })
461 }
462
463 fn payload_end(&self) -> usize {
474 self.offsets.ip_offset + self.ip.total_len()
475 }
476
477 fn flush_if_not_merged(&self, frame_len: usize) -> bool {
480 gro_headroom(frame_len) == 0 || self.transport.flush_if_not_merged()
485 }
486}
487
488enum CoalesceResult {
489 Flush,
490 Continue,
491}
492
493#[derive(Debug, Clone, Copy, PartialEq, Eq)]
495struct TcpOptions {
496 bytes: [u8; MAX_OPTIONS_LEN],
497 len: usize,
498}
499
500impl TcpOptions {
501 fn from_slice(slice: &[u8]) -> Self {
502 let mut bytes = [0u8; MAX_OPTIONS_LEN];
503 let len = slice.len();
507 bytes[..len].copy_from_slice(slice);
508 Self { bytes, len }
509 }
510
511 fn as_bytes(&self) -> &[u8] {
512 &self.bytes[..self.len]
513 }
514}
515
516const PSH_OR_FIN: u8 = packet_formats::tcp::flags::PSH | packet_formats::tcp::flags::FIN;
518
519#[derive(Debug, Clone, PartialEq, Eq)]
521struct TcpFlow {
522 flags: u8,
523 reserved_bits: u8,
524 ack: Option<u32>,
525 win: u16,
526 options: TcpOptions,
527 next_seq: u32,
534 checksum: [u8; 2],
535 orig_tcp_len: usize,
542 tcp_len_is_odd: bool,
548 gso_size: NonZeroU16,
555}
556
557impl TcpFlow {
558 fn new(segment: &TcpSegment<&'_ [u8]>) -> Self {
559 let payload_len = u16::try_from(segment.body().len()).expect("payload len fits in u16");
560 let next_seq = segment.seq_num().wrapping_add(u32::from(payload_len));
561 let options = TcpOptions::from_slice(segment.options().bytes());
562 let gso_size = NonZeroU16::new(payload_len).expect("payload len is non-zero");
566 let tcp_len = segment.total_segment_len();
567 Self {
568 flags: segment.flags(),
569 reserved_bits: segment.reserved_bits(),
570 ack: segment.ack_num(),
571 win: segment.window_size(),
572 options,
573 next_seq,
574 checksum: segment.checksum(),
575 orig_tcp_len: tcp_len,
576 tcp_len_is_odd: tcp_len % 2 == 1,
577 gso_size,
578 }
579 }
580
581 fn can_coalesce(&self, current_frame_len: usize, segment: &TcpSegment<&'_ [u8]>) -> bool {
584 let Self {
585 flags,
586 reserved_bits,
587 ack,
588 win,
589 options,
590 next_seq,
591 checksum: _,
592 orig_tcp_len: _,
593 tcp_len_is_odd: _,
594 gso_size,
595 } = self;
596 let payload_len = segment.body().len();
597 if payload_len == 0 || payload_len > usize::from(gso_size.get()) {
600 return false;
601 }
602 payload_len <= gro_headroom(current_frame_len)
603 && (*flags & !PSH_OR_FIN) == (segment.flags() & !PSH_OR_FIN)
606 && *reserved_bits == segment.reserved_bits()
609 && *ack == segment.ack_num()
610 && *win == segment.window_size()
611 && options.as_bytes() == segment.options().bytes()
612 && *next_seq == segment.seq_num()
614 }
615
616 fn update_checksum(&mut self, ip_flow: &IpFlowId, segment: &TcpSegment<&'_ [u8]>) {
621 let mut partial_sum = ip_flow.recover_payload_partial_sum(segment);
622 if self.tcp_len_is_odd {
629 partial_sum = [partial_sum[1], partial_sum[0]];
630 }
631 self.checksum = internet_checksum::add(self.checksum, &partial_sum);
632 }
633
634 fn coalesce(
640 &mut self,
641 ip_flow: &IpFlowId,
642 segment: &TcpSegment<&'_ [u8]>,
643 coalesce_into: &mut Vec<u8>,
644 ) -> CoalesceResult {
645 coalesce_into.extend_from_slice(segment.body());
646 let added_payload_len =
647 u16::try_from(segment.body().len()).expect("payload len fits in u16");
648 self.update_checksum(ip_flow, segment);
649
650 self.tcp_len_is_odd ^= added_payload_len % 2 == 1;
651 self.next_seq = segment.seq_num().wrapping_add(u32::from(added_payload_len));
652 self.flags |= segment.flags() & PSH_OR_FIN;
653
654 let is_smaller_than_gso = added_payload_len < self.gso_size.get();
660 if is_smaller_than_gso || flag_requires_flush(segment) {
661 CoalesceResult::Flush
662 } else {
663 CoalesceResult::Continue
664 }
665 }
666
667 fn finalize(&self, mut transport_view: &mut [u8]) -> NonZeroU16 {
674 let tcp_len = transport_view.len();
678 let mut tcp =
679 TcpSegmentRaw::parse_mut(&mut transport_view, ()).expect("valid TCP segment header");
680
681 let checksum = internet_checksum::update(
688 self.checksum,
689 &self.orig_tcp_len.to_be_bytes(),
690 &tcp_len.to_be_bytes(),
691 );
692
693 tcp.set_checksum(checksum);
694 tcp.set_flags(self.flags);
695 self.gso_size
696 }
697}
698
699#[derive(Debug, Clone, PartialEq, Eq)]
701enum TransportFlow {
702 Tcp(TcpFlow),
703}
704
705impl TransportFlow {
706 fn new(packet: &TransportPacket<'_>) -> Self {
707 match packet {
708 TransportPacket::Tcp(tcp) => Self::Tcp(TcpFlow::new(tcp)),
709 }
710 }
711
712 fn can_coalesce(&self, current_frame_len: usize, packet: &TransportPacket<'_>) -> bool {
713 match (self, packet) {
714 (Self::Tcp(flow), TransportPacket::Tcp(tcp)) => {
715 flow.can_coalesce(current_frame_len, tcp)
716 }
717 }
718 }
719
720 fn coalesce(
721 &mut self,
722 ip_flow: &IpFlowId,
723 packet: &TransportPacket<'_>,
724 coalesce_into: &mut Vec<u8>,
725 ) -> CoalesceResult {
726 match (self, packet) {
727 (Self::Tcp(flow), TransportPacket::Tcp(tcp)) => {
728 flow.coalesce(ip_flow, tcp, coalesce_into)
729 }
730 }
731 }
732
733 fn finalize(&self, transport_view: &mut [u8]) -> NonZeroU16 {
734 match self {
735 Self::Tcp(tcp) => tcp.finalize(transport_view),
736 }
737 }
738}
739
740#[derive(Debug, Clone, Copy, PartialEq, Eq)]
741enum Ipv4IdState {
742 Initial(u16),
744 Consistent(u16),
746 Increasing(u16),
748}
749
750impl Ipv4IdState {
751 fn can_coalesce(&self, next_id: u16) -> bool {
752 match self {
753 Self::Initial(id) => next_id == *id || next_id == id.wrapping_add(1),
754 Self::Consistent(id) => next_id == *id,
755 Self::Increasing(prev_id) => next_id == prev_id.wrapping_add(1),
756 }
757 }
758
759 fn coalesce(&mut self, next_id: u16) {
760 *self = match self {
761 Self::Initial(id) => {
762 if next_id == *id {
763 Self::Consistent(next_id)
764 } else if next_id == id.wrapping_add(1) {
765 Self::Increasing(next_id)
766 } else {
767 unreachable!("cannot coalesce non-matching ID");
768 }
769 }
770 Self::Consistent(id) => {
771 debug_assert_eq!(next_id, *id);
772 Self::Consistent(next_id)
773 }
774 Self::Increasing(_) => Self::Increasing(next_id),
775 };
776 }
777}
778
779#[derive(Debug, Clone, Copy, PartialEq, Eq)]
781struct Ipv4Flow {
782 ttl: u8,
783 dscp_and_ecn: DscpAndEcn,
784 df_flag: bool,
785 id_state: Ipv4IdState,
786}
787
788impl Ipv4Flow {
789 fn new(packet: &impl Ipv4Header) -> Ipv4Flow {
790 Self {
791 ttl: packet.ttl(),
792 dscp_and_ecn: packet.dscp_and_ecn(),
793 df_flag: packet.df_flag(),
794 id_state: Ipv4IdState::Initial(packet.id()),
795 }
796 }
797
798 fn can_coalesce(&self, packet: &impl Ipv4Header) -> bool {
801 let Self { ttl, dscp_and_ecn, df_flag, id_state } = self;
802 *ttl == packet.ttl()
803 && *dscp_and_ecn == packet.dscp_and_ecn()
804 && *df_flag == packet.df_flag()
805 && id_state.can_coalesce(packet.id())
806 }
807
808 fn coalesce(&mut self, packet: &impl Ipv4Header) {
809 self.id_state.coalesce(packet.id());
810 }
811
812 fn finalize(&self, mut ip_view: &mut [u8]) {
815 let total_len = ip_view.len();
816 let mut ip = Ipv4PacketRaw::parse_mut(&mut ip_view, ()).expect("valid IPv4 header");
817 let new_len_u16 = u16::try_from(total_len).expect("IP total length fits in u16");
818 ip.set_total_len_and_update_checksum(new_len_u16);
819 }
820
821 fn ipv4_id_mode(&self) -> Ipv4IdMode {
822 match self.id_state {
823 Ipv4IdState::Consistent(_) => Ipv4IdMode::Fixed,
824 Ipv4IdState::Increasing(_) => Ipv4IdMode::Incrementing,
825 Ipv4IdState::Initial(_) => {
826 unreachable!("coalesced flow with num_coalesced > 1 cannot be Initial")
827 }
828 }
829 }
830}
831
832#[derive(Debug, Clone, Copy, PartialEq, Eq)]
834struct Ipv6Flow {
835 hop_limit: u8,
836 dscp_and_ecn: DscpAndEcn,
837}
838
839impl Ipv6Flow {
840 fn new(packet: &impl Ipv6Header) -> Ipv6Flow {
841 Self { hop_limit: packet.hop_limit(), dscp_and_ecn: packet.dscp_and_ecn() }
842 }
843
844 fn can_coalesce(&self, packet: &impl Ipv6Header) -> bool {
847 let Self { hop_limit, dscp_and_ecn } = self;
848 *hop_limit == packet.hop_limit() && *dscp_and_ecn == packet.dscp_and_ecn()
849 }
850
851 fn coalesce(&mut self, _packet: &impl Ipv6Header) {
852 }
856
857 fn finalize(&self, mut ip_view: &mut [u8]) {
860 let payload_len = ip_view.len() - IPV6_FIXED_HDR_LEN;
861 let mut ip = Ipv6PacketRaw::parse_mut(&mut ip_view, ()).expect("valid IPv6 header");
862 let payload_len_u16 = u16::try_from(payload_len).expect("IPv6 payload len fits in u16");
863 ip.set_payload_len(payload_len_u16);
864 }
865}
866
867#[derive(Debug, Clone, Copy, PartialEq, Eq)]
869enum IpFlow {
870 Ipv4(Ipv4Flow),
871 Ipv6(Ipv6Flow),
872}
873
874impl IpFlow {
875 fn new(ip: &IpPacket<'_>) -> Self {
876 match ip {
877 IpPacket::V4(packet) => IpFlow::Ipv4(Ipv4Flow::new(packet)),
878 IpPacket::V6(packet) => IpFlow::Ipv6(Ipv6Flow::new(packet)),
879 }
880 }
881
882 fn can_coalesce(&self, ip: &IpPacket<'_>) -> bool {
883 match (self, ip) {
884 (Self::Ipv4(flow), IpPacket::V4(packet)) => flow.can_coalesce(packet),
885 (Self::Ipv6(flow), IpPacket::V6(packet)) => flow.can_coalesce(packet),
886 (Self::Ipv4(_), IpPacket::V6(_)) | (Self::Ipv6(_), IpPacket::V4(_)) => false,
887 }
888 }
889
890 fn coalesce(&mut self, ip: &IpPacket<'_>) {
891 match (self, ip) {
892 (Self::Ipv4(flow), IpPacket::V4(packet)) => flow.coalesce(packet),
893 (Self::Ipv6(flow), IpPacket::V6(packet)) => flow.coalesce(packet),
894 (Self::Ipv4(_), IpPacket::V6(_)) | (Self::Ipv6(_), IpPacket::V4(_)) => {
895 unreachable!("mismatched IP versions can't be coalesced")
896 }
897 }
898 }
899
900 fn finalize(&self, ip_view: &mut [u8]) {
901 match self {
902 Self::Ipv4(flow) => flow.finalize(ip_view),
903 Self::Ipv6(flow) => flow.finalize(ip_view),
904 }
905 }
906
907 fn ipv4_id_mode(&self) -> Option<Ipv4IdMode> {
908 match self {
909 Self::Ipv4(flow) => Some(flow.ipv4_id_mode()),
910 Self::Ipv6(_) => None,
911 }
912 }
913}
914
915struct GroFlow<T> {
916 target: T,
917 flow_id: GroFlowId,
918 offsets: HeaderOffsets,
919 ip: IpFlow,
920 transport: TransportFlow,
921 checksum_offload: ChecksumRxOffloading,
922 num_coalesced: usize,
923}
924
925impl<T: Eq> GroFlow<T> {
926 fn new(target: T, parsed: GroPacket<'_>, checksum_offload: ChecksumRxOffloading) -> Self {
927 let GroPacket { flow_id, offsets, ip, transport } = parsed;
928 Self {
929 target,
930 flow_id,
931 offsets,
932 ip: IpFlow::new(&ip),
933 transport: TransportFlow::new(&transport),
934 checksum_offload,
935 num_coalesced: 1,
936 }
937 }
938
939 fn matches(
940 &self,
941 target: &T,
942 flow_id: &GroFlowId,
943 checksum_offload: ChecksumRxOffloading,
944 ) -> bool {
945 self.target == *target
946 && self.flow_id == *flow_id
947 && self.checksum_offload == checksum_offload
948 }
949
950 fn can_coalesce(&self, current_frame_len: usize, parsed: &GroPacket<'_>) -> bool {
951 self.ip.can_coalesce(&parsed.ip)
952 && self.transport.can_coalesce(current_frame_len, &parsed.transport)
953 }
954
955 fn coalesce(&mut self, parsed: GroPacket<'_>, coalesce_into: &mut Vec<u8>) -> CoalesceResult {
956 self.num_coalesced += 1;
957 self.ip.coalesce(&parsed.ip);
958 self.transport.coalesce(&self.flow_id.ip, &parsed.transport, coalesce_into)
959 }
960
961 fn finalize(&self, view: &mut [u8]) -> Option<GsoInfo> {
962 let HeaderOffsets { ip_offset, transport_offset } = self.offsets;
963 if self.num_coalesced > 1 {
964 self.ip.finalize(&mut view[ip_offset..]);
965 let gso_size = self.transport.finalize(&mut view[transport_offset..]);
966 let ipv4_id_mode = self.ip.ipv4_id_mode();
967 let gso_segs = u16::try_from(self.num_coalesced)
971 .ok()
972 .and_then(NonZeroU16::new)
973 .expect("num_coalesced > 1 and <= MAX_GRO_FRAME_LEN");
974 Some(GsoInfo { gso_size, gso_segs, ipv4_id_mode })
975 } else {
976 None
977 }
978 }
979}
980
981#[derive(Debug, PartialEq, Eq)]
983pub struct GroInputItem<B, T> {
984 pub buffer: B,
986 pub target: T,
988 pub checksum_offload: ChecksumRxOffloading,
990}
991
992#[derive(Debug)]
994pub enum GroOutputBuffers<'a, B, O> {
995 Contiguous(B),
997 Linearized {
999 slice: &'a mut [u8],
1001 buffer: B,
1003 },
1004 Coalesced {
1006 slice: &'a mut [u8],
1008 buffers: O,
1010 },
1011}
1012
1013impl<'a, B: MaybeContiguousBuffer, O> GroOutputBuffers<'a, B, O> {
1014 pub fn slice_mut(&mut self) -> &mut [u8] {
1016 match self {
1017 Self::Contiguous(b) => b.unwrap_contiguous(),
1018 Self::Linearized { slice, .. } | Self::Coalesced { slice, .. } => slice,
1019 }
1020 }
1021}
1022
1023#[derive(Debug)]
1025pub struct GroOutputItem<'a, B, T, O> {
1026 pub target: T,
1028 pub checksum_offload: ChecksumRxOffloading,
1030 pub gso_info: Option<GsoInfo>,
1032 pub buffers: GroOutputBuffers<'a, B, O>,
1034}
1035
1036#[derive(Debug, Default)]
1038#[cfg_attr(
1039 any(test, feature = "testutils"),
1040 derive(PartialEq, netstack3_macros::CounterCollection)
1041)]
1042pub struct GroCounters<C = Counter> {
1043 batches: C,
1049 input_frames: C,
1051 output_frames: C,
1056 flow_evictions: C,
1062}
1063
1064impl Inspectable for GroCounters {
1065 fn record<I: Inspector>(&self, inspector: &mut I) {
1066 let Self { batches, input_frames, output_frames, flow_evictions } = self;
1067 inspector.record_counter("Batches", batches);
1068 inspector.record_counter("InputFrames", input_frames);
1069 inspector.record_counter("OutputFrames", output_frames);
1070 inspector.record_counter("FlowEvictions", flow_evictions);
1071 }
1072}
1073
1074#[derive(Debug, Derivative)]
1076#[derivative(Default(bound = ""))]
1077pub struct GroBufferStorage<B> {
1078 coalescing_vec: Vec<u8>,
1080 coalesced_buffers: Vec<B>,
1083 linearization_vec: Vec<u8>,
1085}
1086
1087impl<B> GroBufferStorage<B> {
1088 pub fn new() -> Self {
1090 Self::default()
1091 }
1092
1093 fn clear(&mut self) {
1094 self.coalescing_vec.clear();
1095 self.linearization_vec.clear();
1096 self.coalesced_buffers.clear();
1097 }
1098}
1099
1100impl<B: MaybeContiguousBuffer> GroBufferStorage<B> {
1101 pub fn coalesce<'a, I, T>(
1103 &'a mut self,
1104 iter: I,
1105 enable_tcp_gro: bool,
1106 counters: &'a GroCounters,
1107 ) -> GroIter<'a, I, B, T>
1108 where
1109 I: Iterator<Item = GroInputItem<B, T>>,
1110 T: GroBufferDestination,
1111 {
1112 let enable_tcp_gro = match iter.size_hint() {
1113 (_, Some(1)) => false,
1114 _ => enable_tcp_gro,
1115 };
1116 GroIter::new(iter, self, counters, enable_tcp_gro)
1117 }
1118
1119 fn build_output<'a, T: Eq>(
1120 &'a mut self,
1121 ActiveFlow { flow, buffers }: ActiveFlow<B, T>,
1122 ) -> GroOutputItem<'a, B, T, alloc::vec::Drain<'a, B>> {
1123 match buffers {
1124 ActiveFlowBuffers::Single { buffer, payload_end: _ } => GroOutputItem {
1127 target: flow.target,
1128 checksum_offload: flow.checksum_offload,
1129 gso_info: None,
1130 buffers: GroOutputBuffers::Contiguous(buffer),
1131 },
1132 ActiveFlowBuffers::Coalesced => {
1133 let GroBufferStorage { coalescing_vec, coalesced_buffers, .. } = self;
1134
1135 let gso_info = flow.finalize(&mut coalescing_vec[..]);
1136
1137 let GroFlow { target, checksum_offload, num_coalesced, .. } = flow;
1138 let buffers = GroOutputBuffers::Coalesced {
1139 slice: &mut coalescing_vec[..],
1140 buffers: coalesced_buffers.drain(..num_coalesced),
1141 };
1142 GroOutputItem { target, checksum_offload, gso_info, buffers }
1143 }
1144 }
1145 }
1146}
1147
1148enum ActiveFlowBuffers<B> {
1150 Single {
1153 buffer: B,
1154 payload_end: usize,
1158 },
1159 Coalesced,
1162}
1163
1164struct ActiveFlow<B, T> {
1166 flow: GroFlow<T>,
1167 buffers: ActiveFlowBuffers<B>,
1168}
1169
1170impl<B, T> ActiveFlow<B, T> {
1171 fn frame_len(&self, coalescing_vec_len: usize) -> usize {
1174 match &self.buffers {
1175 ActiveFlowBuffers::Single { payload_end, .. } => *payload_end,
1179 ActiveFlowBuffers::Coalesced => coalescing_vec_len,
1180 }
1181 }
1182}
1183
1184impl<B: MaybeContiguousBuffer, T: Eq> ActiveFlow<B, T> {
1185 fn matches(
1186 &self,
1187 target: &T,
1188 flow_id: &GroFlowId,
1189 checksum_offload: ChecksumRxOffloading,
1190 ) -> bool {
1191 self.flow.matches(target, flow_id, checksum_offload)
1192 }
1193
1194 fn can_coalesce(&self, coalescing_vec_len: usize, parsed: &GroPacket<'_>) -> bool {
1195 self.flow.can_coalesce(self.frame_len(coalescing_vec_len), parsed)
1196 }
1197
1198 fn coalesce(
1205 &mut self,
1206 parsed: GroPacket<'_>,
1207 coalesce_into: &mut Vec<u8>,
1208 hold_buffers: &mut Vec<B>,
1209 ) -> CoalesceResult {
1210 match core::mem::replace(&mut self.buffers, ActiveFlowBuffers::Coalesced) {
1211 ActiveFlowBuffers::Single { mut buffer, payload_end } => {
1212 coalesce_into.clear();
1213 coalesce_into.extend_from_slice(&buffer.unwrap_contiguous()[..payload_end]);
1222 hold_buffers.push(buffer);
1223 }
1224 ActiveFlowBuffers::Coalesced => {}
1225 }
1226 self.flow.coalesce(parsed, coalesce_into)
1227 }
1228}
1229
1230#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1231enum BufferLinearization {
1232 Contiguous,
1233 Linearized,
1234}
1235
1236struct PendingFlow<B, T> {
1243 buffer: B,
1244 flow: GroFlow<T>,
1245 seed_payload_end: usize,
1249 linearization: BufferLinearization,
1250}
1251
1252struct PendingFlush<B, T> {
1258 buffer: B,
1259 target: T,
1260 linearization: BufferLinearization,
1261 checksum_offload: ChecksumRxOffloading,
1262}
1263
1264enum PendingItem<B, T> {
1265 NewFlow(PendingFlow<B, T>),
1266 Flush(PendingFlush<B, T>),
1267}
1268
1269pub struct GroIter<'a, I, B, T> {
1271 iter: I,
1272 storage: &'a mut GroBufferStorage<B>,
1273 counters: &'a GroCounters,
1274 active_flow: Option<ActiveFlow<B, T>>,
1276 pending_item: Option<PendingItem<B, T>>,
1278 enable_tcp_gro: bool,
1279}
1280
1281impl<'a, I, B, T> GroIter<'a, I, B, T>
1282where
1283 B: MaybeContiguousBuffer,
1284{
1285 fn new(
1286 iter: I,
1287 storage: &'a mut GroBufferStorage<B>,
1288 counters: &'a GroCounters,
1289 enable_tcp_gro: bool,
1290 ) -> Self {
1291 counters.batches.increment();
1292 Self { iter, storage, counters, active_flow: None, pending_item: None, enable_tcp_gro }
1293 }
1294}
1295
1296impl<'a, I, B, T> Drop for GroIter<'a, I, B, T> {
1297 fn drop(&mut self) {
1298 self.storage.clear();
1299 }
1300}
1301
1302enum ProcessingResult<'a, B, T, O> {
1303 Continue,
1304 Return(GroOutputItem<'a, B, T, O>),
1305}
1306
1307impl<'a, I, B, T> GroIter<'a, I, B, T>
1308where
1309 B: MaybeContiguousBuffer,
1310 T: GroBufferDestination,
1311 I: Iterator<Item = GroInputItem<B, T>>,
1312{
1313 pub fn next<'b>(&'b mut self) -> Option<GroOutputItem<'b, B, T, alloc::vec::Drain<'b, B>>> {
1315 let counters = self.counters;
1318 let item = self.next_inner();
1319 if item.is_some() {
1320 counters.output_frames.increment();
1321 }
1322 item
1323 }
1324
1325 fn next_inner<'b>(&'b mut self) -> Option<GroOutputItem<'b, B, T, alloc::vec::Drain<'b, B>>> {
1326 loop {
1327 if let Some(i) = self.pending_item.take() {
1328 match self.process_pending(i) {
1329 ProcessingResult::Continue => continue,
1330 ProcessingResult::Return(out) => return Some(out),
1331 };
1332 }
1333 if let Some(i) = self.iter.next() {
1334 match self.process_input(i) {
1335 ProcessingResult::Continue => continue,
1336 ProcessingResult::Return(out) => return Some(out),
1337 };
1338 }
1339 if let Some(f) = self.active_flow.take() {
1340 return Some(self.storage.build_output(f));
1341 }
1342 return None;
1343 }
1344 }
1345
1346 fn process_pending<'b>(
1347 &'b mut self,
1348 pending: PendingItem<B, T>,
1349 ) -> ProcessingResult<'b, B, T, alloc::vec::Drain<'b, B>> {
1350 let Self { storage, active_flow, .. } = self;
1351 match pending {
1352 PendingItem::NewFlow(pending) => {
1353 let PendingFlow { buffer, flow, seed_payload_end, linearization } = pending;
1354 let buffers = match linearization {
1355 BufferLinearization::Contiguous => {
1359 ActiveFlowBuffers::Single { buffer, payload_end: seed_payload_end }
1360 }
1361 BufferLinearization::Linearized => {
1362 let GroBufferStorage {
1371 coalescing_vec,
1372 coalesced_buffers,
1373 linearization_vec,
1374 } = storage;
1375 coalescing_vec.clear();
1376 coalescing_vec.extend_from_slice(&linearization_vec[..seed_payload_end]);
1377 coalesced_buffers.push(buffer);
1378 ActiveFlowBuffers::Coalesced
1379 }
1380 };
1381
1382 *active_flow = Some(ActiveFlow { flow, buffers });
1383 ProcessingResult::Continue
1384 }
1385 PendingItem::Flush(pending) => {
1386 let PendingFlush { buffer, target, linearization, checksum_offload } = pending;
1387
1388 let buffers = match linearization {
1389 BufferLinearization::Contiguous => GroOutputBuffers::Contiguous(buffer),
1390 BufferLinearization::Linearized => GroOutputBuffers::Linearized {
1391 buffer,
1392 slice: &mut storage.linearization_vec,
1393 },
1394 };
1395 ProcessingResult::Return(GroOutputItem {
1396 target,
1397 checksum_offload,
1398 gso_info: None,
1399 buffers,
1400 })
1401 }
1402 }
1403 }
1404
1405 fn process_input<'b>(
1408 &'b mut self,
1409 item: GroInputItem<B, T>,
1410 ) -> ProcessingResult<'b, B, T, alloc::vec::Drain<'b, B>> {
1411 let Self { storage, counters, active_flow, pending_item, enable_tcp_gro, .. } = self;
1412 let GroInputItem { mut buffer, target, checksum_offload } = item;
1413
1414 counters.input_frames.increment();
1415 storage.linearization_vec.clear();
1416 let buffer_slice = buffer
1417 .linearized(Some(&mut storage.linearization_vec))
1418 .expect("must be `Some` if linearization vec is provided");
1419 let linearization = match &buffer_slice {
1420 BufferSlice::Contiguous(_) => BufferLinearization::Contiguous,
1421 BufferSlice::Linearized(_) => BufferLinearization::Linearized,
1422 };
1423
1424 macro_rules! return_single_buffer {
1430 ($csum_offload:expr) => {{
1431 let buffers = match buffer_slice {
1432 BufferSlice::Contiguous(_) => GroOutputBuffers::Contiguous(buffer),
1433 BufferSlice::Linearized(slice) => {
1434 GroOutputBuffers::Linearized { buffer, slice }
1435 }
1436 };
1437 return ProcessingResult::Return(GroOutputItem {
1438 target,
1439 checksum_offload: $csum_offload,
1440 gso_info: None,
1441 buffers,
1442 });
1443 }};
1444 }
1445
1446 if !*enable_tcp_gro {
1447 return_single_buffer!(checksum_offload);
1448 }
1449
1450 let mut context = NetworkParsingContext::new(checksum_offload);
1451 let parsed = GroPacket::parse(buffer_slice.as_slice(), &target, &mut context);
1452 let checksum_offload = build_csum_offload_output(checksum_offload, &context);
1456 let parsed = match parsed {
1457 Some(p) => p,
1458 None => return_single_buffer!(checksum_offload),
1459 };
1460
1461 let flush_if_not_merged = parsed.flush_if_not_merged(buffer_slice.as_slice().len());
1462 let Some(mut active) = active_flow.take() else {
1463 if flush_if_not_merged {
1467 return_single_buffer!(checksum_offload);
1468 }
1469
1470 let seed_payload_end = parsed.payload_end();
1471 let flow = GroFlow::new(target, parsed, checksum_offload);
1472 let buffers = match linearization {
1473 BufferLinearization::Contiguous => {
1477 ActiveFlowBuffers::Single { buffer, payload_end: seed_payload_end }
1478 }
1479 BufferLinearization::Linearized => {
1480 storage.coalescing_vec.clear();
1481 storage
1485 .coalescing_vec
1486 .extend_from_slice(&buffer_slice.as_slice()[..seed_payload_end]);
1487 storage.coalesced_buffers.push(buffer);
1488 ActiveFlowBuffers::Coalesced
1489 }
1490 };
1491 *active_flow = Some(ActiveFlow { flow, buffers });
1492 return ProcessingResult::Continue;
1493 };
1494
1495 if !active.matches(&target, &parsed.flow_id, checksum_offload) {
1496 if flush_if_not_merged {
1507 *active_flow = Some(active);
1508 return_single_buffer!(checksum_offload);
1509 }
1510
1511 counters.flow_evictions.increment();
1512 let seed_payload_end = parsed.payload_end();
1513 let flow = GroFlow::new(target, parsed, checksum_offload);
1514 let pending =
1515 PendingItem::NewFlow(PendingFlow { buffer, flow, seed_payload_end, linearization });
1516 *pending_item = Some(pending);
1517 return ProcessingResult::Return(storage.build_output(active));
1518 }
1519
1520 if active.can_coalesce(storage.coalescing_vec.len(), &parsed) {
1521 let coalesce_result = active.coalesce(
1522 parsed,
1523 &mut storage.coalescing_vec,
1524 &mut storage.coalesced_buffers,
1525 );
1526
1527 storage.coalesced_buffers.push(buffer);
1528
1529 match coalesce_result {
1530 CoalesceResult::Flush => ProcessingResult::Return(storage.build_output(active)),
1531 CoalesceResult::Continue => {
1532 *active_flow = Some(active);
1533 ProcessingResult::Continue
1534 }
1535 }
1536 } else {
1537 let pending = if flush_if_not_merged {
1542 PendingItem::Flush(PendingFlush { buffer, target, linearization, checksum_offload })
1543 } else {
1544 let seed_payload_end = parsed.payload_end();
1545 let flow = GroFlow::new(target, parsed, checksum_offload);
1546 PendingItem::NewFlow(PendingFlow { buffer, flow, seed_payload_end, linearization })
1547 };
1548 *pending_item = Some(pending);
1549 ProcessingResult::Return(storage.build_output(active))
1550 }
1551 }
1552}
1553
1554#[cfg(test)]
1555mod tests {
1556 use super::*;
1557 use alloc::sync::Arc;
1558 use alloc::vec;
1559 use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1560
1561 use net_declare::{net_ip_v4, net_ip_v6, net_mac};
1562 use net_types::ip::{Ipv4, Ipv6};
1563 use netstack3_base::{CounterCollection as _, NetworkSerializationContext};
1564 use packet::{Buf, InnerPacketBuilder, NestableSerializer as _, Serializer};
1565 use packet_formats::arp::{ArpOp, ArpPacketBuilder};
1566 use packet_formats::ethernet::{EtherType, EthernetFrameBuilder};
1567 use packet_formats::ip::{FragmentOffset, IpExt, IpProto, Ipv4Proto};
1568 use packet_formats::ipv4::options::Ipv4Option;
1569 use packet_formats::ipv4::{Ipv4PacketBuilder, Ipv4PacketBuilderWithOptions};
1570 use packet_formats::ipv6::ext_hdrs::{
1571 ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData,
1572 };
1573 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
1574 use packet_formats::tcp::options::{TcpOptionsBuilder, TimestampOption};
1575 use packet_formats::tcp::{TcpSegmentBuilder, TcpSegmentBuilderWithOptions};
1576 use packet_formats::udp::UdpPacketBuilder;
1577 use test_case::test_case;
1578
1579 impl GroBufferDestination for GroFrameType {
1580 fn frame_type(&self) -> GroFrameType {
1581 *self
1582 }
1583 }
1584
1585 #[derive(Debug)]
1587 struct TrackedBuffer {
1588 buf: Vec<u8>,
1589 contiguous: bool,
1590 dropped: Arc<AtomicBool>,
1591 linearized_count: Arc<AtomicUsize>,
1592 }
1593
1594 impl TrackedBuffer {
1595 fn new(buf: Vec<u8>, contiguous: bool) -> Self {
1596 Self {
1597 buf,
1598 contiguous,
1599 dropped: Arc::new(AtomicBool::new(false)),
1600 linearized_count: Arc::new(AtomicUsize::new(0)),
1601 }
1602 }
1603
1604 fn dropped(&self) -> Arc<AtomicBool> {
1606 self.dropped.clone()
1607 }
1608
1609 fn linearized_count(&self) -> Arc<AtomicUsize> {
1612 self.linearized_count.clone()
1613 }
1614 }
1615
1616 impl Drop for TrackedBuffer {
1617 fn drop(&mut self) {
1618 self.dropped.store(true, Ordering::SeqCst);
1619 }
1620 }
1621
1622 impl MaybeContiguousBuffer for TrackedBuffer {
1623 fn linearized<'a, 'b>(
1624 &'a mut self,
1625 storage: Option<&'b mut Vec<u8>>,
1626 ) -> Option<BufferSlice<'a, 'b>> {
1627 if self.contiguous {
1628 Some(BufferSlice::Contiguous(&mut self.buf[..]))
1629 } else {
1630 let storage = storage?;
1631 let _: usize = self.linearized_count.fetch_add(1, Ordering::SeqCst);
1632 let frame_length = self.buf.len();
1633 if storage.len() < frame_length {
1634 storage.resize(frame_length, 0);
1635 }
1636 let slice = &mut storage[..frame_length];
1637 slice.copy_from_slice(&self.buf);
1638 Some(BufferSlice::Linearized(slice))
1639 }
1640 }
1641 }
1642
1643 fn input_item(buffer: TrackedBuffer) -> GroInputItem<TrackedBuffer, GroFrameType> {
1645 GroInputItem {
1646 buffer,
1647 target: GroFrameType::Ethernet,
1648 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
1649 }
1650 }
1651
1652 #[test]
1653 fn process_gro_handles_fragmented() {
1654 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![
1655 GroInputItem {
1656 buffer: TrackedBuffer::new(vec![1, 2, 3], true),
1657 target: GroFrameType::Ethernet,
1658 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
1659 },
1660 GroInputItem {
1661 buffer: TrackedBuffer::new(vec![4, 5, 6], false),
1662 target: GroFrameType::Ethernet,
1663 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
1664 },
1665 ];
1666
1667 let counters = GroCounters::default();
1668 let mut storage = GroBufferStorage::new();
1669 let mut output = Vec::new();
1670 let mut gro = storage.coalesce(items.into_iter(), false, &counters);
1671 while let Some(mut item) = gro.next() {
1672 output.push(item.buffers.slice_mut().to_vec());
1673 }
1674
1675 assert_eq!(output, vec![vec![1, 2, 3], vec![4, 5, 6]]);
1676 }
1677
1678 const SRC_MAC: Mac = net_mac!("00:11:22:33:44:55");
1679 const DST_MAC: Mac = net_mac!("66:77:88:99:aa:bb");
1680 const SRC_IP_V4: Ipv4Addr = net_ip_v4!("192.168.0.1");
1681 const DST_IP_V4: Ipv4Addr = net_ip_v4!("192.168.0.2");
1682 const SRC_IP_V6: Ipv6Addr = net_ip_v6!("2001:db8::1");
1683 const DST_IP_V6: Ipv6Addr = net_ip_v6!("2001:db8::2");
1684
1685 #[derive(Clone, Debug)]
1690 struct FrameSpec {
1691 ip: IpSpec,
1693 seq: u32,
1694 ack: u32,
1695 win: u16,
1696 psh: bool,
1697 fin: bool,
1698 syn: bool,
1699 rst: bool,
1700 urg: bool,
1701 ttl: u8,
1703 dscp_and_ecn: DscpAndEcn,
1704 timestamp: Option<u32>,
1706 min_body_len: usize,
1708 payload: &'static [u8],
1709 contiguous: bool,
1711 }
1712
1713 #[derive(Clone, Copy, Debug)]
1715 enum IpSpec {
1716 V4 { df: bool, id: u16 },
1717 V6 { flowlabel: u32 },
1718 }
1719
1720 fn v4(seq: u32, payload: &'static [u8]) -> FrameSpec {
1722 FrameSpec {
1723 ip: IpSpec::V4 { df: false, id: 0 },
1724 seq,
1725 ack: 1000,
1726 win: 64240,
1727 psh: false,
1728 fin: false,
1729 syn: false,
1730 rst: false,
1731 urg: false,
1732 ttl: 64,
1733 dscp_and_ecn: DscpAndEcn::default(),
1734 timestamp: None,
1735 min_body_len: 0,
1736 payload,
1737 contiguous: true,
1738 }
1739 }
1740
1741 fn v6(seq: u32, payload: &'static [u8]) -> FrameSpec {
1743 FrameSpec { ip: IpSpec::V6 { flowlabel: 0 }, ..v4(seq, payload) }
1744 }
1745
1746 impl FrameSpec {
1747 fn build(&self) -> Vec<u8> {
1749 let FrameSpec {
1750 ip: ip_spec,
1751 seq,
1752 ack,
1753 win,
1754 psh,
1755 fin,
1756 syn,
1757 rst,
1758 urg,
1759 ttl,
1760 dscp_and_ecn,
1761 timestamp,
1762 min_body_len,
1763 payload,
1764 contiguous: _,
1765 } = *self;
1766
1767 let mut body = payload.to_vec();
1768
1769 macro_rules! tcp_builder {
1770 ($src:expr, $dst:expr) => {{
1771 let mut tcp = TcpSegmentBuilder::new(
1772 $src,
1773 $dst,
1774 TEST_SRC_PORT,
1775 TEST_DST_PORT,
1776 seq,
1777 Some(ack),
1778 win,
1779 );
1780 tcp.psh(psh);
1781 tcp.fin(fin);
1782 tcp.syn(syn);
1783 tcp.rst(rst);
1784 tcp.urg(urg);
1785 tcp
1786 }};
1787 }
1788
1789 macro_rules! serialize {
1790 ($tcp:expr, $ip:expr, $ethertype:expr) => {
1791 Buf::new(&mut body[..], ..)
1792 .wrap_in($tcp)
1793 .wrap_in($ip)
1794 .wrap_in(EthernetFrameBuilder::new(
1795 SRC_MAC,
1796 DST_MAC,
1797 $ethertype,
1798 min_body_len,
1799 ))
1800 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1801 .unwrap()
1802 .unwrap_b()
1803 .as_ref()
1804 .to_vec()
1805 };
1806 }
1807
1808 macro_rules! serialize_with_options {
1811 ($tcp:expr, $ip:expr, $ethertype:expr) => {
1812 match timestamp {
1813 None => serialize!($tcp, $ip, $ethertype),
1814 Some(ts_val) => {
1815 let options = TcpOptionsBuilder {
1816 timestamp: Some(TimestampOption::new(ts_val, 0)),
1817 ..Default::default()
1818 };
1819 let tcp = TcpSegmentBuilderWithOptions::new($tcp, options).unwrap();
1820 serialize!(tcp, $ip, $ethertype)
1821 }
1822 }
1823 };
1824 }
1825
1826 match ip_spec {
1827 IpSpec::V4 { df, id } => {
1828 let mut ip =
1829 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, ttl, IpProto::Tcp.into());
1830 ip.dscp_and_ecn(dscp_and_ecn);
1831 ip.df_flag(df);
1832 ip.id(id);
1833 serialize_with_options!(tcp_builder!(SRC_IP_V4, DST_IP_V4), ip, EtherType::Ipv4)
1834 }
1835 IpSpec::V6 { flowlabel } => {
1836 let mut ip =
1837 Ipv6PacketBuilder::new(SRC_IP_V6, DST_IP_V6, ttl, IpProto::Tcp.into());
1838 ip.dscp_and_ecn(dscp_and_ecn);
1839 ip.flowlabel(flowlabel);
1840 serialize_with_options!(tcp_builder!(SRC_IP_V6, DST_IP_V6), ip, EtherType::Ipv6)
1841 }
1842 }
1843 }
1844 }
1845
1846 fn run_gro(frames: &[FrameSpec], enable_tcp_gro: bool) -> Vec<Vec<u8>> {
1848 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = frames
1849 .iter()
1850 .map(|spec| input_item(TrackedBuffer::new(spec.build(), spec.contiguous)))
1851 .collect();
1852
1853 let counters = GroCounters::default();
1854 let mut storage = GroBufferStorage::new();
1855 let mut output = Vec::new();
1856 let mut gro = storage.coalesce(items.into_iter(), enable_tcp_gro, &counters);
1857 while let Some(mut item) = gro.next() {
1858 output.push(item.buffers.slice_mut().to_vec());
1859 }
1860 output
1861 }
1862
1863 #[test]
1864 fn gro_counters() {
1865 let frames = [
1866 v4(100, b"a1"),
1868 v4(102, b"a2"),
1870 v6(200, b"b1"),
1872 v4(104, b""),
1875 v6(202, b"b2"),
1877 v4(104, b"a3"),
1879 ];
1880 let to_items = |frames: &[FrameSpec]| -> Vec<GroInputItem<TrackedBuffer, GroFrameType>> {
1881 frames
1882 .iter()
1883 .map(|spec| input_item(TrackedBuffer::new(spec.build(), spec.contiguous)))
1884 .collect()
1885 };
1886
1887 let counters = GroCounters::default();
1888 let mut storage = GroBufferStorage::new();
1889 for batch in [to_items(&frames), to_items(&[v4(106, b"a4")])] {
1891 let mut gro = storage.coalesce(batch.into_iter(), true, &counters);
1892 while let Some(_item) = gro.next() {}
1893 }
1894
1895 assert_eq!(
1896 counters.cast::<u64>(),
1897 GroCounters { batches: 2, input_frames: 7, output_frames: 5, flow_evictions: 2 }
1898 );
1899 }
1900
1901 #[track_caller]
1903 fn parse_frame(frame: &[u8]) -> GroPacket<'_> {
1904 GroPacket::parse(
1905 frame,
1906 &GroFrameType::Ethernet,
1907 &mut NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(None)),
1908 )
1909 .expect("frame is a valid GRO packet")
1910 }
1911
1912 #[test_case(
1913 vec![v4(100, b"hello "), v4(106, b"world")],
1914 vec![b"hello world"]; "coalesces_contiguous")]
1915 #[test_case(
1916 vec![
1917 FrameSpec { contiguous: false, ..v4(100, b"foo ") },
1918 FrameSpec { contiguous: false, ..v4(104, b"bar") },
1919 ],
1920 vec![b"foo bar"]; "coalesces_fragmented")]
1921 #[test_case(
1922 vec![v4(100, b"contig "), FrameSpec { contiguous: false, ..v4(107, b"frag") }],
1923 vec![b"contig frag"]; "coalesces_contiguous_then_fragmented")]
1924 #[test_case(
1925 vec![FrameSpec { contiguous: false, ..v4(100, b"single") }],
1926 vec![b"single"]; "lone_fragmented_frame_is_emitted")]
1927 #[test_case(
1928 vec![v4(100, b"first "), v4(106, b"second"), v4(112, b"third")],
1929 vec![b"first secondthird"]; "coalesces_three_frames")]
1930 #[test_case(
1931 vec![v6(200, b"ipv6_1"), FrameSpec { psh: true, ..v6(206, b"ipv6_2") }],
1932 vec![b"ipv6_1ipv6_2"]; "coalesces_ipv6")]
1933 #[test_case(
1934 vec![
1935 FrameSpec { ack: 1000, ..v4(100, b"ack1000 ") },
1936 FrameSpec { ack: 2000, ..v4(108, b"ack2000") },
1937 ],
1938 vec![b"ack1000 ", b"ack2000"]; "ack_mismatch_flushes")]
1939 #[test_case(
1940 vec![
1941 FrameSpec { win: 64240, ..v4(100, b"win64k ") },
1942 FrameSpec { win: 32120, ..v4(107, b"win32k") },
1943 ],
1944 vec![b"win64k ", b"win32k"]; "window_mismatch_flushes")]
1945 #[test_case(
1946 vec![
1947 FrameSpec { timestamp: Some(1), ..v4(100, b"opt1") },
1948 FrameSpec { timestamp: Some(1), ..v4(104, b"opt2") },
1949 ],
1950 vec![b"opt1opt2"]; "matching_options_coalesce")]
1951 #[test_case(
1952 vec![
1953 FrameSpec { timestamp: Some(1), ..v4(100, b"opt1") },
1954 FrameSpec { timestamp: Some(2), ..v4(104, b"mismatch") },
1955 ],
1956 vec![b"opt1", b"mismatch"]; "options_mismatch_flushes")]
1957 #[test_case(
1958 vec![
1959 FrameSpec { ttl: 64, ..v4(100, b"ttl64 ") },
1960 FrameSpec { ttl: 63, ..v4(106, b"ttl63") },
1961 ],
1962 vec![b"ttl64 ", b"ttl63"]; "ttl_mismatch_flushes")]
1963 #[test_case(
1964 vec![
1965 FrameSpec { ip: IpSpec::V4 { df: true, id: 0 }, ..v4(100, b"df1 ") },
1966 FrameSpec { ip: IpSpec::V4 { df: false, id: 0 }, ..v4(104, b"df0") },
1967 ],
1968 vec![b"df1 ", b"df0"]; "df_flag_mismatch_flushes")]
1969 #[test_case(
1970 vec![
1971 FrameSpec { dscp_and_ecn: DscpAndEcn::new(1, 0), ..v4(100, b"ecn1 ") },
1972 FrameSpec { dscp_and_ecn: DscpAndEcn::new(2, 0), ..v4(105, b"ecn2") },
1973 ],
1974 vec![b"ecn1 ", b"ecn2"]; "dscp_ecn_mismatch_flushes")]
1975 #[test_case(
1976 vec![
1977 FrameSpec { ttl: 64, ..v6(200, b"hop64 ") },
1978 FrameSpec { ttl: 63, ..v6(206, b"hop63") },
1979 ],
1980 vec![b"hop64 ", b"hop63"]; "hop_limit_mismatch_flushes")]
1981 #[test_case(
1982 vec![
1983 FrameSpec { ip: IpSpec::V6 { flowlabel: 123 }, ..v6(200, b"lbl1 ") },
1984 FrameSpec { ip: IpSpec::V6 { flowlabel: 456 }, ..v6(205, b"lbl2") },
1985 ],
1986 vec![b"lbl1 ", b"lbl2"]; "flowlabel_mismatch_flushes")]
1987 #[test_case(
1988 vec![v4(100, b"seq100 "), v4(200, b"seq200")],
1989 vec![b"seq100 ", b"seq200"]; "out_of_order_seq_flushes")]
1990 #[test_case(
1991 vec![v4(100, b"seq100 "), v4(200, b"seq200"), v4(206, b"seq206")],
1992 vec![b"seq100 ", b"seq200seq206"]; "merge_failure_starts_new_flow")]
1993 #[test_case(
1994 vec![
1995 FrameSpec { urg: true, ..v4(100, b"urg ") },
1996 FrameSpec { urg: false, ..v4(104, b"normal") },
1997 ],
1998 vec![b"urg ", b"normal"]; "urg_flag_flushes")]
1999 #[test_case(
2000 vec![
2001 FrameSpec { urg: true, ..v4(100, b"urg1") },
2002 FrameSpec { urg: true, ..v4(104, b"urg2") },
2003 ],
2004 vec![b"urg1", b"urg2"]; "consecutive_urg_flags_flush")]
2005 #[test_case(
2006 vec![
2007 FrameSpec { syn: true, ..v4(100, b"syn1") },
2008 FrameSpec { syn: true, ..v4(104, b"syn2") },
2009 ],
2010 vec![b"syn1", b"syn2"]; "syn_flag_flushes")]
2011 #[test_case(
2012 vec![
2013 FrameSpec { rst: true, ..v4(100, b"rst1") },
2014 FrameSpec { rst: true, ..v4(104, b"rst2") },
2015 ],
2016 vec![b"rst1", b"rst2"]; "rst_flag_flushes")]
2017 #[test_case(
2018 vec![
2019 FrameSpec { ip: IpSpec::V4 { df: false, id: 42 }, ..v4(100, b"hello ") },
2020 FrameSpec { ip: IpSpec::V4 { df: false, id: 42 }, ..v4(106, b"world ") },
2021 FrameSpec { ip: IpSpec::V4 { df: false, id: 42 }, ..v4(112, b"again") },
2022 ],
2023 vec![b"hello world again"]; "consistent_ipv4_ids_coalesce")]
2024 #[test_case(
2025 vec![
2026 FrameSpec { ip: IpSpec::V4 { df: false, id: 100 }, ..v4(100, b"hello ") },
2027 FrameSpec { ip: IpSpec::V4 { df: false, id: 101 }, ..v4(106, b"world ") },
2028 FrameSpec { ip: IpSpec::V4 { df: false, id: 102 }, ..v4(112, b"again") },
2029 ],
2030 vec![b"hello world again"]; "increasing_ipv4_ids_coalesce")]
2031 #[test_case(
2032 vec![
2033 FrameSpec { ip: IpSpec::V4 { df: false, id: u16::MAX }, ..v4(100, b"hello ") },
2034 FrameSpec { ip: IpSpec::V4 { df: false, id: 0 }, ..v4(106, b"world") },
2035 ],
2036 vec![b"hello world"]; "wrapping_ipv4_ids_coalesce")]
2037 #[test_case(
2038 vec![
2039 FrameSpec { ip: IpSpec::V4 { df: false, id: 100 }, ..v4(100, b"hello ") },
2040 FrameSpec { ip: IpSpec::V4 { df: false, id: 105 }, ..v4(106, b"world") },
2041 ],
2042 vec![b"hello ", b"world"]; "ipv4_id_mismatch_flushes")]
2043 #[test_case(
2044 vec![
2045 FrameSpec { ip: IpSpec::V4 { df: false, id: 100 }, ..v4(100, b"first ") },
2046 FrameSpec { ip: IpSpec::V4 { df: false, id: 100 }, ..v4(106, b"second") },
2047 FrameSpec { ip: IpSpec::V4 { df: false, id: 101 }, ..v4(112, b"third") },
2048 ],
2049 vec![b"first second", b"third"]; "ipv4_id_mode_switch_flushes")]
2050 #[test_case(
2051 vec![FrameSpec { min_body_len: 46, ..v4(100, b"abcd") }, v4(104, b"e")],
2052 vec![b"abcde"]; "padded_seed_frame_is_trimmed")]
2053 #[test_case(
2054 vec![
2055 FrameSpec { min_body_len: 46, contiguous: false, ..v4(100, b"abcd") },
2056 v4(104, b"e"),
2057 ],
2058 vec![b"abcde"]; "linearized_padded_seed_frame_is_trimmed")]
2059 #[test_case(
2060 vec![FrameSpec { min_body_len: 46, ..v4(100, b"a") }, v4(101, b"normal")],
2061 vec![b"a", b"normal"]; "unmerged_padded_frame_is_emitted_verbatim")]
2062 #[test_case(
2063 vec![v4(100, b"first "), FrameSpec { min_body_len: 46, ..v4(106, b"b") }],
2064 vec![b"first b"]; "padded_second_frame_is_trimmed")]
2065 #[test_case(
2066 vec![FrameSpec { min_body_len: 70, ..v6(200, b"c") }, v6(201, b"ipv6_normal")],
2067 vec![b"c", b"ipv6_normal"]; "unmerged_padded_ipv6_frame_is_emitted_verbatim")]
2068 #[test_case(
2069 vec![v4(100, b"short"), v4(105, b"longer_payload")],
2070 vec![b"short", b"longer_payload"]; "payload_larger_than_gso_size_flushes")]
2071 #[test_case(
2072 vec![v4(100, b"data"), v4(104, b"")],
2073 vec![b"data", b""]; "empty_payload_in_active_flow_flushes")]
2074 #[test_case(
2075 vec![v4(100, b"odd"), v4(103, b"len"), v4(106, b"tcp")],
2076 vec![b"oddlentcp"]; "coalesces_odd_length_payloads")]
2077 #[test_case(
2078 vec![
2079 FrameSpec { timestamp: Some(1), ..v4(100, b"odd") },
2080 FrameSpec { timestamp: Some(1), ..v4(103, b"opt") },
2081 ],
2082 vec![b"oddopt"]; "coalesces_odd_length_payloads_with_options")]
2083 #[test_case(
2084 vec![v4(100, b"first "), v4(106, b"sub"), v4(109, b"more")],
2085 vec![b"first sub", b"more"]; "payload_smaller_than_gso_size_terminates_flow")]
2086 fn gro_coalescing(frames: Vec<FrameSpec>, expected: Vec<&'static [u8]>) {
2087 let output = run_gro(&frames, true);
2088 assert_eq!(output.len(), expected.len(), "wrong number of frames emitted");
2089
2090 for (index, (frame, payload)) in output.iter().zip(expected.iter()).enumerate() {
2091 let parsed = parse_frame(&frame[..]);
2092 let TransportPacket::Tcp(tcp) = parsed.transport;
2093 assert_eq!(tcp.body(), *payload, "frame {index}");
2094 }
2095 }
2096
2097 #[test]
2098 fn gro_disabled_emits_every_frame_unchanged() {
2099 let frames = vec![v4(100, b"hello "), v4(106, b"world")];
2100 let output = run_gro(&frames, false);
2101 assert_eq!(output, vec![frames[0].build(), frames[1].build()]);
2102 }
2103
2104 #[test_case(true; "contiguous")]
2105 #[test_case(false; "fragmented")]
2106 fn gro_emits_unmergeable_frames_unmodified(contiguous: bool) {
2107 let frames = vec![
2110 FrameSpec { urg: true, contiguous, ..v4(100, b"hello ") },
2111 FrameSpec { urg: true, contiguous, ..v4(106, b"world") },
2112 ];
2113 let output = run_gro(&frames, true);
2114 assert_eq!(output.len(), frames.len(), "wrong number of frames emitted");
2115
2116 for (index, (frame, spec)) in output.iter().zip(frames.iter()).enumerate() {
2117 let expected = spec.build();
2118 let parsed = parse_frame(&expected);
2119 let packet_end = parsed.payload_end();
2122 assert!(frame.len() >= packet_end, "frame {index} is truncated");
2123 assert_eq!(&frame[..packet_end], &expected[..packet_end], "frame {index}");
2124 }
2125 }
2126
2127 #[test]
2128 fn gro_merges_psh_into_coalesced_frame() {
2129 let frames = vec![v4(100, b"hello "), FrameSpec { psh: true, ..v4(106, b"world") }];
2130 let output = run_gro(&frames, true);
2131 let output = assert_matches!(&output[..], [output] => output);
2132
2133 let parsed = parse_frame(output);
2134 let TransportPacket::Tcp(tcp) = parsed.transport;
2135 assert_eq!(tcp.body(), b"hello world");
2136 assert_eq!(tcp.seq_num(), 100);
2137 assert_eq!(tcp.ack_num(), Some(1000));
2138 assert_eq!(tcp.window_size(), 64240);
2139 assert!(tcp.psh());
2140 }
2141
2142 #[test]
2143 fn gro_merges_fin_into_coalesced_frame() {
2144 let frames = vec![v4(100, b"data "), FrameSpec { fin: true, ..v4(105, b"fin") }];
2145 let output = run_gro(&frames, true);
2146 let output = assert_matches!(&output[..], [output] => output);
2147
2148 let parsed = parse_frame(output);
2149 let TransportPacket::Tcp(tcp) = parsed.transport;
2150 assert_eq!(tcp.body(), b"data fin");
2151 assert!(tcp.fin());
2152 }
2153
2154 #[test]
2155 fn gro_gso_info_metadata() {
2156 let counters = GroCounters::default();
2157 let mut storage = GroBufferStorage::new();
2158
2159 let items = vec![
2161 input_item(TrackedBuffer::new(
2162 FrameSpec { ip: IpSpec::V4 { df: false, id: 1 }, ..v4(100, b"first ") }.build(),
2163 true,
2164 )),
2165 input_item(TrackedBuffer::new(
2166 FrameSpec { ip: IpSpec::V4 { df: false, id: 1 }, ..v4(106, b"second") }.build(),
2167 true,
2168 )),
2169 ];
2170 let mut gro = storage.coalesce(items.into_iter(), true, &counters);
2171 let item = gro.next().unwrap();
2172 assert_eq!(
2173 item.gso_info,
2174 Some(GsoInfo {
2175 gso_size: NonZeroU16::new(6).unwrap(),
2176 gso_segs: NonZeroU16::new(2).unwrap(),
2177 ipv4_id_mode: Some(Ipv4IdMode::Fixed),
2178 })
2179 );
2180 drop(item);
2181 assert!(gro.next().is_none());
2182 drop(gro);
2183
2184 let items = vec![
2186 input_item(TrackedBuffer::new(
2187 FrameSpec { ip: IpSpec::V4 { df: false, id: 1 }, ..v4(100, b"first ") }.build(),
2188 true,
2189 )),
2190 input_item(TrackedBuffer::new(
2191 FrameSpec { ip: IpSpec::V4 { df: false, id: 2 }, ..v4(106, b"second") }.build(),
2192 true,
2193 )),
2194 ];
2195 let mut gro = storage.coalesce(items.into_iter(), true, &counters);
2196 let item = gro.next().unwrap();
2197 assert_eq!(
2198 item.gso_info,
2199 Some(GsoInfo {
2200 gso_size: NonZeroU16::new(6).unwrap(),
2201 gso_segs: NonZeroU16::new(2).unwrap(),
2202 ipv4_id_mode: Some(Ipv4IdMode::Incrementing),
2203 })
2204 );
2205 drop(item);
2206 assert!(gro.next().is_none());
2207 drop(gro);
2208
2209 let items = vec![
2211 input_item(TrackedBuffer::new(v6(100, b"first ").build(), true)),
2212 input_item(TrackedBuffer::new(v6(106, b"second").build(), true)),
2213 ];
2214 let mut gro = storage.coalesce(items.into_iter(), true, &counters);
2215 let item = gro.next().unwrap();
2216 assert_eq!(
2217 item.gso_info,
2218 Some(GsoInfo {
2219 gso_size: NonZeroU16::new(6).unwrap(),
2220 gso_segs: NonZeroU16::new(2).unwrap(),
2221 ipv4_id_mode: None
2222 })
2223 );
2224 drop(item);
2225 assert!(gro.next().is_none());
2226 }
2227
2228 #[test]
2229 fn gro_buffers_not_dropped_until_frame_processed() {
2230 let pkt1 = v4(100, b"hello ").build();
2231 let pkt2 = v4(106, b"world").build();
2232
2233 let buffer1 = TrackedBuffer::new(pkt1, true);
2234 let buffer2 = TrackedBuffer::new(pkt2, true);
2235 let dropped1 = buffer1.dropped();
2236 let dropped2 = buffer2.dropped();
2237 let items = vec![input_item(buffer1), input_item(buffer2)];
2238
2239 let counters = GroCounters::default();
2240 let mut gro_state = GroBufferStorage::new();
2241 let mut gro = gro_state.coalesce(items.into_iter(), true, &counters);
2242
2243 let item = gro.next();
2244 assert!(item.is_some());
2245 let mut item = item.unwrap();
2246 assert!(item.buffers.slice_mut().ends_with(b"hello world"));
2247
2248 assert!(!dropped1.load(Ordering::SeqCst));
2250 assert!(!dropped2.load(Ordering::SeqCst));
2251
2252 drop(item);
2254 assert!(dropped1.load(Ordering::SeqCst));
2255 assert!(dropped2.load(Ordering::SeqCst));
2256
2257 let next_item = gro.next();
2260 assert!(next_item.is_none());
2261 }
2262
2263 #[test]
2264 fn gro_unmerged_flows_are_not_copied() {
2265 let first = v4(100, b"one").build();
2268 let second = v4(500, b"two").build();
2271 let items = vec![
2272 input_item(TrackedBuffer::new(first.clone(), true)),
2273 input_item(TrackedBuffer::new(second.clone(), true)),
2274 ];
2275
2276 let counters = GroCounters::default();
2277 let mut gro_state = GroBufferStorage::new();
2278 let mut gro = gro_state.coalesce(items.into_iter(), true, &counters);
2279
2280 for expected in [first, second] {
2281 let mut item = gro.next().expect("emits a frame");
2282 assert_matches!(&mut item.buffers, GroOutputBuffers::Contiguous(buffer) => {
2283 assert_eq!(buffer.unwrap_contiguous(), &expected[..]);
2284 });
2285 }
2286 assert!(gro.next().is_none());
2287 }
2288
2289 #[test]
2290 fn gro_iter_drop_releases_held_buffers() {
2291 let pkt1 = v4(100, b"hello ").build();
2292 let buffer1 = TrackedBuffer::new(pkt1, true);
2293 let dropped1 = buffer1.dropped();
2294 let items = vec![input_item(buffer1)];
2295
2296 let counters = GroCounters::default();
2297 let mut gro_state = GroBufferStorage::new();
2298 {
2299 let mut gro = gro_state.coalesce(items.into_iter(), true, &counters);
2300 let item = gro.next();
2301 assert!(item.is_some());
2302 assert!(!dropped1.load(Ordering::SeqCst));
2303 }
2304 assert!(dropped1.load(Ordering::SeqCst));
2306 }
2307
2308 #[test]
2309 fn gro_interleaved_non_gro_packet() {
2310 let pkt1 = v4(100, b"tcp1 ").build();
2311 let non_gro_pkt = vec![0u8; 30];
2312 let pkt2 = v4(105, b"tcp2").build();
2313
2314 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![
2315 GroInputItem {
2316 buffer: TrackedBuffer::new(pkt1, true),
2317 target: GroFrameType::Ethernet,
2318 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
2319 },
2320 GroInputItem {
2321 buffer: TrackedBuffer::new(non_gro_pkt.clone(), true),
2322 target: GroFrameType::Ethernet,
2323 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
2324 },
2325 GroInputItem {
2326 buffer: TrackedBuffer::new(pkt2, true),
2327 target: GroFrameType::Ethernet,
2328 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
2329 },
2330 ];
2331
2332 let counters = GroCounters::default();
2333 let mut gro_state = GroBufferStorage::new();
2334 let mut output_frames = Vec::new();
2335 let mut gro = gro_state.coalesce(items.into_iter(), true, &counters);
2336 while let Some(mut item) = gro.next() {
2337 output_frames.push(item.buffers.slice_mut().to_vec());
2338 }
2339
2340 assert_eq!(output_frames.len(), 2);
2342 assert_eq!(output_frames[0], non_gro_pkt);
2343 assert!(output_frames[1].ends_with(b"tcp1 tcp2"));
2344 }
2345
2346 #[test]
2347 fn gro_buffers_linearized_only_once() {
2348 let buffer1 = TrackedBuffer::new(v4(100, b"first ").build(), false);
2349 let buffer2 = TrackedBuffer::new(v4(106, b"second").build(), false);
2350 let buffer3 = TrackedBuffer::new(v4(112, b"third").build(), false);
2351 let count1 = buffer1.linearized_count();
2352 let count2 = buffer2.linearized_count();
2353 let count3 = buffer3.linearized_count();
2354 let items = vec![input_item(buffer1), input_item(buffer2), input_item(buffer3)];
2355
2356 let counters = GroCounters::default();
2357 let mut gro_state = GroBufferStorage::new();
2358 let mut output_frames = Vec::new();
2359 let mut gro = gro_state.coalesce(items.into_iter(), true, &counters);
2360 while let Some(mut item) = gro.next() {
2361 output_frames.push(item.buffers.slice_mut().to_vec());
2362 }
2363
2364 assert_eq!(output_frames.len(), 1);
2365 assert!(output_frames[0].ends_with(b"first secondthird"));
2366
2367 assert_eq!(count1.load(Ordering::SeqCst), 1);
2368 assert_eq!(count2.load(Ordering::SeqCst), 1);
2369 assert_eq!(count3.load(Ordering::SeqCst), 1);
2370 }
2371
2372 #[test]
2373 fn gro_transport_offset_ignores_padding() {
2374 let pkt_padded = FrameSpec { min_body_len: 46, ..v4(100, b"a") }.build();
2378 let pkt = v4(100, b"a").build();
2379 assert!(pkt_padded.len() > pkt.len(), "packet should have been padded");
2380
2381 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2382 let parsed = GroPacket::parse(&pkt_padded, &GroFrameType::Ethernet, &mut context)
2383 .expect("should parse padded packet");
2384 assert_eq!(parsed.offsets.ip_offset + parsed.ip.total_len(), pkt.len());
2387 let padded_offset = parsed.offsets.transport_offset;
2388
2389 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2390 let parsed = GroPacket::parse(&pkt, &GroFrameType::Ethernet, &mut context)
2391 .expect("should parse unpadded packet");
2392 assert_eq!(parsed.offsets.ip_offset + parsed.ip.total_len(), pkt.len());
2393 assert_eq!(padded_offset, parsed.offsets.transport_offset);
2394
2395 assert_eq!(&pkt_padded[padded_offset..padded_offset + 2], &1234u16.to_be_bytes());
2397 assert_eq!(&pkt_padded[padded_offset + 2..padded_offset + 4], &5678u16.to_be_bytes());
2398 }
2399
2400 #[test]
2401 fn gro_buffers_dropped_when_item_dropped() {
2402 let buffer1 = TrackedBuffer::new(vec![1, 2, 3], true);
2403 let buffer2 = TrackedBuffer::new(vec![4, 5, 6], false);
2404 let dropped1 = buffer1.dropped();
2405 let dropped2 = buffer2.dropped();
2406 let items = vec![input_item(buffer1), input_item(buffer2)];
2407
2408 let counters = GroCounters::default();
2409 let mut storage = GroBufferStorage::new();
2410 let mut gro = storage.coalesce(items.into_iter(), false, &counters);
2411
2412 let mut item1 = gro.next().unwrap();
2413 assert_eq!(item1.buffers.slice_mut(), &[1, 2, 3]);
2414 assert!(!dropped1.load(Ordering::SeqCst));
2415 assert!(!dropped2.load(Ordering::SeqCst));
2416 drop(item1);
2417 assert!(dropped1.load(Ordering::SeqCst));
2418 assert!(!dropped2.load(Ordering::SeqCst));
2419
2420 let mut item2 = gro.next().unwrap();
2421 assert_eq!(item2.buffers.slice_mut(), &[4, 5, 6]);
2422 assert!(dropped1.load(Ordering::SeqCst));
2423 assert!(!dropped2.load(Ordering::SeqCst));
2424 drop(item2);
2425 assert!(dropped2.load(Ordering::SeqCst));
2426 }
2427
2428 const TEST_SRC_PORT: NonZeroU16 = NonZeroU16::new(1234).unwrap();
2429 const TEST_DST_PORT: NonZeroU16 = NonZeroU16::new(5678).unwrap();
2430 const TEST_FLOWLABEL: u32 = 0x12345;
2431 const TEST_PAYLOAD: [u8; 12] = *b"hello world!";
2432
2433 trait TestIpExt: IpExt {
2434 const SRC_IP: Self::Addr;
2435 const DST_IP: Self::Addr;
2436 fn ip_builder(proto: IpProto) -> Self::PacketBuilder<NetworkSerializationContext>;
2437 }
2438
2439 impl TestIpExt for Ipv4 {
2440 const SRC_IP: Ipv4Addr = SRC_IP_V4;
2441 const DST_IP: Ipv4Addr = DST_IP_V4;
2442 fn ip_builder(proto: IpProto) -> Ipv4PacketBuilder {
2443 Ipv4PacketBuilder::new(Self::SRC_IP, Self::DST_IP, 64, Ipv4Proto::Proto(proto))
2444 }
2445 }
2446
2447 impl TestIpExt for Ipv6 {
2448 const SRC_IP: Ipv6Addr = SRC_IP_V6;
2449 const DST_IP: Ipv6Addr = DST_IP_V6;
2450 fn ip_builder(proto: IpProto) -> Ipv6PacketBuilder {
2451 let mut ip = Ipv6PacketBuilder::new(Self::SRC_IP, Self::DST_IP, 64, proto.into());
2452 ip.flowlabel(TEST_FLOWLABEL);
2453 ip
2454 }
2455 }
2456
2457 fn ethernet_builder<I: TestIpExt>() -> EthernetFrameBuilder {
2458 EthernetFrameBuilder::new(SRC_MAC, DST_MAC, I::ETHER_TYPE, 0)
2459 }
2460
2461 fn tcp_builder<I: TestIpExt>() -> TcpSegmentBuilder<I::Addr> {
2462 TcpSegmentBuilder::new(
2463 I::SRC_IP,
2464 I::DST_IP,
2465 TEST_SRC_PORT,
2466 TEST_DST_PORT,
2467 100,
2468 Some(200),
2469 1024,
2470 )
2471 }
2472
2473 fn build_ethernet_tcp_packet<I: TestIpExt>() -> Vec<u8> {
2474 Buf::new(TEST_PAYLOAD.to_vec(), ..)
2475 .wrap_in(tcp_builder::<I>())
2476 .wrap_in(I::ip_builder(IpProto::Tcp))
2477 .wrap_in(ethernet_builder::<I>())
2478 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2479 .unwrap()
2480 .into_inner()
2481 .as_ref()
2482 .to_vec()
2483 }
2484
2485 fn build_pure_ip_tcp_packet<I: TestIpExt>() -> Vec<u8> {
2486 Buf::new(TEST_PAYLOAD.to_vec(), ..)
2487 .wrap_in(tcp_builder::<I>())
2488 .wrap_in(I::ip_builder(IpProto::Tcp))
2489 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2490 .unwrap()
2491 .into_inner()
2492 .as_ref()
2493 .to_vec()
2494 }
2495
2496 #[test_case(
2497 build_ethernet_tcp_packet::<Ipv4>(),
2498 GroFrameType::Ethernet,
2499 HeaderOffsets { ip_offset: 14, transport_offset: 34 },
2500 GroFlowId {
2501 link_layer: LinkLayerFlowId::Ethernet(EthernetFlowId {
2502 src_mac: SRC_MAC,
2503 dst_mac: DST_MAC,
2504 tag: None,
2505 }),
2506 ip: IpFlowId::Ipv4(Ipv4FlowId { src_ip: Ipv4::SRC_IP, dst_ip: Ipv4::DST_IP }),
2507 transport: TransportFlowId::Tcp(TcpFlowId {
2508 src_port: TEST_SRC_PORT,
2509 dst_port: TEST_DST_PORT,
2510 }),
2511 };
2512 "ethernet_ipv4_tcp"
2513 )]
2514 #[test_case(
2515 build_ethernet_tcp_packet::<Ipv6>(),
2516 GroFrameType::Ethernet,
2517 HeaderOffsets { ip_offset: 14, transport_offset: 54 },
2518 GroFlowId {
2519 link_layer: LinkLayerFlowId::Ethernet(EthernetFlowId {
2520 src_mac: SRC_MAC,
2521 dst_mac: DST_MAC,
2522 tag: None,
2523 }),
2524 ip: IpFlowId::Ipv6(Ipv6FlowId {
2525 src_ip: Ipv6::SRC_IP,
2526 dst_ip: Ipv6::DST_IP,
2527 flowlabel: TEST_FLOWLABEL,
2528 }),
2529 transport: TransportFlowId::Tcp(TcpFlowId {
2530 src_port: TEST_SRC_PORT,
2531 dst_port: TEST_DST_PORT,
2532 }),
2533 };
2534 "ethernet_ipv6_tcp"
2535 )]
2536 #[test_case(
2537 build_pure_ip_tcp_packet::<Ipv4>(),
2538 GroFrameType::PureIp(IpVersion::V4),
2539 HeaderOffsets { ip_offset: 0, transport_offset: 20 },
2540 GroFlowId {
2541 link_layer: LinkLayerFlowId::PureIp,
2542 ip: IpFlowId::Ipv4(Ipv4FlowId { src_ip: Ipv4::SRC_IP, dst_ip: Ipv4::DST_IP }),
2543 transport: TransportFlowId::Tcp(TcpFlowId {
2544 src_port: TEST_SRC_PORT,
2545 dst_port: TEST_DST_PORT,
2546 }),
2547 };
2548 "pure_ip_v4_tcp"
2549 )]
2550 #[test_case(
2551 build_pure_ip_tcp_packet::<Ipv6>(),
2552 GroFrameType::PureIp(IpVersion::V6),
2553 HeaderOffsets { ip_offset: 0, transport_offset: 40 },
2554 GroFlowId {
2555 link_layer: LinkLayerFlowId::PureIp,
2556 ip: IpFlowId::Ipv6(Ipv6FlowId {
2557 src_ip: Ipv6::SRC_IP,
2558 dst_ip: Ipv6::DST_IP,
2559 flowlabel: TEST_FLOWLABEL,
2560 }),
2561 transport: TransportFlowId::Tcp(TcpFlowId {
2562 src_port: TEST_SRC_PORT,
2563 dst_port: TEST_DST_PORT,
2564 }),
2565 };
2566 "pure_ip_v6_tcp"
2567 )]
2568 fn gro_packet_parse_success(
2569 packet_bytes: Vec<u8>,
2570 target: GroFrameType,
2571 expected_offsets: HeaderOffsets,
2572 expected_flow_id: GroFlowId,
2573 ) {
2574 let mut context = NetworkParsingContext::default();
2575 let parsed = GroPacket::parse(&packet_bytes, &target, &mut context)
2576 .expect("GroPacket::parse should succeed");
2577 assert_eq!(parsed.offsets, expected_offsets);
2578 assert_eq!(parsed.flow_id, expected_flow_id);
2579 }
2580
2581 #[test]
2582 fn gro_ineligible_non_ip_ethertype() {
2583 let arp =
2584 ArpPacketBuilder::new(ArpOp::Request, SRC_MAC, Ipv4::SRC_IP, DST_MAC, Ipv4::DST_IP);
2585 let arp_bytes = arp
2586 .into_serializer()
2587 .wrap_in(EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Arp, 0))
2588 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2589 .unwrap()
2590 .unwrap_b()
2591 .as_ref()
2592 .to_vec();
2593 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2594 assert!(GroPacket::parse(&arp_bytes, &GroFrameType::Ethernet, &mut context).is_none());
2595 }
2596
2597 #[test]
2598 fn gro_ineligible_ipv4_options() {
2599 let ip = Ipv4PacketBuilderWithOptions::new(
2600 Ipv4::ip_builder(IpProto::Tcp),
2601 [Ipv4Option::RouterAlert { data: 0 }],
2602 )
2603 .unwrap();
2604 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2605 .wrap_in(tcp_builder::<Ipv4>())
2606 .wrap_in(ip)
2607 .wrap_in(ethernet_builder::<Ipv4>())
2608 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2609 .unwrap()
2610 .into_inner()
2611 .as_ref()
2612 .to_vec();
2613 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2614 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2615 }
2616
2617 #[test]
2618 fn gro_ineligible_ipv4_mf_flag() {
2619 let mut ip = Ipv4::ip_builder(IpProto::Tcp);
2620 ip.mf_flag(true);
2621 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2622 .wrap_in(tcp_builder::<Ipv4>())
2623 .wrap_in(ip)
2624 .wrap_in(ethernet_builder::<Ipv4>())
2625 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2626 .unwrap()
2627 .into_inner()
2628 .as_ref()
2629 .to_vec();
2630 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2631 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2632 }
2633
2634 #[test]
2635 fn gro_ineligible_ipv4_fragment_offset() {
2636 let mut ip = Ipv4::ip_builder(IpProto::Tcp);
2637 ip.fragment_offset(FragmentOffset::new(1).unwrap());
2638 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2639 .wrap_in(tcp_builder::<Ipv4>())
2640 .wrap_in(ip)
2641 .wrap_in(ethernet_builder::<Ipv4>())
2642 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2643 .unwrap()
2644 .into_inner()
2645 .as_ref()
2646 .to_vec();
2647 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2648 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2649 }
2650
2651 #[test]
2652 fn gro_ineligible_ipv6_extension_headers() {
2653 let hbh_opt = [HopByHopOption {
2654 action: ExtensionHeaderOptionAction::SkipAndContinue,
2655 mutable: false,
2656 data: HopByHopOptionData::RouterAlert { data: 0 },
2657 }];
2658 let ip =
2659 Ipv6PacketBuilderWithHbhOptions::new(Ipv6::ip_builder(IpProto::Tcp), &hbh_opt).unwrap();
2660 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2661 .wrap_in(tcp_builder::<Ipv6>())
2662 .wrap_in(ip)
2663 .wrap_in(ethernet_builder::<Ipv6>())
2664 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2665 .unwrap()
2666 .into_inner()
2667 .as_ref()
2668 .to_vec();
2669 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2670 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2671 }
2672
2673 #[test]
2674 fn gro_ineligible_non_tcp_transport_proto() {
2675 let udp =
2676 UdpPacketBuilder::new(Ipv4::SRC_IP, Ipv4::DST_IP, Some(TEST_SRC_PORT), TEST_DST_PORT);
2677 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2678 .wrap_in(udp)
2679 .wrap_in(Ipv4::ip_builder(IpProto::Udp))
2680 .wrap_in(ethernet_builder::<Ipv4>())
2681 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2682 .unwrap()
2683 .into_inner()
2684 .as_ref()
2685 .to_vec();
2686 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2687 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2688 }
2689
2690 #[test]
2691 fn gro_corrupt_tcp_checksum() {
2692 let mut packet = build_ethernet_tcp_packet::<Ipv4>();
2693 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2694 let parsed = GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context)
2695 .expect("should parse valid packet");
2696 let checksum_offset =
2697 parsed.offsets.transport_offset + packet_formats::tcp::CHECKSUM_OFFSET;
2698 packet[checksum_offset] ^= 0xff;
2699
2700 let mut context = NetworkParsingContext::default();
2702 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2703
2704 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2706 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_some());
2707 }
2708
2709 #[test]
2710 fn gro_ineligible_pure_ip_version_mismatch() {
2711 let pure_v4 = build_pure_ip_tcp_packet::<Ipv4>();
2712 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2713 assert!(
2714 GroPacket::parse(&pure_v4, &GroFrameType::PureIp(IpVersion::V6), &mut context)
2715 .is_none()
2716 );
2717
2718 let pure_v6 = build_pure_ip_tcp_packet::<Ipv6>();
2719 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2720 assert!(
2721 GroPacket::parse(&pure_v6, &GroFrameType::PureIp(IpVersion::V4), &mut context)
2722 .is_none()
2723 );
2724 }
2725
2726 #[test]
2727 fn upgrades_csum_offload_on_verified_tcp() {
2728 let packet = build_ethernet_tcp_packet::<Ipv4>();
2729 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![GroInputItem {
2730 buffer: TrackedBuffer::new(packet, true),
2731 target: GroFrameType::Ethernet,
2732 checksum_offload: ChecksumRxOffloading::default(),
2733 }];
2734
2735 let counters = GroCounters::default();
2736 let mut storage = GroBufferStorage::new();
2737 let mut gro = GroIter::new(items.into_iter(), &mut storage, &counters, true);
2738 let item = gro.next().unwrap();
2739 assert_eq!(
2740 item.checksum_offload,
2741 ChecksumRxOffloading::Offloaded(Some(NonZeroU16::new(1).unwrap()))
2742 );
2743 }
2744
2745 #[test]
2746 fn preserves_input_csum_offload_on_unparsed_tcp() {
2747 let ip = Ipv4PacketBuilderWithOptions::new(
2750 Ipv4::ip_builder(IpProto::Tcp),
2751 [Ipv4Option::RouterAlert { data: 0 }],
2752 )
2753 .unwrap();
2754 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2755 .wrap_in(tcp_builder::<Ipv4>())
2756 .wrap_in(ip)
2757 .wrap_in(ethernet_builder::<Ipv4>())
2758 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2759 .unwrap()
2760 .into_inner()
2761 .as_ref()
2762 .to_vec();
2763
2764 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![GroInputItem {
2765 buffer: TrackedBuffer::new(packet, true),
2766 target: GroFrameType::Ethernet,
2767 checksum_offload: ChecksumRxOffloading::default(),
2768 }];
2769
2770 let counters = GroCounters::default();
2771 let mut storage = GroBufferStorage::new();
2772 let mut gro = GroIter::new(items.into_iter(), &mut storage, &counters, true);
2773 let item = gro.next().unwrap();
2774 assert_eq!(item.checksum_offload, ChecksumRxOffloading::default());
2775 }
2776
2777 #[test]
2778 fn preserves_input_csum_offload_on_corrupt_csum() {
2779 let mut packet = build_ethernet_tcp_packet::<Ipv4>();
2780 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2781 let parsed = GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context)
2782 .expect("should parse valid packet");
2783 let checksum_offset =
2784 parsed.offsets.transport_offset + packet_formats::tcp::CHECKSUM_OFFSET;
2785 packet[checksum_offset] ^= 0xff;
2786
2787 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![GroInputItem {
2788 buffer: TrackedBuffer::new(packet, true),
2789 target: GroFrameType::Ethernet,
2790 checksum_offload: ChecksumRxOffloading::default(),
2791 }];
2792
2793 let counters = GroCounters::default();
2794 let mut storage = GroBufferStorage::new();
2795 let mut gro = GroIter::new(items.into_iter(), &mut storage, &counters, true);
2796 let item = gro.next().unwrap();
2797 assert_eq!(item.checksum_offload, ChecksumRxOffloading::default());
2798 }
2799}