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;
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>>(
188 &mut self,
189 bindings_ctx: &mut BC,
190 key: &FragmentCacheKey<I>,
191 buffer: BV,
192 ) -> Result<(), FragmentReassemblyError>;
193}
194
195impl<I: IpExt + ReassemblyIpExt, BC: FragmentBindingsContext, CC: FragmentContext<I, BC>>
196 FragmentHandler<I, BC> for CC
197{
198 fn process_fragment<B: SplitByteSlice>(
199 &mut self,
200 bindings_ctx: &mut BC,
201 packet: I::Packet<B>,
202 ) -> FragmentProcessingState<I, B>
203 where
204 I::Packet<B>: FragmentablePacket,
205 {
206 self.with_state_mut(|cache| {
207 let (res, timer_action) = cache.process_fragment(packet);
208
209 if let Some(timer_action) = timer_action {
210 match timer_action {
211 CacheTimerAction::CreateNewTimer(key) => {
214 assert_eq!(
215 cache.timers.schedule_after(
216 bindings_ctx,
217 key,
218 (),
219 I::REASSEMBLY_TIMEOUT,
220 ),
221 None
222 )
223 }
224 CacheTimerAction::CancelExistingTimer(key) => {
225 assert_ne!(cache.timers.cancel(bindings_ctx, &key), None)
226 }
227 }
228 }
229
230 res
231 })
232 }
233
234 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
235 &mut self,
236 bindings_ctx: &mut BC,
237 key: &FragmentCacheKey<I>,
238 buffer: BV,
239 ) -> Result<(), FragmentReassemblyError> {
240 self.with_state_mut(|cache| {
241 let res = cache.reassemble_packet(key, buffer);
242
243 match res {
244 Ok(_) | Err(FragmentReassemblyError::PacketParsingError) => {
245 assert_matches!(cache.timers.cancel(bindings_ctx, key), Some(_));
249 }
250 Err(FragmentReassemblyError::InvalidKey)
251 | Err(FragmentReassemblyError::MissingFragments) => {}
252 }
253
254 res
255 })
256 }
257}
258
259impl<I: ReassemblyIpExt, BC: FragmentBindingsContext, CC: FragmentContext<I, BC>>
260 HandleableTimer<CC, BC> for FragmentTimerId<I>
261{
262 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
263 let Self(IpVersionMarker { .. }) = self;
264 core_ctx.with_state_mut(|cache| {
265 let Some((key, ())) = cache.timers.pop(bindings_ctx) else {
266 return;
267 };
268
269 let FragmentCacheData { missing_blocks: _, body_fragments, header: _, total_size } =
271 assert_matches!(cache.remove_data(&key), Some(c) => c);
272 debug!(
273 "reassembly for {key:?} \
274 timed out with {} fragments and {total_size} bytes",
275 body_fragments.len(),
276 );
277 });
278 }
279}
280
281pub trait FragmentablePacket {
283 fn fragment_data(&self) -> (u32, u16, bool);
293}
294
295impl<B: SplitByteSlice> FragmentablePacket for Ipv4Packet<B> {
296 fn fragment_data(&self) -> (u32, u16, bool) {
297 (u32::from(self.id()), self.fragment_offset().into_raw(), self.mf_flag())
298 }
299}
300
301impl<B: SplitByteSlice> FragmentablePacket for Ipv6Packet<B> {
302 fn fragment_data(&self) -> (u32, u16, bool) {
303 for ext_hdr in self.iter_extension_hdrs() {
304 if let Ipv6ExtensionHeader::Fragment { fragment_data } = ext_hdr {
305 return (
306 fragment_data.identification(),
307 fragment_data.fragment_offset().into_raw(),
308 fragment_data.m_flag(),
309 );
310 }
311 }
312
313 unreachable!(
314 "Should never call this function if the packet does not have a fragment header"
315 );
316 }
317}
318
319#[derive(Debug)]
321pub enum FragmentProcessingState<I: ReassemblyIpExt, B: SplitByteSlice> {
322 NotNeeded(I::Packet<B>),
325
326 InvalidFragment,
338
339 NeedMoreFragments,
343
344 OutOfMemory,
347
348 Ready { key: FragmentCacheKey<I>, packet_len: usize },
353}
354
355#[derive(Debug, PartialEq, Eq)]
357pub enum FragmentReassemblyError {
358 MissingFragments,
360
361 InvalidKey,
366
367 PacketParsingError,
369}
370
371#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
376pub struct FragmentCacheKey<I: ReassemblyIpExt> {
377 src_ip: I::Addr,
378 dst_ip: I::Addr,
379 fragment_id: u32,
380 ip_specific_fields: I::FragmentCacheKeyPart,
381}
382
383#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
388struct BlockRange {
389 start: u16,
390 end: u16,
391}
392
393#[derive(Debug)]
395struct FragmentCacheData {
396 missing_blocks: BTreeSet<BlockRange>,
404
405 body_fragments: BinaryHeap<PacketBodyFragment>,
417
418 header: Option<Vec<u8>>,
423
424 total_size: usize,
430}
431
432impl Default for FragmentCacheData {
433 fn default() -> FragmentCacheData {
434 FragmentCacheData {
435 missing_blocks: core::iter::once(BlockRange { start: 0, end: u16::MAX }).collect(),
436 body_fragments: BinaryHeap::new(),
437 header: None,
438 total_size: 0,
439 }
440 }
441}
442
443impl FragmentCacheData {
444 fn find_gap(&self, BlockRange { start, end }: BlockRange) -> FindGapResult {
446 let result = self.missing_blocks.iter().find_map(|gap| {
447 if gap.start <= start && gap.end >= end {
449 return Some(FindGapResult::Found { gap: *gap });
450 }
451
452 if gap.start > end || gap.end < start {
455 return None;
456 }
457
458 return Some(FindGapResult::Overlap);
461 });
462
463 match result {
464 Some(result) => result,
465 None => {
466 let last = self.body_fragments.peek().unwrap();
476 if last.offset < start {
477 FindGapResult::OutOfBounds
478 } else {
479 FindGapResult::Duplicate
480 }
481 }
482 }
483 }
484}
485
486enum FindGapResult {
488 Found {
491 gap: BlockRange,
492 },
493 Overlap,
497 OutOfBounds,
499 Duplicate,
515}
516
517#[derive(Debug)]
519pub struct IpPacketFragmentCache<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
520 cache: HashMap<FragmentCacheKey<I>, FragmentCacheData>,
521 data_size: usize,
523 num_fragments: usize,
525 capacity: FragmentCacheCapacity,
526 timers: LocalTimerHeap<FragmentCacheKey<I>, (), BT>,
527}
528
529impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> IpPacketFragmentCache<I, BC> {
530 pub fn new<CC: CoreTimerContext<FragmentTimerId<I>, BC>>(
532 bindings_ctx: &mut BC,
533 ) -> IpPacketFragmentCache<I, BC> {
534 IpPacketFragmentCache {
535 cache: HashMap::new(),
536 data_size: 0,
537 num_fragments: 0,
538 capacity: FragmentCacheCapacity::default(),
539 timers: LocalTimerHeap::new(bindings_ctx, CC::convert_timer(Default::default())),
540 }
541 }
542}
543
544enum CacheTimerAction<I: ReassemblyIpExt> {
545 CreateNewTimer(FragmentCacheKey<I>),
546 CancelExistingTimer(FragmentCacheKey<I>),
547}
548
549impl<I: ReassemblyIpExt, BT: FragmentBindingsTypes> IpPacketFragmentCache<I, BT> {
550 fn process_fragment<B: SplitByteSlice>(
556 &mut self,
557 packet: I::Packet<B>,
558 ) -> (FragmentProcessingState<I, B>, Option<CacheTimerAction<I>>)
559 where
560 I::Packet<B>: FragmentablePacket,
561 {
562 if self.above_capacity() {
563 return (FragmentProcessingState::OutOfMemory, None);
564 }
565
566 let (id, offset, m_flag) = packet.fragment_data();
568
569 if offset == 0 && !m_flag {
574 return (FragmentProcessingState::NotNeeded(packet), None);
575 }
576
577 if packet.body().is_empty() {
582 return (FragmentProcessingState::NeedMoreFragments, None);
583 }
584
585 if m_flag && (packet.body().len() % (FRAGMENT_BLOCK_SIZE as usize) != 0) {
589 return (FragmentProcessingState::InvalidFragment, None);
590 }
591
592 let key = FragmentCacheKey {
594 src_ip: packet.src_ip(),
595 dst_ip: packet.dst_ip(),
596 fragment_id: id,
597 ip_specific_fields: I::ip_specific_key_part(&packet),
598 };
599
600 let num_fragment_blocks = 1 + ((packet.body().len() - 1) / (FRAGMENT_BLOCK_SIZE as usize));
614 assert!(num_fragment_blocks > 0);
615
616 let fragment_blocks_range =
622 if let Ok(offset_end) = u16::try_from((offset as usize) + num_fragment_blocks - 1) {
623 if offset_end <= MAX_FRAGMENT_BLOCKS {
624 BlockRange { start: offset, end: offset_end }
625 } else {
626 return (FragmentProcessingState::InvalidFragment, None);
627 }
628 } else {
629 return (FragmentProcessingState::InvalidFragment, None);
630 };
631
632 let (fragment_data, timer_not_yet_scheduled) = self.get_or_create(key);
634
635 let found_gap = match fragment_data.find_gap(fragment_blocks_range) {
637 FindGapResult::Overlap | FindGapResult::OutOfBounds => {
638 assert_matches!(self.remove_data(&key), Some(_));
650
651 return (
652 FragmentProcessingState::InvalidFragment,
653 (!timer_not_yet_scheduled)
654 .then_some(CacheTimerAction::CancelExistingTimer(key)),
655 );
656 }
657 FindGapResult::Duplicate => {
658 return (FragmentProcessingState::NeedMoreFragments, None);
670 }
671 FindGapResult::Found { gap } => gap,
672 };
673
674 let timer_id = timer_not_yet_scheduled.then_some(CacheTimerAction::CreateNewTimer(key));
675
676 if !m_flag && found_gap.end < u16::MAX {
677 return (FragmentProcessingState::InvalidFragment, timer_id);
681 }
682
683 assert!(fragment_data.missing_blocks.remove(&found_gap));
686
687 if found_gap.start < fragment_blocks_range.start {
704 assert!(fragment_data.missing_blocks.insert(BlockRange {
705 start: found_gap.start,
706 end: fragment_blocks_range.start - 1
707 }));
708 }
709
710 if found_gap.end > fragment_blocks_range.end && m_flag {
745 assert!(
746 fragment_data.missing_blocks.insert(BlockRange {
747 start: fragment_blocks_range.end + 1,
748 end: found_gap.end
749 })
750 );
751 } else {
752 assert!(
758 found_gap.end == fragment_blocks_range.end
759 || (!m_flag && found_gap.end == u16::MAX),
760 "found_gap: {:?}, fragment_blocks_range: {:?} offset: {:?}, m_flag: {:?}",
761 found_gap,
762 fragment_blocks_range,
763 offset,
764 m_flag
765 );
766 }
767
768 let mut added_bytes = 0;
769 if offset == 0 {
771 assert_eq!(fragment_data.header, None);
772 let header = get_header::<B, I>(&packet);
773 added_bytes = header.len();
774 fragment_data.header = Some(header);
775 }
776
777 let mut body = Vec::with_capacity(packet.body().len());
779 body.extend_from_slice(packet.body());
780 added_bytes += body.len();
781 fragment_data.total_size += added_bytes;
782 fragment_data.body_fragments.push(PacketBodyFragment::new(offset, body));
783
784 let result = if fragment_data.missing_blocks.is_empty() {
790 FragmentProcessingState::Ready { key, packet_len: fragment_data.total_size }
791 } else {
792 FragmentProcessingState::NeedMoreFragments
793 };
794
795 self.track_fragment(added_bytes);
796 (result, timer_id)
797 }
798
799 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
817 &mut self,
818 key: &FragmentCacheKey<I>,
819 buffer: BV,
820 ) -> Result<(), FragmentReassemblyError> {
821 let entry = match self.cache.entry(*key) {
822 Entry::Occupied(entry) => entry,
823 Entry::Vacant(_) => return Err(FragmentReassemblyError::InvalidKey),
824 };
825
826 if !entry.get().missing_blocks.is_empty() {
828 return Err(FragmentReassemblyError::MissingFragments);
829 }
830 let (_key, data) = entry.remove_entry();
833 self.untrack_data(&data);
834
835 assert_matches!(data.header, Some(_));
837
838 let body_fragments = data.body_fragments.into_sorted_vec().into_iter().map(|x| x.data);
841 I::Packet::reassemble_fragmented_packet(buffer, data.header.unwrap(), body_fragments)
842 .map_err(|_| FragmentReassemblyError::PacketParsingError)
843 }
844
845 fn get_or_create(&mut self, key: FragmentCacheKey<I>) -> (&mut FragmentCacheData, bool) {
850 match self.cache.entry(key) {
851 Entry::Occupied(e) => (e.into_mut(), false),
852 Entry::Vacant(e) => {
853 (e.insert(FragmentCacheData::default()), true)
858 }
859 }
860 }
861
862 fn above_capacity(&self) -> bool {
863 self.data_size >= self.capacity.max_data_bytes
864 || self.num_fragments >= self.capacity.max_fragments
865 || self.cache.len() >= self.capacity.max_keys
866 }
867
868 fn track_fragment(&mut self, added_bytes: usize) {
869 self.data_size += added_bytes;
870 self.num_fragments += 1;
871 }
872
873 fn untrack_data(&mut self, data: &FragmentCacheData) {
874 self.data_size -= data.total_size;
875 self.num_fragments -= data.body_fragments.len();
876 }
877
878 fn remove_data(&mut self, key: &FragmentCacheKey<I>) -> Option<FragmentCacheData> {
879 let data = self.cache.remove(key)?;
880 self.untrack_data(&data);
881 Some(data)
882 }
883}
884
885fn get_header<B: SplitByteSlice, I: IpExt>(packet: &I::Packet<B>) -> Vec<u8> {
887 match packet.as_ip_addr_ref() {
888 IpAddr::V4(packet) => packet.copy_header_bytes_for_fragment(),
889 IpAddr::V6(packet) => {
890 packet.copy_header_bytes_for_fragment()
895 }
896 }
897}
898
899#[derive(Debug, PartialEq, Eq)]
901struct PacketBodyFragment {
902 offset: u16,
903 data: Vec<u8>,
904}
905
906impl PacketBodyFragment {
907 fn new(offset: u16, data: Vec<u8>) -> Self {
909 PacketBodyFragment { offset, data }
910 }
911}
912
913impl PartialOrd for PacketBodyFragment {
916 fn partial_cmp(&self, other: &PacketBodyFragment) -> Option<Ordering> {
917 Some(self.cmp(other))
918 }
919}
920
921impl Ord for PacketBodyFragment {
922 fn cmp(&self, other: &Self) -> Ordering {
923 self.offset.cmp(&other.offset)
924 }
925}
926
927#[derive(Copy, Clone, Debug, Eq, PartialEq)]
933struct FragmentCacheCapacity {
934 max_data_bytes: usize,
936 max_keys: usize,
938 max_fragments: usize,
940}
941
942impl FragmentCacheCapacity {
943 const DEFAULT_MAX_DATA_BYTES: usize = 4 * 1024 * 1024;
947
948 const DEFAULT_MAX_KEYS: usize = 2048;
955
956 const DEFAULT_MAX_FRAGMENTS: usize = Self::DEFAULT_MAX_KEYS * 8;
962}
963
964impl Default for FragmentCacheCapacity {
965 fn default() -> Self {
966 Self {
967 max_data_bytes: Self::DEFAULT_MAX_DATA_BYTES,
968 max_keys: Self::DEFAULT_MAX_KEYS,
969 max_fragments: Self::DEFAULT_MAX_FRAGMENTS,
970 }
971 }
972}
973
974#[cfg(test)]
975mod tests {
976 use alloc::vec;
977
978 use assert_matches::assert_matches;
979 use ip_test_macro::ip_test;
980 use net_declare::{net_ip_v4, net_ip_v6};
981 use net_types::Witness;
982 use net_types::ip::{Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
983 use netstack3_base::testutil::{
984 FakeBindingsCtx, FakeCoreCtx, FakeInstant, FakeTimerCtxExt, TEST_ADDRS_V4, TEST_ADDRS_V6,
985 assert_empty,
986 };
987 use netstack3_base::{CtxPair, IntoCoreTimerCtx, NetworkSerializationContext};
988 use packet::{Buf, NestablePacketBuilder as _, ParsablePacket, ParseBuffer, Serializer};
989 use packet_formats::ip::{FragmentOffset, IpProto, Ipv6Proto};
990 use packet_formats::ipv4::Ipv4PacketBuilder;
991 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithFragmentHeader};
992 use test_case::test_case;
993
994 use super::*;
995
996 struct FakeFragmentContext<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
997 cache: IpPacketFragmentCache<I, BT>,
998 }
999
1000 impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> FakeFragmentContext<I, BC>
1001 where
1002 BC::DispatchId: From<FragmentTimerId<I>>,
1003 {
1004 fn new(bindings_ctx: &mut BC) -> Self {
1005 Self { cache: IpPacketFragmentCache::new::<IntoCoreTimerCtx>(bindings_ctx) }
1006 }
1007 }
1008
1009 type FakeCtxImpl<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
1010 type FakeBindingsCtxImpl<I> = FakeBindingsCtx<FragmentTimerId<I>, (), (), ()>;
1011 type FakeCoreCtxImpl<I> = FakeCoreCtx<FakeFragmentContext<I, FakeBindingsCtxImpl<I>>, (), ()>;
1012
1013 impl<I: ReassemblyIpExt> FragmentContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
1014 fn with_state_mut<
1015 O,
1016 F: FnOnce(&mut IpPacketFragmentCache<I, FakeBindingsCtxImpl<I>>) -> O,
1017 >(
1018 &mut self,
1019 cb: F,
1020 ) -> O {
1021 cb(&mut self.state.cache)
1022 }
1023 }
1024
1025 #[derive(PartialEq)]
1028 enum ExpectedResult<I: ReassemblyIpExt> {
1029 Ready { body_fragment_blocks: u16, key: FragmentCacheKey<I> },
1034
1035 NeedMore,
1038
1039 Invalid,
1041
1042 OutOfMemory,
1044 }
1045
1046 fn get_ipv4_builder() -> Ipv4PacketBuilder {
1048 Ipv4PacketBuilder::new(
1049 TEST_ADDRS_V4.remote_ip,
1050 TEST_ADDRS_V4.local_ip,
1051 10,
1052 <Ipv4 as TestIpExt>::PROTOCOL,
1053 )
1054 }
1055
1056 fn get_ipv6_builder() -> Ipv6PacketBuilder {
1058 Ipv6PacketBuilder::new(
1059 TEST_ADDRS_V6.remote_ip,
1060 TEST_ADDRS_V6.local_ip,
1061 10,
1062 <Ipv6 as TestIpExt>::PROTOCOL,
1063 )
1064 }
1065
1066 fn validate_size<I: ReassemblyIpExt, BT: FragmentBindingsTypes>(
1068 cache: &IpPacketFragmentCache<I, BT>,
1069 ) {
1070 let mut data_size: usize = 0;
1071 let mut num_fragments: usize = 0;
1072
1073 for v in cache.cache.values() {
1074 data_size += v.total_size;
1075 num_fragments += v.body_fragments.len();
1076 }
1077
1078 assert_eq!(data_size, cache.data_size);
1079 assert_eq!(num_fragments, cache.num_fragments);
1080 }
1081
1082 struct FragmentSpec {
1083 id: u16,
1085 offset: u16,
1087 size: u16,
1089 m_flag: bool,
1091 }
1092
1093 fn expected_packet_size<I: TestIpExt>(num_fragment_blocks: u16) -> usize {
1094 usize::from(num_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE) + I::HEADER_LENGTH
1095 }
1096
1097 fn process_ipv4_fragment<CC: FragmentContext<Ipv4, BC>, BC: FragmentBindingsContext>(
1099 core_ctx: &mut CC,
1100 bindings_ctx: &mut BC,
1101 FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1102 mut builder: Ipv4PacketBuilder,
1103 expected_result: ExpectedResult<Ipv4>,
1104 ) {
1105 builder.id(id);
1106 builder.fragment_offset(FragmentOffset::new(offset).unwrap());
1107 builder.mf_flag(m_flag);
1108 let body = generate_body_fragment(
1109 id,
1110 offset,
1111 usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1112 );
1113
1114 let mut buffer = builder
1115 .wrap_body(Buf::new(body, ..))
1116 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1117 .unwrap();
1118 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1119
1120 let actual_result =
1121 FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1122 match expected_result {
1123 ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1124 let (key, packet_len) = assert_matches!(
1125 actual_result,
1126 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1127 );
1128 assert_eq!(key, expected_key);
1129 assert_eq!(packet_len, expected_packet_size::<Ipv4>(body_fragment_blocks));
1130 }
1131 ExpectedResult::NeedMore => {
1132 assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1133 }
1134 ExpectedResult::Invalid => {
1135 assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1136 }
1137 ExpectedResult::OutOfMemory => {
1138 assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1139 }
1140 }
1141 }
1142
1143 fn process_ipv6_fragment<CC: FragmentContext<Ipv6, BC>, BC: FragmentBindingsContext>(
1147 core_ctx: &mut CC,
1148 bindings_ctx: &mut BC,
1149 FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1150 builder: Ipv6PacketBuilder,
1151 expected_result: ExpectedResult<Ipv6>,
1152 ) {
1153 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1154 builder,
1155 FragmentOffset::new(offset).unwrap(),
1156 m_flag,
1157 id.into(),
1158 );
1159
1160 let body = generate_body_fragment(
1161 id,
1162 offset,
1163 usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1164 );
1165
1166 let mut buffer = builder
1167 .wrap_body(Buf::new(body, ..))
1168 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1169 .unwrap();
1170 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1171
1172 let actual_result =
1173 FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1174 match expected_result {
1175 ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1176 let (key, packet_len) = assert_matches!(
1177 actual_result,
1178 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1179 );
1180 assert_eq!(key, expected_key);
1181 assert_eq!(packet_len, expected_packet_size::<Ipv6>(body_fragment_blocks));
1182 }
1183 ExpectedResult::NeedMore => {
1184 assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1185 }
1186 ExpectedResult::Invalid => {
1187 assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1188 }
1189 ExpectedResult::OutOfMemory => {
1190 assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1191 }
1192 }
1193 }
1194
1195 trait TestIpExt: IpExt + netstack3_base::testutil::TestIpExt + ReassemblyIpExt {
1196 const HEADER_LENGTH: usize;
1197
1198 const PROTOCOL: Self::Proto;
1199
1200 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1201 core_ctx: &mut CC,
1202 bindings_ctx: &mut BC,
1203 spec: FragmentSpec,
1204 expected_result: ExpectedResult<Self>,
1205 );
1206 }
1207
1208 impl TestIpExt for Ipv4 {
1209 const HEADER_LENGTH: usize = packet_formats::ipv4::HDR_PREFIX_LEN;
1210
1211 const PROTOCOL: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1212
1213 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1214 core_ctx: &mut CC,
1215 bindings_ctx: &mut BC,
1216 spec: FragmentSpec,
1217 expected_result: ExpectedResult<Ipv4>,
1218 ) {
1219 process_ipv4_fragment(core_ctx, bindings_ctx, spec, get_ipv4_builder(), expected_result)
1220 }
1221 }
1222 impl TestIpExt for Ipv6 {
1223 const HEADER_LENGTH: usize = packet_formats::ipv6::IPV6_FIXED_HDR_LEN;
1224
1225 const PROTOCOL: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1226
1227 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1228 core_ctx: &mut CC,
1229 bindings_ctx: &mut BC,
1230 spec: FragmentSpec,
1231 expected_result: ExpectedResult<Ipv6>,
1232 ) {
1233 process_ipv6_fragment(core_ctx, bindings_ctx, spec, get_ipv6_builder(), expected_result)
1234 }
1235 }
1236
1237 fn try_reassemble_ip_packet<
1241 I: TestIpExt + netstack3_base::IpExt,
1242 CC: FragmentContext<I, BC>,
1243 BC: FragmentBindingsContext,
1244 >(
1245 core_ctx: &mut CC,
1246 bindings_ctx: &mut BC,
1247 fragment_id: u16,
1248 body_fragment_blocks: u16,
1249 ) {
1250 let mut buffer: Vec<u8> = vec![
1251 0;
1252 usize::from(body_fragment_blocks)
1253 * usize::from(FRAGMENT_BLOCK_SIZE)
1254 + I::HEADER_LENGTH
1255 ];
1256 let mut buffer = &mut buffer[..];
1257 let key = test_key(fragment_id);
1258
1259 FragmentHandler::reassemble_packet(core_ctx, bindings_ctx, &key, &mut buffer).unwrap();
1260 let packet = I::Packet::parse_mut(&mut buffer, ()).unwrap();
1261
1262 let expected_body = generate_body_fragment(
1263 fragment_id,
1264 0,
1265 usize::from(body_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE),
1266 );
1267 assert_eq!(packet.body(), &expected_body[..]);
1268 }
1269
1270 fn generate_body_fragment(fragment_id: u16, fragment_offset: u16, len: usize) -> Vec<u8> {
1276 let start = usize::from(fragment_id)
1280 + usize::from(fragment_offset) * usize::from(FRAGMENT_BLOCK_SIZE);
1281 (start..start + len).map(|byte| byte as u8).collect()
1282 }
1283
1284 fn test_key<I: TestIpExt>(id: u16) -> FragmentCacheKey<I> {
1286 #[derive(GenericOverIp)]
1287 #[generic_over_ip(I, Ip)]
1288 struct Wrapper<I: ReassemblyIpExt>(I::FragmentCacheKeyPart);
1289
1290 let Wrapper(ip_specific_fields) =
1291 I::map_ip_out((), |()| Wrapper(Ipv4::PROTOCOL), |()| Wrapper(()));
1292
1293 FragmentCacheKey {
1294 src_ip: I::TEST_ADDRS.remote_ip.get(),
1295 dst_ip: I::TEST_ADDRS.local_ip.get(),
1296 fragment_id: id.into(),
1297 ip_specific_fields,
1298 }
1299 }
1300
1301 fn new_context<I: ReassemblyIpExt>() -> FakeCtxImpl<I> {
1302 FakeCtxImpl::<I>::with_default_bindings_ctx(|bindings_ctx| {
1303 FakeCoreCtxImpl::with_state(FakeFragmentContext::new(bindings_ctx))
1304 })
1305 }
1306
1307 #[test]
1308 fn test_ipv4_reassembly_not_needed() {
1309 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1310
1311 let builder = get_ipv4_builder();
1315 let body = [1, 2, 3, 4, 5];
1316 let mut buffer = builder
1317 .wrap_body(Buf::new(body.to_vec(), ..))
1318 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1319 .unwrap();
1320 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1321 assert_matches!(
1322 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1323 FragmentProcessingState::NotNeeded(unfragmented) if unfragmented.body() == body
1324 );
1325 }
1326
1327 #[test]
1328 #[should_panic(
1329 expected = "internal error: entered unreachable code: Should never call this function if the packet does not have a fragment header"
1330 )]
1331 fn test_ipv6_reassembly_not_needed() {
1332 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1333
1334 let builder = get_ipv6_builder();
1338 let mut buffer = builder
1339 .wrap_body(Buf::new(vec![1, 2, 3, 4, 5], ..))
1340 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1341 .unwrap();
1342 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1343 assert_matches!(
1344 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1345 FragmentProcessingState::InvalidFragment
1346 );
1347 }
1348
1349 #[ip_test(I)]
1350 #[test_case(1)]
1351 #[test_case(10)]
1352 #[test_case(100)]
1353 fn test_ip_reassembly<I: TestIpExt>(size: u16) {
1354 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1355 let id = 5;
1356
1357 I::process_ip_fragment(
1361 &mut core_ctx,
1362 &mut bindings_ctx,
1363 FragmentSpec { id, offset: 0, size, m_flag: true },
1364 ExpectedResult::NeedMore,
1365 );
1366
1367 I::process_ip_fragment(
1369 &mut core_ctx,
1370 &mut bindings_ctx,
1371 FragmentSpec { id, offset: size, size, m_flag: true },
1372 ExpectedResult::NeedMore,
1373 );
1374
1375 I::process_ip_fragment(
1377 &mut core_ctx,
1378 &mut bindings_ctx,
1379 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1380 ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1381 );
1382
1383 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id, 3 * size);
1384 }
1385
1386 #[test]
1387 fn test_ipv4_key_uniqueness() {
1388 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1389
1390 const RIGHT_SRC: Ipv4Addr = net_ip_v4!("192.0.2.1");
1391 const WRONG_SRC: Ipv4Addr = net_ip_v4!("192.0.2.2");
1392
1393 const RIGHT_DST: Ipv4Addr = net_ip_v4!("192.0.2.3");
1394 const WRONG_DST: Ipv4Addr = net_ip_v4!("192.0.2.4");
1395
1396 const RIGHT_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1397 const WRONG_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Udp);
1398
1399 const RIGHT_ID: u16 = 1;
1400 const WRONG_ID: u16 = 2;
1401
1402 const TTL: u8 = 1;
1403
1404 process_ipv4_fragment(
1406 &mut core_ctx,
1407 &mut bindings_ctx,
1408 FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1409 Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1410 ExpectedResult::NeedMore,
1411 );
1412
1413 for (id, src, dst, proto) in [
1416 (RIGHT_ID, RIGHT_SRC, RIGHT_DST, WRONG_PROTO),
1417 (RIGHT_ID, RIGHT_SRC, WRONG_DST, RIGHT_PROTO),
1418 (RIGHT_ID, WRONG_SRC, RIGHT_DST, RIGHT_PROTO),
1419 (WRONG_ID, RIGHT_SRC, RIGHT_DST, RIGHT_PROTO),
1420 ] {
1421 process_ipv4_fragment(
1422 &mut core_ctx,
1423 &mut bindings_ctx,
1424 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1425 Ipv4PacketBuilder::new(src, dst, TTL, proto),
1426 ExpectedResult::NeedMore,
1427 );
1428 }
1429
1430 const KEY: FragmentCacheKey<Ipv4> = FragmentCacheKey {
1433 src_ip: RIGHT_SRC,
1434 dst_ip: RIGHT_DST,
1435 fragment_id: RIGHT_ID as u32,
1436 ip_specific_fields: RIGHT_PROTO,
1437 };
1438 process_ipv4_fragment(
1439 &mut core_ctx,
1440 &mut bindings_ctx,
1441 FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1442 Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1443 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1444 );
1445 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv4>(2)];
1446 let mut buffer = &mut buffer[..];
1447 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1448 .expect("reassembly should succeed");
1449 let _packet = Ipv4Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1450 }
1451
1452 #[test]
1453 fn test_ipv6_key_uniqueness() {
1454 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1455
1456 const RIGHT_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1457 const WRONG_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1458
1459 const RIGHT_DST: Ipv6Addr = net_ip_v6!("2001:0db8::3");
1460 const WRONG_DST: Ipv6Addr = net_ip_v6!("2001:0db8::4");
1461
1462 const RIGHT_ID: u16 = 1;
1463 const WRONG_ID: u16 = 2;
1464
1465 const TTL: u8 = 1;
1466
1467 process_ipv6_fragment(
1469 &mut core_ctx,
1470 &mut bindings_ctx,
1471 FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1472 Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1473 ExpectedResult::NeedMore,
1474 );
1475
1476 for (id, src, dst) in [
1479 (RIGHT_ID, RIGHT_SRC, WRONG_DST),
1480 (RIGHT_ID, WRONG_SRC, RIGHT_DST),
1481 (WRONG_ID, RIGHT_SRC, RIGHT_DST),
1482 ] {
1483 process_ipv6_fragment(
1484 &mut core_ctx,
1485 &mut bindings_ctx,
1486 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1487 Ipv6PacketBuilder::new(src, dst, TTL, Ipv6::PROTOCOL),
1488 ExpectedResult::NeedMore,
1489 );
1490 }
1491
1492 const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1495 src_ip: RIGHT_SRC,
1496 dst_ip: RIGHT_DST,
1497 fragment_id: RIGHT_ID as u32,
1498 ip_specific_fields: (),
1499 };
1500 process_ipv6_fragment(
1501 &mut core_ctx,
1502 &mut bindings_ctx,
1503 FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1504 Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1505 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1506 );
1507 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1508 let mut buffer = &mut buffer[..];
1509 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1510 .expect("reassembly should succeed");
1511 let _packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1512 }
1513
1514 #[test]
1515 fn test_ipv6_reassemble_different_protocols() {
1516 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1517
1518 const SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1519 const DST: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1520 const ID: u16 = 1;
1521 const TTL: u8 = 1;
1522
1523 const PROTO1: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1524 const PROTO2: Ipv6Proto = Ipv6Proto::Proto(IpProto::Udp);
1525
1526 process_ipv6_fragment(
1528 &mut core_ctx,
1529 &mut bindings_ctx,
1530 FragmentSpec { id: ID, offset: 0, size: 1, m_flag: true },
1531 Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO1),
1532 ExpectedResult::NeedMore,
1533 );
1534
1535 const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1539 src_ip: SRC,
1540 dst_ip: DST,
1541 fragment_id: ID as u32,
1542 ip_specific_fields: (),
1543 };
1544 process_ipv6_fragment(
1545 &mut core_ctx,
1546 &mut bindings_ctx,
1547 FragmentSpec { id: ID, offset: 1, size: 1, m_flag: false },
1548 Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO2),
1549 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1550 );
1551 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1552 let mut buffer = &mut buffer[..];
1553 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1554 .expect("reassembly should succeed");
1555 let packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1556 assert_eq!(packet.proto(), PROTO1);
1557 }
1558
1559 #[ip_test(I)]
1560 #[test_case(1)]
1561 #[test_case(10)]
1562 #[test_case(100)]
1563 fn test_ip_reassemble_with_missing_blocks<I: TestIpExt>(size: u16) {
1564 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1565 let id = 5;
1566
1567 I::process_ip_fragment(
1572 &mut core_ctx,
1573 &mut bindings_ctx,
1574 FragmentSpec { id, offset: 0, size, m_flag: true },
1575 ExpectedResult::NeedMore,
1576 );
1577
1578 I::process_ip_fragment(
1580 &mut core_ctx,
1581 &mut bindings_ctx,
1582 FragmentSpec { id, offset: size, size, m_flag: true },
1583 ExpectedResult::NeedMore,
1584 );
1585
1586 let mut buffer: Vec<u8> = vec![0; 1];
1587 let mut buffer = &mut buffer[..];
1588 let key = test_key(id);
1589 assert_eq!(
1590 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1591 .unwrap_err(),
1592 FragmentReassemblyError::MissingFragments,
1593 );
1594 }
1595
1596 #[ip_test(I)]
1597 fn test_ip_reassemble_after_timer<I: TestIpExt>() {
1598 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1599 let id = 5;
1600 let key = test_key::<I>(id);
1601
1602 bindings_ctx.timers.assert_no_timers_installed();
1604 assert_eq!(core_ctx.state.cache.data_size, 0);
1605 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1606
1607 I::process_ip_fragment(
1611 &mut core_ctx,
1612 &mut bindings_ctx,
1613 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1614 ExpectedResult::NeedMore,
1615 );
1616
1617 core_ctx.state.cache.timers.assert_timers([(
1619 key,
1620 (),
1621 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1622 )]);
1623 validate_size(&core_ctx.state.cache);
1624
1625 I::process_ip_fragment(
1627 &mut core_ctx,
1628 &mut bindings_ctx,
1629 FragmentSpec { id, offset: 1, size: 1, m_flag: true },
1630 ExpectedResult::NeedMore,
1631 );
1632 core_ctx.state.cache.timers.assert_timers([(
1634 key,
1635 (),
1636 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1637 )]);
1638 validate_size(&core_ctx.state.cache);
1639
1640 I::process_ip_fragment(
1642 &mut core_ctx,
1643 &mut bindings_ctx,
1644 FragmentSpec { id, offset: 2, size: 1, m_flag: false },
1645 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id) },
1646 );
1647 core_ctx.state.cache.timers.assert_timers([(
1649 key,
1650 (),
1651 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1652 )]);
1653 validate_size(&core_ctx.state.cache);
1654
1655 assert_eq!(
1657 bindings_ctx.trigger_next_timer(&mut core_ctx),
1658 Some(FragmentTimerId::<I>::default())
1659 );
1660
1661 bindings_ctx.timers.assert_no_timers_installed();
1663 assert_eq!(core_ctx.state.cache.data_size, 0);
1664 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1665
1666 let key = test_key(id);
1669 let packet_len = 44;
1670 let mut buffer: Vec<u8> = vec![0; packet_len];
1671 let mut buffer = &mut buffer[..];
1672 assert_eq!(
1673 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1674 .unwrap_err(),
1675 FragmentReassemblyError::InvalidKey,
1676 );
1677 }
1678
1679 #[ip_test(I)]
1680 #[test_case(1)]
1681 #[test_case(10)]
1682 #[test_case(100)]
1683 fn test_ip_fragment_cache_max_data_size<I: TestIpExt>(size: u16) {
1684 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1685 let mut id = 0;
1686 const THRESHOLD: usize = 8196usize;
1687
1688 assert_eq!(core_ctx.state.cache.data_size, 0);
1689 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1690 core_ctx.state.cache.capacity.max_data_bytes = THRESHOLD;
1691
1692 while core_ctx.state.cache.data_size + usize::from(size) <= THRESHOLD {
1695 I::process_ip_fragment(
1696 &mut core_ctx,
1697 &mut bindings_ctx,
1698 FragmentSpec { id, offset: 0, size, m_flag: true },
1699 ExpectedResult::NeedMore,
1700 );
1701 validate_size(&core_ctx.state.cache);
1702 id += 1;
1703 }
1704
1705 I::process_ip_fragment(
1707 &mut core_ctx,
1708 &mut bindings_ctx,
1709 FragmentSpec { id, offset: 0, size, m_flag: true },
1710 ExpectedResult::OutOfMemory,
1711 );
1712 validate_size(&core_ctx.state.cache);
1713
1714 let _timers = bindings_ctx
1716 .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1717 assert_eq!(core_ctx.state.cache.data_size, 0);
1718 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1719 validate_size(&core_ctx.state.cache);
1720
1721 I::process_ip_fragment(
1723 &mut core_ctx,
1724 &mut bindings_ctx,
1725 FragmentSpec { id, offset: 0, size, m_flag: true },
1726 ExpectedResult::NeedMore,
1727 );
1728 }
1729
1730 #[ip_test(I)]
1731 fn test_ip_fragment_cache_max_keys<I: TestIpExt>() {
1732 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1733 const MAX_KEYS: u16 = 3;
1734 core_ctx.state.cache.capacity.max_keys = MAX_KEYS.into();
1735
1736 for id in 0..MAX_KEYS {
1739 I::process_ip_fragment(
1740 &mut core_ctx,
1741 &mut bindings_ctx,
1742 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1743 ExpectedResult::NeedMore,
1744 );
1745 }
1746
1747 I::process_ip_fragment(
1749 &mut core_ctx,
1750 &mut bindings_ctx,
1751 FragmentSpec { id: MAX_KEYS, offset: 0, size: 1, m_flag: true },
1752 ExpectedResult::OutOfMemory,
1753 );
1754
1755 let _timers = bindings_ctx
1757 .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1758 assert_eq!(core_ctx.state.cache.data_size, 0);
1759 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1760 validate_size(&core_ctx.state.cache);
1761
1762 I::process_ip_fragment(
1764 &mut core_ctx,
1765 &mut bindings_ctx,
1766 FragmentSpec { id: MAX_KEYS, offset: 0, size: 1, m_flag: true },
1767 ExpectedResult::NeedMore,
1768 );
1769 }
1770
1771 #[ip_test(I)]
1772 fn test_ip_fragment_cache_max_fragments<I: TestIpExt>() {
1773 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1774 const MAX_FRAGMENTS: u16 = 3;
1775 core_ctx.state.cache.capacity.max_fragments = MAX_FRAGMENTS.into();
1776
1777 for offset in 0..MAX_FRAGMENTS {
1780 I::process_ip_fragment(
1781 &mut core_ctx,
1782 &mut bindings_ctx,
1783 FragmentSpec { id: 0, offset, size: 1, m_flag: true },
1784 ExpectedResult::NeedMore,
1785 );
1786 }
1787
1788 I::process_ip_fragment(
1790 &mut core_ctx,
1791 &mut bindings_ctx,
1792 FragmentSpec { id: 0, offset: MAX_FRAGMENTS, size: 1, m_flag: true },
1793 ExpectedResult::OutOfMemory,
1794 );
1795
1796 let _timers = bindings_ctx
1798 .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1799 assert_eq!(core_ctx.state.cache.data_size, 0);
1800 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1801 validate_size(&core_ctx.state.cache);
1802
1803 I::process_ip_fragment(
1805 &mut core_ctx,
1806 &mut bindings_ctx,
1807 FragmentSpec { id: 0, offset: MAX_FRAGMENTS, size: 1, m_flag: true },
1808 ExpectedResult::NeedMore,
1809 );
1810 }
1811
1812 #[ip_test(I)]
1813 #[test_case(1)]
1814 #[test_case(10)]
1815 #[test_case(100)]
1816 fn test_unordered_fragments<I: TestIpExt>(size: u16) {
1817 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1818 let id = 5;
1819
1820 I::process_ip_fragment(
1822 &mut core_ctx,
1823 &mut bindings_ctx,
1824 FragmentSpec { id, offset: 0, size, m_flag: true },
1825 ExpectedResult::NeedMore,
1826 );
1827
1828 I::process_ip_fragment(
1830 &mut core_ctx,
1831 &mut bindings_ctx,
1832 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1833 ExpectedResult::NeedMore,
1834 );
1835
1836 I::process_ip_fragment(
1838 &mut core_ctx,
1839 &mut bindings_ctx,
1840 FragmentSpec { id, offset: size, size, m_flag: true },
1841 ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1842 );
1843 }
1844
1845 #[ip_test(I)]
1846 #[test_case(1)]
1847 #[test_case(10)]
1848 #[test_case(100)]
1849 fn test_ip_duplicate_fragment<I: TestIpExt>(size: u16) {
1850 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1851 let id = 5;
1852
1853 I::process_ip_fragment(
1855 &mut core_ctx,
1856 &mut bindings_ctx,
1857 FragmentSpec { id, offset: 0, size, m_flag: true },
1858 ExpectedResult::NeedMore,
1859 );
1860
1861 I::process_ip_fragment(
1863 &mut core_ctx,
1864 &mut bindings_ctx,
1865 FragmentSpec { id, offset: 0, size, m_flag: true },
1866 ExpectedResult::NeedMore,
1867 );
1868
1869 I::process_ip_fragment(
1872 &mut core_ctx,
1873 &mut bindings_ctx,
1874 FragmentSpec { id, offset: size, size, m_flag: false },
1875 ExpectedResult::Ready { body_fragment_blocks: 2 * size, key: test_key(id) },
1876 );
1877
1878 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id, 2 * size);
1879 }
1880
1881 #[ip_test(I)]
1882 #[test_case(1)]
1883 #[test_case(10)]
1884 #[test_case(100)]
1885 fn test_ip_out_of_bounds_fragment<I: TestIpExt>(size: u16) {
1886 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1887 let id = 5;
1888
1889 I::process_ip_fragment(
1891 &mut core_ctx,
1892 &mut bindings_ctx,
1893 FragmentSpec { id, offset: size, size, m_flag: false },
1894 ExpectedResult::NeedMore,
1895 );
1896
1897 I::process_ip_fragment(
1900 &mut core_ctx,
1901 &mut bindings_ctx,
1902 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1903 ExpectedResult::Invalid,
1904 );
1905 }
1906
1907 #[ip_test(I)]
1908 #[test_case(50, 100; "overlaps_front")]
1909 #[test_case(150, 100; "overlaps_back")]
1910 #[test_case(50, 200; "overlaps_both")]
1911 fn test_ip_overlapping_fragment<I: TestIpExt>(offset: u16, size: u16) {
1912 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1913 let id = 5;
1914
1915 I::process_ip_fragment(
1917 &mut core_ctx,
1918 &mut bindings_ctx,
1919 FragmentSpec { id, offset: 100, size: 100, m_flag: true },
1920 ExpectedResult::NeedMore,
1921 );
1922
1923 I::process_ip_fragment(
1926 &mut core_ctx,
1927 &mut bindings_ctx,
1928 FragmentSpec { id, offset, size, m_flag: true },
1929 ExpectedResult::Invalid,
1930 );
1931 }
1932
1933 #[test]
1934 fn test_ipv4_fragment_not_multiple_of_offset_unit() {
1935 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1936 let id = 0;
1937
1938 assert_eq!(core_ctx.state.cache.data_size, 0);
1939 assert_eq!(core_ctx.state.cache.num_fragments, 0);
1940 process_ipv4_fragment(
1945 &mut core_ctx,
1946 &mut bindings_ctx,
1947 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1948 get_ipv4_builder(),
1949 ExpectedResult::NeedMore,
1950 );
1951
1952 let mut builder = get_ipv4_builder();
1955 builder.id(id);
1956 builder.fragment_offset(FragmentOffset::new(1).unwrap());
1957 builder.mf_flag(true);
1958 let mut body: Vec<u8> = Vec::new();
1961 body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
1962 let mut buffer = builder
1963 .wrap_body(Buf::new(body, ..))
1964 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1965 .unwrap();
1966 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1967 assert_matches!(
1968 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1969 FragmentProcessingState::InvalidFragment
1970 );
1971
1972 let mut builder = get_ipv4_builder();
1976 builder.id(id);
1977 builder.fragment_offset(FragmentOffset::new(1).unwrap());
1978 builder.mf_flag(false);
1979 let mut body: Vec<u8> = Vec::new();
1982 body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
1983 let mut buffer = builder
1984 .wrap_body(Buf::new(body, ..))
1985 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1986 .unwrap();
1987 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1988 let (key, packet_len) = assert_matches!(
1989 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1990 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1991 );
1992 assert_eq!(key, test_key(id));
1993 assert_eq!(packet_len, 35);
1994 validate_size(&core_ctx.state.cache);
1995 let mut buffer: Vec<u8> = vec![0; packet_len];
1996 let mut buffer = &mut buffer[..];
1997 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1998 .unwrap();
1999 let packet = Ipv4Packet::parse_mut(&mut buffer, ()).unwrap();
2000 let mut expected_body: Vec<u8> = Vec::new();
2001 expected_body.extend(0..15);
2002 assert_eq!(packet.body(), &expected_body[..]);
2003 assert_eq!(core_ctx.state.cache.data_size, 0);
2004 assert_eq!(core_ctx.state.cache.num_fragments, 0);
2005 }
2006
2007 #[test]
2008 fn test_ipv6_fragment_not_multiple_of_offset_unit() {
2009 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
2010 let id = 0;
2011
2012 assert_eq!(core_ctx.state.cache.data_size, 0);
2013 assert_eq!(core_ctx.state.cache.num_fragments, 0);
2014 process_ipv6_fragment(
2019 &mut core_ctx,
2020 &mut bindings_ctx,
2021 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
2022 get_ipv6_builder(),
2023 ExpectedResult::NeedMore,
2024 );
2025
2026 let offset = 1;
2029 let body_size: usize = (FRAGMENT_BLOCK_SIZE - 1).into();
2030 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
2031 get_ipv6_builder(),
2032 FragmentOffset::new(offset).unwrap(),
2033 true,
2034 id.into(),
2035 );
2036 let body = generate_body_fragment(id, offset, body_size);
2037 let mut buffer = builder
2038 .wrap_body(Buf::new(body, ..))
2039 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2040 .unwrap();
2041 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
2042 assert_matches!(
2043 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
2044 FragmentProcessingState::InvalidFragment
2045 );
2046
2047 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
2051 get_ipv6_builder(),
2052 FragmentOffset::new(offset).unwrap(),
2053 false,
2054 id.into(),
2055 );
2056 let body = generate_body_fragment(id, offset, body_size);
2057 let mut buffer = builder
2058 .wrap_body(Buf::new(body, ..))
2059 .serialize_vec_outer(&mut NetworkSerializationContext::default())
2060 .unwrap();
2061 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
2062 let (key, packet_len) = assert_matches!(
2063 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
2064 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
2065 );
2066 assert_eq!(key, test_key(id));
2067 assert_eq!(packet_len, 55);
2068
2069 validate_size(&core_ctx.state.cache);
2070 let mut buffer: Vec<u8> = vec![0; packet_len];
2071 let mut buffer = &mut buffer[..];
2072 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
2073 .unwrap();
2074 let packet = Ipv6Packet::parse_mut(&mut buffer, ()).unwrap();
2075 let mut expected_body: Vec<u8> = Vec::new();
2076 expected_body.extend(0..15);
2077 assert_eq!(packet.body(), &expected_body[..]);
2078 assert_eq!(core_ctx.state.cache.data_size, 0);
2079 assert_eq!(core_ctx.state.cache.num_fragments, 0);
2080 }
2081
2082 #[ip_test(I)]
2083 fn test_ip_reassembly_with_multiple_intertwined_packets<I: TestIpExt>() {
2084 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2085 const SIZE: u16 = 1;
2086 let id_0 = 5;
2087 let id_1 = 10;
2088
2089 I::process_ip_fragment(
2094 &mut core_ctx,
2095 &mut bindings_ctx,
2096 FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
2097 ExpectedResult::NeedMore,
2098 );
2099
2100 I::process_ip_fragment(
2102 &mut core_ctx,
2103 &mut bindings_ctx,
2104 FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
2105 ExpectedResult::NeedMore,
2106 );
2107
2108 I::process_ip_fragment(
2110 &mut core_ctx,
2111 &mut bindings_ctx,
2112 FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
2113 ExpectedResult::NeedMore,
2114 );
2115
2116 I::process_ip_fragment(
2118 &mut core_ctx,
2119 &mut bindings_ctx,
2120 FragmentSpec { id: id_1, offset: 1, size: SIZE, m_flag: true },
2121 ExpectedResult::NeedMore,
2122 );
2123
2124 I::process_ip_fragment(
2126 &mut core_ctx,
2127 &mut bindings_ctx,
2128 FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
2129 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
2130 );
2131
2132 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_0, 3);
2133
2134 I::process_ip_fragment(
2136 &mut core_ctx,
2137 &mut bindings_ctx,
2138 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
2139 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_1) },
2140 );
2141
2142 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_1, 3);
2143 }
2144
2145 #[ip_test(I)]
2146 fn test_ip_reassembly_timer_with_multiple_intertwined_packets<I: TestIpExt>() {
2147 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2148 const SIZE: u16 = 1;
2149 let id_0 = 5;
2150 let id_1 = 10;
2151 let id_2 = 15;
2152
2153 const BEFORE_TIMEOUT1: Duration = Duration::from_secs(1);
2178 const BEFORE_TIMEOUT2: Duration = Duration::from_secs(2);
2179 const BEFORE_TIMEOUT3: Duration = Duration::from_secs(3);
2180 assert!(BEFORE_TIMEOUT1 < I::REASSEMBLY_TIMEOUT);
2181 assert!(BEFORE_TIMEOUT2 < I::REASSEMBLY_TIMEOUT);
2182 assert!(BEFORE_TIMEOUT3 < I::REASSEMBLY_TIMEOUT);
2183
2184 I::process_ip_fragment(
2186 &mut core_ctx,
2187 &mut bindings_ctx,
2188 FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
2189 ExpectedResult::NeedMore,
2190 );
2191
2192 I::process_ip_fragment(
2194 &mut core_ctx,
2195 &mut bindings_ctx,
2196 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
2197 ExpectedResult::NeedMore,
2198 );
2199
2200 I::process_ip_fragment(
2202 &mut core_ctx,
2203 &mut bindings_ctx,
2204 FragmentSpec { id: id_2, offset: 2, size: SIZE, m_flag: false },
2205 ExpectedResult::NeedMore,
2206 );
2207
2208 assert_empty(
2210 bindings_ctx
2211 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT1), &mut core_ctx),
2212 );
2213
2214 I::process_ip_fragment(
2216 &mut core_ctx,
2217 &mut bindings_ctx,
2218 FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
2219 ExpectedResult::NeedMore,
2220 );
2221
2222 assert_empty(
2224 bindings_ctx
2225 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT2), &mut core_ctx),
2226 );
2227
2228 I::process_ip_fragment(
2230 &mut core_ctx,
2231 &mut bindings_ctx,
2232 FragmentSpec { id: id_2, offset: 1, size: SIZE, m_flag: true },
2233 ExpectedResult::NeedMore,
2234 );
2235
2236 I::process_ip_fragment(
2238 &mut core_ctx,
2239 &mut bindings_ctx,
2240 FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
2241 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
2242 );
2243
2244 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_0, 3);
2245
2246 assert_empty(
2248 bindings_ctx
2249 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT3), &mut core_ctx),
2250 );
2251
2252 I::process_ip_fragment(
2254 &mut core_ctx,
2255 &mut bindings_ctx,
2256 FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
2257 ExpectedResult::NeedMore,
2258 );
2259
2260 I::process_ip_fragment(
2262 &mut core_ctx,
2263 &mut bindings_ctx,
2264 FragmentSpec { id: id_2, offset: 0, size: SIZE, m_flag: true },
2265 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_2) },
2266 );
2267
2268 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_2, 3);
2269
2270 bindings_ctx.trigger_timers_until_and_expect_unordered(
2273 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
2274 [FragmentTimerId::<I>::default()],
2275 &mut core_ctx,
2276 );
2277
2278 bindings_ctx.timers.assert_no_timers_installed();
2280
2281 I::process_ip_fragment(
2285 &mut core_ctx,
2286 &mut bindings_ctx,
2287 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: true },
2288 ExpectedResult::NeedMore,
2289 );
2290 }
2291
2292 #[test]
2293 fn test_no_more_fragments_in_middle_of_block() {
2294 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2295 process_ipv4_fragment(
2296 &mut core_ctx,
2297 &mut bindings_ctx,
2298 FragmentSpec { id: 0, offset: 100, size: 1, m_flag: false },
2299 get_ipv4_builder(),
2300 ExpectedResult::NeedMore,
2301 );
2302
2303 process_ipv4_fragment(
2304 &mut core_ctx,
2305 &mut bindings_ctx,
2306 FragmentSpec { id: 0, offset: 50, size: 1, m_flag: false },
2307 get_ipv4_builder(),
2308 ExpectedResult::Invalid,
2309 );
2310 }
2311
2312 #[ip_test(I)]
2313 fn test_cancel_timer_on_overlap<I: TestIpExt>() {
2314 const FRAGMENT_ID: u16 = 1;
2315
2316 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2317
2318 let key = test_key(FRAGMENT_ID);
2319
2320 for _ in 0..=2 {
2323 I::process_ip_fragment(
2324 &mut core_ctx,
2325 &mut bindings_ctx,
2326 FragmentSpec { id: FRAGMENT_ID, offset: 0, size: 10, m_flag: true },
2327 ExpectedResult::NeedMore,
2328 );
2329 core_ctx
2330 .state
2331 .cache
2332 .timers
2333 .assert_timers_after(&mut bindings_ctx, [(key, (), I::REASSEMBLY_TIMEOUT)]);
2334
2335 I::process_ip_fragment(
2336 &mut core_ctx,
2337 &mut bindings_ctx,
2338 FragmentSpec { id: FRAGMENT_ID, offset: 5, size: 10, m_flag: true },
2339 ExpectedResult::Invalid,
2340 );
2341 assert_eq!(bindings_ctx.timers.timers(), [],);
2342 }
2343 }
2344
2345 #[test]
2347 fn test_fragment_reassembly_evasion_cache_corruption() {
2348 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2349 let id = 5;
2350
2351 process_ipv4_fragment(
2353 &mut core_ctx,
2354 &mut bindings_ctx,
2355 FragmentSpec { id, offset: 0, size: 10, m_flag: true },
2356 get_ipv4_builder(),
2357 ExpectedResult::NeedMore,
2358 );
2359
2360 process_ipv4_fragment(
2362 &mut core_ctx,
2363 &mut bindings_ctx,
2364 FragmentSpec { id, offset: 30, size: 10, m_flag: true },
2365 get_ipv4_builder(),
2366 ExpectedResult::NeedMore,
2367 );
2368
2369 process_ipv4_fragment(
2372 &mut core_ctx,
2373 &mut bindings_ctx,
2374 FragmentSpec { id, offset: 10, size: 10, m_flag: false },
2375 get_ipv4_builder(),
2376 ExpectedResult::Invalid,
2377 );
2378
2379 process_ipv4_fragment(
2383 &mut core_ctx,
2384 &mut bindings_ctx,
2385 FragmentSpec { id, offset: 40, size: 10, m_flag: false },
2386 get_ipv4_builder(),
2387 ExpectedResult::NeedMore,
2388 );
2389 }
2390
2391 #[test]
2393 fn test_multiple_last_fragments_evasion() {
2394 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2395 let id = 6;
2396
2397 process_ipv4_fragment(
2400 &mut core_ctx,
2401 &mut bindings_ctx,
2402 FragmentSpec { id, offset: 2, size: 1, m_flag: false },
2403 get_ipv4_builder(),
2404 ExpectedResult::NeedMore,
2405 );
2406
2407 process_ipv4_fragment(
2411 &mut core_ctx,
2412 &mut bindings_ctx,
2413 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
2414 get_ipv4_builder(),
2415 ExpectedResult::Invalid,
2416 );
2417 }
2418}