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
473impl ChecksumRxOffloading {
474 fn skip_checksum_verification(&mut self) -> bool {
475 match self {
476 ChecksumRxOffloading::FullyOffloaded => true,
477 ChecksumRxOffloading::Offloaded(Some(n)) => {
478 *self = ChecksumRxOffloading::Offloaded(NonZeroU16::new(n.get() - 1));
479 true
480 }
481 ChecksumRxOffloading::Offloaded(None) => false,
482 }
483 }
484}
485
486#[derive(Clone, Debug, Default, Eq, PartialEq)]
488pub struct NetworkParsingContext {
489 checksum_offload: ChecksumRxOffloading,
491}
492
493impl NetworkParsingContext {
494 pub fn new(checksum_offload: ChecksumRxOffloading) -> Self {
496 NetworkParsingContext { checksum_offload }
497 }
498
499 pub fn checksum_offload(&self) -> ChecksumRxOffloading {
501 self.checksum_offload
502 }
503}
504
505impl UdpParseContext for &mut NetworkParsingContext {
506 fn skip_checksum_verification(&mut self) -> bool {
507 self.checksum_offload.skip_checksum_verification()
508 }
509}
510
511impl TcpParseContext for &mut NetworkParsingContext {
512 fn skip_checksum_verification(&mut self) -> bool {
513 self.checksum_offload.skip_checksum_verification()
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520 use alloc::vec::Vec;
521 use assert_matches::assert_matches;
522 use core::num::NonZeroU16;
523 use net_types::ethernet::Mac;
524 use net_types::ip::{IpAddress, IpVersionMarker, Ipv4, Ipv4Addr, Ipv6Addr};
525 use packet::{
526 Buf, FragmentedBytesMut, FromRaw, NestablePacketBuilder, NestableSerializer, PacketBuilder,
527 PacketConstraints, ParseBuffer, SerializeTarget, Serializer,
528 };
529 use packet_formats::error::ParseError;
530 use packet_formats::ethernet::{
531 EtherType, EthernetFrame, EthernetFrameBuilder, EthernetFrameLengthCheck,
532 };
533 use packet_formats::ip::{IpPacket, IpProto, Ipv4Proto, Ipv6Proto};
534 use packet_formats::ipv4::options::Ipv4Option;
535 use packet_formats::ipv4::{Ipv4Packet, Ipv4PacketBuilder, Ipv4PacketBuilderWithOptions};
536 use packet_formats::ipv6::ext_hdrs::{
537 ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData,
538 };
539 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
540 use packet_formats::tcp::TcpSegmentBuilder;
541 use packet_formats::udp::{
542 HEADER_BYTES as UDP_HEADER_BYTES, UdpPacket, UdpPacketBuilder, UdpPacketRaw, UdpParseArgs,
543 };
544 use test_case::test_case;
545
546 const SRC_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
547 const DST_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
548 const SRC_IP_V4: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 1]);
549 const DST_IP_V4: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 2]);
550 const SRC_IP_V6: Ipv6Addr = Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 1]);
551 const DST_IP_V6: Ipv6Addr = Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 2]);
552 const SRC_PORT: u16 = 1234;
553 const DST_PORT: u16 = 5678;
554
555 #[test_case(
556 UdpPacketBuilder::new(
557 SRC_IP_V4,
558 DST_IP_V4,
559 NonZeroU16::new(SRC_PORT),
560 NonZeroU16::new(DST_PORT).unwrap(),
561 ),
562 IpProto::Udp ; "udp"
563 )]
564 #[test_case(
565 TcpSegmentBuilder::new(
566 SRC_IP_V4,
567 DST_IP_V4,
568 NonZeroU16::new(SRC_PORT).unwrap(),
569 NonZeroU16::new(DST_PORT).unwrap(),
570 123,
571 None,
572 1000,
573 ),
574 IpProto::Tcp ; "tcp"
575 )]
576 fn ipv4_no_options_csum_offload(
577 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
578 ip_proto: IpProto,
579 ) {
580 let mut payload = [0u8; 100];
581 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
582 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
583
584 let serializer =
585 Buf::new(&mut payload[..], ..).wrap_in(transport_builder).wrap_in(ip).wrap_in(ethernet);
586
587 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
588 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
589 ));
590 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
591
592 let expected_protocol = match ip_proto {
593 IpProto::Udp => OffloadableProtocol::Udp,
594 IpProto::Tcp => OffloadableProtocol::Tcp,
595 _ => panic!("invalid proto"),
596 };
597 assert_eq!(
598 context.csum_offload_result(),
599 Some(ChecksumOffloadResult::ProtocolSpecific(
600 OffloadableProtocols::ETHERNET
601 | OffloadableProtocols::IPV4
602 | expected_protocol.into()
603 ))
604 );
605 }
606
607 #[test_case(
608 UdpPacketBuilder::new(
609 SRC_IP_V4,
610 DST_IP_V4,
611 NonZeroU16::new(SRC_PORT),
612 NonZeroU16::new(DST_PORT).unwrap(),
613 ),
614 IpProto::Udp ; "udp"
615 )]
616 #[test_case(
617 TcpSegmentBuilder::new(
618 SRC_IP_V4,
619 DST_IP_V4,
620 NonZeroU16::new(SRC_PORT).unwrap(),
621 NonZeroU16::new(DST_PORT).unwrap(),
622 123,
623 None,
624 1000,
625 ),
626 IpProto::Tcp ; "tcp"
627 )]
628 fn ipv4_with_options_no_csum_offload(
629 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
630 ip_proto: IpProto,
631 ) {
632 let mut payload = [0u8; 100];
633 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
634 let options = [Ipv4Option::RouterAlert { data: 0 }];
635 let ip_with_options = Ipv4PacketBuilderWithOptions::new(ip, &options).unwrap();
636 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
637
638 let serializer = Buf::new(&mut payload[..], ..)
639 .wrap_in(transport_builder)
640 .wrap_in(ip_with_options)
641 .wrap_in(ethernet);
642
643 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
644 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
645 ));
646 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
647
648 assert_eq!(context.csum_offload_result(), None);
649 }
650
651 #[test_case(
652 UdpPacketBuilder::new(
653 SRC_IP_V6,
654 DST_IP_V6,
655 NonZeroU16::new(SRC_PORT),
656 NonZeroU16::new(DST_PORT).unwrap(),
657 ),
658 IpProto::Udp ; "udp"
659 )]
660 #[test_case(
661 TcpSegmentBuilder::new(
662 SRC_IP_V6,
663 DST_IP_V6,
664 NonZeroU16::new(SRC_PORT).unwrap(),
665 NonZeroU16::new(DST_PORT).unwrap(),
666 123,
667 None,
668 1000,
669 ),
670 IpProto::Tcp ; "tcp"
671 )]
672 fn ipv6_no_extensions_csum_offload(
673 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
674 ip_proto: IpProto,
675 ) {
676 let mut payload = [0u8; 100];
677 let ip = Ipv6PacketBuilder::new(SRC_IP_V6, DST_IP_V6, 64, Ipv6Proto::Proto(ip_proto));
678 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
679
680 let serializer =
681 Buf::new(&mut payload[..], ..).wrap_in(transport_builder).wrap_in(ip).wrap_in(ethernet);
682
683 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
684 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv6(),
685 ));
686 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
687
688 let expected_protocol = match ip_proto {
689 IpProto::Udp => OffloadableProtocol::Udp,
690 IpProto::Tcp => OffloadableProtocol::Tcp,
691 _ => panic!("invalid proto"),
692 };
693 assert_eq!(
694 context.csum_offload_result(),
695 Some(ChecksumOffloadResult::ProtocolSpecific(
696 OffloadableProtocols::ETHERNET
697 | OffloadableProtocols::IPV6
698 | expected_protocol.into()
699 ))
700 );
701 }
702
703 #[test_case(
704 UdpPacketBuilder::new(
705 SRC_IP_V6,
706 DST_IP_V6,
707 NonZeroU16::new(SRC_PORT),
708 NonZeroU16::new(DST_PORT).unwrap(),
709 ),
710 IpProto::Udp ; "udp"
711 )]
712 #[test_case(
713 TcpSegmentBuilder::new(
714 SRC_IP_V6,
715 DST_IP_V6,
716 NonZeroU16::new(SRC_PORT).unwrap(),
717 NonZeroU16::new(DST_PORT).unwrap(),
718 123,
719 None,
720 1000,
721 ),
722 IpProto::Tcp ; "tcp"
723 )]
724 fn ipv6_with_extension_hdrs_no_csum_offload(
725 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
726 ip_proto: IpProto,
727 ) {
728 let mut payload = [0u8; 100];
729 let ip = Ipv6PacketBuilder::new(SRC_IP_V6, DST_IP_V6, 64, Ipv6Proto::Proto(ip_proto));
730 let options = [HopByHopOption {
731 action: ExtensionHeaderOptionAction::SkipAndContinue,
732 mutable: false,
733 data: HopByHopOptionData::RouterAlert { data: 0 },
734 }];
735 let ip_with_options = Ipv6PacketBuilderWithHbhOptions::new(ip, options).unwrap();
736 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
737
738 let serializer = Buf::new(&mut payload[..], ..)
739 .wrap_in(transport_builder)
740 .wrap_in(ip_with_options)
741 .wrap_in(ethernet);
742
743 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
744 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv6(),
745 ));
746 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
747
748 assert_eq!(context.csum_offload_result(), None);
749 }
750
751 #[test]
752 fn generic_csum_offload_preferred_over_protocol_specific() {
753 let mut payload = [0u8; 100];
754 let udp = UdpPacketBuilder::new(
755 SRC_IP_V4,
756 DST_IP_V4,
757 NonZeroU16::new(SRC_PORT),
758 NonZeroU16::new(DST_PORT).unwrap(),
759 );
760 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
761 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
762
763 let serializer = Buf::new(&mut payload[..], ..).wrap_in(udp).wrap_in(ip).wrap_in(ethernet);
764
765 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::any([
766 ChecksumOffloadSpec::generic(),
767 ChecksumOffloadSpec::protocol_specific(
768 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
769 ),
770 ]));
771 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
772
773 assert_matches!(context.csum_offload_result(), Some(ChecksumOffloadResult::Generic(_)));
775 }
776
777 #[derive(Debug)]
778 struct TestPacketBuilder {
779 header_len: usize,
780 }
781 impl NestablePacketBuilder for TestPacketBuilder {
782 fn constraints(&self) -> PacketConstraints {
783 PacketConstraints::new(self.header_len, 0, 0, usize::MAX)
784 }
785 }
786 impl PacketBuilder<NetworkSerializationContext> for TestPacketBuilder {
787 fn context_state(&self) -> OffloadableProtocol {
788 OffloadableProtocol::NotOffloadable
789 }
790 fn serialize(
791 &self,
792 _context: &mut NetworkSerializationContext,
793 _target: &mut SerializeTarget<'_>,
794 _body: FragmentedBytesMut<'_, '_>,
795 ) {
796 }
798 }
799
800 #[test_case(
801 UdpPacketBuilder::new(
802 SRC_IP_V4,
803 DST_IP_V4,
804 NonZeroU16::new(SRC_PORT),
805 NonZeroU16::new(DST_PORT).unwrap(),
806 ),
807 IpProto::Udp,
808 UDP_CHECKSUM_OFFSET ; "udp"
809 )]
810 #[test_case(
811 TcpSegmentBuilder::new(
812 SRC_IP_V4,
813 DST_IP_V4,
814 NonZeroU16::new(SRC_PORT).unwrap(),
815 NonZeroU16::new(DST_PORT).unwrap(),
816 123,
817 None,
818 1000,
819 ),
820 IpProto::Tcp,
821 TCP_CHECKSUM_OFFSET ; "tcp"
822 )]
823 fn generic_csum_offload(
824 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
825 ip_proto: IpProto,
826 expected_csum_offset: u16,
827 ) {
828 let mut payload = [0u8; 100];
829 let test_packet = TestPacketBuilder { header_len: 10 };
830 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
831 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
832
833 let serializer = Buf::new(&mut payload[..], ..)
835 .wrap_in(test_packet)
839 .wrap_in(transport_builder)
840 .wrap_in(ip)
841 .wrap_in(ethernet);
842
843 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
844 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
845
846 assert_eq!(
848 context.csum_offload_result(),
849 Some(ChecksumOffloadResult::Generic(PartialChecksum {
850 start: 34,
851 offset: expected_csum_offset
852 }))
853 );
854 }
855
856 #[test]
857 fn generic_csum_offload_disabled_on_overflow() {
858 let mut payload = [0u8; 100];
859 let test_packet = TestPacketBuilder { header_len: 66000 };
861 let udp = UdpPacketBuilder::new(
862 SRC_IP_V4,
863 DST_IP_V4,
864 NonZeroU16::new(SRC_PORT),
865 NonZeroU16::new(DST_PORT).unwrap(),
866 );
867 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
868 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
869
870 let serializer = Buf::new(&mut payload[..], ..)
874 .wrap_in(udp)
875 .wrap_in(ip)
876 .wrap_in(test_packet)
877 .wrap_in(ethernet);
878
879 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
880 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
881
882 assert_eq!(context.csum_offload_result(), None);
884 }
885
886 #[test]
887 fn generic_csum_offload_enabled_with_inner_overflow() {
888 let mut payload = [0u8; 100];
889 let test_packet = TestPacketBuilder { header_len: 66000 };
891 let udp = UdpPacketBuilder::new(
892 SRC_IP_V6,
893 DST_IP_V6,
894 NonZeroU16::new(SRC_PORT),
895 NonZeroU16::new(DST_PORT).unwrap(),
896 );
897 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
898
899 let serializer =
902 Buf::new(&mut payload[..], ..).wrap_in(test_packet).wrap_in(udp).wrap_in(ethernet);
903
904 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
905 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
906
907 assert_eq!(
910 context.csum_offload_result(),
911 Some(ChecksumOffloadResult::Generic(PartialChecksum {
912 start: 14, offset: UDP_CHECKSUM_OFFSET
914 }))
915 );
916 }
917
918 #[test]
919 fn protocol_specific_csum_offload_with_size_limit() {
920 let mut payload = [0u8; 100];
921 let udp = UdpPacketBuilder::new(
922 SRC_IP_V4,
923 DST_IP_V4,
924 NonZeroU16::new(SRC_PORT),
925 NonZeroU16::new(DST_PORT).unwrap(),
926 );
927 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
928 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
929
930 let serializer = Buf::new(&mut payload[..], ..)
932 .wrap_in(udp)
933 .with_size_limit(1000)
937 .wrap_in(ip)
938 .wrap_in(ethernet);
939
940 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
941 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
942 ));
943 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
944
945 assert_eq!(
946 context.csum_offload_result(),
947 Some(ChecksumOffloadResult::ProtocolSpecific(
948 OffloadableProtocols::ETHERNET
949 | OffloadableProtocols::IPV4
950 | OffloadableProtocols::UDP
951 ))
952 );
953 }
954
955 #[test]
956 fn protocol_specific_csum_offload_duplicate_protocol() {
957 let mut payload = [0u8; 100];
958 let udp_inner = UdpPacketBuilder::new(
959 SRC_IP_V4,
960 DST_IP_V4,
961 NonZeroU16::new(SRC_PORT),
962 NonZeroU16::new(DST_PORT).unwrap(),
963 );
964 let ip_inner =
965 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
966 let udp_outer = UdpPacketBuilder::new(
967 SRC_IP_V4,
968 DST_IP_V4,
969 NonZeroU16::new(SRC_PORT),
970 NonZeroU16::new(DST_PORT).unwrap(),
971 );
972 let ip_outer =
973 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
974 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
975
976 let serializer = Buf::new(&mut payload[..], ..)
978 .wrap_in(udp_inner)
979 .wrap_in(ip_inner)
980 .wrap_in(udp_outer)
981 .wrap_in(ip_outer)
982 .wrap_in(ethernet);
983
984 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
985 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
986 ));
987 let buf =
988 serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
989
990 assert_eq!(
993 context.csum_offload_result(),
994 Some(ChecksumOffloadResult::ProtocolSpecific(
995 OffloadableProtocols::ETHERNET
996 | OffloadableProtocols::IPV4
997 | OffloadableProtocols::UDP
998 ))
999 );
1000
1001 let mut buf_ref = buf.as_ref();
1002 let eth = buf_ref
1003 .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
1004 .expect("ethernet parse should succeed");
1005 let mut body = eth.body();
1006 let ip_out = body.parse::<Ipv4Packet<_>>().expect("outer ipv4 parse should succeed");
1007
1008 let mut outer_udp_bytes = ip_out.body();
1010 let udp_out_raw = outer_udp_bytes
1011 .parse_with::<_, UdpPacketRaw<_>>(IpVersionMarker::<Ipv4>::default())
1012 .expect("outer udp parse should succeed");
1013
1014 assert_eq!(
1017 UdpPacket::try_from_raw_with(
1018 udp_out_raw,
1019 UdpParseArgs::new(ip_out.src_ip(), ip_out.dst_ip())
1020 )
1021 .err(),
1022 Some(ParseError::Checksum),
1023 );
1024
1025 let mut inner_ip_bytes = &ip_out.body()[UDP_HEADER_BYTES..];
1026 let ip_in =
1027 inner_ip_bytes.parse::<Ipv4Packet<_>>().expect("inner ipv4 parse should succeed");
1028 let mut body = ip_in.body();
1029
1030 let _udp_in = body
1033 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(ip_in.src_ip(), ip_in.dst_ip()))
1034 .expect("inner udp parse should succeed");
1035 }
1036
1037 #[test]
1038 fn generic_csum_offload_duplicate_protocol() {
1039 let mut payload = [0u8; 100];
1040 let udp_inner = UdpPacketBuilder::new(
1041 SRC_IP_V4,
1042 DST_IP_V4,
1043 NonZeroU16::new(SRC_PORT),
1044 NonZeroU16::new(DST_PORT).unwrap(),
1045 );
1046 let ip_inner =
1047 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
1048 let udp_outer = UdpPacketBuilder::new(
1049 SRC_IP_V4,
1050 DST_IP_V4,
1051 NonZeroU16::new(SRC_PORT),
1052 NonZeroU16::new(DST_PORT).unwrap(),
1053 );
1054 let ip_outer =
1055 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
1056 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
1057
1058 let serializer = Buf::new(&mut payload[..], ..)
1060 .wrap_in(udp_inner)
1061 .wrap_in(ip_inner)
1062 .wrap_in(udp_outer)
1063 .wrap_in(ip_outer)
1064 .wrap_in(ethernet);
1065
1066 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
1067 let buf =
1068 serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
1069
1070 assert_eq!(
1073 context.csum_offload_result(),
1074 Some(ChecksumOffloadResult::Generic(PartialChecksum {
1075 start: 62,
1076 offset: UDP_CHECKSUM_OFFSET
1077 }))
1078 );
1079
1080 let mut buf_ref = buf.as_ref();
1081 let eth = buf_ref
1082 .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
1083 .expect("ethernet parse should succeed");
1084 let mut body = eth.body();
1085 let ip_out = body.parse::<Ipv4Packet<_>>().expect("outer ipv4 parse should succeed");
1086
1087 let mut outer_udp_bytes = ip_out.body();
1089 let _udp_out = outer_udp_bytes
1090 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(ip_out.src_ip(), ip_out.dst_ip()))
1091 .expect("outer udp parse should succeed");
1092
1093 let mut inner_ip_bytes = &ip_out.body()[UDP_HEADER_BYTES..];
1094 let ip_in =
1095 inner_ip_bytes.parse::<Ipv4Packet<_>>().expect("inner ipv4 parse should succeed");
1096 let mut body = ip_in.body();
1097
1098 let udp_in_raw = body
1100 .parse_with::<_, UdpPacketRaw<_>>(IpVersionMarker::<Ipv4>::default())
1101 .expect("inner udp parse should succeed");
1102
1103 assert_eq!(
1106 UdpPacket::try_from_raw_with(
1107 udp_in_raw,
1108 UdpParseArgs::new(ip_in.src_ip(), ip_in.dst_ip())
1109 )
1110 .err(),
1111 Some(ParseError::Checksum),
1112 );
1113 }
1114
1115 fn build_udp_packet_invalid_csum<I: IpAddress>(
1116 src_ip: I,
1117 dst_ip: I,
1118 body: &mut [u8],
1119 ) -> Vec<u8> {
1120 let mut buf = Buf::new(body, ..)
1121 .wrap_in(UdpPacketBuilder::new(
1122 src_ip,
1123 dst_ip,
1124 NonZeroU16::new(1),
1125 NonZeroU16::new(2).unwrap(),
1126 ))
1127 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1128 .unwrap()
1129 .as_ref()
1130 .to_vec();
1131
1132 buf[packet_formats::udp::CHECKSUM_OFFSET] ^= 0xFF;
1134 buf[packet_formats::udp::CHECKSUM_OFFSET + 1] ^= 0xFF;
1135 buf
1136 }
1137
1138 fn build_nested_udp_packets_invalid_csums<I: IpAddress>(
1141 src_ip: I,
1142 dst_ip: I,
1143 nesting: usize,
1144 ) -> Vec<u8> {
1145 let mut payload = alloc::vec![0u8; 100];
1146 for _ in 0..nesting {
1147 payload = build_udp_packet_invalid_csum(src_ip, dst_ip, &mut payload);
1148 }
1149 payload
1150 }
1151
1152 #[test]
1153 fn checksum_rx_offloading_none() {
1154 let buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 1);
1155
1156 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(None));
1159 let mut buf_ref: &[u8] = buf.as_ref();
1160 assert_eq!(
1161 buf_ref
1162 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1163 SRC_IP_V4, DST_IP_V4, &mut ctx
1164 ))
1165 .err(),
1166 Some(ParseError::Checksum)
1167 );
1168 }
1169
1170 #[test]
1171 fn checksum_rx_offloading_fully_offloaded() {
1172 let mut buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 3);
1173
1174 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
1177 for _ in 0..3 {
1178 let mut buf_ref: &[u8] = buf.as_ref();
1179 buf = buf_ref
1180 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1181 SRC_IP_V4, DST_IP_V4, &mut ctx,
1182 ))
1183 .expect("udp parse should succeed")
1184 .body()
1185 .to_vec();
1186 }
1187 }
1188
1189 #[test]
1190 fn checksum_rx_offloading_offloaded() {
1191 let mut buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 3);
1192
1193 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(Some(
1196 NonZeroU16::new(2).unwrap(),
1197 )));
1198 for _ in 0..2 {
1199 let mut buf_ref: &[u8] = buf.as_ref();
1200 buf = buf_ref
1201 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1202 SRC_IP_V4, DST_IP_V4, &mut ctx,
1203 ))
1204 .expect("udp parse should succeed")
1205 .body()
1206 .to_vec();
1207 }
1208 let mut buf_ref: &[u8] = buf.as_ref();
1209 assert_eq!(
1210 buf_ref
1211 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1212 SRC_IP_V4, DST_IP_V4, &mut ctx
1213 ))
1214 .err(),
1215 Some(ParseError::Checksum)
1216 );
1217 }
1218}