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: 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)
281 || guard.table.contains_key(&exclusive.inner.reply_tuple)
282 {
283 let conn = if let Some(conn) = guard.table.get(&exclusive.inner.original_tuple) {
296 conn
297 } else {
298 guard
299 .table
300 .get(&exclusive.inner.reply_tuple)
301 .expect("checked that tuple is in table and table is locked")
302 };
303 if conn.compatible_with(&exclusive) {
304 return Ok((false, Some(conn.clone())));
305 }
306
307 Err(FinalizeConnectionError::Conflict)
309 } else {
310 let shared = exclusive.make_shared();
311 let clone = Arc::clone(&shared);
312
313 let res = guard.table.insert(shared.inner.original_tuple.clone(), shared.clone());
314 debug_assert!(res.is_none());
315
316 if shared.inner.reply_tuple != shared.inner.original_tuple {
317 let res = guard.table.insert(shared.inner.reply_tuple.clone(), shared);
318 debug_assert!(res.is_none());
319 }
320
321 if bindings_ctx.scheduled_instant(&mut guard.gc_timer).is_none() {
327 schedule_gc(bindings_ctx, &mut guard.gc_timer);
328 }
329
330 Ok((true, Some(clone)))
331 }
332 }
333}
334
335impl<I, E, BC> Table<I, E, BC>
336where
337 I: IpExt,
338 E: Default,
339 BC: FilterBindingsTypes + TimerContext,
340{
341 pub(crate) fn get_connection_for_packet_and_update(
351 &self,
352 bindings_ctx: &BC,
353 packet: PacketMetadata<I>,
354 ) -> Result<Option<(Connection<I, E, BC>, ConnectionDirection)>, GetConnectionError<I, E, BC>>
355 {
356 let tuple = packet.tuple();
357 let mut connection = match self.inner.lock().table.get(&tuple) {
358 Some(connection) => Connection::Shared(connection.clone()),
359 None => match ConnectionExclusive::from_deconstructed_packet(bindings_ctx, &packet) {
360 None => return Ok(None),
361 Some(c) => Connection::Exclusive(c),
362 },
363 };
364
365 let direction = connection
366 .direction(&tuple)
367 .expect("tuple must match connection as we just looked up connection by tuple");
368
369 match connection.update(bindings_ctx, &packet, direction) {
370 Ok(ConnectionUpdateAction::NoAction) => Ok(Some((connection, direction))),
371 Ok(ConnectionUpdateAction::RemoveEntry) => match connection {
372 Connection::Exclusive(mut conn) => {
373 conn.do_not_insert = true;
374 Ok(Some((Connection::Exclusive(conn), direction)))
375 }
376 Connection::Shared(conn) => {
377 let mut guard = self.inner.lock();
381 let _ = guard.table.remove(&conn.inner.original_tuple);
382 let _ = guard.table.remove(&conn.inner.reply_tuple);
383
384 Ok(Some((Connection::Shared(conn), direction)))
385 }
386 },
387 Err(ConnectionUpdateError::InvalidPacket) => {
388 Err(GetConnectionError::InvalidPacket(connection, direction))
389 }
390 }
391 }
392
393 pub(crate) fn perform_gc(&self, bindings_ctx: &mut BC) {
394 let now = bindings_ctx.now();
395 let mut guard = self.inner.lock();
396
397 let to_remove: Vec<_> = guard
406 .table
407 .iter()
408 .filter_map(|(tuple, conn)| {
409 if *tuple == conn.inner.original_tuple && conn.is_expired(now) {
410 Some((conn.inner.original_tuple.clone(), conn.inner.reply_tuple.clone()))
411 } else {
412 None
413 }
414 })
415 .collect();
416
417 for (original_tuple, reply_tuple) in to_remove {
418 assert!(guard.table.remove(&original_tuple).is_some());
419 if reply_tuple != original_tuple {
420 assert!(guard.table.remove(&reply_tuple).is_some());
421 }
422 }
423
424 if !guard.table.is_empty() {
430 schedule_gc(bindings_ctx, &mut guard.gc_timer);
431 }
432 }
433}
434
435impl<I, E, BT> Inspectable for Table<I, E, BT>
436where
437 I: IpExt,
438 E: Inspectable,
439 BT: FilterBindingsTypes,
440{
441 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
442 let guard = self.inner.lock();
443
444 inspector.record_usize("num_entries", guard.table.len());
445 inspector.record_uint("table_limit_hits", guard.table_limit_hits);
446 inspector.record_uint("table_limit_drops", guard.table_limit_drops);
447
448 inspector.record_child("connections", |inspector| {
449 guard
450 .table
451 .iter()
452 .filter_map(|(tuple, connection)| {
453 if *tuple == connection.inner.original_tuple { Some(connection) } else { None }
454 })
455 .for_each(|connection| {
456 inspector.record_unnamed_child(|inspector| {
457 inspector.delegate_inspectable(connection.as_ref())
458 });
459 });
460 });
461 }
462}
463
464#[derive(Debug, Clone, PartialEq, Eq, Hash, GenericOverIp)]
466#[generic_over_ip(I, Ip)]
467pub struct Tuple<I: IpExt> {
468 pub protocol: TransportProtocol,
470 pub src_addr: I::Addr,
472 pub dst_addr: I::Addr,
474 pub src_port_or_id: u16,
476 pub dst_port_or_id: u16,
478}
479
480impl<I: IpExt> Tuple<I> {
481 fn new(
482 src_addr: I::Addr,
483 dst_addr: I::Addr,
484 src_port_or_id: u16,
485 dst_port_or_id: u16,
486 protocol: TransportProtocol,
487 ) -> Self {
488 Self { protocol, src_addr, dst_addr, src_port_or_id, dst_port_or_id }
489 }
490
491 pub(crate) fn invert(self) -> Tuple<I> {
497 Self {
500 protocol: self.protocol,
501 src_addr: self.dst_addr,
502 dst_addr: self.src_addr,
503 src_port_or_id: self.dst_port_or_id,
504 dst_port_or_id: self.src_port_or_id,
505 }
506 }
507}
508
509impl<I: IpExt> Inspectable for Tuple<I> {
510 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
511 inspector.record_debug("protocol", self.protocol);
512 inspector.record_ip_addr("src_addr", self.src_addr);
513 inspector.record_ip_addr("dst_addr", self.dst_addr);
514 inspector.record_usize("src_port_or_id", self.src_port_or_id);
515 inspector.record_usize("dst_port_or_id", self.dst_port_or_id);
516 }
517}
518
519#[derive(Debug, Copy, Clone, PartialEq, Eq)]
521pub enum ConnectionDirection {
522 Original,
525
526 Reply,
529}
530
531#[derive(Debug)]
533pub(crate) enum FinalizeConnectionError {
534 Conflict,
537
538 TableFull,
540}
541
542#[derive(Debug, PartialEq, Eq)]
544enum ConnectionUpdateAction {
545 NoAction,
547
548 RemoveEntry,
550}
551
552#[derive(Debug, PartialEq, Eq)]
554enum ConnectionUpdateError {
555 InvalidPacket,
558}
559
560#[derive(Derivative)]
562#[derivative(Debug(bound = "E: Debug"))]
563pub(crate) enum GetConnectionError<I: IpExt, E, BT: FilterBindingsTypes> {
564 InvalidPacket(Connection<I, E, BT>, ConnectionDirection),
566}
567
568#[derive(Derivative)]
571#[derivative(Debug(bound = "E: Debug"))]
572pub enum Connection<I: IpExt, E, BT: FilterBindingsTypes> {
573 Exclusive(ConnectionExclusive<I, E, BT>),
576
577 Shared(Arc<ConnectionShared<I, E, BT>>),
580}
581
582#[derive(Debug)]
585pub enum WeakConnectionError {
586 EntryRemoved,
588 InvalidEntry,
591}
592
593#[derive(Debug, Clone)]
603pub struct WeakConnection(pub(crate) Weak<dyn Any + Send + Sync>);
604
605impl WeakConnection {
606 pub fn new<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
609 conn: &Connection<I, E, BT>,
610 ) -> Option<Self> {
611 let shared = match conn {
612 Connection::Exclusive(_) => return None,
613 Connection::Shared(shared) => shared,
614 };
615 let weak = Arc::downgrade(shared);
616 Some(Self(weak))
617 }
618
619 pub fn into_inner<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
628 self,
629 ) -> Result<Connection<I, E, BT>, WeakConnectionError> {
630 let Self(inner) = self;
631 let shared = inner
632 .upgrade()
633 .ok_or(WeakConnectionError::EntryRemoved)?
634 .downcast()
635 .map_err(|_err: Arc<_>| WeakConnectionError::InvalidEntry)?;
636 Ok(Connection::Shared(shared))
637 }
638}
639
640impl<I: IpExt, E, BT: FilterBindingsTypes> Connection<I, E, BT> {
641 pub fn original_tuple(&self) -> &Tuple<I> {
643 match self {
644 Connection::Exclusive(c) => &c.inner.original_tuple,
645 Connection::Shared(c) => &c.inner.original_tuple,
646 }
647 }
648
649 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
651 match self {
652 Connection::Exclusive(c) => &c.inner.reply_tuple,
653 Connection::Shared(c) => &c.inner.reply_tuple,
654 }
655 }
656
657 pub fn external_data(&self) -> &E {
659 match self {
660 Connection::Exclusive(c) => &c.inner.external_data,
661 Connection::Shared(c) => &c.inner.external_data,
662 }
663 }
664
665 pub(crate) fn direction(&self, tuple: &Tuple<I>) -> Option<ConnectionDirection> {
668 let (original, reply) = match self {
669 Connection::Exclusive(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
670 Connection::Shared(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
671 };
672
673 if tuple == reply {
681 Some(ConnectionDirection::Reply)
682 } else if tuple == original {
683 Some(ConnectionDirection::Original)
684 } else {
685 None
686 }
687 }
688
689 #[allow(dead_code)]
691 pub(crate) fn state(&self) -> ConnectionState<BT> {
692 match self {
693 Connection::Exclusive(c) => c.state.clone(),
694 Connection::Shared(c) => c.state.lock().clone(),
695 }
696 }
697}
698
699impl<I, E, BC> Connection<I, E, BC>
700where
701 I: IpExt,
702 BC: FilterBindingsTypes + TimerContext,
703{
704 fn update(
705 &mut self,
706 bindings_ctx: &BC,
707 packet: &PacketMetadata<I>,
708 direction: ConnectionDirection,
709 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
710 match packet {
711 PacketMetadata::Full { transport_data, .. } => {
712 let now = bindings_ctx.now();
713 match self {
714 Connection::Exclusive(c) => c.state.update(direction, &transport_data, now),
715 Connection::Shared(c) => c.state.lock().update(direction, &transport_data, now),
716 }
717 }
718 PacketMetadata::IcmpError(_) => Ok(ConnectionUpdateAction::NoAction),
719 }
720 }
721}
722
723#[derive(Derivative)]
725#[derivative(Debug(bound = "E: Debug"), PartialEq(bound = "E: PartialEq"))]
726pub struct ConnectionCommon<I: IpExt, E> {
727 pub(crate) original_tuple: Tuple<I>,
730
731 pub(crate) reply_tuple: Tuple<I>,
734
735 pub(crate) external_data: E,
739}
740
741impl<I: IpExt, E: Inspectable> Inspectable for ConnectionCommon<I, E> {
742 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
743 inspector.record_child("original_tuple", |inspector| {
744 inspector.delegate_inspectable(&self.original_tuple);
745 });
746
747 inspector.record_child("reply_tuple", |inspector| {
748 inspector.delegate_inspectable(&self.reply_tuple);
749 });
750
751 inspector.record_child("external_data", |inspector| {
755 inspector.delegate_inspectable(&self.external_data);
756 });
757 }
758}
759
760#[derive(Debug, Clone)]
761enum ProtocolState {
762 Tcp(tcp::Connection),
763 Udp,
764 Other,
765}
766
767impl ProtocolState {
768 fn update(
769 &mut self,
770 dir: ConnectionDirection,
771 transport_data: &TransportPacketData,
772 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
773 match self {
774 ProtocolState::Tcp(tcp_conn) => {
775 let (segment, payload_len) = assert_matches!(
776 transport_data,
777 TransportPacketData::Tcp { segment, payload_len, .. } => (segment, payload_len)
778 );
779 tcp_conn.update(&segment, *payload_len, dir)
780 }
781 ProtocolState::Udp | ProtocolState::Other => Ok(ConnectionUpdateAction::NoAction),
782 }
783 }
784}
785
786#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
797enum EstablishmentLifecycle {
798 SeenOriginal,
799 SeenReply,
800 Established,
801}
802
803impl EstablishmentLifecycle {
804 fn update(self, dir: ConnectionDirection) -> Self {
805 match self {
806 EstablishmentLifecycle::SeenOriginal => match dir {
807 ConnectionDirection::Original => self,
808 ConnectionDirection::Reply => EstablishmentLifecycle::SeenReply,
809 },
810 EstablishmentLifecycle::SeenReply => match dir {
811 ConnectionDirection::Original => EstablishmentLifecycle::Established,
812 ConnectionDirection::Reply => self,
813 },
814 EstablishmentLifecycle::Established => self,
815 }
816 }
817}
818
819#[derive(Derivative)]
821#[derivative(Clone(bound = ""), Debug(bound = ""))]
822pub(crate) struct ConnectionState<BT: FilterBindingsTypes> {
823 last_packet_time: BT::Instant,
826
827 establishment_lifecycle: EstablishmentLifecycle,
829
830 protocol_state: ProtocolState,
832}
833
834impl<BT: FilterBindingsTypes> ConnectionState<BT> {
835 fn update(
836 &mut self,
837 dir: ConnectionDirection,
838 transport_data: &TransportPacketData,
839 now: BT::Instant,
840 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
841 if self.last_packet_time < now {
842 self.last_packet_time = now;
843 }
844
845 self.establishment_lifecycle = self.establishment_lifecycle.update(dir);
846
847 self.protocol_state.update(dir, transport_data)
848 }
849
850 fn is_expired(&self, now: BT::Instant) -> bool {
851 let duration = now.saturating_duration_since(self.last_packet_time);
852
853 let expiry_duration = match &self.protocol_state {
854 ProtocolState::Tcp(tcp_conn) => tcp_conn.expiry_duration(self.establishment_lifecycle),
855 ProtocolState::Udp => CONNECTION_EXPIRY_TIME_UDP,
856 ProtocolState::Other => CONNECTION_EXPIRY_OTHER,
861 };
862
863 duration >= expiry_duration
864 }
865}
866
867impl<BT: FilterBindingsTypes> Inspectable for ConnectionState<BT> {
868 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
869 inspector.record_bool(
870 "established",
871 match self.establishment_lifecycle {
872 EstablishmentLifecycle::SeenOriginal | EstablishmentLifecycle::SeenReply => false,
873 EstablishmentLifecycle::Established => true,
874 },
875 );
876 inspector.record_inspectable_value("last_packet_time", &self.last_packet_time);
877 }
878}
879
880#[derive(Derivative)]
887#[derivative(Debug(bound = "E: Debug"))]
888pub struct ConnectionExclusive<I: IpExt, E, BT: FilterBindingsTypes> {
889 pub(crate) inner: ConnectionCommon<I, E>,
890 pub(crate) state: ConnectionState<BT>,
891
892 do_not_insert: bool,
897}
898
899impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionExclusive<I, E, BT> {
900 fn make_shared(self) -> Arc<ConnectionShared<I, E, BT>> {
903 Arc::new(ConnectionShared { inner: self.inner, state: Mutex::new(self.state) })
904 }
905
906 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
907 &self.inner.reply_tuple
908 }
909
910 pub(crate) fn rewrite_reply_dst_addr(&mut self, addr: I::Addr) {
911 self.inner.reply_tuple.dst_addr = addr;
912 }
913
914 pub(crate) fn rewrite_reply_src_addr(&mut self, addr: I::Addr) {
915 self.inner.reply_tuple.src_addr = addr;
916 }
917
918 pub(crate) fn rewrite_reply_src_port_or_id(&mut self, port_or_id: u16) {
919 self.inner.reply_tuple.src_port_or_id = port_or_id;
920 match self.inner.reply_tuple.protocol {
921 TransportProtocol::Icmp => {
922 self.inner.reply_tuple.dst_port_or_id = port_or_id;
930 }
931 TransportProtocol::Tcp | TransportProtocol::Udp | TransportProtocol::Other(_) => {}
932 }
933 }
934
935 pub(crate) fn rewrite_reply_dst_port_or_id(&mut self, port_or_id: u16) {
936 self.inner.reply_tuple.dst_port_or_id = port_or_id;
937 match self.inner.reply_tuple.protocol {
938 TransportProtocol::Icmp => {
939 self.inner.reply_tuple.src_port_or_id = port_or_id;
947 }
948 TransportProtocol::Tcp | TransportProtocol::Udp | TransportProtocol::Other(_) => {}
949 }
950 }
951}
952
953impl<I, E, BC> ConnectionExclusive<I, E, BC>
954where
955 I: IpExt,
956 E: Default,
957 BC: FilterBindingsTypes + TimerContext,
958{
959 pub(crate) fn from_deconstructed_packet(
960 bindings_ctx: &BC,
961 packet_metadata: &PacketMetadata<I>,
962 ) -> Option<Self> {
963 let (tuple, transport_data) = match packet_metadata {
964 PacketMetadata::Full { tuple, transport_data } => (tuple, transport_data),
965 PacketMetadata::IcmpError(_) => return None,
966 };
967
968 let reply_tuple = tuple.clone().invert();
969 let self_connected = reply_tuple == *tuple;
970
971 Some(Self {
972 inner: ConnectionCommon {
973 original_tuple: tuple.clone(),
974 reply_tuple,
975 external_data: E::default(),
976 },
977 state: ConnectionState {
978 last_packet_time: bindings_ctx.now(),
979 establishment_lifecycle: EstablishmentLifecycle::SeenOriginal,
980 protocol_state: match tuple.protocol {
981 TransportProtocol::Tcp => {
982 let (segment, payload_len) = transport_data
983 .tcp_segment_and_len()
984 .expect("protocol was TCP, so transport data should have TCP info");
985
986 ProtocolState::Tcp(tcp::Connection::new(
987 segment,
988 payload_len,
989 self_connected,
990 )?)
991 }
992 TransportProtocol::Udp => ProtocolState::Udp,
993 TransportProtocol::Icmp | TransportProtocol::Other(_) => ProtocolState::Other,
994 },
995 },
996 do_not_insert: false,
997 })
998 }
999}
1000
1001#[derive(Derivative)]
1007#[derivative(Debug(bound = "E: Debug"))]
1008pub struct ConnectionShared<I: IpExt, E, BT: FilterBindingsTypes> {
1009 inner: ConnectionCommon<I, E>,
1010 state: Mutex<ConnectionState<BT>>,
1011}
1012
1013#[allow(missing_docs)]
1015#[derive(Copy, Clone, PartialEq, Eq, Hash, GenericOverIp)]
1016#[generic_over_ip()]
1017pub enum TransportProtocol {
1018 Tcp,
1019 Udp,
1020 Icmp,
1021 Other(u8),
1022}
1023
1024impl From<Ipv4Proto> for TransportProtocol {
1025 fn from(value: Ipv4Proto) -> Self {
1026 match value {
1027 Ipv4Proto::Proto(IpProto::Tcp) => TransportProtocol::Tcp,
1028 Ipv4Proto::Proto(IpProto::Udp) => TransportProtocol::Udp,
1029 Ipv4Proto::Icmp => TransportProtocol::Icmp,
1030 v => TransportProtocol::Other(v.into()),
1031 }
1032 }
1033}
1034
1035impl From<Ipv6Proto> for TransportProtocol {
1036 fn from(value: Ipv6Proto) -> Self {
1037 match value {
1038 Ipv6Proto::Proto(IpProto::Tcp) => TransportProtocol::Tcp,
1039 Ipv6Proto::Proto(IpProto::Udp) => TransportProtocol::Udp,
1040 Ipv6Proto::Icmpv6 => TransportProtocol::Icmp,
1041 v => TransportProtocol::Other(v.into()),
1042 }
1043 }
1044}
1045
1046impl From<IpProto> for TransportProtocol {
1047 fn from(value: IpProto) -> Self {
1048 match value {
1049 IpProto::Tcp => TransportProtocol::Tcp,
1050 IpProto::Udp => TransportProtocol::Udp,
1051 v @ IpProto::Reserved => TransportProtocol::Other(v.into()),
1052 }
1053 }
1054}
1055
1056impl Display for TransportProtocol {
1057 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1058 match self {
1059 TransportProtocol::Tcp => write!(f, "TCP"),
1060 TransportProtocol::Udp => write!(f, "UDP"),
1061 TransportProtocol::Icmp => write!(f, "ICMP"),
1062 TransportProtocol::Other(n) => write!(f, "Other({n})"),
1063 }
1064 }
1065}
1066
1067impl Debug for TransportProtocol {
1068 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1069 Display::fmt(&self, f)
1070 }
1071}
1072
1073impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1074 fn is_expired(&self, now: BT::Instant) -> bool {
1075 self.state.lock().is_expired(now)
1076 }
1077}
1078
1079impl<I: IpExt, E: CompatibleWith, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1080 pub(crate) fn compatible_with(&self, conn: &ConnectionExclusive<I, E, BT>) -> bool {
1084 self.inner.original_tuple == conn.inner.original_tuple
1085 && self.inner.reply_tuple == conn.inner.reply_tuple
1086 && self.inner.external_data.compatible_with(&conn.inner.external_data)
1087 }
1088}
1089
1090impl<I: IpExt, E: Inspectable, BT: FilterBindingsTypes> Inspectable for ConnectionShared<I, E, BT> {
1091 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
1092 inspector.delegate_inspectable(&self.inner);
1093 inspector.delegate_inspectable(&*self.state.lock());
1094 }
1095}
1096
1097pub trait CompatibleWith {
1100 fn compatible_with(&self, other: &Self) -> bool;
1103}
1104
1105#[derive(Debug, Clone, PartialEq, Eq)]
1110pub enum PacketMetadata<I: IpExt> {
1111 Full { tuple: Tuple<I>, transport_data: TransportPacketData },
1112 IcmpError(Tuple<I>),
1113}
1114
1115impl<I: IpExt> PacketMetadata<I> {
1116 pub(crate) fn new(
1117 src_addr: I::Addr,
1118 dst_addr: I::Addr,
1119 protocol: TransportProtocol,
1120 transport_data: TransportPacketData,
1121 ) -> Self {
1122 match protocol {
1123 TransportProtocol::Tcp => {
1124 assert_matches!(transport_data, TransportPacketData::Tcp { .. })
1125 }
1126 TransportProtocol::Udp | TransportProtocol::Icmp | TransportProtocol::Other(_) => {
1127 assert_matches!(transport_data, TransportPacketData::Generic { .. })
1128 }
1129 }
1130
1131 Self::Full {
1132 tuple: Tuple::new(
1133 src_addr,
1134 dst_addr,
1135 transport_data.src_port(),
1136 transport_data.dst_port(),
1137 protocol,
1138 ),
1139 transport_data,
1140 }
1141 }
1142
1143 pub(crate) fn new_from_icmp_error(
1144 src_addr: I::Addr,
1145 dst_addr: I::Addr,
1146 src_port: u16,
1147 dst_port: u16,
1148 protocol: TransportProtocol,
1149 ) -> Self {
1150 Self::IcmpError(Tuple::new(src_addr, dst_addr, src_port, dst_port, protocol))
1151 }
1152
1153 pub(crate) fn tuple(&self) -> Tuple<I> {
1154 match self {
1155 PacketMetadata::Full { tuple, .. } => tuple.clone(),
1156 PacketMetadata::IcmpError(tuple) => tuple.clone().invert(),
1184 }
1185 }
1186}
1187
1188#[cfg(test)]
1189pub(crate) mod testutils {
1190 use crate::packets::testutil::internal::{FakeIpPacket, FakeUdpPacket, TestIpExt};
1191
1192 pub(crate) fn make_test_udp_packets<I: TestIpExt>(
1195 index: u32,
1196 ) -> (FakeIpPacket<I, FakeUdpPacket>, FakeIpPacket<I, FakeUdpPacket>) {
1197 let src_port = (index % (u16::MAX as u32)) as u16;
1200 let dst_port = (index / (u16::MAX as u32)) as u16;
1201
1202 let packet = FakeIpPacket::<I, _> {
1203 src_ip: I::SRC_IP,
1204 dst_ip: I::DST_IP,
1205 body: FakeUdpPacket { src_port, dst_port },
1206 };
1207 let reply_packet = FakeIpPacket::<I, _> {
1208 src_ip: I::DST_IP,
1209 dst_ip: I::SRC_IP,
1210 body: FakeUdpPacket { src_port: dst_port, dst_port: src_port },
1211 };
1212
1213 (packet, reply_packet)
1214 }
1215}
1216
1217#[cfg(test)]
1218mod tests {
1219 use assert_matches::assert_matches;
1220 use ip_test_macro::ip_test;
1221 use netstack3_base::testutil::FakeTimerCtxExt;
1222 use netstack3_base::{Control, IntoCoreTimerCtx, SegmentHeader, SeqNum, UnscaledWindowSize};
1223 use test_case::test_case;
1224
1225 use super::testutils::make_test_udp_packets;
1226 use super::*;
1227 use crate::context::testutil::{FakeBindingsCtx, FakeCtx};
1228 use crate::packets::IpPacket;
1229 use crate::packets::testutil::internal::ArbitraryValue;
1230 use crate::state::IpRoutines;
1231 use crate::testutil::TestIpExt;
1232
1233 impl CompatibleWith for () {
1234 fn compatible_with(&self, (): &()) -> bool {
1235 true
1236 }
1237 }
1238
1239 #[test_case(
1240 EstablishmentLifecycle::SeenOriginal,
1241 ConnectionDirection::Original
1242 => EstablishmentLifecycle::SeenOriginal
1243 )]
1244 #[test_case(
1245 EstablishmentLifecycle::SeenOriginal,
1246 ConnectionDirection::Reply
1247 => EstablishmentLifecycle::SeenReply
1248 )]
1249 #[test_case(
1250 EstablishmentLifecycle::SeenReply,
1251 ConnectionDirection::Original
1252 => EstablishmentLifecycle::Established
1253 )]
1254 #[test_case(
1255 EstablishmentLifecycle::SeenReply,
1256 ConnectionDirection::Reply
1257 => EstablishmentLifecycle::SeenReply
1258 )]
1259 #[test_case(
1260 EstablishmentLifecycle::Established,
1261 ConnectionDirection::Original
1262 => EstablishmentLifecycle::Established
1263 )]
1264 #[test_case(
1265 EstablishmentLifecycle::Established,
1266 ConnectionDirection::Reply
1267 => EstablishmentLifecycle::Established
1268 )]
1269 fn establishment_lifecycle_test(
1270 lifecycle: EstablishmentLifecycle,
1271 dir: ConnectionDirection,
1272 ) -> EstablishmentLifecycle {
1273 lifecycle.update(dir)
1274 }
1275
1276 #[ip_test(I)]
1277 #[test_case(TransportProtocol::Udp)]
1278 #[test_case(TransportProtocol::Tcp)]
1279 fn tuple_invert_udp_tcp<I: IpExt + TestIpExt>(protocol: TransportProtocol) {
1280 let orig_tuple = Tuple::<I> {
1281 protocol: protocol,
1282 src_addr: I::SRC_IP,
1283 dst_addr: I::DST_IP,
1284 src_port_or_id: I::SRC_PORT,
1285 dst_port_or_id: I::DST_PORT,
1286 };
1287
1288 let expected = Tuple::<I> {
1289 protocol: protocol,
1290 src_addr: I::DST_IP,
1291 dst_addr: I::SRC_IP,
1292 src_port_or_id: I::DST_PORT,
1293 dst_port_or_id: I::SRC_PORT,
1294 };
1295
1296 let inverted = orig_tuple.invert();
1297
1298 assert_eq!(inverted, expected);
1299 }
1300
1301 #[ip_test(I)]
1302 fn tuple_from_tcp_packet<I: IpExt + TestIpExt>() {
1303 let expected = Tuple::<I> {
1304 protocol: TransportProtocol::Tcp,
1305 src_addr: I::SRC_IP,
1306 dst_addr: I::DST_IP,
1307 src_port_or_id: I::SRC_PORT,
1308 dst_port_or_id: I::DST_PORT,
1309 };
1310
1311 let packet = PacketMetadata::<I>::new(
1312 I::SRC_IP,
1313 I::DST_IP,
1314 TransportProtocol::Tcp,
1315 TransportPacketData::Tcp {
1316 src_port: I::SRC_PORT,
1317 dst_port: I::DST_PORT,
1318 segment: SegmentHeader::arbitrary_value(),
1319 payload_len: 4,
1320 },
1321 );
1322
1323 assert_eq!(packet.tuple(), expected);
1324 }
1325
1326 #[ip_test(I)]
1327 fn connection_from_tuple<I: IpExt + TestIpExt>() {
1328 let bindings_ctx = FakeBindingsCtx::<I>::new();
1329
1330 let packet = PacketMetadata::<I>::new(
1331 I::SRC_IP,
1332 I::DST_IP,
1333 TransportProtocol::Udp,
1334 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1335 );
1336 let original_tuple = packet.tuple();
1337 let reply_tuple = packet.tuple().invert();
1338
1339 let connection =
1340 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1341 .unwrap();
1342
1343 assert_eq!(&connection.inner.original_tuple, &original_tuple);
1344 assert_eq!(&connection.inner.reply_tuple, &reply_tuple);
1345 }
1346
1347 #[ip_test(I)]
1348 fn connection_make_shared_has_same_underlying_info<I: IpExt + TestIpExt>() {
1349 let bindings_ctx = FakeBindingsCtx::<I>::new();
1350
1351 let packet = PacketMetadata::<I>::new(
1352 I::SRC_IP,
1353 I::DST_IP,
1354 TransportProtocol::Udp,
1355 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1356 );
1357 let original_tuple = packet.tuple();
1358 let reply_tuple = original_tuple.clone().invert();
1359
1360 let mut connection =
1361 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1362 connection.inner.external_data = 1234;
1363 let shared = connection.make_shared();
1364
1365 assert_eq!(shared.inner.original_tuple, original_tuple);
1366 assert_eq!(shared.inner.reply_tuple, reply_tuple);
1367 assert_eq!(shared.inner.external_data, 1234);
1368 }
1369
1370 enum ConnectionKind {
1371 Exclusive,
1372 Shared,
1373 }
1374
1375 #[ip_test(I)]
1376 #[test_case(ConnectionKind::Exclusive)]
1377 #[test_case(ConnectionKind::Shared)]
1378 fn connection_getters<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1379 let bindings_ctx = FakeBindingsCtx::<I>::new();
1380
1381 let packet = PacketMetadata::<I>::new(
1382 I::SRC_IP,
1383 I::DST_IP,
1384 TransportProtocol::Udp,
1385 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1386 );
1387 let original_tuple = packet.tuple();
1388 let reply_tuple = original_tuple.clone().invert();
1389
1390 let mut connection =
1391 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1392 connection.inner.external_data = 1234;
1393
1394 let connection = match connection_kind {
1395 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1396 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1397 };
1398
1399 assert_eq!(connection.original_tuple(), &original_tuple);
1400 assert_eq!(connection.reply_tuple(), &reply_tuple);
1401 assert_eq!(connection.external_data(), &1234);
1402 }
1403
1404 #[ip_test(I)]
1405 #[test_case(ConnectionKind::Exclusive)]
1406 #[test_case(ConnectionKind::Shared)]
1407 fn connection_direction<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1408 let bindings_ctx = FakeBindingsCtx::<I>::new();
1409
1410 let packet = PacketMetadata::<I>::new(
1411 I::SRC_IP,
1412 I::DST_IP,
1413 TransportProtocol::Udp,
1414 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1415 );
1416 let original_tuple = packet.tuple();
1417 let reply_tuple = original_tuple.clone().invert();
1418
1419 let mut other_tuple = original_tuple.clone();
1420 other_tuple.src_port_or_id += 1;
1421
1422 let connection: ConnectionExclusive<_, (), _> =
1423 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1424 let connection = match connection_kind {
1425 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1426 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1427 };
1428
1429 assert_matches!(connection.direction(&original_tuple), Some(ConnectionDirection::Original));
1430 assert_matches!(connection.direction(&reply_tuple), Some(ConnectionDirection::Reply));
1431 assert_matches!(connection.direction(&other_tuple), None);
1432 }
1433
1434 #[ip_test(I)]
1435 #[test_case(ConnectionKind::Exclusive)]
1436 #[test_case(ConnectionKind::Shared)]
1437 fn connection_update<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1438 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1439 bindings_ctx.sleep(Duration::from_secs(1));
1440
1441 let packet = PacketMetadata::<I>::new(
1442 I::SRC_IP,
1443 I::DST_IP,
1444 TransportProtocol::Udp,
1445 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1446 );
1447
1448 let reply_packet = PacketMetadata::<I>::new(
1449 I::DST_IP,
1450 I::SRC_IP,
1451 TransportProtocol::Udp,
1452 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1453 );
1454
1455 let connection =
1456 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1457 .unwrap();
1458 let mut connection = match connection_kind {
1459 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1460 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1461 };
1462
1463 assert_matches!(
1464 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1465 Ok(ConnectionUpdateAction::NoAction)
1466 );
1467 let state = connection.state();
1468 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1469 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1470
1471 bindings_ctx.sleep(Duration::from_secs(1));
1474 assert_matches!(
1475 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1476 Ok(ConnectionUpdateAction::NoAction)
1477 );
1478 let state = connection.state();
1479 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1480 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1481 }
1482
1483 #[ip_test(I)]
1484 #[test_case(ConnectionKind::Exclusive)]
1485 #[test_case(ConnectionKind::Shared)]
1486 fn skip_connection_update_for_icmp_error<I: IpExt + TestIpExt>(
1487 connection_kind: ConnectionKind,
1488 ) {
1489 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1490 bindings_ctx.sleep(Duration::from_secs(1));
1491
1492 let packet = PacketMetadata::<I>::new(
1493 I::SRC_IP,
1494 I::DST_IP,
1495 TransportProtocol::Udp,
1496 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1497 );
1498
1499 let reply_packet = PacketMetadata::<I>::new_from_icmp_error(
1500 I::DST_IP,
1501 I::SRC_IP,
1502 I::DST_PORT,
1503 I::SRC_PORT,
1504 TransportProtocol::Udp,
1505 );
1506
1507 let connection =
1508 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1509 .unwrap();
1510 let mut connection = match connection_kind {
1511 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1512 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1513 };
1514
1515 assert_matches!(
1516 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1517 Ok(ConnectionUpdateAction::NoAction)
1518 );
1519 let state = connection.state();
1520 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1521 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1522
1523 bindings_ctx.sleep(Duration::from_secs(1));
1526 assert_matches!(
1527 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1528 Ok(ConnectionUpdateAction::NoAction)
1529 );
1530 let state = connection.state();
1531 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1532 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1533 }
1534
1535 #[ip_test(I)]
1536 fn skip_connection_creation_for_icmp_error<I: IpExt + TestIpExt>() {
1537 let mut bindings_ctx = FakeBindingsCtx::new();
1538 bindings_ctx.sleep(Duration::from_secs(1));
1539 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1540
1541 let packet = PacketMetadata::<I>::new_from_icmp_error(
1542 I::DST_IP,
1543 I::SRC_IP,
1544 I::DST_PORT,
1545 I::SRC_PORT,
1546 TransportProtocol::Udp,
1547 );
1548
1549 assert!(
1552 table
1553 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1554 .expect("packet should be valid")
1555 .is_none()
1556 );
1557 assert!(!table.contains_tuple(&packet.tuple()));
1558 }
1559
1560 #[ip_test(I)]
1561 fn table_get_exclusive_connection_and_finalize_shared<I: IpExt + TestIpExt>() {
1562 let mut bindings_ctx = FakeBindingsCtx::new();
1563 bindings_ctx.sleep(Duration::from_secs(1));
1564 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1565
1566 let packet = PacketMetadata::<I>::new(
1567 I::SRC_IP,
1568 I::DST_IP,
1569 TransportProtocol::Udp,
1570 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1571 );
1572
1573 let reply_packet = PacketMetadata::<I>::new(
1574 I::DST_IP,
1575 I::SRC_IP,
1576 TransportProtocol::Udp,
1577 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1578 );
1579
1580 let original_tuple = packet.tuple();
1581 let reply_tuple = reply_packet.tuple();
1582
1583 let (conn, dir) = table
1584 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1585 .expect("packet should be valid")
1586 .expect("connection should be present");
1587 let state = conn.state();
1588 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1589 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1590
1591 assert_matches!(conn, Connection::Exclusive(_));
1595 assert_eq!(dir, ConnectionDirection::Original);
1596 assert!(!table.contains_tuple(&original_tuple));
1597 assert!(!table.contains_tuple(&reply_tuple));
1598
1599 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
1601 assert!(table.contains_tuple(&original_tuple));
1602 assert!(table.contains_tuple(&reply_tuple));
1603
1604 bindings_ctx.sleep(Duration::from_secs(1));
1607 let (conn, dir) = table
1608 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1609 .expect("packet should be valid")
1610 .expect("connection should be present");
1611 assert_eq!(dir, ConnectionDirection::Original);
1612 let state = conn.state();
1613 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1614 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1615 let conn = assert_matches!(conn, Connection::Shared(conn) => conn);
1616
1617 bindings_ctx.sleep(Duration::from_secs(1));
1618 let (reply_conn, dir) = table
1619 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
1620 .expect("packet should be valid")
1621 .expect("connection should be present");
1622 assert_eq!(dir, ConnectionDirection::Reply);
1623 let state = reply_conn.state();
1624 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1625 assert_eq!(state.last_packet_time.offset, Duration::from_secs(3));
1626 let reply_conn = assert_matches!(reply_conn, Connection::Shared(conn) => conn);
1627
1628 assert!(Arc::ptr_eq(&conn, &reply_conn));
1630
1631 let (conn, _dir) = table
1633 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1634 .expect("packet should be valid")
1635 .unwrap();
1636 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
1637 assert!(table.contains_tuple(&original_tuple));
1638 assert!(table.contains_tuple(&reply_tuple));
1639 }
1640
1641 #[ip_test(I)]
1642 fn table_conflict<I: IpExt + TestIpExt>() {
1643 let mut bindings_ctx = FakeBindingsCtx::new();
1644 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1645
1646 let original_packet = PacketMetadata::<I>::new(
1647 I::SRC_IP,
1648 I::DST_IP,
1649 TransportProtocol::Udp,
1650 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1651 );
1652
1653 let nated_original_packet = PacketMetadata::<I>::new(
1654 I::SRC_IP,
1655 I::DST_IP,
1656 TransportProtocol::Udp,
1657 TransportPacketData::Generic { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT + 1 },
1658 );
1659
1660 let conn1 = Connection::Exclusive(
1661 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1662 &bindings_ctx,
1663 &original_packet,
1664 )
1665 .unwrap(),
1666 );
1667
1668 let mut conn2 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1671 &bindings_ctx,
1672 &original_packet,
1673 )
1674 .unwrap();
1675 conn2.inner.original_tuple = nated_original_packet.tuple();
1676 let conn2 = Connection::Exclusive(conn2);
1677
1678 let mut conn3 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1681 &bindings_ctx,
1682 &original_packet,
1683 )
1684 .unwrap();
1685 conn3.inner.reply_tuple = nated_original_packet.tuple().invert();
1686 let conn3 = Connection::Exclusive(conn3);
1687
1688 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn1), Ok((true, Some(_))));
1689 assert_matches!(
1690 table.finalize_connection(&mut bindings_ctx, conn2),
1691 Err(FinalizeConnectionError::Conflict)
1692 );
1693 assert_matches!(
1694 table.finalize_connection(&mut bindings_ctx, conn3),
1695 Err(FinalizeConnectionError::Conflict)
1696 );
1697 }
1698
1699 #[ip_test(I)]
1700 fn table_conflict_identical_connection<
1701 I: IpExt + crate::packets::testutil::internal::TestIpExt,
1702 >() {
1703 let mut bindings_ctx = FakeBindingsCtx::new();
1704 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1705
1706 let original_packet = PacketMetadata::<I>::new(
1707 I::SRC_IP,
1708 I::DST_IP,
1709 TransportProtocol::Udp,
1710 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1711 );
1712
1713 let conn = Connection::Exclusive(
1717 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1718 &bindings_ctx,
1719 &original_packet,
1720 )
1721 .unwrap(),
1722 );
1723 let finalized = assert_matches!(
1724 table.finalize_connection(&mut bindings_ctx, conn),
1725 Ok((true, Some(conn))) => conn
1726 );
1727
1728 let conn = Connection::Exclusive(
1729 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1730 &bindings_ctx,
1731 &original_packet,
1732 )
1733 .unwrap(),
1734 );
1735 let conn = assert_matches!(
1736 table.finalize_connection(&mut bindings_ctx, conn),
1737 Ok((false, Some(conn))) => conn
1738 );
1739 assert!(Arc::ptr_eq(&finalized, &conn));
1740 }
1741
1742 #[derive(Copy, Clone)]
1743 enum GcTrigger {
1744 Direct,
1746 Timer,
1748 }
1749
1750 #[ip_test(I)]
1751 #[test_case(GcTrigger::Direct)]
1752 #[test_case(GcTrigger::Timer)]
1753 fn garbage_collection<I: TestIpExt>(gc_trigger: GcTrigger) {
1754 fn perform_gc<I: TestIpExt>(
1755 core_ctx: &mut FakeCtx<I>,
1756 bindings_ctx: &mut FakeBindingsCtx<I>,
1757 gc_trigger: GcTrigger,
1758 ) {
1759 match gc_trigger {
1760 GcTrigger::Direct => core_ctx.conntrack().perform_gc(bindings_ctx),
1761 GcTrigger::Timer => {
1762 for timer in bindings_ctx
1763 .trigger_timers_until_instant(bindings_ctx.timer_ctx.instant.time, core_ctx)
1764 {
1765 assert_matches!(timer, FilterTimerId::ConntrackGc(_));
1766 }
1767 }
1768 }
1769 }
1770
1771 let mut bindings_ctx = FakeBindingsCtx::new();
1772 let mut core_ctx = FakeCtx::with_ip_routines(&mut bindings_ctx, IpRoutines::default());
1773
1774 let first_packet = PacketMetadata::<I>::new(
1775 I::SRC_IP,
1776 I::DST_IP,
1777 TransportProtocol::Udp,
1778 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1779 );
1780
1781 let second_packet = PacketMetadata::<I>::new(
1782 I::SRC_IP,
1783 I::DST_IP,
1784 TransportProtocol::Udp,
1785 TransportPacketData::Generic { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT },
1786 );
1787 let second_packet_reply = PacketMetadata::<I>::new(
1788 I::DST_IP,
1789 I::SRC_IP,
1790 TransportProtocol::Udp,
1791 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT + 1 },
1792 );
1793
1794 let first_tuple = first_packet.tuple();
1795 let first_tuple_reply = first_tuple.clone().invert();
1796 let second_tuple = second_packet.tuple();
1797 let second_tuple_reply = second_packet_reply.tuple();
1798
1799 let (conn, _dir) = core_ctx
1801 .conntrack()
1802 .get_connection_for_packet_and_update(&bindings_ctx, first_packet)
1803 .expect("packet should be valid")
1804 .expect("packet should be trackable");
1805 assert_matches!(
1806 core_ctx
1807 .conntrack()
1808 .finalize_connection(&mut bindings_ctx, conn)
1809 .expect("connection finalize should succeed"),
1810 (true, Some(_))
1811 );
1812 let (conn, _dir) = core_ctx
1813 .conntrack()
1814 .get_connection_for_packet_and_update(&bindings_ctx, second_packet)
1815 .expect("packet should be valid")
1816 .expect("packet should be trackable");
1817 assert_matches!(
1818 core_ctx
1819 .conntrack()
1820 .finalize_connection(&mut bindings_ctx, conn)
1821 .expect("connection finalize should succeed"),
1822 (true, Some(_))
1823 );
1824 assert!(core_ctx.conntrack().contains_tuple(&first_tuple));
1825 assert!(core_ctx.conntrack().contains_tuple(&second_tuple));
1826 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1827
1828 bindings_ctx.sleep(GC_INTERVAL);
1831 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1832 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1833 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1834 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1835 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1836 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1837
1838 let (conn, _dir) = core_ctx
1840 .conntrack()
1841 .get_connection_for_packet_and_update(&bindings_ctx, second_packet_reply)
1842 .expect("packet should be valid")
1843 .expect("packet should be trackable");
1844 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1845 assert_matches!(
1846 core_ctx
1847 .conntrack()
1848 .finalize_connection(&mut bindings_ctx, conn)
1849 .expect("connection finalize should succeed"),
1850 (false, Some(_))
1851 );
1852 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1853 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1854 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1855 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1856 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1857
1858 bindings_ctx.sleep(GC_INTERVAL);
1868 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1869 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1870 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1871 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1872 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1873 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1874
1875 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - 2 * GC_INTERVAL);
1879 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1880 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1881 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1882 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1883 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1884 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 2);
1885
1886 bindings_ctx.sleep(GC_INTERVAL);
1890 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1891 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1892 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1893 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), false);
1894 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), false);
1895 assert!(core_ctx.conntrack().inner.lock().table.is_empty());
1896 }
1897
1898 fn fill_table<I, E, BC>(
1899 bindings_ctx: &mut BC,
1900 table: &Table<I, E, BC>,
1901 entries: impl Iterator<Item = u32>,
1902 establishment_lifecycle: EstablishmentLifecycle,
1903 ) where
1904 I: IpExt + TestIpExt,
1905 E: Debug + Default + Send + Sync + PartialEq + CompatibleWith + 'static,
1906 BC: FilterBindingsTypes + TimerContext,
1907 {
1908 for i in entries {
1909 let (packet, reply_packet) = make_test_udp_packets(i);
1910 let packet = packet.conntrack_packet().unwrap();
1911 let reply_packet = reply_packet.conntrack_packet().unwrap();
1912
1913 let (conn, _dir) = table
1914 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1915 .expect("packet should be valid")
1916 .expect("packet should be trackable");
1917 assert_matches!(
1918 table
1919 .finalize_connection(bindings_ctx, conn)
1920 .expect("connection finalize should succeed"),
1921 (true, Some(_))
1922 );
1923
1924 if establishment_lifecycle >= EstablishmentLifecycle::SeenReply {
1925 let (conn, _dir) = table
1926 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet.clone())
1927 .expect("packet should be valid")
1928 .expect("packet should be trackable");
1929 assert_matches!(
1930 table
1931 .finalize_connection(bindings_ctx, conn)
1932 .expect("connection finalize should succeed"),
1933 (false, Some(_))
1934 );
1935
1936 if establishment_lifecycle >= EstablishmentLifecycle::Established {
1937 let (conn, _dir) = table
1938 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1939 .expect("packet should be valid")
1940 .expect("packet should be trackable");
1941 assert_matches!(
1942 table
1943 .finalize_connection(bindings_ctx, conn)
1944 .expect("connection finalize should succeed"),
1945 (false, Some(_))
1946 );
1947 }
1948 }
1949 }
1950 }
1951
1952 #[ip_test(I)]
1953 #[test_case(EstablishmentLifecycle::SeenOriginal; "existing connections unestablished")]
1954 #[test_case(EstablishmentLifecycle::SeenReply; "existing connections partially established")]
1955 #[test_case(EstablishmentLifecycle::Established; "existing connections established")]
1956 fn table_size_limit_evict_less_established<I: IpExt + TestIpExt>(
1957 existing_lifecycle: EstablishmentLifecycle,
1958 ) {
1959 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1960 bindings_ctx.sleep(Duration::from_secs(1));
1961 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1962
1963 fill_table(
1964 &mut bindings_ctx,
1965 &table,
1966 0..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
1967 existing_lifecycle,
1968 );
1969
1970 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
1974
1975 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
1976 let packet = packet.conntrack_packet().unwrap();
1977 let (conn, _dir) = table
1978 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1979 .expect("packet should be valid")
1980 .expect("packet should be trackable");
1981 if existing_lifecycle == EstablishmentLifecycle::Established {
1982 assert_matches!(
1985 table.finalize_connection(&mut bindings_ctx, conn),
1986 Err(FinalizeConnectionError::TableFull)
1987 );
1988
1989 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 - 1).try_into().unwrap());
1992 let packet = packet.conntrack_packet().unwrap();
1993 let (conn, _dir) = table
1994 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1995 .expect("packet should be valid")
1996 .expect("packet should be trackable");
1997 assert_matches!(
1998 table
1999 .finalize_connection(&mut bindings_ctx, conn)
2000 .expect("connection finalize should succeed"),
2001 (false, Some(_))
2002 );
2003 } else {
2004 assert_matches!(
2005 table
2006 .finalize_connection(&mut bindings_ctx, conn)
2007 .expect("connection finalize should succeed"),
2008 (true, Some(_))
2009 );
2010 }
2011 }
2012
2013 #[ip_test(I)]
2014 fn table_size_limit_evict_expired<I: IpExt + TestIpExt>() {
2015 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2016 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2017
2018 let evicted_tuple = {
2020 let (packet, _) = make_test_udp_packets(0);
2021 let packet = packet.conntrack_packet().unwrap();
2022 packet.tuple()
2023 };
2024 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::Established);
2025 bindings_ctx.sleep(Duration::from_secs(1));
2026 fill_table(
2027 &mut bindings_ctx,
2028 &table,
2029 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2030 EstablishmentLifecycle::Established,
2031 );
2032
2033 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2034 assert!(table.contains_tuple(&evicted_tuple));
2035
2036 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2037 let packet = packet.conntrack_packet().unwrap();
2038 let (conn, _dir) = table
2041 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2042 .expect("packet should be valid")
2043 .expect("packet should be trackable");
2044 assert_matches!(
2045 table.finalize_connection(&mut bindings_ctx, conn),
2046 Err(FinalizeConnectionError::TableFull)
2047 );
2048
2049 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - Duration::from_secs(1));
2052 let (conn, _dir) = table
2053 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2054 .expect("packet should be valid")
2055 .expect("packet should be trackable");
2056 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2057 assert!(!table.contains_tuple(&evicted_tuple));
2058 }
2059
2060 #[ip_test(I)]
2061 fn table_size_limit_less_established<I: IpExt + TestIpExt>() {
2062 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2063 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2064
2065 let evicted_tuple = {
2066 let (packet, _) = make_test_udp_packets(0);
2067 let packet = packet.conntrack_packet().unwrap();
2068 packet.tuple()
2069 };
2070 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::SeenOriginal);
2072 bindings_ctx.sleep(Duration::from_secs(1));
2073 fill_table(&mut bindings_ctx, &table, 1..=1, EstablishmentLifecycle::SeenOriginal);
2074 fill_table(
2075 &mut bindings_ctx,
2076 &table,
2077 2..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2078 EstablishmentLifecycle::SeenReply,
2079 );
2080
2081 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2082 assert!(table.contains_tuple(&evicted_tuple));
2083
2084 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2088 let packet = packet.conntrack_packet().unwrap();
2089 let (conn, _dir) = table
2090 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2091 .expect("packet should be valid")
2092 .expect("packet should be trackable");
2093 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2094 assert!(!table.contains_tuple(&evicted_tuple));
2095 }
2096
2097 #[cfg(target_os = "fuchsia")]
2098 #[ip_test(I)]
2099 fn inspect<I: IpExt + TestIpExt>() {
2100 use alloc::string::ToString;
2101 use diagnostics_assertions::assert_data_tree;
2102 use diagnostics_traits::FuchsiaInspector;
2103 use fuchsia_inspect::Inspector;
2104
2105 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2106 bindings_ctx.sleep(Duration::from_secs(1));
2107 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2108
2109 {
2110 let inspector = Inspector::new(Default::default());
2111 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2112 bindings_inspector.delegate_inspectable(&table);
2113
2114 let mut exec = fuchsia_async::TestExecutor::new();
2115
2116 assert_data_tree!(@executor exec, inspector, "root": {
2117 "table_limit_drops": 0u64,
2118 "table_limit_hits": 0u64,
2119 "num_entries": 0u64,
2120 "connections": {},
2121 });
2122 }
2123
2124 let (packet, _) = make_test_udp_packets::<I>(0);
2127 let packet = packet.conntrack_packet().unwrap();
2128 let (conn, _dir) = table
2129 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2130 .expect("packet should be valid")
2131 .expect("packet should be trackable");
2132 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
2133 assert_matches!(
2134 table
2135 .finalize_connection(&mut bindings_ctx, conn)
2136 .expect("connection finalize should succeed"),
2137 (true, Some(_))
2138 );
2139
2140 {
2141 let inspector = Inspector::new(Default::default());
2142 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2143 bindings_inspector.delegate_inspectable(&table);
2144
2145 let mut exec = fuchsia_async::TestExecutor::new();
2146 assert_data_tree!(@executor exec, inspector, "root": {
2147 "table_limit_drops": 0u64,
2148 "table_limit_hits": 0u64,
2149 "num_entries": 2u64,
2150 "connections": {
2151 "0": {
2152 "original_tuple": {
2153 "protocol": "UDP",
2154 "src_addr": I::SRC_IP.to_string(),
2155 "dst_addr": I::DST_IP.to_string(),
2156 "src_port_or_id": 0u64,
2157 "dst_port_or_id": 0u64,
2158 },
2159 "reply_tuple": {
2160 "protocol": "UDP",
2161 "src_addr": I::DST_IP.to_string(),
2162 "dst_addr": I::SRC_IP.to_string(),
2163 "src_port_or_id": 0u64,
2164 "dst_port_or_id": 0u64,
2165 },
2166 "external_data": {},
2167 "established": false,
2168 "last_packet_time": 1_000_000_000u64,
2169 }
2170 },
2171 });
2172 }
2173
2174 fill_table(
2176 &mut bindings_ctx,
2177 &table,
2178 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2179 EstablishmentLifecycle::Established,
2180 );
2181
2182 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2183
2184 let (packet, reply_packet) =
2187 make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2188 let packet = packet.conntrack_packet().unwrap();
2189 let reply_packet = reply_packet.conntrack_packet().unwrap();
2190 let (conn, _dir) = table
2191 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2192 .expect("packet should be valid")
2193 .expect("packet should be trackable");
2194 assert_matches!(
2195 table
2196 .finalize_connection(&mut bindings_ctx, conn)
2197 .expect("connection finalize should succeed"),
2198 (true, Some(_))
2199 );
2200 let (conn, _dir) = table
2201 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2202 .expect("packet should be valid")
2203 .expect("packet should be trackable");
2204 assert_matches!(
2205 table
2206 .finalize_connection(&mut bindings_ctx, conn)
2207 .expect("connection finalize should succeed"),
2208 (false, Some(_))
2209 );
2210 let (conn, _dir) = table
2211 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2212 .expect("packet should be valid")
2213 .expect("packet should be trackable");
2214 assert_matches!(
2215 table
2216 .finalize_connection(&mut bindings_ctx, conn)
2217 .expect("connection finalize should succeed"),
2218 (false, Some(_))
2219 );
2220
2221 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 + 1).try_into().unwrap());
2224 let packet = packet.conntrack_packet().unwrap();
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.finalize_connection(&mut bindings_ctx, conn),
2231 Err(FinalizeConnectionError::TableFull)
2232 );
2233
2234 {
2235 let inspector = Inspector::new(Default::default());
2236 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2237 bindings_inspector.delegate_inspectable(&table);
2238
2239 let mut exec = fuchsia_async::TestExecutor::new();
2240 assert_data_tree!(@executor exec, inspector, "root": contains {
2241 "table_limit_drops": 1u64,
2242 "table_limit_hits": 2u64,
2243 "num_entries": MAXIMUM_ENTRIES as u64,
2244 });
2245 }
2246 }
2247
2248 #[ip_test(I)]
2249 fn self_connected_socket<I: IpExt + TestIpExt>() {
2250 let mut bindings_ctx = FakeBindingsCtx::new();
2251 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2252
2253 let packet = PacketMetadata::<I>::new(
2254 I::SRC_IP,
2255 I::SRC_IP,
2256 TransportProtocol::Udp,
2257 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::SRC_PORT },
2258 );
2259
2260 let tuple = packet.tuple();
2261 let reply_tuple = tuple.clone().invert();
2262
2263 assert_eq!(tuple, reply_tuple);
2264
2265 let (conn, _dir) = table
2266 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2267 .expect("packet should be valid")
2268 .expect("packet should be trackable");
2269 let state = conn.state();
2270 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
2273
2274 assert_matches!(conn, Connection::Exclusive(_));
2275 assert!(!table.contains_tuple(&tuple));
2276
2277 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2279 assert!(table.contains_tuple(&tuple));
2280
2281 assert_eq!(table.inner.lock().table.len(), 1);
2284
2285 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP);
2286 table.perform_gc(&mut bindings_ctx);
2287
2288 assert!(table.inner.lock().table.is_empty());
2289 }
2290
2291 #[ip_test(I)]
2292 fn remove_entry_on_update<I: IpExt + TestIpExt>() {
2293 let mut bindings_ctx = FakeBindingsCtx::new();
2294 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2295
2296 let original_packet = PacketMetadata::<I>::new(
2297 I::SRC_IP,
2298 I::DST_IP,
2299 TransportProtocol::Tcp,
2300 TransportPacketData::Tcp {
2301 src_port: I::SRC_PORT,
2302 dst_port: I::DST_PORT,
2303 segment: SegmentHeader {
2304 seq: SeqNum::new(1024),
2305 wnd: UnscaledWindowSize::from(16u16),
2306 control: Some(Control::SYN),
2307 ..Default::default()
2308 },
2309 payload_len: 0,
2310 },
2311 );
2312
2313 let reply_packet = PacketMetadata::<I>::new(
2314 I::DST_IP,
2315 I::SRC_IP,
2316 TransportProtocol::Tcp,
2317 TransportPacketData::Tcp {
2318 src_port: I::DST_PORT,
2319 dst_port: I::SRC_PORT,
2320 segment: SegmentHeader {
2321 seq: SeqNum::new(0),
2322 ack: Some(SeqNum::new(1025)),
2323 wnd: UnscaledWindowSize::from(16u16),
2324 control: Some(Control::RST),
2325 ..Default::default()
2326 },
2327 payload_len: 0,
2328 },
2329 );
2330
2331 let tuple = original_packet.tuple();
2332 let reply_tuple = tuple.clone().invert();
2333
2334 let (conn, _dir) = table
2335 .get_connection_for_packet_and_update(&bindings_ctx, original_packet)
2336 .expect("packet should be valid")
2337 .expect("packet should be trackable");
2338 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2339
2340 assert!(table.contains_tuple(&tuple));
2341 assert!(table.contains_tuple(&reply_tuple));
2342
2343 let (conn, _dir) = table
2346 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2347 .expect("packet should be valid")
2348 .expect("packet should be trackable");
2349
2350 assert!(!table.contains_tuple(&tuple));
2351 assert!(!table.contains_tuple(&reply_tuple));
2352 assert!(table.inner.lock().table.is_empty());
2353
2354 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
2356
2357 assert!(!table.contains_tuple(&tuple));
2358 assert!(!table.contains_tuple(&reply_tuple));
2359 assert!(table.inner.lock().table.is_empty());
2360
2361 bindings_ctx.sleep(Duration::from_secs(60 * 60 * 24 * 6));
2363 table.perform_gc(&mut bindings_ctx);
2364 }
2365
2366 #[ip_test(I)]
2367 fn do_not_insert<I: IpExt + TestIpExt>() {
2368 let mut bindings_ctx = FakeBindingsCtx::new();
2369 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2370
2371 let packet = PacketMetadata::<I>::new(
2372 I::SRC_IP,
2373 I::DST_IP,
2374 TransportProtocol::Udp,
2375 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
2376 );
2377
2378 let tuple = packet.tuple();
2379 let reply_tuple = tuple.clone().invert();
2380
2381 let (conn, _dir) = table
2382 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2383 .expect("packet should be valid")
2384 .expect("packet should be trackable");
2385 let mut conn = assert_matches!(conn, Connection::Exclusive(conn) => conn);
2386 conn.do_not_insert = true;
2387 assert_matches!(
2388 table.finalize_connection(&mut bindings_ctx, Connection::Exclusive(conn)),
2389 Ok((false, None))
2390 );
2391
2392 assert!(!table.contains_tuple(&tuple));
2393 assert!(!table.contains_tuple(&reply_tuple));
2394 }
2395}