1use crate::inspect::{Inspectable, Inspector};
8use bitflags::bitflags;
9use core::num::NonZeroU16;
10use net_types::ip::IpInvariant;
11use packet::{
12 DynamicPartialSerializer, DynamicSerializer, PacketBuilder, PacketConstraints,
13 PartialSerializer, SerializationContext, Serializer,
14};
15use packet_formats::TransportChecksumAction;
16use packet_formats::ethernet::{EthernetEnvelope, EthernetSerializationContext};
17use packet_formats::icmp::{IcmpEnvelope, IcmpSerializationContext};
18use packet_formats::ip::{IpEnvelope, IpExt, IpSerializationContext};
19use packet_formats::tcp::{TcpEnvelope, TcpParseContext, TcpSerializationContext};
20use packet_formats::udp::{UdpEnvelope, UdpParseContext, UdpSerializationContext};
21use static_assertions::const_assert;
22
23pub trait NetworkSerializer: Serializer<NetworkSerializationContext> {}
25impl<S: Serializer<NetworkSerializationContext>> NetworkSerializer for S {}
26
27pub trait NetworkPartialSerializer: PartialSerializer<NetworkSerializationContext> {}
29impl<S: PartialSerializer<NetworkSerializationContext>> NetworkPartialSerializer for S {}
30
31pub trait DynamicNetworkSerializer: DynamicSerializer<NetworkSerializationContext> {}
33impl<S: DynamicSerializer<NetworkSerializationContext>> DynamicNetworkSerializer for S {}
34
35pub trait DynamicNetworkPartialSerializer:
37 DynamicPartialSerializer<NetworkSerializationContext>
38{
39}
40impl<S: DynamicPartialSerializer<NetworkSerializationContext>> DynamicNetworkPartialSerializer
41 for S
42{
43}
44
45#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
47pub enum OffloadableProtocol {
48 #[default]
50 None,
51 NotOffloadable,
53 Tcp,
55 Udp,
57 Ipv4,
59 Ipv6,
61 Ethernet,
63}
64
65bitflags! {
66 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
68 pub struct OffloadableProtocols: u8 {
69 const NOT_OFFLOADABLE = 1 << 0;
71 const TCP = 1 << 1;
73 const UDP = 1 << 2;
75 const IPV4 = 1 << 3;
77 const IPV6 = 1 << 4;
79 const ETHERNET = 1 << 5;
81 }
82}
83
84impl From<OffloadableProtocol> for OffloadableProtocols {
85 fn from(p: OffloadableProtocol) -> Self {
86 match p {
87 OffloadableProtocol::None => Self::empty(),
88 OffloadableProtocol::NotOffloadable => Self::NOT_OFFLOADABLE,
89 OffloadableProtocol::Tcp => Self::TCP,
90 OffloadableProtocol::Udp => Self::UDP,
91 OffloadableProtocol::Ipv4 => Self::IPV4,
92 OffloadableProtocol::Ipv6 => Self::IPV6,
93 OffloadableProtocol::Ethernet => Self::ETHERNET,
94 }
95 }
96}
97
98bitflags! {
99 #[derive(Clone, Debug, Default, Eq, PartialEq)]
101 pub struct ProtocolSpecificOffloadSpec: u8 {
102 const ETH_IPV4_UDP = 1 << 0;
104 const ETH_IPV4_TCP = 1 << 1;
106 const ETH_IPV6_UDP = 1 << 2;
109 const ETH_IPV6_TCP = 1 << 3;
112 }
113}
114
115impl ProtocolSpecificOffloadSpec {
116 pub fn tcp_or_udp_over_ipv4() -> Self {
121 Self::ETH_IPV4_UDP | Self::ETH_IPV4_TCP
122 }
123
124 pub fn tcp_or_udp_over_ipv6() -> Self {
129 Self::ETH_IPV6_UDP | Self::ETH_IPV6_TCP
130 }
131
132 fn or(self, other: Self) -> Self {
135 self | other
136 }
137
138 fn matches(&self, current: OffloadableProtocols) -> bool {
140 use OffloadableProtocols as P;
141 match current {
142 f if f == (P::ETHERNET | P::IPV4 | P::UDP) => self.contains(Self::ETH_IPV4_UDP),
143 f if f == (P::ETHERNET | P::IPV4 | P::TCP) => self.contains(Self::ETH_IPV4_TCP),
144 f if f == (P::ETHERNET | P::IPV6 | P::UDP) => self.contains(Self::ETH_IPV6_UDP),
145 f if f == (P::ETHERNET | P::IPV6 | P::TCP) => self.contains(Self::ETH_IPV6_TCP),
146 _ => false,
147 }
148 }
149}
150
151#[derive(Clone, Debug, Default, Eq, PartialEq)]
153struct GenericOffloadSpec;
154
155#[derive(Clone, Debug, Default, Eq, PartialEq)]
157pub struct ChecksumOffloadSpec {
158 protocol_specific: Option<ProtocolSpecificOffloadSpec>,
160 generic: Option<GenericOffloadSpec>,
162}
163
164impl ChecksumOffloadSpec {
165 pub fn none() -> Self {
168 Self { protocol_specific: None, generic: None }
169 }
170
171 pub fn protocol_specific(spec: ProtocolSpecificOffloadSpec) -> Self {
174 Self { protocol_specific: Some(spec), generic: None }
175 }
176
177 pub fn generic() -> Self {
180 Self { protocol_specific: None, generic: Some(GenericOffloadSpec::default()) }
181 }
182
183 pub fn any<I: IntoIterator<Item = Self>>(specs: I) -> Self {
185 specs.into_iter().fold(Self::none(), |acc, spec| Self {
186 protocol_specific: match (acc.protocol_specific, spec.protocol_specific) {
187 (Some(acc), Some(spec)) => Some(acc.or(spec)),
188 (Some(acc), None) => Some(acc),
189 (None, Some(spec)) => Some(spec),
190 (None, None) => None,
191 },
192 generic: acc.generic.or(spec.generic),
193 })
194 }
195}
196
197impl Inspectable for ChecksumOffloadSpec {
198 fn record<I: Inspector>(&self, inspector: &mut I) {
199 inspector.record_bool("TxChecksumOffloadGeneric", self.generic.is_some());
200 if let Some(protocol_specific) = &self.protocol_specific {
201 inspector.record_child("TxChecksumOffloadProtocolSpecific", |inspector| {
202 inspector.record_bool(
203 "EthIpv4Udp",
204 protocol_specific.contains(ProtocolSpecificOffloadSpec::ETH_IPV4_UDP),
205 );
206 inspector.record_bool(
207 "EthIpv4Tcp",
208 protocol_specific.contains(ProtocolSpecificOffloadSpec::ETH_IPV4_TCP),
209 );
210 inspector.record_bool(
211 "EthIpv6Udp",
212 protocol_specific.contains(ProtocolSpecificOffloadSpec::ETH_IPV6_UDP),
213 );
214 inspector.record_bool(
215 "EthIpv6Tcp",
216 protocol_specific.contains(ProtocolSpecificOffloadSpec::ETH_IPV6_TCP),
217 );
218 });
219 }
220 }
221}
222
223#[derive(Clone, Debug, Eq, PartialEq)]
224struct ProtocolStackInfo {
225 header_offset: Option<u16>,
228 protocols: OffloadableProtocols,
230}
231
232impl Default for ProtocolStackInfo {
233 fn default() -> Self {
234 Self { header_offset: Some(0), protocols: OffloadableProtocols::empty() }
235 }
236}
237
238#[derive(Clone, Debug, Default, Eq, PartialEq)]
239struct ChecksumOffloadState {
240 spec: ChecksumOffloadSpec,
241 stack_info: ProtocolStackInfo,
242}
243
244impl ChecksumOffloadState {
245 fn new(spec: ChecksumOffloadSpec) -> Self {
246 Self { spec, stack_info: Default::default() }
247 }
248
249 fn update(
255 &mut self,
256 protocol: OffloadableProtocol,
257 constraints: &PacketConstraints,
258 ) -> ProtocolStackInfo {
259 let previous = self.stack_info.clone();
260
261 let ProtocolStackInfo { header_offset, protocols } = &mut self.stack_info;
262
263 *header_offset = header_offset.and_then(|_| constraints.header_len().try_into().ok());
267
268 if protocol == OffloadableProtocol::None {
269 return previous;
270 }
271 let protocol = protocol.into();
272 if protocols.contains(protocol) {
273 protocols.insert(OffloadableProtocol::NotOffloadable.into());
278 } else {
279 protocols.insert(protocol);
280 }
281
282 previous
283 }
284
285 fn restore(&mut self, previous: ProtocolStackInfo) {
287 self.stack_info = previous;
288 }
289
290 fn try_offload(&self, csum_offset: u16) -> Option<ChecksumOffloadResult> {
291 let ProtocolStackInfo { header_offset, protocols } = self.stack_info;
292
293 if let Some(start) = self.spec.generic.as_ref().and(header_offset) {
297 Some(ChecksumOffloadResult::Generic(PartialChecksum { start, offset: csum_offset }))
298 } else if self
299 .spec
300 .protocol_specific
301 .as_ref()
302 .map(|spec| spec.matches(protocols))
303 .unwrap_or(false)
304 {
305 Some(ChecksumOffloadResult::ProtocolSpecific(protocols))
306 } else {
307 None
308 }
309 }
310}
311
312#[derive(Clone, Debug, Default, Eq, PartialEq)]
317pub struct PartialChecksum {
318 pub start: u16,
321 pub offset: u16,
323}
324
325#[derive(Clone, Debug, Eq, PartialEq)]
327pub enum ChecksumOffloadResult {
328 ProtocolSpecific(OffloadableProtocols),
330 Generic(PartialChecksum),
332}
333
334#[derive(Clone, Debug, Default, Eq, PartialEq)]
336pub struct NetworkSerializationContext {
337 csum_offload_state: ChecksumOffloadState,
338 csum_offload_result: Option<ChecksumOffloadResult>,
343}
344
345impl NetworkSerializationContext {
346 pub fn new(csum_offload_spec: ChecksumOffloadSpec) -> Self {
348 Self {
349 csum_offload_state: ChecksumOffloadState::new(csum_offload_spec),
350 csum_offload_result: None,
351 }
352 }
353
354 fn transport_checksum_action(&mut self, csum_offset: u16) -> TransportChecksumAction {
355 if self.csum_offload_result.is_some() {
356 TransportChecksumAction::ComputeFull
360 } else {
361 self.csum_offload_result = self.csum_offload_state.try_offload(csum_offset);
362 self.csum_offload_result
363 .as_ref()
364 .map(|_| TransportChecksumAction::ComputePartial)
365 .unwrap_or(TransportChecksumAction::ComputeFull)
366 }
367 }
368
369 pub fn csum_offload_result(self) -> Option<ChecksumOffloadResult> {
372 self.csum_offload_result
373 }
374}
375
376impl SerializationContext for NetworkSerializationContext {
377 type ContextState = OffloadableProtocol;
378
379 fn serialize_nested<O: PacketBuilder<Self>, R>(
380 &mut self,
381 outer: &O,
382 constraints: PacketConstraints,
383 serialize_fn: impl FnOnce(&mut Self, PacketConstraints) -> R,
384 ) -> R {
385 let previous_state = self.csum_offload_state.update(outer.context_state(), &constraints);
386 let result = serialize_fn(self, constraints);
387 self.csum_offload_state.restore(previous_state);
388 result
389 }
390}
391
392impl EthernetSerializationContext for NetworkSerializationContext {
393 fn envelope_to_state(_envelope: EthernetEnvelope) -> Self::ContextState {
394 OffloadableProtocol::Ethernet
395 }
396}
397
398impl<I: IpExt> IpSerializationContext<I> for NetworkSerializationContext {
399 fn envelope_to_state(envelope: IpEnvelope<I>) -> Self::ContextState {
400 I::map_ip_in(
401 IpInvariant(envelope),
402 |IpInvariant(envelope)| {
403 if envelope.has_options {
404 OffloadableProtocol::NotOffloadable
405 } else {
406 OffloadableProtocol::Ipv4
407 }
408 },
409 |IpInvariant(envelope)| {
410 if envelope.has_options {
411 OffloadableProtocol::NotOffloadable
412 } else {
413 OffloadableProtocol::Ipv6
414 }
415 },
416 )
417 }
418}
419
420impl IcmpSerializationContext for NetworkSerializationContext {
421 fn envelope_to_state(_envelope: IcmpEnvelope) -> Self::ContextState {
422 OffloadableProtocol::NotOffloadable
423 }
424}
425
426const_assert!(packet_formats::udp::CHECKSUM_OFFSET <= u16::MAX as usize);
427const UDP_CHECKSUM_OFFSET: u16 = packet_formats::udp::CHECKSUM_OFFSET as u16;
428
429impl UdpSerializationContext for NetworkSerializationContext {
430 fn envelope_to_state(_envelope: UdpEnvelope) -> Self::ContextState {
431 OffloadableProtocol::Udp
432 }
433
434 fn checksum_action(&mut self) -> TransportChecksumAction {
435 self.transport_checksum_action(UDP_CHECKSUM_OFFSET)
436 }
437}
438
439const_assert!(packet_formats::tcp::CHECKSUM_OFFSET <= u16::MAX as usize);
440const TCP_CHECKSUM_OFFSET: u16 = packet_formats::tcp::CHECKSUM_OFFSET as u16;
441
442impl TcpSerializationContext for NetworkSerializationContext {
443 fn envelope_to_state(_envelope: TcpEnvelope) -> Self::ContextState {
444 OffloadableProtocol::Tcp
445 }
446
447 fn checksum_action(&mut self) -> TransportChecksumAction {
448 self.transport_checksum_action(TCP_CHECKSUM_OFFSET)
449 }
450}
451
452#[derive(Clone, Copy, Debug, Eq, PartialEq)]
455pub enum ChecksumRxOffloading {
456 Offloaded(Option<NonZeroU16>),
461 FullyOffloaded,
465}
466
467impl Default for ChecksumRxOffloading {
468 fn default() -> Self {
469 ChecksumRxOffloading::Offloaded(None)
470 }
471}
472
473#[derive(Clone, Debug, Default, Eq, PartialEq)]
475pub struct NetworkParsingContext {
476 checksum_offload: ChecksumRxOffloading,
478 verified_checksum_count: u16,
479}
480
481impl NetworkParsingContext {
482 pub fn new(checksum_offload: ChecksumRxOffloading) -> Self {
484 NetworkParsingContext { checksum_offload, verified_checksum_count: 0 }
485 }
486
487 pub fn checksum_offload(&self) -> ChecksumRxOffloading {
489 self.checksum_offload
490 }
491
492 pub fn verified_checksum_count(&self) -> u16 {
498 self.verified_checksum_count
499 }
500
501 fn verify_checksum_if_needed_inner<E>(
507 &mut self,
508 f: impl FnOnce() -> Result<(), E>,
509 ) -> Result<(), E> {
510 match self.checksum_offload {
511 ChecksumRxOffloading::FullyOffloaded => Ok(()),
512 ChecksumRxOffloading::Offloaded(Some(n)) => {
513 self.checksum_offload =
514 ChecksumRxOffloading::Offloaded(NonZeroU16::new(n.get() - 1));
515 Ok(())
516 }
517 ChecksumRxOffloading::Offloaded(None) => match f() {
518 Ok(()) => {
519 self.verified_checksum_count = self.verified_checksum_count.saturating_add(1);
520 Ok(())
521 }
522 Err(e) => Err(e),
523 },
524 }
525 }
526}
527
528impl UdpParseContext for &mut NetworkParsingContext {
529 fn verify_checksum_if_needed<E>(&mut self, f: impl FnOnce() -> Result<(), E>) -> Result<(), E> {
530 self.verify_checksum_if_needed_inner(f)
531 }
532}
533
534impl TcpParseContext for &mut NetworkParsingContext {
535 fn verify_checksum_if_needed<E>(&mut self, f: impl FnOnce() -> Result<(), E>) -> Result<(), E> {
536 self.verify_checksum_if_needed_inner(f)
537 }
538}
539
540#[cfg(test)]
541mod tests {
542 use super::*;
543 use alloc::vec::Vec;
544 use assert_matches::assert_matches;
545 use core::num::NonZeroU16;
546 use net_types::ethernet::Mac;
547 use net_types::ip::{IpAddress, IpVersionMarker, Ipv4, Ipv4Addr, Ipv6Addr};
548 use packet::{
549 Buf, FragmentedBytesMut, FromRaw, NestablePacketBuilder, NestableSerializer, PacketBuilder,
550 PacketConstraints, ParseBuffer, SerializeTarget, Serializer,
551 };
552 use packet_formats::error::ParseError;
553 use packet_formats::ethernet::{
554 EtherType, EthernetFrame, EthernetFrameBuilder, EthernetFrameLengthCheck,
555 };
556 use packet_formats::ip::{IpPacket, IpProto, Ipv4Proto, Ipv6Proto};
557 use packet_formats::ipv4::options::Ipv4Option;
558 use packet_formats::ipv4::{Ipv4Packet, Ipv4PacketBuilder, Ipv4PacketBuilderWithOptions};
559 use packet_formats::ipv6::ext_hdrs::{
560 ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData,
561 };
562 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
563 use packet_formats::tcp::TcpSegmentBuilder;
564 use packet_formats::udp::{
565 HEADER_BYTES as UDP_HEADER_BYTES, UdpPacket, UdpPacketBuilder, UdpPacketRaw, UdpParseArgs,
566 };
567 use test_case::test_case;
568
569 const SRC_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
570 const DST_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
571 const SRC_IP_V4: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 1]);
572 const DST_IP_V4: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 2]);
573 const SRC_IP_V6: Ipv6Addr = Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 1]);
574 const DST_IP_V6: Ipv6Addr = Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 2]);
575 const SRC_PORT: u16 = 1234;
576 const DST_PORT: u16 = 5678;
577
578 #[test_case(
579 UdpPacketBuilder::new(
580 SRC_IP_V4,
581 DST_IP_V4,
582 NonZeroU16::new(SRC_PORT),
583 NonZeroU16::new(DST_PORT).unwrap(),
584 ),
585 IpProto::Udp ; "udp"
586 )]
587 #[test_case(
588 TcpSegmentBuilder::new(
589 SRC_IP_V4,
590 DST_IP_V4,
591 NonZeroU16::new(SRC_PORT).unwrap(),
592 NonZeroU16::new(DST_PORT).unwrap(),
593 123,
594 None,
595 1000,
596 ),
597 IpProto::Tcp ; "tcp"
598 )]
599 fn ipv4_no_options_csum_offload(
600 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
601 ip_proto: IpProto,
602 ) {
603 let mut payload = [0u8; 100];
604 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
605 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
606
607 let serializer =
608 Buf::new(&mut payload[..], ..).wrap_in(transport_builder).wrap_in(ip).wrap_in(ethernet);
609
610 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
611 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
612 ));
613 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
614
615 let expected_protocol = match ip_proto {
616 IpProto::Udp => OffloadableProtocol::Udp,
617 IpProto::Tcp => OffloadableProtocol::Tcp,
618 _ => panic!("invalid proto"),
619 };
620 assert_eq!(
621 context.csum_offload_result(),
622 Some(ChecksumOffloadResult::ProtocolSpecific(
623 OffloadableProtocols::ETHERNET
624 | OffloadableProtocols::IPV4
625 | expected_protocol.into()
626 ))
627 );
628 }
629
630 #[test_case(
631 UdpPacketBuilder::new(
632 SRC_IP_V4,
633 DST_IP_V4,
634 NonZeroU16::new(SRC_PORT),
635 NonZeroU16::new(DST_PORT).unwrap(),
636 ),
637 IpProto::Udp ; "udp"
638 )]
639 #[test_case(
640 TcpSegmentBuilder::new(
641 SRC_IP_V4,
642 DST_IP_V4,
643 NonZeroU16::new(SRC_PORT).unwrap(),
644 NonZeroU16::new(DST_PORT).unwrap(),
645 123,
646 None,
647 1000,
648 ),
649 IpProto::Tcp ; "tcp"
650 )]
651 fn ipv4_with_options_no_csum_offload(
652 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
653 ip_proto: IpProto,
654 ) {
655 let mut payload = [0u8; 100];
656 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
657 let options = [Ipv4Option::RouterAlert { data: 0 }];
658 let ip_with_options = Ipv4PacketBuilderWithOptions::new(ip, &options).unwrap();
659 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
660
661 let serializer = Buf::new(&mut payload[..], ..)
662 .wrap_in(transport_builder)
663 .wrap_in(ip_with_options)
664 .wrap_in(ethernet);
665
666 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
667 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
668 ));
669 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
670
671 assert_eq!(context.csum_offload_result(), None);
672 }
673
674 #[test_case(
675 UdpPacketBuilder::new(
676 SRC_IP_V6,
677 DST_IP_V6,
678 NonZeroU16::new(SRC_PORT),
679 NonZeroU16::new(DST_PORT).unwrap(),
680 ),
681 IpProto::Udp ; "udp"
682 )]
683 #[test_case(
684 TcpSegmentBuilder::new(
685 SRC_IP_V6,
686 DST_IP_V6,
687 NonZeroU16::new(SRC_PORT).unwrap(),
688 NonZeroU16::new(DST_PORT).unwrap(),
689 123,
690 None,
691 1000,
692 ),
693 IpProto::Tcp ; "tcp"
694 )]
695 fn ipv6_no_extensions_csum_offload(
696 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
697 ip_proto: IpProto,
698 ) {
699 let mut payload = [0u8; 100];
700 let ip = Ipv6PacketBuilder::new(SRC_IP_V6, DST_IP_V6, 64, Ipv6Proto::Proto(ip_proto));
701 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
702
703 let serializer =
704 Buf::new(&mut payload[..], ..).wrap_in(transport_builder).wrap_in(ip).wrap_in(ethernet);
705
706 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
707 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv6(),
708 ));
709 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
710
711 let expected_protocol = match ip_proto {
712 IpProto::Udp => OffloadableProtocol::Udp,
713 IpProto::Tcp => OffloadableProtocol::Tcp,
714 _ => panic!("invalid proto"),
715 };
716 assert_eq!(
717 context.csum_offload_result(),
718 Some(ChecksumOffloadResult::ProtocolSpecific(
719 OffloadableProtocols::ETHERNET
720 | OffloadableProtocols::IPV6
721 | expected_protocol.into()
722 ))
723 );
724 }
725
726 #[test_case(
727 UdpPacketBuilder::new(
728 SRC_IP_V6,
729 DST_IP_V6,
730 NonZeroU16::new(SRC_PORT),
731 NonZeroU16::new(DST_PORT).unwrap(),
732 ),
733 IpProto::Udp ; "udp"
734 )]
735 #[test_case(
736 TcpSegmentBuilder::new(
737 SRC_IP_V6,
738 DST_IP_V6,
739 NonZeroU16::new(SRC_PORT).unwrap(),
740 NonZeroU16::new(DST_PORT).unwrap(),
741 123,
742 None,
743 1000,
744 ),
745 IpProto::Tcp ; "tcp"
746 )]
747 fn ipv6_with_extension_hdrs_no_csum_offload(
748 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
749 ip_proto: IpProto,
750 ) {
751 let mut payload = [0u8; 100];
752 let ip = Ipv6PacketBuilder::new(SRC_IP_V6, DST_IP_V6, 64, Ipv6Proto::Proto(ip_proto));
753 let options = [HopByHopOption {
754 action: ExtensionHeaderOptionAction::SkipAndContinue,
755 mutable: false,
756 data: HopByHopOptionData::RouterAlert { data: 0 },
757 }];
758 let ip_with_options = Ipv6PacketBuilderWithHbhOptions::new(ip, options).unwrap();
759 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
760
761 let serializer = Buf::new(&mut payload[..], ..)
762 .wrap_in(transport_builder)
763 .wrap_in(ip_with_options)
764 .wrap_in(ethernet);
765
766 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
767 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv6(),
768 ));
769 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
770
771 assert_eq!(context.csum_offload_result(), None);
772 }
773
774 #[test]
775 fn generic_csum_offload_preferred_over_protocol_specific() {
776 let mut payload = [0u8; 100];
777 let udp = UdpPacketBuilder::new(
778 SRC_IP_V4,
779 DST_IP_V4,
780 NonZeroU16::new(SRC_PORT),
781 NonZeroU16::new(DST_PORT).unwrap(),
782 );
783 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
784 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
785
786 let serializer = Buf::new(&mut payload[..], ..).wrap_in(udp).wrap_in(ip).wrap_in(ethernet);
787
788 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::any([
789 ChecksumOffloadSpec::generic(),
790 ChecksumOffloadSpec::protocol_specific(
791 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
792 ),
793 ]));
794 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
795
796 assert_matches!(context.csum_offload_result(), Some(ChecksumOffloadResult::Generic(_)));
798 }
799
800 #[derive(Debug)]
801 struct TestPacketBuilder {
802 header_len: usize,
803 }
804 impl NestablePacketBuilder for TestPacketBuilder {
805 fn constraints(&self) -> PacketConstraints {
806 PacketConstraints::new(self.header_len, 0, 0, usize::MAX)
807 }
808 }
809 impl PacketBuilder<NetworkSerializationContext> for TestPacketBuilder {
810 fn context_state(&self) -> OffloadableProtocol {
811 OffloadableProtocol::NotOffloadable
812 }
813 fn serialize(
814 &self,
815 _context: &mut NetworkSerializationContext,
816 _target: &mut SerializeTarget<'_>,
817 _body: FragmentedBytesMut<'_, '_>,
818 ) {
819 }
821 }
822
823 #[test_case(
824 UdpPacketBuilder::new(
825 SRC_IP_V4,
826 DST_IP_V4,
827 NonZeroU16::new(SRC_PORT),
828 NonZeroU16::new(DST_PORT).unwrap(),
829 ),
830 IpProto::Udp,
831 UDP_CHECKSUM_OFFSET ; "udp"
832 )]
833 #[test_case(
834 TcpSegmentBuilder::new(
835 SRC_IP_V4,
836 DST_IP_V4,
837 NonZeroU16::new(SRC_PORT).unwrap(),
838 NonZeroU16::new(DST_PORT).unwrap(),
839 123,
840 None,
841 1000,
842 ),
843 IpProto::Tcp,
844 TCP_CHECKSUM_OFFSET ; "tcp"
845 )]
846 fn generic_csum_offload(
847 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
848 ip_proto: IpProto,
849 expected_csum_offset: u16,
850 ) {
851 let mut payload = [0u8; 100];
852 let test_packet = TestPacketBuilder { header_len: 10 };
853 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
854 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
855
856 let serializer = Buf::new(&mut payload[..], ..)
858 .wrap_in(test_packet)
862 .wrap_in(transport_builder)
863 .wrap_in(ip)
864 .wrap_in(ethernet);
865
866 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
867 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
868
869 assert_eq!(
871 context.csum_offload_result(),
872 Some(ChecksumOffloadResult::Generic(PartialChecksum {
873 start: 34,
874 offset: expected_csum_offset
875 }))
876 );
877 }
878
879 #[test]
880 fn generic_csum_offload_disabled_on_overflow() {
881 let mut payload = [0u8; 100];
882 let test_packet = TestPacketBuilder { header_len: 66000 };
884 let udp = UdpPacketBuilder::new(
885 SRC_IP_V4,
886 DST_IP_V4,
887 NonZeroU16::new(SRC_PORT),
888 NonZeroU16::new(DST_PORT).unwrap(),
889 );
890 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
891 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
892
893 let serializer = Buf::new(&mut payload[..], ..)
897 .wrap_in(udp)
898 .wrap_in(ip)
899 .wrap_in(test_packet)
900 .wrap_in(ethernet);
901
902 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
903 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
904
905 assert_eq!(context.csum_offload_result(), None);
907 }
908
909 #[test]
910 fn generic_csum_offload_enabled_with_inner_overflow() {
911 let mut payload = [0u8; 100];
912 let test_packet = TestPacketBuilder { header_len: 66000 };
914 let udp = UdpPacketBuilder::new(
915 SRC_IP_V6,
916 DST_IP_V6,
917 NonZeroU16::new(SRC_PORT),
918 NonZeroU16::new(DST_PORT).unwrap(),
919 );
920 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
921
922 let serializer =
925 Buf::new(&mut payload[..], ..).wrap_in(test_packet).wrap_in(udp).wrap_in(ethernet);
926
927 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
928 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
929
930 assert_eq!(
933 context.csum_offload_result(),
934 Some(ChecksumOffloadResult::Generic(PartialChecksum {
935 start: 14, offset: UDP_CHECKSUM_OFFSET
937 }))
938 );
939 }
940
941 #[test]
942 fn protocol_specific_csum_offload_with_size_limit() {
943 let mut payload = [0u8; 100];
944 let udp = UdpPacketBuilder::new(
945 SRC_IP_V4,
946 DST_IP_V4,
947 NonZeroU16::new(SRC_PORT),
948 NonZeroU16::new(DST_PORT).unwrap(),
949 );
950 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
951 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
952
953 let serializer = Buf::new(&mut payload[..], ..)
955 .wrap_in(udp)
956 .with_size_limit(1000)
960 .wrap_in(ip)
961 .wrap_in(ethernet);
962
963 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
964 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
965 ));
966 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
967
968 assert_eq!(
969 context.csum_offload_result(),
970 Some(ChecksumOffloadResult::ProtocolSpecific(
971 OffloadableProtocols::ETHERNET
972 | OffloadableProtocols::IPV4
973 | OffloadableProtocols::UDP
974 ))
975 );
976 }
977
978 #[test]
979 fn protocol_specific_csum_offload_duplicate_protocol() {
980 let mut payload = [0u8; 100];
981 let udp_inner = UdpPacketBuilder::new(
982 SRC_IP_V4,
983 DST_IP_V4,
984 NonZeroU16::new(SRC_PORT),
985 NonZeroU16::new(DST_PORT).unwrap(),
986 );
987 let ip_inner =
988 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
989 let udp_outer = UdpPacketBuilder::new(
990 SRC_IP_V4,
991 DST_IP_V4,
992 NonZeroU16::new(SRC_PORT),
993 NonZeroU16::new(DST_PORT).unwrap(),
994 );
995 let ip_outer =
996 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
997 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
998
999 let serializer = Buf::new(&mut payload[..], ..)
1001 .wrap_in(udp_inner)
1002 .wrap_in(ip_inner)
1003 .wrap_in(udp_outer)
1004 .wrap_in(ip_outer)
1005 .wrap_in(ethernet);
1006
1007 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
1008 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
1009 ));
1010 let buf =
1011 serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
1012
1013 assert_eq!(
1016 context.csum_offload_result(),
1017 Some(ChecksumOffloadResult::ProtocolSpecific(
1018 OffloadableProtocols::ETHERNET
1019 | OffloadableProtocols::IPV4
1020 | OffloadableProtocols::UDP
1021 ))
1022 );
1023
1024 let mut buf_ref = buf.as_ref();
1025 let eth = buf_ref
1026 .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
1027 .expect("ethernet parse should succeed");
1028 let mut body = eth.body();
1029 let ip_out = body.parse::<Ipv4Packet<_>>().expect("outer ipv4 parse should succeed");
1030
1031 let mut outer_udp_bytes = ip_out.body();
1033 let udp_out_raw = outer_udp_bytes
1034 .parse_with::<_, UdpPacketRaw<_>>(IpVersionMarker::<Ipv4>::default())
1035 .expect("outer udp parse should succeed");
1036
1037 assert_eq!(
1040 UdpPacket::try_from_raw_with(
1041 udp_out_raw,
1042 UdpParseArgs::new(ip_out.src_ip(), ip_out.dst_ip())
1043 )
1044 .err(),
1045 Some(ParseError::Checksum),
1046 );
1047
1048 let mut inner_ip_bytes = &ip_out.body()[UDP_HEADER_BYTES..];
1049 let ip_in =
1050 inner_ip_bytes.parse::<Ipv4Packet<_>>().expect("inner ipv4 parse should succeed");
1051 let mut body = ip_in.body();
1052
1053 let _udp_in = body
1056 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(ip_in.src_ip(), ip_in.dst_ip()))
1057 .expect("inner udp parse should succeed");
1058 }
1059
1060 #[test]
1061 fn generic_csum_offload_duplicate_protocol() {
1062 let mut payload = [0u8; 100];
1063 let udp_inner = UdpPacketBuilder::new(
1064 SRC_IP_V4,
1065 DST_IP_V4,
1066 NonZeroU16::new(SRC_PORT),
1067 NonZeroU16::new(DST_PORT).unwrap(),
1068 );
1069 let ip_inner =
1070 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
1071 let udp_outer = UdpPacketBuilder::new(
1072 SRC_IP_V4,
1073 DST_IP_V4,
1074 NonZeroU16::new(SRC_PORT),
1075 NonZeroU16::new(DST_PORT).unwrap(),
1076 );
1077 let ip_outer =
1078 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
1079 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
1080
1081 let serializer = Buf::new(&mut payload[..], ..)
1083 .wrap_in(udp_inner)
1084 .wrap_in(ip_inner)
1085 .wrap_in(udp_outer)
1086 .wrap_in(ip_outer)
1087 .wrap_in(ethernet);
1088
1089 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
1090 let buf =
1091 serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
1092
1093 assert_eq!(
1096 context.csum_offload_result(),
1097 Some(ChecksumOffloadResult::Generic(PartialChecksum {
1098 start: 62,
1099 offset: UDP_CHECKSUM_OFFSET
1100 }))
1101 );
1102
1103 let mut buf_ref = buf.as_ref();
1104 let eth = buf_ref
1105 .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
1106 .expect("ethernet parse should succeed");
1107 let mut body = eth.body();
1108 let ip_out = body.parse::<Ipv4Packet<_>>().expect("outer ipv4 parse should succeed");
1109
1110 let mut outer_udp_bytes = ip_out.body();
1112 let _udp_out = outer_udp_bytes
1113 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(ip_out.src_ip(), ip_out.dst_ip()))
1114 .expect("outer udp parse should succeed");
1115
1116 let mut inner_ip_bytes = &ip_out.body()[UDP_HEADER_BYTES..];
1117 let ip_in =
1118 inner_ip_bytes.parse::<Ipv4Packet<_>>().expect("inner ipv4 parse should succeed");
1119 let mut body = ip_in.body();
1120
1121 let udp_in_raw = body
1123 .parse_with::<_, UdpPacketRaw<_>>(IpVersionMarker::<Ipv4>::default())
1124 .expect("inner udp parse should succeed");
1125
1126 assert_eq!(
1129 UdpPacket::try_from_raw_with(
1130 udp_in_raw,
1131 UdpParseArgs::new(ip_in.src_ip(), ip_in.dst_ip())
1132 )
1133 .err(),
1134 Some(ParseError::Checksum),
1135 );
1136 }
1137
1138 fn build_udp_packet_invalid_csum<I: IpAddress>(
1139 src_ip: I,
1140 dst_ip: I,
1141 body: &mut [u8],
1142 ) -> Vec<u8> {
1143 let mut buf = Buf::new(body, ..)
1144 .wrap_in(UdpPacketBuilder::new(
1145 src_ip,
1146 dst_ip,
1147 NonZeroU16::new(1),
1148 NonZeroU16::new(2).unwrap(),
1149 ))
1150 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1151 .unwrap()
1152 .as_ref()
1153 .to_vec();
1154
1155 buf[packet_formats::udp::CHECKSUM_OFFSET] ^= 0xFF;
1157 buf[packet_formats::udp::CHECKSUM_OFFSET + 1] ^= 0xFF;
1158 buf
1159 }
1160
1161 fn build_nested_udp_packets_invalid_csums<I: IpAddress>(
1164 src_ip: I,
1165 dst_ip: I,
1166 nesting: usize,
1167 ) -> Vec<u8> {
1168 let mut payload = alloc::vec![0u8; 100];
1169 for _ in 0..nesting {
1170 payload = build_udp_packet_invalid_csum(src_ip, dst_ip, &mut payload);
1171 }
1172 payload
1173 }
1174
1175 #[test]
1176 fn checksum_rx_offloading_none() {
1177 let buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 1);
1178
1179 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(None));
1182 let mut buf_ref: &[u8] = buf.as_ref();
1183 assert_eq!(
1184 buf_ref
1185 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1186 SRC_IP_V4, DST_IP_V4, &mut ctx
1187 ))
1188 .err(),
1189 Some(ParseError::Checksum)
1190 );
1191 assert_eq!(ctx.verified_checksum_count(), 0);
1192 }
1193
1194 #[test]
1195 fn checksum_rx_offloading_fully_offloaded() {
1196 let mut buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 3);
1197
1198 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
1201 for _ in 0..3 {
1202 let mut buf_ref: &[u8] = buf.as_ref();
1203 buf = buf_ref
1204 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1205 SRC_IP_V4, DST_IP_V4, &mut ctx,
1206 ))
1207 .expect("udp parse should succeed")
1208 .body()
1209 .to_vec();
1210 }
1211 assert_eq!(ctx.verified_checksum_count(), 0);
1212 }
1213
1214 #[test]
1215 fn checksum_rx_offloading_offloaded() {
1216 let mut buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 3);
1217
1218 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(Some(
1221 NonZeroU16::new(2).unwrap(),
1222 )));
1223 for _ in 0..2 {
1224 let mut buf_ref: &[u8] = buf.as_ref();
1225 buf = buf_ref
1226 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1227 SRC_IP_V4, DST_IP_V4, &mut ctx,
1228 ))
1229 .expect("udp parse should succeed")
1230 .body()
1231 .to_vec();
1232 }
1233 let mut buf_ref: &[u8] = buf.as_ref();
1234 assert_eq!(
1235 buf_ref
1236 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1237 SRC_IP_V4, DST_IP_V4, &mut ctx
1238 ))
1239 .err(),
1240 Some(ParseError::Checksum)
1241 );
1242 assert_eq!(ctx.verified_checksum_count(), 0);
1243 }
1244
1245 #[test]
1246 fn checksum_rx_offloading_verified_ok() {
1247 let mut payload = [0u8; 10];
1248 let buf = Buf::new(&mut payload[..], ..)
1249 .wrap_in(UdpPacketBuilder::new(
1250 SRC_IP_V4,
1251 DST_IP_V4,
1252 Some(NonZeroU16::new(1234).unwrap()),
1253 NonZeroU16::new(5678).unwrap(),
1254 ))
1255 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1256 .unwrap()
1257 .as_ref()
1258 .to_vec();
1259
1260 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(None));
1261 let mut buf_ref: &[u8] = buf.as_ref();
1262 assert!(
1263 buf_ref
1264 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1265 SRC_IP_V4, DST_IP_V4, &mut ctx
1266 ))
1267 .is_ok()
1268 );
1269 assert_eq!(ctx.verified_checksum_count(), 1);
1270 }
1271}