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 if !m_flag && found_gap.end < u16::MAX {
681 return (FragmentProcessingState::InvalidFragment, timer_id);
685 }
686
687 assert!(fragment_data.missing_blocks.remove(&found_gap));
690
691 if found_gap.start < fragment_blocks_range.start {
708 assert!(fragment_data.missing_blocks.insert(BlockRange {
709 start: found_gap.start,
710 end: fragment_blocks_range.start - 1
711 }));
712 }
713
714 if found_gap.end > fragment_blocks_range.end && m_flag {
749 assert!(
750 fragment_data.missing_blocks.insert(BlockRange {
751 start: fragment_blocks_range.end + 1,
752 end: found_gap.end
753 })
754 );
755 } else {
756 assert!(
762 found_gap.end == fragment_blocks_range.end
763 || (!m_flag && found_gap.end == u16::MAX),
764 "found_gap: {:?}, fragment_blocks_range: {:?} offset: {:?}, m_flag: {:?}",
765 found_gap,
766 fragment_blocks_range,
767 offset,
768 m_flag
769 );
770 }
771
772 let mut added_bytes = 0;
773 if offset == 0 {
775 assert_eq!(fragment_data.header, None);
776 let header = get_header::<B, I>(&packet);
777 added_bytes = header.len();
778 fragment_data.header = Some(header);
779 }
780
781 let mut body = Vec::with_capacity(packet.body().len());
783 body.extend_from_slice(packet.body());
784 added_bytes += body.len();
785 fragment_data.total_size += added_bytes;
786 fragment_data.body_fragments.push(PacketBodyFragment::new(offset, body));
787
788 let result = if fragment_data.missing_blocks.is_empty() {
794 FragmentProcessingState::Ready { key, packet_len: fragment_data.total_size }
795 } else {
796 FragmentProcessingState::NeedMoreFragments
797 };
798
799 self.increment_size(added_bytes);
800 (result, timer_id)
801 }
802
803 fn reassemble_packet<B: SplitByteSliceMut, BV: BufferViewMut<B>>(
821 &mut self,
822 key: &FragmentCacheKey<I>,
823 buffer: BV,
824 ) -> Result<(), FragmentReassemblyError> {
825 let entry = match self.cache.entry(*key) {
826 Entry::Occupied(entry) => entry,
827 Entry::Vacant(_) => return Err(FragmentReassemblyError::InvalidKey),
828 };
829
830 if !entry.get().missing_blocks.is_empty() {
832 return Err(FragmentReassemblyError::MissingFragments);
833 }
834 let (_key, data) = entry.remove_entry();
837 self.size -= data.total_size;
838
839 assert_matches!(data.header, Some(_));
841
842 let body_fragments = data.body_fragments.into_sorted_vec().into_iter().map(|x| x.data);
845 I::Packet::reassemble_fragmented_packet(buffer, data.header.unwrap(), body_fragments)
846 .map_err(|_| FragmentReassemblyError::PacketParsingError)
847 }
848
849 fn get_or_create(&mut self, key: FragmentCacheKey<I>) -> (&mut FragmentCacheData, bool) {
854 match self.cache.entry(key) {
855 Entry::Occupied(e) => (e.into_mut(), false),
856 Entry::Vacant(e) => {
857 (e.insert(FragmentCacheData::default()), true)
862 }
863 }
864 }
865
866 fn above_size_threshold(&self) -> bool {
867 self.size >= self.threshold
868 }
869
870 fn increment_size(&mut self, sz: usize) {
871 assert!(!self.above_size_threshold());
872 self.size += sz;
873 }
874
875 fn remove_data(&mut self, key: &FragmentCacheKey<I>) -> Option<FragmentCacheData> {
876 let data = self.cache.remove(key)?;
877 self.size -= data.total_size;
878 Some(data)
879 }
880}
881
882fn get_header<B: SplitByteSlice, I: IpExt>(packet: &I::Packet<B>) -> Vec<u8> {
884 match packet.as_ip_addr_ref() {
885 IpAddr::V4(packet) => packet.copy_header_bytes_for_fragment(),
886 IpAddr::V6(packet) => {
887 packet.copy_header_bytes_for_fragment()
892 }
893 }
894}
895
896#[derive(Debug, PartialEq, Eq)]
898struct PacketBodyFragment {
899 offset: u16,
900 data: Vec<u8>,
901}
902
903impl PacketBodyFragment {
904 fn new(offset: u16, data: Vec<u8>) -> Self {
906 PacketBodyFragment { offset, data }
907 }
908}
909
910impl PartialOrd for PacketBodyFragment {
913 fn partial_cmp(&self, other: &PacketBodyFragment) -> Option<Ordering> {
914 Some(self.cmp(other))
915 }
916}
917
918impl Ord for PacketBodyFragment {
919 fn cmp(&self, other: &Self) -> Ordering {
920 self.offset.cmp(&other.offset)
921 }
922}
923
924#[cfg(test)]
925mod tests {
926 use alloc::vec;
927
928 use assert_matches::assert_matches;
929 use ip_test_macro::ip_test;
930 use net_declare::{net_ip_v4, net_ip_v6};
931 use net_types::Witness;
932 use net_types::ip::{Ipv4, Ipv4Addr, Ipv6, Ipv6Addr};
933 use netstack3_base::testutil::{
934 FakeBindingsCtx, FakeCoreCtx, FakeInstant, FakeTimerCtxExt, TEST_ADDRS_V4, TEST_ADDRS_V6,
935 assert_empty,
936 };
937 use netstack3_base::{CtxPair, IntoCoreTimerCtx, NetworkSerializationContext};
938 use packet::{Buf, NestablePacketBuilder as _, ParsablePacket, ParseBuffer, Serializer};
939 use packet_formats::ip::{FragmentOffset, IpProto, Ipv6Proto};
940 use packet_formats::ipv4::Ipv4PacketBuilder;
941 use packet_formats::ipv6::{Ipv6PacketBuilder, Ipv6PacketBuilderWithFragmentHeader};
942 use test_case::test_case;
943
944 use super::*;
945
946 struct FakeFragmentContext<I: ReassemblyIpExt, BT: FragmentBindingsTypes> {
947 cache: IpPacketFragmentCache<I, BT>,
948 }
949
950 impl<I: ReassemblyIpExt, BC: FragmentBindingsContext> FakeFragmentContext<I, BC>
951 where
952 BC::DispatchId: From<FragmentTimerId<I>>,
953 {
954 fn new(bindings_ctx: &mut BC) -> Self {
955 Self { cache: IpPacketFragmentCache::new::<IntoCoreTimerCtx>(bindings_ctx) }
956 }
957 }
958
959 type FakeCtxImpl<I> = CtxPair<FakeCoreCtxImpl<I>, FakeBindingsCtxImpl<I>>;
960 type FakeBindingsCtxImpl<I> = FakeBindingsCtx<FragmentTimerId<I>, (), (), ()>;
961 type FakeCoreCtxImpl<I> = FakeCoreCtx<FakeFragmentContext<I, FakeBindingsCtxImpl<I>>, (), ()>;
962
963 impl<I: ReassemblyIpExt> FragmentContext<I, FakeBindingsCtxImpl<I>> for FakeCoreCtxImpl<I> {
964 fn with_state_mut<
965 O,
966 F: FnOnce(&mut IpPacketFragmentCache<I, FakeBindingsCtxImpl<I>>) -> O,
967 >(
968 &mut self,
969 cb: F,
970 ) -> O {
971 cb(&mut self.state.cache)
972 }
973 }
974
975 #[derive(PartialEq)]
978 enum ExpectedResult<I: ReassemblyIpExt> {
979 Ready { body_fragment_blocks: u16, key: FragmentCacheKey<I> },
984
985 NeedMore,
988
989 Invalid,
991
992 OutOfMemory,
994 }
995
996 fn get_ipv4_builder() -> Ipv4PacketBuilder {
998 Ipv4PacketBuilder::new(
999 TEST_ADDRS_V4.remote_ip,
1000 TEST_ADDRS_V4.local_ip,
1001 10,
1002 <Ipv4 as TestIpExt>::PROTOCOL,
1003 )
1004 }
1005
1006 fn get_ipv6_builder() -> Ipv6PacketBuilder {
1008 Ipv6PacketBuilder::new(
1009 TEST_ADDRS_V6.remote_ip,
1010 TEST_ADDRS_V6.local_ip,
1011 10,
1012 <Ipv6 as TestIpExt>::PROTOCOL,
1013 )
1014 }
1015
1016 fn validate_size<I: ReassemblyIpExt, BT: FragmentBindingsTypes>(
1018 cache: &IpPacketFragmentCache<I, BT>,
1019 ) {
1020 let mut sz: usize = 0;
1021
1022 for v in cache.cache.values() {
1023 sz += v.total_size;
1024 }
1025
1026 assert_eq!(sz, cache.size);
1027 }
1028
1029 struct FragmentSpec {
1030 id: u16,
1032 offset: u16,
1034 size: u16,
1036 m_flag: bool,
1038 }
1039
1040 fn expected_packet_size<I: TestIpExt>(num_fragment_blocks: u16) -> usize {
1041 usize::from(num_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE) + I::HEADER_LENGTH
1042 }
1043
1044 fn process_ipv4_fragment<CC: FragmentContext<Ipv4, BC>, BC: FragmentBindingsContext>(
1046 core_ctx: &mut CC,
1047 bindings_ctx: &mut BC,
1048 FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1049 mut builder: Ipv4PacketBuilder,
1050 expected_result: ExpectedResult<Ipv4>,
1051 ) {
1052 builder.id(id);
1053 builder.fragment_offset(FragmentOffset::new(offset).unwrap());
1054 builder.mf_flag(m_flag);
1055 let body = generate_body_fragment(
1056 id,
1057 offset,
1058 usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1059 );
1060
1061 let mut buffer = builder
1062 .wrap_body(Buf::new(body, ..))
1063 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1064 .unwrap();
1065 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1066
1067 let actual_result =
1068 FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1069 match expected_result {
1070 ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1071 let (key, packet_len) = assert_matches!(
1072 actual_result,
1073 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1074 );
1075 assert_eq!(key, expected_key);
1076 assert_eq!(packet_len, expected_packet_size::<Ipv4>(body_fragment_blocks));
1077 }
1078 ExpectedResult::NeedMore => {
1079 assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1080 }
1081 ExpectedResult::Invalid => {
1082 assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1083 }
1084 ExpectedResult::OutOfMemory => {
1085 assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1086 }
1087 }
1088 }
1089
1090 fn process_ipv6_fragment<CC: FragmentContext<Ipv6, BC>, BC: FragmentBindingsContext>(
1094 core_ctx: &mut CC,
1095 bindings_ctx: &mut BC,
1096 FragmentSpec { id, offset, size, m_flag }: FragmentSpec,
1097 builder: Ipv6PacketBuilder,
1098 expected_result: ExpectedResult<Ipv6>,
1099 ) {
1100 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1101 builder,
1102 FragmentOffset::new(offset).unwrap(),
1103 m_flag,
1104 id.into(),
1105 );
1106
1107 let body = generate_body_fragment(
1108 id,
1109 offset,
1110 usize::from(size) * usize::from(FRAGMENT_BLOCK_SIZE),
1111 );
1112
1113 let mut buffer = builder
1114 .wrap_body(Buf::new(body, ..))
1115 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1116 .unwrap();
1117 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1118
1119 let actual_result =
1120 FragmentHandler::process_fragment::<&[u8]>(core_ctx, bindings_ctx, packet);
1121 match expected_result {
1122 ExpectedResult::Ready { body_fragment_blocks, key: expected_key } => {
1123 let (key, packet_len) = assert_matches!(
1124 actual_result,
1125 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1126 );
1127 assert_eq!(key, expected_key);
1128 assert_eq!(packet_len, expected_packet_size::<Ipv6>(body_fragment_blocks));
1129 }
1130 ExpectedResult::NeedMore => {
1131 assert_matches!(actual_result, FragmentProcessingState::NeedMoreFragments);
1132 }
1133 ExpectedResult::Invalid => {
1134 assert_matches!(actual_result, FragmentProcessingState::InvalidFragment);
1135 }
1136 ExpectedResult::OutOfMemory => {
1137 assert_matches!(actual_result, FragmentProcessingState::OutOfMemory);
1138 }
1139 }
1140 }
1141
1142 trait TestIpExt: IpExt + netstack3_base::testutil::TestIpExt + ReassemblyIpExt {
1143 const HEADER_LENGTH: usize;
1144
1145 const PROTOCOL: Self::Proto;
1146
1147 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1148 core_ctx: &mut CC,
1149 bindings_ctx: &mut BC,
1150 spec: FragmentSpec,
1151 expected_result: ExpectedResult<Self>,
1152 );
1153 }
1154
1155 impl TestIpExt for Ipv4 {
1156 const HEADER_LENGTH: usize = packet_formats::ipv4::HDR_PREFIX_LEN;
1157
1158 const PROTOCOL: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1159
1160 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1161 core_ctx: &mut CC,
1162 bindings_ctx: &mut BC,
1163 spec: FragmentSpec,
1164 expected_result: ExpectedResult<Ipv4>,
1165 ) {
1166 process_ipv4_fragment(core_ctx, bindings_ctx, spec, get_ipv4_builder(), expected_result)
1167 }
1168 }
1169 impl TestIpExt for Ipv6 {
1170 const HEADER_LENGTH: usize = packet_formats::ipv6::IPV6_FIXED_HDR_LEN;
1171
1172 const PROTOCOL: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1173
1174 fn process_ip_fragment<CC: FragmentContext<Self, BC>, BC: FragmentBindingsContext>(
1175 core_ctx: &mut CC,
1176 bindings_ctx: &mut BC,
1177 spec: FragmentSpec,
1178 expected_result: ExpectedResult<Ipv6>,
1179 ) {
1180 process_ipv6_fragment(core_ctx, bindings_ctx, spec, get_ipv6_builder(), expected_result)
1181 }
1182 }
1183
1184 fn try_reassemble_ip_packet<
1188 I: TestIpExt + netstack3_base::IpExt,
1189 CC: FragmentContext<I, BC>,
1190 BC: FragmentBindingsContext,
1191 >(
1192 core_ctx: &mut CC,
1193 bindings_ctx: &mut BC,
1194 fragment_id: u16,
1195 body_fragment_blocks: u16,
1196 ) {
1197 let mut buffer: Vec<u8> = vec![
1198 0;
1199 usize::from(body_fragment_blocks)
1200 * usize::from(FRAGMENT_BLOCK_SIZE)
1201 + I::HEADER_LENGTH
1202 ];
1203 let mut buffer = &mut buffer[..];
1204 let key = test_key(fragment_id);
1205
1206 FragmentHandler::reassemble_packet(core_ctx, bindings_ctx, &key, &mut buffer).unwrap();
1207 let packet = I::Packet::parse_mut(&mut buffer, ()).unwrap();
1208
1209 let expected_body = generate_body_fragment(
1210 fragment_id,
1211 0,
1212 usize::from(body_fragment_blocks) * usize::from(FRAGMENT_BLOCK_SIZE),
1213 );
1214 assert_eq!(packet.body(), &expected_body[..]);
1215 }
1216
1217 fn generate_body_fragment(fragment_id: u16, fragment_offset: u16, len: usize) -> Vec<u8> {
1223 let start = usize::from(fragment_id)
1227 + usize::from(fragment_offset) * usize::from(FRAGMENT_BLOCK_SIZE);
1228 (start..start + len).map(|byte| byte as u8).collect()
1229 }
1230
1231 fn test_key<I: TestIpExt>(id: u16) -> FragmentCacheKey<I> {
1233 #[derive(GenericOverIp)]
1234 #[generic_over_ip(I, Ip)]
1235 struct Wrapper<I: ReassemblyIpExt>(I::FragmentCacheKeyPart);
1236
1237 let Wrapper(ip_specific_fields) =
1238 I::map_ip_out((), |()| Wrapper(Ipv4::PROTOCOL), |()| Wrapper(()));
1239
1240 FragmentCacheKey {
1241 src_ip: I::TEST_ADDRS.remote_ip.get(),
1242 dst_ip: I::TEST_ADDRS.local_ip.get(),
1243 fragment_id: id.into(),
1244 ip_specific_fields,
1245 }
1246 }
1247
1248 fn new_context<I: ReassemblyIpExt>() -> FakeCtxImpl<I> {
1249 FakeCtxImpl::<I>::with_default_bindings_ctx(|bindings_ctx| {
1250 FakeCoreCtxImpl::with_state(FakeFragmentContext::new(bindings_ctx))
1251 })
1252 }
1253
1254 #[test]
1255 fn test_ipv4_reassembly_not_needed() {
1256 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1257
1258 let builder = get_ipv4_builder();
1262 let body = [1, 2, 3, 4, 5];
1263 let mut buffer = builder
1264 .wrap_body(Buf::new(body.to_vec(), ..))
1265 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1266 .unwrap();
1267 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1268 assert_matches!(
1269 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1270 FragmentProcessingState::NotNeeded(unfragmented) if unfragmented.body() == body
1271 );
1272 }
1273
1274 #[test]
1275 #[should_panic(
1276 expected = "internal error: entered unreachable code: Should never call this function if the packet does not have a fragment header"
1277 )]
1278 fn test_ipv6_reassembly_not_needed() {
1279 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1280
1281 let builder = get_ipv6_builder();
1285 let mut buffer = builder
1286 .wrap_body(Buf::new(vec![1, 2, 3, 4, 5], ..))
1287 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1288 .unwrap();
1289 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1290 assert_matches!(
1291 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1292 FragmentProcessingState::InvalidFragment
1293 );
1294 }
1295
1296 #[ip_test(I)]
1297 #[test_case(1)]
1298 #[test_case(10)]
1299 #[test_case(100)]
1300 fn test_ip_reassembly<I: TestIpExt>(size: u16) {
1301 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1302 let id = 5;
1303
1304 I::process_ip_fragment(
1308 &mut core_ctx,
1309 &mut bindings_ctx,
1310 FragmentSpec { id, offset: 0, size, m_flag: true },
1311 ExpectedResult::NeedMore,
1312 );
1313
1314 I::process_ip_fragment(
1316 &mut core_ctx,
1317 &mut bindings_ctx,
1318 FragmentSpec { id, offset: size, size, m_flag: true },
1319 ExpectedResult::NeedMore,
1320 );
1321
1322 I::process_ip_fragment(
1324 &mut core_ctx,
1325 &mut bindings_ctx,
1326 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1327 ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1328 );
1329
1330 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id, 3 * size);
1331 }
1332
1333 #[test]
1334 fn test_ipv4_key_uniqueness() {
1335 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1336
1337 const RIGHT_SRC: Ipv4Addr = net_ip_v4!("192.0.2.1");
1338 const WRONG_SRC: Ipv4Addr = net_ip_v4!("192.0.2.2");
1339
1340 const RIGHT_DST: Ipv4Addr = net_ip_v4!("192.0.2.3");
1341 const WRONG_DST: Ipv4Addr = net_ip_v4!("192.0.2.4");
1342
1343 const RIGHT_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Tcp);
1344 const WRONG_PROTO: Ipv4Proto = Ipv4Proto::Proto(IpProto::Udp);
1345
1346 const RIGHT_ID: u16 = 1;
1347 const WRONG_ID: u16 = 2;
1348
1349 const TTL: u8 = 1;
1350
1351 process_ipv4_fragment(
1353 &mut core_ctx,
1354 &mut bindings_ctx,
1355 FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1356 Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1357 ExpectedResult::NeedMore,
1358 );
1359
1360 for (id, src, dst, proto) in [
1363 (RIGHT_ID, RIGHT_SRC, RIGHT_DST, WRONG_PROTO),
1364 (RIGHT_ID, RIGHT_SRC, WRONG_DST, RIGHT_PROTO),
1365 (RIGHT_ID, WRONG_SRC, RIGHT_DST, RIGHT_PROTO),
1366 (WRONG_ID, RIGHT_SRC, RIGHT_DST, RIGHT_PROTO),
1367 ] {
1368 process_ipv4_fragment(
1369 &mut core_ctx,
1370 &mut bindings_ctx,
1371 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1372 Ipv4PacketBuilder::new(src, dst, TTL, proto),
1373 ExpectedResult::NeedMore,
1374 );
1375 }
1376
1377 const KEY: FragmentCacheKey<Ipv4> = FragmentCacheKey {
1380 src_ip: RIGHT_SRC,
1381 dst_ip: RIGHT_DST,
1382 fragment_id: RIGHT_ID as u32,
1383 ip_specific_fields: RIGHT_PROTO,
1384 };
1385 process_ipv4_fragment(
1386 &mut core_ctx,
1387 &mut bindings_ctx,
1388 FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1389 Ipv4PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, RIGHT_PROTO),
1390 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1391 );
1392 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv4>(2)];
1393 let mut buffer = &mut buffer[..];
1394 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1395 .expect("reassembly should succeed");
1396 let _packet = Ipv4Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1397 }
1398
1399 #[test]
1400 fn test_ipv6_key_uniqueness() {
1401 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1402
1403 const RIGHT_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1404 const WRONG_SRC: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1405
1406 const RIGHT_DST: Ipv6Addr = net_ip_v6!("2001:0db8::3");
1407 const WRONG_DST: Ipv6Addr = net_ip_v6!("2001:0db8::4");
1408
1409 const RIGHT_ID: u16 = 1;
1410 const WRONG_ID: u16 = 2;
1411
1412 const TTL: u8 = 1;
1413
1414 process_ipv6_fragment(
1416 &mut core_ctx,
1417 &mut bindings_ctx,
1418 FragmentSpec { id: RIGHT_ID, offset: 0, size: 1, m_flag: true },
1419 Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1420 ExpectedResult::NeedMore,
1421 );
1422
1423 for (id, src, dst) in [
1426 (RIGHT_ID, RIGHT_SRC, WRONG_DST),
1427 (RIGHT_ID, WRONG_SRC, RIGHT_DST),
1428 (WRONG_ID, RIGHT_SRC, RIGHT_DST),
1429 ] {
1430 process_ipv6_fragment(
1431 &mut core_ctx,
1432 &mut bindings_ctx,
1433 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
1434 Ipv6PacketBuilder::new(src, dst, TTL, Ipv6::PROTOCOL),
1435 ExpectedResult::NeedMore,
1436 );
1437 }
1438
1439 const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1442 src_ip: RIGHT_SRC,
1443 dst_ip: RIGHT_DST,
1444 fragment_id: RIGHT_ID as u32,
1445 ip_specific_fields: (),
1446 };
1447 process_ipv6_fragment(
1448 &mut core_ctx,
1449 &mut bindings_ctx,
1450 FragmentSpec { id: RIGHT_ID, offset: 1, size: 1, m_flag: false },
1451 Ipv6PacketBuilder::new(RIGHT_SRC, RIGHT_DST, TTL, Ipv6::PROTOCOL),
1452 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1453 );
1454 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1455 let mut buffer = &mut buffer[..];
1456 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1457 .expect("reassembly should succeed");
1458 let _packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1459 }
1460
1461 #[test]
1462 fn test_ipv6_reassemble_different_protocols() {
1463 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1464
1465 const SRC: Ipv6Addr = net_ip_v6!("2001:0db8::1");
1466 const DST: Ipv6Addr = net_ip_v6!("2001:0db8::2");
1467 const ID: u16 = 1;
1468 const TTL: u8 = 1;
1469
1470 const PROTO1: Ipv6Proto = Ipv6Proto::Proto(IpProto::Tcp);
1471 const PROTO2: Ipv6Proto = Ipv6Proto::Proto(IpProto::Udp);
1472
1473 process_ipv6_fragment(
1475 &mut core_ctx,
1476 &mut bindings_ctx,
1477 FragmentSpec { id: ID, offset: 0, size: 1, m_flag: true },
1478 Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO1),
1479 ExpectedResult::NeedMore,
1480 );
1481
1482 const KEY: FragmentCacheKey<Ipv6> = FragmentCacheKey {
1486 src_ip: SRC,
1487 dst_ip: DST,
1488 fragment_id: ID as u32,
1489 ip_specific_fields: (),
1490 };
1491 process_ipv6_fragment(
1492 &mut core_ctx,
1493 &mut bindings_ctx,
1494 FragmentSpec { id: ID, offset: 1, size: 1, m_flag: false },
1495 Ipv6PacketBuilder::new(SRC, DST, TTL, PROTO2),
1496 ExpectedResult::Ready { body_fragment_blocks: 2, key: KEY },
1497 );
1498 let mut buffer: Vec<u8> = vec![0; expected_packet_size::<Ipv6>(2)];
1499 let mut buffer = &mut buffer[..];
1500 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &KEY, &mut buffer)
1501 .expect("reassembly should succeed");
1502 let packet = Ipv6Packet::parse_mut(&mut buffer, ()).expect("parse should succeed");
1503 assert_eq!(packet.proto(), PROTO1);
1504 }
1505
1506 #[ip_test(I)]
1507 #[test_case(1)]
1508 #[test_case(10)]
1509 #[test_case(100)]
1510 fn test_ip_reassemble_with_missing_blocks<I: TestIpExt>(size: u16) {
1511 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1512 let id = 5;
1513
1514 I::process_ip_fragment(
1519 &mut core_ctx,
1520 &mut bindings_ctx,
1521 FragmentSpec { id, offset: 0, size, m_flag: true },
1522 ExpectedResult::NeedMore,
1523 );
1524
1525 I::process_ip_fragment(
1527 &mut core_ctx,
1528 &mut bindings_ctx,
1529 FragmentSpec { id, offset: size, size, m_flag: true },
1530 ExpectedResult::NeedMore,
1531 );
1532
1533 let mut buffer: Vec<u8> = vec![0; 1];
1534 let mut buffer = &mut buffer[..];
1535 let key = test_key(id);
1536 assert_eq!(
1537 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1538 .unwrap_err(),
1539 FragmentReassemblyError::MissingFragments,
1540 );
1541 }
1542
1543 #[ip_test(I)]
1544 fn test_ip_reassemble_after_timer<I: TestIpExt>() {
1545 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1546 let id = 5;
1547 let key = test_key::<I>(id);
1548
1549 bindings_ctx.timers.assert_no_timers_installed();
1551 assert_eq!(core_ctx.state.cache.size, 0);
1552
1553 I::process_ip_fragment(
1557 &mut core_ctx,
1558 &mut bindings_ctx,
1559 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1560 ExpectedResult::NeedMore,
1561 );
1562
1563 core_ctx.state.cache.timers.assert_timers([(
1565 key,
1566 (),
1567 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1568 )]);
1569 validate_size(&core_ctx.state.cache);
1570
1571 I::process_ip_fragment(
1573 &mut core_ctx,
1574 &mut bindings_ctx,
1575 FragmentSpec { id, offset: 1, size: 1, m_flag: true },
1576 ExpectedResult::NeedMore,
1577 );
1578 core_ctx.state.cache.timers.assert_timers([(
1580 key,
1581 (),
1582 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1583 )]);
1584 validate_size(&core_ctx.state.cache);
1585
1586 I::process_ip_fragment(
1588 &mut core_ctx,
1589 &mut bindings_ctx,
1590 FragmentSpec { id, offset: 2, size: 1, m_flag: false },
1591 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id) },
1592 );
1593 core_ctx.state.cache.timers.assert_timers([(
1595 key,
1596 (),
1597 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
1598 )]);
1599 validate_size(&core_ctx.state.cache);
1600
1601 assert_eq!(
1603 bindings_ctx.trigger_next_timer(&mut core_ctx),
1604 Some(FragmentTimerId::<I>::default())
1605 );
1606
1607 bindings_ctx.timers.assert_no_timers_installed();
1609 assert_eq!(core_ctx.state.cache.size, 0);
1610
1611 let key = test_key(id);
1614 let packet_len = 44;
1615 let mut buffer: Vec<u8> = vec![0; packet_len];
1616 let mut buffer = &mut buffer[..];
1617 assert_eq!(
1618 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1619 .unwrap_err(),
1620 FragmentReassemblyError::InvalidKey,
1621 );
1622 }
1623
1624 #[ip_test(I)]
1625 #[test_case(1)]
1626 #[test_case(10)]
1627 #[test_case(100)]
1628 fn test_ip_fragment_cache_oom<I: TestIpExt>(size: u16) {
1629 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1630 let mut id = 0;
1631 const THRESHOLD: usize = 8196usize;
1632
1633 assert_eq!(core_ctx.state.cache.size, 0);
1634 core_ctx.state.cache.threshold = THRESHOLD;
1635
1636 while core_ctx.state.cache.size + usize::from(size) <= THRESHOLD {
1639 I::process_ip_fragment(
1640 &mut core_ctx,
1641 &mut bindings_ctx,
1642 FragmentSpec { id, offset: 0, size, m_flag: true },
1643 ExpectedResult::NeedMore,
1644 );
1645 validate_size(&core_ctx.state.cache);
1646 id += 1;
1647 }
1648
1649 I::process_ip_fragment(
1651 &mut core_ctx,
1652 &mut bindings_ctx,
1653 FragmentSpec { id, offset: 0, size, m_flag: true },
1654 ExpectedResult::OutOfMemory,
1655 );
1656 validate_size(&core_ctx.state.cache);
1657
1658 let _timers = bindings_ctx
1660 .trigger_timers_for(I::REASSEMBLY_TIMEOUT + Duration::from_secs(1), &mut core_ctx);
1661 assert_eq!(core_ctx.state.cache.size, 0);
1662 validate_size(&core_ctx.state.cache);
1663
1664 I::process_ip_fragment(
1666 &mut core_ctx,
1667 &mut bindings_ctx,
1668 FragmentSpec { id, offset: 0, size, m_flag: true },
1669 ExpectedResult::NeedMore,
1670 );
1671 }
1672
1673 #[ip_test(I)]
1674 #[test_case(1)]
1675 #[test_case(10)]
1676 #[test_case(100)]
1677 fn test_unordered_fragments<I: TestIpExt>(size: u16) {
1678 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1679 let id = 5;
1680
1681 I::process_ip_fragment(
1683 &mut core_ctx,
1684 &mut bindings_ctx,
1685 FragmentSpec { id, offset: 0, size, m_flag: true },
1686 ExpectedResult::NeedMore,
1687 );
1688
1689 I::process_ip_fragment(
1691 &mut core_ctx,
1692 &mut bindings_ctx,
1693 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1694 ExpectedResult::NeedMore,
1695 );
1696
1697 I::process_ip_fragment(
1699 &mut core_ctx,
1700 &mut bindings_ctx,
1701 FragmentSpec { id, offset: size, size, m_flag: true },
1702 ExpectedResult::Ready { body_fragment_blocks: 3 * size, key: test_key(id) },
1703 );
1704 }
1705
1706 #[ip_test(I)]
1707 #[test_case(1)]
1708 #[test_case(10)]
1709 #[test_case(100)]
1710 fn test_ip_duplicate_fragment<I: TestIpExt>(size: u16) {
1711 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1712 let id = 5;
1713
1714 I::process_ip_fragment(
1716 &mut core_ctx,
1717 &mut bindings_ctx,
1718 FragmentSpec { id, offset: 0, size, m_flag: true },
1719 ExpectedResult::NeedMore,
1720 );
1721
1722 I::process_ip_fragment(
1724 &mut core_ctx,
1725 &mut bindings_ctx,
1726 FragmentSpec { id, offset: 0, size, m_flag: true },
1727 ExpectedResult::NeedMore,
1728 );
1729
1730 I::process_ip_fragment(
1733 &mut core_ctx,
1734 &mut bindings_ctx,
1735 FragmentSpec { id, offset: size, size, m_flag: false },
1736 ExpectedResult::Ready { body_fragment_blocks: 2 * size, key: test_key(id) },
1737 );
1738
1739 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id, 2 * size);
1740 }
1741
1742 #[ip_test(I)]
1743 #[test_case(1)]
1744 #[test_case(10)]
1745 #[test_case(100)]
1746 fn test_ip_out_of_bounds_fragment<I: TestIpExt>(size: u16) {
1747 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1748 let id = 5;
1749
1750 I::process_ip_fragment(
1752 &mut core_ctx,
1753 &mut bindings_ctx,
1754 FragmentSpec { id, offset: size, size, m_flag: false },
1755 ExpectedResult::NeedMore,
1756 );
1757
1758 I::process_ip_fragment(
1761 &mut core_ctx,
1762 &mut bindings_ctx,
1763 FragmentSpec { id, offset: 2 * size, size, m_flag: false },
1764 ExpectedResult::Invalid,
1765 );
1766 }
1767
1768 #[ip_test(I)]
1769 #[test_case(50, 100; "overlaps_front")]
1770 #[test_case(150, 100; "overlaps_back")]
1771 #[test_case(50, 200; "overlaps_both")]
1772 fn test_ip_overlapping_fragment<I: TestIpExt>(offset: u16, size: u16) {
1773 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1774 let id = 5;
1775
1776 I::process_ip_fragment(
1778 &mut core_ctx,
1779 &mut bindings_ctx,
1780 FragmentSpec { id, offset: 100, size: 100, m_flag: true },
1781 ExpectedResult::NeedMore,
1782 );
1783
1784 I::process_ip_fragment(
1787 &mut core_ctx,
1788 &mut bindings_ctx,
1789 FragmentSpec { id, offset, size, m_flag: true },
1790 ExpectedResult::Invalid,
1791 );
1792 }
1793
1794 #[test]
1795 fn test_ipv4_fragment_not_multiple_of_offset_unit() {
1796 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
1797 let id = 0;
1798
1799 assert_eq!(core_ctx.state.cache.size, 0);
1800 process_ipv4_fragment(
1805 &mut core_ctx,
1806 &mut bindings_ctx,
1807 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1808 get_ipv4_builder(),
1809 ExpectedResult::NeedMore,
1810 );
1811
1812 let mut builder = get_ipv4_builder();
1815 builder.id(id);
1816 builder.fragment_offset(FragmentOffset::new(1).unwrap());
1817 builder.mf_flag(true);
1818 let mut body: Vec<u8> = Vec::new();
1821 body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
1822 let mut buffer = builder
1823 .wrap_body(Buf::new(body, ..))
1824 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1825 .unwrap();
1826 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1827 assert_matches!(
1828 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1829 FragmentProcessingState::InvalidFragment
1830 );
1831
1832 let mut builder = get_ipv4_builder();
1836 builder.id(id);
1837 builder.fragment_offset(FragmentOffset::new(1).unwrap());
1838 builder.mf_flag(false);
1839 let mut body: Vec<u8> = Vec::new();
1842 body.extend(FRAGMENT_BLOCK_SIZE..FRAGMENT_BLOCK_SIZE * 2 - 1);
1843 let mut buffer = builder
1844 .wrap_body(Buf::new(body, ..))
1845 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1846 .unwrap();
1847 let packet = buffer.parse::<Ipv4Packet<_>>().unwrap();
1848 let (key, packet_len) = assert_matches!(
1849 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1850 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1851 );
1852 assert_eq!(key, test_key(id));
1853 assert_eq!(packet_len, 35);
1854 validate_size(&core_ctx.state.cache);
1855 let mut buffer: Vec<u8> = vec![0; packet_len];
1856 let mut buffer = &mut buffer[..];
1857 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1858 .unwrap();
1859 let packet = Ipv4Packet::parse_mut(&mut buffer, ()).unwrap();
1860 let mut expected_body: Vec<u8> = Vec::new();
1861 expected_body.extend(0..15);
1862 assert_eq!(packet.body(), &expected_body[..]);
1863 assert_eq!(core_ctx.state.cache.size, 0);
1864 }
1865
1866 #[test]
1867 fn test_ipv6_fragment_not_multiple_of_offset_unit() {
1868 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv6>();
1869 let id = 0;
1870
1871 assert_eq!(core_ctx.state.cache.size, 0);
1872 process_ipv6_fragment(
1877 &mut core_ctx,
1878 &mut bindings_ctx,
1879 FragmentSpec { id, offset: 0, size: 1, m_flag: true },
1880 get_ipv6_builder(),
1881 ExpectedResult::NeedMore,
1882 );
1883
1884 let offset = 1;
1887 let body_size: usize = (FRAGMENT_BLOCK_SIZE - 1).into();
1888 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1889 get_ipv6_builder(),
1890 FragmentOffset::new(offset).unwrap(),
1891 true,
1892 id.into(),
1893 );
1894 let body = generate_body_fragment(id, offset, body_size);
1895 let mut buffer = builder
1896 .wrap_body(Buf::new(body, ..))
1897 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1898 .unwrap();
1899 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1900 assert_matches!(
1901 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1902 FragmentProcessingState::InvalidFragment
1903 );
1904
1905 let builder = Ipv6PacketBuilderWithFragmentHeader::new(
1909 get_ipv6_builder(),
1910 FragmentOffset::new(offset).unwrap(),
1911 false,
1912 id.into(),
1913 );
1914 let body = generate_body_fragment(id, offset, body_size);
1915 let mut buffer = builder
1916 .wrap_body(Buf::new(body, ..))
1917 .serialize_vec_outer(&mut NetworkSerializationContext::default())
1918 .unwrap();
1919 let packet = buffer.parse::<Ipv6Packet<_>>().unwrap();
1920 let (key, packet_len) = assert_matches!(
1921 FragmentHandler::process_fragment::<&[u8]>(&mut core_ctx, &mut bindings_ctx, packet),
1922 FragmentProcessingState::Ready {key, packet_len} => (key, packet_len)
1923 );
1924 assert_eq!(key, test_key(id));
1925 assert_eq!(packet_len, 55);
1926
1927 validate_size(&core_ctx.state.cache);
1928 let mut buffer: Vec<u8> = vec![0; packet_len];
1929 let mut buffer = &mut buffer[..];
1930 FragmentHandler::reassemble_packet(&mut core_ctx, &mut bindings_ctx, &key, &mut buffer)
1931 .unwrap();
1932 let packet = Ipv6Packet::parse_mut(&mut buffer, ()).unwrap();
1933 let mut expected_body: Vec<u8> = Vec::new();
1934 expected_body.extend(0..15);
1935 assert_eq!(packet.body(), &expected_body[..]);
1936 assert_eq!(core_ctx.state.cache.size, 0);
1937 }
1938
1939 #[ip_test(I)]
1940 fn test_ip_reassembly_with_multiple_intertwined_packets<I: TestIpExt>() {
1941 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
1942 const SIZE: u16 = 1;
1943 let id_0 = 5;
1944 let id_1 = 10;
1945
1946 I::process_ip_fragment(
1951 &mut core_ctx,
1952 &mut bindings_ctx,
1953 FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
1954 ExpectedResult::NeedMore,
1955 );
1956
1957 I::process_ip_fragment(
1959 &mut core_ctx,
1960 &mut bindings_ctx,
1961 FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
1962 ExpectedResult::NeedMore,
1963 );
1964
1965 I::process_ip_fragment(
1967 &mut core_ctx,
1968 &mut bindings_ctx,
1969 FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
1970 ExpectedResult::NeedMore,
1971 );
1972
1973 I::process_ip_fragment(
1975 &mut core_ctx,
1976 &mut bindings_ctx,
1977 FragmentSpec { id: id_1, offset: 1, size: SIZE, m_flag: true },
1978 ExpectedResult::NeedMore,
1979 );
1980
1981 I::process_ip_fragment(
1983 &mut core_ctx,
1984 &mut bindings_ctx,
1985 FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
1986 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
1987 );
1988
1989 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_0, 3);
1990
1991 I::process_ip_fragment(
1993 &mut core_ctx,
1994 &mut bindings_ctx,
1995 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
1996 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_1) },
1997 );
1998
1999 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_1, 3);
2000 }
2001
2002 #[ip_test(I)]
2003 fn test_ip_reassembly_timer_with_multiple_intertwined_packets<I: TestIpExt>() {
2004 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2005 const SIZE: u16 = 1;
2006 let id_0 = 5;
2007 let id_1 = 10;
2008 let id_2 = 15;
2009
2010 const BEFORE_TIMEOUT1: Duration = Duration::from_secs(1);
2035 const BEFORE_TIMEOUT2: Duration = Duration::from_secs(2);
2036 const BEFORE_TIMEOUT3: Duration = Duration::from_secs(3);
2037 assert!(BEFORE_TIMEOUT1 < I::REASSEMBLY_TIMEOUT);
2038 assert!(BEFORE_TIMEOUT2 < I::REASSEMBLY_TIMEOUT);
2039 assert!(BEFORE_TIMEOUT3 < I::REASSEMBLY_TIMEOUT);
2040
2041 I::process_ip_fragment(
2043 &mut core_ctx,
2044 &mut bindings_ctx,
2045 FragmentSpec { id: id_0, offset: 0, size: SIZE, m_flag: true },
2046 ExpectedResult::NeedMore,
2047 );
2048
2049 I::process_ip_fragment(
2051 &mut core_ctx,
2052 &mut bindings_ctx,
2053 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: false },
2054 ExpectedResult::NeedMore,
2055 );
2056
2057 I::process_ip_fragment(
2059 &mut core_ctx,
2060 &mut bindings_ctx,
2061 FragmentSpec { id: id_2, offset: 2, size: SIZE, m_flag: false },
2062 ExpectedResult::NeedMore,
2063 );
2064
2065 assert_empty(
2067 bindings_ctx
2068 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT1), &mut core_ctx),
2069 );
2070
2071 I::process_ip_fragment(
2073 &mut core_ctx,
2074 &mut bindings_ctx,
2075 FragmentSpec { id: id_0, offset: 2, size: SIZE, m_flag: false },
2076 ExpectedResult::NeedMore,
2077 );
2078
2079 assert_empty(
2081 bindings_ctx
2082 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT2), &mut core_ctx),
2083 );
2084
2085 I::process_ip_fragment(
2087 &mut core_ctx,
2088 &mut bindings_ctx,
2089 FragmentSpec { id: id_2, offset: 1, size: SIZE, m_flag: true },
2090 ExpectedResult::NeedMore,
2091 );
2092
2093 I::process_ip_fragment(
2095 &mut core_ctx,
2096 &mut bindings_ctx,
2097 FragmentSpec { id: id_0, offset: 1, size: SIZE, m_flag: true },
2098 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_0) },
2099 );
2100
2101 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_0, 3);
2102
2103 assert_empty(
2105 bindings_ctx
2106 .trigger_timers_until_instant(FakeInstant::from(BEFORE_TIMEOUT3), &mut core_ctx),
2107 );
2108
2109 I::process_ip_fragment(
2111 &mut core_ctx,
2112 &mut bindings_ctx,
2113 FragmentSpec { id: id_1, offset: 0, size: SIZE, m_flag: true },
2114 ExpectedResult::NeedMore,
2115 );
2116
2117 I::process_ip_fragment(
2119 &mut core_ctx,
2120 &mut bindings_ctx,
2121 FragmentSpec { id: id_2, offset: 0, size: SIZE, m_flag: true },
2122 ExpectedResult::Ready { body_fragment_blocks: 3, key: test_key(id_2) },
2123 );
2124
2125 try_reassemble_ip_packet(&mut core_ctx, &mut bindings_ctx, id_2, 3);
2126
2127 bindings_ctx.trigger_timers_until_and_expect_unordered(
2130 FakeInstant::from(I::REASSEMBLY_TIMEOUT),
2131 [FragmentTimerId::<I>::default()],
2132 &mut core_ctx,
2133 );
2134
2135 bindings_ctx.timers.assert_no_timers_installed();
2137
2138 I::process_ip_fragment(
2142 &mut core_ctx,
2143 &mut bindings_ctx,
2144 FragmentSpec { id: id_1, offset: 2, size: SIZE, m_flag: true },
2145 ExpectedResult::NeedMore,
2146 );
2147 }
2148
2149 #[test]
2150 fn test_no_more_fragments_in_middle_of_block() {
2151 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2152 process_ipv4_fragment(
2153 &mut core_ctx,
2154 &mut bindings_ctx,
2155 FragmentSpec { id: 0, offset: 100, size: 1, m_flag: false },
2156 get_ipv4_builder(),
2157 ExpectedResult::NeedMore,
2158 );
2159
2160 process_ipv4_fragment(
2161 &mut core_ctx,
2162 &mut bindings_ctx,
2163 FragmentSpec { id: 0, offset: 50, size: 1, m_flag: false },
2164 get_ipv4_builder(),
2165 ExpectedResult::Invalid,
2166 );
2167 }
2168
2169 #[ip_test(I)]
2170 fn test_cancel_timer_on_overlap<I: TestIpExt>() {
2171 const FRAGMENT_ID: u16 = 1;
2172
2173 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<I>();
2174
2175 let key = test_key(FRAGMENT_ID);
2176
2177 for _ in 0..=2 {
2180 I::process_ip_fragment(
2181 &mut core_ctx,
2182 &mut bindings_ctx,
2183 FragmentSpec { id: FRAGMENT_ID, offset: 0, size: 10, m_flag: true },
2184 ExpectedResult::NeedMore,
2185 );
2186 core_ctx
2187 .state
2188 .cache
2189 .timers
2190 .assert_timers_after(&mut bindings_ctx, [(key, (), I::REASSEMBLY_TIMEOUT)]);
2191
2192 I::process_ip_fragment(
2193 &mut core_ctx,
2194 &mut bindings_ctx,
2195 FragmentSpec { id: FRAGMENT_ID, offset: 5, size: 10, m_flag: true },
2196 ExpectedResult::Invalid,
2197 );
2198 assert_eq!(bindings_ctx.timers.timers(), [],);
2199 }
2200 }
2201
2202 #[test]
2204 fn test_fragment_reassembly_evasion_cache_corruption() {
2205 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2206 let id = 5;
2207
2208 process_ipv4_fragment(
2210 &mut core_ctx,
2211 &mut bindings_ctx,
2212 FragmentSpec { id, offset: 0, size: 10, m_flag: true },
2213 get_ipv4_builder(),
2214 ExpectedResult::NeedMore,
2215 );
2216
2217 process_ipv4_fragment(
2219 &mut core_ctx,
2220 &mut bindings_ctx,
2221 FragmentSpec { id, offset: 30, size: 10, m_flag: true },
2222 get_ipv4_builder(),
2223 ExpectedResult::NeedMore,
2224 );
2225
2226 process_ipv4_fragment(
2229 &mut core_ctx,
2230 &mut bindings_ctx,
2231 FragmentSpec { id, offset: 10, size: 10, m_flag: false },
2232 get_ipv4_builder(),
2233 ExpectedResult::Invalid,
2234 );
2235
2236 process_ipv4_fragment(
2240 &mut core_ctx,
2241 &mut bindings_ctx,
2242 FragmentSpec { id, offset: 40, size: 10, m_flag: false },
2243 get_ipv4_builder(),
2244 ExpectedResult::NeedMore,
2245 );
2246 }
2247
2248 #[test]
2250 fn test_multiple_last_fragments_evasion() {
2251 let FakeCtxImpl { mut core_ctx, mut bindings_ctx } = new_context::<Ipv4>();
2252 let id = 6;
2253
2254 process_ipv4_fragment(
2257 &mut core_ctx,
2258 &mut bindings_ctx,
2259 FragmentSpec { id, offset: 2, size: 1, m_flag: false },
2260 get_ipv4_builder(),
2261 ExpectedResult::NeedMore,
2262 );
2263
2264 process_ipv4_fragment(
2268 &mut core_ctx,
2269 &mut bindings_ctx,
2270 FragmentSpec { id, offset: 1, size: 1, m_flag: false },
2271 get_ipv4_builder(),
2272 ExpectedResult::Invalid,
2273 );
2274 }
2275}