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::Ipv6ExtensionHeaderData;
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
136const MAX_FRAGMENT_CACHE_SIZE: usize = 4 * 1024 * 1024;
143
144pub trait FragmentContext<I: Ip, BT: FragmentBindingsTypes> {
146 fn with_state_mut<O, F: FnOnce(&mut IpPacketFragmentCache<I, BT>) -> O>(&mut self, cb: F) -> O;
148}
149
150pub trait FragmentBindingsTypes: TimerBindingsTypes + InstantBindingsTypes {}
152impl<BT> FragmentBindingsTypes for BT where BT: TimerBindingsTypes + InstantBindingsTypes {}
153
154pub trait FragmentBindingsContext: TimerContext + FragmentBindingsTypes {}
156impl<BC> FragmentBindingsContext for BC where BC: TimerContext + FragmentBindingsTypes {}
157
158#[derive(Hash, Eq, PartialEq, Default, Clone, Debug, GenericOverIp)]
160#[generic_over_ip(I, Ip)]
161pub struct FragmentTimerId<I: Ip>(IpVersionMarker<I>);
162
163pub trait FragmentHandler<I: ReassemblyIpExt, BC> {
165 fn process_fragment<B: SplitByteSlice>(
171 &mut self,
172 bindings_ctx: &mut BC,
173 packet: I::Packet<B>,
174 ) -> FragmentProcessingState<I, B>
175 where
176 I::Packet<B>: FragmentablePacket;
177
178 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
196 &mut self,
197 bindings_ctx: &mut BC,
198 key: &FragmentCacheKey<I>,
199 buffer: BV,
200 ) -> Result<(), FragmentReassemblyError>;
201}
202
203impl<I: IpExt + ReassemblyIpExt, BC: FragmentBindingsContext, CC: FragmentContext<I, BC>>
204 FragmentHandler<I, BC> for CC
205{
206 fn process_fragment<B: SplitByteSlice>(
207 &mut self,
208 bindings_ctx: &mut BC,
209 packet: I::Packet<B>,
210 ) -> FragmentProcessingState<I, B>
211 where
212 I::Packet<B>: FragmentablePacket,
213 {
214 self.with_state_mut(|cache| {
215 let (res, timer_action) = cache.process_fragment(packet);
216
217 if let Some(timer_action) = timer_action {
218 match timer_action {
219 CacheTimerAction::CreateNewTimer(key) => {
222 assert_eq!(
223 cache.timers.schedule_after(
224 bindings_ctx,
225 key,
226 (),
227 I::REASSEMBLY_TIMEOUT,
228 ),
229 None
230 )
231 }
232 CacheTimerAction::CancelExistingTimer(key) => {
233 assert_ne!(cache.timers.cancel(bindings_ctx, &key), None)
234 }
235 }
236 }
237
238 res
239 })
240 }
241
242 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
243 &mut self,
244 bindings_ctx: &mut BC,
245 key: &FragmentCacheKey<I>,
246 buffer: BV,
247 ) -> Result<(), FragmentReassemblyError> {
248 self.with_state_mut(|cache| {
249 let res = cache.reassemble_packet(key, buffer);
250
251 match res {
252 Ok(_) | Err(FragmentReassemblyError::PacketParsingError) => {
253 assert_matches!(cache.timers.cancel(bindings_ctx, key), Some(_));
257 }
258 Err(FragmentReassemblyError::InvalidKey)
259 | Err(FragmentReassemblyError::MissingFragments) => {}
260 }
261
262 res
263 })
264 }
265}
266
267impl<I: ReassemblyIpExt, BC: FragmentBindingsContext, CC: FragmentContext<I, BC>>
268 HandleableTimer<CC, BC> for FragmentTimerId<I>
269{
270 fn handle(self, core_ctx: &mut CC, bindings_ctx: &mut BC, _: BC::UniqueTimerId) {
271 let Self(IpVersionMarker { .. }) = self;
272 core_ctx.with_state_mut(|cache| {
273 let Some((key, ())) = cache.timers.pop(bindings_ctx) else {
274 return;
275 };
276
277 let FragmentCacheData { missing_blocks: _, body_fragments, header: _, total_size } =
279 assert_matches!(cache.remove_data(&key), Some(c) => c);
280 debug!(
281 "reassembly for {key:?} \
282 timed out with {} fragments and {total_size} bytes",
283 body_fragments.len(),
284 );
285 });
286 }
287}
288
289pub trait FragmentablePacket {
291 fn fragment_data(&self) -> (u32, u16, bool);
301}
302
303impl<B: SplitByteSlice> FragmentablePacket for Ipv4Packet<B> {
304 fn fragment_data(&self) -> (u32, u16, bool) {
305 (u32::from(self.id()), self.fragment_offset().into_raw(), self.mf_flag())
306 }
307}
308
309impl<B: SplitByteSlice> FragmentablePacket for Ipv6Packet<B> {
310 fn fragment_data(&self) -> (u32, u16, bool) {
311 for ext_hdr in self.iter_extension_hdrs() {
312 if let Ipv6ExtensionHeaderData::Fragment { fragment_data } = ext_hdr.data() {
313 return (
314 fragment_data.identification(),
315 fragment_data.fragment_offset().into_raw(),
316 fragment_data.m_flag(),
317 );
318 }
319 }
320
321 unreachable!(
322 "Should never call this function if the packet does not have a fragment header"
323 );
324 }
325}
326
327#[derive(Debug)]
329pub enum FragmentProcessingState<I: ReassemblyIpExt, B: SplitByteSlice> {
330 NotNeeded(I::Packet<B>),
333
334 InvalidFragment,
346
347 NeedMoreFragments,
351
352 OutOfMemory,
355
356 Ready { key: FragmentCacheKey<I>, packet_len: usize },
361}
362
363#[derive(Debug, PartialEq, Eq)]
365pub enum FragmentReassemblyError {
366 MissingFragments,
368
369 InvalidKey,
374
375 PacketParsingError,
377}
378
379#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
384pub struct FragmentCacheKey<I: ReassemblyIpExt> {
385 src_ip: I::Addr,
386 dst_ip: I::Addr,
387 fragment_id: u32,
388 ip_specific_fields: I::FragmentCacheKeyPart,
389}
390
391#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
396struct BlockRange {
397 start: u16,
398 end: u16,
399}
400
401#[derive(Debug)]
403struct FragmentCacheData {
404 missing_blocks: BTreeSet<BlockRange>,
412
413 body_fragments: BinaryHeap<PacketBodyFragment>,
425
426 header: Option<Vec<u8>>,
431
432 total_size: usize,
438}
439
440impl Default for FragmentCacheData {
441 fn default() -> FragmentCacheData {
442 FragmentCacheData {
443 missing_blocks: core::iter::once(BlockRange { start: 0, end: u16::MAX }).collect(),
444 body_fragments: BinaryHeap::new(),
445 header: None,
446 total_size: 0,
447 }
448 }
449}
450
451impl FragmentCacheData {
452 fn find_gap(&self, BlockRange { start, end }: BlockRange) -> FindGapResult {
454 let result = self.missing_blocks.iter().find_map(|gap| {
455 if gap.start <= start && gap.end >= end {
457 return Some(FindGapResult::Found { gap: *gap });
458 }
459
460 if gap.start > end || gap.end < start {
463 return None;
464 }
465
466 return Some(FindGapResult::Overlap);
469 });
470
471 match result {
472 Some(result) => result,
473 None => {
474 let last = self.body_fragments.peek().unwrap();
484 if last.offset < start {
485 FindGapResult::OutOfBounds
486 } else {
487 FindGapResult::Duplicate
488 }
489 }
490 }
491 }
492}
493
494enum FindGapResult {
496 Found {
499 gap: BlockRange,
500 },
501 Overlap,
505 OutOfBounds,
507 Duplicate,
523}
524
525#[derive(Debug)]
527pub struct IpPacketFragmentCache<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
528 cache: HashMap<FragmentCacheKey<I>, FragmentCacheData>,
529 size: usize,
530 threshold: usize,
531 timers: LocalTimerHeap<FragmentCacheKey<I>, (), BT>,
532}
533
534impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> IpPacketFragmentCache<I, BC> {
535 pub fn new<CC: CoreTimerContext<FragmentTimerId<I>, BC>>(
537 bindings_ctx: &mut BC,
538 ) -> IpPacketFragmentCache<I, BC> {
539 IpPacketFragmentCache {
540 cache: HashMap::new(),
541 size: 0,
542 threshold: MAX_FRAGMENT_CACHE_SIZE,
543 timers: LocalTimerHeap::new(bindings_ctx, CC::convert_timer(Default::default())),
544 }
545 }
546}
547
548enum CacheTimerAction<I: ReassemblyIpExt> {
549 CreateNewTimer(FragmentCacheKey<I>),
550 CancelExistingTimer(FragmentCacheKey<I>),
551}
552
553impl<I: ReassemblyIpExt, BT: FragmentBindingsTypes> IpPacketFragmentCache<I, BT> {
554 fn process_fragment<B: SplitByteSlice>(
560 &mut self,
561 packet: I::Packet<B>,
562 ) -> (FragmentProcessingState<I, B>, Option<CacheTimerAction<I>>)
563 where
564 I::Packet<B>: FragmentablePacket,
565 {
566 if self.above_size_threshold() {
567 return (FragmentProcessingState::OutOfMemory, None);
568 }
569
570 let (id, offset, m_flag) = packet.fragment_data();
572
573 if offset == 0 && !m_flag {
578 return (FragmentProcessingState::NotNeeded(packet), None);
579 }
580
581 if packet.body().is_empty() {
586 return (FragmentProcessingState::NeedMoreFragments, None);
587 }
588
589 if m_flag && (packet.body().len() % (FRAGMENT_BLOCK_SIZE as usize) != 0) {
593 return (FragmentProcessingState::InvalidFragment, None);
594 }
595
596 let key = FragmentCacheKey {
598 src_ip: packet.src_ip(),
599 dst_ip: packet.dst_ip(),
600 fragment_id: id,
601 ip_specific_fields: I::ip_specific_key_part(&packet),
602 };
603
604 let num_fragment_blocks = 1 + ((packet.body().len() - 1) / (FRAGMENT_BLOCK_SIZE as usize));
618 assert!(num_fragment_blocks > 0);
619
620 let fragment_blocks_range =
626 if let Ok(offset_end) = u16::try_from((offset as usize) + num_fragment_blocks - 1) {
627 if offset_end <= MAX_FRAGMENT_BLOCKS {
628 BlockRange { start: offset, end: offset_end }
629 } else {
630 return (FragmentProcessingState::InvalidFragment, None);
631 }
632 } else {
633 return (FragmentProcessingState::InvalidFragment, None);
634 };
635
636 let (fragment_data, timer_not_yet_scheduled) = self.get_or_create(key);
638
639 let found_gap = match fragment_data.find_gap(fragment_blocks_range) {
641 FindGapResult::Overlap | FindGapResult::OutOfBounds => {
642 assert_matches!(self.remove_data(&key), Some(_));
654
655 return (
656 FragmentProcessingState::InvalidFragment,
657 (!timer_not_yet_scheduled)
658 .then_some(CacheTimerAction::CancelExistingTimer(key)),
659 );
660 }
661 FindGapResult::Duplicate => {
662 return (FragmentProcessingState::NeedMoreFragments, None);
674 }
675 FindGapResult::Found { gap } => gap,
676 };
677
678 let timer_id = timer_not_yet_scheduled.then_some(CacheTimerAction::CreateNewTimer(key));
679
680 assert!(fragment_data.missing_blocks.remove(&found_gap));
683
684 if found_gap.start < fragment_blocks_range.start {
701 assert!(fragment_data.missing_blocks.insert(BlockRange {
702 start: found_gap.start,
703 end: fragment_blocks_range.start - 1
704 }));
705 }
706
707 if found_gap.end > fragment_blocks_range.end && m_flag {
742 assert!(
743 fragment_data.missing_blocks.insert(BlockRange {
744 start: fragment_blocks_range.end + 1,
745 end: found_gap.end
746 })
747 );
748 } else if found_gap.end > fragment_blocks_range.end && !m_flag && found_gap.end < u16::MAX {
749 return (FragmentProcessingState::InvalidFragment, timer_id);
753 } else {
754 assert!(
760 found_gap.end == fragment_blocks_range.end
761 || (!m_flag && found_gap.end == u16::MAX),
762 "found_gap: {:?}, fragment_blocks_range: {:?} offset: {:?}, m_flag: {:?}",
763 found_gap,
764 fragment_blocks_range,
765 offset,
766 m_flag
767 );
768 }
769
770 let mut added_bytes = 0;
771 if offset == 0 {
773 assert_eq!(fragment_data.header, None);
774 let header = get_header::<B, I>(&packet);
775 added_bytes = header.len();
776 fragment_data.header = Some(header);
777 }
778
779 let mut body = Vec::with_capacity(packet.body().len());
781 body.extend_from_slice(packet.body());
782 added_bytes += body.len();
783 fragment_data.total_size += added_bytes;
784 fragment_data.body_fragments.push(PacketBodyFragment::new(offset, body));
785
786 let result = if fragment_data.missing_blocks.is_empty() {
792 FragmentProcessingState::Ready { key, packet_len: fragment_data.total_size }
793 } else {
794 FragmentProcessingState::NeedMoreFragments
795 };
796
797 self.increment_size(added_bytes);
798 (result, timer_id)
799 }
800
801 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
819 &mut self,
820 key: &FragmentCacheKey<I>,
821 buffer: BV,
822 ) -> Result<(), FragmentReassemblyError> {
823 let entry = match self.cache.entry(*key) {
824 Entry::Occupied(entry) => entry,
825 Entry::Vacant(_) => return Err(FragmentReassemblyError::InvalidKey),
826 };
827
828 if !entry.get().missing_blocks.is_empty() {
830 return Err(FragmentReassemblyError::MissingFragments);
831 }
832 let (_key, data) = entry.remove_entry();
835 self.size -= data.total_size;
836
837 assert_matches!(data.header, Some(_));
839
840 let body_fragments = data.body_fragments.into_sorted_vec().into_iter().map(|x| x.data);
843 I::Packet::reassemble_fragmented_packet(buffer, data.header.unwrap(), body_fragments)
844 .map_err(|_| FragmentReassemblyError::PacketParsingError)
845 }
846
847 fn get_or_create(&mut self, key: FragmentCacheKey<I>) -> (&mut FragmentCacheData, bool) {
852 match self.cache.entry(key) {
853 Entry::Occupied(e) => (e.into_mut(), false),
854 Entry::Vacant(e) => {
855 (e.insert(FragmentCacheData::default()), true)
860 }
861 }
862 }
863
864 fn above_size_threshold(&self) -> bool {
865 self.size >= self.threshold
866 }
867
868 fn increment_size(&mut self, sz: usize) {
869 assert!(!self.above_size_threshold());
870 self.size += sz;
871 }
872
873 fn remove_data(&mut self, key: &FragmentCacheKey<I>) -> Option<FragmentCacheData> {
874 let data = self.cache.remove(key)?;
875 self.size -= data.total_size;
876 Some(data)
877 }
878}
879
880fn get_header<B: SplitByteSlice, I: IpExt>(packet: &I::Packet<B>) -> Vec<u8> {
882 match packet.as_ip_addr_ref() {
883 IpAddr::V4(packet) => packet.copy_header_bytes_for_fragment(),
884 IpAddr::V6(packet) => {
885 packet.copy_header_bytes_for_fragment()
890 }
891 }
892}
893
894#[derive(Debug, PartialEq, Eq)]
896struct PacketBodyFragment {
897 offset: u16,
898 data: Vec<u8>,
899}
900
901impl PacketBodyFragment {
902 fn new(offset: u16, data: Vec<u8>) -> Self {
904 PacketBodyFragment { offset, data }
905 }
906}
907
908impl PartialOrd for PacketBodyFragment {
911 fn partial_cmp(&self, other: &PacketBodyFragment) -> Option<Ordering> {
912 Some(self.cmp(other))
913 }
914}
915
916impl Ord for PacketBodyFragment {
917 fn cmp(&self, other: &Self) -> Ordering {
918 self.offset.cmp(&other.offset)
919 }
920}
921
922#[cfg(test)]
923mod tests {
924 use alloc::vec;
925
926 use assert_matches::assert_matches;
927 use ip_test_macro::ip_test;
928 use net_declare::{net_ip_v4, net_ip_v6};
929 use net_types::Witness;
930 use net_types::ip::{Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
931 use netstack3_base::testutil::{
932 FakeBindingsCtx, FakeCoreCtx, FakeInstant, FakeTimerCtxExt, TEST_ADDRS_V4, TEST_ADDRS_V6,
933 assert_empty,
934 };
935 use netstack3_base::{CtxPair, IntoCoreTimerCtx, NetworkSerializationContext};
936 use packet::{Buf, NestablePacketBuilder as _, ParsablePacket, ParseBuffer, Serializer};
937 use packet_formats::ip::{FragmentOffset, IpProto, Ipv6Proto};
938 use packet_formats::ipv4::Ipv4PacketBuilder;
939 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithFragmentHeader};
940 use test_case::test_case;
941
942 use super::*;
943
944 struct FakeFragmentContext<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
945 cache: IpPacketFragmentCache<I, BT>,
946 }
947
948 impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> FakeFragmentContext<I, BC>
949 where
950 BC::DispatchId: From<FragmentTimerId<I>>,
951 {
952 fn new(bindings_ctx: &mut BC) -> Self {
953 Self { cache: IpPacketFragmentCache::new::<IntoCoreTimerCtx>(bindings_ctx) }
954 }
955 }
956
957 type FakeCtxImpl<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
958 type FakeBindingsCtxImpl<I> = FakeBindingsCtx<FragmentTimerId<I>, (), (), ()>;
959 type FakeCoreCtxImpl<I> = FakeCoreCtx<FakeFragmentContext<I, FakeBindingsCtxImpl<I>>, (), ()>;
960
961 impl<I: ReassemblyIpExt> FragmentContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
962 fn with_state_mut<
963 O,
964 F: FnOnce(&mut IpPacketFragmentCache<I, FakeBindingsCtxImpl<I>>) -> O,
965 >(
966 &mut self,
967 cb: F,
968 ) -> O {
969 cb(&mut self.state.cache)
970 }
971 }
972
973 #[derive(PartialEq)]
976 enum ExpectedResult<I: ReassemblyIpExt> {
977 Ready { body_fragment_blocks: u16, key: FragmentCacheKey<I> },
982
983 NeedMore,
986
987 Invalid,
989
990 OutOfMemory,
992 }
993
994 fn get_ipv4_builder() -> Ipv4PacketBuilder {
996 Ipv4PacketBuilder::new(
997 TEST_ADDRS_V4.remote_ip,
998 TEST_ADDRS_V4.local_ip,
999 10,
1000 <Ipv4 as TestIpExt>::PROTOCOL,
1001 )
1002 }
1003
1004 fn get_ipv6_builder() -> Ipv6PacketBuilder {
1006 Ipv6PacketBuilder::new(
1007 TEST_ADDRS_V6.remote_ip,
1008 TEST_ADDRS_V6.local_ip,
1009 10,
1010 <Ipv6 as TestIpExt>::PROTOCOL,
1011 )
1012 }
1013
1014 fn validate_size<I: ReassemblyIpExt, BT: FragmentBindingsTypes>(
1016 cache: &IpPacketFragmentCache<I, BT>,
1017 ) {
1018 let mut sz: usize = 0;
1019
1020 for v in cache.cache.values() {
1021 sz += v.total_size;
1022 }
1023
1024 assert_eq!(sz, cache.size);
1025 }
1026
1027 struct FragmentSpec {
1028 id: u16,
1030 offset: u16,
1032 size: u16,
1034 m_flag: bool,
1036 }
1037
1038 fn expected_packet_size<I: TestIpExt>(num_fragment_blocks: u16) -> usize {
1039 usize::from(num_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE) + I::HEADER_LENGTH
1040 }
1041
1042 fn process_ipv4_fragment<CC: FragmentContext<Ipv4, BC>, BC: FragmentBindingsContext>(
1044 core_ctx: &mut CC,
1045 bindings_ctx: &mut BC,
1046 FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1047 mut builder: Ipv4PacketBuilder,
1048 expected_result: ExpectedResult<Ipv4>,
1049 ) {
1050 builder.id(id);
1051 builder.fragment_offset(FragmentOffset::new(offset).unwrap());
1052 builder.mf_flag(m_flag);
1053 let body = generate_body_fragment(
1054 id,
1055 offset,
1056 usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1057 );
1058
1059 let mut buffer = builder
1060 .wrap_body(Buf::new(body, ..))
1061 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1062 .unwrap();
1063 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1064
1065 let actual_result =
1066 FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1067 match expected_result {
1068 ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1069 let (key, packet_len) = assert_matches!(
1070 actual_result,
1071 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1072 );
1073 assert_eq!(key, expected_key);
1074 assert_eq!(packet_len, expected_packet_size::<Ipv4>(body_fragment_blocks));
1075 }
1076 ExpectedResult::NeedMore => {
1077 assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1078 }
1079 ExpectedResult::Invalid => {
1080 assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1081 }
1082 ExpectedResult::OutOfMemory => {
1083 assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1084 }
1085 }
1086 }
1087
1088 fn process_ipv6_fragment<CC: FragmentContext<Ipv6, BC>, BC: FragmentBindingsContext>(
1092 core_ctx: &mut CC,
1093 bindings_ctx: &mut BC,
1094 FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1095 builder: Ipv6PacketBuilder,
1096 expected_result: ExpectedResult<Ipv6>,
1097 ) {
1098 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1099 builder,
1100 FragmentOffset::new(offset).unwrap(),
1101 m_flag,
1102 id.into(),
1103 );
1104
1105 let body = generate_body_fragment(
1106 id,
1107 offset,
1108 usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1109 );
1110
1111 let mut buffer = builder
1112 .wrap_body(Buf::new(body, ..))
1113 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1114 .unwrap();
1115 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1116
1117 let actual_result =
1118 FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1119 match expected_result {
1120 ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1121 let (key, packet_len) = assert_matches!(
1122 actual_result,
1123 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1124 );
1125 assert_eq!(key, expected_key);
1126 assert_eq!(packet_len, expected_packet_size::<Ipv6>(body_fragment_blocks));
1127 }
1128 ExpectedResult::NeedMore => {
1129 assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1130 }
1131 ExpectedResult::Invalid => {
1132 assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1133 }
1134 ExpectedResult::OutOfMemory => {
1135 assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1136 }
1137 }
1138 }
1139
1140 trait TestIpExt: IpExt + netstack3_base::testutil::TestIpExt + ReassemblyIpExt {
1141 const HEADER_LENGTH: usize;
1142
1143 const PROTOCOL: Self::Proto;
1144
1145 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1146 core_ctx: &mut CC,
1147 bindings_ctx: &mut BC,
1148 spec: FragmentSpec,
1149 expected_result: ExpectedResult<Self>,
1150 );
1151 }
1152
1153 impl TestIpExt for Ipv4 {
1154 const HEADER_LENGTH: usize = packet_formats::ipv4::HDR_PREFIX_LEN;
1155
1156 const PROTOCOL: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1157
1158 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1159 core_ctx: &mut CC,
1160 bindings_ctx: &mut BC,
1161 spec: FragmentSpec,
1162 expected_result: ExpectedResult<Ipv4>,
1163 ) {
1164 process_ipv4_fragment(core_ctx, bindings_ctx, spec, get_ipv4_builder(), expected_result)
1165 }
1166 }
1167 impl TestIpExt for Ipv6 {
1168 const HEADER_LENGTH: usize = packet_formats::ipv6::IPV6_FIXED_HDR_LEN;
1169
1170 const PROTOCOL: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1171
1172 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1173 core_ctx: &mut CC,
1174 bindings_ctx: &mut BC,
1175 spec: FragmentSpec,
1176 expected_result: ExpectedResult<Ipv6>,
1177 ) {
1178 process_ipv6_fragment(core_ctx, bindings_ctx, spec, get_ipv6_builder(), expected_result)
1179 }
1180 }
1181
1182 fn try_reassemble_ip_packet<
1186 I: TestIpExt + netstack3_base::IpExt,
1187 CC: FragmentContext<I, BC>,
1188 BC: FragmentBindingsContext,
1189 >(
1190 core_ctx: &mut CC,
1191 bindings_ctx: &mut BC,
1192 fragment_id: u16,
1193 body_fragment_blocks: u16,
1194 ) {
1195 let mut buffer: Vec<u8> = vec![
1196 0;
1197 usize::from(body_fragment_blocks)
1198 * usize::from(FRAGMENT_BLOCK_SIZE)
1199 + I::HEADER_LENGTH
1200 ];
1201 let mut buffer = &mut buffer[..];
1202 let key = test_key(fragment_id);
1203
1204 FragmentHandler::reassemble_packet(core_ctx, bindings_ctx, &key, &mut buffer).unwrap();
1205 let packet = I::Packet::parse_mut(&mut buffer, ()).unwrap();
1206
1207 let expected_body = generate_body_fragment(
1208 fragment_id,
1209 0,
1210 usize::from(body_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE),
1211 );
1212 assert_eq!(packet.body(), &expected_body[..]);
1213 }
1214
1215 fn generate_body_fragment(fragment_id: u16, fragment_offset: u16, len: usize) -> Vec<u8> {
1221 let start = usize::from(fragment_id)
1225 + usize::from(fragment_offset) * usize::from(FRAGMENT_BLOCK_SIZE);
1226 (start..start + len).map(|byte| byte as u8).collect()
1227 }
1228
1229 fn test_key<I: TestIpExt>(id: u16) -> FragmentCacheKey<I> {
1231 #[derive(GenericOverIp)]
1232 #[generic_over_ip(I, Ip)]
1233 struct Wrapper<I: ReassemblyIpExt>(I::FragmentCacheKeyPart);
1234
1235 let Wrapper(ip_specific_fields) =
1236 I::map_ip_out((), |()| Wrapper(Ipv4::PROTOCOL), |()| Wrapper(()));
1237
1238 FragmentCacheKey {
1239 src_ip: I::TEST_ADDRS.remote_ip.get(),
1240 dst_ip: I::TEST_ADDRS.local_ip.get(),
1241 fragment_id: id.into(),
1242 ip_specific_fields,
1243 }
1244 }
1245
1246 fn new_context<I: ReassemblyIpExt>() -> FakeCtxImpl<I> {
1247 FakeCtxImpl::<I>::with_default_bindings_ctx(|bindings_ctx| {
1248 FakeCoreCtxImpl::with_state(FakeFragmentContext::new(bindings_ctx))
1249 })
1250 }
1251
1252 #[test]
1253 fn test_ipv4_reassembly_not_needed() {
1254 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1255
1256 let builder = get_ipv4_builder();
1260 let body = [1, 2, 3, 4, 5];
1261 let mut buffer = builder
1262 .wrap_body(Buf::new(body.to_vec(), ..))
1263 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1264 .unwrap();
1265 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1266 assert_matches!(
1267 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1268 FragmentProcessingState::NotNeeded(unfragmented) if unfragmented.body() == body
1269 );
1270 }
1271
1272 #[test]
1273 #[should_panic(
1274 expected = "internal error: entered unreachable code: Should never call this function if the packet does not have a fragment header"
1275 )]
1276 fn test_ipv6_reassembly_not_needed() {
1277 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1278
1279 let builder = get_ipv6_builder();
1283 let mut buffer = builder
1284 .wrap_body(Buf::new(vec![1, 2, 3, 4, 5], ..))
1285 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1286 .unwrap();
1287 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1288 assert_matches!(
1289 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1290 FragmentProcessingState::InvalidFragment
1291 );
1292 }
1293
1294 #[ip_test(I)]
1295 #[test_case(1)]
1296 #[test_case(10)]
1297 #[test_case(100)]
1298 fn test_ip_reassembly<I: TestIpExt>(size: u16) {
1299 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1300 let id = 5;
1301
1302 I::process_ip_fragment(
1306 &mut core_ctx,
1307 &mut bindings_ctx,
1308 FragmentSpec { id, offset: 0, size, m_flag: true },
1309 ExpectedResult::NeedMore,
1310 );
1311
1312 I::process_ip_fragment(
1314 &mut core_ctx,
1315 &mut bindings_ctx,
1316 FragmentSpec { id, offset: size, size, m_flag: true },
1317 ExpectedResult::NeedMore,
1318 );
1319
1320 I::process_ip_fragment(
1322 &mut core_ctx,
1323 &mut bindings_ctx,
1324 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1325 ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1326 );
1327
1328 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id, 3 * size);
1329 }
1330
1331 #[test]
1332 fn test_ipv4_key_uniqueness() {
1333 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1334
1335 const RIGHT_SRC: Ipv4Addr = net_ip_v4!("192.0.2.1");
1336 const WRONG_SRC: Ipv4Addr = net_ip_v4!("192.0.2.2");
1337
1338 const RIGHT_DST: Ipv4Addr = net_ip_v4!("192.0.2.3");
1339 const WRONG_DST: Ipv4Addr = net_ip_v4!("192.0.2.4");
1340
1341 const RIGHT_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1342 const WRONG_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Udp);
1343
1344 const RIGHT_ID: u16 = 1;
1345 const WRONG_ID: u16 = 2;
1346
1347 const TTL: u8 = 1;
1348
1349 process_ipv4_fragment(
1351 &mut core_ctx,
1352 &mut bindings_ctx,
1353 FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1354 Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1355 ExpectedResult::NeedMore,
1356 );
1357
1358 for (id, src, dst, proto) in [
1361 (RIGHT_ID, RIGHT_SRC, RIGHT_DST, WRONG_PROTO),
1362 (RIGHT_ID, RIGHT_SRC, WRONG_DST, RIGHT_PROTO),
1363 (RIGHT_ID, WRONG_SRC, RIGHT_DST, RIGHT_PROTO),
1364 (WRONG_ID, RIGHT_SRC, RIGHT_DST, RIGHT_PROTO),
1365 ] {
1366 process_ipv4_fragment(
1367 &mut core_ctx,
1368 &mut bindings_ctx,
1369 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1370 Ipv4PacketBuilder::new(src, dst, TTL, proto),
1371 ExpectedResult::NeedMore,
1372 );
1373 }
1374
1375 const KEY: FragmentCacheKey<Ipv4> = FragmentCacheKey {
1378 src_ip: RIGHT_SRC,
1379 dst_ip: RIGHT_DST,
1380 fragment_id: RIGHT_ID as u32,
1381 ip_specific_fields: RIGHT_PROTO,
1382 };
1383 process_ipv4_fragment(
1384 &mut core_ctx,
1385 &mut bindings_ctx,
1386 FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1387 Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1388 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1389 );
1390 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv4>(2)];
1391 let mut buffer = &mut buffer[..];
1392 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1393 .expect("reassembly should succeed");
1394 let _packet = Ipv4Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1395 }
1396
1397 #[test]
1398 fn test_ipv6_key_uniqueness() {
1399 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1400
1401 const RIGHT_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1402 const WRONG_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1403
1404 const RIGHT_DST: Ipv6Addr = net_ip_v6!("2001:0db8::3");
1405 const WRONG_DST: Ipv6Addr = net_ip_v6!("2001:0db8::4");
1406
1407 const RIGHT_ID: u16 = 1;
1408 const WRONG_ID: u16 = 2;
1409
1410 const TTL: u8 = 1;
1411
1412 process_ipv6_fragment(
1414 &mut core_ctx,
1415 &mut bindings_ctx,
1416 FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1417 Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1418 ExpectedResult::NeedMore,
1419 );
1420
1421 for (id, src, dst) in [
1424 (RIGHT_ID, RIGHT_SRC, WRONG_DST),
1425 (RIGHT_ID, WRONG_SRC, RIGHT_DST),
1426 (WRONG_ID, RIGHT_SRC, RIGHT_DST),
1427 ] {
1428 process_ipv6_fragment(
1429 &mut core_ctx,
1430 &mut bindings_ctx,
1431 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1432 Ipv6PacketBuilder::new(src, dst, TTL, Ipv6::PROTOCOL),
1433 ExpectedResult::NeedMore,
1434 );
1435 }
1436
1437 const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1440 src_ip: RIGHT_SRC,
1441 dst_ip: RIGHT_DST,
1442 fragment_id: RIGHT_ID as u32,
1443 ip_specific_fields: (),
1444 };
1445 process_ipv6_fragment(
1446 &mut core_ctx,
1447 &mut bindings_ctx,
1448 FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1449 Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1450 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1451 );
1452 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1453 let mut buffer = &mut buffer[..];
1454 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1455 .expect("reassembly should succeed");
1456 let _packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1457 }
1458
1459 #[test]
1460 fn test_ipv6_reassemble_different_protocols() {
1461 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1462
1463 const SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1464 const DST: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1465 const ID: u16 = 1;
1466 const TTL: u8 = 1;
1467
1468 const PROTO1: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1469 const PROTO2: Ipv6Proto = Ipv6Proto::Proto(IpProto::Udp);
1470
1471 process_ipv6_fragment(
1473 &mut core_ctx,
1474 &mut bindings_ctx,
1475 FragmentSpec { id: ID, offset: 0, size: 1, m_flag: true },
1476 Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO1),
1477 ExpectedResult::NeedMore,
1478 );
1479
1480 const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1484 src_ip: SRC,
1485 dst_ip: DST,
1486 fragment_id: ID as u32,
1487 ip_specific_fields: (),
1488 };
1489 process_ipv6_fragment(
1490 &mut core_ctx,
1491 &mut bindings_ctx,
1492 FragmentSpec { id: ID, offset: 1, size: 1, m_flag: false },
1493 Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO2),
1494 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1495 );
1496 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1497 let mut buffer = &mut buffer[..];
1498 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1499 .expect("reassembly should succeed");
1500 let packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1501 assert_eq!(packet.proto(), PROTO1);
1502 }
1503
1504 #[ip_test(I)]
1505 #[test_case(1)]
1506 #[test_case(10)]
1507 #[test_case(100)]
1508 fn test_ip_reassemble_with_missing_blocks<I: TestIpExt>(size: u16) {
1509 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1510 let id = 5;
1511
1512 I::process_ip_fragment(
1517 &mut core_ctx,
1518 &mut bindings_ctx,
1519 FragmentSpec { id, offset: 0, size, m_flag: true },
1520 ExpectedResult::NeedMore,
1521 );
1522
1523 I::process_ip_fragment(
1525 &mut core_ctx,
1526 &mut bindings_ctx,
1527 FragmentSpec { id, offset: size, size, m_flag: true },
1528 ExpectedResult::NeedMore,
1529 );
1530
1531 let mut buffer: Vec<u8> = vec![0; 1];
1532 let mut buffer = &mut buffer[..];
1533 let key = test_key(id);
1534 assert_eq!(
1535 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1536 .unwrap_err(),
1537 FragmentReassemblyError::MissingFragments,
1538 );
1539 }
1540
1541 #[ip_test(I)]
1542 fn test_ip_reassemble_after_timer<I: TestIpExt>() {
1543 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1544 let id = 5;
1545 let key = test_key::<I>(id);
1546
1547 bindings_ctx.timers.assert_no_timers_installed();
1549 assert_eq!(core_ctx.state.cache.size, 0);
1550
1551 I::process_ip_fragment(
1555 &mut core_ctx,
1556 &mut bindings_ctx,
1557 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1558 ExpectedResult::NeedMore,
1559 );
1560
1561 core_ctx.state.cache.timers.assert_timers([(
1563 key,
1564 (),
1565 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1566 )]);
1567 validate_size(&core_ctx.state.cache);
1568
1569 I::process_ip_fragment(
1571 &mut core_ctx,
1572 &mut bindings_ctx,
1573 FragmentSpec { id, offset: 1, size: 1, m_flag: true },
1574 ExpectedResult::NeedMore,
1575 );
1576 core_ctx.state.cache.timers.assert_timers([(
1578 key,
1579 (),
1580 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1581 )]);
1582 validate_size(&core_ctx.state.cache);
1583
1584 I::process_ip_fragment(
1586 &mut core_ctx,
1587 &mut bindings_ctx,
1588 FragmentSpec { id, offset: 2, size: 1, m_flag: false },
1589 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id) },
1590 );
1591 core_ctx.state.cache.timers.assert_timers([(
1593 key,
1594 (),
1595 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1596 )]);
1597 validate_size(&core_ctx.state.cache);
1598
1599 assert_eq!(
1601 bindings_ctx.trigger_next_timer(&mut core_ctx),
1602 Some(FragmentTimerId::<I>::default())
1603 );
1604
1605 bindings_ctx.timers.assert_no_timers_installed();
1607 assert_eq!(core_ctx.state.cache.size, 0);
1608
1609 let key = test_key(id);
1612 let packet_len = 44;
1613 let mut buffer: Vec<u8> = vec![0; packet_len];
1614 let mut buffer = &mut buffer[..];
1615 assert_eq!(
1616 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1617 .unwrap_err(),
1618 FragmentReassemblyError::InvalidKey,
1619 );
1620 }
1621
1622 #[ip_test(I)]
1623 #[test_case(1)]
1624 #[test_case(10)]
1625 #[test_case(100)]
1626 fn test_ip_fragment_cache_oom<I: TestIpExt>(size: u16) {
1627 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1628 let mut id = 0;
1629 const THRESHOLD: usize = 8196usize;
1630
1631 assert_eq!(core_ctx.state.cache.size, 0);
1632 core_ctx.state.cache.threshold = THRESHOLD;
1633
1634 while core_ctx.state.cache.size + usize::from(size) <= THRESHOLD {
1637 I::process_ip_fragment(
1638 &mut core_ctx,
1639 &mut bindings_ctx,
1640 FragmentSpec { id, offset: 0, size, m_flag: true },
1641 ExpectedResult::NeedMore,
1642 );
1643 validate_size(&core_ctx.state.cache);
1644 id += 1;
1645 }
1646
1647 I::process_ip_fragment(
1649 &mut core_ctx,
1650 &mut bindings_ctx,
1651 FragmentSpec { id, offset: 0, size, m_flag: true },
1652 ExpectedResult::OutOfMemory,
1653 );
1654 validate_size(&core_ctx.state.cache);
1655
1656 let _timers = bindings_ctx
1658 .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1659 assert_eq!(core_ctx.state.cache.size, 0);
1660 validate_size(&core_ctx.state.cache);
1661
1662 I::process_ip_fragment(
1664 &mut core_ctx,
1665 &mut bindings_ctx,
1666 FragmentSpec { id, offset: 0, size, m_flag: true },
1667 ExpectedResult::NeedMore,
1668 );
1669 }
1670
1671 #[ip_test(I)]
1672 #[test_case(1)]
1673 #[test_case(10)]
1674 #[test_case(100)]
1675 fn test_unordered_fragments<I: TestIpExt>(size: u16) {
1676 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1677 let id = 5;
1678
1679 I::process_ip_fragment(
1681 &mut core_ctx,
1682 &mut bindings_ctx,
1683 FragmentSpec { id, offset: 0, size, m_flag: true },
1684 ExpectedResult::NeedMore,
1685 );
1686
1687 I::process_ip_fragment(
1689 &mut core_ctx,
1690 &mut bindings_ctx,
1691 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1692 ExpectedResult::NeedMore,
1693 );
1694
1695 I::process_ip_fragment(
1697 &mut core_ctx,
1698 &mut bindings_ctx,
1699 FragmentSpec { id, offset: size, size, m_flag: true },
1700 ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1701 );
1702 }
1703
1704 #[ip_test(I)]
1705 #[test_case(1)]
1706 #[test_case(10)]
1707 #[test_case(100)]
1708 fn test_ip_duplicate_fragment<I: TestIpExt>(size: u16) {
1709 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1710 let id = 5;
1711
1712 I::process_ip_fragment(
1714 &mut core_ctx,
1715 &mut bindings_ctx,
1716 FragmentSpec { id, offset: 0, size, m_flag: true },
1717 ExpectedResult::NeedMore,
1718 );
1719
1720 I::process_ip_fragment(
1722 &mut core_ctx,
1723 &mut bindings_ctx,
1724 FragmentSpec { id, offset: 0, size, m_flag: true },
1725 ExpectedResult::NeedMore,
1726 );
1727
1728 I::process_ip_fragment(
1731 &mut core_ctx,
1732 &mut bindings_ctx,
1733 FragmentSpec { id, offset: size, size, m_flag: false },
1734 ExpectedResult::Ready { body_fragment_blocks: 2 * size, key: test_key(id) },
1735 );
1736
1737 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id, 2 * size);
1738 }
1739
1740 #[ip_test(I)]
1741 #[test_case(1)]
1742 #[test_case(10)]
1743 #[test_case(100)]
1744 fn test_ip_out_of_bounds_fragment<I: TestIpExt>(size: u16) {
1745 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1746 let id = 5;
1747
1748 I::process_ip_fragment(
1750 &mut core_ctx,
1751 &mut bindings_ctx,
1752 FragmentSpec { id, offset: size, size, m_flag: false },
1753 ExpectedResult::NeedMore,
1754 );
1755
1756 I::process_ip_fragment(
1759 &mut core_ctx,
1760 &mut bindings_ctx,
1761 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1762 ExpectedResult::Invalid,
1763 );
1764 }
1765
1766 #[ip_test(I)]
1767 #[test_case(50, 100; "overlaps_front")]
1768 #[test_case(150, 100; "overlaps_back")]
1769 #[test_case(50, 200; "overlaps_both")]
1770 fn test_ip_overlapping_fragment<I: TestIpExt>(offset: u16, size: u16) {
1771 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1772 let id = 5;
1773
1774 I::process_ip_fragment(
1776 &mut core_ctx,
1777 &mut bindings_ctx,
1778 FragmentSpec { id, offset: 100, size: 100, m_flag: true },
1779 ExpectedResult::NeedMore,
1780 );
1781
1782 I::process_ip_fragment(
1785 &mut core_ctx,
1786 &mut bindings_ctx,
1787 FragmentSpec { id, offset, size, m_flag: true },
1788 ExpectedResult::Invalid,
1789 );
1790 }
1791
1792 #[test]
1793 fn test_ipv4_fragment_not_multiple_of_offset_unit() {
1794 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1795 let id = 0;
1796
1797 assert_eq!(core_ctx.state.cache.size, 0);
1798 process_ipv4_fragment(
1803 &mut core_ctx,
1804 &mut bindings_ctx,
1805 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1806 get_ipv4_builder(),
1807 ExpectedResult::NeedMore,
1808 );
1809
1810 let mut builder = get_ipv4_builder();
1813 builder.id(id);
1814 builder.fragment_offset(FragmentOffset::new(1).unwrap());
1815 builder.mf_flag(true);
1816 let mut body: Vec<u8> = Vec::new();
1819 body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
1820 let mut buffer = builder
1821 .wrap_body(Buf::new(body, ..))
1822 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1823 .unwrap();
1824 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1825 assert_matches!(
1826 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1827 FragmentProcessingState::InvalidFragment
1828 );
1829
1830 let mut builder = get_ipv4_builder();
1834 builder.id(id);
1835 builder.fragment_offset(FragmentOffset::new(1).unwrap());
1836 builder.mf_flag(false);
1837 let mut body: Vec<u8> = Vec::new();
1840 body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
1841 let mut buffer = builder
1842 .wrap_body(Buf::new(body, ..))
1843 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1844 .unwrap();
1845 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1846 let (key, packet_len) = assert_matches!(
1847 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1848 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1849 );
1850 assert_eq!(key, test_key(id));
1851 assert_eq!(packet_len, 35);
1852 validate_size(&core_ctx.state.cache);
1853 let mut buffer: Vec<u8> = vec![0; packet_len];
1854 let mut buffer = &mut buffer[..];
1855 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1856 .unwrap();
1857 let packet = Ipv4Packet::parse_mut(&mut buffer, ()).unwrap();
1858 let mut expected_body: Vec<u8> = Vec::new();
1859 expected_body.extend(0..15);
1860 assert_eq!(packet.body(), &expected_body[..]);
1861 assert_eq!(core_ctx.state.cache.size, 0);
1862 }
1863
1864 #[test]
1865 fn test_ipv6_fragment_not_multiple_of_offset_unit() {
1866 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1867 let id = 0;
1868
1869 assert_eq!(core_ctx.state.cache.size, 0);
1870 process_ipv6_fragment(
1875 &mut core_ctx,
1876 &mut bindings_ctx,
1877 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1878 get_ipv6_builder(),
1879 ExpectedResult::NeedMore,
1880 );
1881
1882 let offset = 1;
1885 let body_size: usize = (FRAGMENT_BLOCK_SIZE - 1).into();
1886 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1887 get_ipv6_builder(),
1888 FragmentOffset::new(offset).unwrap(),
1889 true,
1890 id.into(),
1891 );
1892 let body = generate_body_fragment(id, offset, body_size);
1893 let mut buffer = builder
1894 .wrap_body(Buf::new(body, ..))
1895 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1896 .unwrap();
1897 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1898 assert_matches!(
1899 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1900 FragmentProcessingState::InvalidFragment
1901 );
1902
1903 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1907 get_ipv6_builder(),
1908 FragmentOffset::new(offset).unwrap(),
1909 false,
1910 id.into(),
1911 );
1912 let body = generate_body_fragment(id, offset, body_size);
1913 let mut buffer = builder
1914 .wrap_body(Buf::new(body, ..))
1915 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1916 .unwrap();
1917 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1918 let (key, packet_len) = assert_matches!(
1919 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1920 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1921 );
1922 assert_eq!(key, test_key(id));
1923 assert_eq!(packet_len, 55);
1924
1925 validate_size(&core_ctx.state.cache);
1926 let mut buffer: Vec<u8> = vec![0; packet_len];
1927 let mut buffer = &mut buffer[..];
1928 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1929 .unwrap();
1930 let packet = Ipv6Packet::parse_mut(&mut buffer, ()).unwrap();
1931 let mut expected_body: Vec<u8> = Vec::new();
1932 expected_body.extend(0..15);
1933 assert_eq!(packet.body(), &expected_body[..]);
1934 assert_eq!(core_ctx.state.cache.size, 0);
1935 }
1936
1937 #[ip_test(I)]
1938 fn test_ip_reassembly_with_multiple_intertwined_packets<I: TestIpExt>() {
1939 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1940 const SIZE: u16 = 1;
1941 let id_0 = 5;
1942 let id_1 = 10;
1943
1944 I::process_ip_fragment(
1949 &mut core_ctx,
1950 &mut bindings_ctx,
1951 FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
1952 ExpectedResult::NeedMore,
1953 );
1954
1955 I::process_ip_fragment(
1957 &mut core_ctx,
1958 &mut bindings_ctx,
1959 FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
1960 ExpectedResult::NeedMore,
1961 );
1962
1963 I::process_ip_fragment(
1965 &mut core_ctx,
1966 &mut bindings_ctx,
1967 FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
1968 ExpectedResult::NeedMore,
1969 );
1970
1971 I::process_ip_fragment(
1973 &mut core_ctx,
1974 &mut bindings_ctx,
1975 FragmentSpec { id: id_1, offset: 1, size: SIZE, m_flag: true },
1976 ExpectedResult::NeedMore,
1977 );
1978
1979 I::process_ip_fragment(
1981 &mut core_ctx,
1982 &mut bindings_ctx,
1983 FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
1984 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
1985 );
1986
1987 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_0, 3);
1988
1989 I::process_ip_fragment(
1991 &mut core_ctx,
1992 &mut bindings_ctx,
1993 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
1994 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_1) },
1995 );
1996
1997 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_1, 3);
1998 }
1999
2000 #[ip_test(I)]
2001 fn test_ip_reassembly_timer_with_multiple_intertwined_packets<I: TestIpExt>() {
2002 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2003 const SIZE: u16 = 1;
2004 let id_0 = 5;
2005 let id_1 = 10;
2006 let id_2 = 15;
2007
2008 const BEFORE_TIMEOUT1: Duration = Duration::from_secs(1);
2033 const BEFORE_TIMEOUT2: Duration = Duration::from_secs(2);
2034 const BEFORE_TIMEOUT3: Duration = Duration::from_secs(3);
2035 assert!(BEFORE_TIMEOUT1 < I::REASSEMBLY_TIMEOUT);
2036 assert!(BEFORE_TIMEOUT2 < I::REASSEMBLY_TIMEOUT);
2037 assert!(BEFORE_TIMEOUT3 < I::REASSEMBLY_TIMEOUT);
2038
2039 I::process_ip_fragment(
2041 &mut core_ctx,
2042 &mut bindings_ctx,
2043 FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
2044 ExpectedResult::NeedMore,
2045 );
2046
2047 I::process_ip_fragment(
2049 &mut core_ctx,
2050 &mut bindings_ctx,
2051 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
2052 ExpectedResult::NeedMore,
2053 );
2054
2055 I::process_ip_fragment(
2057 &mut core_ctx,
2058 &mut bindings_ctx,
2059 FragmentSpec { id: id_2, offset: 2, size: SIZE, m_flag: false },
2060 ExpectedResult::NeedMore,
2061 );
2062
2063 assert_empty(
2065 bindings_ctx
2066 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT1), &mut core_ctx),
2067 );
2068
2069 I::process_ip_fragment(
2071 &mut core_ctx,
2072 &mut bindings_ctx,
2073 FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
2074 ExpectedResult::NeedMore,
2075 );
2076
2077 assert_empty(
2079 bindings_ctx
2080 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT2), &mut core_ctx),
2081 );
2082
2083 I::process_ip_fragment(
2085 &mut core_ctx,
2086 &mut bindings_ctx,
2087 FragmentSpec { id: id_2, offset: 1, size: SIZE, m_flag: true },
2088 ExpectedResult::NeedMore,
2089 );
2090
2091 I::process_ip_fragment(
2093 &mut core_ctx,
2094 &mut bindings_ctx,
2095 FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
2096 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
2097 );
2098
2099 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_0, 3);
2100
2101 assert_empty(
2103 bindings_ctx
2104 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT3), &mut core_ctx),
2105 );
2106
2107 I::process_ip_fragment(
2109 &mut core_ctx,
2110 &mut bindings_ctx,
2111 FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
2112 ExpectedResult::NeedMore,
2113 );
2114
2115 I::process_ip_fragment(
2117 &mut core_ctx,
2118 &mut bindings_ctx,
2119 FragmentSpec { id: id_2, offset: 0, size: SIZE, m_flag: true },
2120 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_2) },
2121 );
2122
2123 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_2, 3);
2124
2125 bindings_ctx.trigger_timers_until_and_expect_unordered(
2128 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
2129 [FragmentTimerId::<I>::default()],
2130 &mut core_ctx,
2131 );
2132
2133 bindings_ctx.timers.assert_no_timers_installed();
2135
2136 I::process_ip_fragment(
2140 &mut core_ctx,
2141 &mut bindings_ctx,
2142 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: true },
2143 ExpectedResult::NeedMore,
2144 );
2145 }
2146
2147 #[test]
2148 fn test_no_more_fragments_in_middle_of_block() {
2149 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2150 process_ipv4_fragment(
2151 &mut core_ctx,
2152 &mut bindings_ctx,
2153 FragmentSpec { id: 0, offset: 100, size: 1, m_flag: false },
2154 get_ipv4_builder(),
2155 ExpectedResult::NeedMore,
2156 );
2157
2158 process_ipv4_fragment(
2159 &mut core_ctx,
2160 &mut bindings_ctx,
2161 FragmentSpec { id: 0, offset: 50, size: 1, m_flag: false },
2162 get_ipv4_builder(),
2163 ExpectedResult::Invalid,
2164 );
2165 }
2166
2167 #[ip_test(I)]
2168 fn test_cancel_timer_on_overlap<I: TestIpExt>() {
2169 const FRAGMENT_ID: u16 = 1;
2170
2171 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2172
2173 let key = test_key(FRAGMENT_ID);
2174
2175 for _ in 0..=2 {
2178 I::process_ip_fragment(
2179 &mut core_ctx,
2180 &mut bindings_ctx,
2181 FragmentSpec { id: FRAGMENT_ID, offset: 0, size: 10, m_flag: true },
2182 ExpectedResult::NeedMore,
2183 );
2184 core_ctx
2185 .state
2186 .cache
2187 .timers
2188 .assert_timers_after(&mut bindings_ctx, [(key, (), I::REASSEMBLY_TIMEOUT)]);
2189
2190 I::process_ip_fragment(
2191 &mut core_ctx,
2192 &mut bindings_ctx,
2193 FragmentSpec { id: FRAGMENT_ID, offset: 5, size: 10, m_flag: true },
2194 ExpectedResult::Invalid,
2195 );
2196 assert_eq!(bindings_ctx.timers.timers(), [],);
2197 }
2198 }
2199}