1mod tcp;
6
7use alloc::fmt::Debug;
8use alloc::sync::{Arc, Weak};
9use alloc::vec::Vec;
10use assert_matches::assert_matches;
11use core::any::Any;
12use core::fmt::Display;
13use core::hash::Hash;
14use core::time::Duration;
15
16use derivative::Derivative;
17use net_types::ip::{GenericOverIp, Ip, IpVersionMarker};
18use netstack3_hashmap::HashMap;
19use packet_formats::ip::{IpExt, IpProto, Ipv4Proto, Ipv6Proto};
20
21use crate::context::FilterBindingsTypes;
22use crate::logic::FilterTimerId;
23use crate::packets::TransportPacketData;
24use netstack3_base::sync::Mutex;
25use netstack3_base::{CoreTimerContext, Inspectable, Inspector, Instant, TimerContext};
26
27const GC_INTERVAL: Duration = Duration::from_secs(10);
29
30const CONNECTION_EXPIRY_TIME_UDP: Duration = Duration::from_secs(120);
36
37const CONNECTION_EXPIRY_OTHER: Duration = Duration::from_secs(30);
40
41pub(crate) const MAXIMUM_ENTRIES: usize = 100_000;
47
48pub struct Table<I: IpExt, E, BT: FilterBindingsTypes> {
54 inner: Mutex<TableInner<I, E, BT>>,
55}
56
57struct TableInner<I: IpExt, E, BT: FilterBindingsTypes> {
58 table: HashMap<Tuple<I>, Arc<ConnectionShared<I, E, BT>>>,
61 gc_timer: BT::Timer,
63 table_limit_hits: u32,
65 table_limit_drops: u32,
68}
69
70impl<I: IpExt, E, BT: FilterBindingsTypes> Table<I, E, BT> {
71 pub fn contains_tuple(&self, tuple: &Tuple<I>) -> bool {
78 self.inner.lock().table.contains_key(tuple)
79 }
80
81 pub(crate) fn get_shared_connection(
83 &self,
84 tuple: &Tuple<I>,
85 ) -> Option<Arc<ConnectionShared<I, E, BT>>> {
86 let guard = self.inner.lock();
87 let conn = guard.table.get(tuple)?;
88 Some(conn.clone())
89 }
90
91 pub fn get_connection(&self, tuple: &Tuple<I>) -> Option<Connection<I, E, BT>> {
93 let guard = self.inner.lock();
94 let conn = guard.table.get(tuple)?;
95 Some(Connection::Shared(conn.clone()))
96 }
97
98 #[cfg(feature = "testutils")]
103 pub fn num_entries(&self) -> usize {
104 self.inner.lock().table.len()
105 }
106
107 #[cfg(feature = "testutils")]
110 pub fn remove_connection(&mut self, tuple: &Tuple<I>) -> Option<Connection<I, E, BT>> {
111 let mut guard = self.inner.lock();
112
113 let conn = guard.table.remove(tuple)?;
115 let (original, reply) = (&conn.inner.original_tuple, &conn.inner.reply_tuple);
116
117 if original != reply {
120 if tuple == original {
121 assert!(guard.table.remove(reply).is_some());
122 } else {
123 assert!(guard.table.remove(original).is_some());
124 }
125 }
126
127 Some(Connection::Shared(conn))
128 }
129}
130
131fn schedule_gc<BC>(bindings_ctx: &mut BC, timer: &mut BC::Timer)
132where
133 BC: TimerContext,
134{
135 let _ = bindings_ctx.schedule_timer(GC_INTERVAL, timer);
136}
137
138impl<I, E, BC> Table<I, E, BC>
139where
140 I: IpExt,
141 BC: FilterBindingsTypes + TimerContext,
142{
143 pub(crate) fn new<CC>(bindings_ctx: &mut BC) -> Self
144 where
145 CC: CoreTimerContext<FilterTimerId<I>, BC>,
146 {
147 Self {
148 inner: Mutex::new(TableInner {
149 table: HashMap::new(),
150 gc_timer: CC::new_timer(
151 bindings_ctx,
152 FilterTimerId::ConntrackGc(IpVersionMarker::<I>::new()),
153 ),
154 table_limit_hits: 0,
155 table_limit_drops: 0,
156 }),
157 }
158 }
159}
160
161impl<I, E, BC> Table<I, E, BC>
162where
163 I: IpExt,
164 E: Debug + Default + Send + Sync + PartialEq + CompatibleWith + 'static,
165 BC: FilterBindingsTypes + TimerContext,
166{
167 pub(crate) fn finalize_connection(
176 &self,
177 bindings_ctx: &mut BC,
178 connection: Connection<I, E, BC>,
179 ) -> Result<(bool, Option<Arc<ConnectionShared<I, E, BC>>>), FinalizeConnectionError> {
180 let exclusive = match connection {
181 Connection::Exclusive(c) => c,
182 Connection::Shared(inner) => return Ok((false, Some(inner))),
186 };
187
188 if exclusive.do_not_insert {
189 return Ok((false, None));
190 }
191
192 let mut guard = self.inner.lock();
193
194 if guard.table.len() >= MAXIMUM_ENTRIES {
195 guard.table_limit_hits = guard.table_limit_hits.saturating_add(1);
196
197 struct Info<'a, I: IpExt, BT: FilterBindingsTypes> {
198 original_tuple: &'a Tuple<I>,
199 reply_tuple: &'a Tuple<I>,
200 lifecycle: EstablishmentLifecycle,
201 last_seen: BT::Instant,
202 }
203
204 let mut info: Option<Info<'_, I, BC>> = None;
205
206 let now = bindings_ctx.now();
207 for (_, conn) in &guard.table {
218 let state = conn.state.lock();
219 if state.is_expired(now) {
220 info = Some(Info {
221 original_tuple: &conn.inner.original_tuple,
222 reply_tuple: &conn.inner.reply_tuple,
223 lifecycle: state.establishment_lifecycle,
224 last_seen: state.last_packet_time,
225 });
226 break;
227 }
228
229 match state.establishment_lifecycle {
230 EstablishmentLifecycle::SeenOriginal | EstablishmentLifecycle::SeenReply => {
231 match &info {
232 None => {
233 info = Some(Info {
234 original_tuple: &conn.inner.original_tuple,
235 reply_tuple: &conn.inner.reply_tuple,
236 lifecycle: state.establishment_lifecycle,
237 last_seen: state.last_packet_time,
238 })
239 }
240 Some(existing) => {
241 if state.establishment_lifecycle < existing.lifecycle
242 || (state.establishment_lifecycle == existing.lifecycle
243 && state.last_packet_time < existing.last_seen)
244 {
245 info = Some(Info {
246 original_tuple: &conn.inner.original_tuple,
247 reply_tuple: &conn.inner.reply_tuple,
248 lifecycle: state.establishment_lifecycle,
249 last_seen: state.last_packet_time,
250 })
251 }
252 }
253 }
254 }
255 EstablishmentLifecycle::Established => {}
256 }
257 }
258
259 if let Some(Info { original_tuple, reply_tuple, .. }) = info {
260 let original_tuple = original_tuple.clone();
261 let reply_tuple = reply_tuple.clone();
262
263 assert!(guard.table.remove(&original_tuple).is_some());
264 if original_tuple != reply_tuple {
265 assert!(guard.table.remove(&reply_tuple).is_some());
266 }
267 } else {
268 guard.table_limit_drops = guard.table_limit_drops.saturating_add(1);
269 return Err(FinalizeConnectionError::TableFull);
270 }
271 }
272
273 if guard.table.contains_key(&exclusive.inner.original_tuple)
286 || guard.table.contains_key(&exclusive.inner.reply_tuple)
287 {
288 let conn = if let Some(conn) = guard.table.get(&exclusive.inner.original_tuple) {
301 conn
302 } else {
303 guard
304 .table
305 .get(&exclusive.inner.reply_tuple)
306 .expect("checked that tuple is in table and table is locked")
307 };
308 if conn.compatible_with(&exclusive) {
309 return Ok((false, Some(conn.clone())));
310 }
311
312 Err(FinalizeConnectionError::Conflict)
314 } else {
315 let shared = exclusive.make_shared();
316 let clone = Arc::clone(&shared);
317
318 assert_matches!(
319 guard.table.insert(shared.inner.original_tuple.clone(), shared.clone()),
320 None
321 );
322
323 if shared.inner.reply_tuple != shared.inner.original_tuple {
324 assert_matches!(guard.table.insert(shared.inner.reply_tuple.clone(), shared), None);
325 }
326
327 if bindings_ctx.scheduled_instant(&mut guard.gc_timer).is_none() {
333 schedule_gc(bindings_ctx, &mut guard.gc_timer);
334 }
335
336 Ok((true, Some(clone)))
337 }
338 }
339}
340
341impl<I, E, BC> Table<I, E, BC>
342where
343 I: IpExt,
344 E: Debug + Default,
345 BC: FilterBindingsTypes + TimerContext,
346{
347 pub(crate) fn get_connection_for_packet_and_update(
357 &self,
358 bindings_ctx: &BC,
359 packet: PacketMetadata<I>,
360 ) -> Result<Option<(Connection<I, E, BC>, ConnectionDirection)>, GetConnectionError<I, E, BC>>
361 {
362 let tuple = packet.tuple();
363 let mut connection = match self.inner.lock().table.get(&tuple) {
364 Some(connection) => Connection::Shared(connection.clone()),
365 None => match ConnectionExclusive::from_deconstructed_packet(bindings_ctx, &packet) {
366 None => return Ok(None),
367 Some(c) => Connection::Exclusive(c),
368 },
369 };
370
371 let direction = match connection.direction(&tuple) {
372 Some(direction) => direction,
373 None => unreachable!(
374 "tuple didn't match connection after looking up in map: {tuple:?} {connection:?}"
375 ),
376 };
377
378 match connection.update(bindings_ctx, &packet, direction) {
379 Ok(ConnectionUpdateAction::NoAction) => Ok(Some((connection, direction))),
380 Ok(ConnectionUpdateAction::RemoveEntry) => match connection {
381 Connection::Exclusive(mut conn) => {
382 conn.do_not_insert = true;
383 Ok(Some((Connection::Exclusive(conn), direction)))
384 }
385 Connection::Shared(conn) => {
386 let mut guard = self.inner.lock();
390 let _ = guard.table.remove(&conn.inner.original_tuple);
391 let _ = guard.table.remove(&conn.inner.reply_tuple);
392
393 Ok(Some((Connection::Shared(conn), direction)))
394 }
395 },
396 Err(ConnectionUpdateError::InvalidPacket) => {
397 Err(GetConnectionError::InvalidPacket(connection, direction))
398 }
399 }
400 }
401
402 pub(crate) fn perform_gc(&self, bindings_ctx: &mut BC) {
403 let now = bindings_ctx.now();
404 let mut guard = self.inner.lock();
405
406 let to_remove: Vec<_> = guard
415 .table
416 .iter()
417 .filter_map(|(tuple, conn)| {
418 if *tuple == conn.inner.original_tuple && conn.is_expired(now) {
419 Some((conn.inner.original_tuple.clone(), conn.inner.reply_tuple.clone()))
420 } else {
421 None
422 }
423 })
424 .collect();
425
426 for (original_tuple, reply_tuple) in to_remove {
427 assert!(guard.table.remove(&original_tuple).is_some());
428 if reply_tuple != original_tuple {
429 assert!(guard.table.remove(&reply_tuple).is_some());
430 }
431 }
432
433 if !guard.table.is_empty() {
439 schedule_gc(bindings_ctx, &mut guard.gc_timer);
440 }
441 }
442}
443
444impl<I, E, BT> Inspectable for Table<I, E, BT>
445where
446 I: IpExt,
447 E: Inspectable,
448 BT: FilterBindingsTypes,
449{
450 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
451 let guard = self.inner.lock();
452
453 inspector.record_usize("num_entries", guard.table.len());
454 inspector.record_uint("table_limit_hits", guard.table_limit_hits);
455 inspector.record_uint("table_limit_drops", guard.table_limit_drops);
456
457 inspector.record_child("connections", |inspector| {
458 guard
459 .table
460 .iter()
461 .filter_map(|(tuple, connection)| {
462 if *tuple == connection.inner.original_tuple { Some(connection) } else { None }
463 })
464 .for_each(|connection| {
465 inspector.record_unnamed_child(|inspector| {
466 inspector.delegate_inspectable(connection.as_ref())
467 });
468 });
469 });
470 }
471}
472
473#[derive(Debug, Clone, PartialEq, Eq, Hash, GenericOverIp)]
475#[generic_over_ip(I, Ip)]
476pub struct Tuple<I: IpExt> {
477 pub protocol: TransportProtocol,
479 pub src_addr: I::Addr,
481 pub dst_addr: I::Addr,
483 pub src_port_or_id: u16,
485 pub dst_port_or_id: u16,
487}
488
489impl<I: IpExt> Tuple<I> {
490 fn new(
491 src_addr: I::Addr,
492 dst_addr: I::Addr,
493 src_port_or_id: u16,
494 dst_port_or_id: u16,
495 protocol: TransportProtocol,
496 ) -> Self {
497 Self { protocol, src_addr, dst_addr, src_port_or_id, dst_port_or_id }
498 }
499
500 pub(crate) fn invert(self) -> Tuple<I> {
506 Self {
509 protocol: self.protocol,
510 src_addr: self.dst_addr,
511 dst_addr: self.src_addr,
512 src_port_or_id: self.dst_port_or_id,
513 dst_port_or_id: self.src_port_or_id,
514 }
515 }
516}
517
518impl<I: IpExt> Inspectable for Tuple<I> {
519 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
520 inspector.record_debug("protocol", self.protocol);
521 inspector.record_ip_addr("src_addr", self.src_addr);
522 inspector.record_ip_addr("dst_addr", self.dst_addr);
523 inspector.record_usize("src_port_or_id", self.src_port_or_id);
524 inspector.record_usize("dst_port_or_id", self.dst_port_or_id);
525 }
526}
527
528#[derive(Debug, Copy, Clone, PartialEq, Eq)]
530pub enum ConnectionDirection {
531 Original,
534
535 Reply,
538}
539
540#[derive(Debug)]
542pub(crate) enum FinalizeConnectionError {
543 Conflict,
546
547 TableFull,
549}
550
551#[derive(Debug, PartialEq, Eq)]
553enum ConnectionUpdateAction {
554 NoAction,
556
557 RemoveEntry,
559}
560
561#[derive(Debug, PartialEq, Eq)]
563enum ConnectionUpdateError {
564 InvalidPacket,
567}
568
569#[derive(Derivative)]
571#[derivative(Debug(bound = "E: Debug"))]
572pub(crate) enum GetConnectionError<I: IpExt, E, BT: FilterBindingsTypes> {
573 InvalidPacket(Connection<I, E, BT>, ConnectionDirection),
575}
576
577#[derive(Derivative)]
580#[derivative(Debug(bound = "E: Debug"))]
581pub enum Connection<I: IpExt, E, BT: FilterBindingsTypes> {
582 Exclusive(ConnectionExclusive<I, E, BT>),
585
586 Shared(Arc<ConnectionShared<I, E, BT>>),
589}
590
591#[derive(Debug)]
594pub enum WeakConnectionError {
595 EntryRemoved,
597 InvalidEntry,
600}
601
602#[derive(Debug, Clone)]
612pub struct WeakConnection(pub(crate) Weak<dyn Any + Send + Sync>);
613
614impl WeakConnection {
615 pub fn new<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
618 conn: &Connection<I, E, BT>,
619 ) -> Option<Self> {
620 let shared = match conn {
621 Connection::Exclusive(_) => return None,
622 Connection::Shared(shared) => shared,
623 };
624 let weak = Arc::downgrade(shared);
625 Some(Self(weak))
626 }
627
628 pub fn into_inner<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
637 self,
638 ) -> Result<Connection<I, E, BT>, WeakConnectionError> {
639 let Self(inner) = self;
640 let shared = inner
641 .upgrade()
642 .ok_or(WeakConnectionError::EntryRemoved)?
643 .downcast()
644 .map_err(|_err: Arc<_>| WeakConnectionError::InvalidEntry)?;
645 Ok(Connection::Shared(shared))
646 }
647}
648
649impl<I: IpExt, E, BT: FilterBindingsTypes> Connection<I, E, BT> {
650 pub fn original_tuple(&self) -> &Tuple<I> {
652 match self {
653 Connection::Exclusive(c) => &c.inner.original_tuple,
654 Connection::Shared(c) => &c.inner.original_tuple,
655 }
656 }
657
658 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
660 match self {
661 Connection::Exclusive(c) => &c.inner.reply_tuple,
662 Connection::Shared(c) => &c.inner.reply_tuple,
663 }
664 }
665
666 pub fn external_data(&self) -> &E {
668 match self {
669 Connection::Exclusive(c) => &c.inner.external_data,
670 Connection::Shared(c) => &c.inner.external_data,
671 }
672 }
673
674 pub(crate) fn direction(&self, tuple: &Tuple<I>) -> Option<ConnectionDirection> {
677 let (original, reply) = match self {
678 Connection::Exclusive(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
679 Connection::Shared(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
680 };
681
682 if tuple == reply {
690 Some(ConnectionDirection::Reply)
691 } else if tuple == original {
692 Some(ConnectionDirection::Original)
693 } else {
694 None
695 }
696 }
697
698 #[allow(dead_code)]
700 pub(crate) fn state(&self) -> ConnectionState<BT> {
701 match self {
702 Connection::Exclusive(c) => c.state.clone(),
703 Connection::Shared(c) => c.state.lock().clone(),
704 }
705 }
706}
707
708impl<I, E, BC> Connection<I, E, BC>
709where
710 I: IpExt,
711 BC: FilterBindingsTypes + TimerContext,
712{
713 fn update(
714 &mut self,
715 bindings_ctx: &BC,
716 packet: &PacketMetadata<I>,
717 direction: ConnectionDirection,
718 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
719 match packet {
720 PacketMetadata::Full { transport_data, .. } => {
721 let now = bindings_ctx.now();
722 match self {
723 Connection::Exclusive(c) => c.state.update(direction, &transport_data, now),
724 Connection::Shared(c) => c.state.lock().update(direction, &transport_data, now),
725 }
726 }
727 PacketMetadata::IcmpError(_) => Ok(ConnectionUpdateAction::NoAction),
728 }
729 }
730}
731
732#[derive(Derivative)]
734#[derivative(Debug(bound = "E: Debug"), PartialEq(bound = "E: PartialEq"))]
735pub struct ConnectionCommon<I: IpExt, E> {
736 pub(crate) original_tuple: Tuple<I>,
739
740 pub(crate) reply_tuple: Tuple<I>,
743
744 pub(crate) external_data: E,
748}
749
750impl<I: IpExt, E: Inspectable> Inspectable for ConnectionCommon<I, E> {
751 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
752 inspector.record_child("original_tuple", |inspector| {
753 inspector.delegate_inspectable(&self.original_tuple);
754 });
755
756 inspector.record_child("reply_tuple", |inspector| {
757 inspector.delegate_inspectable(&self.reply_tuple);
758 });
759
760 inspector.record_child("external_data", |inspector| {
764 inspector.delegate_inspectable(&self.external_data);
765 });
766 }
767}
768
769#[derive(Debug, Clone)]
770enum ProtocolState {
771 Tcp(tcp::Connection),
772 Udp,
773 Other,
774}
775
776impl ProtocolState {
777 fn update(
778 &mut self,
779 dir: ConnectionDirection,
780 transport_data: &TransportPacketData,
781 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
782 match self {
783 ProtocolState::Tcp(tcp_conn) => {
784 let (segment, payload_len) = assert_matches!(
785 transport_data,
786 TransportPacketData::Tcp { segment, payload_len, .. } => (segment, payload_len)
787 );
788 tcp_conn.update(&segment, *payload_len, dir)
789 }
790 ProtocolState::Udp | ProtocolState::Other => Ok(ConnectionUpdateAction::NoAction),
791 }
792 }
793}
794
795#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
806enum EstablishmentLifecycle {
807 SeenOriginal,
808 SeenReply,
809 Established,
810}
811
812impl EstablishmentLifecycle {
813 fn update(self, dir: ConnectionDirection) -> Self {
814 match self {
815 EstablishmentLifecycle::SeenOriginal => match dir {
816 ConnectionDirection::Original => self,
817 ConnectionDirection::Reply => EstablishmentLifecycle::SeenReply,
818 },
819 EstablishmentLifecycle::SeenReply => match dir {
820 ConnectionDirection::Original => EstablishmentLifecycle::Established,
821 ConnectionDirection::Reply => self,
822 },
823 EstablishmentLifecycle::Established => self,
824 }
825 }
826}
827
828#[derive(Derivative)]
830#[derivative(Clone(bound = ""), Debug(bound = ""))]
831pub(crate) struct ConnectionState<BT: FilterBindingsTypes> {
832 last_packet_time: BT::Instant,
835
836 establishment_lifecycle: EstablishmentLifecycle,
838
839 protocol_state: ProtocolState,
841}
842
843impl<BT: FilterBindingsTypes> ConnectionState<BT> {
844 fn update(
845 &mut self,
846 dir: ConnectionDirection,
847 transport_data: &TransportPacketData,
848 now: BT::Instant,
849 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
850 if self.last_packet_time < now {
851 self.last_packet_time = now;
852 }
853
854 self.establishment_lifecycle = self.establishment_lifecycle.update(dir);
855
856 self.protocol_state.update(dir, transport_data)
857 }
858
859 fn is_expired(&self, now: BT::Instant) -> bool {
860 let duration = now.saturating_duration_since(self.last_packet_time);
861
862 let expiry_duration = match &self.protocol_state {
863 ProtocolState::Tcp(tcp_conn) => tcp_conn.expiry_duration(self.establishment_lifecycle),
864 ProtocolState::Udp => CONNECTION_EXPIRY_TIME_UDP,
865 ProtocolState::Other => CONNECTION_EXPIRY_OTHER,
870 };
871
872 duration >= expiry_duration
873 }
874}
875
876impl<BT: FilterBindingsTypes> Inspectable for ConnectionState<BT> {
877 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
878 inspector.record_bool(
879 "established",
880 match self.establishment_lifecycle {
881 EstablishmentLifecycle::SeenOriginal | EstablishmentLifecycle::SeenReply => false,
882 EstablishmentLifecycle::Established => true,
883 },
884 );
885 inspector.record_inspectable_value("last_packet_time", &self.last_packet_time);
886 }
887}
888
889#[derive(Derivative)]
896#[derivative(Debug(bound = "E: Debug"))]
897pub struct ConnectionExclusive<I: IpExt, E, BT: FilterBindingsTypes> {
898 pub(crate) inner: ConnectionCommon<I, E>,
899 pub(crate) state: ConnectionState<BT>,
900
901 do_not_insert: bool,
906}
907
908impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionExclusive<I, E, BT> {
909 fn make_shared(self) -> Arc<ConnectionShared<I, E, BT>> {
912 Arc::new(ConnectionShared { inner: self.inner, state: Mutex::new(self.state) })
913 }
914
915 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
916 &self.inner.reply_tuple
917 }
918
919 pub(crate) fn rewrite_reply_dst_addr(&mut self, addr: I::Addr) {
920 self.inner.reply_tuple.dst_addr = addr;
921 }
922
923 pub(crate) fn rewrite_reply_src_addr(&mut self, addr: I::Addr) {
924 self.inner.reply_tuple.src_addr = addr;
925 }
926
927 pub(crate) fn rewrite_reply_src_port_or_id(&mut self, port_or_id: u16) {
928 self.inner.reply_tuple.src_port_or_id = port_or_id;
929 match self.inner.reply_tuple.protocol {
930 TransportProtocol::Icmp => {
931 self.inner.reply_tuple.dst_port_or_id = port_or_id;
939 }
940 TransportProtocol::Tcp | TransportProtocol::Udp | TransportProtocol::Other(_) => {}
941 }
942 }
943
944 pub(crate) fn rewrite_reply_dst_port_or_id(&mut self, port_or_id: u16) {
945 self.inner.reply_tuple.dst_port_or_id = port_or_id;
946 match self.inner.reply_tuple.protocol {
947 TransportProtocol::Icmp => {
948 self.inner.reply_tuple.src_port_or_id = port_or_id;
956 }
957 TransportProtocol::Tcp | TransportProtocol::Udp | TransportProtocol::Other(_) => {}
958 }
959 }
960}
961
962impl<I, E, BC> ConnectionExclusive<I, E, BC>
963where
964 I: IpExt,
965 E: Default,
966 BC: FilterBindingsTypes + TimerContext,
967{
968 pub(crate) fn from_deconstructed_packet(
969 bindings_ctx: &BC,
970 packet_metadata: &PacketMetadata<I>,
971 ) -> Option<Self> {
972 let (tuple, transport_data) = match packet_metadata {
973 PacketMetadata::Full { tuple, transport_data } => (tuple, transport_data),
974 PacketMetadata::IcmpError(_) => return None,
975 };
976
977 let reply_tuple = tuple.clone().invert();
978 let self_connected = reply_tuple == *tuple;
979
980 Some(Self {
981 inner: ConnectionCommon {
982 original_tuple: tuple.clone(),
983 reply_tuple,
984 external_data: E::default(),
985 },
986 state: ConnectionState {
987 last_packet_time: bindings_ctx.now(),
988 establishment_lifecycle: EstablishmentLifecycle::SeenOriginal,
989 protocol_state: match tuple.protocol {
990 TransportProtocol::Tcp => {
991 let (segment, payload_len) = match transport_data.tcp_segment_and_len() {
992 Some(v) => v,
993 None => unreachable!(
997 "protocol was TCP, but didn't have TCP info: {transport_data:?}"
998 ),
999 };
1000
1001 ProtocolState::Tcp(tcp::Connection::new(
1002 segment,
1003 payload_len,
1004 self_connected,
1005 )?)
1006 }
1007 TransportProtocol::Udp => ProtocolState::Udp,
1008 TransportProtocol::Icmp | TransportProtocol::Other(_) => ProtocolState::Other,
1009 },
1010 },
1011 do_not_insert: false,
1012 })
1013 }
1014}
1015
1016#[derive(Derivative)]
1022#[derivative(Debug(bound = "E: Debug"))]
1023pub struct ConnectionShared<I: IpExt, E, BT: FilterBindingsTypes> {
1024 inner: ConnectionCommon<I, E>,
1025 state: Mutex<ConnectionState<BT>>,
1026}
1027
1028#[allow(missing_docs)]
1030#[derive(Copy, Clone, PartialEq, Eq, Hash, GenericOverIp)]
1031#[generic_over_ip()]
1032pub enum TransportProtocol {
1033 Tcp,
1034 Udp,
1035 Icmp,
1036 Other(u8),
1037}
1038
1039impl From<Ipv4Proto> for TransportProtocol {
1040 fn from(value: Ipv4Proto) -> Self {
1041 match value {
1042 Ipv4Proto::Proto(IpProto::Tcp) => TransportProtocol::Tcp,
1043 Ipv4Proto::Proto(IpProto::Udp) => TransportProtocol::Udp,
1044 Ipv4Proto::Icmp => TransportProtocol::Icmp,
1045 v => TransportProtocol::Other(v.into()),
1046 }
1047 }
1048}
1049
1050impl From<Ipv6Proto> for TransportProtocol {
1051 fn from(value: Ipv6Proto) -> Self {
1052 match value {
1053 Ipv6Proto::Proto(IpProto::Tcp) => TransportProtocol::Tcp,
1054 Ipv6Proto::Proto(IpProto::Udp) => TransportProtocol::Udp,
1055 Ipv6Proto::Icmpv6 => TransportProtocol::Icmp,
1056 v => TransportProtocol::Other(v.into()),
1057 }
1058 }
1059}
1060
1061impl From<IpProto> for TransportProtocol {
1062 fn from(value: IpProto) -> Self {
1063 match value {
1064 IpProto::Tcp => TransportProtocol::Tcp,
1065 IpProto::Udp => TransportProtocol::Udp,
1066 v @ IpProto::Reserved => TransportProtocol::Other(v.into()),
1067 }
1068 }
1069}
1070
1071impl Display for TransportProtocol {
1072 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1073 match self {
1074 TransportProtocol::Tcp => write!(f, "TCP"),
1075 TransportProtocol::Udp => write!(f, "UDP"),
1076 TransportProtocol::Icmp => write!(f, "ICMP"),
1077 TransportProtocol::Other(n) => write!(f, "Other({n})"),
1078 }
1079 }
1080}
1081
1082impl Debug for TransportProtocol {
1083 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1084 Display::fmt(&self, f)
1085 }
1086}
1087
1088impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1089 fn is_expired(&self, now: BT::Instant) -> bool {
1090 self.state.lock().is_expired(now)
1091 }
1092}
1093
1094impl<I: IpExt, E: CompatibleWith, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1095 pub(crate) fn compatible_with(&self, conn: &ConnectionExclusive<I, E, BT>) -> bool {
1099 self.inner.original_tuple == conn.inner.original_tuple
1100 && self.inner.reply_tuple == conn.inner.reply_tuple
1101 && self.inner.external_data.compatible_with(&conn.inner.external_data)
1102 }
1103}
1104
1105impl<I: IpExt, E: Inspectable, BT: FilterBindingsTypes> Inspectable for ConnectionShared<I, E, BT> {
1106 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
1107 inspector.delegate_inspectable(&self.inner);
1108 inspector.delegate_inspectable(&*self.state.lock());
1109 }
1110}
1111
1112pub trait CompatibleWith {
1115 fn compatible_with(&self, other: &Self) -> bool;
1118}
1119
1120#[derive(Debug, Clone, PartialEq, Eq)]
1125pub enum PacketMetadata<I: IpExt> {
1126 Full { tuple: Tuple<I>, transport_data: TransportPacketData },
1127 IcmpError(Tuple<I>),
1128}
1129
1130impl<I: IpExt> PacketMetadata<I> {
1131 pub(crate) fn new(
1132 src_addr: I::Addr,
1133 dst_addr: I::Addr,
1134 protocol: TransportProtocol,
1135 transport_data: TransportPacketData,
1136 ) -> Self {
1137 match protocol {
1138 TransportProtocol::Tcp => {
1139 assert_matches!(transport_data, TransportPacketData::Tcp { .. })
1140 }
1141 TransportProtocol::Udp | TransportProtocol::Icmp | TransportProtocol::Other(_) => {
1142 assert_matches!(transport_data, TransportPacketData::Generic { .. })
1143 }
1144 }
1145
1146 Self::Full {
1147 tuple: Tuple::new(
1148 src_addr,
1149 dst_addr,
1150 transport_data.src_port(),
1151 transport_data.dst_port(),
1152 protocol,
1153 ),
1154 transport_data,
1155 }
1156 }
1157
1158 pub(crate) fn new_from_icmp_error(
1159 src_addr: I::Addr,
1160 dst_addr: I::Addr,
1161 src_port: u16,
1162 dst_port: u16,
1163 protocol: TransportProtocol,
1164 ) -> Self {
1165 Self::IcmpError(Tuple::new(src_addr, dst_addr, src_port, dst_port, protocol))
1166 }
1167
1168 pub(crate) fn tuple(&self) -> Tuple<I> {
1169 match self {
1170 PacketMetadata::Full { tuple, .. } => tuple.clone(),
1171 PacketMetadata::IcmpError(tuple) => tuple.clone().invert(),
1199 }
1200 }
1201}
1202
1203#[cfg(test)]
1204pub(crate) mod testutils {
1205 use crate::packets::testutil::internal::{FakeIpPacket, FakeUdpPacket, TestIpExt};
1206
1207 pub(crate) fn make_test_udp_packets<I: TestIpExt>(
1210 index: u32,
1211 ) -> (FakeIpPacket<I, FakeUdpPacket>, FakeIpPacket<I, FakeUdpPacket>) {
1212 let src_port = (index % (u16::MAX as u32)) as u16;
1215 let dst_port = (index / (u16::MAX as u32)) as u16;
1216
1217 let packet = FakeIpPacket::<I, _> {
1218 src_ip: I::SRC_IP,
1219 dst_ip: I::DST_IP,
1220 body: FakeUdpPacket { src_port, dst_port },
1221 };
1222 let reply_packet = FakeIpPacket::<I, _> {
1223 src_ip: I::DST_IP,
1224 dst_ip: I::SRC_IP,
1225 body: FakeUdpPacket { src_port: dst_port, dst_port: src_port },
1226 };
1227
1228 (packet, reply_packet)
1229 }
1230}
1231
1232#[cfg(test)]
1233mod tests {
1234 use assert_matches::assert_matches;
1235 use ip_test_macro::ip_test;
1236 use netstack3_base::testutil::FakeTimerCtxExt;
1237 use netstack3_base::{Control, IntoCoreTimerCtx, SegmentHeader, SeqNum, UnscaledWindowSize};
1238 use test_case::test_case;
1239
1240 use super::testutils::make_test_udp_packets;
1241 use super::*;
1242 use crate::context::testutil::{FakeBindingsCtx, FakeCtx};
1243 use crate::packets::IpPacket;
1244 use crate::packets::testutil::internal::ArbitraryValue;
1245 use crate::state::IpRoutines;
1246 use crate::testutil::TestIpExt;
1247
1248 impl CompatibleWith for () {
1249 fn compatible_with(&self, (): &()) -> bool {
1250 true
1251 }
1252 }
1253
1254 #[test_case(
1255 EstablishmentLifecycle::SeenOriginal,
1256 ConnectionDirection::Original
1257 => EstablishmentLifecycle::SeenOriginal
1258 )]
1259 #[test_case(
1260 EstablishmentLifecycle::SeenOriginal,
1261 ConnectionDirection::Reply
1262 => EstablishmentLifecycle::SeenReply
1263 )]
1264 #[test_case(
1265 EstablishmentLifecycle::SeenReply,
1266 ConnectionDirection::Original
1267 => EstablishmentLifecycle::Established
1268 )]
1269 #[test_case(
1270 EstablishmentLifecycle::SeenReply,
1271 ConnectionDirection::Reply
1272 => EstablishmentLifecycle::SeenReply
1273 )]
1274 #[test_case(
1275 EstablishmentLifecycle::Established,
1276 ConnectionDirection::Original
1277 => EstablishmentLifecycle::Established
1278 )]
1279 #[test_case(
1280 EstablishmentLifecycle::Established,
1281 ConnectionDirection::Reply
1282 => EstablishmentLifecycle::Established
1283 )]
1284 fn establishment_lifecycle_test(
1285 lifecycle: EstablishmentLifecycle,
1286 dir: ConnectionDirection,
1287 ) -> EstablishmentLifecycle {
1288 lifecycle.update(dir)
1289 }
1290
1291 #[ip_test(I)]
1292 #[test_case(TransportProtocol::Udp)]
1293 #[test_case(TransportProtocol::Tcp)]
1294 fn tuple_invert_udp_tcp<I: IpExt + TestIpExt>(protocol: TransportProtocol) {
1295 let orig_tuple = Tuple::<I> {
1296 protocol: protocol,
1297 src_addr: I::SRC_IP,
1298 dst_addr: I::DST_IP,
1299 src_port_or_id: I::SRC_PORT,
1300 dst_port_or_id: I::DST_PORT,
1301 };
1302
1303 let expected = Tuple::<I> {
1304 protocol: protocol,
1305 src_addr: I::DST_IP,
1306 dst_addr: I::SRC_IP,
1307 src_port_or_id: I::DST_PORT,
1308 dst_port_or_id: I::SRC_PORT,
1309 };
1310
1311 let inverted = orig_tuple.invert();
1312
1313 assert_eq!(inverted, expected);
1314 }
1315
1316 #[ip_test(I)]
1317 fn tuple_from_tcp_packet<I: IpExt + TestIpExt>() {
1318 let expected = Tuple::<I> {
1319 protocol: TransportProtocol::Tcp,
1320 src_addr: I::SRC_IP,
1321 dst_addr: I::DST_IP,
1322 src_port_or_id: I::SRC_PORT,
1323 dst_port_or_id: I::DST_PORT,
1324 };
1325
1326 let packet = PacketMetadata::<I>::new(
1327 I::SRC_IP,
1328 I::DST_IP,
1329 TransportProtocol::Tcp,
1330 TransportPacketData::Tcp {
1331 src_port: I::SRC_PORT,
1332 dst_port: I::DST_PORT,
1333 segment: SegmentHeader::arbitrary_value(),
1334 payload_len: 4,
1335 },
1336 );
1337
1338 assert_eq!(packet.tuple(), expected);
1339 }
1340
1341 #[ip_test(I)]
1342 fn connection_from_tuple<I: IpExt + TestIpExt>() {
1343 let bindings_ctx = FakeBindingsCtx::<I>::new();
1344
1345 let packet = PacketMetadata::<I>::new(
1346 I::SRC_IP,
1347 I::DST_IP,
1348 TransportProtocol::Udp,
1349 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1350 );
1351 let original_tuple = packet.tuple();
1352 let reply_tuple = packet.tuple().invert();
1353
1354 let connection =
1355 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1356 .unwrap();
1357
1358 assert_eq!(&connection.inner.original_tuple, &original_tuple);
1359 assert_eq!(&connection.inner.reply_tuple, &reply_tuple);
1360 }
1361
1362 #[ip_test(I)]
1363 fn connection_make_shared_has_same_underlying_info<I: IpExt + TestIpExt>() {
1364 let bindings_ctx = FakeBindingsCtx::<I>::new();
1365
1366 let packet = PacketMetadata::<I>::new(
1367 I::SRC_IP,
1368 I::DST_IP,
1369 TransportProtocol::Udp,
1370 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1371 );
1372 let original_tuple = packet.tuple();
1373 let reply_tuple = original_tuple.clone().invert();
1374
1375 let mut connection =
1376 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1377 connection.inner.external_data = 1234;
1378 let shared = connection.make_shared();
1379
1380 assert_eq!(shared.inner.original_tuple, original_tuple);
1381 assert_eq!(shared.inner.reply_tuple, reply_tuple);
1382 assert_eq!(shared.inner.external_data, 1234);
1383 }
1384
1385 enum ConnectionKind {
1386 Exclusive,
1387 Shared,
1388 }
1389
1390 #[ip_test(I)]
1391 #[test_case(ConnectionKind::Exclusive)]
1392 #[test_case(ConnectionKind::Shared)]
1393 fn connection_getters<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1394 let bindings_ctx = FakeBindingsCtx::<I>::new();
1395
1396 let packet = PacketMetadata::<I>::new(
1397 I::SRC_IP,
1398 I::DST_IP,
1399 TransportProtocol::Udp,
1400 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1401 );
1402 let original_tuple = packet.tuple();
1403 let reply_tuple = original_tuple.clone().invert();
1404
1405 let mut connection =
1406 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1407 connection.inner.external_data = 1234;
1408
1409 let connection = match connection_kind {
1410 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1411 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1412 };
1413
1414 assert_eq!(connection.original_tuple(), &original_tuple);
1415 assert_eq!(connection.reply_tuple(), &reply_tuple);
1416 assert_eq!(connection.external_data(), &1234);
1417 }
1418
1419 #[ip_test(I)]
1420 #[test_case(ConnectionKind::Exclusive)]
1421 #[test_case(ConnectionKind::Shared)]
1422 fn connection_direction<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1423 let bindings_ctx = FakeBindingsCtx::<I>::new();
1424
1425 let packet = PacketMetadata::<I>::new(
1426 I::SRC_IP,
1427 I::DST_IP,
1428 TransportProtocol::Udp,
1429 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1430 );
1431 let original_tuple = packet.tuple();
1432 let reply_tuple = original_tuple.clone().invert();
1433
1434 let mut other_tuple = original_tuple.clone();
1435 other_tuple.src_port_or_id += 1;
1436
1437 let connection: ConnectionExclusive<_, (), _> =
1438 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1439 let connection = match connection_kind {
1440 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1441 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1442 };
1443
1444 assert_matches!(connection.direction(&original_tuple), Some(ConnectionDirection::Original));
1445 assert_matches!(connection.direction(&reply_tuple), Some(ConnectionDirection::Reply));
1446 assert_matches!(connection.direction(&other_tuple), None);
1447 }
1448
1449 #[ip_test(I)]
1450 #[test_case(ConnectionKind::Exclusive)]
1451 #[test_case(ConnectionKind::Shared)]
1452 fn connection_update<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1453 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1454 bindings_ctx.sleep(Duration::from_secs(1));
1455
1456 let packet = PacketMetadata::<I>::new(
1457 I::SRC_IP,
1458 I::DST_IP,
1459 TransportProtocol::Udp,
1460 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1461 );
1462
1463 let reply_packet = PacketMetadata::<I>::new(
1464 I::DST_IP,
1465 I::SRC_IP,
1466 TransportProtocol::Udp,
1467 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1468 );
1469
1470 let connection =
1471 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1472 .unwrap();
1473 let mut connection = match connection_kind {
1474 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1475 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1476 };
1477
1478 assert_matches!(
1479 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1480 Ok(ConnectionUpdateAction::NoAction)
1481 );
1482 let state = connection.state();
1483 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1484 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1485
1486 bindings_ctx.sleep(Duration::from_secs(1));
1489 assert_matches!(
1490 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1491 Ok(ConnectionUpdateAction::NoAction)
1492 );
1493 let state = connection.state();
1494 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1495 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1496 }
1497
1498 #[ip_test(I)]
1499 #[test_case(ConnectionKind::Exclusive)]
1500 #[test_case(ConnectionKind::Shared)]
1501 fn skip_connection_update_for_icmp_error<I: IpExt + TestIpExt>(
1502 connection_kind: ConnectionKind,
1503 ) {
1504 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1505 bindings_ctx.sleep(Duration::from_secs(1));
1506
1507 let packet = PacketMetadata::<I>::new(
1508 I::SRC_IP,
1509 I::DST_IP,
1510 TransportProtocol::Udp,
1511 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1512 );
1513
1514 let reply_packet = PacketMetadata::<I>::new_from_icmp_error(
1515 I::DST_IP,
1516 I::SRC_IP,
1517 I::DST_PORT,
1518 I::SRC_PORT,
1519 TransportProtocol::Udp,
1520 );
1521
1522 let connection =
1523 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1524 .unwrap();
1525 let mut connection = match connection_kind {
1526 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1527 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1528 };
1529
1530 assert_matches!(
1531 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1532 Ok(ConnectionUpdateAction::NoAction)
1533 );
1534 let state = connection.state();
1535 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1536 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1537
1538 bindings_ctx.sleep(Duration::from_secs(1));
1541 assert_matches!(
1542 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1543 Ok(ConnectionUpdateAction::NoAction)
1544 );
1545 let state = connection.state();
1546 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1547 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1548 }
1549
1550 #[ip_test(I)]
1551 fn skip_connection_creation_for_icmp_error<I: IpExt + TestIpExt>() {
1552 let mut bindings_ctx = FakeBindingsCtx::new();
1553 bindings_ctx.sleep(Duration::from_secs(1));
1554 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1555
1556 let packet = PacketMetadata::<I>::new_from_icmp_error(
1557 I::DST_IP,
1558 I::SRC_IP,
1559 I::DST_PORT,
1560 I::SRC_PORT,
1561 TransportProtocol::Udp,
1562 );
1563
1564 assert!(
1567 table
1568 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1569 .expect("packet should be valid")
1570 .is_none()
1571 );
1572 assert!(!table.contains_tuple(&packet.tuple()));
1573 }
1574
1575 #[ip_test(I)]
1576 fn table_get_exclusive_connection_and_finalize_shared<I: IpExt + TestIpExt>() {
1577 let mut bindings_ctx = FakeBindingsCtx::new();
1578 bindings_ctx.sleep(Duration::from_secs(1));
1579 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1580
1581 let packet = PacketMetadata::<I>::new(
1582 I::SRC_IP,
1583 I::DST_IP,
1584 TransportProtocol::Udp,
1585 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1586 );
1587
1588 let reply_packet = PacketMetadata::<I>::new(
1589 I::DST_IP,
1590 I::SRC_IP,
1591 TransportProtocol::Udp,
1592 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1593 );
1594
1595 let original_tuple = packet.tuple();
1596 let reply_tuple = reply_packet.tuple();
1597
1598 let (conn, dir) = table
1599 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1600 .expect("packet should be valid")
1601 .expect("connection should be present");
1602 let state = conn.state();
1603 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1604 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1605
1606 assert_matches!(conn, Connection::Exclusive(_));
1610 assert_eq!(dir, ConnectionDirection::Original);
1611 assert!(!table.contains_tuple(&original_tuple));
1612 assert!(!table.contains_tuple(&reply_tuple));
1613
1614 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
1616 assert!(table.contains_tuple(&original_tuple));
1617 assert!(table.contains_tuple(&reply_tuple));
1618
1619 bindings_ctx.sleep(Duration::from_secs(1));
1622 let (conn, dir) = table
1623 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1624 .expect("packet should be valid")
1625 .expect("connection should be present");
1626 assert_eq!(dir, ConnectionDirection::Original);
1627 let state = conn.state();
1628 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1629 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1630 let conn = assert_matches!(conn, Connection::Shared(conn) => conn);
1631
1632 bindings_ctx.sleep(Duration::from_secs(1));
1633 let (reply_conn, dir) = table
1634 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
1635 .expect("packet should be valid")
1636 .expect("connection should be present");
1637 assert_eq!(dir, ConnectionDirection::Reply);
1638 let state = reply_conn.state();
1639 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1640 assert_eq!(state.last_packet_time.offset, Duration::from_secs(3));
1641 let reply_conn = assert_matches!(reply_conn, Connection::Shared(conn) => conn);
1642
1643 assert!(Arc::ptr_eq(&conn, &reply_conn));
1645
1646 let (conn, _dir) = table
1648 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1649 .expect("packet should be valid")
1650 .unwrap();
1651 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
1652 assert!(table.contains_tuple(&original_tuple));
1653 assert!(table.contains_tuple(&reply_tuple));
1654 }
1655
1656 #[ip_test(I)]
1657 fn table_conflict<I: IpExt + TestIpExt>() {
1658 let mut bindings_ctx = FakeBindingsCtx::new();
1659 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1660
1661 let original_packet = PacketMetadata::<I>::new(
1662 I::SRC_IP,
1663 I::DST_IP,
1664 TransportProtocol::Udp,
1665 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1666 );
1667
1668 let nated_original_packet = PacketMetadata::<I>::new(
1669 I::SRC_IP,
1670 I::DST_IP,
1671 TransportProtocol::Udp,
1672 TransportPacketData::Generic { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT + 1 },
1673 );
1674
1675 let conn1 = Connection::Exclusive(
1676 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1677 &bindings_ctx,
1678 &original_packet,
1679 )
1680 .unwrap(),
1681 );
1682
1683 let mut conn2 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1686 &bindings_ctx,
1687 &original_packet,
1688 )
1689 .unwrap();
1690 conn2.inner.original_tuple = nated_original_packet.tuple();
1691 let conn2 = Connection::Exclusive(conn2);
1692
1693 let mut conn3 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1696 &bindings_ctx,
1697 &original_packet,
1698 )
1699 .unwrap();
1700 conn3.inner.reply_tuple = nated_original_packet.tuple().invert();
1701 let conn3 = Connection::Exclusive(conn3);
1702
1703 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn1), Ok((true, Some(_))));
1704 assert_matches!(
1705 table.finalize_connection(&mut bindings_ctx, conn2),
1706 Err(FinalizeConnectionError::Conflict)
1707 );
1708 assert_matches!(
1709 table.finalize_connection(&mut bindings_ctx, conn3),
1710 Err(FinalizeConnectionError::Conflict)
1711 );
1712 }
1713
1714 #[ip_test(I)]
1715 fn table_conflict_identical_connection<
1716 I: IpExt + crate::packets::testutil::internal::TestIpExt,
1717 >() {
1718 let mut bindings_ctx = FakeBindingsCtx::new();
1719 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1720
1721 let original_packet = PacketMetadata::<I>::new(
1722 I::SRC_IP,
1723 I::DST_IP,
1724 TransportProtocol::Udp,
1725 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1726 );
1727
1728 let conn = Connection::Exclusive(
1732 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1733 &bindings_ctx,
1734 &original_packet,
1735 )
1736 .unwrap(),
1737 );
1738 let finalized = assert_matches!(
1739 table.finalize_connection(&mut bindings_ctx, conn),
1740 Ok((true, Some(conn))) => conn
1741 );
1742
1743 let conn = Connection::Exclusive(
1744 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1745 &bindings_ctx,
1746 &original_packet,
1747 )
1748 .unwrap(),
1749 );
1750 let conn = assert_matches!(
1751 table.finalize_connection(&mut bindings_ctx, conn),
1752 Ok((false, Some(conn))) => conn
1753 );
1754 assert!(Arc::ptr_eq(&finalized, &conn));
1755 }
1756
1757 #[derive(Copy, Clone)]
1758 enum GcTrigger {
1759 Direct,
1761 Timer,
1763 }
1764
1765 #[ip_test(I)]
1766 #[test_case(GcTrigger::Direct)]
1767 #[test_case(GcTrigger::Timer)]
1768 fn garbage_collection<I: TestIpExt>(gc_trigger: GcTrigger) {
1769 fn perform_gc<I: TestIpExt>(
1770 core_ctx: &mut FakeCtx<I>,
1771 bindings_ctx: &mut FakeBindingsCtx<I>,
1772 gc_trigger: GcTrigger,
1773 ) {
1774 match gc_trigger {
1775 GcTrigger::Direct => core_ctx.conntrack().perform_gc(bindings_ctx),
1776 GcTrigger::Timer => {
1777 for timer in bindings_ctx
1778 .trigger_timers_until_instant(bindings_ctx.timer_ctx.instant.time, core_ctx)
1779 {
1780 assert_matches!(timer, FilterTimerId::ConntrackGc(_));
1781 }
1782 }
1783 }
1784 }
1785
1786 let mut bindings_ctx = FakeBindingsCtx::new();
1787 let mut core_ctx = FakeCtx::with_ip_routines(&mut bindings_ctx, IpRoutines::default());
1788
1789 let first_packet = PacketMetadata::<I>::new(
1790 I::SRC_IP,
1791 I::DST_IP,
1792 TransportProtocol::Udp,
1793 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1794 );
1795
1796 let second_packet = PacketMetadata::<I>::new(
1797 I::SRC_IP,
1798 I::DST_IP,
1799 TransportProtocol::Udp,
1800 TransportPacketData::Generic { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT },
1801 );
1802 let second_packet_reply = PacketMetadata::<I>::new(
1803 I::DST_IP,
1804 I::SRC_IP,
1805 TransportProtocol::Udp,
1806 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT + 1 },
1807 );
1808
1809 let first_tuple = first_packet.tuple();
1810 let first_tuple_reply = first_tuple.clone().invert();
1811 let second_tuple = second_packet.tuple();
1812 let second_tuple_reply = second_packet_reply.tuple();
1813
1814 let (conn, _dir) = core_ctx
1816 .conntrack()
1817 .get_connection_for_packet_and_update(&bindings_ctx, first_packet)
1818 .expect("packet should be valid")
1819 .expect("packet should be trackable");
1820 assert_matches!(
1821 core_ctx
1822 .conntrack()
1823 .finalize_connection(&mut bindings_ctx, conn)
1824 .expect("connection finalize should succeed"),
1825 (true, Some(_))
1826 );
1827 let (conn, _dir) = core_ctx
1828 .conntrack()
1829 .get_connection_for_packet_and_update(&bindings_ctx, second_packet)
1830 .expect("packet should be valid")
1831 .expect("packet should be trackable");
1832 assert_matches!(
1833 core_ctx
1834 .conntrack()
1835 .finalize_connection(&mut bindings_ctx, conn)
1836 .expect("connection finalize should succeed"),
1837 (true, Some(_))
1838 );
1839 assert!(core_ctx.conntrack().contains_tuple(&first_tuple));
1840 assert!(core_ctx.conntrack().contains_tuple(&second_tuple));
1841 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1842
1843 bindings_ctx.sleep(GC_INTERVAL);
1846 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1847 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1848 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1849 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1850 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1851 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1852
1853 let (conn, _dir) = core_ctx
1855 .conntrack()
1856 .get_connection_for_packet_and_update(&bindings_ctx, second_packet_reply)
1857 .expect("packet should be valid")
1858 .expect("packet should be trackable");
1859 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1860 assert_matches!(
1861 core_ctx
1862 .conntrack()
1863 .finalize_connection(&mut bindings_ctx, conn)
1864 .expect("connection finalize should succeed"),
1865 (false, Some(_))
1866 );
1867 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1868 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1869 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1870 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1871 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1872
1873 bindings_ctx.sleep(GC_INTERVAL);
1883 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1884 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1885 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1886 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1887 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1888 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1889
1890 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - 2 * GC_INTERVAL);
1894 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1895 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1896 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1897 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1898 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1899 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 2);
1900
1901 bindings_ctx.sleep(GC_INTERVAL);
1905 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1906 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1907 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1908 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), false);
1909 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), false);
1910 assert!(core_ctx.conntrack().inner.lock().table.is_empty());
1911 }
1912
1913 fn fill_table<I, E, BC>(
1914 bindings_ctx: &mut BC,
1915 table: &Table<I, E, BC>,
1916 entries: impl Iterator<Item = u32>,
1917 establishment_lifecycle: EstablishmentLifecycle,
1918 ) where
1919 I: IpExt + TestIpExt,
1920 E: Debug + Default + Send + Sync + PartialEq + CompatibleWith + 'static,
1921 BC: FilterBindingsTypes + TimerContext,
1922 {
1923 for i in entries {
1924 let (packet, reply_packet) = make_test_udp_packets(i);
1925 let packet = packet.conntrack_packet().unwrap();
1926 let reply_packet = reply_packet.conntrack_packet().unwrap();
1927
1928 let (conn, _dir) = table
1929 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1930 .expect("packet should be valid")
1931 .expect("packet should be trackable");
1932 assert_matches!(
1933 table
1934 .finalize_connection(bindings_ctx, conn)
1935 .expect("connection finalize should succeed"),
1936 (true, Some(_))
1937 );
1938
1939 if establishment_lifecycle >= EstablishmentLifecycle::SeenReply {
1940 let (conn, _dir) = table
1941 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet.clone())
1942 .expect("packet should be valid")
1943 .expect("packet should be trackable");
1944 assert_matches!(
1945 table
1946 .finalize_connection(bindings_ctx, conn)
1947 .expect("connection finalize should succeed"),
1948 (false, Some(_))
1949 );
1950
1951 if establishment_lifecycle >= EstablishmentLifecycle::Established {
1952 let (conn, _dir) = table
1953 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1954 .expect("packet should be valid")
1955 .expect("packet should be trackable");
1956 assert_matches!(
1957 table
1958 .finalize_connection(bindings_ctx, conn)
1959 .expect("connection finalize should succeed"),
1960 (false, Some(_))
1961 );
1962 }
1963 }
1964 }
1965 }
1966
1967 #[ip_test(I)]
1968 #[test_case(EstablishmentLifecycle::SeenOriginal; "existing connections unestablished")]
1969 #[test_case(EstablishmentLifecycle::SeenReply; "existing connections partially established")]
1970 #[test_case(EstablishmentLifecycle::Established; "existing connections established")]
1971 fn table_size_limit_evict_less_established<I: IpExt + TestIpExt>(
1972 existing_lifecycle: EstablishmentLifecycle,
1973 ) {
1974 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1975 bindings_ctx.sleep(Duration::from_secs(1));
1976 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1977
1978 fill_table(
1979 &mut bindings_ctx,
1980 &table,
1981 0..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
1982 existing_lifecycle,
1983 );
1984
1985 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
1989
1990 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
1991 let packet = packet.conntrack_packet().unwrap();
1992 let (conn, _dir) = table
1993 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1994 .expect("packet should be valid")
1995 .expect("packet should be trackable");
1996 if existing_lifecycle == EstablishmentLifecycle::Established {
1997 assert_matches!(
2000 table.finalize_connection(&mut bindings_ctx, conn),
2001 Err(FinalizeConnectionError::TableFull)
2002 );
2003
2004 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 - 1).try_into().unwrap());
2007 let packet = packet.conntrack_packet().unwrap();
2008 let (conn, _dir) = table
2009 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2010 .expect("packet should be valid")
2011 .expect("packet should be trackable");
2012 assert_matches!(
2013 table
2014 .finalize_connection(&mut bindings_ctx, conn)
2015 .expect("connection finalize should succeed"),
2016 (false, Some(_))
2017 );
2018 } else {
2019 assert_matches!(
2020 table
2021 .finalize_connection(&mut bindings_ctx, conn)
2022 .expect("connection finalize should succeed"),
2023 (true, Some(_))
2024 );
2025 }
2026 }
2027
2028 #[ip_test(I)]
2029 fn table_size_limit_evict_expired<I: IpExt + TestIpExt>() {
2030 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2031 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2032
2033 let evicted_tuple = {
2035 let (packet, _) = make_test_udp_packets(0);
2036 let packet = packet.conntrack_packet().unwrap();
2037 packet.tuple()
2038 };
2039 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::Established);
2040 bindings_ctx.sleep(Duration::from_secs(1));
2041 fill_table(
2042 &mut bindings_ctx,
2043 &table,
2044 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2045 EstablishmentLifecycle::Established,
2046 );
2047
2048 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2049 assert!(table.contains_tuple(&evicted_tuple));
2050
2051 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2052 let packet = packet.conntrack_packet().unwrap();
2053 let (conn, _dir) = table
2056 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2057 .expect("packet should be valid")
2058 .expect("packet should be trackable");
2059 assert_matches!(
2060 table.finalize_connection(&mut bindings_ctx, conn),
2061 Err(FinalizeConnectionError::TableFull)
2062 );
2063
2064 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - Duration::from_secs(1));
2067 let (conn, _dir) = table
2068 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2069 .expect("packet should be valid")
2070 .expect("packet should be trackable");
2071 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2072 assert!(!table.contains_tuple(&evicted_tuple));
2073 }
2074
2075 #[ip_test(I)]
2076 fn table_size_limit_less_established<I: IpExt + TestIpExt>() {
2077 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2078 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2079
2080 let evicted_tuple = {
2081 let (packet, _) = make_test_udp_packets(0);
2082 let packet = packet.conntrack_packet().unwrap();
2083 packet.tuple()
2084 };
2085 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::SeenOriginal);
2087 bindings_ctx.sleep(Duration::from_secs(1));
2088 fill_table(&mut bindings_ctx, &table, 1..=1, EstablishmentLifecycle::SeenOriginal);
2089 fill_table(
2090 &mut bindings_ctx,
2091 &table,
2092 2..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2093 EstablishmentLifecycle::SeenReply,
2094 );
2095
2096 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2097 assert!(table.contains_tuple(&evicted_tuple));
2098
2099 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2103 let packet = packet.conntrack_packet().unwrap();
2104 let (conn, _dir) = table
2105 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2106 .expect("packet should be valid")
2107 .expect("packet should be trackable");
2108 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2109 assert!(!table.contains_tuple(&evicted_tuple));
2110 }
2111
2112 #[cfg(target_os = "fuchsia")]
2113 #[ip_test(I)]
2114 fn inspect<I: IpExt + TestIpExt>() {
2115 use alloc::string::ToString;
2116 use diagnostics_assertions::assert_data_tree;
2117 use diagnostics_traits::FuchsiaInspector;
2118 use fuchsia_inspect::Inspector;
2119
2120 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2121 bindings_ctx.sleep(Duration::from_secs(1));
2122 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2123
2124 {
2125 let inspector = Inspector::new(Default::default());
2126 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2127 bindings_inspector.delegate_inspectable(&table);
2128
2129 let mut exec = fuchsia_async::TestExecutor::new();
2130
2131 assert_data_tree!(@executor exec, inspector, "root": {
2132 "table_limit_drops": 0u64,
2133 "table_limit_hits": 0u64,
2134 "num_entries": 0u64,
2135 "connections": {},
2136 });
2137 }
2138
2139 let (packet, _) = make_test_udp_packets::<I>(0);
2142 let packet = packet.conntrack_packet().unwrap();
2143 let (conn, _dir) = table
2144 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2145 .expect("packet should be valid")
2146 .expect("packet should be trackable");
2147 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
2148 assert_matches!(
2149 table
2150 .finalize_connection(&mut bindings_ctx, conn)
2151 .expect("connection finalize should succeed"),
2152 (true, Some(_))
2153 );
2154
2155 {
2156 let inspector = Inspector::new(Default::default());
2157 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2158 bindings_inspector.delegate_inspectable(&table);
2159
2160 let mut exec = fuchsia_async::TestExecutor::new();
2161 assert_data_tree!(@executor exec, inspector, "root": {
2162 "table_limit_drops": 0u64,
2163 "table_limit_hits": 0u64,
2164 "num_entries": 2u64,
2165 "connections": {
2166 "0": {
2167 "original_tuple": {
2168 "protocol": "UDP",
2169 "src_addr": I::SRC_IP.to_string(),
2170 "dst_addr": I::DST_IP.to_string(),
2171 "src_port_or_id": 0u64,
2172 "dst_port_or_id": 0u64,
2173 },
2174 "reply_tuple": {
2175 "protocol": "UDP",
2176 "src_addr": I::DST_IP.to_string(),
2177 "dst_addr": I::SRC_IP.to_string(),
2178 "src_port_or_id": 0u64,
2179 "dst_port_or_id": 0u64,
2180 },
2181 "external_data": {},
2182 "established": false,
2183 "last_packet_time": 1_000_000_000u64,
2184 }
2185 },
2186 });
2187 }
2188
2189 fill_table(
2191 &mut bindings_ctx,
2192 &table,
2193 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2194 EstablishmentLifecycle::Established,
2195 );
2196
2197 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2198
2199 let (packet, reply_packet) =
2202 make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2203 let packet = packet.conntrack_packet().unwrap();
2204 let reply_packet = reply_packet.conntrack_packet().unwrap();
2205 let (conn, _dir) = table
2206 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2207 .expect("packet should be valid")
2208 .expect("packet should be trackable");
2209 assert_matches!(
2210 table
2211 .finalize_connection(&mut bindings_ctx, conn)
2212 .expect("connection finalize should succeed"),
2213 (true, Some(_))
2214 );
2215 let (conn, _dir) = table
2216 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2217 .expect("packet should be valid")
2218 .expect("packet should be trackable");
2219 assert_matches!(
2220 table
2221 .finalize_connection(&mut bindings_ctx, conn)
2222 .expect("connection finalize should succeed"),
2223 (false, Some(_))
2224 );
2225 let (conn, _dir) = table
2226 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2227 .expect("packet should be valid")
2228 .expect("packet should be trackable");
2229 assert_matches!(
2230 table
2231 .finalize_connection(&mut bindings_ctx, conn)
2232 .expect("connection finalize should succeed"),
2233 (false, Some(_))
2234 );
2235
2236 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 + 1).try_into().unwrap());
2239 let packet = packet.conntrack_packet().unwrap();
2240 let (conn, _dir) = table
2241 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2242 .expect("packet should be valid")
2243 .expect("packet should be trackable");
2244 assert_matches!(
2245 table.finalize_connection(&mut bindings_ctx, conn),
2246 Err(FinalizeConnectionError::TableFull)
2247 );
2248
2249 {
2250 let inspector = Inspector::new(Default::default());
2251 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2252 bindings_inspector.delegate_inspectable(&table);
2253
2254 let mut exec = fuchsia_async::TestExecutor::new();
2255 assert_data_tree!(@executor exec, inspector, "root": contains {
2256 "table_limit_drops": 1u64,
2257 "table_limit_hits": 2u64,
2258 "num_entries": MAXIMUM_ENTRIES as u64,
2259 });
2260 }
2261 }
2262
2263 #[ip_test(I)]
2264 fn self_connected_socket<I: IpExt + TestIpExt>() {
2265 let mut bindings_ctx = FakeBindingsCtx::new();
2266 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2267
2268 let packet = PacketMetadata::<I>::new(
2269 I::SRC_IP,
2270 I::SRC_IP,
2271 TransportProtocol::Udp,
2272 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::SRC_PORT },
2273 );
2274
2275 let tuple = packet.tuple();
2276 let reply_tuple = tuple.clone().invert();
2277
2278 assert_eq!(tuple, reply_tuple);
2279
2280 let (conn, _dir) = table
2281 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2282 .expect("packet should be valid")
2283 .expect("packet should be trackable");
2284 let state = conn.state();
2285 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
2288
2289 assert_matches!(conn, Connection::Exclusive(_));
2290 assert!(!table.contains_tuple(&tuple));
2291
2292 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2294 assert!(table.contains_tuple(&tuple));
2295
2296 assert_eq!(table.inner.lock().table.len(), 1);
2299
2300 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP);
2301 table.perform_gc(&mut bindings_ctx);
2302
2303 assert!(table.inner.lock().table.is_empty());
2304 }
2305
2306 #[ip_test(I)]
2307 fn remove_entry_on_update<I: IpExt + TestIpExt>() {
2308 let mut bindings_ctx = FakeBindingsCtx::new();
2309 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2310
2311 let original_packet = PacketMetadata::<I>::new(
2312 I::SRC_IP,
2313 I::DST_IP,
2314 TransportProtocol::Tcp,
2315 TransportPacketData::Tcp {
2316 src_port: I::SRC_PORT,
2317 dst_port: I::DST_PORT,
2318 segment: SegmentHeader {
2319 seq: SeqNum::new(1024),
2320 wnd: UnscaledWindowSize::from(16u16),
2321 control: Some(Control::SYN),
2322 ..Default::default()
2323 },
2324 payload_len: 0,
2325 },
2326 );
2327
2328 let reply_packet = PacketMetadata::<I>::new(
2329 I::DST_IP,
2330 I::SRC_IP,
2331 TransportProtocol::Tcp,
2332 TransportPacketData::Tcp {
2333 src_port: I::DST_PORT,
2334 dst_port: I::SRC_PORT,
2335 segment: SegmentHeader {
2336 seq: SeqNum::new(0),
2337 ack: Some(SeqNum::new(1025)),
2338 wnd: UnscaledWindowSize::from(16u16),
2339 control: Some(Control::RST),
2340 ..Default::default()
2341 },
2342 payload_len: 0,
2343 },
2344 );
2345
2346 let tuple = original_packet.tuple();
2347 let reply_tuple = tuple.clone().invert();
2348
2349 let (conn, _dir) = table
2350 .get_connection_for_packet_and_update(&bindings_ctx, original_packet)
2351 .expect("packet should be valid")
2352 .expect("packet should be trackable");
2353 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2354
2355 assert!(table.contains_tuple(&tuple));
2356 assert!(table.contains_tuple(&reply_tuple));
2357
2358 let (conn, _dir) = table
2361 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2362 .expect("packet should be valid")
2363 .expect("packet should be trackable");
2364
2365 assert!(!table.contains_tuple(&tuple));
2366 assert!(!table.contains_tuple(&reply_tuple));
2367 assert!(table.inner.lock().table.is_empty());
2368
2369 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
2371
2372 assert!(!table.contains_tuple(&tuple));
2373 assert!(!table.contains_tuple(&reply_tuple));
2374 assert!(table.inner.lock().table.is_empty());
2375
2376 bindings_ctx.sleep(Duration::from_secs(60 * 60 * 24 * 6));
2378 table.perform_gc(&mut bindings_ctx);
2379 }
2380
2381 #[ip_test(I)]
2382 fn do_not_insert<I: IpExt + TestIpExt>() {
2383 let mut bindings_ctx = FakeBindingsCtx::new();
2384 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2385
2386 let packet = PacketMetadata::<I>::new(
2387 I::SRC_IP,
2388 I::DST_IP,
2389 TransportProtocol::Udp,
2390 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
2391 );
2392
2393 let tuple = packet.tuple();
2394 let reply_tuple = tuple.clone().invert();
2395
2396 let (conn, _dir) = table
2397 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2398 .expect("packet should be valid")
2399 .expect("packet should be trackable");
2400 let mut conn = assert_matches!(conn, Connection::Exclusive(conn) => conn);
2401 conn.do_not_insert = true;
2402 assert_matches!(
2403 table.finalize_connection(&mut bindings_ctx, Connection::Exclusive(conn)),
2404 Ok((false, None))
2405 );
2406
2407 assert!(!table.contains_tuple(&tuple));
2408 assert!(!table.contains_tuple(&reply_tuple));
2409 }
2410}