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::{FilterBindingsContext, 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: IpExt, E, BC: FilterBindingsContext> Table<I, E, BC> {
139 pub(crate) fn new<CC: CoreTimerContext<FilterTimerId<I>, BC>>(bindings_ctx: &mut BC) -> Self {
140 Self {
141 inner: Mutex::new(TableInner {
142 table: HashMap::new(),
143 gc_timer: CC::new_timer(
144 bindings_ctx,
145 FilterTimerId::ConntrackGc(IpVersionMarker::<I>::new()),
146 ),
147 table_limit_hits: 0,
148 table_limit_drops: 0,
149 }),
150 }
151 }
152}
153
154impl<
155 I: IpExt,
156 E: Default + Send + Sync + PartialEq + CompatibleWith + 'static,
157 BC: FilterBindingsContext,
158> Table<I, E, BC>
159{
160 pub(crate) fn finalize_connection(
169 &self,
170 bindings_ctx: &mut BC,
171 connection: Connection<I, E, BC>,
172 ) -> Result<(bool, Option<Arc<ConnectionShared<I, E, BC>>>), FinalizeConnectionError> {
173 let exclusive = match connection {
174 Connection::Exclusive(c) => c,
175 Connection::Shared(inner) => return Ok((false, Some(inner))),
179 };
180
181 if exclusive.do_not_insert {
182 return Ok((false, None));
183 }
184
185 let mut guard = self.inner.lock();
186
187 if guard.table.len() >= MAXIMUM_ENTRIES {
188 guard.table_limit_hits = guard.table_limit_hits.saturating_add(1);
189
190 struct Info<'a, I: IpExt, BT: FilterBindingsTypes> {
191 original_tuple: &'a Tuple<I>,
192 reply_tuple: &'a Tuple<I>,
193 lifecycle: EstablishmentLifecycle,
194 last_seen: BT::Instant,
195 }
196
197 let mut info: Option<Info<'_, I, BC>> = None;
198
199 let now = bindings_ctx.now();
200 for (_, conn) in &guard.table {
211 let state = conn.state.lock();
212 if state.is_expired(now) {
213 info = Some(Info {
214 original_tuple: &conn.inner.original_tuple,
215 reply_tuple: &conn.inner.reply_tuple,
216 lifecycle: state.establishment_lifecycle,
217 last_seen: state.last_packet_time,
218 });
219 break;
220 }
221
222 match state.establishment_lifecycle {
223 EstablishmentLifecycle::SeenOriginal | EstablishmentLifecycle::SeenReply => {
224 match &info {
225 None => {
226 info = Some(Info {
227 original_tuple: &conn.inner.original_tuple,
228 reply_tuple: &conn.inner.reply_tuple,
229 lifecycle: state.establishment_lifecycle,
230 last_seen: state.last_packet_time,
231 })
232 }
233 Some(existing) => {
234 if state.establishment_lifecycle < existing.lifecycle
235 || (state.establishment_lifecycle == existing.lifecycle
236 && state.last_packet_time < existing.last_seen)
237 {
238 info = Some(Info {
239 original_tuple: &conn.inner.original_tuple,
240 reply_tuple: &conn.inner.reply_tuple,
241 lifecycle: state.establishment_lifecycle,
242 last_seen: state.last_packet_time,
243 })
244 }
245 }
246 }
247 }
248 EstablishmentLifecycle::Established => {}
249 }
250 }
251
252 if let Some(Info { original_tuple, reply_tuple, .. }) = info {
253 let original_tuple = original_tuple.clone();
254 let reply_tuple = reply_tuple.clone();
255
256 assert!(guard.table.remove(&original_tuple).is_some());
257 if original_tuple != reply_tuple {
258 assert!(guard.table.remove(&reply_tuple).is_some());
259 }
260 } else {
261 guard.table_limit_drops = guard.table_limit_drops.saturating_add(1);
262 return Err(FinalizeConnectionError::TableFull);
263 }
264 }
265
266 if guard.table.contains_key(&exclusive.inner.original_tuple)
274 || guard.table.contains_key(&exclusive.inner.reply_tuple)
275 {
276 let conn = if let Some(conn) = guard.table.get(&exclusive.inner.original_tuple) {
289 conn
290 } else {
291 guard
292 .table
293 .get(&exclusive.inner.reply_tuple)
294 .expect("checked that tuple is in table and table is locked")
295 };
296 if conn.compatible_with(&exclusive) {
297 return Ok((false, Some(conn.clone())));
298 }
299
300 Err(FinalizeConnectionError::Conflict)
302 } else {
303 let shared = exclusive.make_shared();
304 let clone = Arc::clone(&shared);
305
306 let res = guard.table.insert(shared.inner.original_tuple.clone(), shared.clone());
307 debug_assert!(res.is_none());
308
309 if shared.inner.reply_tuple != shared.inner.original_tuple {
310 let res = guard.table.insert(shared.inner.reply_tuple.clone(), shared);
311 debug_assert!(res.is_none());
312 }
313
314 if bindings_ctx.scheduled_instant(&mut guard.gc_timer).is_none() {
320 schedule_gc(bindings_ctx, &mut guard.gc_timer);
321 }
322
323 Ok((true, Some(clone)))
324 }
325 }
326}
327
328impl<I: IpExt, E: Default, BC: FilterBindingsContext> Table<I, E, BC> {
329 pub(crate) fn get_connection_for_packet_and_update(
339 &self,
340 bindings_ctx: &BC,
341 packet: PacketMetadata<I>,
342 ) -> Result<Option<(Connection<I, E, BC>, ConnectionDirection)>, GetConnectionError<I, E, BC>>
343 {
344 let tuple = packet.tuple();
345 let mut connection = match self.inner.lock().table.get(&tuple) {
346 Some(connection) => Connection::Shared(connection.clone()),
347 None => match ConnectionExclusive::from_deconstructed_packet(bindings_ctx, &packet) {
348 None => return Ok(None),
349 Some(c) => Connection::Exclusive(c),
350 },
351 };
352
353 let direction = connection
354 .direction(&tuple)
355 .expect("tuple must match connection as we just looked up connection by tuple");
356
357 match connection.update(bindings_ctx, &packet, direction) {
358 Ok(ConnectionUpdateAction::NoAction) => Ok(Some((connection, direction))),
359 Ok(ConnectionUpdateAction::RemoveEntry) => match connection {
360 Connection::Exclusive(mut conn) => {
361 conn.do_not_insert = true;
362 Ok(Some((Connection::Exclusive(conn), direction)))
363 }
364 Connection::Shared(conn) => {
365 let mut guard = self.inner.lock();
369 let _ = guard.table.remove(&conn.inner.original_tuple);
370 let _ = guard.table.remove(&conn.inner.reply_tuple);
371
372 Ok(Some((Connection::Shared(conn), direction)))
373 }
374 },
375 Err(ConnectionUpdateError::InvalidPacket) => {
376 Err(GetConnectionError::InvalidPacket(connection, direction))
377 }
378 }
379 }
380
381 pub(crate) fn perform_gc(&self, bindings_ctx: &mut BC) {
382 let now = bindings_ctx.now();
383 let mut guard = self.inner.lock();
384
385 let to_remove: Vec<_> = guard
394 .table
395 .iter()
396 .filter_map(|(tuple, conn)| {
397 if *tuple == conn.inner.original_tuple && conn.is_expired(now) {
398 Some((conn.inner.original_tuple.clone(), conn.inner.reply_tuple.clone()))
399 } else {
400 None
401 }
402 })
403 .collect();
404
405 for (original_tuple, reply_tuple) in to_remove {
406 assert!(guard.table.remove(&original_tuple).is_some());
407 if reply_tuple != original_tuple {
408 assert!(guard.table.remove(&reply_tuple).is_some());
409 }
410 }
411
412 if !guard.table.is_empty() {
418 schedule_gc(bindings_ctx, &mut guard.gc_timer);
419 }
420 }
421}
422
423impl<I: IpExt, E: Inspectable, BT: FilterBindingsTypes> Inspectable for Table<I, E, BT> {
424 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
425 let guard = self.inner.lock();
426
427 inspector.record_usize("num_entries", guard.table.len());
428 inspector.record_uint("table_limit_hits", guard.table_limit_hits);
429 inspector.record_uint("table_limit_drops", guard.table_limit_drops);
430
431 inspector.record_child("connections", |inspector| {
432 guard
433 .table
434 .iter()
435 .filter_map(|(tuple, connection)| {
436 if *tuple == connection.inner.original_tuple { Some(connection) } else { None }
437 })
438 .for_each(|connection| {
439 inspector.record_unnamed_child(|inspector| {
440 inspector.delegate_inspectable(connection.as_ref())
441 });
442 });
443 });
444 }
445}
446
447#[derive(Debug, Clone, PartialEq, Eq, Hash, GenericOverIp)]
449#[generic_over_ip(I, Ip)]
450pub struct Tuple<I: IpExt> {
451 pub protocol: TransportProtocol,
453 pub src_addr: I::Addr,
455 pub dst_addr: I::Addr,
457 pub src_port_or_id: u16,
459 pub dst_port_or_id: u16,
461}
462
463impl<I: IpExt> Tuple<I> {
464 fn new(
465 src_addr: I::Addr,
466 dst_addr: I::Addr,
467 src_port_or_id: u16,
468 dst_port_or_id: u16,
469 protocol: TransportProtocol,
470 ) -> Self {
471 Self { protocol, src_addr, dst_addr, src_port_or_id, dst_port_or_id }
472 }
473
474 pub(crate) fn invert(self) -> Tuple<I> {
480 Self {
483 protocol: self.protocol,
484 src_addr: self.dst_addr,
485 dst_addr: self.src_addr,
486 src_port_or_id: self.dst_port_or_id,
487 dst_port_or_id: self.src_port_or_id,
488 }
489 }
490}
491
492impl<I: IpExt> Inspectable for Tuple<I> {
493 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
494 inspector.record_debug("protocol", self.protocol);
495 inspector.record_ip_addr("src_addr", self.src_addr);
496 inspector.record_ip_addr("dst_addr", self.dst_addr);
497 inspector.record_usize("src_port_or_id", self.src_port_or_id);
498 inspector.record_usize("dst_port_or_id", self.dst_port_or_id);
499 }
500}
501
502#[derive(Debug, Copy, Clone, PartialEq, Eq)]
504pub enum ConnectionDirection {
505 Original,
508
509 Reply,
512}
513
514#[derive(Debug)]
516pub(crate) enum FinalizeConnectionError {
517 Conflict,
520
521 TableFull,
523}
524
525#[derive(Debug, PartialEq, Eq)]
527enum ConnectionUpdateAction {
528 NoAction,
530
531 RemoveEntry,
533}
534
535#[derive(Debug, PartialEq, Eq)]
537enum ConnectionUpdateError {
538 InvalidPacket,
541}
542
543#[derive(Derivative)]
545#[derivative(Debug(bound = "E: Debug"))]
546pub(crate) enum GetConnectionError<I: IpExt, E, BT: FilterBindingsTypes> {
547 InvalidPacket(Connection<I, E, BT>, ConnectionDirection),
549}
550
551#[derive(Derivative)]
554#[derivative(Debug(bound = "E: Debug"))]
555pub enum Connection<I: IpExt, E, BT: FilterBindingsTypes> {
556 Exclusive(ConnectionExclusive<I, E, BT>),
559
560 Shared(Arc<ConnectionShared<I, E, BT>>),
563}
564
565#[derive(Debug)]
568pub enum WeakConnectionError {
569 EntryRemoved,
571 InvalidEntry,
574}
575
576#[derive(Debug, Clone)]
586pub struct WeakConnection(pub(crate) Weak<dyn Any + Send + Sync>);
587
588impl WeakConnection {
589 pub fn new<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
592 conn: &Connection<I, E, BT>,
593 ) -> Option<Self> {
594 let shared = match conn {
595 Connection::Exclusive(_) => return None,
596 Connection::Shared(shared) => shared,
597 };
598 let weak = Arc::downgrade(shared);
599 Some(Self(weak))
600 }
601
602 pub fn into_inner<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
611 self,
612 ) -> Result<Connection<I, E, BT>, WeakConnectionError> {
613 let Self(inner) = self;
614 let shared = inner
615 .upgrade()
616 .ok_or(WeakConnectionError::EntryRemoved)?
617 .downcast()
618 .map_err(|_err: Arc<_>| WeakConnectionError::InvalidEntry)?;
619 Ok(Connection::Shared(shared))
620 }
621}
622
623impl<I: IpExt, E, BT: FilterBindingsTypes> Connection<I, E, BT> {
624 pub fn original_tuple(&self) -> &Tuple<I> {
626 match self {
627 Connection::Exclusive(c) => &c.inner.original_tuple,
628 Connection::Shared(c) => &c.inner.original_tuple,
629 }
630 }
631
632 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
634 match self {
635 Connection::Exclusive(c) => &c.inner.reply_tuple,
636 Connection::Shared(c) => &c.inner.reply_tuple,
637 }
638 }
639
640 pub fn external_data(&self) -> &E {
642 match self {
643 Connection::Exclusive(c) => &c.inner.external_data,
644 Connection::Shared(c) => &c.inner.external_data,
645 }
646 }
647
648 pub(crate) fn direction(&self, tuple: &Tuple<I>) -> Option<ConnectionDirection> {
651 let (original, reply) = match self {
652 Connection::Exclusive(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
653 Connection::Shared(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
654 };
655
656 if tuple == reply {
664 Some(ConnectionDirection::Reply)
665 } else if tuple == original {
666 Some(ConnectionDirection::Original)
667 } else {
668 None
669 }
670 }
671
672 #[allow(dead_code)]
674 pub(crate) fn state(&self) -> ConnectionState<BT> {
675 match self {
676 Connection::Exclusive(c) => c.state.clone(),
677 Connection::Shared(c) => c.state.lock().clone(),
678 }
679 }
680}
681
682impl<I: IpExt, E, BC: FilterBindingsContext> Connection<I, E, BC> {
683 fn update(
684 &mut self,
685 bindings_ctx: &BC,
686 packet: &PacketMetadata<I>,
687 direction: ConnectionDirection,
688 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
689 match packet {
690 PacketMetadata::Full { transport_data, .. } => {
691 let now = bindings_ctx.now();
692 match self {
693 Connection::Exclusive(c) => c.state.update(direction, &transport_data, now),
694 Connection::Shared(c) => c.state.lock().update(direction, &transport_data, now),
695 }
696 }
697 PacketMetadata::IcmpError(_) => Ok(ConnectionUpdateAction::NoAction),
698 }
699 }
700}
701
702#[derive(Derivative)]
704#[derivative(Debug(bound = "E: Debug"), PartialEq(bound = "E: PartialEq"))]
705pub struct ConnectionCommon<I: IpExt, E> {
706 pub(crate) original_tuple: Tuple<I>,
709
710 pub(crate) reply_tuple: Tuple<I>,
713
714 pub(crate) external_data: E,
718}
719
720impl<I: IpExt, E: Inspectable> Inspectable for ConnectionCommon<I, E> {
721 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
722 inspector.record_child("original_tuple", |inspector| {
723 inspector.delegate_inspectable(&self.original_tuple);
724 });
725
726 inspector.record_child("reply_tuple", |inspector| {
727 inspector.delegate_inspectable(&self.reply_tuple);
728 });
729
730 inspector.record_child("external_data", |inspector| {
734 inspector.delegate_inspectable(&self.external_data);
735 });
736 }
737}
738
739#[derive(Debug, Clone)]
740enum ProtocolState {
741 Tcp(tcp::Connection),
742 Udp,
743 Other,
744}
745
746impl ProtocolState {
747 fn update(
748 &mut self,
749 dir: ConnectionDirection,
750 transport_data: &TransportPacketData,
751 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
752 match self {
753 ProtocolState::Tcp(tcp_conn) => {
754 let (segment, payload_len) = assert_matches!(
755 transport_data,
756 TransportPacketData::Tcp { segment, payload_len, .. } => (segment, payload_len)
757 );
758 tcp_conn.update(&segment, *payload_len, dir)
759 }
760 ProtocolState::Udp | ProtocolState::Other => Ok(ConnectionUpdateAction::NoAction),
761 }
762 }
763}
764
765#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
776enum EstablishmentLifecycle {
777 SeenOriginal,
778 SeenReply,
779 Established,
780}
781
782impl EstablishmentLifecycle {
783 fn update(self, dir: ConnectionDirection) -> Self {
784 match self {
785 EstablishmentLifecycle::SeenOriginal => match dir {
786 ConnectionDirection::Original => self,
787 ConnectionDirection::Reply => EstablishmentLifecycle::SeenReply,
788 },
789 EstablishmentLifecycle::SeenReply => match dir {
790 ConnectionDirection::Original => EstablishmentLifecycle::Established,
791 ConnectionDirection::Reply => self,
792 },
793 EstablishmentLifecycle::Established => self,
794 }
795 }
796}
797
798#[derive(Derivative)]
800#[derivative(Clone(bound = ""), Debug(bound = ""))]
801pub(crate) struct ConnectionState<BT: FilterBindingsTypes> {
802 last_packet_time: BT::Instant,
805
806 establishment_lifecycle: EstablishmentLifecycle,
808
809 protocol_state: ProtocolState,
811}
812
813impl<BT: FilterBindingsTypes> ConnectionState<BT> {
814 fn update(
815 &mut self,
816 dir: ConnectionDirection,
817 transport_data: &TransportPacketData,
818 now: BT::Instant,
819 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
820 if self.last_packet_time < now {
821 self.last_packet_time = now;
822 }
823
824 self.establishment_lifecycle = self.establishment_lifecycle.update(dir);
825
826 self.protocol_state.update(dir, transport_data)
827 }
828
829 fn is_expired(&self, now: BT::Instant) -> bool {
830 let duration = now.saturating_duration_since(self.last_packet_time);
831
832 let expiry_duration = match &self.protocol_state {
833 ProtocolState::Tcp(tcp_conn) => tcp_conn.expiry_duration(self.establishment_lifecycle),
834 ProtocolState::Udp => CONNECTION_EXPIRY_TIME_UDP,
835 ProtocolState::Other => CONNECTION_EXPIRY_OTHER,
840 };
841
842 duration >= expiry_duration
843 }
844}
845
846impl<BT: FilterBindingsTypes> Inspectable for ConnectionState<BT> {
847 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
848 inspector.record_bool(
849 "established",
850 match self.establishment_lifecycle {
851 EstablishmentLifecycle::SeenOriginal | EstablishmentLifecycle::SeenReply => false,
852 EstablishmentLifecycle::Established => true,
853 },
854 );
855 inspector.record_inspectable_value("last_packet_time", &self.last_packet_time);
856 }
857}
858
859#[derive(Derivative)]
866#[derivative(Debug(bound = "E: Debug"))]
867pub struct ConnectionExclusive<I: IpExt, E, BT: FilterBindingsTypes> {
868 pub(crate) inner: ConnectionCommon<I, E>,
869 pub(crate) state: ConnectionState<BT>,
870
871 do_not_insert: bool,
876}
877
878impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionExclusive<I, E, BT> {
879 fn make_shared(self) -> Arc<ConnectionShared<I, E, BT>> {
882 Arc::new(ConnectionShared { inner: self.inner, state: Mutex::new(self.state) })
883 }
884
885 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
886 &self.inner.reply_tuple
887 }
888
889 pub(crate) fn rewrite_reply_dst_addr(&mut self, addr: I::Addr) {
890 self.inner.reply_tuple.dst_addr = addr;
891 }
892
893 pub(crate) fn rewrite_reply_src_addr(&mut self, addr: I::Addr) {
894 self.inner.reply_tuple.src_addr = addr;
895 }
896
897 pub(crate) fn rewrite_reply_src_port_or_id(&mut self, port_or_id: u16) {
898 self.inner.reply_tuple.src_port_or_id = port_or_id;
899 match self.inner.reply_tuple.protocol {
900 TransportProtocol::Icmp => {
901 self.inner.reply_tuple.dst_port_or_id = port_or_id;
909 }
910 TransportProtocol::Tcp | TransportProtocol::Udp | TransportProtocol::Other(_) => {}
911 }
912 }
913
914 pub(crate) fn rewrite_reply_dst_port_or_id(&mut self, port_or_id: u16) {
915 self.inner.reply_tuple.dst_port_or_id = port_or_id;
916 match self.inner.reply_tuple.protocol {
917 TransportProtocol::Icmp => {
918 self.inner.reply_tuple.src_port_or_id = port_or_id;
926 }
927 TransportProtocol::Tcp | TransportProtocol::Udp | TransportProtocol::Other(_) => {}
928 }
929 }
930}
931
932impl<I: IpExt, E: Default, BC: FilterBindingsContext> ConnectionExclusive<I, E, BC> {
933 pub(crate) fn from_deconstructed_packet(
934 bindings_ctx: &BC,
935 packet_metadata: &PacketMetadata<I>,
936 ) -> Option<Self> {
937 let (tuple, transport_data) = match packet_metadata {
938 PacketMetadata::Full { tuple, transport_data } => (tuple, transport_data),
939 PacketMetadata::IcmpError(_) => return None,
940 };
941
942 let reply_tuple = tuple.clone().invert();
943 let self_connected = reply_tuple == *tuple;
944
945 Some(Self {
946 inner: ConnectionCommon {
947 original_tuple: tuple.clone(),
948 reply_tuple,
949 external_data: E::default(),
950 },
951 state: ConnectionState {
952 last_packet_time: bindings_ctx.now(),
953 establishment_lifecycle: EstablishmentLifecycle::SeenOriginal,
954 protocol_state: match tuple.protocol {
955 TransportProtocol::Tcp => {
956 let (segment, payload_len) = transport_data
957 .tcp_segment_and_len()
958 .expect("protocol was TCP, so transport data should have TCP info");
959
960 ProtocolState::Tcp(tcp::Connection::new(
961 segment,
962 payload_len,
963 self_connected,
964 )?)
965 }
966 TransportProtocol::Udp => ProtocolState::Udp,
967 TransportProtocol::Icmp | TransportProtocol::Other(_) => ProtocolState::Other,
968 },
969 },
970 do_not_insert: false,
971 })
972 }
973}
974
975#[derive(Derivative)]
981#[derivative(Debug(bound = "E: Debug"))]
982pub struct ConnectionShared<I: IpExt, E, BT: FilterBindingsTypes> {
983 inner: ConnectionCommon<I, E>,
984 state: Mutex<ConnectionState<BT>>,
985}
986
987#[allow(missing_docs)]
989#[derive(Copy, Clone, PartialEq, Eq, Hash, GenericOverIp)]
990#[generic_over_ip()]
991pub enum TransportProtocol {
992 Tcp,
993 Udp,
994 Icmp,
995 Other(u8),
996}
997
998impl From<Ipv4Proto> for TransportProtocol {
999 fn from(value: Ipv4Proto) -> Self {
1000 match value {
1001 Ipv4Proto::Proto(IpProto::Tcp) => TransportProtocol::Tcp,
1002 Ipv4Proto::Proto(IpProto::Udp) => TransportProtocol::Udp,
1003 Ipv4Proto::Icmp => TransportProtocol::Icmp,
1004 v => TransportProtocol::Other(v.into()),
1005 }
1006 }
1007}
1008
1009impl From<Ipv6Proto> for TransportProtocol {
1010 fn from(value: Ipv6Proto) -> Self {
1011 match value {
1012 Ipv6Proto::Proto(IpProto::Tcp) => TransportProtocol::Tcp,
1013 Ipv6Proto::Proto(IpProto::Udp) => TransportProtocol::Udp,
1014 Ipv6Proto::Icmpv6 => TransportProtocol::Icmp,
1015 v => TransportProtocol::Other(v.into()),
1016 }
1017 }
1018}
1019
1020impl From<IpProto> for TransportProtocol {
1021 fn from(value: IpProto) -> Self {
1022 match value {
1023 IpProto::Tcp => TransportProtocol::Tcp,
1024 IpProto::Udp => TransportProtocol::Udp,
1025 v @ IpProto::Reserved => TransportProtocol::Other(v.into()),
1026 }
1027 }
1028}
1029
1030impl Display for TransportProtocol {
1031 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1032 match self {
1033 TransportProtocol::Tcp => write!(f, "TCP"),
1034 TransportProtocol::Udp => write!(f, "UDP"),
1035 TransportProtocol::Icmp => write!(f, "ICMP"),
1036 TransportProtocol::Other(n) => write!(f, "Other({n})"),
1037 }
1038 }
1039}
1040
1041impl Debug for TransportProtocol {
1042 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1043 Display::fmt(&self, f)
1044 }
1045}
1046
1047impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1048 fn is_expired(&self, now: BT::Instant) -> bool {
1049 self.state.lock().is_expired(now)
1050 }
1051}
1052
1053impl<I: IpExt, E: CompatibleWith, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1054 pub(crate) fn compatible_with(&self, conn: &ConnectionExclusive<I, E, BT>) -> bool {
1058 self.inner.original_tuple == conn.inner.original_tuple
1059 && self.inner.reply_tuple == conn.inner.reply_tuple
1060 && self.inner.external_data.compatible_with(&conn.inner.external_data)
1061 }
1062}
1063
1064impl<I: IpExt, E: Inspectable, BT: FilterBindingsTypes> Inspectable for ConnectionShared<I, E, BT> {
1065 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
1066 inspector.delegate_inspectable(&self.inner);
1067 inspector.delegate_inspectable(&*self.state.lock());
1068 }
1069}
1070
1071pub trait CompatibleWith {
1074 fn compatible_with(&self, other: &Self) -> bool;
1077}
1078
1079#[derive(Debug, Clone, PartialEq, Eq)]
1084pub enum PacketMetadata<I: IpExt> {
1085 Full { tuple: Tuple<I>, transport_data: TransportPacketData },
1086 IcmpError(Tuple<I>),
1087}
1088
1089impl<I: IpExt> PacketMetadata<I> {
1090 pub(crate) fn new(
1091 src_addr: I::Addr,
1092 dst_addr: I::Addr,
1093 protocol: TransportProtocol,
1094 transport_data: TransportPacketData,
1095 ) -> Self {
1096 match protocol {
1097 TransportProtocol::Tcp => {
1098 assert_matches!(transport_data, TransportPacketData::Tcp { .. })
1099 }
1100 TransportProtocol::Udp | TransportProtocol::Icmp | TransportProtocol::Other(_) => {
1101 assert_matches!(transport_data, TransportPacketData::Generic { .. })
1102 }
1103 }
1104
1105 Self::Full {
1106 tuple: Tuple::new(
1107 src_addr,
1108 dst_addr,
1109 transport_data.src_port(),
1110 transport_data.dst_port(),
1111 protocol,
1112 ),
1113 transport_data,
1114 }
1115 }
1116
1117 pub(crate) fn new_from_icmp_error(
1118 src_addr: I::Addr,
1119 dst_addr: I::Addr,
1120 src_port: u16,
1121 dst_port: u16,
1122 protocol: TransportProtocol,
1123 ) -> Self {
1124 Self::IcmpError(Tuple::new(src_addr, dst_addr, src_port, dst_port, protocol))
1125 }
1126
1127 pub(crate) fn tuple(&self) -> Tuple<I> {
1128 match self {
1129 PacketMetadata::Full { tuple, .. } => tuple.clone(),
1130 PacketMetadata::IcmpError(tuple) => tuple.clone().invert(),
1158 }
1159 }
1160}
1161
1162#[cfg(test)]
1163pub(crate) mod testutils {
1164 use crate::packets::testutil::internal::{FakeIpPacket, FakeUdpPacket, TestIpExt};
1165
1166 pub(crate) fn make_test_udp_packets<I: TestIpExt>(
1169 index: u32,
1170 ) -> (FakeIpPacket<I, FakeUdpPacket>, FakeIpPacket<I, FakeUdpPacket>) {
1171 let src_port = (index % (u16::MAX as u32)) as u16;
1174 let dst_port = (index / (u16::MAX as u32)) as u16;
1175
1176 let packet = FakeIpPacket::<I, _> {
1177 src_ip: I::SRC_IP,
1178 dst_ip: I::DST_IP,
1179 body: FakeUdpPacket { src_port, dst_port },
1180 };
1181 let reply_packet = FakeIpPacket::<I, _> {
1182 src_ip: I::DST_IP,
1183 dst_ip: I::SRC_IP,
1184 body: FakeUdpPacket { src_port: dst_port, dst_port: src_port },
1185 };
1186
1187 (packet, reply_packet)
1188 }
1189}
1190
1191#[cfg(test)]
1192mod tests {
1193 use assert_matches::assert_matches;
1194 use ip_test_macro::ip_test;
1195 use netstack3_base::testutil::FakeTimerCtxExt;
1196 use netstack3_base::{Control, IntoCoreTimerCtx, SegmentHeader, SeqNum, UnscaledWindowSize};
1197 use test_case::test_case;
1198
1199 use super::testutils::make_test_udp_packets;
1200 use super::*;
1201 use crate::context::testutil::{FakeBindingsCtx, FakeCtx};
1202 use crate::packets::IpPacket;
1203 use crate::packets::testutil::internal::ArbitraryValue;
1204 use crate::state::IpRoutines;
1205 use crate::testutil::TestIpExt;
1206
1207 impl CompatibleWith for () {
1208 fn compatible_with(&self, (): &()) -> bool {
1209 true
1210 }
1211 }
1212
1213 #[test_case(
1214 EstablishmentLifecycle::SeenOriginal,
1215 ConnectionDirection::Original
1216 => EstablishmentLifecycle::SeenOriginal
1217 )]
1218 #[test_case(
1219 EstablishmentLifecycle::SeenOriginal,
1220 ConnectionDirection::Reply
1221 => EstablishmentLifecycle::SeenReply
1222 )]
1223 #[test_case(
1224 EstablishmentLifecycle::SeenReply,
1225 ConnectionDirection::Original
1226 => EstablishmentLifecycle::Established
1227 )]
1228 #[test_case(
1229 EstablishmentLifecycle::SeenReply,
1230 ConnectionDirection::Reply
1231 => EstablishmentLifecycle::SeenReply
1232 )]
1233 #[test_case(
1234 EstablishmentLifecycle::Established,
1235 ConnectionDirection::Original
1236 => EstablishmentLifecycle::Established
1237 )]
1238 #[test_case(
1239 EstablishmentLifecycle::Established,
1240 ConnectionDirection::Reply
1241 => EstablishmentLifecycle::Established
1242 )]
1243 fn establishment_lifecycle_test(
1244 lifecycle: EstablishmentLifecycle,
1245 dir: ConnectionDirection,
1246 ) -> EstablishmentLifecycle {
1247 lifecycle.update(dir)
1248 }
1249
1250 #[ip_test(I)]
1251 #[test_case(TransportProtocol::Udp)]
1252 #[test_case(TransportProtocol::Tcp)]
1253 fn tuple_invert_udp_tcp<I: IpExt + TestIpExt>(protocol: TransportProtocol) {
1254 let orig_tuple = Tuple::<I> {
1255 protocol: protocol,
1256 src_addr: I::SRC_IP,
1257 dst_addr: I::DST_IP,
1258 src_port_or_id: I::SRC_PORT,
1259 dst_port_or_id: I::DST_PORT,
1260 };
1261
1262 let expected = Tuple::<I> {
1263 protocol: protocol,
1264 src_addr: I::DST_IP,
1265 dst_addr: I::SRC_IP,
1266 src_port_or_id: I::DST_PORT,
1267 dst_port_or_id: I::SRC_PORT,
1268 };
1269
1270 let inverted = orig_tuple.invert();
1271
1272 assert_eq!(inverted, expected);
1273 }
1274
1275 #[ip_test(I)]
1276 fn tuple_from_tcp_packet<I: IpExt + TestIpExt>() {
1277 let expected = Tuple::<I> {
1278 protocol: TransportProtocol::Tcp,
1279 src_addr: I::SRC_IP,
1280 dst_addr: I::DST_IP,
1281 src_port_or_id: I::SRC_PORT,
1282 dst_port_or_id: I::DST_PORT,
1283 };
1284
1285 let packet = PacketMetadata::<I>::new(
1286 I::SRC_IP,
1287 I::DST_IP,
1288 TransportProtocol::Tcp,
1289 TransportPacketData::Tcp {
1290 src_port: I::SRC_PORT,
1291 dst_port: I::DST_PORT,
1292 segment: SegmentHeader::arbitrary_value(),
1293 payload_len: 4,
1294 },
1295 );
1296
1297 assert_eq!(packet.tuple(), expected);
1298 }
1299
1300 #[ip_test(I)]
1301 fn connection_from_tuple<I: IpExt + TestIpExt>() {
1302 let bindings_ctx = FakeBindingsCtx::<I>::new();
1303
1304 let packet = PacketMetadata::<I>::new(
1305 I::SRC_IP,
1306 I::DST_IP,
1307 TransportProtocol::Udp,
1308 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1309 );
1310 let original_tuple = packet.tuple();
1311 let reply_tuple = packet.tuple().invert();
1312
1313 let connection =
1314 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1315 .unwrap();
1316
1317 assert_eq!(&connection.inner.original_tuple, &original_tuple);
1318 assert_eq!(&connection.inner.reply_tuple, &reply_tuple);
1319 }
1320
1321 #[ip_test(I)]
1322 fn connection_make_shared_has_same_underlying_info<I: IpExt + TestIpExt>() {
1323 let bindings_ctx = FakeBindingsCtx::<I>::new();
1324
1325 let packet = PacketMetadata::<I>::new(
1326 I::SRC_IP,
1327 I::DST_IP,
1328 TransportProtocol::Udp,
1329 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1330 );
1331 let original_tuple = packet.tuple();
1332 let reply_tuple = original_tuple.clone().invert();
1333
1334 let mut connection =
1335 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1336 connection.inner.external_data = 1234;
1337 let shared = connection.make_shared();
1338
1339 assert_eq!(shared.inner.original_tuple, original_tuple);
1340 assert_eq!(shared.inner.reply_tuple, reply_tuple);
1341 assert_eq!(shared.inner.external_data, 1234);
1342 }
1343
1344 enum ConnectionKind {
1345 Exclusive,
1346 Shared,
1347 }
1348
1349 #[ip_test(I)]
1350 #[test_case(ConnectionKind::Exclusive)]
1351 #[test_case(ConnectionKind::Shared)]
1352 fn connection_getters<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1353 let bindings_ctx = FakeBindingsCtx::<I>::new();
1354
1355 let packet = PacketMetadata::<I>::new(
1356 I::SRC_IP,
1357 I::DST_IP,
1358 TransportProtocol::Udp,
1359 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1360 );
1361 let original_tuple = packet.tuple();
1362 let reply_tuple = original_tuple.clone().invert();
1363
1364 let mut connection =
1365 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1366 connection.inner.external_data = 1234;
1367
1368 let connection = match connection_kind {
1369 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1370 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1371 };
1372
1373 assert_eq!(connection.original_tuple(), &original_tuple);
1374 assert_eq!(connection.reply_tuple(), &reply_tuple);
1375 assert_eq!(connection.external_data(), &1234);
1376 }
1377
1378 #[ip_test(I)]
1379 #[test_case(ConnectionKind::Exclusive)]
1380 #[test_case(ConnectionKind::Shared)]
1381 fn connection_direction<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1382 let bindings_ctx = FakeBindingsCtx::<I>::new();
1383
1384 let packet = PacketMetadata::<I>::new(
1385 I::SRC_IP,
1386 I::DST_IP,
1387 TransportProtocol::Udp,
1388 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1389 );
1390 let original_tuple = packet.tuple();
1391 let reply_tuple = original_tuple.clone().invert();
1392
1393 let mut other_tuple = original_tuple.clone();
1394 other_tuple.src_port_or_id += 1;
1395
1396 let connection: ConnectionExclusive<_, (), _> =
1397 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1398 let connection = match connection_kind {
1399 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1400 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1401 };
1402
1403 assert_matches!(connection.direction(&original_tuple), Some(ConnectionDirection::Original));
1404 assert_matches!(connection.direction(&reply_tuple), Some(ConnectionDirection::Reply));
1405 assert_matches!(connection.direction(&other_tuple), None);
1406 }
1407
1408 #[ip_test(I)]
1409 #[test_case(ConnectionKind::Exclusive)]
1410 #[test_case(ConnectionKind::Shared)]
1411 fn connection_update<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1412 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1413 bindings_ctx.sleep(Duration::from_secs(1));
1414
1415 let packet = PacketMetadata::<I>::new(
1416 I::SRC_IP,
1417 I::DST_IP,
1418 TransportProtocol::Udp,
1419 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1420 );
1421
1422 let reply_packet = PacketMetadata::<I>::new(
1423 I::DST_IP,
1424 I::SRC_IP,
1425 TransportProtocol::Udp,
1426 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1427 );
1428
1429 let connection =
1430 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1431 .unwrap();
1432 let mut connection = match connection_kind {
1433 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1434 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1435 };
1436
1437 assert_matches!(
1438 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1439 Ok(ConnectionUpdateAction::NoAction)
1440 );
1441 let state = connection.state();
1442 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1443 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1444
1445 bindings_ctx.sleep(Duration::from_secs(1));
1448 assert_matches!(
1449 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1450 Ok(ConnectionUpdateAction::NoAction)
1451 );
1452 let state = connection.state();
1453 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1454 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1455 }
1456
1457 #[ip_test(I)]
1458 #[test_case(ConnectionKind::Exclusive)]
1459 #[test_case(ConnectionKind::Shared)]
1460 fn skip_connection_update_for_icmp_error<I: IpExt + TestIpExt>(
1461 connection_kind: ConnectionKind,
1462 ) {
1463 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1464 bindings_ctx.sleep(Duration::from_secs(1));
1465
1466 let packet = PacketMetadata::<I>::new(
1467 I::SRC_IP,
1468 I::DST_IP,
1469 TransportProtocol::Udp,
1470 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1471 );
1472
1473 let reply_packet = PacketMetadata::<I>::new_from_icmp_error(
1474 I::DST_IP,
1475 I::SRC_IP,
1476 I::DST_PORT,
1477 I::SRC_PORT,
1478 TransportProtocol::Udp,
1479 );
1480
1481 let connection =
1482 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1483 .unwrap();
1484 let mut connection = match connection_kind {
1485 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1486 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1487 };
1488
1489 assert_matches!(
1490 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1491 Ok(ConnectionUpdateAction::NoAction)
1492 );
1493 let state = connection.state();
1494 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1495 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1496
1497 bindings_ctx.sleep(Duration::from_secs(1));
1500 assert_matches!(
1501 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1502 Ok(ConnectionUpdateAction::NoAction)
1503 );
1504 let state = connection.state();
1505 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1506 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1507 }
1508
1509 #[ip_test(I)]
1510 fn skip_connection_creation_for_icmp_error<I: IpExt + TestIpExt>() {
1511 let mut bindings_ctx = FakeBindingsCtx::new();
1512 bindings_ctx.sleep(Duration::from_secs(1));
1513 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1514
1515 let packet = PacketMetadata::<I>::new_from_icmp_error(
1516 I::DST_IP,
1517 I::SRC_IP,
1518 I::DST_PORT,
1519 I::SRC_PORT,
1520 TransportProtocol::Udp,
1521 );
1522
1523 assert!(
1526 table
1527 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1528 .expect("packet should be valid")
1529 .is_none()
1530 );
1531 assert!(!table.contains_tuple(&packet.tuple()));
1532 }
1533
1534 #[ip_test(I)]
1535 fn table_get_exclusive_connection_and_finalize_shared<I: IpExt + TestIpExt>() {
1536 let mut bindings_ctx = FakeBindingsCtx::new();
1537 bindings_ctx.sleep(Duration::from_secs(1));
1538 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1539
1540 let packet = PacketMetadata::<I>::new(
1541 I::SRC_IP,
1542 I::DST_IP,
1543 TransportProtocol::Udp,
1544 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1545 );
1546
1547 let reply_packet = PacketMetadata::<I>::new(
1548 I::DST_IP,
1549 I::SRC_IP,
1550 TransportProtocol::Udp,
1551 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1552 );
1553
1554 let original_tuple = packet.tuple();
1555 let reply_tuple = reply_packet.tuple();
1556
1557 let (conn, dir) = table
1558 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1559 .expect("packet should be valid")
1560 .expect("connection should be present");
1561 let state = conn.state();
1562 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1563 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1564
1565 assert_matches!(conn, Connection::Exclusive(_));
1569 assert_eq!(dir, ConnectionDirection::Original);
1570 assert!(!table.contains_tuple(&original_tuple));
1571 assert!(!table.contains_tuple(&reply_tuple));
1572
1573 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
1575 assert!(table.contains_tuple(&original_tuple));
1576 assert!(table.contains_tuple(&reply_tuple));
1577
1578 bindings_ctx.sleep(Duration::from_secs(1));
1581 let (conn, dir) = table
1582 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1583 .expect("packet should be valid")
1584 .expect("connection should be present");
1585 assert_eq!(dir, ConnectionDirection::Original);
1586 let state = conn.state();
1587 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1588 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1589 let conn = assert_matches!(conn, Connection::Shared(conn) => conn);
1590
1591 bindings_ctx.sleep(Duration::from_secs(1));
1592 let (reply_conn, dir) = table
1593 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
1594 .expect("packet should be valid")
1595 .expect("connection should be present");
1596 assert_eq!(dir, ConnectionDirection::Reply);
1597 let state = reply_conn.state();
1598 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1599 assert_eq!(state.last_packet_time.offset, Duration::from_secs(3));
1600 let reply_conn = assert_matches!(reply_conn, Connection::Shared(conn) => conn);
1601
1602 assert!(Arc::ptr_eq(&conn, &reply_conn));
1604
1605 let (conn, _dir) = table
1607 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1608 .expect("packet should be valid")
1609 .unwrap();
1610 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
1611 assert!(table.contains_tuple(&original_tuple));
1612 assert!(table.contains_tuple(&reply_tuple));
1613 }
1614
1615 #[ip_test(I)]
1616 fn table_conflict<I: IpExt + TestIpExt>() {
1617 let mut bindings_ctx = FakeBindingsCtx::new();
1618 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1619
1620 let original_packet = PacketMetadata::<I>::new(
1621 I::SRC_IP,
1622 I::DST_IP,
1623 TransportProtocol::Udp,
1624 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1625 );
1626
1627 let nated_original_packet = PacketMetadata::<I>::new(
1628 I::SRC_IP,
1629 I::DST_IP,
1630 TransportProtocol::Udp,
1631 TransportPacketData::Generic { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT + 1 },
1632 );
1633
1634 let conn1 = Connection::Exclusive(
1635 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1636 &bindings_ctx,
1637 &original_packet,
1638 )
1639 .unwrap(),
1640 );
1641
1642 let mut conn2 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1645 &bindings_ctx,
1646 &original_packet,
1647 )
1648 .unwrap();
1649 conn2.inner.original_tuple = nated_original_packet.tuple();
1650 let conn2 = Connection::Exclusive(conn2);
1651
1652 let mut conn3 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1655 &bindings_ctx,
1656 &original_packet,
1657 )
1658 .unwrap();
1659 conn3.inner.reply_tuple = nated_original_packet.tuple().invert();
1660 let conn3 = Connection::Exclusive(conn3);
1661
1662 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn1), Ok((true, Some(_))));
1663 assert_matches!(
1664 table.finalize_connection(&mut bindings_ctx, conn2),
1665 Err(FinalizeConnectionError::Conflict)
1666 );
1667 assert_matches!(
1668 table.finalize_connection(&mut bindings_ctx, conn3),
1669 Err(FinalizeConnectionError::Conflict)
1670 );
1671 }
1672
1673 #[ip_test(I)]
1674 fn table_conflict_identical_connection<
1675 I: IpExt + crate::packets::testutil::internal::TestIpExt,
1676 >() {
1677 let mut bindings_ctx = FakeBindingsCtx::new();
1678 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1679
1680 let original_packet = PacketMetadata::<I>::new(
1681 I::SRC_IP,
1682 I::DST_IP,
1683 TransportProtocol::Udp,
1684 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1685 );
1686
1687 let conn = Connection::Exclusive(
1691 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1692 &bindings_ctx,
1693 &original_packet,
1694 )
1695 .unwrap(),
1696 );
1697 let finalized = assert_matches!(
1698 table.finalize_connection(&mut bindings_ctx, conn),
1699 Ok((true, Some(conn))) => conn
1700 );
1701
1702 let conn = Connection::Exclusive(
1703 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1704 &bindings_ctx,
1705 &original_packet,
1706 )
1707 .unwrap(),
1708 );
1709 let conn = assert_matches!(
1710 table.finalize_connection(&mut bindings_ctx, conn),
1711 Ok((false, Some(conn))) => conn
1712 );
1713 assert!(Arc::ptr_eq(&finalized, &conn));
1714 }
1715
1716 #[derive(Copy, Clone)]
1717 enum GcTrigger {
1718 Direct,
1720 Timer,
1722 }
1723
1724 #[ip_test(I)]
1725 #[test_case(GcTrigger::Direct)]
1726 #[test_case(GcTrigger::Timer)]
1727 fn garbage_collection<I: TestIpExt>(gc_trigger: GcTrigger) {
1728 fn perform_gc<I: TestIpExt>(
1729 core_ctx: &mut FakeCtx<I>,
1730 bindings_ctx: &mut FakeBindingsCtx<I>,
1731 gc_trigger: GcTrigger,
1732 ) {
1733 match gc_trigger {
1734 GcTrigger::Direct => core_ctx.conntrack().perform_gc(bindings_ctx),
1735 GcTrigger::Timer => {
1736 for timer in bindings_ctx
1737 .trigger_timers_until_instant(bindings_ctx.timer_ctx.instant.time, core_ctx)
1738 {
1739 assert_matches!(timer, FilterTimerId::ConntrackGc(_));
1740 }
1741 }
1742 }
1743 }
1744
1745 let mut bindings_ctx = FakeBindingsCtx::new();
1746 let mut core_ctx = FakeCtx::with_ip_routines(&mut bindings_ctx, IpRoutines::default());
1747
1748 let first_packet = PacketMetadata::<I>::new(
1749 I::SRC_IP,
1750 I::DST_IP,
1751 TransportProtocol::Udp,
1752 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1753 );
1754
1755 let second_packet = PacketMetadata::<I>::new(
1756 I::SRC_IP,
1757 I::DST_IP,
1758 TransportProtocol::Udp,
1759 TransportPacketData::Generic { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT },
1760 );
1761 let second_packet_reply = PacketMetadata::<I>::new(
1762 I::DST_IP,
1763 I::SRC_IP,
1764 TransportProtocol::Udp,
1765 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT + 1 },
1766 );
1767
1768 let first_tuple = first_packet.tuple();
1769 let first_tuple_reply = first_tuple.clone().invert();
1770 let second_tuple = second_packet.tuple();
1771 let second_tuple_reply = second_packet_reply.tuple();
1772
1773 let (conn, _dir) = core_ctx
1775 .conntrack()
1776 .get_connection_for_packet_and_update(&bindings_ctx, first_packet)
1777 .expect("packet should be valid")
1778 .expect("packet should be trackable");
1779 assert_matches!(
1780 core_ctx
1781 .conntrack()
1782 .finalize_connection(&mut bindings_ctx, conn)
1783 .expect("connection finalize should succeed"),
1784 (true, Some(_))
1785 );
1786 let (conn, _dir) = core_ctx
1787 .conntrack()
1788 .get_connection_for_packet_and_update(&bindings_ctx, second_packet)
1789 .expect("packet should be valid")
1790 .expect("packet should be trackable");
1791 assert_matches!(
1792 core_ctx
1793 .conntrack()
1794 .finalize_connection(&mut bindings_ctx, conn)
1795 .expect("connection finalize should succeed"),
1796 (true, Some(_))
1797 );
1798 assert!(core_ctx.conntrack().contains_tuple(&first_tuple));
1799 assert!(core_ctx.conntrack().contains_tuple(&second_tuple));
1800 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1801
1802 bindings_ctx.sleep(GC_INTERVAL);
1805 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1806 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1807 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1808 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1809 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1810 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1811
1812 let (conn, _dir) = core_ctx
1814 .conntrack()
1815 .get_connection_for_packet_and_update(&bindings_ctx, second_packet_reply)
1816 .expect("packet should be valid")
1817 .expect("packet should be trackable");
1818 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1819 assert_matches!(
1820 core_ctx
1821 .conntrack()
1822 .finalize_connection(&mut bindings_ctx, conn)
1823 .expect("connection finalize should succeed"),
1824 (false, Some(_))
1825 );
1826 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1827 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1828 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1829 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1830 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1831
1832 bindings_ctx.sleep(GC_INTERVAL);
1842 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1843 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1844 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1845 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1846 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1847 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1848
1849 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - 2 * GC_INTERVAL);
1853 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1854 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1855 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1856 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1857 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1858 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 2);
1859
1860 bindings_ctx.sleep(GC_INTERVAL);
1864 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1865 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1866 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1867 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), false);
1868 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), false);
1869 assert!(core_ctx.conntrack().inner.lock().table.is_empty());
1870 }
1871
1872 fn fill_table<I, E, BC>(
1873 bindings_ctx: &mut BC,
1874 table: &Table<I, E, BC>,
1875 entries: impl Iterator<Item = u32>,
1876 establishment_lifecycle: EstablishmentLifecycle,
1877 ) where
1878 I: IpExt + TestIpExt,
1879 E: Debug + Default + Send + Sync + PartialEq + CompatibleWith + 'static,
1880 BC: FilterBindingsContext,
1881 {
1882 for i in entries {
1883 let (packet, reply_packet) = make_test_udp_packets(i);
1884 let packet = packet.conntrack_packet().unwrap();
1885 let reply_packet = reply_packet.conntrack_packet().unwrap();
1886
1887 let (conn, _dir) = table
1888 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1889 .expect("packet should be valid")
1890 .expect("packet should be trackable");
1891 assert_matches!(
1892 table
1893 .finalize_connection(bindings_ctx, conn)
1894 .expect("connection finalize should succeed"),
1895 (true, Some(_))
1896 );
1897
1898 if establishment_lifecycle >= EstablishmentLifecycle::SeenReply {
1899 let (conn, _dir) = table
1900 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet.clone())
1901 .expect("packet should be valid")
1902 .expect("packet should be trackable");
1903 assert_matches!(
1904 table
1905 .finalize_connection(bindings_ctx, conn)
1906 .expect("connection finalize should succeed"),
1907 (false, Some(_))
1908 );
1909
1910 if establishment_lifecycle >= EstablishmentLifecycle::Established {
1911 let (conn, _dir) = table
1912 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1913 .expect("packet should be valid")
1914 .expect("packet should be trackable");
1915 assert_matches!(
1916 table
1917 .finalize_connection(bindings_ctx, conn)
1918 .expect("connection finalize should succeed"),
1919 (false, Some(_))
1920 );
1921 }
1922 }
1923 }
1924 }
1925
1926 #[ip_test(I)]
1927 #[test_case(EstablishmentLifecycle::SeenOriginal; "existing connections unestablished")]
1928 #[test_case(EstablishmentLifecycle::SeenReply; "existing connections partially established")]
1929 #[test_case(EstablishmentLifecycle::Established; "existing connections established")]
1930 fn table_size_limit_evict_less_established<I: IpExt + TestIpExt>(
1931 existing_lifecycle: EstablishmentLifecycle,
1932 ) {
1933 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1934 bindings_ctx.sleep(Duration::from_secs(1));
1935 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1936
1937 fill_table(
1938 &mut bindings_ctx,
1939 &table,
1940 0..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
1941 existing_lifecycle,
1942 );
1943
1944 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
1948
1949 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
1950 let packet = packet.conntrack_packet().unwrap();
1951 let (conn, _dir) = table
1952 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1953 .expect("packet should be valid")
1954 .expect("packet should be trackable");
1955 if existing_lifecycle == EstablishmentLifecycle::Established {
1956 assert_matches!(
1959 table.finalize_connection(&mut bindings_ctx, conn),
1960 Err(FinalizeConnectionError::TableFull)
1961 );
1962
1963 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 - 1).try_into().unwrap());
1966 let packet = packet.conntrack_packet().unwrap();
1967 let (conn, _dir) = table
1968 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1969 .expect("packet should be valid")
1970 .expect("packet should be trackable");
1971 assert_matches!(
1972 table
1973 .finalize_connection(&mut bindings_ctx, conn)
1974 .expect("connection finalize should succeed"),
1975 (false, Some(_))
1976 );
1977 } else {
1978 assert_matches!(
1979 table
1980 .finalize_connection(&mut bindings_ctx, conn)
1981 .expect("connection finalize should succeed"),
1982 (true, Some(_))
1983 );
1984 }
1985 }
1986
1987 #[ip_test(I)]
1988 fn table_size_limit_evict_expired<I: IpExt + TestIpExt>() {
1989 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1990 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1991
1992 let evicted_tuple = {
1994 let (packet, _) = make_test_udp_packets(0);
1995 let packet = packet.conntrack_packet().unwrap();
1996 packet.tuple()
1997 };
1998 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::Established);
1999 bindings_ctx.sleep(Duration::from_secs(1));
2000 fill_table(
2001 &mut bindings_ctx,
2002 &table,
2003 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2004 EstablishmentLifecycle::Established,
2005 );
2006
2007 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2008 assert!(table.contains_tuple(&evicted_tuple));
2009
2010 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2011 let packet = packet.conntrack_packet().unwrap();
2012 let (conn, _dir) = table
2015 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2016 .expect("packet should be valid")
2017 .expect("packet should be trackable");
2018 assert_matches!(
2019 table.finalize_connection(&mut bindings_ctx, conn),
2020 Err(FinalizeConnectionError::TableFull)
2021 );
2022
2023 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - Duration::from_secs(1));
2026 let (conn, _dir) = table
2027 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2028 .expect("packet should be valid")
2029 .expect("packet should be trackable");
2030 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2031 assert!(!table.contains_tuple(&evicted_tuple));
2032 }
2033
2034 #[ip_test(I)]
2035 fn table_size_limit_less_established<I: IpExt + TestIpExt>() {
2036 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2037 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2038
2039 let evicted_tuple = {
2040 let (packet, _) = make_test_udp_packets(0);
2041 let packet = packet.conntrack_packet().unwrap();
2042 packet.tuple()
2043 };
2044 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::SeenOriginal);
2046 bindings_ctx.sleep(Duration::from_secs(1));
2047 fill_table(&mut bindings_ctx, &table, 1..=1, EstablishmentLifecycle::SeenOriginal);
2048 fill_table(
2049 &mut bindings_ctx,
2050 &table,
2051 2..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2052 EstablishmentLifecycle::SeenReply,
2053 );
2054
2055 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2056 assert!(table.contains_tuple(&evicted_tuple));
2057
2058 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2062 let packet = packet.conntrack_packet().unwrap();
2063 let (conn, _dir) = table
2064 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2065 .expect("packet should be valid")
2066 .expect("packet should be trackable");
2067 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2068 assert!(!table.contains_tuple(&evicted_tuple));
2069 }
2070
2071 #[cfg(target_os = "fuchsia")]
2072 #[ip_test(I)]
2073 fn inspect<I: IpExt + TestIpExt>() {
2074 use alloc::string::ToString;
2075 use diagnostics_assertions::assert_data_tree;
2076 use diagnostics_traits::FuchsiaInspector;
2077 use fuchsia_inspect::Inspector;
2078
2079 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2080 bindings_ctx.sleep(Duration::from_secs(1));
2081 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2082
2083 {
2084 let inspector = Inspector::new(Default::default());
2085 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2086 bindings_inspector.delegate_inspectable(&table);
2087
2088 let mut exec = fuchsia_async::TestExecutor::new();
2089
2090 assert_data_tree!(@executor exec, inspector, "root": {
2091 "table_limit_drops": 0u64,
2092 "table_limit_hits": 0u64,
2093 "num_entries": 0u64,
2094 "connections": {},
2095 });
2096 }
2097
2098 let (packet, _) = make_test_udp_packets::<I>(0);
2101 let packet = packet.conntrack_packet().unwrap();
2102 let (conn, _dir) = table
2103 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2104 .expect("packet should be valid")
2105 .expect("packet should be trackable");
2106 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
2107 assert_matches!(
2108 table
2109 .finalize_connection(&mut bindings_ctx, conn)
2110 .expect("connection finalize should succeed"),
2111 (true, Some(_))
2112 );
2113
2114 {
2115 let inspector = Inspector::new(Default::default());
2116 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2117 bindings_inspector.delegate_inspectable(&table);
2118
2119 let mut exec = fuchsia_async::TestExecutor::new();
2120 assert_data_tree!(@executor exec, inspector, "root": {
2121 "table_limit_drops": 0u64,
2122 "table_limit_hits": 0u64,
2123 "num_entries": 2u64,
2124 "connections": {
2125 "0": {
2126 "original_tuple": {
2127 "protocol": "UDP",
2128 "src_addr": I::SRC_IP.to_string(),
2129 "dst_addr": I::DST_IP.to_string(),
2130 "src_port_or_id": 0u64,
2131 "dst_port_or_id": 0u64,
2132 },
2133 "reply_tuple": {
2134 "protocol": "UDP",
2135 "src_addr": I::DST_IP.to_string(),
2136 "dst_addr": I::SRC_IP.to_string(),
2137 "src_port_or_id": 0u64,
2138 "dst_port_or_id": 0u64,
2139 },
2140 "external_data": {},
2141 "established": false,
2142 "last_packet_time": 1_000_000_000u64,
2143 }
2144 },
2145 });
2146 }
2147
2148 fill_table(
2150 &mut bindings_ctx,
2151 &table,
2152 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2153 EstablishmentLifecycle::Established,
2154 );
2155
2156 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2157
2158 let (packet, reply_packet) =
2161 make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2162 let packet = packet.conntrack_packet().unwrap();
2163 let reply_packet = reply_packet.conntrack_packet().unwrap();
2164 let (conn, _dir) = table
2165 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2166 .expect("packet should be valid")
2167 .expect("packet should be trackable");
2168 assert_matches!(
2169 table
2170 .finalize_connection(&mut bindings_ctx, conn)
2171 .expect("connection finalize should succeed"),
2172 (true, Some(_))
2173 );
2174 let (conn, _dir) = table
2175 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2176 .expect("packet should be valid")
2177 .expect("packet should be trackable");
2178 assert_matches!(
2179 table
2180 .finalize_connection(&mut bindings_ctx, conn)
2181 .expect("connection finalize should succeed"),
2182 (false, Some(_))
2183 );
2184 let (conn, _dir) = table
2185 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2186 .expect("packet should be valid")
2187 .expect("packet should be trackable");
2188 assert_matches!(
2189 table
2190 .finalize_connection(&mut bindings_ctx, conn)
2191 .expect("connection finalize should succeed"),
2192 (false, Some(_))
2193 );
2194
2195 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 + 1).try_into().unwrap());
2198 let packet = packet.conntrack_packet().unwrap();
2199 let (conn, _dir) = table
2200 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2201 .expect("packet should be valid")
2202 .expect("packet should be trackable");
2203 assert_matches!(
2204 table.finalize_connection(&mut bindings_ctx, conn),
2205 Err(FinalizeConnectionError::TableFull)
2206 );
2207
2208 {
2209 let inspector = Inspector::new(Default::default());
2210 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2211 bindings_inspector.delegate_inspectable(&table);
2212
2213 let mut exec = fuchsia_async::TestExecutor::new();
2214 assert_data_tree!(@executor exec, inspector, "root": contains {
2215 "table_limit_drops": 1u64,
2216 "table_limit_hits": 2u64,
2217 "num_entries": MAXIMUM_ENTRIES as u64,
2218 });
2219 }
2220 }
2221
2222 #[ip_test(I)]
2223 fn self_connected_socket<I: IpExt + TestIpExt>() {
2224 let mut bindings_ctx = FakeBindingsCtx::new();
2225 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2226
2227 let packet = PacketMetadata::<I>::new(
2228 I::SRC_IP,
2229 I::SRC_IP,
2230 TransportProtocol::Udp,
2231 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::SRC_PORT },
2232 );
2233
2234 let tuple = packet.tuple();
2235 let reply_tuple = tuple.clone().invert();
2236
2237 assert_eq!(tuple, reply_tuple);
2238
2239 let (conn, _dir) = table
2240 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2241 .expect("packet should be valid")
2242 .expect("packet should be trackable");
2243 let state = conn.state();
2244 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
2247
2248 assert_matches!(conn, Connection::Exclusive(_));
2249 assert!(!table.contains_tuple(&tuple));
2250
2251 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2253 assert!(table.contains_tuple(&tuple));
2254
2255 assert_eq!(table.inner.lock().table.len(), 1);
2258
2259 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP);
2260 table.perform_gc(&mut bindings_ctx);
2261
2262 assert!(table.inner.lock().table.is_empty());
2263 }
2264
2265 #[ip_test(I)]
2266 fn remove_entry_on_update<I: IpExt + TestIpExt>() {
2267 let mut bindings_ctx = FakeBindingsCtx::new();
2268 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2269
2270 let original_packet = PacketMetadata::<I>::new(
2271 I::SRC_IP,
2272 I::DST_IP,
2273 TransportProtocol::Tcp,
2274 TransportPacketData::Tcp {
2275 src_port: I::SRC_PORT,
2276 dst_port: I::DST_PORT,
2277 segment: SegmentHeader {
2278 seq: SeqNum::new(1024),
2279 wnd: UnscaledWindowSize::from(16u16),
2280 control: Some(Control::SYN),
2281 ..Default::default()
2282 },
2283 payload_len: 0,
2284 },
2285 );
2286
2287 let reply_packet = PacketMetadata::<I>::new(
2288 I::DST_IP,
2289 I::SRC_IP,
2290 TransportProtocol::Tcp,
2291 TransportPacketData::Tcp {
2292 src_port: I::DST_PORT,
2293 dst_port: I::SRC_PORT,
2294 segment: SegmentHeader {
2295 seq: SeqNum::new(0),
2296 ack: Some(SeqNum::new(1025)),
2297 wnd: UnscaledWindowSize::from(16u16),
2298 control: Some(Control::RST),
2299 ..Default::default()
2300 },
2301 payload_len: 0,
2302 },
2303 );
2304
2305 let tuple = original_packet.tuple();
2306 let reply_tuple = tuple.clone().invert();
2307
2308 let (conn, _dir) = table
2309 .get_connection_for_packet_and_update(&bindings_ctx, original_packet)
2310 .expect("packet should be valid")
2311 .expect("packet should be trackable");
2312 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2313
2314 assert!(table.contains_tuple(&tuple));
2315 assert!(table.contains_tuple(&reply_tuple));
2316
2317 let (conn, _dir) = table
2320 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2321 .expect("packet should be valid")
2322 .expect("packet should be trackable");
2323
2324 assert!(!table.contains_tuple(&tuple));
2325 assert!(!table.contains_tuple(&reply_tuple));
2326 assert!(table.inner.lock().table.is_empty());
2327
2328 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
2330
2331 assert!(!table.contains_tuple(&tuple));
2332 assert!(!table.contains_tuple(&reply_tuple));
2333 assert!(table.inner.lock().table.is_empty());
2334
2335 bindings_ctx.sleep(Duration::from_secs(60 * 60 * 24 * 6));
2337 table.perform_gc(&mut bindings_ctx);
2338 }
2339
2340 #[ip_test(I)]
2341 fn do_not_insert<I: IpExt + TestIpExt>() {
2342 let mut bindings_ctx = FakeBindingsCtx::new();
2343 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2344
2345 let packet = PacketMetadata::<I>::new(
2346 I::SRC_IP,
2347 I::DST_IP,
2348 TransportProtocol::Udp,
2349 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
2350 );
2351
2352 let tuple = packet.tuple();
2353 let reply_tuple = tuple.clone().invert();
2354
2355 let (conn, _dir) = table
2356 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2357 .expect("packet should be valid")
2358 .expect("packet should be trackable");
2359 let mut conn = assert_matches!(conn, Connection::Exclusive(conn) => conn);
2360 conn.do_not_insert = true;
2361 assert_matches!(
2362 table.finalize_connection(&mut bindings_ctx, Connection::Exclusive(conn)),
2363 Ok((false, None))
2364 );
2365
2366 assert!(!table.contains_tuple(&tuple));
2367 assert!(!table.contains_tuple(&reply_tuple));
2368 }
2369}