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};
21
22use netstack3_base::{ChecksumRxOffloading, GsoInfo, Ipv4IdMode, NetworkParsingContext};
23
24const MAX_GRO_FRAME_LEN: u16 = u16::MAX;
35
36fn gro_headroom(frame_len: usize) -> usize {
39 usize::from(MAX_GRO_FRAME_LEN).saturating_sub(frame_len)
40}
41
42#[derive(Debug)]
45pub enum BufferSlice<'a, 'b> {
46 Contiguous(&'a mut [u8]),
48 Linearized(&'b mut [u8]),
51}
52
53impl BufferSlice<'_, '_> {
54 pub fn as_slice(&self) -> &[u8] {
56 match self {
57 Self::Contiguous(s) => s,
58 Self::Linearized(s) => s,
59 }
60 }
61
62 pub fn as_slice_mut(&mut self) -> &mut [u8] {
64 match self {
65 Self::Contiguous(s) => s,
66 Self::Linearized(s) => s,
67 }
68 }
69}
70
71pub trait MaybeContiguousBuffer {
73 fn linearized<'a, 'b>(
77 &'a mut self,
78 storage: Option<&'b mut Vec<u8>>,
79 ) -> Option<BufferSlice<'a, 'b>>;
80
81 fn unwrap_contiguous<'a>(&'a mut self) -> &'a mut [u8] {
84 assert_matches!(
85 self.linearized(None).expect("must be `Some` if contiguous"),
86 BufferSlice::Contiguous(slice) => slice
87 )
88 }
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub enum GroFrameType {
94 Ethernet,
96 PureIp(IpVersion),
98}
99
100pub trait GroBufferDestination: Eq {
103 fn frame_type(&self) -> GroFrameType;
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109struct EthernetFlowId {
110 src_mac: Mac,
111 dst_mac: Mac,
112 tag: Option<u32>,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117enum LinkLayerFlowId {
118 Ethernet(EthernetFlowId),
119 PureIp,
120}
121
122enum LinkLayerFrame {
124 Ethernet { flow_id: EthernetFlowId, ethertype: Option<EtherType> },
125 PureIp(IpVersion),
126}
127
128impl LinkLayerFrame {
129 fn parse<T: GroBufferDestination>(slice: &mut &[u8], target: &T) -> Option<LinkLayerFrame> {
130 match target.frame_type() {
131 GroFrameType::Ethernet => {
132 let frame = EthernetFrame::parse(slice, EthernetFrameLengthCheck::NoCheck).ok()?;
133 let flow_id = EthernetFlowId {
134 src_mac: frame.src_mac(),
135 dst_mac: frame.dst_mac(),
136 tag: frame.tag(),
137 };
138 Some(LinkLayerFrame::Ethernet { flow_id, ethertype: frame.ethertype() })
139 }
140 GroFrameType::PureIp(ip_version) => Some(LinkLayerFrame::PureIp(ip_version)),
141 }
142 }
143
144 fn is_eligible_for_gro(&self) -> bool {
145 match self {
146 Self::Ethernet { .. } => true,
147 Self::PureIp(_) => true,
148 }
149 }
150
151 fn ethertype(&self) -> Option<EtherType> {
152 match self {
153 Self::Ethernet { ethertype, .. } => *ethertype,
154 Self::PureIp(ip_version) => Some(EtherType::from_ip_version(*ip_version)),
155 }
156 }
157
158 fn flow_id(&self) -> LinkLayerFlowId {
159 match self {
160 Self::Ethernet { flow_id, .. } => LinkLayerFlowId::Ethernet(*flow_id),
161 Self::PureIp(_) => LinkLayerFlowId::PureIp,
162 }
163 }
164}
165
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
168struct Ipv4FlowId {
169 src_ip: Ipv4Addr,
170 dst_ip: Ipv4Addr,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175struct Ipv6FlowId {
176 src_ip: Ipv6Addr,
177 dst_ip: Ipv6Addr,
178 flowlabel: u32,
179}
180
181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183enum IpFlowId {
184 Ipv4(Ipv4FlowId),
185 Ipv6(Ipv6FlowId),
186}
187
188impl IpFlowId {
189 fn recover_payload_partial_sum(&self, segment: &TcpSegment<&'_ [u8]>) -> [u8; 2] {
192 fn recover<A: IpAddress>(src_ip: A, dst_ip: A, segment: &TcpSegment<&'_ [u8]>) -> [u8; 2] {
193 segment
194 .recover_payload_partial_sum::<A::Version>(src_ip, dst_ip)
195 .expect("transport len fits in IP total length")
196 }
197
198 match self {
199 IpFlowId::Ipv4(Ipv4FlowId { src_ip, dst_ip }) => recover(*src_ip, *dst_ip, segment),
200 IpFlowId::Ipv6(Ipv6FlowId { src_ip, dst_ip, flowlabel: _ }) => {
201 recover(*src_ip, *dst_ip, segment)
202 }
203 }
204 }
205}
206
207enum IpPacket<'a> {
208 V4(Ipv4Packet<&'a [u8]>),
209 V6(Ipv6Packet<&'a [u8]>),
210}
211
212impl<'a> From<Ipv4Packet<&'a [u8]>> for IpPacket<'a> {
213 fn from(packet: Ipv4Packet<&'a [u8]>) -> IpPacket<'a> {
214 IpPacket::V4(packet)
215 }
216}
217
218impl<'a> From<Ipv6Packet<&'a [u8]>> for IpPacket<'a> {
219 fn from(packet: Ipv6Packet<&'a [u8]>) -> IpPacket<'a> {
220 IpPacket::V6(packet)
221 }
222}
223
224impl IpPacket<'_> {
225 fn total_len(&self) -> usize {
226 match self {
227 IpPacket::V4(packet) => packet.header_len() + packet.body().len(),
228 IpPacket::V6(packet) => packet.header_len() + packet.body().len(),
229 }
230 }
231}
232
233trait GroIpPacket<I: IpExt> {
234 fn is_eligible_for_gro(&self) -> bool;
235 fn ip_proto(&self) -> Option<IpProto>;
236 fn flow_id(&self) -> IpFlowId;
237}
238
239impl GroIpPacket<Ipv4> for Ipv4Packet<&[u8]> {
240 fn is_eligible_for_gro(&self) -> bool {
241 self.fragment_offset() == packet_formats::ip::FragmentOffset::ZERO
244 && !self.mf_flag()
245 && self.header_len() == packet_formats::ipv4::HDR_PREFIX_LEN
249 }
250
251 fn ip_proto(&self) -> Option<IpProto> {
252 match self.proto() {
253 Ipv4Proto::Proto(proto) => Some(proto),
254 Ipv4Proto::Icmp | Ipv4Proto::Igmp | Ipv4Proto::Other(_) => None,
255 }
256 }
257
258 fn flow_id(&self) -> IpFlowId {
259 IpFlowId::Ipv4(Ipv4FlowId { src_ip: self.src_ip(), dst_ip: self.dst_ip() })
260 }
261}
262
263impl GroIpPacket<Ipv6> for Ipv6Packet<&[u8]> {
264 fn is_eligible_for_gro(&self) -> bool {
265 self.iter_extension_hdrs().next().is_none()
271 }
272
273 fn ip_proto(&self) -> Option<IpProto> {
274 match self.proto() {
275 Ipv6Proto::Proto(proto) => Some(proto),
276 Ipv6Proto::Icmpv6 | Ipv6Proto::NoNextHeader | Ipv6Proto::Other(_) => None,
277 }
278 }
279
280 fn flow_id(&self) -> IpFlowId {
281 IpFlowId::Ipv6(Ipv6FlowId {
282 src_ip: self.src_ip(),
283 dst_ip: self.dst_ip(),
284 flowlabel: self.flowlabel(),
285 })
286 }
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq)]
291struct TcpFlowId {
292 src_port: NonZeroU16,
293 dst_port: NonZeroU16,
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq)]
298enum TransportFlowId {
299 Tcp(TcpFlowId),
300}
301
302fn flag_requires_flush(segment: &TcpSegment<&'_ [u8]>) -> bool {
303 segment.syn() || segment.fin() || segment.rst() || segment.urg() || segment.psh()
304}
305
306enum TransportPacket<'a> {
307 Tcp(TcpSegment<&'a [u8]>),
308}
309
310impl<'a> TransportPacket<'a> {
311 fn parse<A: IpAddress>(
312 transport_view: &mut &'a [u8],
313 proto: IpProto,
314 src_ip: A,
315 dst_ip: A,
316 context: &mut NetworkParsingContext,
317 ) -> Option<TransportPacket<'a>> {
318 match proto {
319 IpProto::Tcp => TcpSegment::parse(
320 transport_view,
321 TcpParseArgs::with_context(src_ip, dst_ip, context),
322 )
323 .ok()
324 .map(TransportPacket::Tcp),
325 IpProto::Udp | IpProto::Reserved => None,
327 }
328 }
329
330 fn is_eligible_for_gro(&self) -> bool {
331 match self {
332 Self::Tcp(_) => true,
333 }
334 }
335
336 fn flow_id(&self) -> TransportFlowId {
337 match self {
338 Self::Tcp(tcp) => TransportFlowId::Tcp(TcpFlowId {
339 src_port: tcp.src_port(),
340 dst_port: tcp.dst_port(),
341 }),
342 }
343 }
344
345 fn flush_if_not_merged(&self) -> bool {
351 match self {
352 Self::Tcp(tcp) => {
353 let is_payload_segment = !tcp.body().is_empty();
354 !is_payload_segment || flag_requires_flush(tcp)
355 }
356 }
357 }
358}
359
360#[derive(Debug, Clone, Copy, PartialEq, Eq)]
366struct GroFlowId {
367 link_layer: LinkLayerFlowId,
368 ip: IpFlowId,
369 transport: TransportFlowId,
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq)]
373struct HeaderOffsets {
374 pub ip_offset: usize,
375 pub transport_offset: usize,
376}
377
378fn build_csum_offload_output(
382 input: ChecksumRxOffloading,
383 context_post_parse: &NetworkParsingContext,
384) -> ChecksumRxOffloading {
385 let verified = context_post_parse.verified_checksum_count();
386 match input {
387 ChecksumRxOffloading::FullyOffloaded => ChecksumRxOffloading::FullyOffloaded,
388 ChecksumRxOffloading::Offloaded(Some(n)) => {
389 ChecksumRxOffloading::Offloaded(Some(n.saturating_add(verified)))
390 }
391 ChecksumRxOffloading::Offloaded(None) => {
392 ChecksumRxOffloading::Offloaded(NonZeroU16::new(verified))
393 }
394 }
395}
396
397struct GroPacket<'a> {
400 pub flow_id: GroFlowId,
401 pub offsets: HeaderOffsets,
402 pub ip: IpPacket<'a>,
403 pub transport: TransportPacket<'a>,
404}
405
406impl<'a> GroPacket<'a> {
407 fn parse<T: GroBufferDestination>(
408 slice: &'a [u8],
409 target: &T,
410 context: &mut NetworkParsingContext,
411 ) -> Option<GroPacket<'a>> {
412 let total_len = slice.len();
413 let mut view = slice;
414
415 let ll_frame =
416 LinkLayerFrame::parse(&mut view, target).filter(|f| f.is_eligible_for_gro())?;
417 let ethertype = ll_frame.ethertype()?;
418
419 let ip_offset = total_len - view.len();
420 let ip_version = ethertype.to_ip_version()?;
421 let (ip, ip_flow_id, transport_offset, transport) = for_any_ip_version!(ip_version, I, {
422 let ip = <I as IpExt>::Packet::parse(&mut view, ())
423 .ok()
424 .filter(|p| p.is_eligible_for_gro())?;
425 let flow_id = ip.flow_id();
426 let proto = ip.ip_proto()?;
427 let src = ip.src_ip();
428 let dst = ip.dst_ip();
429
430 let transport_offset = ip_offset + ip.header_len();
436 let transport = TransportPacket::parse(&mut view, proto, src, dst, context)
437 .filter(|p| p.is_eligible_for_gro())?;
438
439 (ip.into(), flow_id, transport_offset, transport)
440 });
441
442 let offsets = HeaderOffsets { ip_offset, transport_offset };
443 let flow_id = GroFlowId {
444 link_layer: ll_frame.flow_id(),
445 ip: ip_flow_id,
446 transport: transport.flow_id(),
447 };
448
449 Some(GroPacket { flow_id, offsets, ip, transport })
450 }
451
452 fn payload_end(&self) -> usize {
463 self.offsets.ip_offset + self.ip.total_len()
464 }
465
466 fn flush_if_not_merged(&self, frame_len: usize) -> bool {
469 gro_headroom(frame_len) == 0 || self.transport.flush_if_not_merged()
474 }
475}
476
477enum CoalesceResult {
478 Flush,
479 Continue,
480}
481
482#[derive(Debug, Clone, Copy, PartialEq, Eq)]
484struct TcpOptions {
485 bytes: [u8; MAX_OPTIONS_LEN],
486 len: usize,
487}
488
489impl TcpOptions {
490 fn from_slice(slice: &[u8]) -> Self {
491 let mut bytes = [0u8; MAX_OPTIONS_LEN];
492 let len = slice.len();
496 bytes[..len].copy_from_slice(slice);
497 Self { bytes, len }
498 }
499
500 fn as_bytes(&self) -> &[u8] {
501 &self.bytes[..self.len]
502 }
503}
504
505const PSH_OR_FIN: u8 = packet_formats::tcp::flags::PSH | packet_formats::tcp::flags::FIN;
507
508#[derive(Debug, Clone, PartialEq, Eq)]
510struct TcpFlow {
511 flags: u8,
512 reserved_bits: u8,
513 ack: Option<u32>,
514 win: u16,
515 options: TcpOptions,
516 next_seq: u32,
523 checksum: [u8; 2],
524 orig_tcp_len: usize,
531 tcp_len_is_odd: bool,
537 gso_size: NonZeroU16,
544}
545
546impl TcpFlow {
547 fn new(segment: &TcpSegment<&'_ [u8]>) -> Self {
548 let payload_len = u16::try_from(segment.body().len()).expect("payload len fits in u16");
549 let next_seq = segment.seq_num().wrapping_add(u32::from(payload_len));
550 let options = TcpOptions::from_slice(segment.options().bytes());
551 let gso_size = NonZeroU16::new(payload_len).expect("payload len is non-zero");
555 let tcp_len = segment.total_segment_len();
556 Self {
557 flags: segment.flags(),
558 reserved_bits: segment.reserved_bits(),
559 ack: segment.ack_num(),
560 win: segment.window_size(),
561 options,
562 next_seq,
563 checksum: segment.checksum(),
564 orig_tcp_len: tcp_len,
565 tcp_len_is_odd: tcp_len % 2 == 1,
566 gso_size,
567 }
568 }
569
570 fn can_coalesce(&self, current_frame_len: usize, segment: &TcpSegment<&'_ [u8]>) -> bool {
573 let Self {
574 flags,
575 reserved_bits,
576 ack,
577 win,
578 options,
579 next_seq,
580 checksum: _,
581 orig_tcp_len: _,
582 tcp_len_is_odd: _,
583 gso_size,
584 } = self;
585 let payload_len = segment.body().len();
586 if payload_len == 0 || payload_len > usize::from(gso_size.get()) {
589 return false;
590 }
591 payload_len <= gro_headroom(current_frame_len)
592 && (*flags & !PSH_OR_FIN) == (segment.flags() & !PSH_OR_FIN)
595 && *reserved_bits == segment.reserved_bits()
598 && *ack == segment.ack_num()
599 && *win == segment.window_size()
600 && options.as_bytes() == segment.options().bytes()
601 && *next_seq == segment.seq_num()
603 }
604
605 fn update_checksum(&mut self, ip_flow: &IpFlowId, segment: &TcpSegment<&'_ [u8]>) {
610 let mut partial_sum = ip_flow.recover_payload_partial_sum(segment);
611 if self.tcp_len_is_odd {
618 partial_sum = [partial_sum[1], partial_sum[0]];
619 }
620 self.checksum = internet_checksum::add(self.checksum, &partial_sum);
621 }
622
623 fn coalesce(
629 &mut self,
630 ip_flow: &IpFlowId,
631 segment: &TcpSegment<&'_ [u8]>,
632 coalesce_into: &mut Vec<u8>,
633 ) -> CoalesceResult {
634 coalesce_into.extend_from_slice(segment.body());
635 let added_payload_len =
636 u16::try_from(segment.body().len()).expect("payload len fits in u16");
637 self.update_checksum(ip_flow, segment);
638
639 self.tcp_len_is_odd ^= added_payload_len % 2 == 1;
640 self.next_seq = segment.seq_num().wrapping_add(u32::from(added_payload_len));
641 self.flags |= segment.flags() & PSH_OR_FIN;
642
643 let is_smaller_than_gso = added_payload_len < self.gso_size.get();
649 if is_smaller_than_gso || flag_requires_flush(segment) {
650 CoalesceResult::Flush
651 } else {
652 CoalesceResult::Continue
653 }
654 }
655
656 fn finalize(&self, mut transport_view: &mut [u8]) -> NonZeroU16 {
663 let tcp_len = transport_view.len();
667 let mut tcp =
668 TcpSegmentRaw::parse_mut(&mut transport_view, ()).expect("valid TCP segment header");
669
670 let checksum = internet_checksum::update(
677 self.checksum,
678 &self.orig_tcp_len.to_be_bytes(),
679 &tcp_len.to_be_bytes(),
680 );
681
682 tcp.set_checksum(checksum);
683 tcp.set_flags(self.flags);
684 self.gso_size
685 }
686}
687
688#[derive(Debug, Clone, PartialEq, Eq)]
690enum TransportFlow {
691 Tcp(TcpFlow),
692}
693
694impl TransportFlow {
695 fn new(packet: &TransportPacket<'_>) -> Self {
696 match packet {
697 TransportPacket::Tcp(tcp) => Self::Tcp(TcpFlow::new(tcp)),
698 }
699 }
700
701 fn can_coalesce(&self, current_frame_len: usize, packet: &TransportPacket<'_>) -> bool {
702 match (self, packet) {
703 (Self::Tcp(flow), TransportPacket::Tcp(tcp)) => {
704 flow.can_coalesce(current_frame_len, tcp)
705 }
706 }
707 }
708
709 fn coalesce(
710 &mut self,
711 ip_flow: &IpFlowId,
712 packet: &TransportPacket<'_>,
713 coalesce_into: &mut Vec<u8>,
714 ) -> CoalesceResult {
715 match (self, packet) {
716 (Self::Tcp(flow), TransportPacket::Tcp(tcp)) => {
717 flow.coalesce(ip_flow, tcp, coalesce_into)
718 }
719 }
720 }
721
722 fn finalize(&self, transport_view: &mut [u8]) -> NonZeroU16 {
723 match self {
724 Self::Tcp(tcp) => tcp.finalize(transport_view),
725 }
726 }
727}
728
729#[derive(Debug, Clone, Copy, PartialEq, Eq)]
730enum Ipv4IdState {
731 Initial(u16),
733 Consistent(u16),
735 Increasing(u16),
737}
738
739impl Ipv4IdState {
740 fn can_coalesce(&self, next_id: u16) -> bool {
741 match self {
742 Self::Initial(id) => next_id == *id || next_id == id.wrapping_add(1),
743 Self::Consistent(id) => next_id == *id,
744 Self::Increasing(prev_id) => next_id == prev_id.wrapping_add(1),
745 }
746 }
747
748 fn coalesce(&mut self, next_id: u16) {
749 *self = match self {
750 Self::Initial(id) => {
751 if next_id == *id {
752 Self::Consistent(next_id)
753 } else if next_id == id.wrapping_add(1) {
754 Self::Increasing(next_id)
755 } else {
756 unreachable!("cannot coalesce non-matching ID");
757 }
758 }
759 Self::Consistent(id) => {
760 debug_assert_eq!(next_id, *id);
761 Self::Consistent(next_id)
762 }
763 Self::Increasing(_) => Self::Increasing(next_id),
764 };
765 }
766}
767
768#[derive(Debug, Clone, Copy, PartialEq, Eq)]
770struct Ipv4Flow {
771 ttl: u8,
772 dscp_and_ecn: DscpAndEcn,
773 df_flag: bool,
774 id_state: Ipv4IdState,
775}
776
777impl Ipv4Flow {
778 fn new(packet: &impl Ipv4Header) -> Ipv4Flow {
779 Self {
780 ttl: packet.ttl(),
781 dscp_and_ecn: packet.dscp_and_ecn(),
782 df_flag: packet.df_flag(),
783 id_state: Ipv4IdState::Initial(packet.id()),
784 }
785 }
786
787 fn can_coalesce(&self, packet: &impl Ipv4Header) -> bool {
790 let Self { ttl, dscp_and_ecn, df_flag, id_state } = self;
791 *ttl == packet.ttl()
792 && *dscp_and_ecn == packet.dscp_and_ecn()
793 && *df_flag == packet.df_flag()
794 && id_state.can_coalesce(packet.id())
795 }
796
797 fn coalesce(&mut self, packet: &impl Ipv4Header) {
798 self.id_state.coalesce(packet.id());
799 }
800
801 fn finalize(&self, mut ip_view: &mut [u8]) {
804 let total_len = ip_view.len();
805 let mut ip = Ipv4PacketRaw::parse_mut(&mut ip_view, ()).expect("valid IPv4 header");
806 let new_len_u16 = u16::try_from(total_len).expect("IP total length fits in u16");
807 ip.set_total_len_and_update_checksum(new_len_u16);
808 }
809
810 fn ipv4_id_mode(&self) -> Ipv4IdMode {
811 match self.id_state {
812 Ipv4IdState::Consistent(_) => Ipv4IdMode::Fixed,
813 Ipv4IdState::Increasing(_) => Ipv4IdMode::Incrementing,
814 Ipv4IdState::Initial(_) => {
815 unreachable!("coalesced flow with num_coalesced > 1 cannot be Initial")
816 }
817 }
818 }
819}
820
821#[derive(Debug, Clone, Copy, PartialEq, Eq)]
823struct Ipv6Flow {
824 hop_limit: u8,
825 dscp_and_ecn: DscpAndEcn,
826}
827
828impl Ipv6Flow {
829 fn new(packet: &impl Ipv6Header) -> Ipv6Flow {
830 Self { hop_limit: packet.hop_limit(), dscp_and_ecn: packet.dscp_and_ecn() }
831 }
832
833 fn can_coalesce(&self, packet: &impl Ipv6Header) -> bool {
836 let Self { hop_limit, dscp_and_ecn } = self;
837 *hop_limit == packet.hop_limit() && *dscp_and_ecn == packet.dscp_and_ecn()
838 }
839
840 fn coalesce(&mut self, _packet: &impl Ipv6Header) {
841 }
845
846 fn finalize(&self, mut ip_view: &mut [u8]) {
849 let payload_len = ip_view.len() - IPV6_FIXED_HDR_LEN;
850 let mut ip = Ipv6PacketRaw::parse_mut(&mut ip_view, ()).expect("valid IPv6 header");
851 let payload_len_u16 = u16::try_from(payload_len).expect("IPv6 payload len fits in u16");
852 ip.set_payload_len(payload_len_u16);
853 }
854}
855
856#[derive(Debug, Clone, Copy, PartialEq, Eq)]
858enum IpFlow {
859 Ipv4(Ipv4Flow),
860 Ipv6(Ipv6Flow),
861}
862
863impl IpFlow {
864 fn new(ip: &IpPacket<'_>) -> Self {
865 match ip {
866 IpPacket::V4(packet) => IpFlow::Ipv4(Ipv4Flow::new(packet)),
867 IpPacket::V6(packet) => IpFlow::Ipv6(Ipv6Flow::new(packet)),
868 }
869 }
870
871 fn can_coalesce(&self, ip: &IpPacket<'_>) -> bool {
872 match (self, ip) {
873 (Self::Ipv4(flow), IpPacket::V4(packet)) => flow.can_coalesce(packet),
874 (Self::Ipv6(flow), IpPacket::V6(packet)) => flow.can_coalesce(packet),
875 (Self::Ipv4(_), IpPacket::V6(_)) | (Self::Ipv6(_), IpPacket::V4(_)) => false,
876 }
877 }
878
879 fn coalesce(&mut self, ip: &IpPacket<'_>) {
880 match (self, ip) {
881 (Self::Ipv4(flow), IpPacket::V4(packet)) => flow.coalesce(packet),
882 (Self::Ipv6(flow), IpPacket::V6(packet)) => flow.coalesce(packet),
883 (Self::Ipv4(_), IpPacket::V6(_)) | (Self::Ipv6(_), IpPacket::V4(_)) => {
884 unreachable!("mismatched IP versions can't be coalesced")
885 }
886 }
887 }
888
889 fn finalize(&self, ip_view: &mut [u8]) {
890 match self {
891 Self::Ipv4(flow) => flow.finalize(ip_view),
892 Self::Ipv6(flow) => flow.finalize(ip_view),
893 }
894 }
895
896 fn ipv4_id_mode(&self) -> Option<Ipv4IdMode> {
897 match self {
898 Self::Ipv4(flow) => Some(flow.ipv4_id_mode()),
899 Self::Ipv6(_) => None,
900 }
901 }
902}
903
904struct GroFlow<T> {
905 target: T,
906 flow_id: GroFlowId,
907 offsets: HeaderOffsets,
908 ip: IpFlow,
909 transport: TransportFlow,
910 checksum_offload: ChecksumRxOffloading,
911 num_coalesced: usize,
912}
913
914impl<T: Eq> GroFlow<T> {
915 fn new(target: T, parsed: GroPacket<'_>, checksum_offload: ChecksumRxOffloading) -> Self {
916 let GroPacket { flow_id, offsets, ip, transport } = parsed;
917 Self {
918 target,
919 flow_id,
920 offsets,
921 ip: IpFlow::new(&ip),
922 transport: TransportFlow::new(&transport),
923 checksum_offload,
924 num_coalesced: 1,
925 }
926 }
927
928 fn matches(
929 &self,
930 target: &T,
931 flow_id: &GroFlowId,
932 checksum_offload: ChecksumRxOffloading,
933 ) -> bool {
934 self.target == *target
935 && self.flow_id == *flow_id
936 && self.checksum_offload == checksum_offload
937 }
938
939 fn can_coalesce(&self, current_frame_len: usize, parsed: &GroPacket<'_>) -> bool {
940 self.ip.can_coalesce(&parsed.ip)
941 && self.transport.can_coalesce(current_frame_len, &parsed.transport)
942 }
943
944 fn coalesce(&mut self, parsed: GroPacket<'_>, coalesce_into: &mut Vec<u8>) -> CoalesceResult {
945 self.num_coalesced += 1;
946 self.ip.coalesce(&parsed.ip);
947 self.transport.coalesce(&self.flow_id.ip, &parsed.transport, coalesce_into)
948 }
949
950 fn finalize(&self, view: &mut [u8]) -> Option<GsoInfo> {
951 let HeaderOffsets { ip_offset, transport_offset } = self.offsets;
952 if self.num_coalesced > 1 {
953 self.ip.finalize(&mut view[ip_offset..]);
954 let gso_size = self.transport.finalize(&mut view[transport_offset..]);
955 let ipv4_id_mode = self.ip.ipv4_id_mode();
956 Some(GsoInfo { gso_size, ipv4_id_mode })
957 } else {
958 None
959 }
960 }
961}
962
963#[derive(Debug, PartialEq, Eq)]
965pub struct GroInputItem<B, T> {
966 pub buffer: B,
968 pub target: T,
970 pub checksum_offload: ChecksumRxOffloading,
972}
973
974#[derive(Debug)]
976pub enum GroOutputBuffers<'a, B, O> {
977 Contiguous(B),
979 Linearized {
981 slice: &'a mut [u8],
983 buffer: B,
985 },
986 Coalesced {
988 slice: &'a mut [u8],
990 buffers: O,
992 },
993}
994
995impl<'a, B: MaybeContiguousBuffer, O> GroOutputBuffers<'a, B, O> {
996 pub fn slice_mut(&mut self) -> &mut [u8] {
998 match self {
999 Self::Contiguous(b) => b.unwrap_contiguous(),
1000 Self::Linearized { slice, .. } | Self::Coalesced { slice, .. } => slice,
1001 }
1002 }
1003}
1004
1005#[derive(Debug)]
1007pub struct GroOutputItem<'a, B, T, O> {
1008 pub target: T,
1010 pub checksum_offload: ChecksumRxOffloading,
1012 pub gso_info: Option<GsoInfo>,
1014 pub buffers: GroOutputBuffers<'a, B, O>,
1016}
1017
1018#[derive(Debug, Derivative)]
1020#[derivative(Default(bound = ""))]
1021pub struct GroBufferStorage<B> {
1022 coalescing_vec: Vec<u8>,
1024 coalesced_buffers: Vec<B>,
1027 linearization_vec: Vec<u8>,
1029}
1030
1031impl<B> GroBufferStorage<B> {
1032 pub fn new() -> Self {
1034 Self::default()
1035 }
1036
1037 fn clear(&mut self) {
1038 self.coalescing_vec.clear();
1039 self.linearization_vec.clear();
1040 self.coalesced_buffers.clear();
1041 }
1042}
1043
1044impl<B: MaybeContiguousBuffer> GroBufferStorage<B> {
1045 pub fn coalesce<I, T>(&mut self, iter: I, enable_tcp_gro: bool) -> GroIter<'_, I, B, T>
1047 where
1048 I: Iterator<Item = GroInputItem<B, T>>,
1049 T: GroBufferDestination,
1050 {
1051 let enable_tcp_gro = match iter.size_hint() {
1052 (_, Some(1)) => false,
1053 _ => enable_tcp_gro,
1054 };
1055 GroIter::new(iter, self, enable_tcp_gro)
1056 }
1057
1058 fn build_output<'a, T: Eq>(
1059 &'a mut self,
1060 ActiveFlow { flow, buffers }: ActiveFlow<B, T>,
1061 ) -> GroOutputItem<'a, B, T, alloc::vec::Drain<'a, B>> {
1062 match buffers {
1063 ActiveFlowBuffers::Single { buffer, payload_end: _ } => GroOutputItem {
1066 target: flow.target,
1067 checksum_offload: flow.checksum_offload,
1068 gso_info: None,
1069 buffers: GroOutputBuffers::Contiguous(buffer),
1070 },
1071 ActiveFlowBuffers::Coalesced => {
1072 let GroBufferStorage { coalescing_vec, coalesced_buffers, .. } = self;
1073
1074 let gso_info = flow.finalize(&mut coalescing_vec[..]);
1075
1076 let GroFlow { target, checksum_offload, num_coalesced, .. } = flow;
1077 let buffers = GroOutputBuffers::Coalesced {
1078 slice: &mut coalescing_vec[..],
1079 buffers: coalesced_buffers.drain(..num_coalesced),
1080 };
1081 GroOutputItem { target, checksum_offload, gso_info, buffers }
1082 }
1083 }
1084 }
1085}
1086
1087enum ActiveFlowBuffers<B> {
1089 Single {
1092 buffer: B,
1093 payload_end: usize,
1097 },
1098 Coalesced,
1101}
1102
1103struct ActiveFlow<B, T> {
1105 flow: GroFlow<T>,
1106 buffers: ActiveFlowBuffers<B>,
1107}
1108
1109impl<B, T> ActiveFlow<B, T> {
1110 fn frame_len(&self, coalescing_vec_len: usize) -> usize {
1113 match &self.buffers {
1114 ActiveFlowBuffers::Single { payload_end, .. } => *payload_end,
1118 ActiveFlowBuffers::Coalesced => coalescing_vec_len,
1119 }
1120 }
1121}
1122
1123impl<B: MaybeContiguousBuffer, T: Eq> ActiveFlow<B, T> {
1124 fn matches(
1125 &self,
1126 target: &T,
1127 flow_id: &GroFlowId,
1128 checksum_offload: ChecksumRxOffloading,
1129 ) -> bool {
1130 self.flow.matches(target, flow_id, checksum_offload)
1131 }
1132
1133 fn can_coalesce(&self, coalescing_vec_len: usize, parsed: &GroPacket<'_>) -> bool {
1134 self.flow.can_coalesce(self.frame_len(coalescing_vec_len), parsed)
1135 }
1136
1137 fn coalesce(
1144 &mut self,
1145 parsed: GroPacket<'_>,
1146 coalesce_into: &mut Vec<u8>,
1147 hold_buffers: &mut Vec<B>,
1148 ) -> CoalesceResult {
1149 match core::mem::replace(&mut self.buffers, ActiveFlowBuffers::Coalesced) {
1150 ActiveFlowBuffers::Single { mut buffer, payload_end } => {
1151 coalesce_into.clear();
1152 coalesce_into.extend_from_slice(&buffer.unwrap_contiguous()[..payload_end]);
1161 hold_buffers.push(buffer);
1162 }
1163 ActiveFlowBuffers::Coalesced => {}
1164 }
1165 self.flow.coalesce(parsed, coalesce_into)
1166 }
1167}
1168
1169#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1170enum BufferLinearization {
1171 Contiguous,
1172 Linearized,
1173}
1174
1175struct PendingFlow<B, T> {
1182 buffer: B,
1183 flow: GroFlow<T>,
1184 seed_payload_end: usize,
1188 linearization: BufferLinearization,
1189}
1190
1191struct PendingFlush<B, T> {
1197 buffer: B,
1198 target: T,
1199 linearization: BufferLinearization,
1200 checksum_offload: ChecksumRxOffloading,
1201}
1202
1203enum PendingItem<B, T> {
1204 NewFlow(PendingFlow<B, T>),
1205 Flush(PendingFlush<B, T>),
1206}
1207
1208pub struct GroIter<'a, I, B, T> {
1210 iter: I,
1211 storage: &'a mut GroBufferStorage<B>,
1212 active_flow: Option<ActiveFlow<B, T>>,
1214 pending_item: Option<PendingItem<B, T>>,
1216 enable_tcp_gro: bool,
1217}
1218
1219impl<'a, I, B, T> GroIter<'a, I, B, T>
1220where
1221 B: MaybeContiguousBuffer,
1222{
1223 fn new(iter: I, storage: &'a mut GroBufferStorage<B>, enable_tcp_gro: bool) -> Self {
1224 Self { iter, storage, active_flow: None, pending_item: None, enable_tcp_gro }
1225 }
1226}
1227
1228impl<'a, I, B, T> Drop for GroIter<'a, I, B, T> {
1229 fn drop(&mut self) {
1230 self.storage.clear();
1231 }
1232}
1233
1234enum ProcessingResult<'a, B, T, O> {
1235 Continue,
1236 Return(GroOutputItem<'a, B, T, O>),
1237}
1238
1239impl<'a, I, B, T> GroIter<'a, I, B, T>
1240where
1241 B: MaybeContiguousBuffer,
1242 T: GroBufferDestination,
1243 I: Iterator<Item = GroInputItem<B, T>>,
1244{
1245 pub fn next<'b>(&'b mut self) -> Option<GroOutputItem<'b, B, T, alloc::vec::Drain<'b, B>>> {
1247 loop {
1248 if let Some(i) = self.pending_item.take() {
1249 match self.process_pending(i) {
1250 ProcessingResult::Continue => continue,
1251 ProcessingResult::Return(out) => return Some(out),
1252 };
1253 }
1254 if let Some(i) = self.iter.next() {
1255 match self.process_input(i) {
1256 ProcessingResult::Continue => continue,
1257 ProcessingResult::Return(out) => return Some(out),
1258 };
1259 }
1260 if let Some(f) = self.active_flow.take() {
1261 return Some(self.storage.build_output(f));
1262 }
1263 return None;
1264 }
1265 }
1266
1267 fn process_pending<'b>(
1268 &'b mut self,
1269 pending: PendingItem<B, T>,
1270 ) -> ProcessingResult<'b, B, T, alloc::vec::Drain<'b, B>> {
1271 let Self { storage, active_flow, .. } = self;
1272 match pending {
1273 PendingItem::NewFlow(pending) => {
1274 let PendingFlow { buffer, flow, seed_payload_end, linearization } = pending;
1275 let buffers = match linearization {
1276 BufferLinearization::Contiguous => {
1280 ActiveFlowBuffers::Single { buffer, payload_end: seed_payload_end }
1281 }
1282 BufferLinearization::Linearized => {
1283 let GroBufferStorage {
1292 coalescing_vec,
1293 coalesced_buffers,
1294 linearization_vec,
1295 } = storage;
1296 coalescing_vec.clear();
1297 coalescing_vec.extend_from_slice(&linearization_vec[..seed_payload_end]);
1298 coalesced_buffers.push(buffer);
1299 ActiveFlowBuffers::Coalesced
1300 }
1301 };
1302
1303 *active_flow = Some(ActiveFlow { flow, buffers });
1304 ProcessingResult::Continue
1305 }
1306 PendingItem::Flush(pending) => {
1307 let PendingFlush { buffer, target, linearization, checksum_offload } = pending;
1308
1309 let buffers = match linearization {
1310 BufferLinearization::Contiguous => GroOutputBuffers::Contiguous(buffer),
1311 BufferLinearization::Linearized => GroOutputBuffers::Linearized {
1312 buffer,
1313 slice: &mut storage.linearization_vec,
1314 },
1315 };
1316 ProcessingResult::Return(GroOutputItem {
1317 target,
1318 checksum_offload,
1319 gso_info: None,
1320 buffers,
1321 })
1322 }
1323 }
1324 }
1325
1326 fn process_input<'b>(
1329 &'b mut self,
1330 item: GroInputItem<B, T>,
1331 ) -> ProcessingResult<'b, B, T, alloc::vec::Drain<'b, B>> {
1332 let Self { storage, active_flow, pending_item, enable_tcp_gro, .. } = self;
1333 let GroInputItem { mut buffer, target, checksum_offload } = item;
1334
1335 storage.linearization_vec.clear();
1336 let buffer_slice = buffer
1337 .linearized(Some(&mut storage.linearization_vec))
1338 .expect("must be `Some` if linearization vec is provided");
1339 let linearization = match &buffer_slice {
1340 BufferSlice::Contiguous(_) => BufferLinearization::Contiguous,
1341 BufferSlice::Linearized(_) => BufferLinearization::Linearized,
1342 };
1343
1344 macro_rules! return_single_buffer {
1350 ($csum_offload:expr) => {{
1351 let buffers = match buffer_slice {
1352 BufferSlice::Contiguous(_) => GroOutputBuffers::Contiguous(buffer),
1353 BufferSlice::Linearized(slice) => {
1354 GroOutputBuffers::Linearized { buffer, slice }
1355 }
1356 };
1357 return ProcessingResult::Return(GroOutputItem {
1358 target,
1359 checksum_offload: $csum_offload,
1360 gso_info: None,
1361 buffers,
1362 });
1363 }};
1364 }
1365
1366 if !*enable_tcp_gro {
1367 return_single_buffer!(checksum_offload);
1368 }
1369
1370 let mut context = NetworkParsingContext::new(checksum_offload);
1371 let parsed = GroPacket::parse(buffer_slice.as_slice(), &target, &mut context);
1372 let checksum_offload = build_csum_offload_output(checksum_offload, &context);
1376 let parsed = match parsed {
1377 Some(p) => p,
1378 None => return_single_buffer!(checksum_offload),
1379 };
1380
1381 let flush_if_not_merged = parsed.flush_if_not_merged(buffer_slice.as_slice().len());
1382 let Some(mut active) = active_flow.take() else {
1383 if flush_if_not_merged {
1387 return_single_buffer!(checksum_offload);
1388 }
1389
1390 let seed_payload_end = parsed.payload_end();
1391 let flow = GroFlow::new(target, parsed, checksum_offload);
1392 let buffers = match linearization {
1393 BufferLinearization::Contiguous => {
1397 ActiveFlowBuffers::Single { buffer, payload_end: seed_payload_end }
1398 }
1399 BufferLinearization::Linearized => {
1400 storage.coalescing_vec.clear();
1401 storage
1405 .coalescing_vec
1406 .extend_from_slice(&buffer_slice.as_slice()[..seed_payload_end]);
1407 storage.coalesced_buffers.push(buffer);
1408 ActiveFlowBuffers::Coalesced
1409 }
1410 };
1411 *active_flow = Some(ActiveFlow { flow, buffers });
1412 return ProcessingResult::Continue;
1413 };
1414
1415 if !active.matches(&target, &parsed.flow_id, checksum_offload) {
1416 if flush_if_not_merged {
1427 *active_flow = Some(active);
1428 return_single_buffer!(checksum_offload);
1429 }
1430
1431 let seed_payload_end = parsed.payload_end();
1432 let flow = GroFlow::new(target, parsed, checksum_offload);
1433 let pending =
1434 PendingItem::NewFlow(PendingFlow { buffer, flow, seed_payload_end, linearization });
1435 *pending_item = Some(pending);
1436 return ProcessingResult::Return(storage.build_output(active));
1437 }
1438
1439 if active.can_coalesce(storage.coalescing_vec.len(), &parsed) {
1440 let coalesce_result = active.coalesce(
1441 parsed,
1442 &mut storage.coalescing_vec,
1443 &mut storage.coalesced_buffers,
1444 );
1445
1446 storage.coalesced_buffers.push(buffer);
1447
1448 match coalesce_result {
1449 CoalesceResult::Flush => ProcessingResult::Return(storage.build_output(active)),
1450 CoalesceResult::Continue => {
1451 *active_flow = Some(active);
1452 ProcessingResult::Continue
1453 }
1454 }
1455 } else {
1456 let pending = if flush_if_not_merged {
1461 PendingItem::Flush(PendingFlush { buffer, target, linearization, checksum_offload })
1462 } else {
1463 let seed_payload_end = parsed.payload_end();
1464 let flow = GroFlow::new(target, parsed, checksum_offload);
1465 PendingItem::NewFlow(PendingFlow { buffer, flow, seed_payload_end, linearization })
1466 };
1467 *pending_item = Some(pending);
1468 ProcessingResult::Return(storage.build_output(active))
1469 }
1470 }
1471}
1472
1473#[cfg(test)]
1474mod tests {
1475 use super::*;
1476 use alloc::sync::Arc;
1477 use alloc::vec;
1478 use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1479
1480 use net_declare::{net_ip_v4, net_ip_v6, net_mac};
1481 use net_types::ip::{Ipv4, Ipv6};
1482 use netstack3_base::NetworkSerializationContext;
1483 use packet::{Buf, InnerPacketBuilder, NestableSerializer as _, Serializer};
1484 use packet_formats::arp::{ArpOp, ArpPacketBuilder};
1485 use packet_formats::ethernet::{EtherType, EthernetFrameBuilder};
1486 use packet_formats::ip::{FragmentOffset, IpExt, IpProto, Ipv4Proto};
1487 use packet_formats::ipv4::options::Ipv4Option;
1488 use packet_formats::ipv4::{Ipv4PacketBuilder, Ipv4PacketBuilderWithOptions};
1489 use packet_formats::ipv6::ext_hdrs::{
1490 ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData,
1491 };
1492 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
1493 use packet_formats::tcp::options::{TcpOptionsBuilder, TimestampOption};
1494 use packet_formats::tcp::{TcpSegmentBuilder, TcpSegmentBuilderWithOptions};
1495 use packet_formats::udp::UdpPacketBuilder;
1496 use test_case::test_case;
1497
1498 impl GroBufferDestination for GroFrameType {
1499 fn frame_type(&self) -> GroFrameType {
1500 *self
1501 }
1502 }
1503
1504 #[derive(Debug)]
1506 struct TrackedBuffer {
1507 buf: Vec<u8>,
1508 contiguous: bool,
1509 dropped: Arc<AtomicBool>,
1510 linearized_count: Arc<AtomicUsize>,
1511 }
1512
1513 impl TrackedBuffer {
1514 fn new(buf: Vec<u8>, contiguous: bool) -> Self {
1515 Self {
1516 buf,
1517 contiguous,
1518 dropped: Arc::new(AtomicBool::new(false)),
1519 linearized_count: Arc::new(AtomicUsize::new(0)),
1520 }
1521 }
1522
1523 fn dropped(&self) -> Arc<AtomicBool> {
1525 self.dropped.clone()
1526 }
1527
1528 fn linearized_count(&self) -> Arc<AtomicUsize> {
1531 self.linearized_count.clone()
1532 }
1533 }
1534
1535 impl Drop for TrackedBuffer {
1536 fn drop(&mut self) {
1537 self.dropped.store(true, Ordering::SeqCst);
1538 }
1539 }
1540
1541 impl MaybeContiguousBuffer for TrackedBuffer {
1542 fn linearized<'a, 'b>(
1543 &'a mut self,
1544 storage: Option<&'b mut Vec<u8>>,
1545 ) -> Option<BufferSlice<'a, 'b>> {
1546 if self.contiguous {
1547 Some(BufferSlice::Contiguous(&mut self.buf[..]))
1548 } else {
1549 let storage = storage?;
1550 let _: usize = self.linearized_count.fetch_add(1, Ordering::SeqCst);
1551 let frame_length = self.buf.len();
1552 if storage.len() < frame_length {
1553 storage.resize(frame_length, 0);
1554 }
1555 let slice = &mut storage[..frame_length];
1556 slice.copy_from_slice(&self.buf);
1557 Some(BufferSlice::Linearized(slice))
1558 }
1559 }
1560 }
1561
1562 fn input_item(buffer: TrackedBuffer) -> GroInputItem<TrackedBuffer, GroFrameType> {
1564 GroInputItem {
1565 buffer,
1566 target: GroFrameType::Ethernet,
1567 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
1568 }
1569 }
1570
1571 #[test]
1572 fn process_gro_handles_fragmented() {
1573 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![
1574 GroInputItem {
1575 buffer: TrackedBuffer::new(vec![1, 2, 3], true),
1576 target: GroFrameType::Ethernet,
1577 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
1578 },
1579 GroInputItem {
1580 buffer: TrackedBuffer::new(vec![4, 5, 6], false),
1581 target: GroFrameType::Ethernet,
1582 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
1583 },
1584 ];
1585
1586 let mut storage = GroBufferStorage::new();
1587 let mut output = Vec::new();
1588 let mut gro = storage.coalesce(items.into_iter(), false);
1589 while let Some(mut item) = gro.next() {
1590 output.push(item.buffers.slice_mut().to_vec());
1591 }
1592
1593 assert_eq!(output, vec![vec![1, 2, 3], vec![4, 5, 6]]);
1594 }
1595
1596 const SRC_MAC: Mac = net_mac!("00:11:22:33:44:55");
1597 const DST_MAC: Mac = net_mac!("66:77:88:99:aa:bb");
1598 const SRC_IP_V4: Ipv4Addr = net_ip_v4!("192.168.0.1");
1599 const DST_IP_V4: Ipv4Addr = net_ip_v4!("192.168.0.2");
1600 const SRC_IP_V6: Ipv6Addr = net_ip_v6!("2001:db8::1");
1601 const DST_IP_V6: Ipv6Addr = net_ip_v6!("2001:db8::2");
1602
1603 #[derive(Clone, Debug)]
1608 struct FrameSpec {
1609 ip: IpSpec,
1611 seq: u32,
1612 ack: u32,
1613 win: u16,
1614 psh: bool,
1615 fin: bool,
1616 syn: bool,
1617 rst: bool,
1618 urg: bool,
1619 ttl: u8,
1621 dscp_and_ecn: DscpAndEcn,
1622 timestamp: Option<u32>,
1624 min_body_len: usize,
1626 payload: &'static [u8],
1627 contiguous: bool,
1629 }
1630
1631 #[derive(Clone, Copy, Debug)]
1633 enum IpSpec {
1634 V4 { df: bool, id: u16 },
1635 V6 { flowlabel: u32 },
1636 }
1637
1638 fn v4(seq: u32, payload: &'static [u8]) -> FrameSpec {
1640 FrameSpec {
1641 ip: IpSpec::V4 { df: false, id: 0 },
1642 seq,
1643 ack: 1000,
1644 win: 64240,
1645 psh: false,
1646 fin: false,
1647 syn: false,
1648 rst: false,
1649 urg: false,
1650 ttl: 64,
1651 dscp_and_ecn: DscpAndEcn::default(),
1652 timestamp: None,
1653 min_body_len: 0,
1654 payload,
1655 contiguous: true,
1656 }
1657 }
1658
1659 fn v6(seq: u32, payload: &'static [u8]) -> FrameSpec {
1661 FrameSpec { ip: IpSpec::V6 { flowlabel: 0 }, ..v4(seq, payload) }
1662 }
1663
1664 impl FrameSpec {
1665 fn build(&self) -> Vec<u8> {
1667 let FrameSpec {
1668 ip: ip_spec,
1669 seq,
1670 ack,
1671 win,
1672 psh,
1673 fin,
1674 syn,
1675 rst,
1676 urg,
1677 ttl,
1678 dscp_and_ecn,
1679 timestamp,
1680 min_body_len,
1681 payload,
1682 contiguous: _,
1683 } = *self;
1684
1685 let mut body = payload.to_vec();
1686
1687 macro_rules! tcp_builder {
1688 ($src:expr, $dst:expr) => {{
1689 let mut tcp = TcpSegmentBuilder::new(
1690 $src,
1691 $dst,
1692 TEST_SRC_PORT,
1693 TEST_DST_PORT,
1694 seq,
1695 Some(ack),
1696 win,
1697 );
1698 tcp.psh(psh);
1699 tcp.fin(fin);
1700 tcp.syn(syn);
1701 tcp.rst(rst);
1702 tcp.urg(urg);
1703 tcp
1704 }};
1705 }
1706
1707 macro_rules! serialize {
1708 ($tcp:expr, $ip:expr, $ethertype:expr) => {
1709 Buf::new(&mut body[..], ..)
1710 .wrap_in($tcp)
1711 .wrap_in($ip)
1712 .wrap_in(EthernetFrameBuilder::new(
1713 SRC_MAC,
1714 DST_MAC,
1715 $ethertype,
1716 min_body_len,
1717 ))
1718 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1719 .unwrap()
1720 .unwrap_b()
1721 .as_ref()
1722 .to_vec()
1723 };
1724 }
1725
1726 macro_rules! serialize_with_options {
1729 ($tcp:expr, $ip:expr, $ethertype:expr) => {
1730 match timestamp {
1731 None => serialize!($tcp, $ip, $ethertype),
1732 Some(ts_val) => {
1733 let options = TcpOptionsBuilder {
1734 timestamp: Some(TimestampOption::new(ts_val, 0)),
1735 ..Default::default()
1736 };
1737 let tcp = TcpSegmentBuilderWithOptions::new($tcp, options).unwrap();
1738 serialize!(tcp, $ip, $ethertype)
1739 }
1740 }
1741 };
1742 }
1743
1744 match ip_spec {
1745 IpSpec::V4 { df, id } => {
1746 let mut ip =
1747 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, ttl, IpProto::Tcp.into());
1748 ip.dscp_and_ecn(dscp_and_ecn);
1749 ip.df_flag(df);
1750 ip.id(id);
1751 serialize_with_options!(tcp_builder!(SRC_IP_V4, DST_IP_V4), ip, EtherType::Ipv4)
1752 }
1753 IpSpec::V6 { flowlabel } => {
1754 let mut ip =
1755 Ipv6PacketBuilder::new(SRC_IP_V6, DST_IP_V6, ttl, IpProto::Tcp.into());
1756 ip.dscp_and_ecn(dscp_and_ecn);
1757 ip.flowlabel(flowlabel);
1758 serialize_with_options!(tcp_builder!(SRC_IP_V6, DST_IP_V6), ip, EtherType::Ipv6)
1759 }
1760 }
1761 }
1762 }
1763
1764 fn run_gro(frames: &[FrameSpec], enable_tcp_gro: bool) -> Vec<Vec<u8>> {
1766 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = frames
1767 .iter()
1768 .map(|spec| input_item(TrackedBuffer::new(spec.build(), spec.contiguous)))
1769 .collect();
1770
1771 let mut storage = GroBufferStorage::new();
1772 let mut output = Vec::new();
1773 let mut gro = storage.coalesce(items.into_iter(), enable_tcp_gro);
1774 while let Some(mut item) = gro.next() {
1775 output.push(item.buffers.slice_mut().to_vec());
1776 }
1777 output
1778 }
1779
1780 #[track_caller]
1782 fn parse_frame(frame: &[u8]) -> GroPacket<'_> {
1783 GroPacket::parse(
1784 frame,
1785 &GroFrameType::Ethernet,
1786 &mut NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(None)),
1787 )
1788 .expect("frame is a valid GRO packet")
1789 }
1790
1791 #[test_case(
1792 vec![v4(100, b"hello "), v4(106, b"world")],
1793 vec![b"hello world"]; "coalesces_contiguous")]
1794 #[test_case(
1795 vec![
1796 FrameSpec { contiguous: false, ..v4(100, b"foo ") },
1797 FrameSpec { contiguous: false, ..v4(104, b"bar") },
1798 ],
1799 vec![b"foo bar"]; "coalesces_fragmented")]
1800 #[test_case(
1801 vec![v4(100, b"contig "), FrameSpec { contiguous: false, ..v4(107, b"frag") }],
1802 vec![b"contig frag"]; "coalesces_contiguous_then_fragmented")]
1803 #[test_case(
1804 vec![FrameSpec { contiguous: false, ..v4(100, b"single") }],
1805 vec![b"single"]; "lone_fragmented_frame_is_emitted")]
1806 #[test_case(
1807 vec![v4(100, b"first "), v4(106, b"second"), v4(112, b"third")],
1808 vec![b"first secondthird"]; "coalesces_three_frames")]
1809 #[test_case(
1810 vec![v6(200, b"ipv6_1"), FrameSpec { psh: true, ..v6(206, b"ipv6_2") }],
1811 vec![b"ipv6_1ipv6_2"]; "coalesces_ipv6")]
1812 #[test_case(
1813 vec![
1814 FrameSpec { ack: 1000, ..v4(100, b"ack1000 ") },
1815 FrameSpec { ack: 2000, ..v4(108, b"ack2000") },
1816 ],
1817 vec![b"ack1000 ", b"ack2000"]; "ack_mismatch_flushes")]
1818 #[test_case(
1819 vec![
1820 FrameSpec { win: 64240, ..v4(100, b"win64k ") },
1821 FrameSpec { win: 32120, ..v4(107, b"win32k") },
1822 ],
1823 vec![b"win64k ", b"win32k"]; "window_mismatch_flushes")]
1824 #[test_case(
1825 vec![
1826 FrameSpec { timestamp: Some(1), ..v4(100, b"opt1") },
1827 FrameSpec { timestamp: Some(1), ..v4(104, b"opt2") },
1828 ],
1829 vec![b"opt1opt2"]; "matching_options_coalesce")]
1830 #[test_case(
1831 vec![
1832 FrameSpec { timestamp: Some(1), ..v4(100, b"opt1") },
1833 FrameSpec { timestamp: Some(2), ..v4(104, b"mismatch") },
1834 ],
1835 vec![b"opt1", b"mismatch"]; "options_mismatch_flushes")]
1836 #[test_case(
1837 vec![
1838 FrameSpec { ttl: 64, ..v4(100, b"ttl64 ") },
1839 FrameSpec { ttl: 63, ..v4(106, b"ttl63") },
1840 ],
1841 vec![b"ttl64 ", b"ttl63"]; "ttl_mismatch_flushes")]
1842 #[test_case(
1843 vec![
1844 FrameSpec { ip: IpSpec::V4 { df: true, id: 0 }, ..v4(100, b"df1 ") },
1845 FrameSpec { ip: IpSpec::V4 { df: false, id: 0 }, ..v4(104, b"df0") },
1846 ],
1847 vec![b"df1 ", b"df0"]; "df_flag_mismatch_flushes")]
1848 #[test_case(
1849 vec![
1850 FrameSpec { dscp_and_ecn: DscpAndEcn::new(1, 0), ..v4(100, b"ecn1 ") },
1851 FrameSpec { dscp_and_ecn: DscpAndEcn::new(2, 0), ..v4(105, b"ecn2") },
1852 ],
1853 vec![b"ecn1 ", b"ecn2"]; "dscp_ecn_mismatch_flushes")]
1854 #[test_case(
1855 vec![
1856 FrameSpec { ttl: 64, ..v6(200, b"hop64 ") },
1857 FrameSpec { ttl: 63, ..v6(206, b"hop63") },
1858 ],
1859 vec![b"hop64 ", b"hop63"]; "hop_limit_mismatch_flushes")]
1860 #[test_case(
1861 vec![
1862 FrameSpec { ip: IpSpec::V6 { flowlabel: 123 }, ..v6(200, b"lbl1 ") },
1863 FrameSpec { ip: IpSpec::V6 { flowlabel: 456 }, ..v6(205, b"lbl2") },
1864 ],
1865 vec![b"lbl1 ", b"lbl2"]; "flowlabel_mismatch_flushes")]
1866 #[test_case(
1867 vec![v4(100, b"seq100 "), v4(200, b"seq200")],
1868 vec![b"seq100 ", b"seq200"]; "out_of_order_seq_flushes")]
1869 #[test_case(
1870 vec![v4(100, b"seq100 "), v4(200, b"seq200"), v4(206, b"seq206")],
1871 vec![b"seq100 ", b"seq200seq206"]; "merge_failure_starts_new_flow")]
1872 #[test_case(
1873 vec![
1874 FrameSpec { urg: true, ..v4(100, b"urg ") },
1875 FrameSpec { urg: false, ..v4(104, b"normal") },
1876 ],
1877 vec![b"urg ", b"normal"]; "urg_flag_flushes")]
1878 #[test_case(
1879 vec![
1880 FrameSpec { urg: true, ..v4(100, b"urg1") },
1881 FrameSpec { urg: true, ..v4(104, b"urg2") },
1882 ],
1883 vec![b"urg1", b"urg2"]; "consecutive_urg_flags_flush")]
1884 #[test_case(
1885 vec![
1886 FrameSpec { syn: true, ..v4(100, b"syn1") },
1887 FrameSpec { syn: true, ..v4(104, b"syn2") },
1888 ],
1889 vec![b"syn1", b"syn2"]; "syn_flag_flushes")]
1890 #[test_case(
1891 vec![
1892 FrameSpec { rst: true, ..v4(100, b"rst1") },
1893 FrameSpec { rst: true, ..v4(104, b"rst2") },
1894 ],
1895 vec![b"rst1", b"rst2"]; "rst_flag_flushes")]
1896 #[test_case(
1897 vec![
1898 FrameSpec { ip: IpSpec::V4 { df: false, id: 42 }, ..v4(100, b"hello ") },
1899 FrameSpec { ip: IpSpec::V4 { df: false, id: 42 }, ..v4(106, b"world ") },
1900 FrameSpec { ip: IpSpec::V4 { df: false, id: 42 }, ..v4(112, b"again") },
1901 ],
1902 vec![b"hello world again"]; "consistent_ipv4_ids_coalesce")]
1903 #[test_case(
1904 vec![
1905 FrameSpec { ip: IpSpec::V4 { df: false, id: 100 }, ..v4(100, b"hello ") },
1906 FrameSpec { ip: IpSpec::V4 { df: false, id: 101 }, ..v4(106, b"world ") },
1907 FrameSpec { ip: IpSpec::V4 { df: false, id: 102 }, ..v4(112, b"again") },
1908 ],
1909 vec![b"hello world again"]; "increasing_ipv4_ids_coalesce")]
1910 #[test_case(
1911 vec![
1912 FrameSpec { ip: IpSpec::V4 { df: false, id: u16::MAX }, ..v4(100, b"hello ") },
1913 FrameSpec { ip: IpSpec::V4 { df: false, id: 0 }, ..v4(106, b"world") },
1914 ],
1915 vec![b"hello world"]; "wrapping_ipv4_ids_coalesce")]
1916 #[test_case(
1917 vec![
1918 FrameSpec { ip: IpSpec::V4 { df: false, id: 100 }, ..v4(100, b"hello ") },
1919 FrameSpec { ip: IpSpec::V4 { df: false, id: 105 }, ..v4(106, b"world") },
1920 ],
1921 vec![b"hello ", b"world"]; "ipv4_id_mismatch_flushes")]
1922 #[test_case(
1923 vec![
1924 FrameSpec { ip: IpSpec::V4 { df: false, id: 100 }, ..v4(100, b"first ") },
1925 FrameSpec { ip: IpSpec::V4 { df: false, id: 100 }, ..v4(106, b"second") },
1926 FrameSpec { ip: IpSpec::V4 { df: false, id: 101 }, ..v4(112, b"third") },
1927 ],
1928 vec![b"first second", b"third"]; "ipv4_id_mode_switch_flushes")]
1929 #[test_case(
1930 vec![FrameSpec { min_body_len: 46, ..v4(100, b"abcd") }, v4(104, b"e")],
1931 vec![b"abcde"]; "padded_seed_frame_is_trimmed")]
1932 #[test_case(
1933 vec![
1934 FrameSpec { min_body_len: 46, contiguous: false, ..v4(100, b"abcd") },
1935 v4(104, b"e"),
1936 ],
1937 vec![b"abcde"]; "linearized_padded_seed_frame_is_trimmed")]
1938 #[test_case(
1939 vec![FrameSpec { min_body_len: 46, ..v4(100, b"a") }, v4(101, b"normal")],
1940 vec![b"a", b"normal"]; "unmerged_padded_frame_is_emitted_verbatim")]
1941 #[test_case(
1942 vec![v4(100, b"first "), FrameSpec { min_body_len: 46, ..v4(106, b"b") }],
1943 vec![b"first b"]; "padded_second_frame_is_trimmed")]
1944 #[test_case(
1945 vec![FrameSpec { min_body_len: 70, ..v6(200, b"c") }, v6(201, b"ipv6_normal")],
1946 vec![b"c", b"ipv6_normal"]; "unmerged_padded_ipv6_frame_is_emitted_verbatim")]
1947 #[test_case(
1948 vec![v4(100, b"short"), v4(105, b"longer_payload")],
1949 vec![b"short", b"longer_payload"]; "payload_larger_than_gso_size_flushes")]
1950 #[test_case(
1951 vec![v4(100, b"data"), v4(104, b"")],
1952 vec![b"data", b""]; "empty_payload_in_active_flow_flushes")]
1953 #[test_case(
1954 vec![v4(100, b"odd"), v4(103, b"len"), v4(106, b"tcp")],
1955 vec![b"oddlentcp"]; "coalesces_odd_length_payloads")]
1956 #[test_case(
1957 vec![
1958 FrameSpec { timestamp: Some(1), ..v4(100, b"odd") },
1959 FrameSpec { timestamp: Some(1), ..v4(103, b"opt") },
1960 ],
1961 vec![b"oddopt"]; "coalesces_odd_length_payloads_with_options")]
1962 #[test_case(
1963 vec![v4(100, b"first "), v4(106, b"sub"), v4(109, b"more")],
1964 vec![b"first sub", b"more"]; "payload_smaller_than_gso_size_terminates_flow")]
1965 fn gro_coalescing(frames: Vec<FrameSpec>, expected: Vec<&'static [u8]>) {
1966 let output = run_gro(&frames, true);
1967 assert_eq!(output.len(), expected.len(), "wrong number of frames emitted");
1968
1969 for (index, (frame, payload)) in output.iter().zip(expected.iter()).enumerate() {
1970 let parsed = parse_frame(&frame[..]);
1971 let TransportPacket::Tcp(tcp) = parsed.transport;
1972 assert_eq!(tcp.body(), *payload, "frame {index}");
1973 }
1974 }
1975
1976 #[test]
1977 fn gro_disabled_emits_every_frame_unchanged() {
1978 let frames = vec![v4(100, b"hello "), v4(106, b"world")];
1979 let output = run_gro(&frames, false);
1980 assert_eq!(output, vec![frames[0].build(), frames[1].build()]);
1981 }
1982
1983 #[test_case(true; "contiguous")]
1984 #[test_case(false; "fragmented")]
1985 fn gro_emits_unmergeable_frames_unmodified(contiguous: bool) {
1986 let frames = vec![
1989 FrameSpec { urg: true, contiguous, ..v4(100, b"hello ") },
1990 FrameSpec { urg: true, contiguous, ..v4(106, b"world") },
1991 ];
1992 let output = run_gro(&frames, true);
1993 assert_eq!(output.len(), frames.len(), "wrong number of frames emitted");
1994
1995 for (index, (frame, spec)) in output.iter().zip(frames.iter()).enumerate() {
1996 let expected = spec.build();
1997 let parsed = parse_frame(&expected);
1998 let packet_end = parsed.payload_end();
2001 assert!(frame.len() >= packet_end, "frame {index} is truncated");
2002 assert_eq!(&frame[..packet_end], &expected[..packet_end], "frame {index}");
2003 }
2004 }
2005
2006 #[test]
2007 fn gro_merges_psh_into_coalesced_frame() {
2008 let frames = vec![v4(100, b"hello "), FrameSpec { psh: true, ..v4(106, b"world") }];
2009 let output = run_gro(&frames, true);
2010 let output = assert_matches!(&output[..], [output] => output);
2011
2012 let parsed = parse_frame(output);
2013 let TransportPacket::Tcp(tcp) = parsed.transport;
2014 assert_eq!(tcp.body(), b"hello world");
2015 assert_eq!(tcp.seq_num(), 100);
2016 assert_eq!(tcp.ack_num(), Some(1000));
2017 assert_eq!(tcp.window_size(), 64240);
2018 assert!(tcp.psh());
2019 }
2020
2021 #[test]
2022 fn gro_merges_fin_into_coalesced_frame() {
2023 let frames = vec![v4(100, b"data "), FrameSpec { fin: true, ..v4(105, b"fin") }];
2024 let output = run_gro(&frames, true);
2025 let output = assert_matches!(&output[..], [output] => output);
2026
2027 let parsed = parse_frame(output);
2028 let TransportPacket::Tcp(tcp) = parsed.transport;
2029 assert_eq!(tcp.body(), b"data fin");
2030 assert!(tcp.fin());
2031 }
2032
2033 #[test]
2034 fn gro_gso_info_metadata() {
2035 let mut storage = GroBufferStorage::new();
2036
2037 let items = vec![
2039 input_item(TrackedBuffer::new(
2040 FrameSpec { ip: IpSpec::V4 { df: false, id: 1 }, ..v4(100, b"first ") }.build(),
2041 true,
2042 )),
2043 input_item(TrackedBuffer::new(
2044 FrameSpec { ip: IpSpec::V4 { df: false, id: 1 }, ..v4(106, b"second") }.build(),
2045 true,
2046 )),
2047 ];
2048 let mut gro = storage.coalesce(items.into_iter(), true);
2049 let item = gro.next().unwrap();
2050 assert_eq!(
2051 item.gso_info,
2052 Some(GsoInfo {
2053 gso_size: NonZeroU16::new(6).unwrap(),
2054 ipv4_id_mode: Some(Ipv4IdMode::Fixed),
2055 })
2056 );
2057 drop(item);
2058 assert!(gro.next().is_none());
2059 drop(gro);
2060
2061 let items = vec![
2063 input_item(TrackedBuffer::new(
2064 FrameSpec { ip: IpSpec::V4 { df: false, id: 1 }, ..v4(100, b"first ") }.build(),
2065 true,
2066 )),
2067 input_item(TrackedBuffer::new(
2068 FrameSpec { ip: IpSpec::V4 { df: false, id: 2 }, ..v4(106, b"second") }.build(),
2069 true,
2070 )),
2071 ];
2072 let mut gro = storage.coalesce(items.into_iter(), true);
2073 let item = gro.next().unwrap();
2074 assert_eq!(
2075 item.gso_info,
2076 Some(GsoInfo {
2077 gso_size: NonZeroU16::new(6).unwrap(),
2078 ipv4_id_mode: Some(Ipv4IdMode::Incrementing),
2079 })
2080 );
2081 drop(item);
2082 assert!(gro.next().is_none());
2083 drop(gro);
2084
2085 let items = vec![
2087 input_item(TrackedBuffer::new(v6(100, b"first ").build(), true)),
2088 input_item(TrackedBuffer::new(v6(106, b"second").build(), true)),
2089 ];
2090 let mut gro = storage.coalesce(items.into_iter(), true);
2091 let item = gro.next().unwrap();
2092 assert_eq!(
2093 item.gso_info,
2094 Some(GsoInfo { gso_size: NonZeroU16::new(6).unwrap(), ipv4_id_mode: None })
2095 );
2096 drop(item);
2097 assert!(gro.next().is_none());
2098 }
2099
2100 #[test]
2101 fn gro_buffers_not_dropped_until_frame_processed() {
2102 let pkt1 = v4(100, b"hello ").build();
2103 let pkt2 = v4(106, b"world").build();
2104
2105 let buffer1 = TrackedBuffer::new(pkt1, true);
2106 let buffer2 = TrackedBuffer::new(pkt2, true);
2107 let dropped1 = buffer1.dropped();
2108 let dropped2 = buffer2.dropped();
2109 let items = vec![input_item(buffer1), input_item(buffer2)];
2110
2111 let mut gro_state = GroBufferStorage::new();
2112 let mut gro = gro_state.coalesce(items.into_iter(), true);
2113
2114 let item = gro.next();
2115 assert!(item.is_some());
2116 let mut item = item.unwrap();
2117 assert!(item.buffers.slice_mut().ends_with(b"hello world"));
2118
2119 assert!(!dropped1.load(Ordering::SeqCst));
2121 assert!(!dropped2.load(Ordering::SeqCst));
2122
2123 drop(item);
2125 assert!(dropped1.load(Ordering::SeqCst));
2126 assert!(dropped2.load(Ordering::SeqCst));
2127
2128 let next_item = gro.next();
2131 assert!(next_item.is_none());
2132 }
2133
2134 #[test]
2135 fn gro_unmerged_flows_are_not_copied() {
2136 let first = v4(100, b"one").build();
2139 let second = v4(500, b"two").build();
2142 let items = vec![
2143 input_item(TrackedBuffer::new(first.clone(), true)),
2144 input_item(TrackedBuffer::new(second.clone(), true)),
2145 ];
2146
2147 let mut gro_state = GroBufferStorage::new();
2148 let mut gro = gro_state.coalesce(items.into_iter(), true);
2149
2150 for expected in [first, second] {
2151 let mut item = gro.next().expect("emits a frame");
2152 assert_matches!(&mut item.buffers, GroOutputBuffers::Contiguous(buffer) => {
2153 assert_eq!(buffer.unwrap_contiguous(), &expected[..]);
2154 });
2155 }
2156 assert!(gro.next().is_none());
2157 }
2158
2159 #[test]
2160 fn gro_iter_drop_releases_held_buffers() {
2161 let pkt1 = v4(100, b"hello ").build();
2162 let buffer1 = TrackedBuffer::new(pkt1, true);
2163 let dropped1 = buffer1.dropped();
2164 let items = vec![input_item(buffer1)];
2165
2166 let mut gro_state = GroBufferStorage::new();
2167 {
2168 let mut gro = gro_state.coalesce(items.into_iter(), true);
2169 let item = gro.next();
2170 assert!(item.is_some());
2171 assert!(!dropped1.load(Ordering::SeqCst));
2172 }
2173 assert!(dropped1.load(Ordering::SeqCst));
2175 }
2176
2177 #[test]
2178 fn gro_interleaved_non_gro_packet() {
2179 let pkt1 = v4(100, b"tcp1 ").build();
2180 let non_gro_pkt = vec![0u8; 30];
2181 let pkt2 = v4(105, b"tcp2").build();
2182
2183 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![
2184 GroInputItem {
2185 buffer: TrackedBuffer::new(pkt1, true),
2186 target: GroFrameType::Ethernet,
2187 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
2188 },
2189 GroInputItem {
2190 buffer: TrackedBuffer::new(non_gro_pkt.clone(), true),
2191 target: GroFrameType::Ethernet,
2192 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
2193 },
2194 GroInputItem {
2195 buffer: TrackedBuffer::new(pkt2, true),
2196 target: GroFrameType::Ethernet,
2197 checksum_offload: ChecksumRxOffloading::FullyOffloaded,
2198 },
2199 ];
2200
2201 let mut gro_state = GroBufferStorage::new();
2202 let mut output_frames = Vec::new();
2203 let mut gro = gro_state.coalesce(items.into_iter(), true);
2204 while let Some(mut item) = gro.next() {
2205 output_frames.push(item.buffers.slice_mut().to_vec());
2206 }
2207
2208 assert_eq!(output_frames.len(), 2);
2210 assert_eq!(output_frames[0], non_gro_pkt);
2211 assert!(output_frames[1].ends_with(b"tcp1 tcp2"));
2212 }
2213
2214 #[test]
2215 fn gro_buffers_linearized_only_once() {
2216 let buffer1 = TrackedBuffer::new(v4(100, b"first ").build(), false);
2217 let buffer2 = TrackedBuffer::new(v4(106, b"second").build(), false);
2218 let buffer3 = TrackedBuffer::new(v4(112, b"third").build(), false);
2219 let count1 = buffer1.linearized_count();
2220 let count2 = buffer2.linearized_count();
2221 let count3 = buffer3.linearized_count();
2222 let items = vec![input_item(buffer1), input_item(buffer2), input_item(buffer3)];
2223
2224 let mut gro_state = GroBufferStorage::new();
2225 let mut output_frames = Vec::new();
2226 let mut gro = gro_state.coalesce(items.into_iter(), true);
2227 while let Some(mut item) = gro.next() {
2228 output_frames.push(item.buffers.slice_mut().to_vec());
2229 }
2230
2231 assert_eq!(output_frames.len(), 1);
2232 assert!(output_frames[0].ends_with(b"first secondthird"));
2233
2234 assert_eq!(count1.load(Ordering::SeqCst), 1);
2235 assert_eq!(count2.load(Ordering::SeqCst), 1);
2236 assert_eq!(count3.load(Ordering::SeqCst), 1);
2237 }
2238
2239 #[test]
2240 fn gro_transport_offset_ignores_padding() {
2241 let pkt_padded = FrameSpec { min_body_len: 46, ..v4(100, b"a") }.build();
2245 let pkt = v4(100, b"a").build();
2246 assert!(pkt_padded.len() > pkt.len(), "packet should have been padded");
2247
2248 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2249 let parsed = GroPacket::parse(&pkt_padded, &GroFrameType::Ethernet, &mut context)
2250 .expect("should parse padded packet");
2251 assert_eq!(parsed.offsets.ip_offset + parsed.ip.total_len(), pkt.len());
2254 let padded_offset = parsed.offsets.transport_offset;
2255
2256 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2257 let parsed = GroPacket::parse(&pkt, &GroFrameType::Ethernet, &mut context)
2258 .expect("should parse unpadded packet");
2259 assert_eq!(parsed.offsets.ip_offset + parsed.ip.total_len(), pkt.len());
2260 assert_eq!(padded_offset, parsed.offsets.transport_offset);
2261
2262 assert_eq!(&pkt_padded[padded_offset..padded_offset + 2], &1234u16.to_be_bytes());
2264 assert_eq!(&pkt_padded[padded_offset + 2..padded_offset + 4], &5678u16.to_be_bytes());
2265 }
2266
2267 #[test]
2268 fn gro_buffers_dropped_when_item_dropped() {
2269 let buffer1 = TrackedBuffer::new(vec![1, 2, 3], true);
2270 let buffer2 = TrackedBuffer::new(vec![4, 5, 6], false);
2271 let dropped1 = buffer1.dropped();
2272 let dropped2 = buffer2.dropped();
2273 let items = vec![input_item(buffer1), input_item(buffer2)];
2274
2275 let mut storage = GroBufferStorage::new();
2276 let mut gro = storage.coalesce(items.into_iter(), false);
2277
2278 let mut item1 = gro.next().unwrap();
2279 assert_eq!(item1.buffers.slice_mut(), &[1, 2, 3]);
2280 assert!(!dropped1.load(Ordering::SeqCst));
2281 assert!(!dropped2.load(Ordering::SeqCst));
2282 drop(item1);
2283 assert!(dropped1.load(Ordering::SeqCst));
2284 assert!(!dropped2.load(Ordering::SeqCst));
2285
2286 let mut item2 = gro.next().unwrap();
2287 assert_eq!(item2.buffers.slice_mut(), &[4, 5, 6]);
2288 assert!(dropped1.load(Ordering::SeqCst));
2289 assert!(!dropped2.load(Ordering::SeqCst));
2290 drop(item2);
2291 assert!(dropped2.load(Ordering::SeqCst));
2292 }
2293
2294 const TEST_SRC_PORT: NonZeroU16 = NonZeroU16::new(1234).unwrap();
2295 const TEST_DST_PORT: NonZeroU16 = NonZeroU16::new(5678).unwrap();
2296 const TEST_FLOWLABEL: u32 = 0x12345;
2297 const TEST_PAYLOAD: [u8; 12] = *b"hello world!";
2298
2299 trait TestIpExt: IpExt {
2300 const SRC_IP: Self::Addr;
2301 const DST_IP: Self::Addr;
2302 fn ip_builder(proto: IpProto) -> Self::PacketBuilder<NetworkSerializationContext>;
2303 }
2304
2305 impl TestIpExt for Ipv4 {
2306 const SRC_IP: Ipv4Addr = SRC_IP_V4;
2307 const DST_IP: Ipv4Addr = DST_IP_V4;
2308 fn ip_builder(proto: IpProto) -> Ipv4PacketBuilder {
2309 Ipv4PacketBuilder::new(Self::SRC_IP, Self::DST_IP, 64, Ipv4Proto::Proto(proto))
2310 }
2311 }
2312
2313 impl TestIpExt for Ipv6 {
2314 const SRC_IP: Ipv6Addr = SRC_IP_V6;
2315 const DST_IP: Ipv6Addr = DST_IP_V6;
2316 fn ip_builder(proto: IpProto) -> Ipv6PacketBuilder {
2317 let mut ip = Ipv6PacketBuilder::new(Self::SRC_IP, Self::DST_IP, 64, proto.into());
2318 ip.flowlabel(TEST_FLOWLABEL);
2319 ip
2320 }
2321 }
2322
2323 fn ethernet_builder<I: TestIpExt>() -> EthernetFrameBuilder {
2324 EthernetFrameBuilder::new(SRC_MAC, DST_MAC, I::ETHER_TYPE, 0)
2325 }
2326
2327 fn tcp_builder<I: TestIpExt>() -> TcpSegmentBuilder<I::Addr> {
2328 TcpSegmentBuilder::new(
2329 I::SRC_IP,
2330 I::DST_IP,
2331 TEST_SRC_PORT,
2332 TEST_DST_PORT,
2333 100,
2334 Some(200),
2335 1024,
2336 )
2337 }
2338
2339 fn build_ethernet_tcp_packet<I: TestIpExt>() -> Vec<u8> {
2340 Buf::new(TEST_PAYLOAD.to_vec(), ..)
2341 .wrap_in(tcp_builder::<I>())
2342 .wrap_in(I::ip_builder(IpProto::Tcp))
2343 .wrap_in(ethernet_builder::<I>())
2344 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2345 .unwrap()
2346 .into_inner()
2347 .as_ref()
2348 .to_vec()
2349 }
2350
2351 fn build_pure_ip_tcp_packet<I: TestIpExt>() -> Vec<u8> {
2352 Buf::new(TEST_PAYLOAD.to_vec(), ..)
2353 .wrap_in(tcp_builder::<I>())
2354 .wrap_in(I::ip_builder(IpProto::Tcp))
2355 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2356 .unwrap()
2357 .into_inner()
2358 .as_ref()
2359 .to_vec()
2360 }
2361
2362 #[test_case(
2363 build_ethernet_tcp_packet::<Ipv4>(),
2364 GroFrameType::Ethernet,
2365 HeaderOffsets { ip_offset: 14, transport_offset: 34 },
2366 GroFlowId {
2367 link_layer: LinkLayerFlowId::Ethernet(EthernetFlowId {
2368 src_mac: SRC_MAC,
2369 dst_mac: DST_MAC,
2370 tag: None,
2371 }),
2372 ip: IpFlowId::Ipv4(Ipv4FlowId { src_ip: Ipv4::SRC_IP, dst_ip: Ipv4::DST_IP }),
2373 transport: TransportFlowId::Tcp(TcpFlowId {
2374 src_port: TEST_SRC_PORT,
2375 dst_port: TEST_DST_PORT,
2376 }),
2377 };
2378 "ethernet_ipv4_tcp"
2379 )]
2380 #[test_case(
2381 build_ethernet_tcp_packet::<Ipv6>(),
2382 GroFrameType::Ethernet,
2383 HeaderOffsets { ip_offset: 14, transport_offset: 54 },
2384 GroFlowId {
2385 link_layer: LinkLayerFlowId::Ethernet(EthernetFlowId {
2386 src_mac: SRC_MAC,
2387 dst_mac: DST_MAC,
2388 tag: None,
2389 }),
2390 ip: IpFlowId::Ipv6(Ipv6FlowId {
2391 src_ip: Ipv6::SRC_IP,
2392 dst_ip: Ipv6::DST_IP,
2393 flowlabel: TEST_FLOWLABEL,
2394 }),
2395 transport: TransportFlowId::Tcp(TcpFlowId {
2396 src_port: TEST_SRC_PORT,
2397 dst_port: TEST_DST_PORT,
2398 }),
2399 };
2400 "ethernet_ipv6_tcp"
2401 )]
2402 #[test_case(
2403 build_pure_ip_tcp_packet::<Ipv4>(),
2404 GroFrameType::PureIp(IpVersion::V4),
2405 HeaderOffsets { ip_offset: 0, transport_offset: 20 },
2406 GroFlowId {
2407 link_layer: LinkLayerFlowId::PureIp,
2408 ip: IpFlowId::Ipv4(Ipv4FlowId { src_ip: Ipv4::SRC_IP, dst_ip: Ipv4::DST_IP }),
2409 transport: TransportFlowId::Tcp(TcpFlowId {
2410 src_port: TEST_SRC_PORT,
2411 dst_port: TEST_DST_PORT,
2412 }),
2413 };
2414 "pure_ip_v4_tcp"
2415 )]
2416 #[test_case(
2417 build_pure_ip_tcp_packet::<Ipv6>(),
2418 GroFrameType::PureIp(IpVersion::V6),
2419 HeaderOffsets { ip_offset: 0, transport_offset: 40 },
2420 GroFlowId {
2421 link_layer: LinkLayerFlowId::PureIp,
2422 ip: IpFlowId::Ipv6(Ipv6FlowId {
2423 src_ip: Ipv6::SRC_IP,
2424 dst_ip: Ipv6::DST_IP,
2425 flowlabel: TEST_FLOWLABEL,
2426 }),
2427 transport: TransportFlowId::Tcp(TcpFlowId {
2428 src_port: TEST_SRC_PORT,
2429 dst_port: TEST_DST_PORT,
2430 }),
2431 };
2432 "pure_ip_v6_tcp"
2433 )]
2434 fn gro_packet_parse_success(
2435 packet_bytes: Vec<u8>,
2436 target: GroFrameType,
2437 expected_offsets: HeaderOffsets,
2438 expected_flow_id: GroFlowId,
2439 ) {
2440 let mut context = NetworkParsingContext::default();
2441 let parsed = GroPacket::parse(&packet_bytes, &target, &mut context)
2442 .expect("GroPacket::parse should succeed");
2443 assert_eq!(parsed.offsets, expected_offsets);
2444 assert_eq!(parsed.flow_id, expected_flow_id);
2445 }
2446
2447 #[test]
2448 fn gro_ineligible_non_ip_ethertype() {
2449 let arp =
2450 ArpPacketBuilder::new(ArpOp::Request, SRC_MAC, Ipv4::SRC_IP, DST_MAC, Ipv4::DST_IP);
2451 let arp_bytes = arp
2452 .into_serializer()
2453 .wrap_in(EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Arp, 0))
2454 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2455 .unwrap()
2456 .unwrap_b()
2457 .as_ref()
2458 .to_vec();
2459 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2460 assert!(GroPacket::parse(&arp_bytes, &GroFrameType::Ethernet, &mut context).is_none());
2461 }
2462
2463 #[test]
2464 fn gro_ineligible_ipv4_options() {
2465 let ip = Ipv4PacketBuilderWithOptions::new(
2466 Ipv4::ip_builder(IpProto::Tcp),
2467 [Ipv4Option::RouterAlert { data: 0 }],
2468 )
2469 .unwrap();
2470 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2471 .wrap_in(tcp_builder::<Ipv4>())
2472 .wrap_in(ip)
2473 .wrap_in(ethernet_builder::<Ipv4>())
2474 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2475 .unwrap()
2476 .into_inner()
2477 .as_ref()
2478 .to_vec();
2479 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2480 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2481 }
2482
2483 #[test]
2484 fn gro_ineligible_ipv4_mf_flag() {
2485 let mut ip = Ipv4::ip_builder(IpProto::Tcp);
2486 ip.mf_flag(true);
2487 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2488 .wrap_in(tcp_builder::<Ipv4>())
2489 .wrap_in(ip)
2490 .wrap_in(ethernet_builder::<Ipv4>())
2491 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2492 .unwrap()
2493 .into_inner()
2494 .as_ref()
2495 .to_vec();
2496 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2497 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2498 }
2499
2500 #[test]
2501 fn gro_ineligible_ipv4_fragment_offset() {
2502 let mut ip = Ipv4::ip_builder(IpProto::Tcp);
2503 ip.fragment_offset(FragmentOffset::new(1).unwrap());
2504 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2505 .wrap_in(tcp_builder::<Ipv4>())
2506 .wrap_in(ip)
2507 .wrap_in(ethernet_builder::<Ipv4>())
2508 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2509 .unwrap()
2510 .into_inner()
2511 .as_ref()
2512 .to_vec();
2513 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2514 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2515 }
2516
2517 #[test]
2518 fn gro_ineligible_ipv6_extension_headers() {
2519 let hbh_opt = [HopByHopOption {
2520 action: ExtensionHeaderOptionAction::SkipAndContinue,
2521 mutable: false,
2522 data: HopByHopOptionData::RouterAlert { data: 0 },
2523 }];
2524 let ip =
2525 Ipv6PacketBuilderWithHbhOptions::new(Ipv6::ip_builder(IpProto::Tcp), &hbh_opt).unwrap();
2526 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2527 .wrap_in(tcp_builder::<Ipv6>())
2528 .wrap_in(ip)
2529 .wrap_in(ethernet_builder::<Ipv6>())
2530 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2531 .unwrap()
2532 .into_inner()
2533 .as_ref()
2534 .to_vec();
2535 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2536 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2537 }
2538
2539 #[test]
2540 fn gro_ineligible_non_tcp_transport_proto() {
2541 let udp =
2542 UdpPacketBuilder::new(Ipv4::SRC_IP, Ipv4::DST_IP, Some(TEST_SRC_PORT), TEST_DST_PORT);
2543 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2544 .wrap_in(udp)
2545 .wrap_in(Ipv4::ip_builder(IpProto::Udp))
2546 .wrap_in(ethernet_builder::<Ipv4>())
2547 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2548 .unwrap()
2549 .into_inner()
2550 .as_ref()
2551 .to_vec();
2552 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2553 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2554 }
2555
2556 #[test]
2557 fn gro_corrupt_tcp_checksum() {
2558 let mut packet = build_ethernet_tcp_packet::<Ipv4>();
2559 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2560 let parsed = GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context)
2561 .expect("should parse valid packet");
2562 let checksum_offset =
2563 parsed.offsets.transport_offset + packet_formats::tcp::CHECKSUM_OFFSET;
2564 packet[checksum_offset] ^= 0xff;
2565
2566 let mut context = NetworkParsingContext::default();
2568 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_none());
2569
2570 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2572 assert!(GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context).is_some());
2573 }
2574
2575 #[test]
2576 fn gro_ineligible_pure_ip_version_mismatch() {
2577 let pure_v4 = build_pure_ip_tcp_packet::<Ipv4>();
2578 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2579 assert!(
2580 GroPacket::parse(&pure_v4, &GroFrameType::PureIp(IpVersion::V6), &mut context)
2581 .is_none()
2582 );
2583
2584 let pure_v6 = build_pure_ip_tcp_packet::<Ipv6>();
2585 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2586 assert!(
2587 GroPacket::parse(&pure_v6, &GroFrameType::PureIp(IpVersion::V4), &mut context)
2588 .is_none()
2589 );
2590 }
2591
2592 #[test]
2593 fn upgrades_csum_offload_on_verified_tcp() {
2594 let packet = build_ethernet_tcp_packet::<Ipv4>();
2595 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![GroInputItem {
2596 buffer: TrackedBuffer::new(packet, true),
2597 target: GroFrameType::Ethernet,
2598 checksum_offload: ChecksumRxOffloading::default(),
2599 }];
2600
2601 let mut storage = GroBufferStorage::new();
2602 let mut gro = GroIter::new(items.into_iter(), &mut storage, true);
2603 let item = gro.next().unwrap();
2604 assert_eq!(
2605 item.checksum_offload,
2606 ChecksumRxOffloading::Offloaded(Some(NonZeroU16::new(1).unwrap()))
2607 );
2608 }
2609
2610 #[test]
2611 fn preserves_input_csum_offload_on_unparsed_tcp() {
2612 let ip = Ipv4PacketBuilderWithOptions::new(
2615 Ipv4::ip_builder(IpProto::Tcp),
2616 [Ipv4Option::RouterAlert { data: 0 }],
2617 )
2618 .unwrap();
2619 let packet = Buf::new(TEST_PAYLOAD.to_vec(), ..)
2620 .wrap_in(tcp_builder::<Ipv4>())
2621 .wrap_in(ip)
2622 .wrap_in(ethernet_builder::<Ipv4>())
2623 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2624 .unwrap()
2625 .into_inner()
2626 .as_ref()
2627 .to_vec();
2628
2629 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![GroInputItem {
2630 buffer: TrackedBuffer::new(packet, true),
2631 target: GroFrameType::Ethernet,
2632 checksum_offload: ChecksumRxOffloading::default(),
2633 }];
2634
2635 let mut storage = GroBufferStorage::new();
2636 let mut gro = GroIter::new(items.into_iter(), &mut storage, true);
2637 let item = gro.next().unwrap();
2638 assert_eq!(item.checksum_offload, ChecksumRxOffloading::default());
2639 }
2640
2641 #[test]
2642 fn preserves_input_csum_offload_on_corrupt_csum() {
2643 let mut packet = build_ethernet_tcp_packet::<Ipv4>();
2644 let mut context = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
2645 let parsed = GroPacket::parse(&packet, &GroFrameType::Ethernet, &mut context)
2646 .expect("should parse valid packet");
2647 let checksum_offset =
2648 parsed.offsets.transport_offset + packet_formats::tcp::CHECKSUM_OFFSET;
2649 packet[checksum_offset] ^= 0xff;
2650
2651 let items: Vec<GroInputItem<TrackedBuffer, GroFrameType>> = vec![GroInputItem {
2652 buffer: TrackedBuffer::new(packet, true),
2653 target: GroFrameType::Ethernet,
2654 checksum_offload: ChecksumRxOffloading::default(),
2655 }];
2656
2657 let mut storage = GroBufferStorage::new();
2658 let mut gro = GroIter::new(items.into_iter(), &mut storage, true);
2659 let item = gro.next().unwrap();
2660 assert_eq!(item.checksum_offload, ChecksumRxOffloading::default());
2661 }
2662}