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