1use bitflags::bitflags;
8use core::num::NonZeroU16;
9use net_types::ip::IpInvariant;
10use packet::{
11 DynamicSerializer, PacketBuilder, PacketConstraints, PartialSerializer, SerializationContext,
12 Serializer,
13};
14use packet_formats::TransportChecksumAction;
15use packet_formats::ethernet::{EthernetEnvelope, EthernetSerializationContext};
16use packet_formats::icmp::{IcmpEnvelope, IcmpSerializationContext};
17use packet_formats::ip::{IpEnvelope, IpExt, IpSerializationContext};
18use packet_formats::tcp::{TcpEnvelope, TcpParseContext, TcpSerializationContext};
19use packet_formats::udp::{UdpEnvelope, UdpParseContext, UdpSerializationContext};
20use static_assertions::const_assert;
21
22pub trait NetworkSerializer: Serializer<NetworkSerializationContext> {}
24impl<S: Serializer<NetworkSerializationContext>> NetworkSerializer for S {}
25
26pub trait NetworkPartialSerializer: PartialSerializer<NetworkSerializationContext> {}
28impl<S: PartialSerializer<NetworkSerializationContext>> NetworkPartialSerializer for S {}
29
30pub trait DynamicNetworkSerializer: DynamicSerializer<NetworkSerializationContext> {}
32impl<S: DynamicSerializer<NetworkSerializationContext>> DynamicNetworkSerializer for S {}
33
34#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
36pub enum OffloadableProtocol {
37 #[default]
39 None,
40 NotOffloadable,
42 Tcp,
44 Udp,
46 Ipv4,
48 Ipv6,
50 Ethernet,
52}
53
54bitflags! {
55 #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
57 pub struct OffloadableProtocols: u8 {
58 const NOT_OFFLOADABLE = 1 << 0;
60 const TCP = 1 << 1;
62 const UDP = 1 << 2;
64 const IPV4 = 1 << 3;
66 const IPV6 = 1 << 4;
68 const ETHERNET = 1 << 5;
70 }
71}
72
73impl From<OffloadableProtocol> for OffloadableProtocols {
74 fn from(p: OffloadableProtocol) -> Self {
75 match p {
76 OffloadableProtocol::None => Self::empty(),
77 OffloadableProtocol::NotOffloadable => Self::NOT_OFFLOADABLE,
78 OffloadableProtocol::Tcp => Self::TCP,
79 OffloadableProtocol::Udp => Self::UDP,
80 OffloadableProtocol::Ipv4 => Self::IPV4,
81 OffloadableProtocol::Ipv6 => Self::IPV6,
82 OffloadableProtocol::Ethernet => Self::ETHERNET,
83 }
84 }
85}
86
87bitflags! {
88 #[derive(Clone, Debug, Default, Eq, PartialEq)]
90 pub struct ProtocolSpecificOffloadSpec: u8 {
91 const ETH_IPV4_UDP = 1 << 0;
93 const ETH_IPV4_TCP = 1 << 1;
95 const ETH_IPV6_UDP = 1 << 2;
98 const ETH_IPV6_TCP = 1 << 3;
101 }
102}
103
104impl ProtocolSpecificOffloadSpec {
105 pub fn tcp_or_udp_over_ipv4() -> Self {
110 Self::ETH_IPV4_UDP | Self::ETH_IPV4_TCP
111 }
112
113 pub fn tcp_or_udp_over_ipv6() -> Self {
118 Self::ETH_IPV6_UDP | Self::ETH_IPV6_TCP
119 }
120
121 fn or(self, other: Self) -> Self {
124 self | other
125 }
126
127 fn matches(&self, current: OffloadableProtocols) -> bool {
129 use OffloadableProtocols as P;
130 match current {
131 f if f == (P::ETHERNET | P::IPV4 | P::UDP) => self.contains(Self::ETH_IPV4_UDP),
132 f if f == (P::ETHERNET | P::IPV4 | P::TCP) => self.contains(Self::ETH_IPV4_TCP),
133 f if f == (P::ETHERNET | P::IPV6 | P::UDP) => self.contains(Self::ETH_IPV6_UDP),
134 f if f == (P::ETHERNET | P::IPV6 | P::TCP) => self.contains(Self::ETH_IPV6_TCP),
135 _ => false,
136 }
137 }
138}
139
140#[derive(Clone, Debug, Default, Eq, PartialEq)]
142struct GenericOffloadSpec;
143
144#[derive(Clone, Debug, Default, Eq, PartialEq)]
146pub struct ChecksumOffloadSpec {
147 protocol_specific: Option<ProtocolSpecificOffloadSpec>,
149 generic: Option<GenericOffloadSpec>,
151}
152
153impl ChecksumOffloadSpec {
154 pub fn none() -> Self {
157 Self { protocol_specific: None, generic: None }
158 }
159
160 pub fn protocol_specific(spec: ProtocolSpecificOffloadSpec) -> Self {
163 Self { protocol_specific: Some(spec), generic: None }
164 }
165
166 pub fn generic() -> Self {
169 Self { protocol_specific: None, generic: Some(GenericOffloadSpec::default()) }
170 }
171
172 pub fn any<I: IntoIterator<Item = Self>>(specs: I) -> Self {
174 specs.into_iter().fold(Self::none(), |acc, spec| Self {
175 protocol_specific: match (acc.protocol_specific, spec.protocol_specific) {
176 (Some(acc), Some(spec)) => Some(acc.or(spec)),
177 (Some(acc), None) => Some(acc),
178 (None, Some(spec)) => Some(spec),
179 (None, None) => None,
180 },
181 generic: acc.generic.or(spec.generic),
182 })
183 }
184}
185
186#[derive(Clone, Debug, Eq, PartialEq)]
187struct ProtocolStackInfo {
188 header_offset: Option<u16>,
191 protocols: OffloadableProtocols,
193}
194
195impl Default for ProtocolStackInfo {
196 fn default() -> Self {
197 Self { header_offset: Some(0), protocols: OffloadableProtocols::empty() }
198 }
199}
200
201#[derive(Clone, Debug, Default, Eq, PartialEq)]
202struct ChecksumOffloadState {
203 spec: ChecksumOffloadSpec,
204 stack_info: ProtocolStackInfo,
205}
206
207impl ChecksumOffloadState {
208 fn new(spec: ChecksumOffloadSpec) -> Self {
209 Self { spec, stack_info: Default::default() }
210 }
211
212 fn update(
218 &mut self,
219 protocol: OffloadableProtocol,
220 constraints: &PacketConstraints,
221 ) -> ProtocolStackInfo {
222 let previous = self.stack_info.clone();
223
224 let ProtocolStackInfo { header_offset, protocols } = &mut self.stack_info;
225
226 *header_offset = header_offset.and_then(|_| constraints.header_len().try_into().ok());
230
231 if protocol == OffloadableProtocol::None {
232 return previous;
233 }
234 let protocol = protocol.into();
235 if protocols.contains(protocol) {
236 protocols.insert(OffloadableProtocol::NotOffloadable.into());
241 } else {
242 protocols.insert(protocol);
243 }
244
245 previous
246 }
247
248 fn restore(&mut self, previous: ProtocolStackInfo) {
250 self.stack_info = previous;
251 }
252
253 fn try_offload(&self, csum_offset: u16) -> Option<ChecksumOffloadResult> {
254 let ProtocolStackInfo { header_offset, protocols } = self.stack_info;
255
256 if let Some(start) = self.spec.generic.as_ref().and(header_offset) {
260 Some(ChecksumOffloadResult::Generic(PartialChecksum { start, offset: csum_offset }))
261 } else if self
262 .spec
263 .protocol_specific
264 .as_ref()
265 .map(|spec| spec.matches(protocols))
266 .unwrap_or(false)
267 {
268 Some(ChecksumOffloadResult::ProtocolSpecific(protocols))
269 } else {
270 None
271 }
272 }
273}
274
275#[derive(Clone, Debug, Default, Eq, PartialEq)]
280pub struct PartialChecksum {
281 pub start: u16,
284 pub offset: u16,
286}
287
288#[derive(Clone, Debug, Eq, PartialEq)]
290pub enum ChecksumOffloadResult {
291 ProtocolSpecific(OffloadableProtocols),
293 Generic(PartialChecksum),
295}
296
297#[derive(Clone, Debug, Default, Eq, PartialEq)]
299pub struct NetworkSerializationContext {
300 csum_offload_state: ChecksumOffloadState,
301 csum_offload_result: Option<ChecksumOffloadResult>,
306}
307
308impl NetworkSerializationContext {
309 pub fn new(csum_offload_spec: ChecksumOffloadSpec) -> Self {
311 Self {
312 csum_offload_state: ChecksumOffloadState::new(csum_offload_spec),
313 csum_offload_result: None,
314 }
315 }
316
317 fn transport_checksum_action(&mut self, csum_offset: u16) -> TransportChecksumAction {
318 if self.csum_offload_result.is_some() {
319 TransportChecksumAction::ComputeFull
320 } else {
321 self.csum_offload_result = self.csum_offload_state.try_offload(csum_offset);
322 self.csum_offload_result
323 .as_ref()
324 .map(|_| TransportChecksumAction::ComputePartial)
325 .unwrap_or(TransportChecksumAction::ComputeFull)
326 }
327 }
328
329 pub fn csum_offload_result(self) -> Option<ChecksumOffloadResult> {
332 self.csum_offload_result
333 }
334}
335
336impl SerializationContext for NetworkSerializationContext {
337 type ContextState = OffloadableProtocol;
338
339 fn serialize_nested<O: PacketBuilder<Self>, R>(
340 &mut self,
341 outer: &O,
342 constraints: PacketConstraints,
343 serialize_fn: impl FnOnce(&mut Self, PacketConstraints) -> R,
344 ) -> R {
345 let previous_state = self.csum_offload_state.update(outer.context_state(), &constraints);
346 let result = serialize_fn(self, constraints);
347 self.csum_offload_state.restore(previous_state);
348 result
349 }
350}
351
352impl EthernetSerializationContext for NetworkSerializationContext {
353 fn envelope_to_state(_envelope: EthernetEnvelope) -> Self::ContextState {
354 OffloadableProtocol::Ethernet
355 }
356}
357
358impl<I: IpExt> IpSerializationContext<I> for NetworkSerializationContext {
359 fn envelope_to_state(envelope: IpEnvelope<I>) -> Self::ContextState {
360 I::map_ip_in(
361 IpInvariant(envelope),
362 |IpInvariant(envelope)| {
363 if envelope.has_options {
364 OffloadableProtocol::NotOffloadable
365 } else {
366 OffloadableProtocol::Ipv4
367 }
368 },
369 |IpInvariant(envelope)| {
370 if envelope.has_options {
371 OffloadableProtocol::NotOffloadable
372 } else {
373 OffloadableProtocol::Ipv6
374 }
375 },
376 )
377 }
378}
379
380impl IcmpSerializationContext for NetworkSerializationContext {
381 fn envelope_to_state(_envelope: IcmpEnvelope) -> Self::ContextState {
382 OffloadableProtocol::NotOffloadable
383 }
384}
385
386const_assert!(packet_formats::udp::CHECKSUM_OFFSET <= u16::MAX as usize);
387const UDP_CHECKSUM_OFFSET: u16 = packet_formats::udp::CHECKSUM_OFFSET as u16;
388
389impl UdpSerializationContext for NetworkSerializationContext {
390 fn envelope_to_state(_envelope: UdpEnvelope) -> Self::ContextState {
391 OffloadableProtocol::Udp
392 }
393
394 fn checksum_action(&mut self) -> TransportChecksumAction {
395 self.transport_checksum_action(UDP_CHECKSUM_OFFSET)
396 }
397}
398
399const_assert!(packet_formats::tcp::CHECKSUM_OFFSET <= u16::MAX as usize);
400const TCP_CHECKSUM_OFFSET: u16 = packet_formats::tcp::CHECKSUM_OFFSET as u16;
401
402impl TcpSerializationContext for NetworkSerializationContext {
403 fn envelope_to_state(_envelope: TcpEnvelope) -> Self::ContextState {
404 OffloadableProtocol::Tcp
405 }
406
407 fn checksum_action(&mut self) -> TransportChecksumAction {
408 self.transport_checksum_action(TCP_CHECKSUM_OFFSET)
409 }
410}
411
412#[derive(Clone, Copy, Debug, Eq, PartialEq)]
415pub enum ChecksumRxOffloading {
416 Offloaded(Option<NonZeroU16>),
421 FullyOffloaded,
425}
426
427impl Default for ChecksumRxOffloading {
428 fn default() -> Self {
429 ChecksumRxOffloading::Offloaded(None)
430 }
431}
432
433impl ChecksumRxOffloading {
434 fn skip_checksum_verification(&mut self) -> bool {
435 match self {
436 ChecksumRxOffloading::FullyOffloaded => true,
437 ChecksumRxOffloading::Offloaded(Some(n)) => {
438 *self = ChecksumRxOffloading::Offloaded(NonZeroU16::new(n.get() - 1));
439 true
440 }
441 ChecksumRxOffloading::Offloaded(None) => false,
442 }
443 }
444}
445
446#[derive(Clone, Debug, Default, Eq, PartialEq)]
448pub struct NetworkParsingContext {
449 checksum_offload: ChecksumRxOffloading,
451}
452
453impl NetworkParsingContext {
454 pub fn new(checksum_offload: ChecksumRxOffloading) -> Self {
456 NetworkParsingContext { checksum_offload }
457 }
458}
459
460impl UdpParseContext for &mut NetworkParsingContext {
461 fn skip_checksum_verification(&mut self) -> bool {
462 self.checksum_offload.skip_checksum_verification()
463 }
464}
465
466impl TcpParseContext for &mut NetworkParsingContext {
467 fn skip_checksum_verification(&mut self) -> bool {
468 self.checksum_offload.skip_checksum_verification()
469 }
470}
471
472#[cfg(test)]
473mod tests {
474 use super::*;
475 use alloc::vec::Vec;
476 use assert_matches::assert_matches;
477 use core::num::NonZeroU16;
478 use net_types::ethernet::Mac;
479 use net_types::ip::{IpAddress, IpVersionMarker, Ipv4, Ipv4Addr, Ipv6Addr};
480 use packet::{
481 Buf, FragmentedBytesMut, FromRaw, NestablePacketBuilder, NestableSerializer, PacketBuilder,
482 PacketConstraints, ParseBuffer, SerializeTarget, Serializer,
483 };
484 use packet_formats::error::ParseError;
485 use packet_formats::ethernet::{
486 EtherType, EthernetFrame, EthernetFrameBuilder, EthernetFrameLengthCheck,
487 };
488 use packet_formats::ip::{IpPacket, IpProto, Ipv4Proto, Ipv6Proto};
489 use packet_formats::ipv4::options::Ipv4Option;
490 use packet_formats::ipv4::{Ipv4Packet, Ipv4PacketBuilder, Ipv4PacketBuilderWithOptions};
491 use packet_formats::ipv6::ext_hdrs::{
492 ExtensionHeaderOptionAction, HopByHopOption, HopByHopOptionData,
493 };
494 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithHbhOptions};
495 use packet_formats::tcp::TcpSegmentBuilder;
496 use packet_formats::udp::{
497 HEADER_BYTES as UDP_HEADER_BYTES, UdpPacket, UdpPacketBuilder, UdpPacketRaw, UdpParseArgs,
498 };
499 use test_case::test_case;
500
501 const SRC_MAC: Mac = Mac::new([0, 1, 2, 3, 4, 5]);
502 const DST_MAC: Mac = Mac::new([6, 7, 8, 9, 10, 11]);
503 const SRC_IP_V4: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 1]);
504 const DST_IP_V4: Ipv4Addr = Ipv4Addr::new([192, 168, 0, 2]);
505 const SRC_IP_V6: Ipv6Addr = Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 1]);
506 const DST_IP_V6: Ipv6Addr = Ipv6Addr::new([0, 0, 0, 0, 0, 0, 0, 2]);
507 const SRC_PORT: u16 = 1234;
508 const DST_PORT: u16 = 5678;
509
510 #[test_case(
511 UdpPacketBuilder::new(
512 SRC_IP_V4,
513 DST_IP_V4,
514 NonZeroU16::new(SRC_PORT),
515 NonZeroU16::new(DST_PORT).unwrap(),
516 ),
517 IpProto::Udp ; "udp"
518 )]
519 #[test_case(
520 TcpSegmentBuilder::new(
521 SRC_IP_V4,
522 DST_IP_V4,
523 NonZeroU16::new(SRC_PORT).unwrap(),
524 NonZeroU16::new(DST_PORT).unwrap(),
525 123,
526 None,
527 1000,
528 ),
529 IpProto::Tcp ; "tcp"
530 )]
531 fn ipv4_no_options_csum_offload(
532 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
533 ip_proto: IpProto,
534 ) {
535 let mut payload = [0u8; 100];
536 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
537 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
538
539 let serializer =
540 Buf::new(&mut payload[..], ..).wrap_in(transport_builder).wrap_in(ip).wrap_in(ethernet);
541
542 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
543 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
544 ));
545 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
546
547 let expected_protocol = match ip_proto {
548 IpProto::Udp => OffloadableProtocol::Udp,
549 IpProto::Tcp => OffloadableProtocol::Tcp,
550 _ => panic!("invalid proto"),
551 };
552 assert_eq!(
553 context.csum_offload_result(),
554 Some(ChecksumOffloadResult::ProtocolSpecific(
555 OffloadableProtocols::ETHERNET
556 | OffloadableProtocols::IPV4
557 | expected_protocol.into()
558 ))
559 );
560 }
561
562 #[test_case(
563 UdpPacketBuilder::new(
564 SRC_IP_V4,
565 DST_IP_V4,
566 NonZeroU16::new(SRC_PORT),
567 NonZeroU16::new(DST_PORT).unwrap(),
568 ),
569 IpProto::Udp ; "udp"
570 )]
571 #[test_case(
572 TcpSegmentBuilder::new(
573 SRC_IP_V4,
574 DST_IP_V4,
575 NonZeroU16::new(SRC_PORT).unwrap(),
576 NonZeroU16::new(DST_PORT).unwrap(),
577 123,
578 None,
579 1000,
580 ),
581 IpProto::Tcp ; "tcp"
582 )]
583 fn ipv4_with_options_no_csum_offload(
584 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
585 ip_proto: IpProto,
586 ) {
587 let mut payload = [0u8; 100];
588 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
589 let options = [Ipv4Option::RouterAlert { data: 0 }];
590 let ip_with_options = Ipv4PacketBuilderWithOptions::new(ip, &options).unwrap();
591 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
592
593 let serializer = Buf::new(&mut payload[..], ..)
594 .wrap_in(transport_builder)
595 .wrap_in(ip_with_options)
596 .wrap_in(ethernet);
597
598 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
599 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
600 ));
601 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
602
603 assert_eq!(context.csum_offload_result(), None);
604 }
605
606 #[test_case(
607 UdpPacketBuilder::new(
608 SRC_IP_V6,
609 DST_IP_V6,
610 NonZeroU16::new(SRC_PORT),
611 NonZeroU16::new(DST_PORT).unwrap(),
612 ),
613 IpProto::Udp ; "udp"
614 )]
615 #[test_case(
616 TcpSegmentBuilder::new(
617 SRC_IP_V6,
618 DST_IP_V6,
619 NonZeroU16::new(SRC_PORT).unwrap(),
620 NonZeroU16::new(DST_PORT).unwrap(),
621 123,
622 None,
623 1000,
624 ),
625 IpProto::Tcp ; "tcp"
626 )]
627 fn ipv6_no_extensions_csum_offload(
628 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
629 ip_proto: IpProto,
630 ) {
631 let mut payload = [0u8; 100];
632 let ip = Ipv6PacketBuilder::new(SRC_IP_V6, DST_IP_V6, 64, Ipv6Proto::Proto(ip_proto));
633 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
634
635 let serializer =
636 Buf::new(&mut payload[..], ..).wrap_in(transport_builder).wrap_in(ip).wrap_in(ethernet);
637
638 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
639 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv6(),
640 ));
641 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
642
643 let expected_protocol = match ip_proto {
644 IpProto::Udp => OffloadableProtocol::Udp,
645 IpProto::Tcp => OffloadableProtocol::Tcp,
646 _ => panic!("invalid proto"),
647 };
648 assert_eq!(
649 context.csum_offload_result(),
650 Some(ChecksumOffloadResult::ProtocolSpecific(
651 OffloadableProtocols::ETHERNET
652 | OffloadableProtocols::IPV6
653 | expected_protocol.into()
654 ))
655 );
656 }
657
658 #[test_case(
659 UdpPacketBuilder::new(
660 SRC_IP_V6,
661 DST_IP_V6,
662 NonZeroU16::new(SRC_PORT),
663 NonZeroU16::new(DST_PORT).unwrap(),
664 ),
665 IpProto::Udp ; "udp"
666 )]
667 #[test_case(
668 TcpSegmentBuilder::new(
669 SRC_IP_V6,
670 DST_IP_V6,
671 NonZeroU16::new(SRC_PORT).unwrap(),
672 NonZeroU16::new(DST_PORT).unwrap(),
673 123,
674 None,
675 1000,
676 ),
677 IpProto::Tcp ; "tcp"
678 )]
679 fn ipv6_with_extension_hdrs_no_csum_offload(
680 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
681 ip_proto: IpProto,
682 ) {
683 let mut payload = [0u8; 100];
684 let ip = Ipv6PacketBuilder::new(SRC_IP_V6, DST_IP_V6, 64, Ipv6Proto::Proto(ip_proto));
685 let options = [HopByHopOption {
686 action: ExtensionHeaderOptionAction::SkipAndContinue,
687 mutable: false,
688 data: HopByHopOptionData::RouterAlert { data: 0 },
689 }];
690 let ip_with_options = Ipv6PacketBuilderWithHbhOptions::new(ip, options).unwrap();
691 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
692
693 let serializer = Buf::new(&mut payload[..], ..)
694 .wrap_in(transport_builder)
695 .wrap_in(ip_with_options)
696 .wrap_in(ethernet);
697
698 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
699 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv6(),
700 ));
701 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
702
703 assert_eq!(context.csum_offload_result(), None);
704 }
705
706 #[test]
707 fn generic_csum_offload_preferred_over_protocol_specific() {
708 let mut payload = [0u8; 100];
709 let udp = UdpPacketBuilder::new(
710 SRC_IP_V4,
711 DST_IP_V4,
712 NonZeroU16::new(SRC_PORT),
713 NonZeroU16::new(DST_PORT).unwrap(),
714 );
715 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
716 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
717
718 let serializer = Buf::new(&mut payload[..], ..).wrap_in(udp).wrap_in(ip).wrap_in(ethernet);
719
720 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::any([
721 ChecksumOffloadSpec::generic(),
722 ChecksumOffloadSpec::protocol_specific(
723 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
724 ),
725 ]));
726 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
727
728 assert_matches!(context.csum_offload_result(), Some(ChecksumOffloadResult::Generic(_)));
730 }
731
732 #[derive(Debug)]
733 struct TestPacketBuilder {
734 header_len: usize,
735 }
736 impl NestablePacketBuilder for TestPacketBuilder {
737 fn constraints(&self) -> PacketConstraints {
738 PacketConstraints::new(self.header_len, 0, 0, usize::MAX)
739 }
740 }
741 impl PacketBuilder<NetworkSerializationContext> for TestPacketBuilder {
742 fn context_state(&self) -> OffloadableProtocol {
743 OffloadableProtocol::NotOffloadable
744 }
745 fn serialize(
746 &self,
747 _context: &mut NetworkSerializationContext,
748 _target: &mut SerializeTarget<'_>,
749 _body: FragmentedBytesMut<'_, '_>,
750 ) {
751 }
753 }
754
755 #[test_case(
756 UdpPacketBuilder::new(
757 SRC_IP_V4,
758 DST_IP_V4,
759 NonZeroU16::new(SRC_PORT),
760 NonZeroU16::new(DST_PORT).unwrap(),
761 ),
762 IpProto::Udp,
763 UDP_CHECKSUM_OFFSET ; "udp"
764 )]
765 #[test_case(
766 TcpSegmentBuilder::new(
767 SRC_IP_V4,
768 DST_IP_V4,
769 NonZeroU16::new(SRC_PORT).unwrap(),
770 NonZeroU16::new(DST_PORT).unwrap(),
771 123,
772 None,
773 1000,
774 ),
775 IpProto::Tcp,
776 TCP_CHECKSUM_OFFSET ; "tcp"
777 )]
778 fn generic_csum_offload(
779 transport_builder: impl PacketBuilder<NetworkSerializationContext> + core::fmt::Debug,
780 ip_proto: IpProto,
781 expected_csum_offset: u16,
782 ) {
783 let mut payload = [0u8; 100];
784 let test_packet = TestPacketBuilder { header_len: 10 };
785 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(ip_proto));
786 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
787
788 let serializer = Buf::new(&mut payload[..], ..)
790 .wrap_in(test_packet)
794 .wrap_in(transport_builder)
795 .wrap_in(ip)
796 .wrap_in(ethernet);
797
798 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
799 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
800
801 assert_eq!(
803 context.csum_offload_result(),
804 Some(ChecksumOffloadResult::Generic(PartialChecksum {
805 start: 34,
806 offset: expected_csum_offset
807 }))
808 );
809 }
810
811 #[test]
812 fn generic_csum_offload_disabled_on_overflow() {
813 let mut payload = [0u8; 100];
814 let test_packet = TestPacketBuilder { header_len: 66000 };
816 let udp = UdpPacketBuilder::new(
817 SRC_IP_V4,
818 DST_IP_V4,
819 NonZeroU16::new(SRC_PORT),
820 NonZeroU16::new(DST_PORT).unwrap(),
821 );
822 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
823 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
824
825 let serializer = Buf::new(&mut payload[..], ..)
829 .wrap_in(udp)
830 .wrap_in(ip)
831 .wrap_in(test_packet)
832 .wrap_in(ethernet);
833
834 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
835 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
836
837 assert_eq!(context.csum_offload_result(), None);
839 }
840
841 #[test]
842 fn generic_csum_offload_enabled_with_inner_overflow() {
843 let mut payload = [0u8; 100];
844 let test_packet = TestPacketBuilder { header_len: 66000 };
846 let udp = UdpPacketBuilder::new(
847 SRC_IP_V6,
848 DST_IP_V6,
849 NonZeroU16::new(SRC_PORT),
850 NonZeroU16::new(DST_PORT).unwrap(),
851 );
852 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv6, 0);
853
854 let serializer =
857 Buf::new(&mut payload[..], ..).wrap_in(test_packet).wrap_in(udp).wrap_in(ethernet);
858
859 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
860 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
861
862 assert_eq!(
865 context.csum_offload_result(),
866 Some(ChecksumOffloadResult::Generic(PartialChecksum {
867 start: 14, offset: UDP_CHECKSUM_OFFSET
869 }))
870 );
871 }
872
873 #[test]
874 fn protocol_specific_csum_offload_with_size_limit() {
875 let mut payload = [0u8; 100];
876 let udp = UdpPacketBuilder::new(
877 SRC_IP_V4,
878 DST_IP_V4,
879 NonZeroU16::new(SRC_PORT),
880 NonZeroU16::new(DST_PORT).unwrap(),
881 );
882 let ip = Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
883 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
884
885 let serializer = Buf::new(&mut payload[..], ..)
887 .wrap_in(udp)
888 .with_size_limit(1000)
892 .wrap_in(ip)
893 .wrap_in(ethernet);
894
895 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
896 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
897 ));
898 let _ = serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
899
900 assert_eq!(
901 context.csum_offload_result(),
902 Some(ChecksumOffloadResult::ProtocolSpecific(
903 OffloadableProtocols::ETHERNET
904 | OffloadableProtocols::IPV4
905 | OffloadableProtocols::UDP
906 ))
907 );
908 }
909
910 #[test]
911 fn protocol_specific_csum_offload_duplicate_protocol() {
912 let mut payload = [0u8; 100];
913 let udp_inner = UdpPacketBuilder::new(
914 SRC_IP_V4,
915 DST_IP_V4,
916 NonZeroU16::new(SRC_PORT),
917 NonZeroU16::new(DST_PORT).unwrap(),
918 );
919 let ip_inner =
920 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
921 let udp_outer = 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_outer =
928 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
929 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
930
931 let serializer = Buf::new(&mut payload[..], ..)
933 .wrap_in(udp_inner)
934 .wrap_in(ip_inner)
935 .wrap_in(udp_outer)
936 .wrap_in(ip_outer)
937 .wrap_in(ethernet);
938
939 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::protocol_specific(
940 ProtocolSpecificOffloadSpec::tcp_or_udp_over_ipv4(),
941 ));
942 let buf =
943 serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
944
945 assert_eq!(
948 context.csum_offload_result(),
949 Some(ChecksumOffloadResult::ProtocolSpecific(
950 OffloadableProtocols::ETHERNET
951 | OffloadableProtocols::IPV4
952 | OffloadableProtocols::UDP
953 ))
954 );
955
956 let mut buf_ref = buf.as_ref();
957 let eth = buf_ref
958 .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
959 .expect("ethernet parse should succeed");
960 let mut body = eth.body();
961 let ip_out = body.parse::<Ipv4Packet<_>>().expect("outer ipv4 parse should succeed");
962
963 let mut outer_udp_bytes = ip_out.body();
965 let udp_out_raw = outer_udp_bytes
966 .parse_with::<_, UdpPacketRaw<_>>(IpVersionMarker::<Ipv4>::default())
967 .expect("outer udp parse should succeed");
968
969 assert_eq!(
972 UdpPacket::try_from_raw_with(
973 udp_out_raw,
974 UdpParseArgs::new(ip_out.src_ip(), ip_out.dst_ip())
975 )
976 .err(),
977 Some(ParseError::Checksum),
978 );
979
980 let mut inner_ip_bytes = &ip_out.body()[UDP_HEADER_BYTES..];
981 let ip_in =
982 inner_ip_bytes.parse::<Ipv4Packet<_>>().expect("inner ipv4 parse should succeed");
983 let mut body = ip_in.body();
984
985 let _udp_in = body
988 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(ip_in.src_ip(), ip_in.dst_ip()))
989 .expect("inner udp parse should succeed");
990 }
991
992 #[test]
993 fn generic_csum_offload_duplicate_protocol() {
994 let mut payload = [0u8; 100];
995 let udp_inner = UdpPacketBuilder::new(
996 SRC_IP_V4,
997 DST_IP_V4,
998 NonZeroU16::new(SRC_PORT),
999 NonZeroU16::new(DST_PORT).unwrap(),
1000 );
1001 let ip_inner =
1002 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
1003 let udp_outer = UdpPacketBuilder::new(
1004 SRC_IP_V4,
1005 DST_IP_V4,
1006 NonZeroU16::new(SRC_PORT),
1007 NonZeroU16::new(DST_PORT).unwrap(),
1008 );
1009 let ip_outer =
1010 Ipv4PacketBuilder::new(SRC_IP_V4, DST_IP_V4, 64, Ipv4Proto::Proto(IpProto::Udp));
1011 let ethernet = EthernetFrameBuilder::new(SRC_MAC, DST_MAC, EtherType::Ipv4, 0);
1012
1013 let serializer = Buf::new(&mut payload[..], ..)
1015 .wrap_in(udp_inner)
1016 .wrap_in(ip_inner)
1017 .wrap_in(udp_outer)
1018 .wrap_in(ip_outer)
1019 .wrap_in(ethernet);
1020
1021 let mut context = NetworkSerializationContext::new(ChecksumOffloadSpec::generic());
1022 let buf =
1023 serializer.serialize_vec_outer(&mut context).expect("serialization should succeed");
1024
1025 assert_eq!(
1028 context.csum_offload_result(),
1029 Some(ChecksumOffloadResult::Generic(PartialChecksum {
1030 start: 62,
1031 offset: UDP_CHECKSUM_OFFSET
1032 }))
1033 );
1034
1035 let mut buf_ref = buf.as_ref();
1036 let eth = buf_ref
1037 .parse_with::<_, EthernetFrame<_>>(EthernetFrameLengthCheck::Check)
1038 .expect("ethernet parse should succeed");
1039 let mut body = eth.body();
1040 let ip_out = body.parse::<Ipv4Packet<_>>().expect("outer ipv4 parse should succeed");
1041
1042 let mut outer_udp_bytes = ip_out.body();
1044 let _udp_out = outer_udp_bytes
1045 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::new(ip_out.src_ip(), ip_out.dst_ip()))
1046 .expect("outer udp parse should succeed");
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_raw = body
1055 .parse_with::<_, UdpPacketRaw<_>>(IpVersionMarker::<Ipv4>::default())
1056 .expect("inner udp parse should succeed");
1057
1058 assert_eq!(
1061 UdpPacket::try_from_raw_with(
1062 udp_in_raw,
1063 UdpParseArgs::new(ip_in.src_ip(), ip_in.dst_ip())
1064 )
1065 .err(),
1066 Some(ParseError::Checksum),
1067 );
1068 }
1069
1070 fn build_udp_packet_invalid_csum<I: IpAddress>(
1071 src_ip: I,
1072 dst_ip: I,
1073 body: &mut [u8],
1074 ) -> Vec<u8> {
1075 let mut buf = Buf::new(body, ..)
1076 .wrap_in(UdpPacketBuilder::new(
1077 src_ip,
1078 dst_ip,
1079 NonZeroU16::new(1),
1080 NonZeroU16::new(2).unwrap(),
1081 ))
1082 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1083 .unwrap()
1084 .as_ref()
1085 .to_vec();
1086
1087 buf[packet_formats::udp::CHECKSUM_OFFSET] ^= 0xFF;
1089 buf[packet_formats::udp::CHECKSUM_OFFSET + 1] ^= 0xFF;
1090 buf
1091 }
1092
1093 fn build_nested_udp_packets_invalid_csums<I: IpAddress>(
1096 src_ip: I,
1097 dst_ip: I,
1098 nesting: usize,
1099 ) -> Vec<u8> {
1100 let mut payload = alloc::vec![0u8; 100];
1101 for _ in 0..nesting {
1102 payload = build_udp_packet_invalid_csum(src_ip, dst_ip, &mut payload);
1103 }
1104 payload
1105 }
1106
1107 #[test]
1108 fn checksum_rx_offloading_none() {
1109 let buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 1);
1110
1111 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(None));
1114 let mut buf_ref: &[u8] = buf.as_ref();
1115 assert_eq!(
1116 buf_ref
1117 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1118 SRC_IP_V4, DST_IP_V4, &mut ctx
1119 ))
1120 .err(),
1121 Some(ParseError::Checksum)
1122 );
1123 }
1124
1125 #[test]
1126 fn checksum_rx_offloading_fully_offloaded() {
1127 let mut buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 3);
1128
1129 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::FullyOffloaded);
1132 for _ in 0..3 {
1133 let mut buf_ref: &[u8] = buf.as_ref();
1134 buf = buf_ref
1135 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1136 SRC_IP_V4, DST_IP_V4, &mut ctx,
1137 ))
1138 .expect("udp parse should succeed")
1139 .body()
1140 .to_vec();
1141 }
1142 }
1143
1144 #[test]
1145 fn checksum_rx_offloading_offloaded() {
1146 let mut buf = build_nested_udp_packets_invalid_csums(SRC_IP_V4, DST_IP_V4, 3);
1147
1148 let mut ctx = NetworkParsingContext::new(ChecksumRxOffloading::Offloaded(Some(
1151 NonZeroU16::new(2).unwrap(),
1152 )));
1153 for _ in 0..2 {
1154 let mut buf_ref: &[u8] = buf.as_ref();
1155 buf = buf_ref
1156 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1157 SRC_IP_V4, DST_IP_V4, &mut ctx,
1158 ))
1159 .expect("udp parse should succeed")
1160 .body()
1161 .to_vec();
1162 }
1163 let mut buf_ref: &[u8] = buf.as_ref();
1164 assert_eq!(
1165 buf_ref
1166 .parse_with::<_, UdpPacket<_>>(UdpParseArgs::with_context(
1167 SRC_IP_V4, DST_IP_V4, &mut ctx
1168 ))
1169 .err(),
1170 Some(ParseError::Checksum)
1171 );
1172 }
1173}