1use alloc::collections::{BTreeSet, BinaryHeap};
34use alloc::vec::Vec;
35use core::cmp::Ordering;
36use core::fmt::Debug;
37use core::hash::Hash;
38use core::time::Duration;
39
40use assert_matches::assert_matches;
41use log::debug;
42use net_types::ip::{GenericOverIp, Ip, IpAddr, IpVersionMarker, Ipv4, Ipv6};
43use netstack3_base::{
44 CoreTimerContext, HandleableTimer, InstantBindingsTypes, IpExt, LocalTimerHeap,
45 TimerBindingsTypes, TimerContext,
46};
47use netstack3_hashmap::hash_map::{Entry, HashMap};
48use packet::{BufferViewMut, ParsablePacket as _};
49use packet_formats::ip::{IpPacket, Ipv4Proto};
50use packet_formats::ipv4::{Ipv4Header, Ipv4Packet};
51use packet_formats::ipv6::Ipv6Packet;
52use packet_formats::ipv6::ext_hdrs::Ipv6ExtensionHeader;
53use zerocopy::{SplitByteSlice, SplitByteSliceMut};
54
55pub trait ReassemblyIpExt: IpExt {
57 const REASSEMBLY_TIMEOUT: Duration;
63
64 type FragmentCacheKeyPart: Copy + Clone + Debug + Hash + PartialEq + Eq;
67
68 fn ip_specific_key_part<B: SplitByteSlice>(
71 packet: &Self::Packet<B>,
72 ) -> Self::FragmentCacheKeyPart;
73}
74
75impl ReassemblyIpExt for Ipv4 {
76 const REASSEMBLY_TIMEOUT: Duration = Duration::from_secs(15);
80
81 type FragmentCacheKeyPart = Ipv4Proto;
88
89 fn ip_specific_key_part<B: SplitByteSlice>(
90 packet: &Self::Packet<B>,
91 ) -> Self::FragmentCacheKeyPart {
92 IpPacket::proto(packet)
93 }
94}
95
96impl ReassemblyIpExt for Ipv6 {
97 const REASSEMBLY_TIMEOUT: Duration = Duration::from_secs(60);
104
105 type FragmentCacheKeyPart = ();
111
112 fn ip_specific_key_part<B: SplitByteSlice>(
113 _packet: &Self::Packet<B>,
114 ) -> Self::FragmentCacheKeyPart {
115 ()
116 }
117}
118
119const FRAGMENT_BLOCK_SIZE: u8 = 8;
129
130const MAX_FRAGMENT_BLOCKS: u16 = 8191;
135
136pub trait FragmentContext<I: Ip, BT: FragmentBindingsTypes> {
138 fn with_state_mut<O, F: FnOnce(&mut IpPacketFragmentCache<I, BT>) -> O>(&mut self, cb: F) -> O;
140}
141
142pub trait FragmentBindingsTypes: TimerBindingsTypes + InstantBindingsTypes {}
144impl<BT> FragmentBindingsTypes for BT where BT: TimerBindingsTypes + InstantBindingsTypes {}
145
146pub trait FragmentBindingsContext: TimerContext + FragmentBindingsTypes {}
148impl<BC> FragmentBindingsContext for BC where BC: TimerContext + FragmentBindingsTypes {}
149
150#[derive(Hash, Eq, PartialEq, Default, Clone, Debug, GenericOverIp)]
152#[generic_over_ip(I, Ip)]
153pub struct FragmentTimerId<I: Ip>(IpVersionMarker<I>);
154
155pub trait FragmentHandler<I: ReassemblyIpExt, BC> {
157 fn process_fragment<B: SplitByteSlice>(
163 &mut self,
164 bindings_ctx: &mut BC,
165 packet: I::Packet<B>,
166 ) -> FragmentProcessingState<I, B>
167 where
168 I::Packet<B>: FragmentablePacket;
169
170 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
190 &mut self,
191 bindings_ctx: &mut BC,
192 key: &FragmentCacheKey<I>,
193 buffer: BV,
194 ) -> Result<usize, FragmentReassemblyError>;
195}
196
197impl<I: IpExt + ReassemblyIpExt, BC: FragmentBindingsContext, CC: FragmentContext<I, BC>>
198 FragmentHandler<I, BC> for CC
199{
200 fn process_fragment<B: SplitByteSlice>(
201 &mut self,
202 bindings_ctx: &mut BC,
203 packet: I::Packet<B>,
204 ) -> FragmentProcessingState<I, B>
205 where
206 I::Packet<B>: FragmentablePacket,
207 {
208 self.with_state_mut(|cache| {
209 let (res, timer_action) = cache.process_fragment(packet);
210
211 if let Some(timer_action) = timer_action {
212 match timer_action {
213 CacheTimerAction::CreateNewTimer(key) => {
216 assert_eq!(
217 cache.timers.schedule_after(
218 bindings_ctx,
219 key,
220 (),
221 I::REASSEMBLY_TIMEOUT,
222 ),
223 None
224 )
225 }
226 CacheTimerAction::CancelExistingTimer(key) => {
227 assert_ne!(cache.timers.cancel(bindings_ctx, &key), None)
228 }
229 }
230 }
231
232 res
233 })
234 }
235
236 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
237 &mut self,
238 bindings_ctx: &mut BC,
239 key: &FragmentCacheKey<I>,
240 buffer: BV,
241 ) -> Result<usize, FragmentReassemblyError> {
242 self.with_state_mut(|cache| {
243 let res = cache.reassemble_packet(key, buffer);
244
245 match res {
246 Ok(_) | Err(FragmentReassemblyError::PacketParsingError) => {
247 assert_matches!(cache.timers.cancel(bindings_ctx, key), Some(_));
251 }
252 Err(FragmentReassemblyError::InvalidKey)
253 | Err(FragmentReassemblyError::MissingFragments) => {}
254 }
255
256 res
257 })
258 }
259}
260
261impl<I: ReassemblyIpExt, BC: FragmentBindingsContext, CC: FragmentContext<I, BC>>
262 HandleableTimer<CC, BC> for FragmentTimerId<I>
263{
264 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
265 let Self(IpVersionMarker { .. }) = self;
266 core_ctx.with_state_mut(|cache| {
267 let Some((key, ())) = cache.timers.pop(bindings_ctx) else {
268 return;
269 };
270
271 let FragmentCacheData {
273 missing_blocks: _,
274 body_fragments,
275 header: _,
276 total_size,
277 max_fragment_len: _,
278 } = assert_matches!(cache.remove_data(&key), Some(c) => c);
279 debug!(
280 "reassembly for {key:?} \
281 timed out with {} fragments and {total_size} bytes",
282 body_fragments.len(),
283 );
284 });
285 }
286}
287
288pub trait FragmentablePacket {
290 fn fragment_data(&self) -> (u32, u16, bool);
300}
301
302impl<B: SplitByteSlice> FragmentablePacket for Ipv4Packet<B> {
303 fn fragment_data(&self) -> (u32, u16, bool) {
304 (u32::from(self.id()), self.fragment_offset().into_raw(), self.mf_flag())
305 }
306}
307
308impl<B: SplitByteSlice> FragmentablePacket for Ipv6Packet<B> {
309 fn fragment_data(&self) -> (u32, u16, bool) {
310 for ext_hdr in self.iter_extension_hdrs() {
311 if let Ipv6ExtensionHeader::Fragment { fragment_data } = ext_hdr {
312 return (
313 fragment_data.identification(),
314 fragment_data.fragment_offset().into_raw(),
315 fragment_data.m_flag(),
316 );
317 }
318 }
319
320 unreachable!(
321 "Should never call this function if the packet does not have a fragment header"
322 );
323 }
324}
325
326#[derive(Debug)]
328pub enum FragmentProcessingState<I: ReassemblyIpExt, B: SplitByteSlice> {
329 NotNeeded(I::Packet<B>),
332
333 InvalidFragment,
345
346 NeedMoreFragments,
350
351 OutOfMemory,
354
355 Ready { key: FragmentCacheKey<I>, packet_len: usize },
360}
361
362#[derive(Debug, PartialEq, Eq)]
364pub enum FragmentReassemblyError {
365 MissingFragments,
367
368 InvalidKey,
373
374 PacketParsingError,
376}
377
378#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
383pub struct FragmentCacheKey<I: ReassemblyIpExt> {
384 src_ip: I::Addr,
385 dst_ip: I::Addr,
386 fragment_id: u32,
387 ip_specific_fields: I::FragmentCacheKeyPart,
388}
389
390#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
395struct BlockRange {
396 start: u16,
397 end: u16,
398}
399
400#[derive(Debug)]
402struct FragmentCacheData {
403 missing_blocks: BTreeSet<BlockRange>,
411
412 body_fragments: BinaryHeap<PacketBodyFragment>,
424
425 header: Option<Vec<u8>>,
430
431 total_size: usize,
437
438 max_fragment_len: usize,
440}
441
442impl Default for FragmentCacheData {
443 fn default() -> FragmentCacheData {
444 FragmentCacheData {
445 missing_blocks: core::iter::once(BlockRange { start: 0, end: u16::MAX }).collect(),
446 body_fragments: BinaryHeap::new(),
447 header: None,
448 total_size: 0,
449 max_fragment_len: 0,
450 }
451 }
452}
453
454impl FragmentCacheData {
455 fn find_gap(&self, BlockRange { start, end }: BlockRange) -> FindGapResult {
457 let result = self.missing_blocks.iter().find_map(|gap| {
458 if gap.start <= start && gap.end >= end {
460 return Some(FindGapResult::Found { gap: *gap });
461 }
462
463 if gap.start > end || gap.end < start {
466 return None;
467 }
468
469 return Some(FindGapResult::Overlap);
472 });
473
474 match result {
475 Some(result) => result,
476 None => {
477 let last = self.body_fragments.peek().unwrap();
487 if last.offset < start {
488 FindGapResult::OutOfBounds
489 } else {
490 FindGapResult::Duplicate
491 }
492 }
493 }
494 }
495}
496
497enum FindGapResult {
499 Found {
502 gap: BlockRange,
503 },
504 Overlap,
508 OutOfBounds,
510 Duplicate,
526}
527
528#[derive(Debug)]
530pub struct IpPacketFragmentCache<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
531 cache: HashMap<FragmentCacheKey<I>, FragmentCacheData>,
532 data_size: usize,
534 num_fragments: usize,
536 capacity: FragmentCacheCapacity,
537 timers: LocalTimerHeap<FragmentCacheKey<I>, (), BT>,
538}
539
540impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> IpPacketFragmentCache<I, BC> {
541 pub fn new<CC: CoreTimerContext<FragmentTimerId<I>, BC>>(
543 bindings_ctx: &mut BC,
544 ) -> IpPacketFragmentCache<I, BC> {
545 IpPacketFragmentCache {
546 cache: HashMap::new(),
547 data_size: 0,
548 num_fragments: 0,
549 capacity: FragmentCacheCapacity::default(),
550 timers: LocalTimerHeap::new(bindings_ctx, CC::convert_timer(Default::default())),
551 }
552 }
553}
554
555enum CacheTimerAction<I: ReassemblyIpExt> {
556 CreateNewTimer(FragmentCacheKey<I>),
557 CancelExistingTimer(FragmentCacheKey<I>),
558}
559
560impl<I: ReassemblyIpExt, BT: FragmentBindingsTypes> IpPacketFragmentCache<I, BT> {
561 fn process_fragment<B: SplitByteSlice>(
567 &mut self,
568 packet: I::Packet<B>,
569 ) -> (FragmentProcessingState<I, B>, Option<CacheTimerAction<I>>)
570 where
571 I::Packet<B>: FragmentablePacket,
572 {
573 if self.above_capacity() {
574 return (FragmentProcessingState::OutOfMemory, None);
575 }
576
577 let (id, offset, m_flag) = packet.fragment_data();
579
580 if offset == 0 && !m_flag {
585 return (FragmentProcessingState::NotNeeded(packet), None);
586 }
587
588 if packet.body().is_empty() {
593 return (FragmentProcessingState::NeedMoreFragments, None);
594 }
595
596 if m_flag && (packet.body().len() % (FRAGMENT_BLOCK_SIZE as usize) != 0) {
600 return (FragmentProcessingState::InvalidFragment, None);
601 }
602
603 let key = FragmentCacheKey {
605 src_ip: packet.src_ip(),
606 dst_ip: packet.dst_ip(),
607 fragment_id: id,
608 ip_specific_fields: I::ip_specific_key_part(&packet),
609 };
610
611 let num_fragment_blocks = 1 + ((packet.body().len() - 1) / (FRAGMENT_BLOCK_SIZE as usize));
625 assert!(num_fragment_blocks > 0);
626
627 let fragment_blocks_range =
633 if let Ok(offset_end) = u16::try_from((offset as usize) + num_fragment_blocks - 1) {
634 if offset_end <= MAX_FRAGMENT_BLOCKS {
635 BlockRange { start: offset, end: offset_end }
636 } else {
637 return (FragmentProcessingState::InvalidFragment, None);
638 }
639 } else {
640 return (FragmentProcessingState::InvalidFragment, None);
641 };
642
643 let (fragment_data, timer_not_yet_scheduled) = self.get_or_create(key);
645
646 let found_gap = match fragment_data.find_gap(fragment_blocks_range) {
648 FindGapResult::Overlap | FindGapResult::OutOfBounds => {
649 assert_matches!(self.remove_data(&key), Some(_));
661
662 return (
663 FragmentProcessingState::InvalidFragment,
664 (!timer_not_yet_scheduled)
665 .then_some(CacheTimerAction::CancelExistingTimer(key)),
666 );
667 }
668 FindGapResult::Duplicate => {
669 return (FragmentProcessingState::NeedMoreFragments, None);
681 }
682 FindGapResult::Found { gap } => gap,
683 };
684
685 let timer_id = timer_not_yet_scheduled.then_some(CacheTimerAction::CreateNewTimer(key));
686
687 if !m_flag && found_gap.end < u16::MAX {
688 return (FragmentProcessingState::InvalidFragment, timer_id);
692 }
693
694 assert!(fragment_data.missing_blocks.remove(&found_gap));
697
698 if found_gap.start < fragment_blocks_range.start {
715 assert!(fragment_data.missing_blocks.insert(BlockRange {
716 start: found_gap.start,
717 end: fragment_blocks_range.start - 1
718 }));
719 }
720
721 if found_gap.end > fragment_blocks_range.end && m_flag {
756 assert!(
757 fragment_data.missing_blocks.insert(BlockRange {
758 start: fragment_blocks_range.end + 1,
759 end: found_gap.end
760 })
761 );
762 } else {
763 assert!(
769 found_gap.end == fragment_blocks_range.end
770 || (!m_flag && found_gap.end == u16::MAX),
771 "found_gap: {:?}, fragment_blocks_range: {:?} offset: {:?}, m_flag: {:?}",
772 found_gap,
773 fragment_blocks_range,
774 offset,
775 m_flag
776 );
777 }
778
779 let mut added_bytes = 0;
780 let fragment_len = packet.parse_metadata().header_len() + packet.body().len();
781 fragment_data.max_fragment_len = fragment_data.max_fragment_len.max(fragment_len);
782 if offset == 0 {
784 assert_eq!(fragment_data.header, None);
785 let header = get_header::<B, I>(&packet);
786 added_bytes = header.len();
787 fragment_data.header = Some(header);
788 }
789
790 let mut body = Vec::with_capacity(packet.body().len());
792 body.extend_from_slice(packet.body());
793 added_bytes += body.len();
794 fragment_data.total_size += added_bytes;
795 fragment_data.body_fragments.push(PacketBodyFragment::new(offset, body));
796
797 let result = if fragment_data.missing_blocks.is_empty() {
803 FragmentProcessingState::Ready { key, packet_len: fragment_data.total_size }
804 } else {
805 FragmentProcessingState::NeedMoreFragments
806 };
807
808 self.track_fragment(added_bytes);
809 (result, timer_id)
810 }
811
812 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
832 &mut self,
833 key: &FragmentCacheKey<I>,
834 buffer: BV,
835 ) -> Result<usize, FragmentReassemblyError> {
836 let entry = match self.cache.entry(*key) {
837 Entry::Occupied(entry) => entry,
838 Entry::Vacant(_) => return Err(FragmentReassemblyError::InvalidKey),
839 };
840
841 if !entry.get().missing_blocks.is_empty() {
843 return Err(FragmentReassemblyError::MissingFragments);
844 }
845 let (_key, data) = entry.remove_entry();
848 self.untrack_data(&data);
849
850 let header = data.header.expect("should have header if we're not missing fragments");
852
853 let fragments = data.body_fragments.into_sorted_vec();
856 let body_fragments = fragments.iter().map(|x| x.data.as_slice());
857 I::Packet::reassemble_fragmented_packet(buffer, header.as_slice(), body_fragments)
858 .map_err(|_| FragmentReassemblyError::PacketParsingError)?;
859 Ok(data.max_fragment_len)
860 }
861
862 fn get_or_create(&mut self, key: FragmentCacheKey<I>) -> (&mut FragmentCacheData, bool) {
867 match self.cache.entry(key) {
868 Entry::Occupied(e) => (e.into_mut(), false),
869 Entry::Vacant(e) => {
870 (e.insert(FragmentCacheData::default()), true)
875 }
876 }
877 }
878
879 fn above_capacity(&self) -> bool {
880 self.data_size >= self.capacity.max_data_bytes
881 || self.num_fragments >= self.capacity.max_fragments
882 || self.cache.len() >= self.capacity.max_keys
883 }
884
885 fn track_fragment(&mut self, added_bytes: usize) {
886 self.data_size += added_bytes;
887 self.num_fragments += 1;
888 }
889
890 fn untrack_data(&mut self, data: &FragmentCacheData) {
891 self.data_size -= data.total_size;
892 self.num_fragments -= data.body_fragments.len();
893 }
894
895 fn remove_data(&mut self, key: &FragmentCacheKey<I>) -> Option<FragmentCacheData> {
896 let data = self.cache.remove(key)?;
897 self.untrack_data(&data);
898 Some(data)
899 }
900}
901
902fn get_header<B: SplitByteSlice, I: IpExt>(packet: &I::Packet<B>) -> Vec<u8> {
904 match packet.as_ip_addr_ref() {
905 IpAddr::V4(packet) => packet.copy_header_bytes_for_fragment(),
906 IpAddr::V6(packet) => {
907 packet.copy_header_bytes_for_fragment()
912 }
913 }
914}
915
916#[derive(Debug, PartialEq, Eq)]
918struct PacketBodyFragment {
919 offset: u16,
920 data: Vec<u8>,
921}
922
923impl PacketBodyFragment {
924 fn new(offset: u16, data: Vec<u8>) -> Self {
926 PacketBodyFragment { offset, data }
927 }
928}
929
930impl PartialOrd for PacketBodyFragment {
933 fn partial_cmp(&self, other: &PacketBodyFragment) -> Option<Ordering> {
934 Some(self.cmp(other))
935 }
936}
937
938impl Ord for PacketBodyFragment {
939 fn cmp(&self, other: &Self) -> Ordering {
940 self.offset.cmp(&other.offset)
941 }
942}
943
944#[derive(Copy, Clone, Debug, Eq, PartialEq)]
950struct FragmentCacheCapacity {
951 max_data_bytes: usize,
953 max_keys: usize,
955 max_fragments: usize,
957}
958
959impl FragmentCacheCapacity {
960 const DEFAULT_MAX_DATA_BYTES: usize = 4 * 1024 * 1024;
964
965 const DEFAULT_MAX_KEYS: usize = 2048;
972
973 const DEFAULT_MAX_FRAGMENTS: usize = Self::DEFAULT_MAX_KEYS * 8;
979}
980
981impl Default for FragmentCacheCapacity {
982 fn default() -> Self {
983 Self {
984 max_data_bytes: Self::DEFAULT_MAX_DATA_BYTES,
985 max_keys: Self::DEFAULT_MAX_KEYS,
986 max_fragments: Self::DEFAULT_MAX_FRAGMENTS,
987 }
988 }
989}
990
991#[cfg(test)]
992mod tests {
993 use alloc::vec;
994
995 use assert_matches::assert_matches;
996 use ip_test_macro::ip_test;
997 use net_declare::{net_ip_v4, net_ip_v6};
998 use net_types::Witness;
999 use net_types::ip::{IpVersion, Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
1000 use netstack3_base::testutil::{
1001 FakeBindingsCtx, FakeCoreCtx, FakeInstant, FakeTimerCtxExt, TEST_ADDRS_V4, TEST_ADDRS_V6,
1002 assert_empty,
1003 };
1004 use netstack3_base::{CtxPair, IntoCoreTimerCtx, NetworkSerializationContext};
1005 use packet::{Buf, NestablePacketBuilder as _, ParsablePacket, ParseBuffer, Serializer};
1006 use packet_formats::ip::{FragmentOffset, IpProto, Ipv6Proto};
1007 use packet_formats::ipv4::Ipv4PacketBuilder;
1008 use packet_formats::ipv6::ext_hdrs::IPV6_FRAGMENT_EXT_HDR_LEN;
1009 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithFragmentHeader};
1010 use test_case::test_case;
1011
1012 use super::*;
1013
1014 struct FakeFragmentContext<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
1015 cache: IpPacketFragmentCache<I, BT>,
1016 }
1017
1018 impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> FakeFragmentContext<I, BC>
1019 where
1020 BC::DispatchId: From<FragmentTimerId<I>>,
1021 {
1022 fn new(bindings_ctx: &mut BC) -> Self {
1023 Self { cache: IpPacketFragmentCache::new::<IntoCoreTimerCtx>(bindings_ctx) }
1024 }
1025 }
1026
1027 type FakeCtxImpl<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
1028 type FakeBindingsCtxImpl<I> = FakeBindingsCtx<FragmentTimerId<I>, (), (), ()>;
1029 type FakeCoreCtxImpl<I> = FakeCoreCtx<FakeFragmentContext<I, FakeBindingsCtxImpl<I>>, (), ()>;
1030
1031 impl<I: ReassemblyIpExt> FragmentContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
1032 fn with_state_mut<
1033 O,
1034 F: FnOnce(&mut IpPacketFragmentCache<I, FakeBindingsCtxImpl<I>>) -> O,
1035 >(
1036 &mut self,
1037 cb: F,
1038 ) -> O {
1039 cb(&mut self.state.cache)
1040 }
1041 }
1042
1043 #[derive(PartialEq)]
1046 enum ExpectedResult<I: ReassemblyIpExt> {
1047 Ready { body_fragment_blocks: u16, key: FragmentCacheKey<I> },
1052
1053 NeedMore,
1056
1057 Invalid,
1059
1060 OutOfMemory,
1062 }
1063
1064 fn get_ipv4_builder() -> Ipv4PacketBuilder {
1066 Ipv4PacketBuilder::new(
1067 TEST_ADDRS_V4.remote_ip,
1068 TEST_ADDRS_V4.local_ip,
1069 10,
1070 <Ipv4 as TestIpExt>::PROTOCOL,
1071 )
1072 }
1073
1074 fn get_ipv6_builder() -> Ipv6PacketBuilder {
1076 Ipv6PacketBuilder::new(
1077 TEST_ADDRS_V6.remote_ip,
1078 TEST_ADDRS_V6.local_ip,
1079 10,
1080 <Ipv6 as TestIpExt>::PROTOCOL,
1081 )
1082 }
1083
1084 fn validate_size<I: ReassemblyIpExt, BT: FragmentBindingsTypes>(
1086 cache: &IpPacketFragmentCache<I, BT>,
1087 ) {
1088 let mut data_size: usize = 0;
1089 let mut num_fragments: usize = 0;
1090
1091 for v in cache.cache.values() {
1092 data_size += v.total_size;
1093 num_fragments += v.body_fragments.len();
1094 }
1095
1096 assert_eq!(data_size, cache.data_size);
1097 assert_eq!(num_fragments, cache.num_fragments);
1098 }
1099
1100 struct FragmentSpec {
1101 id: u16,
1103 offset: u16,
1105 size: u16,
1107 m_flag: bool,
1109 }
1110
1111 fn expected_packet_size<I: TestIpExt>(num_fragment_blocks: u16) -> usize {
1112 usize::from(num_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE) + I::HEADER_LENGTH
1113 }
1114
1115 fn process_ipv4_fragment<CC: FragmentContext<Ipv4, BC>, BC: FragmentBindingsContext>(
1117 core_ctx: &mut CC,
1118 bindings_ctx: &mut BC,
1119 FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1120 mut builder: Ipv4PacketBuilder,
1121 expected_result: ExpectedResult<Ipv4>,
1122 ) {
1123 builder.id(id);
1124 builder.fragment_offset(FragmentOffset::new(offset).unwrap());
1125 builder.mf_flag(m_flag);
1126 let body = generate_body_fragment(
1127 id,
1128 offset,
1129 usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1130 );
1131
1132 let mut buffer = builder
1133 .wrap_body(Buf::new(body, ..))
1134 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1135 .unwrap();
1136 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1137
1138 let actual_result =
1139 FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1140 match expected_result {
1141 ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1142 let (key, packet_len) = assert_matches!(
1143 actual_result,
1144 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1145 );
1146 assert_eq!(key, expected_key);
1147 assert_eq!(packet_len, expected_packet_size::<Ipv4>(body_fragment_blocks));
1148 }
1149 ExpectedResult::NeedMore => {
1150 assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1151 }
1152 ExpectedResult::Invalid => {
1153 assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1154 }
1155 ExpectedResult::OutOfMemory => {
1156 assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1157 }
1158 }
1159 }
1160
1161 fn process_ipv6_fragment<CC: FragmentContext<Ipv6, BC>, BC: FragmentBindingsContext>(
1165 core_ctx: &mut CC,
1166 bindings_ctx: &mut BC,
1167 FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1168 builder: Ipv6PacketBuilder,
1169 expected_result: ExpectedResult<Ipv6>,
1170 ) {
1171 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1172 builder,
1173 FragmentOffset::new(offset).unwrap(),
1174 m_flag,
1175 id.into(),
1176 );
1177
1178 let body = generate_body_fragment(
1179 id,
1180 offset,
1181 usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1182 );
1183
1184 let mut buffer = builder
1185 .wrap_body(Buf::new(body, ..))
1186 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1187 .unwrap();
1188 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1189
1190 let actual_result =
1191 FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1192 match expected_result {
1193 ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1194 let (key, packet_len) = assert_matches!(
1195 actual_result,
1196 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1197 );
1198 assert_eq!(key, expected_key);
1199 assert_eq!(packet_len, expected_packet_size::<Ipv6>(body_fragment_blocks));
1200 }
1201 ExpectedResult::NeedMore => {
1202 assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1203 }
1204 ExpectedResult::Invalid => {
1205 assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1206 }
1207 ExpectedResult::OutOfMemory => {
1208 assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1209 }
1210 }
1211 }
1212
1213 trait TestIpExt: IpExt + netstack3_base::testutil::TestIpExt + ReassemblyIpExt {
1214 const HEADER_LENGTH: usize;
1215
1216 const PROTOCOL: Self::Proto;
1217
1218 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1219 core_ctx: &mut CC,
1220 bindings_ctx: &mut BC,
1221 spec: FragmentSpec,
1222 expected_result: ExpectedResult<Self>,
1223 );
1224 }
1225
1226 impl TestIpExt for Ipv4 {
1227 const HEADER_LENGTH: usize = packet_formats::ipv4::HDR_PREFIX_LEN;
1228
1229 const PROTOCOL: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1230
1231 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1232 core_ctx: &mut CC,
1233 bindings_ctx: &mut BC,
1234 spec: FragmentSpec,
1235 expected_result: ExpectedResult<Ipv4>,
1236 ) {
1237 process_ipv4_fragment(core_ctx, bindings_ctx, spec, get_ipv4_builder(), expected_result)
1238 }
1239 }
1240 impl TestIpExt for Ipv6 {
1241 const HEADER_LENGTH: usize = packet_formats::ipv6::IPV6_FIXED_HDR_LEN;
1242
1243 const PROTOCOL: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1244
1245 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1246 core_ctx: &mut CC,
1247 bindings_ctx: &mut BC,
1248 spec: FragmentSpec,
1249 expected_result: ExpectedResult<Ipv6>,
1250 ) {
1251 process_ipv6_fragment(core_ctx, bindings_ctx, spec, get_ipv6_builder(), expected_result)
1252 }
1253 }
1254
1255 fn expected_max_fragment_len<I: TestIpExt>(num_blocks: u16) -> usize {
1256 let mut len = I::HEADER_LENGTH + usize::from(num_blocks) * usize::from(FRAGMENT_BLOCK_SIZE);
1257 if I::VERSION == IpVersion::V6 {
1258 len += IPV6_FRAGMENT_EXT_HDR_LEN;
1259 }
1260 len
1261 }
1262
1263 fn try_reassemble_ip_packet<
1267 I: TestIpExt + netstack3_base::IpExt,
1268 CC: FragmentContext<I, BC>,
1269 BC: FragmentBindingsContext,
1270 >(
1271 core_ctx: &mut CC,
1272 bindings_ctx: &mut BC,
1273 fragment_id: u16,
1274 body_fragment_blocks: u16,
1275 expected_max_fragment_len: usize,
1276 ) {
1277 let mut buffer: Vec<u8> = vec![
1278 0;
1279 usize::from(body_fragment_blocks)
1280 * usize::from(FRAGMENT_BLOCK_SIZE)
1281 + I::HEADER_LENGTH
1282 ];
1283 let mut buffer = &mut buffer[..];
1284 let key = test_key(fragment_id);
1285 let max_fragment_len =
1286 FragmentHandler::reassemble_packet(core_ctx, bindings_ctx, &key, &mut buffer).unwrap();
1287 assert_eq!(max_fragment_len, expected_max_fragment_len);
1288 let packet = I::Packet::parse_mut(&mut buffer, ()).unwrap();
1289
1290 let expected_body = generate_body_fragment(
1291 fragment_id,
1292 0,
1293 usize::from(body_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE),
1294 );
1295 assert_eq!(packet.body(), &expected_body[..]);
1296 }
1297
1298 fn generate_body_fragment(fragment_id: u16, fragment_offset: u16, len: usize) -> Vec<u8> {
1304 let start = usize::from(fragment_id)
1308 + usize::from(fragment_offset) * usize::from(FRAGMENT_BLOCK_SIZE);
1309 (start..start + len).map(|byte| byte as u8).collect()
1310 }
1311
1312 fn test_key<I: TestIpExt>(id: u16) -> FragmentCacheKey<I> {
1314 #[derive(GenericOverIp)]
1315 #[generic_over_ip(I, Ip)]
1316 struct Wrapper<I: ReassemblyIpExt>(I::FragmentCacheKeyPart);
1317
1318 let Wrapper(ip_specific_fields) =
1319 I::map_ip_out((), |()| Wrapper(Ipv4::PROTOCOL), |()| Wrapper(()));
1320
1321 FragmentCacheKey {
1322 src_ip: I::TEST_ADDRS.remote_ip.get(),
1323 dst_ip: I::TEST_ADDRS.local_ip.get(),
1324 fragment_id: id.into(),
1325 ip_specific_fields,
1326 }
1327 }
1328
1329 fn new_context<I: ReassemblyIpExt>() -> FakeCtxImpl<I> {
1330 FakeCtxImpl::<I>::with_default_bindings_ctx(|bindings_ctx| {
1331 FakeCoreCtxImpl::with_state(FakeFragmentContext::new(bindings_ctx))
1332 })
1333 }
1334
1335 #[test]
1336 fn test_ipv4_reassembly_not_needed() {
1337 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1338
1339 let builder = get_ipv4_builder();
1343 let body = [1, 2, 3, 4, 5];
1344 let mut buffer = builder
1345 .wrap_body(Buf::new(body.to_vec(), ..))
1346 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1347 .unwrap();
1348 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1349 assert_matches!(
1350 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1351 FragmentProcessingState::NotNeeded(unfragmented) if unfragmented.body() == body
1352 );
1353 }
1354
1355 #[test]
1356 #[should_panic(
1357 expected = "internal error: entered unreachable code: Should never call this function if the packet does not have a fragment header"
1358 )]
1359 fn test_ipv6_reassembly_not_needed() {
1360 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1361
1362 let builder = get_ipv6_builder();
1366 let mut buffer = builder
1367 .wrap_body(Buf::new(vec![1, 2, 3, 4, 5], ..))
1368 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1369 .unwrap();
1370 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1371 assert_matches!(
1372 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1373 FragmentProcessingState::InvalidFragment
1374 );
1375 }
1376
1377 #[ip_test(I)]
1378 #[test_case(1)]
1379 #[test_case(10)]
1380 #[test_case(100)]
1381 fn test_ip_reassembly<I: TestIpExt>(size: u16) {
1382 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1383 let id = 5;
1384
1385 I::process_ip_fragment(
1389 &mut core_ctx,
1390 &mut bindings_ctx,
1391 FragmentSpec { id, offset: 0, size, m_flag: true },
1392 ExpectedResult::NeedMore,
1393 );
1394
1395 I::process_ip_fragment(
1397 &mut core_ctx,
1398 &mut bindings_ctx,
1399 FragmentSpec { id, offset: size, size, m_flag: true },
1400 ExpectedResult::NeedMore,
1401 );
1402
1403 I::process_ip_fragment(
1405 &mut core_ctx,
1406 &mut bindings_ctx,
1407 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1408 ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1409 );
1410
1411 try_reassemble_ip_packet(
1412 &mut core_ctx,
1413 &mut bindings_ctx,
1414 id,
1415 3 * size,
1416 expected_max_fragment_len::<I>(size),
1417 );
1418 }
1419
1420 #[test]
1421 fn test_ipv4_key_uniqueness() {
1422 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1423
1424 const RIGHT_SRC: Ipv4Addr = net_ip_v4!("192.0.2.1");
1425 const WRONG_SRC: Ipv4Addr = net_ip_v4!("192.0.2.2");
1426
1427 const RIGHT_DST: Ipv4Addr = net_ip_v4!("192.0.2.3");
1428 const WRONG_DST: Ipv4Addr = net_ip_v4!("192.0.2.4");
1429
1430 const RIGHT_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1431 const WRONG_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Udp);
1432
1433 const RIGHT_ID: u16 = 1;
1434 const WRONG_ID: u16 = 2;
1435
1436 const TTL: u8 = 1;
1437
1438 process_ipv4_fragment(
1440 &mut core_ctx,
1441 &mut bindings_ctx,
1442 FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1443 Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1444 ExpectedResult::NeedMore,
1445 );
1446
1447 for (id, src, dst, proto) in [
1450 (RIGHT_ID, RIGHT_SRC, RIGHT_DST, WRONG_PROTO),
1451 (RIGHT_ID, RIGHT_SRC, WRONG_DST, RIGHT_PROTO),
1452 (RIGHT_ID, WRONG_SRC, RIGHT_DST, RIGHT_PROTO),
1453 (WRONG_ID, RIGHT_SRC, RIGHT_DST, RIGHT_PROTO),
1454 ] {
1455 process_ipv4_fragment(
1456 &mut core_ctx,
1457 &mut bindings_ctx,
1458 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1459 Ipv4PacketBuilder::new(src, dst, TTL, proto),
1460 ExpectedResult::NeedMore,
1461 );
1462 }
1463
1464 const KEY: FragmentCacheKey<Ipv4> = FragmentCacheKey {
1467 src_ip: RIGHT_SRC,
1468 dst_ip: RIGHT_DST,
1469 fragment_id: RIGHT_ID as u32,
1470 ip_specific_fields: RIGHT_PROTO,
1471 };
1472 process_ipv4_fragment(
1473 &mut core_ctx,
1474 &mut bindings_ctx,
1475 FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1476 Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1477 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1478 );
1479 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv4>(2)];
1480 let mut buffer = &mut buffer[..];
1481 let max_fragment_len =
1482 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1483 .expect("reassembly should succeed");
1484 assert_eq!(max_fragment_len, expected_max_fragment_len::<Ipv4>(1));
1485 let _packet = Ipv4Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1486 }
1487
1488 #[test]
1489 fn test_ipv6_key_uniqueness() {
1490 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1491
1492 const RIGHT_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1493 const WRONG_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1494
1495 const RIGHT_DST: Ipv6Addr = net_ip_v6!("2001:0db8::3");
1496 const WRONG_DST: Ipv6Addr = net_ip_v6!("2001:0db8::4");
1497
1498 const RIGHT_ID: u16 = 1;
1499 const WRONG_ID: u16 = 2;
1500
1501 const TTL: u8 = 1;
1502
1503 process_ipv6_fragment(
1505 &mut core_ctx,
1506 &mut bindings_ctx,
1507 FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1508 Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1509 ExpectedResult::NeedMore,
1510 );
1511
1512 for (id, src, dst) in [
1515 (RIGHT_ID, RIGHT_SRC, WRONG_DST),
1516 (RIGHT_ID, WRONG_SRC, RIGHT_DST),
1517 (WRONG_ID, RIGHT_SRC, RIGHT_DST),
1518 ] {
1519 process_ipv6_fragment(
1520 &mut core_ctx,
1521 &mut bindings_ctx,
1522 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1523 Ipv6PacketBuilder::new(src, dst, TTL, Ipv6::PROTOCOL),
1524 ExpectedResult::NeedMore,
1525 );
1526 }
1527
1528 const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1531 src_ip: RIGHT_SRC,
1532 dst_ip: RIGHT_DST,
1533 fragment_id: RIGHT_ID as u32,
1534 ip_specific_fields: (),
1535 };
1536 process_ipv6_fragment(
1537 &mut core_ctx,
1538 &mut bindings_ctx,
1539 FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1540 Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1541 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1542 );
1543 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1544 let mut buffer = &mut buffer[..];
1545 let max_fragment_len =
1546 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1547 .expect("reassembly should succeed");
1548 assert_eq!(max_fragment_len, expected_max_fragment_len::<Ipv6>(1));
1549 let _packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1550 }
1551
1552 #[test]
1553 fn test_ipv6_reassemble_different_protocols() {
1554 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1555
1556 const SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1557 const DST: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1558 const ID: u16 = 1;
1559 const TTL: u8 = 1;
1560
1561 const PROTO1: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1562 const PROTO2: Ipv6Proto = Ipv6Proto::Proto(IpProto::Udp);
1563
1564 process_ipv6_fragment(
1566 &mut core_ctx,
1567 &mut bindings_ctx,
1568 FragmentSpec { id: ID, offset: 0, size: 1, m_flag: true },
1569 Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO1),
1570 ExpectedResult::NeedMore,
1571 );
1572
1573 const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1577 src_ip: SRC,
1578 dst_ip: DST,
1579 fragment_id: ID as u32,
1580 ip_specific_fields: (),
1581 };
1582 process_ipv6_fragment(
1583 &mut core_ctx,
1584 &mut bindings_ctx,
1585 FragmentSpec { id: ID, offset: 1, size: 1, m_flag: false },
1586 Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO2),
1587 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1588 );
1589 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1590 let mut buffer = &mut buffer[..];
1591 let max_fragment_len =
1592 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1593 .expect("reassembly should succeed");
1594 assert_eq!(max_fragment_len, expected_max_fragment_len::<Ipv6>(1));
1595 let packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1596 assert_eq!(packet.proto(), PROTO1);
1597 }
1598
1599 #[ip_test(I)]
1600 #[test_case(1)]
1601 #[test_case(10)]
1602 #[test_case(100)]
1603 fn test_ip_reassemble_with_missing_blocks<I: TestIpExt>(size: u16) {
1604 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1605 let id = 5;
1606
1607 I::process_ip_fragment(
1612 &mut core_ctx,
1613 &mut bindings_ctx,
1614 FragmentSpec { id, offset: 0, size, m_flag: true },
1615 ExpectedResult::NeedMore,
1616 );
1617
1618 I::process_ip_fragment(
1620 &mut core_ctx,
1621 &mut bindings_ctx,
1622 FragmentSpec { id, offset: size, size, m_flag: true },
1623 ExpectedResult::NeedMore,
1624 );
1625
1626 let mut buffer: Vec<u8> = vec![0; 1];
1627 let mut buffer = &mut buffer[..];
1628 let key = test_key(id);
1629 assert_eq!(
1630 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1631 .unwrap_err(),
1632 FragmentReassemblyError::MissingFragments,
1633 );
1634 }
1635
1636 #[ip_test(I)]
1637 fn test_ip_reassemble_after_timer<I: TestIpExt>() {
1638 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1639 let id = 5;
1640 let key = test_key::<I>(id);
1641
1642 bindings_ctx.timers.assert_no_timers_installed();
1644 assert_eq!(core_ctx.state.cache.data_size, 0);
1645 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1646
1647 I::process_ip_fragment(
1651 &mut core_ctx,
1652 &mut bindings_ctx,
1653 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1654 ExpectedResult::NeedMore,
1655 );
1656
1657 core_ctx.state.cache.timers.assert_timers([(
1659 key,
1660 (),
1661 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1662 )]);
1663 validate_size(&core_ctx.state.cache);
1664
1665 I::process_ip_fragment(
1667 &mut core_ctx,
1668 &mut bindings_ctx,
1669 FragmentSpec { id, offset: 1, size: 1, m_flag: true },
1670 ExpectedResult::NeedMore,
1671 );
1672 core_ctx.state.cache.timers.assert_timers([(
1674 key,
1675 (),
1676 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1677 )]);
1678 validate_size(&core_ctx.state.cache);
1679
1680 I::process_ip_fragment(
1682 &mut core_ctx,
1683 &mut bindings_ctx,
1684 FragmentSpec { id, offset: 2, size: 1, m_flag: false },
1685 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id) },
1686 );
1687 core_ctx.state.cache.timers.assert_timers([(
1689 key,
1690 (),
1691 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1692 )]);
1693 validate_size(&core_ctx.state.cache);
1694
1695 assert_eq!(
1697 bindings_ctx.trigger_next_timer(&mut core_ctx),
1698 Some(FragmentTimerId::<I>::default())
1699 );
1700
1701 bindings_ctx.timers.assert_no_timers_installed();
1703 assert_eq!(core_ctx.state.cache.data_size, 0);
1704 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1705
1706 let key = test_key(id);
1709 let packet_len = 44;
1710 let mut buffer: Vec<u8> = vec![0; packet_len];
1711 let mut buffer = &mut buffer[..];
1712 assert_eq!(
1713 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1714 .unwrap_err(),
1715 FragmentReassemblyError::InvalidKey,
1716 );
1717 }
1718
1719 #[ip_test(I)]
1720 #[test_case(1)]
1721 #[test_case(10)]
1722 #[test_case(100)]
1723 fn test_ip_fragment_cache_max_data_size<I: TestIpExt>(size: u16) {
1724 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1725 let mut id = 0;
1726 const THRESHOLD: usize = 8196usize;
1727
1728 assert_eq!(core_ctx.state.cache.data_size, 0);
1729 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1730 core_ctx.state.cache.capacity.max_data_bytes = THRESHOLD;
1731
1732 while core_ctx.state.cache.data_size + usize::from(size) <= THRESHOLD {
1735 I::process_ip_fragment(
1736 &mut core_ctx,
1737 &mut bindings_ctx,
1738 FragmentSpec { id, offset: 0, size, m_flag: true },
1739 ExpectedResult::NeedMore,
1740 );
1741 validate_size(&core_ctx.state.cache);
1742 id += 1;
1743 }
1744
1745 I::process_ip_fragment(
1747 &mut core_ctx,
1748 &mut bindings_ctx,
1749 FragmentSpec { id, offset: 0, size, m_flag: true },
1750 ExpectedResult::OutOfMemory,
1751 );
1752 validate_size(&core_ctx.state.cache);
1753
1754 let _timers = bindings_ctx
1756 .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1757 assert_eq!(core_ctx.state.cache.data_size, 0);
1758 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1759 validate_size(&core_ctx.state.cache);
1760
1761 I::process_ip_fragment(
1763 &mut core_ctx,
1764 &mut bindings_ctx,
1765 FragmentSpec { id, offset: 0, size, m_flag: true },
1766 ExpectedResult::NeedMore,
1767 );
1768 }
1769
1770 #[ip_test(I)]
1771 fn test_ip_fragment_cache_max_keys<I: TestIpExt>() {
1772 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1773 const MAX_KEYS: u16 = 3;
1774 core_ctx.state.cache.capacity.max_keys = MAX_KEYS.into();
1775
1776 for id in 0..MAX_KEYS {
1779 I::process_ip_fragment(
1780 &mut core_ctx,
1781 &mut bindings_ctx,
1782 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1783 ExpectedResult::NeedMore,
1784 );
1785 }
1786
1787 I::process_ip_fragment(
1789 &mut core_ctx,
1790 &mut bindings_ctx,
1791 FragmentSpec { id: MAX_KEYS, offset: 0, size: 1, m_flag: true },
1792 ExpectedResult::OutOfMemory,
1793 );
1794
1795 let _timers = bindings_ctx
1797 .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1798 assert_eq!(core_ctx.state.cache.data_size, 0);
1799 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1800 validate_size(&core_ctx.state.cache);
1801
1802 I::process_ip_fragment(
1804 &mut core_ctx,
1805 &mut bindings_ctx,
1806 FragmentSpec { id: MAX_KEYS, offset: 0, size: 1, m_flag: true },
1807 ExpectedResult::NeedMore,
1808 );
1809 }
1810
1811 #[ip_test(I)]
1812 fn test_ip_fragment_cache_max_fragments<I: TestIpExt>() {
1813 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1814 const MAX_FRAGMENTS: u16 = 3;
1815 core_ctx.state.cache.capacity.max_fragments = MAX_FRAGMENTS.into();
1816
1817 for offset in 0..MAX_FRAGMENTS {
1820 I::process_ip_fragment(
1821 &mut core_ctx,
1822 &mut bindings_ctx,
1823 FragmentSpec { id: 0, offset, size: 1, m_flag: true },
1824 ExpectedResult::NeedMore,
1825 );
1826 }
1827
1828 I::process_ip_fragment(
1830 &mut core_ctx,
1831 &mut bindings_ctx,
1832 FragmentSpec { id: 0, offset: MAX_FRAGMENTS, size: 1, m_flag: true },
1833 ExpectedResult::OutOfMemory,
1834 );
1835
1836 let _timers = bindings_ctx
1838 .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1839 assert_eq!(core_ctx.state.cache.data_size, 0);
1840 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1841 validate_size(&core_ctx.state.cache);
1842
1843 I::process_ip_fragment(
1845 &mut core_ctx,
1846 &mut bindings_ctx,
1847 FragmentSpec { id: 0, offset: MAX_FRAGMENTS, size: 1, m_flag: true },
1848 ExpectedResult::NeedMore,
1849 );
1850 }
1851
1852 #[ip_test(I)]
1853 #[test_case(1)]
1854 #[test_case(10)]
1855 #[test_case(100)]
1856 fn test_unordered_fragments<I: TestIpExt>(size: u16) {
1857 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1858 let id = 5;
1859
1860 I::process_ip_fragment(
1862 &mut core_ctx,
1863 &mut bindings_ctx,
1864 FragmentSpec { id, offset: 0, size, m_flag: true },
1865 ExpectedResult::NeedMore,
1866 );
1867
1868 I::process_ip_fragment(
1870 &mut core_ctx,
1871 &mut bindings_ctx,
1872 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1873 ExpectedResult::NeedMore,
1874 );
1875
1876 I::process_ip_fragment(
1878 &mut core_ctx,
1879 &mut bindings_ctx,
1880 FragmentSpec { id, offset: size, size, m_flag: true },
1881 ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1882 );
1883 }
1884
1885 #[ip_test(I)]
1886 #[test_case(1)]
1887 #[test_case(10)]
1888 #[test_case(100)]
1889 fn test_ip_duplicate_fragment<I: TestIpExt>(size: u16) {
1890 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1891 let id = 5;
1892
1893 I::process_ip_fragment(
1895 &mut core_ctx,
1896 &mut bindings_ctx,
1897 FragmentSpec { id, offset: 0, size, m_flag: true },
1898 ExpectedResult::NeedMore,
1899 );
1900
1901 I::process_ip_fragment(
1903 &mut core_ctx,
1904 &mut bindings_ctx,
1905 FragmentSpec { id, offset: 0, size, m_flag: true },
1906 ExpectedResult::NeedMore,
1907 );
1908
1909 I::process_ip_fragment(
1912 &mut core_ctx,
1913 &mut bindings_ctx,
1914 FragmentSpec { id, offset: size, size, m_flag: false },
1915 ExpectedResult::Ready { body_fragment_blocks: 2 * size, key: test_key(id) },
1916 );
1917
1918 try_reassemble_ip_packet(
1919 &mut core_ctx,
1920 &mut bindings_ctx,
1921 id,
1922 2 * size,
1923 expected_max_fragment_len::<I>(size),
1924 );
1925 }
1926
1927 #[ip_test(I)]
1928 #[test_case(1)]
1929 #[test_case(10)]
1930 #[test_case(100)]
1931 fn test_ip_out_of_bounds_fragment<I: TestIpExt>(size: u16) {
1932 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1933 let id = 5;
1934
1935 I::process_ip_fragment(
1937 &mut core_ctx,
1938 &mut bindings_ctx,
1939 FragmentSpec { id, offset: size, size, m_flag: false },
1940 ExpectedResult::NeedMore,
1941 );
1942
1943 I::process_ip_fragment(
1946 &mut core_ctx,
1947 &mut bindings_ctx,
1948 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1949 ExpectedResult::Invalid,
1950 );
1951 }
1952
1953 #[ip_test(I)]
1954 #[test_case(50, 100; "overlaps_front")]
1955 #[test_case(150, 100; "overlaps_back")]
1956 #[test_case(50, 200; "overlaps_both")]
1957 fn test_ip_overlapping_fragment<I: TestIpExt>(offset: u16, size: u16) {
1958 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1959 let id = 5;
1960
1961 I::process_ip_fragment(
1963 &mut core_ctx,
1964 &mut bindings_ctx,
1965 FragmentSpec { id, offset: 100, size: 100, m_flag: true },
1966 ExpectedResult::NeedMore,
1967 );
1968
1969 I::process_ip_fragment(
1972 &mut core_ctx,
1973 &mut bindings_ctx,
1974 FragmentSpec { id, offset, size, m_flag: true },
1975 ExpectedResult::Invalid,
1976 );
1977 }
1978
1979 #[test]
1980 fn test_ipv4_fragment_not_multiple_of_offset_unit() {
1981 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1982 let id = 0;
1983
1984 assert_eq!(core_ctx.state.cache.data_size, 0);
1985 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1986 process_ipv4_fragment(
1991 &mut core_ctx,
1992 &mut bindings_ctx,
1993 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1994 get_ipv4_builder(),
1995 ExpectedResult::NeedMore,
1996 );
1997
1998 let mut builder = get_ipv4_builder();
2001 builder.id(id);
2002 builder.fragment_offset(FragmentOffset::new(1).unwrap());
2003 builder.mf_flag(true);
2004 let mut body: Vec<u8> = Vec::new();
2007 body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
2008 let mut buffer = builder
2009 .wrap_body(Buf::new(body, ..))
2010 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2011 .unwrap();
2012 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
2013 assert_matches!(
2014 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
2015 FragmentProcessingState::InvalidFragment
2016 );
2017
2018 let mut builder = get_ipv4_builder();
2022 builder.id(id);
2023 builder.fragment_offset(FragmentOffset::new(1).unwrap());
2024 builder.mf_flag(false);
2025 let mut body: Vec<u8> = Vec::new();
2028 body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
2029 let mut buffer = builder
2030 .wrap_body(Buf::new(body, ..))
2031 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2032 .unwrap();
2033 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
2034 let (key, packet_len) = assert_matches!(
2035 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
2036 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
2037 );
2038 assert_eq!(key, test_key(id));
2039 assert_eq!(packet_len, 35);
2040 validate_size(&core_ctx.state.cache);
2041 let mut buffer: Vec<u8> = vec![0; packet_len];
2042 let mut buffer = &mut buffer[..];
2043 let max_fragment_len =
2044 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
2045 .unwrap();
2046 assert_eq!(max_fragment_len, expected_max_fragment_len::<Ipv4>(1));
2047 let packet = Ipv4Packet::parse_mut(&mut buffer, ()).unwrap();
2048 let mut expected_body: Vec<u8> = Vec::new();
2049 expected_body.extend(0..15);
2050 assert_eq!(packet.body(), &expected_body[..]);
2051 assert_eq!(core_ctx.state.cache.data_size, 0);
2052 assert_eq!(core_ctx.state.cache.num_fragments, 0);
2053 }
2054
2055 #[test]
2056 fn test_ipv6_fragment_not_multiple_of_offset_unit() {
2057 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
2058 let id = 0;
2059
2060 assert_eq!(core_ctx.state.cache.data_size, 0);
2061 assert_eq!(core_ctx.state.cache.num_fragments, 0);
2062 process_ipv6_fragment(
2067 &mut core_ctx,
2068 &mut bindings_ctx,
2069 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
2070 get_ipv6_builder(),
2071 ExpectedResult::NeedMore,
2072 );
2073
2074 let offset = 1;
2077 let body_size: usize = (FRAGMENT_BLOCK_SIZE - 1).into();
2078 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
2079 get_ipv6_builder(),
2080 FragmentOffset::new(offset).unwrap(),
2081 true,
2082 id.into(),
2083 );
2084 let body = generate_body_fragment(id, offset, body_size);
2085 let mut buffer = builder
2086 .wrap_body(Buf::new(body, ..))
2087 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2088 .unwrap();
2089 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
2090 assert_matches!(
2091 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
2092 FragmentProcessingState::InvalidFragment
2093 );
2094
2095 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
2099 get_ipv6_builder(),
2100 FragmentOffset::new(offset).unwrap(),
2101 false,
2102 id.into(),
2103 );
2104 let body = generate_body_fragment(id, offset, body_size);
2105 let mut buffer = builder
2106 .wrap_body(Buf::new(body, ..))
2107 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2108 .unwrap();
2109 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
2110 let (key, packet_len) = assert_matches!(
2111 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
2112 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
2113 );
2114 assert_eq!(key, test_key(id));
2115 assert_eq!(packet_len, 55);
2116
2117 validate_size(&core_ctx.state.cache);
2118 let mut buffer: Vec<u8> = vec![0; packet_len];
2119 let mut buffer = &mut buffer[..];
2120 let max_fragment_len =
2121 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
2122 .unwrap();
2123 assert_eq!(max_fragment_len, expected_max_fragment_len::<Ipv6>(1));
2124 let packet = Ipv6Packet::parse_mut(&mut buffer, ()).unwrap();
2125 let mut expected_body: Vec<u8> = Vec::new();
2126 expected_body.extend(0..15);
2127 assert_eq!(packet.body(), &expected_body[..]);
2128 assert_eq!(core_ctx.state.cache.data_size, 0);
2129 assert_eq!(core_ctx.state.cache.num_fragments, 0);
2130 }
2131
2132 #[ip_test(I)]
2133 fn test_ip_reassembly_with_multiple_intertwined_packets<I: TestIpExt>() {
2134 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2135 const SIZE: u16 = 1;
2136 let id_0 = 5;
2137 let id_1 = 10;
2138
2139 I::process_ip_fragment(
2144 &mut core_ctx,
2145 &mut bindings_ctx,
2146 FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
2147 ExpectedResult::NeedMore,
2148 );
2149
2150 I::process_ip_fragment(
2152 &mut core_ctx,
2153 &mut bindings_ctx,
2154 FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
2155 ExpectedResult::NeedMore,
2156 );
2157
2158 I::process_ip_fragment(
2160 &mut core_ctx,
2161 &mut bindings_ctx,
2162 FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
2163 ExpectedResult::NeedMore,
2164 );
2165
2166 I::process_ip_fragment(
2168 &mut core_ctx,
2169 &mut bindings_ctx,
2170 FragmentSpec { id: id_1, offset: 1, size: SIZE, m_flag: true },
2171 ExpectedResult::NeedMore,
2172 );
2173
2174 I::process_ip_fragment(
2176 &mut core_ctx,
2177 &mut bindings_ctx,
2178 FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
2179 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
2180 );
2181
2182 try_reassemble_ip_packet(
2183 &mut core_ctx,
2184 &mut bindings_ctx,
2185 id_0,
2186 3,
2187 expected_max_fragment_len::<I>(SIZE),
2188 );
2189
2190 I::process_ip_fragment(
2192 &mut core_ctx,
2193 &mut bindings_ctx,
2194 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
2195 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_1) },
2196 );
2197
2198 try_reassemble_ip_packet(
2199 &mut core_ctx,
2200 &mut bindings_ctx,
2201 id_1,
2202 3,
2203 expected_max_fragment_len::<I>(SIZE),
2204 );
2205 }
2206
2207 #[ip_test(I)]
2208 fn test_ip_reassembly_timer_with_multiple_intertwined_packets<I: TestIpExt>() {
2209 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2210 const SIZE: u16 = 1;
2211 let id_0 = 5;
2212 let id_1 = 10;
2213 let id_2 = 15;
2214
2215 const BEFORE_TIMEOUT1: Duration = Duration::from_secs(1);
2240 const BEFORE_TIMEOUT2: Duration = Duration::from_secs(2);
2241 const BEFORE_TIMEOUT3: Duration = Duration::from_secs(3);
2242 assert!(BEFORE_TIMEOUT1 < I::REASSEMBLY_TIMEOUT);
2243 assert!(BEFORE_TIMEOUT2 < I::REASSEMBLY_TIMEOUT);
2244 assert!(BEFORE_TIMEOUT3 < I::REASSEMBLY_TIMEOUT);
2245
2246 I::process_ip_fragment(
2248 &mut core_ctx,
2249 &mut bindings_ctx,
2250 FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
2251 ExpectedResult::NeedMore,
2252 );
2253
2254 I::process_ip_fragment(
2256 &mut core_ctx,
2257 &mut bindings_ctx,
2258 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
2259 ExpectedResult::NeedMore,
2260 );
2261
2262 I::process_ip_fragment(
2264 &mut core_ctx,
2265 &mut bindings_ctx,
2266 FragmentSpec { id: id_2, offset: 2, size: SIZE, m_flag: false },
2267 ExpectedResult::NeedMore,
2268 );
2269
2270 assert_empty(
2272 bindings_ctx
2273 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT1), &mut core_ctx),
2274 );
2275
2276 I::process_ip_fragment(
2278 &mut core_ctx,
2279 &mut bindings_ctx,
2280 FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
2281 ExpectedResult::NeedMore,
2282 );
2283
2284 assert_empty(
2286 bindings_ctx
2287 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT2), &mut core_ctx),
2288 );
2289
2290 I::process_ip_fragment(
2292 &mut core_ctx,
2293 &mut bindings_ctx,
2294 FragmentSpec { id: id_2, offset: 1, size: SIZE, m_flag: true },
2295 ExpectedResult::NeedMore,
2296 );
2297
2298 I::process_ip_fragment(
2300 &mut core_ctx,
2301 &mut bindings_ctx,
2302 FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
2303 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
2304 );
2305
2306 try_reassemble_ip_packet(
2307 &mut core_ctx,
2308 &mut bindings_ctx,
2309 id_0,
2310 3,
2311 expected_max_fragment_len::<I>(SIZE),
2312 );
2313
2314 assert_empty(
2316 bindings_ctx
2317 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT3), &mut core_ctx),
2318 );
2319
2320 I::process_ip_fragment(
2322 &mut core_ctx,
2323 &mut bindings_ctx,
2324 FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
2325 ExpectedResult::NeedMore,
2326 );
2327
2328 I::process_ip_fragment(
2330 &mut core_ctx,
2331 &mut bindings_ctx,
2332 FragmentSpec { id: id_2, offset: 0, size: SIZE, m_flag: true },
2333 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_2) },
2334 );
2335
2336 try_reassemble_ip_packet(
2337 &mut core_ctx,
2338 &mut bindings_ctx,
2339 id_2,
2340 3,
2341 expected_max_fragment_len::<I>(SIZE),
2342 );
2343
2344 bindings_ctx.trigger_timers_until_and_expect_unordered(
2347 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
2348 [FragmentTimerId::<I>::default()],
2349 &mut core_ctx,
2350 );
2351
2352 bindings_ctx.timers.assert_no_timers_installed();
2354
2355 I::process_ip_fragment(
2359 &mut core_ctx,
2360 &mut bindings_ctx,
2361 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: true },
2362 ExpectedResult::NeedMore,
2363 );
2364 }
2365
2366 #[test]
2367 fn test_no_more_fragments_in_middle_of_block() {
2368 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2369 process_ipv4_fragment(
2370 &mut core_ctx,
2371 &mut bindings_ctx,
2372 FragmentSpec { id: 0, offset: 100, size: 1, m_flag: false },
2373 get_ipv4_builder(),
2374 ExpectedResult::NeedMore,
2375 );
2376
2377 process_ipv4_fragment(
2378 &mut core_ctx,
2379 &mut bindings_ctx,
2380 FragmentSpec { id: 0, offset: 50, size: 1, m_flag: false },
2381 get_ipv4_builder(),
2382 ExpectedResult::Invalid,
2383 );
2384 }
2385
2386 #[ip_test(I)]
2387 fn test_cancel_timer_on_overlap<I: TestIpExt>() {
2388 const FRAGMENT_ID: u16 = 1;
2389
2390 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2391
2392 let key = test_key(FRAGMENT_ID);
2393
2394 for _ in 0..=2 {
2397 I::process_ip_fragment(
2398 &mut core_ctx,
2399 &mut bindings_ctx,
2400 FragmentSpec { id: FRAGMENT_ID, offset: 0, size: 10, m_flag: true },
2401 ExpectedResult::NeedMore,
2402 );
2403 core_ctx
2404 .state
2405 .cache
2406 .timers
2407 .assert_timers_after(&mut bindings_ctx, [(key, (), I::REASSEMBLY_TIMEOUT)]);
2408
2409 I::process_ip_fragment(
2410 &mut core_ctx,
2411 &mut bindings_ctx,
2412 FragmentSpec { id: FRAGMENT_ID, offset: 5, size: 10, m_flag: true },
2413 ExpectedResult::Invalid,
2414 );
2415 assert_eq!(bindings_ctx.timers.timers(), [],);
2416 }
2417 }
2418
2419 #[test]
2421 fn test_fragment_reassembly_evasion_cache_corruption() {
2422 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2423 let id = 5;
2424
2425 process_ipv4_fragment(
2427 &mut core_ctx,
2428 &mut bindings_ctx,
2429 FragmentSpec { id, offset: 0, size: 10, m_flag: true },
2430 get_ipv4_builder(),
2431 ExpectedResult::NeedMore,
2432 );
2433
2434 process_ipv4_fragment(
2436 &mut core_ctx,
2437 &mut bindings_ctx,
2438 FragmentSpec { id, offset: 30, size: 10, m_flag: true },
2439 get_ipv4_builder(),
2440 ExpectedResult::NeedMore,
2441 );
2442
2443 process_ipv4_fragment(
2446 &mut core_ctx,
2447 &mut bindings_ctx,
2448 FragmentSpec { id, offset: 10, size: 10, m_flag: false },
2449 get_ipv4_builder(),
2450 ExpectedResult::Invalid,
2451 );
2452
2453 process_ipv4_fragment(
2457 &mut core_ctx,
2458 &mut bindings_ctx,
2459 FragmentSpec { id, offset: 40, size: 10, m_flag: false },
2460 get_ipv4_builder(),
2461 ExpectedResult::NeedMore,
2462 );
2463 }
2464
2465 #[test]
2467 fn test_multiple_last_fragments_evasion() {
2468 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2469 let id = 6;
2470
2471 process_ipv4_fragment(
2474 &mut core_ctx,
2475 &mut bindings_ctx,
2476 FragmentSpec { id, offset: 2, size: 1, m_flag: false },
2477 get_ipv4_builder(),
2478 ExpectedResult::NeedMore,
2479 );
2480
2481 process_ipv4_fragment(
2485 &mut core_ctx,
2486 &mut bindings_ctx,
2487 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
2488 get_ipv4_builder(),
2489 ExpectedResult::Invalid,
2490 );
2491 }
2492
2493 #[ip_test(I)]
2495 #[test_case(1)]
2496 #[test_case(10)]
2497 #[test_case(100)]
2498 fn test_max_fragment_len<I: TestIpExt>(size: u16) {
2499 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2500 let id = 5;
2501
2502 I::process_ip_fragment(
2504 &mut core_ctx,
2505 &mut bindings_ctx,
2506 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
2507 ExpectedResult::NeedMore,
2508 );
2509
2510 I::process_ip_fragment(
2512 &mut core_ctx,
2513 &mut bindings_ctx,
2514 FragmentSpec { id, offset: 1, size, m_flag: true },
2515 ExpectedResult::NeedMore,
2516 );
2517
2518 I::process_ip_fragment(
2520 &mut core_ctx,
2521 &mut bindings_ctx,
2522 FragmentSpec { id, offset: size + 1, size: 1, m_flag: false },
2523 ExpectedResult::Ready { body_fragment_blocks: size + 2, key: test_key(id) },
2524 );
2525
2526 try_reassemble_ip_packet(
2527 &mut core_ctx,
2528 &mut bindings_ctx,
2529 id,
2530 size + 2,
2531 expected_max_fragment_len::<I>(size),
2532 );
2533 }
2534}