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 netstack3_hashmap::hash_map::Entry;
20use packet_formats::ip::{IpExt, IpProto, Ipv4Proto, Ipv6Proto};
21
22use crate::context::FilterBindingsTypes;
23use crate::logic::FilterTimerId;
24use crate::packets::TransportPacketData;
25use netstack3_base::sync::Mutex;
26use netstack3_base::{CoreTimerContext, Inspectable, Inspector, Instant, TimerContext};
27
28const GC_INTERVAL: Duration = Duration::from_secs(10);
30
31const CONNECTION_EXPIRY_TIME_UDP: Duration = Duration::from_secs(120);
37
38const CONNECTION_EXPIRY_OTHER: Duration = Duration::from_secs(30);
41
42pub(crate) const MAXIMUM_ENTRIES: usize = 100_000;
48
49pub struct Table<I: IpExt, E, BT: FilterBindingsTypes> {
55 inner: Mutex<TableInner<I, E, BT>>,
56}
57
58struct TableInner<I: IpExt, E, BT: FilterBindingsTypes> {
59 table: HashMap<Tuple<I>, Arc<ConnectionShared<I, E, BT>>>,
62 gc_timer: BT::Timer,
64 table_limit_hits: u32,
66 table_limit_drops: u32,
69}
70
71impl<I: IpExt, E, BT: FilterBindingsTypes> Table<I, E, BT> {
72 pub fn contains_tuple(&self, tuple: &Tuple<I>) -> bool {
79 self.inner.lock().table.contains_key(tuple)
80 }
81
82 pub(crate) fn get_shared_connection(
84 &self,
85 tuple: &Tuple<I>,
86 ) -> Option<Arc<ConnectionShared<I, E, BT>>> {
87 let guard = self.inner.lock();
88 let conn = guard.table.get(tuple)?;
89 Some(conn.clone())
90 }
91
92 pub fn get_connection(&self, tuple: &Tuple<I>) -> Option<Connection<I, E, BT>> {
94 let guard = self.inner.lock();
95 let conn = guard.table.get(tuple)?;
96 Some(Connection::Shared(conn.clone()))
97 }
98
99 #[cfg(feature = "testutils")]
104 pub fn num_entries(&self) -> usize {
105 self.inner.lock().table.len()
106 }
107
108 #[cfg(feature = "testutils")]
111 pub fn remove_connection(&mut self, tuple: &Tuple<I>) -> Option<Connection<I, E, BT>> {
112 let mut guard = self.inner.lock();
113
114 let conn = guard.table.remove(tuple)?;
116 let (original, reply) = (&conn.inner.original_tuple, &conn.inner.reply_tuple);
117
118 if original != reply {
121 if tuple == original {
122 assert!(guard.table.remove(reply).is_some());
123 } else {
124 assert!(guard.table.remove(original).is_some());
125 }
126 }
127
128 Some(Connection::Shared(conn))
129 }
130}
131
132fn schedule_gc<BC>(bindings_ctx: &mut BC, timer: &mut BC::Timer)
133where
134 BC: TimerContext,
135{
136 let _ = bindings_ctx.schedule_timer(GC_INTERVAL, timer);
137}
138
139impl<I, E, BC> Table<I, E, BC>
140where
141 I: IpExt,
142 BC: FilterBindingsTypes + TimerContext,
143{
144 pub(crate) fn new<CC>(bindings_ctx: &mut BC) -> Self
145 where
146 CC: CoreTimerContext<FilterTimerId<I>, BC>,
147 {
148 Self {
149 inner: Mutex::new(TableInner {
150 table: HashMap::new(),
151 gc_timer: CC::new_timer(
152 bindings_ctx,
153 FilterTimerId::ConntrackGc(IpVersionMarker::<I>::new()),
154 ),
155 table_limit_hits: 0,
156 table_limit_drops: 0,
157 }),
158 }
159 }
160}
161
162impl<I, E, BC> Table<I, E, BC>
163where
164 I: IpExt,
165 E: Debug + Default + Send + Sync + PartialEq + CompatibleWith + 'static,
166 BC: FilterBindingsTypes + TimerContext,
167{
168 pub(crate) fn finalize_connection(
177 &self,
178 bindings_ctx: &mut BC,
179 connection: Connection<I, E, BC>,
180 ) -> Result<(bool, Option<Arc<ConnectionShared<I, E, BC>>>), FinalizeConnectionError> {
181 let exclusive = match connection {
182 Connection::Exclusive(c) => c,
183 Connection::Shared(inner) => return Ok((false, Some(inner))),
187 };
188
189 if exclusive.do_not_insert {
190 return Ok((false, None));
191 }
192
193 let mut guard = self.inner.lock();
194
195 if guard.table.len() >= MAXIMUM_ENTRIES {
196 guard.table_limit_hits = guard.table_limit_hits.saturating_add(1);
197
198 struct Info<'a, I: IpExt, BT: FilterBindingsTypes> {
199 original_tuple: &'a Tuple<I>,
200 reply_tuple: &'a Tuple<I>,
201 lifecycle: EstablishmentLifecycle,
202 last_seen: BT::Instant,
203 }
204
205 let mut info: Option<Info<'_, I, BC>> = None;
206
207 let now = bindings_ctx.now();
208 for (_, conn) in &guard.table {
219 let state = conn.state.lock();
220 if state.is_expired(now) {
221 info = Some(Info {
222 original_tuple: &conn.inner.original_tuple,
223 reply_tuple: &conn.inner.reply_tuple,
224 lifecycle: state.establishment_lifecycle,
225 last_seen: state.last_packet_time,
226 });
227 break;
228 }
229
230 match state.establishment_lifecycle {
231 EstablishmentLifecycle::SeenOriginal | EstablishmentLifecycle::SeenReply => {
232 match &info {
233 None => {
234 info = Some(Info {
235 original_tuple: &conn.inner.original_tuple,
236 reply_tuple: &conn.inner.reply_tuple,
237 lifecycle: state.establishment_lifecycle,
238 last_seen: state.last_packet_time,
239 })
240 }
241 Some(existing) => {
242 if state.establishment_lifecycle < existing.lifecycle
243 || (state.establishment_lifecycle == existing.lifecycle
244 && state.last_packet_time < existing.last_seen)
245 {
246 info = Some(Info {
247 original_tuple: &conn.inner.original_tuple,
248 reply_tuple: &conn.inner.reply_tuple,
249 lifecycle: state.establishment_lifecycle,
250 last_seen: state.last_packet_time,
251 })
252 }
253 }
254 }
255 }
256 EstablishmentLifecycle::Established => {}
257 }
258 }
259
260 if let Some(Info { original_tuple, reply_tuple, .. }) = info {
261 let original_tuple = original_tuple.clone();
262 let reply_tuple = reply_tuple.clone();
263
264 assert!(guard.table.remove(&original_tuple).is_some());
265 if original_tuple != reply_tuple {
266 assert!(guard.table.remove(&reply_tuple).is_some());
267 }
268 } else {
269 guard.table_limit_drops = guard.table_limit_drops.saturating_add(1);
270 return Err(FinalizeConnectionError::TableFull);
271 }
272 }
273
274 if guard.table.contains_key(&exclusive.inner.original_tuple)
287 || guard.table.contains_key(&exclusive.inner.reply_tuple)
288 {
289 let conn = if let Some(conn) = guard.table.get(&exclusive.inner.original_tuple) {
302 conn
303 } else {
304 guard
305 .table
306 .get(&exclusive.inner.reply_tuple)
307 .expect("checked that tuple is in table and table is locked")
308 };
309 if conn.compatible_with(&exclusive) {
310 return Ok((false, Some(conn.clone())));
311 }
312
313 Err(FinalizeConnectionError::Conflict)
315 } else {
316 let shared = exclusive.make_shared();
317 let clone = Arc::clone(&shared);
318
319 assert_matches!(
320 guard.table.insert(shared.inner.original_tuple.clone(), shared.clone()),
321 None
322 );
323
324 if shared.inner.reply_tuple != shared.inner.original_tuple {
325 assert_matches!(guard.table.insert(shared.inner.reply_tuple.clone(), shared), None);
326 }
327
328 if bindings_ctx.scheduled_instant(&mut guard.gc_timer).is_none() {
334 schedule_gc(bindings_ctx, &mut guard.gc_timer);
335 }
336
337 Ok((true, Some(clone)))
338 }
339 }
340}
341
342impl<I, E, BC> Table<I, E, BC>
343where
344 I: IpExt,
345 E: Debug + Default,
346 BC: FilterBindingsTypes + TimerContext,
347{
348 pub(crate) fn get_connection_for_packet_and_update(
358 &self,
359 bindings_ctx: &BC,
360 packet: PacketMetadata<I>,
361 ) -> Result<Option<(Connection<I, E, BC>, ConnectionDirection)>, GetConnectionError<I, E, BC>>
362 {
363 let tuple = packet.tuple();
364 let mut connection = match self.inner.lock().table.get(&tuple) {
365 Some(connection) => Connection::Shared(connection.clone()),
366 None => match ConnectionExclusive::from_deconstructed_packet(bindings_ctx, &packet) {
367 None => return Ok(None),
368 Some(c) => Connection::Exclusive(c),
369 },
370 };
371
372 let direction = match connection.direction(&tuple) {
373 Some(direction) => direction,
374 None => unreachable!(
375 "tuple didn't match connection after looking up in map: {tuple:?} {connection:?}"
376 ),
377 };
378
379 match connection.update(bindings_ctx, &packet, direction) {
380 Ok(ConnectionUpdateAction::NoAction) => Ok(Some((connection, direction))),
381 Ok(ConnectionUpdateAction::RemoveEntry) => match connection {
382 Connection::Exclusive(mut conn) => {
383 conn.do_not_insert = true;
384 Ok(Some((Connection::Exclusive(conn), direction)))
385 }
386 Connection::Shared(conn) => {
387 let mut guard = self.inner.lock();
396
397 let (original_tuple, reply_tuple) =
398 (&conn.inner.original_tuple, &conn.inner.reply_tuple);
399
400 let mut remove_tuple = |tuple| {
403 match guard.table.entry(tuple) {
404 Entry::Occupied(occupied) => {
405 if Arc::ptr_eq(occupied.get(), &conn) {
407 let _ = occupied.remove();
408 true
409 } else {
410 false
411 }
412 }
413 Entry::Vacant(_) => false,
414 }
415 };
416
417 let original_removed = remove_tuple(original_tuple.clone());
418 if reply_tuple != original_tuple {
419 let reply_removed = remove_tuple(reply_tuple.clone());
420
421 assert_eq!(
426 original_removed, reply_removed,
427 "Only one tuple removed: {:?}={} {:?}={}",
428 original_tuple, original_removed, reply_tuple, reply_removed,
429 );
430 }
431
432 Ok(Some((Connection::Shared(conn), direction)))
433 }
434 },
435 Err(ConnectionUpdateError::InvalidPacket) => {
436 Err(GetConnectionError::InvalidPacket(connection, direction))
437 }
438 }
439 }
440
441 pub(crate) fn perform_gc(&self, bindings_ctx: &mut BC) {
442 let now = bindings_ctx.now();
443 let mut guard = self.inner.lock();
444
445 let to_remove: Vec<_> = guard
454 .table
455 .iter()
456 .filter_map(|(tuple, conn)| {
457 if *tuple == conn.inner.original_tuple && conn.is_expired(now) {
458 Some((conn.inner.original_tuple.clone(), conn.inner.reply_tuple.clone()))
459 } else {
460 None
461 }
462 })
463 .collect();
464
465 for (original_tuple, reply_tuple) in to_remove {
466 assert!(guard.table.remove(&original_tuple).is_some());
467 if reply_tuple != original_tuple {
468 assert!(guard.table.remove(&reply_tuple).is_some());
469 }
470 }
471
472 if !guard.table.is_empty() {
478 schedule_gc(bindings_ctx, &mut guard.gc_timer);
479 }
480 }
481}
482
483impl<I, E, BT> Inspectable for Table<I, E, BT>
484where
485 I: IpExt,
486 E: Inspectable,
487 BT: FilterBindingsTypes,
488{
489 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
490 let guard = self.inner.lock();
491
492 inspector.record_usize("num_entries", guard.table.len());
493 inspector.record_uint("table_limit_hits", guard.table_limit_hits);
494 inspector.record_uint("table_limit_drops", guard.table_limit_drops);
495
496 inspector.record_child("connections", |inspector| {
497 guard
498 .table
499 .iter()
500 .filter_map(|(tuple, connection)| {
501 if *tuple == connection.inner.original_tuple { Some(connection) } else { None }
502 })
503 .for_each(|connection| {
504 inspector.record_unnamed_child(|inspector| {
505 inspector.delegate_inspectable(connection.as_ref())
506 });
507 });
508 });
509 }
510}
511
512#[derive(Debug, Clone, PartialEq, Eq, Hash, GenericOverIp)]
514#[generic_over_ip(I, Ip)]
515pub struct Tuple<I: IpExt> {
516 pub protocol: TransportProtocol,
518 pub src_addr: I::Addr,
520 pub dst_addr: I::Addr,
522 pub src_port_or_id: u16,
524 pub dst_port_or_id: u16,
526}
527
528impl<I: IpExt> Tuple<I> {
529 fn new(
530 src_addr: I::Addr,
531 dst_addr: I::Addr,
532 src_port_or_id: u16,
533 dst_port_or_id: u16,
534 protocol: TransportProtocol,
535 ) -> Self {
536 Self { protocol, src_addr, dst_addr, src_port_or_id, dst_port_or_id }
537 }
538
539 pub(crate) fn invert(self) -> Tuple<I> {
545 Self {
548 protocol: self.protocol,
549 src_addr: self.dst_addr,
550 dst_addr: self.src_addr,
551 src_port_or_id: self.dst_port_or_id,
552 dst_port_or_id: self.src_port_or_id,
553 }
554 }
555}
556
557impl<I: IpExt> Inspectable for Tuple<I> {
558 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
559 inspector.record_debug("protocol", self.protocol);
560 inspector.record_ip_addr("src_addr", self.src_addr);
561 inspector.record_ip_addr("dst_addr", self.dst_addr);
562 inspector.record_usize("src_port_or_id", self.src_port_or_id);
563 inspector.record_usize("dst_port_or_id", self.dst_port_or_id);
564 }
565}
566
567#[derive(Debug, Copy, Clone, PartialEq, Eq)]
569pub enum ConnectionDirection {
570 Original,
573
574 Reply,
577}
578
579#[derive(Debug)]
581pub(crate) enum FinalizeConnectionError {
582 Conflict,
585
586 TableFull,
588}
589
590#[derive(Debug, PartialEq, Eq)]
592enum ConnectionUpdateAction {
593 NoAction,
595
596 RemoveEntry,
598}
599
600#[derive(Debug, PartialEq, Eq)]
602enum ConnectionUpdateError {
603 InvalidPacket,
606}
607
608#[derive(Derivative)]
610#[derivative(Debug(bound = "E: Debug"))]
611pub(crate) enum GetConnectionError<I: IpExt, E, BT: FilterBindingsTypes> {
612 InvalidPacket(Connection<I, E, BT>, ConnectionDirection),
614}
615
616#[derive(Derivative)]
619#[derivative(Debug(bound = "E: Debug"))]
620pub enum Connection<I: IpExt, E, BT: FilterBindingsTypes> {
621 Exclusive(ConnectionExclusive<I, E, BT>),
624
625 Shared(Arc<ConnectionShared<I, E, BT>>),
628}
629
630#[derive(Debug)]
633pub enum WeakConnectionError {
634 EntryRemoved,
636 InvalidEntry,
639}
640
641#[derive(Debug, Clone)]
651pub struct WeakConnection(pub(crate) Weak<dyn Any + Send + Sync>);
652
653impl WeakConnection {
654 pub fn new<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
657 conn: &Connection<I, E, BT>,
658 ) -> Option<Self> {
659 let shared = match conn {
660 Connection::Exclusive(_) => return None,
661 Connection::Shared(shared) => shared,
662 };
663 let weak = Arc::downgrade(shared);
664 Some(Self(weak))
665 }
666
667 pub fn into_inner<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
676 self,
677 ) -> Result<Connection<I, E, BT>, WeakConnectionError> {
678 let Self(inner) = self;
679 let shared = inner
680 .upgrade()
681 .ok_or(WeakConnectionError::EntryRemoved)?
682 .downcast()
683 .map_err(|_err: Arc<_>| WeakConnectionError::InvalidEntry)?;
684 Ok(Connection::Shared(shared))
685 }
686}
687
688impl<I: IpExt, E, BT: FilterBindingsTypes> Connection<I, E, BT> {
689 pub fn original_tuple(&self) -> &Tuple<I> {
691 match self {
692 Connection::Exclusive(c) => &c.inner.original_tuple,
693 Connection::Shared(c) => &c.inner.original_tuple,
694 }
695 }
696
697 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
699 match self {
700 Connection::Exclusive(c) => &c.inner.reply_tuple,
701 Connection::Shared(c) => &c.inner.reply_tuple,
702 }
703 }
704
705 pub fn external_data(&self) -> &E {
707 match self {
708 Connection::Exclusive(c) => &c.inner.external_data,
709 Connection::Shared(c) => &c.inner.external_data,
710 }
711 }
712
713 pub(crate) fn direction(&self, tuple: &Tuple<I>) -> Option<ConnectionDirection> {
716 let (original, reply) = match self {
717 Connection::Exclusive(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
718 Connection::Shared(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
719 };
720
721 if tuple == reply {
729 Some(ConnectionDirection::Reply)
730 } else if tuple == original {
731 Some(ConnectionDirection::Original)
732 } else {
733 None
734 }
735 }
736
737 #[cfg(test)]
739 pub(crate) fn state(&self) -> ConnectionState<BT> {
740 match self {
741 Connection::Exclusive(c) => c.state.clone(),
742 Connection::Shared(c) => c.state.lock().clone(),
743 }
744 }
745}
746
747impl<I, E, BC> Connection<I, E, BC>
748where
749 I: IpExt,
750 BC: FilterBindingsTypes + TimerContext,
751{
752 fn update(
753 &mut self,
754 bindings_ctx: &BC,
755 packet: &PacketMetadata<I>,
756 direction: ConnectionDirection,
757 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
758 match packet {
759 PacketMetadata::Full { transport_data, .. } => {
760 let now = bindings_ctx.now();
761 match self {
762 Connection::Exclusive(c) => c.state.update(direction, &transport_data, now),
763 Connection::Shared(c) => c.state.lock().update(direction, &transport_data, now),
764 }
765 }
766 PacketMetadata::IcmpError(_) => Ok(ConnectionUpdateAction::NoAction),
767 }
768 }
769}
770
771#[derive(Derivative)]
773#[derivative(Debug(bound = "E: Debug"), PartialEq(bound = "E: PartialEq"))]
774pub struct ConnectionCommon<I: IpExt, E> {
775 pub(crate) original_tuple: Tuple<I>,
778
779 pub(crate) reply_tuple: Tuple<I>,
782
783 pub(crate) external_data: E,
787}
788
789impl<I: IpExt, E: Inspectable> Inspectable for ConnectionCommon<I, E> {
790 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
791 inspector.record_child("original_tuple", |inspector| {
792 inspector.delegate_inspectable(&self.original_tuple);
793 });
794
795 inspector.record_child("reply_tuple", |inspector| {
796 inspector.delegate_inspectable(&self.reply_tuple);
797 });
798
799 inspector.record_child("external_data", |inspector| {
803 inspector.delegate_inspectable(&self.external_data);
804 });
805 }
806}
807
808#[derive(Debug, Clone)]
809enum ProtocolState {
810 Tcp(tcp::Connection),
811 Udp,
812 Other,
813}
814
815impl ProtocolState {
816 fn update(
817 &mut self,
818 dir: ConnectionDirection,
819 transport_data: &TransportPacketData,
820 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
821 match self {
822 ProtocolState::Tcp(tcp_conn) => {
823 let (segment, payload_len) = assert_matches!(
824 transport_data,
825 TransportPacketData::Tcp { segment, payload_len, .. } => (segment, payload_len)
826 );
827 tcp_conn.update(&segment, *payload_len, dir)
828 }
829 ProtocolState::Udp | ProtocolState::Other => Ok(ConnectionUpdateAction::NoAction),
830 }
831 }
832}
833
834#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
845enum EstablishmentLifecycle {
846 SeenOriginal,
847 SeenReply,
848 Established,
849}
850
851impl EstablishmentLifecycle {
852 fn update(self, dir: ConnectionDirection) -> Self {
853 match self {
854 EstablishmentLifecycle::SeenOriginal => match dir {
855 ConnectionDirection::Original => self,
856 ConnectionDirection::Reply => EstablishmentLifecycle::SeenReply,
857 },
858 EstablishmentLifecycle::SeenReply => match dir {
859 ConnectionDirection::Original => EstablishmentLifecycle::Established,
860 ConnectionDirection::Reply => self,
861 },
862 EstablishmentLifecycle::Established => self,
863 }
864 }
865}
866
867#[derive(Derivative)]
869#[derivative(Clone(bound = ""), Debug(bound = ""))]
870pub(crate) struct ConnectionState<BT: FilterBindingsTypes> {
871 last_packet_time: BT::Instant,
874
875 establishment_lifecycle: EstablishmentLifecycle,
877
878 protocol_state: ProtocolState,
880}
881
882impl<BT: FilterBindingsTypes> ConnectionState<BT> {
883 fn update(
884 &mut self,
885 dir: ConnectionDirection,
886 transport_data: &TransportPacketData,
887 now: BT::Instant,
888 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
889 let action = self.protocol_state.update(dir, transport_data)?;
890
891 if self.last_packet_time < now {
892 self.last_packet_time = now;
893 }
894
895 self.establishment_lifecycle = self.establishment_lifecycle.update(dir);
896
897 Ok(action)
898 }
899
900 fn is_expired(&self, now: BT::Instant) -> bool {
901 let duration = now.saturating_duration_since(self.last_packet_time);
902
903 let expiry_duration = match &self.protocol_state {
904 ProtocolState::Tcp(tcp_conn) => tcp_conn.expiry_duration(self.establishment_lifecycle),
905 ProtocolState::Udp => CONNECTION_EXPIRY_TIME_UDP,
906 ProtocolState::Other => CONNECTION_EXPIRY_OTHER,
911 };
912
913 duration >= expiry_duration
914 }
915}
916
917impl<BT: FilterBindingsTypes> Inspectable for ConnectionState<BT> {
918 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
919 inspector.record_bool(
920 "established",
921 match self.establishment_lifecycle {
922 EstablishmentLifecycle::SeenOriginal | EstablishmentLifecycle::SeenReply => false,
923 EstablishmentLifecycle::Established => true,
924 },
925 );
926 inspector.record_inspectable_value("last_packet_time", &self.last_packet_time);
927 }
928}
929
930#[derive(Derivative)]
937#[derivative(Debug(bound = "E: Debug"))]
938pub struct ConnectionExclusive<I: IpExt, E, BT: FilterBindingsTypes> {
939 pub(crate) inner: ConnectionCommon<I, E>,
940 pub(crate) state: ConnectionState<BT>,
941
942 do_not_insert: bool,
947}
948
949impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionExclusive<I, E, BT> {
950 fn make_shared(self) -> Arc<ConnectionShared<I, E, BT>> {
953 Arc::new(ConnectionShared { inner: self.inner, state: Mutex::new(self.state) })
954 }
955
956 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
957 &self.inner.reply_tuple
958 }
959
960 pub(crate) fn rewrite_reply_dst_addr(&mut self, addr: I::Addr) {
961 self.inner.reply_tuple.dst_addr = addr;
962 }
963
964 pub(crate) fn rewrite_reply_src_addr(&mut self, addr: I::Addr) {
965 self.inner.reply_tuple.src_addr = addr;
966 }
967
968 pub(crate) fn rewrite_reply_src_port_or_id(&mut self, port_or_id: u16) {
969 self.inner.reply_tuple.src_port_or_id = port_or_id;
970 match self.inner.reply_tuple.protocol {
971 TransportProtocol::Icmp => {
972 self.inner.reply_tuple.dst_port_or_id = port_or_id;
980 }
981 TransportProtocol::Tcp | TransportProtocol::Udp | TransportProtocol::Other(_) => {}
982 }
983 }
984
985 pub(crate) fn rewrite_reply_dst_port_or_id(&mut self, port_or_id: u16) {
986 self.inner.reply_tuple.dst_port_or_id = port_or_id;
987 match self.inner.reply_tuple.protocol {
988 TransportProtocol::Icmp => {
989 self.inner.reply_tuple.src_port_or_id = port_or_id;
997 }
998 TransportProtocol::Tcp | TransportProtocol::Udp | TransportProtocol::Other(_) => {}
999 }
1000 }
1001}
1002
1003impl<I, E, BC> ConnectionExclusive<I, E, BC>
1004where
1005 I: IpExt,
1006 E: Default,
1007 BC: FilterBindingsTypes + TimerContext,
1008{
1009 pub(crate) fn from_deconstructed_packet(
1010 bindings_ctx: &BC,
1011 packet_metadata: &PacketMetadata<I>,
1012 ) -> Option<Self> {
1013 let (tuple, transport_data) = match packet_metadata {
1014 PacketMetadata::Full { tuple, transport_data } => (tuple, transport_data),
1015 PacketMetadata::IcmpError(_) => return None,
1016 };
1017
1018 let reply_tuple = tuple.clone().invert();
1019 let self_connected = reply_tuple == *tuple;
1020
1021 Some(Self {
1022 inner: ConnectionCommon {
1023 original_tuple: tuple.clone(),
1024 reply_tuple,
1025 external_data: E::default(),
1026 },
1027 state: ConnectionState {
1028 last_packet_time: bindings_ctx.now(),
1029 establishment_lifecycle: EstablishmentLifecycle::SeenOriginal,
1030 protocol_state: match tuple.protocol {
1031 TransportProtocol::Tcp => {
1032 let (segment, payload_len) = match transport_data.tcp_segment_and_len() {
1033 Some(v) => v,
1034 None => unreachable!(
1038 "protocol was TCP, but didn't have TCP info: {transport_data:?}"
1039 ),
1040 };
1041
1042 ProtocolState::Tcp(tcp::Connection::new(
1043 segment,
1044 payload_len,
1045 self_connected,
1046 )?)
1047 }
1048 TransportProtocol::Udp => ProtocolState::Udp,
1049 TransportProtocol::Icmp | TransportProtocol::Other(_) => ProtocolState::Other,
1050 },
1051 },
1052 do_not_insert: false,
1053 })
1054 }
1055}
1056
1057#[derive(Derivative)]
1063#[derivative(Debug(bound = "E: Debug"))]
1064pub struct ConnectionShared<I: IpExt, E, BT: FilterBindingsTypes> {
1065 inner: ConnectionCommon<I, E>,
1066 state: Mutex<ConnectionState<BT>>,
1067}
1068
1069#[allow(missing_docs)]
1071#[derive(Copy, Clone, PartialEq, Eq, Hash, GenericOverIp)]
1072#[generic_over_ip()]
1073pub enum TransportProtocol {
1074 Tcp,
1075 Udp,
1076 Icmp,
1077 Other(u8),
1078}
1079
1080impl From<Ipv4Proto> for TransportProtocol {
1081 fn from(value: Ipv4Proto) -> Self {
1082 match value {
1083 Ipv4Proto::Proto(IpProto::Tcp) => TransportProtocol::Tcp,
1084 Ipv4Proto::Proto(IpProto::Udp) => TransportProtocol::Udp,
1085 Ipv4Proto::Icmp => TransportProtocol::Icmp,
1086 v => TransportProtocol::Other(v.into()),
1087 }
1088 }
1089}
1090
1091impl From<Ipv6Proto> for TransportProtocol {
1092 fn from(value: Ipv6Proto) -> Self {
1093 match value {
1094 Ipv6Proto::Proto(IpProto::Tcp) => TransportProtocol::Tcp,
1095 Ipv6Proto::Proto(IpProto::Udp) => TransportProtocol::Udp,
1096 Ipv6Proto::Icmpv6 => TransportProtocol::Icmp,
1097 v => TransportProtocol::Other(v.into()),
1098 }
1099 }
1100}
1101
1102impl From<IpProto> for TransportProtocol {
1103 fn from(value: IpProto) -> Self {
1104 match value {
1105 IpProto::Tcp => TransportProtocol::Tcp,
1106 IpProto::Udp => TransportProtocol::Udp,
1107 v @ IpProto::Reserved => TransportProtocol::Other(v.into()),
1108 }
1109 }
1110}
1111
1112impl Display for TransportProtocol {
1113 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1114 match self {
1115 TransportProtocol::Tcp => write!(f, "TCP"),
1116 TransportProtocol::Udp => write!(f, "UDP"),
1117 TransportProtocol::Icmp => write!(f, "ICMP"),
1118 TransportProtocol::Other(n) => write!(f, "Other({n})"),
1119 }
1120 }
1121}
1122
1123impl Debug for TransportProtocol {
1124 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1125 Display::fmt(&self, f)
1126 }
1127}
1128
1129impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1130 fn is_expired(&self, now: BT::Instant) -> bool {
1131 self.state.lock().is_expired(now)
1132 }
1133}
1134
1135impl<I: IpExt, E: CompatibleWith, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1136 pub(crate) fn compatible_with(&self, conn: &ConnectionExclusive<I, E, BT>) -> bool {
1140 self.inner.original_tuple == conn.inner.original_tuple
1141 && self.inner.reply_tuple == conn.inner.reply_tuple
1142 && self.inner.external_data.compatible_with(&conn.inner.external_data)
1143 }
1144}
1145
1146impl<I: IpExt, E: Inspectable, BT: FilterBindingsTypes> Inspectable for ConnectionShared<I, E, BT> {
1147 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
1148 inspector.delegate_inspectable(&self.inner);
1149 inspector.delegate_inspectable(&*self.state.lock());
1150 }
1151}
1152
1153pub trait CompatibleWith {
1156 fn compatible_with(&self, other: &Self) -> bool;
1159}
1160
1161#[derive(Debug, Clone, PartialEq, Eq)]
1166pub enum PacketMetadata<I: IpExt> {
1167 Full { tuple: Tuple<I>, transport_data: TransportPacketData },
1168 IcmpError(Tuple<I>),
1169}
1170
1171impl<I: IpExt> PacketMetadata<I> {
1172 pub(crate) fn new(
1173 src_addr: I::Addr,
1174 dst_addr: I::Addr,
1175 protocol: TransportProtocol,
1176 transport_data: TransportPacketData,
1177 ) -> Self {
1178 match protocol {
1179 TransportProtocol::Tcp => {
1180 assert_matches!(transport_data, TransportPacketData::Tcp { .. })
1181 }
1182 TransportProtocol::Udp | TransportProtocol::Icmp | TransportProtocol::Other(_) => {
1183 assert_matches!(transport_data, TransportPacketData::Generic { .. })
1184 }
1185 }
1186
1187 Self::Full {
1188 tuple: Tuple::new(
1189 src_addr,
1190 dst_addr,
1191 transport_data.src_port(),
1192 transport_data.dst_port(),
1193 protocol,
1194 ),
1195 transport_data,
1196 }
1197 }
1198
1199 pub(crate) fn new_from_icmp_error(
1200 src_addr: I::Addr,
1201 dst_addr: I::Addr,
1202 src_port: u16,
1203 dst_port: u16,
1204 protocol: TransportProtocol,
1205 ) -> Self {
1206 Self::IcmpError(Tuple::new(src_addr, dst_addr, src_port, dst_port, protocol))
1207 }
1208
1209 pub(crate) fn tuple(&self) -> Tuple<I> {
1210 match self {
1211 PacketMetadata::Full { tuple, .. } => tuple.clone(),
1212 PacketMetadata::IcmpError(tuple) => tuple.clone().invert(),
1240 }
1241 }
1242}
1243
1244#[cfg(test)]
1245pub(crate) mod testutils {
1246 use crate::packets::testutil::internal::{FakeIpPacket, FakeUdpPacket, TestIpExt};
1247
1248 pub(crate) fn make_test_udp_packets<I: TestIpExt>(
1251 index: u32,
1252 ) -> (FakeIpPacket<I, FakeUdpPacket>, FakeIpPacket<I, FakeUdpPacket>) {
1253 let src_port = (index % (u16::MAX as u32)) as u16;
1256 let dst_port = (index / (u16::MAX as u32)) as u16;
1257
1258 let packet = FakeIpPacket::<I, _> {
1259 src_ip: I::SRC_IP,
1260 dst_ip: I::DST_IP,
1261 body: FakeUdpPacket { src_port, dst_port },
1262 };
1263 let reply_packet = FakeIpPacket::<I, _> {
1264 src_ip: I::DST_IP,
1265 dst_ip: I::SRC_IP,
1266 body: FakeUdpPacket { src_port: dst_port, dst_port: src_port },
1267 };
1268
1269 (packet, reply_packet)
1270 }
1271}
1272
1273#[cfg(test)]
1274mod tests {
1275 use assert_matches::assert_matches;
1276 use ip_test_macro::ip_test;
1277 use netstack3_base::testutil::FakeTimerCtxExt;
1278 use netstack3_base::{Control, IntoCoreTimerCtx, SegmentHeader, SeqNum, UnscaledWindowSize};
1279 use test_case::test_case;
1280
1281 use super::testutils::make_test_udp_packets;
1282 use super::*;
1283 use crate::context::testutil::{FakeBindingsCtx, FakeCtx};
1284 use crate::packets::IpPacket;
1285 use crate::packets::testutil::internal::ArbitraryValue;
1286 use crate::state::IpRoutines;
1287 use crate::testutil::TestIpExt;
1288
1289 impl CompatibleWith for () {
1290 fn compatible_with(&self, (): &()) -> bool {
1291 true
1292 }
1293 }
1294
1295 #[test_case(
1296 EstablishmentLifecycle::SeenOriginal,
1297 ConnectionDirection::Original
1298 => EstablishmentLifecycle::SeenOriginal
1299 )]
1300 #[test_case(
1301 EstablishmentLifecycle::SeenOriginal,
1302 ConnectionDirection::Reply
1303 => EstablishmentLifecycle::SeenReply
1304 )]
1305 #[test_case(
1306 EstablishmentLifecycle::SeenReply,
1307 ConnectionDirection::Original
1308 => EstablishmentLifecycle::Established
1309 )]
1310 #[test_case(
1311 EstablishmentLifecycle::SeenReply,
1312 ConnectionDirection::Reply
1313 => EstablishmentLifecycle::SeenReply
1314 )]
1315 #[test_case(
1316 EstablishmentLifecycle::Established,
1317 ConnectionDirection::Original
1318 => EstablishmentLifecycle::Established
1319 )]
1320 #[test_case(
1321 EstablishmentLifecycle::Established,
1322 ConnectionDirection::Reply
1323 => EstablishmentLifecycle::Established
1324 )]
1325 fn establishment_lifecycle_test(
1326 lifecycle: EstablishmentLifecycle,
1327 dir: ConnectionDirection,
1328 ) -> EstablishmentLifecycle {
1329 lifecycle.update(dir)
1330 }
1331
1332 #[ip_test(I)]
1333 #[test_case(TransportProtocol::Udp)]
1334 #[test_case(TransportProtocol::Tcp)]
1335 fn tuple_invert_udp_tcp<I: IpExt + TestIpExt>(protocol: TransportProtocol) {
1336 let orig_tuple = Tuple::<I> {
1337 protocol: protocol,
1338 src_addr: I::SRC_IP,
1339 dst_addr: I::DST_IP,
1340 src_port_or_id: I::SRC_PORT,
1341 dst_port_or_id: I::DST_PORT,
1342 };
1343
1344 let expected = Tuple::<I> {
1345 protocol: protocol,
1346 src_addr: I::DST_IP,
1347 dst_addr: I::SRC_IP,
1348 src_port_or_id: I::DST_PORT,
1349 dst_port_or_id: I::SRC_PORT,
1350 };
1351
1352 let inverted = orig_tuple.invert();
1353
1354 assert_eq!(inverted, expected);
1355 }
1356
1357 #[ip_test(I)]
1358 fn tuple_from_tcp_packet<I: IpExt + TestIpExt>() {
1359 let expected = Tuple::<I> {
1360 protocol: TransportProtocol::Tcp,
1361 src_addr: I::SRC_IP,
1362 dst_addr: I::DST_IP,
1363 src_port_or_id: I::SRC_PORT,
1364 dst_port_or_id: I::DST_PORT,
1365 };
1366
1367 let packet = PacketMetadata::<I>::new(
1368 I::SRC_IP,
1369 I::DST_IP,
1370 TransportProtocol::Tcp,
1371 TransportPacketData::Tcp {
1372 src_port: I::SRC_PORT,
1373 dst_port: I::DST_PORT,
1374 segment: SegmentHeader::arbitrary_value(),
1375 payload_len: 4,
1376 },
1377 );
1378
1379 assert_eq!(packet.tuple(), expected);
1380 }
1381
1382 #[ip_test(I)]
1383 fn connection_from_tuple<I: IpExt + TestIpExt>() {
1384 let bindings_ctx = FakeBindingsCtx::<I>::new();
1385
1386 let packet = PacketMetadata::<I>::new(
1387 I::SRC_IP,
1388 I::DST_IP,
1389 TransportProtocol::Udp,
1390 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1391 );
1392 let original_tuple = packet.tuple();
1393 let reply_tuple = packet.tuple().invert();
1394
1395 let connection =
1396 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1397 .unwrap();
1398
1399 assert_eq!(&connection.inner.original_tuple, &original_tuple);
1400 assert_eq!(&connection.inner.reply_tuple, &reply_tuple);
1401 }
1402
1403 #[ip_test(I)]
1404 fn connection_make_shared_has_same_underlying_info<I: IpExt + TestIpExt>() {
1405 let bindings_ctx = FakeBindingsCtx::<I>::new();
1406
1407 let packet = PacketMetadata::<I>::new(
1408 I::SRC_IP,
1409 I::DST_IP,
1410 TransportProtocol::Udp,
1411 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1412 );
1413 let original_tuple = packet.tuple();
1414 let reply_tuple = original_tuple.clone().invert();
1415
1416 let mut connection =
1417 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1418 connection.inner.external_data = 1234;
1419 let shared = connection.make_shared();
1420
1421 assert_eq!(shared.inner.original_tuple, original_tuple);
1422 assert_eq!(shared.inner.reply_tuple, reply_tuple);
1423 assert_eq!(shared.inner.external_data, 1234);
1424 }
1425
1426 enum ConnectionKind {
1427 Exclusive,
1428 Shared,
1429 }
1430
1431 #[ip_test(I)]
1432 #[test_case(ConnectionKind::Exclusive)]
1433 #[test_case(ConnectionKind::Shared)]
1434 fn connection_getters<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1435 let bindings_ctx = FakeBindingsCtx::<I>::new();
1436
1437 let packet = PacketMetadata::<I>::new(
1438 I::SRC_IP,
1439 I::DST_IP,
1440 TransportProtocol::Udp,
1441 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1442 );
1443 let original_tuple = packet.tuple();
1444 let reply_tuple = original_tuple.clone().invert();
1445
1446 let mut connection =
1447 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1448 connection.inner.external_data = 1234;
1449
1450 let connection = match connection_kind {
1451 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1452 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1453 };
1454
1455 assert_eq!(connection.original_tuple(), &original_tuple);
1456 assert_eq!(connection.reply_tuple(), &reply_tuple);
1457 assert_eq!(connection.external_data(), &1234);
1458 }
1459
1460 #[ip_test(I)]
1461 #[test_case(ConnectionKind::Exclusive)]
1462 #[test_case(ConnectionKind::Shared)]
1463 fn connection_direction<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1464 let bindings_ctx = FakeBindingsCtx::<I>::new();
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 let original_tuple = packet.tuple();
1473 let reply_tuple = original_tuple.clone().invert();
1474
1475 let mut other_tuple = original_tuple.clone();
1476 other_tuple.src_port_or_id += 1;
1477
1478 let connection: ConnectionExclusive<_, (), _> =
1479 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1480 let connection = match connection_kind {
1481 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1482 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1483 };
1484
1485 assert_matches!(connection.direction(&original_tuple), Some(ConnectionDirection::Original));
1486 assert_matches!(connection.direction(&reply_tuple), Some(ConnectionDirection::Reply));
1487 assert_matches!(connection.direction(&other_tuple), None);
1488 }
1489
1490 #[ip_test(I)]
1491 #[test_case(ConnectionKind::Exclusive)]
1492 #[test_case(ConnectionKind::Shared)]
1493 fn connection_update<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1494 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1495 bindings_ctx.sleep(Duration::from_secs(1));
1496
1497 let packet = PacketMetadata::<I>::new(
1498 I::SRC_IP,
1499 I::DST_IP,
1500 TransportProtocol::Udp,
1501 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1502 );
1503
1504 let reply_packet = PacketMetadata::<I>::new(
1505 I::DST_IP,
1506 I::SRC_IP,
1507 TransportProtocol::Udp,
1508 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1509 );
1510
1511 let connection =
1512 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1513 .unwrap();
1514 let mut connection = match connection_kind {
1515 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1516 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1517 };
1518
1519 assert_matches!(
1520 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1521 Ok(ConnectionUpdateAction::NoAction)
1522 );
1523 let state = connection.state();
1524 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1525 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1526
1527 bindings_ctx.sleep(Duration::from_secs(1));
1530 assert_matches!(
1531 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1532 Ok(ConnectionUpdateAction::NoAction)
1533 );
1534 let state = connection.state();
1535 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1536 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1537 }
1538
1539 #[ip_test(I)]
1540 #[test_case(ConnectionKind::Exclusive)]
1541 #[test_case(ConnectionKind::Shared)]
1542 fn skip_connection_update_for_icmp_error<I: IpExt + TestIpExt>(
1543 connection_kind: ConnectionKind,
1544 ) {
1545 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1546 bindings_ctx.sleep(Duration::from_secs(1));
1547
1548 let packet = PacketMetadata::<I>::new(
1549 I::SRC_IP,
1550 I::DST_IP,
1551 TransportProtocol::Udp,
1552 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1553 );
1554
1555 let reply_packet = PacketMetadata::<I>::new_from_icmp_error(
1556 I::DST_IP,
1557 I::SRC_IP,
1558 I::DST_PORT,
1559 I::SRC_PORT,
1560 TransportProtocol::Udp,
1561 );
1562
1563 let connection =
1564 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1565 .unwrap();
1566 let mut connection = match connection_kind {
1567 ConnectionKind::Exclusive => Connection::Exclusive(connection),
1568 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1569 };
1570
1571 assert_matches!(
1572 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1573 Ok(ConnectionUpdateAction::NoAction)
1574 );
1575 let state = connection.state();
1576 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1577 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1578
1579 bindings_ctx.sleep(Duration::from_secs(1));
1582 assert_matches!(
1583 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1584 Ok(ConnectionUpdateAction::NoAction)
1585 );
1586 let state = connection.state();
1587 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1588 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1589 }
1590
1591 #[ip_test(I)]
1592 fn skip_connection_creation_for_icmp_error<I: IpExt + TestIpExt>() {
1593 let mut bindings_ctx = FakeBindingsCtx::new();
1594 bindings_ctx.sleep(Duration::from_secs(1));
1595 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1596
1597 let packet = PacketMetadata::<I>::new_from_icmp_error(
1598 I::DST_IP,
1599 I::SRC_IP,
1600 I::DST_PORT,
1601 I::SRC_PORT,
1602 TransportProtocol::Udp,
1603 );
1604
1605 assert!(
1608 table
1609 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1610 .expect("packet should be valid")
1611 .is_none()
1612 );
1613 assert!(!table.contains_tuple(&packet.tuple()));
1614 }
1615
1616 #[ip_test(I)]
1617 fn table_get_exclusive_connection_and_finalize_shared<I: IpExt + TestIpExt>() {
1618 let mut bindings_ctx = FakeBindingsCtx::new();
1619 bindings_ctx.sleep(Duration::from_secs(1));
1620 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1621
1622 let packet = PacketMetadata::<I>::new(
1623 I::SRC_IP,
1624 I::DST_IP,
1625 TransportProtocol::Udp,
1626 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1627 );
1628
1629 let reply_packet = PacketMetadata::<I>::new(
1630 I::DST_IP,
1631 I::SRC_IP,
1632 TransportProtocol::Udp,
1633 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1634 );
1635
1636 let original_tuple = packet.tuple();
1637 let reply_tuple = reply_packet.tuple();
1638
1639 let (conn, dir) = table
1640 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1641 .expect("packet should be valid")
1642 .expect("connection should be present");
1643 let state = conn.state();
1644 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1645 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1646
1647 assert_matches!(conn, Connection::Exclusive(_));
1651 assert_eq!(dir, ConnectionDirection::Original);
1652 assert!(!table.contains_tuple(&original_tuple));
1653 assert!(!table.contains_tuple(&reply_tuple));
1654
1655 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
1657 assert!(table.contains_tuple(&original_tuple));
1658 assert!(table.contains_tuple(&reply_tuple));
1659
1660 bindings_ctx.sleep(Duration::from_secs(1));
1663 let (conn, dir) = table
1664 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1665 .expect("packet should be valid")
1666 .expect("connection should be present");
1667 assert_eq!(dir, ConnectionDirection::Original);
1668 let state = conn.state();
1669 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1670 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1671 let conn = assert_matches!(conn, Connection::Shared(conn) => conn);
1672
1673 bindings_ctx.sleep(Duration::from_secs(1));
1674 let (reply_conn, dir) = table
1675 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
1676 .expect("packet should be valid")
1677 .expect("connection should be present");
1678 assert_eq!(dir, ConnectionDirection::Reply);
1679 let state = reply_conn.state();
1680 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1681 assert_eq!(state.last_packet_time.offset, Duration::from_secs(3));
1682 let reply_conn = assert_matches!(reply_conn, Connection::Shared(conn) => conn);
1683
1684 assert!(Arc::ptr_eq(&conn, &reply_conn));
1686
1687 let (conn, _dir) = table
1689 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1690 .expect("packet should be valid")
1691 .unwrap();
1692 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
1693 assert!(table.contains_tuple(&original_tuple));
1694 assert!(table.contains_tuple(&reply_tuple));
1695 }
1696
1697 #[ip_test(I)]
1698 fn table_conflict<I: IpExt + TestIpExt>() {
1699 let mut bindings_ctx = FakeBindingsCtx::new();
1700 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1701
1702 let original_packet = PacketMetadata::<I>::new(
1703 I::SRC_IP,
1704 I::DST_IP,
1705 TransportProtocol::Udp,
1706 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1707 );
1708
1709 let nated_original_packet = PacketMetadata::<I>::new(
1710 I::SRC_IP,
1711 I::DST_IP,
1712 TransportProtocol::Udp,
1713 TransportPacketData::Generic { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT + 1 },
1714 );
1715
1716 let conn1 = Connection::Exclusive(
1717 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1718 &bindings_ctx,
1719 &original_packet,
1720 )
1721 .unwrap(),
1722 );
1723
1724 let mut conn2 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1727 &bindings_ctx,
1728 &original_packet,
1729 )
1730 .unwrap();
1731 conn2.inner.original_tuple = nated_original_packet.tuple();
1732 let conn2 = Connection::Exclusive(conn2);
1733
1734 let mut conn3 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1737 &bindings_ctx,
1738 &original_packet,
1739 )
1740 .unwrap();
1741 conn3.inner.reply_tuple = nated_original_packet.tuple().invert();
1742 let conn3 = Connection::Exclusive(conn3);
1743
1744 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn1), Ok((true, Some(_))));
1745 assert_matches!(
1746 table.finalize_connection(&mut bindings_ctx, conn2),
1747 Err(FinalizeConnectionError::Conflict)
1748 );
1749 assert_matches!(
1750 table.finalize_connection(&mut bindings_ctx, conn3),
1751 Err(FinalizeConnectionError::Conflict)
1752 );
1753 }
1754
1755 #[ip_test(I)]
1756 fn table_conflict_identical_connection<
1757 I: IpExt + crate::packets::testutil::internal::TestIpExt,
1758 >() {
1759 let mut bindings_ctx = FakeBindingsCtx::new();
1760 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1761
1762 let original_packet = PacketMetadata::<I>::new(
1763 I::SRC_IP,
1764 I::DST_IP,
1765 TransportProtocol::Udp,
1766 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1767 );
1768
1769 let conn = Connection::Exclusive(
1773 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1774 &bindings_ctx,
1775 &original_packet,
1776 )
1777 .unwrap(),
1778 );
1779 let finalized = assert_matches!(
1780 table.finalize_connection(&mut bindings_ctx, conn),
1781 Ok((true, Some(conn))) => conn
1782 );
1783
1784 let conn = Connection::Exclusive(
1785 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1786 &bindings_ctx,
1787 &original_packet,
1788 )
1789 .unwrap(),
1790 );
1791 let conn = assert_matches!(
1792 table.finalize_connection(&mut bindings_ctx, conn),
1793 Ok((false, Some(conn))) => conn
1794 );
1795 assert!(Arc::ptr_eq(&finalized, &conn));
1796 }
1797
1798 #[derive(Copy, Clone)]
1799 enum GcTrigger {
1800 Direct,
1802 Timer,
1804 }
1805
1806 #[ip_test(I)]
1807 #[test_case(GcTrigger::Direct)]
1808 #[test_case(GcTrigger::Timer)]
1809 fn garbage_collection<I: TestIpExt>(gc_trigger: GcTrigger) {
1810 fn perform_gc<I: TestIpExt>(
1811 core_ctx: &mut FakeCtx<I>,
1812 bindings_ctx: &mut FakeBindingsCtx<I>,
1813 gc_trigger: GcTrigger,
1814 ) {
1815 match gc_trigger {
1816 GcTrigger::Direct => core_ctx.conntrack().perform_gc(bindings_ctx),
1817 GcTrigger::Timer => {
1818 for timer in bindings_ctx
1819 .trigger_timers_until_instant(bindings_ctx.timer_ctx.instant.time, core_ctx)
1820 {
1821 assert_matches!(timer, FilterTimerId::ConntrackGc(_));
1822 }
1823 }
1824 }
1825 }
1826
1827 let mut bindings_ctx = FakeBindingsCtx::new();
1828 let mut core_ctx = FakeCtx::with_ip_routines(&mut bindings_ctx, IpRoutines::default());
1829
1830 let first_packet = PacketMetadata::<I>::new(
1831 I::SRC_IP,
1832 I::DST_IP,
1833 TransportProtocol::Udp,
1834 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1835 );
1836
1837 let second_packet = PacketMetadata::<I>::new(
1838 I::SRC_IP,
1839 I::DST_IP,
1840 TransportProtocol::Udp,
1841 TransportPacketData::Generic { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT },
1842 );
1843 let second_packet_reply = PacketMetadata::<I>::new(
1844 I::DST_IP,
1845 I::SRC_IP,
1846 TransportProtocol::Udp,
1847 TransportPacketData::Generic { src_port: I::DST_PORT, dst_port: I::SRC_PORT + 1 },
1848 );
1849
1850 let first_tuple = first_packet.tuple();
1851 let first_tuple_reply = first_tuple.clone().invert();
1852 let second_tuple = second_packet.tuple();
1853 let second_tuple_reply = second_packet_reply.tuple();
1854
1855 let (conn, _dir) = core_ctx
1857 .conntrack()
1858 .get_connection_for_packet_and_update(&bindings_ctx, first_packet)
1859 .expect("packet should be valid")
1860 .expect("packet should be trackable");
1861 assert_matches!(
1862 core_ctx
1863 .conntrack()
1864 .finalize_connection(&mut bindings_ctx, conn)
1865 .expect("connection finalize should succeed"),
1866 (true, Some(_))
1867 );
1868 let (conn, _dir) = core_ctx
1869 .conntrack()
1870 .get_connection_for_packet_and_update(&bindings_ctx, second_packet)
1871 .expect("packet should be valid")
1872 .expect("packet should be trackable");
1873 assert_matches!(
1874 core_ctx
1875 .conntrack()
1876 .finalize_connection(&mut bindings_ctx, conn)
1877 .expect("connection finalize should succeed"),
1878 (true, Some(_))
1879 );
1880 assert!(core_ctx.conntrack().contains_tuple(&first_tuple));
1881 assert!(core_ctx.conntrack().contains_tuple(&second_tuple));
1882 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1883
1884 bindings_ctx.sleep(GC_INTERVAL);
1887 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1888 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1889 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1890 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1891 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1892 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1893
1894 let (conn, _dir) = core_ctx
1896 .conntrack()
1897 .get_connection_for_packet_and_update(&bindings_ctx, second_packet_reply)
1898 .expect("packet should be valid")
1899 .expect("packet should be trackable");
1900 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1901 assert_matches!(
1902 core_ctx
1903 .conntrack()
1904 .finalize_connection(&mut bindings_ctx, conn)
1905 .expect("connection finalize should succeed"),
1906 (false, Some(_))
1907 );
1908 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1909 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1910 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1911 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1912 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1913
1914 bindings_ctx.sleep(GC_INTERVAL);
1924 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1925 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1926 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1927 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1928 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1929 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1930
1931 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - 2 * GC_INTERVAL);
1935 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1936 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1937 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1938 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1939 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1940 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 2);
1941
1942 bindings_ctx.sleep(GC_INTERVAL);
1946 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1947 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1948 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1949 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), false);
1950 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), false);
1951 assert!(core_ctx.conntrack().inner.lock().table.is_empty());
1952 }
1953
1954 fn fill_table<I, E, BC>(
1955 bindings_ctx: &mut BC,
1956 table: &Table<I, E, BC>,
1957 entries: impl Iterator<Item = u32>,
1958 establishment_lifecycle: EstablishmentLifecycle,
1959 ) where
1960 I: IpExt + TestIpExt,
1961 E: Debug + Default + Send + Sync + PartialEq + CompatibleWith + 'static,
1962 BC: FilterBindingsTypes + TimerContext,
1963 {
1964 for i in entries {
1965 let (packet, reply_packet) = make_test_udp_packets(i);
1966 let packet = packet.conntrack_packet().unwrap();
1967 let reply_packet = reply_packet.conntrack_packet().unwrap();
1968
1969 let (conn, _dir) = table
1970 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1971 .expect("packet should be valid")
1972 .expect("packet should be trackable");
1973 assert_matches!(
1974 table
1975 .finalize_connection(bindings_ctx, conn)
1976 .expect("connection finalize should succeed"),
1977 (true, Some(_))
1978 );
1979
1980 if establishment_lifecycle >= EstablishmentLifecycle::SeenReply {
1981 let (conn, _dir) = table
1982 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet.clone())
1983 .expect("packet should be valid")
1984 .expect("packet should be trackable");
1985 assert_matches!(
1986 table
1987 .finalize_connection(bindings_ctx, conn)
1988 .expect("connection finalize should succeed"),
1989 (false, Some(_))
1990 );
1991
1992 if establishment_lifecycle >= EstablishmentLifecycle::Established {
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(bindings_ctx, conn)
2000 .expect("connection finalize should succeed"),
2001 (false, Some(_))
2002 );
2003 }
2004 }
2005 }
2006 }
2007
2008 #[ip_test(I)]
2009 #[test_case(EstablishmentLifecycle::SeenOriginal; "existing connections unestablished")]
2010 #[test_case(EstablishmentLifecycle::SeenReply; "existing connections partially established")]
2011 #[test_case(EstablishmentLifecycle::Established; "existing connections established")]
2012 fn table_size_limit_evict_less_established<I: IpExt + TestIpExt>(
2013 existing_lifecycle: EstablishmentLifecycle,
2014 ) {
2015 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2016 bindings_ctx.sleep(Duration::from_secs(1));
2017 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2018
2019 fill_table(
2020 &mut bindings_ctx,
2021 &table,
2022 0..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2023 existing_lifecycle,
2024 );
2025
2026 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2030
2031 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2032 let packet = packet.conntrack_packet().unwrap();
2033 let (conn, _dir) = table
2034 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2035 .expect("packet should be valid")
2036 .expect("packet should be trackable");
2037 if existing_lifecycle == EstablishmentLifecycle::Established {
2038 assert_matches!(
2041 table.finalize_connection(&mut bindings_ctx, conn),
2042 Err(FinalizeConnectionError::TableFull)
2043 );
2044
2045 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 - 1).try_into().unwrap());
2048 let packet = packet.conntrack_packet().unwrap();
2049 let (conn, _dir) = table
2050 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2051 .expect("packet should be valid")
2052 .expect("packet should be trackable");
2053 assert_matches!(
2054 table
2055 .finalize_connection(&mut bindings_ctx, conn)
2056 .expect("connection finalize should succeed"),
2057 (false, Some(_))
2058 );
2059 } else {
2060 assert_matches!(
2061 table
2062 .finalize_connection(&mut bindings_ctx, conn)
2063 .expect("connection finalize should succeed"),
2064 (true, Some(_))
2065 );
2066 }
2067 }
2068
2069 #[ip_test(I)]
2070 fn table_size_limit_evict_expired<I: IpExt + TestIpExt>() {
2071 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2072 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2073
2074 let evicted_tuple = {
2076 let (packet, _) = make_test_udp_packets(0);
2077 let packet = packet.conntrack_packet().unwrap();
2078 packet.tuple()
2079 };
2080 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::Established);
2081 bindings_ctx.sleep(Duration::from_secs(1));
2082 fill_table(
2083 &mut bindings_ctx,
2084 &table,
2085 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2086 EstablishmentLifecycle::Established,
2087 );
2088
2089 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2090 assert!(table.contains_tuple(&evicted_tuple));
2091
2092 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2093 let packet = packet.conntrack_packet().unwrap();
2094 let (conn, _dir) = table
2097 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2098 .expect("packet should be valid")
2099 .expect("packet should be trackable");
2100 assert_matches!(
2101 table.finalize_connection(&mut bindings_ctx, conn),
2102 Err(FinalizeConnectionError::TableFull)
2103 );
2104
2105 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - Duration::from_secs(1));
2108 let (conn, _dir) = table
2109 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2110 .expect("packet should be valid")
2111 .expect("packet should be trackable");
2112 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2113 assert!(!table.contains_tuple(&evicted_tuple));
2114 }
2115
2116 #[ip_test(I)]
2117 fn table_size_limit_less_established<I: IpExt + TestIpExt>() {
2118 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2119 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2120
2121 let evicted_tuple = {
2122 let (packet, _) = make_test_udp_packets(0);
2123 let packet = packet.conntrack_packet().unwrap();
2124 packet.tuple()
2125 };
2126 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::SeenOriginal);
2128 bindings_ctx.sleep(Duration::from_secs(1));
2129 fill_table(&mut bindings_ctx, &table, 1..=1, EstablishmentLifecycle::SeenOriginal);
2130 fill_table(
2131 &mut bindings_ctx,
2132 &table,
2133 2..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2134 EstablishmentLifecycle::SeenReply,
2135 );
2136
2137 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2138 assert!(table.contains_tuple(&evicted_tuple));
2139
2140 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2144 let packet = packet.conntrack_packet().unwrap();
2145 let (conn, _dir) = table
2146 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2147 .expect("packet should be valid")
2148 .expect("packet should be trackable");
2149 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2150 assert!(!table.contains_tuple(&evicted_tuple));
2151 }
2152
2153 #[cfg(target_os = "fuchsia")]
2154 #[ip_test(I)]
2155 fn inspect<I: IpExt + TestIpExt>() {
2156 use alloc::string::ToString;
2157 use diagnostics_assertions::assert_data_tree;
2158 use diagnostics_traits::FuchsiaInspector;
2159 use fuchsia_inspect::Inspector;
2160
2161 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2162 bindings_ctx.sleep(Duration::from_secs(1));
2163 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2164
2165 {
2166 let inspector = Inspector::new(Default::default());
2167 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2168 bindings_inspector.delegate_inspectable(&table);
2169
2170 let mut exec = fuchsia_async::TestExecutor::new();
2171
2172 assert_data_tree!(@executor exec, inspector, "root": {
2173 "table_limit_drops": 0u64,
2174 "table_limit_hits": 0u64,
2175 "num_entries": 0u64,
2176 "connections": {},
2177 });
2178 }
2179
2180 let (packet, _) = make_test_udp_packets::<I>(0);
2183 let packet = packet.conntrack_packet().unwrap();
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!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
2189 assert_matches!(
2190 table
2191 .finalize_connection(&mut bindings_ctx, conn)
2192 .expect("connection finalize should succeed"),
2193 (true, Some(_))
2194 );
2195
2196 {
2197 let inspector = Inspector::new(Default::default());
2198 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2199 bindings_inspector.delegate_inspectable(&table);
2200
2201 let mut exec = fuchsia_async::TestExecutor::new();
2202 assert_data_tree!(@executor exec, inspector, "root": {
2203 "table_limit_drops": 0u64,
2204 "table_limit_hits": 0u64,
2205 "num_entries": 2u64,
2206 "connections": {
2207 "0": {
2208 "original_tuple": {
2209 "protocol": "UDP",
2210 "src_addr": I::SRC_IP.to_string(),
2211 "dst_addr": I::DST_IP.to_string(),
2212 "src_port_or_id": 0u64,
2213 "dst_port_or_id": 0u64,
2214 },
2215 "reply_tuple": {
2216 "protocol": "UDP",
2217 "src_addr": I::DST_IP.to_string(),
2218 "dst_addr": I::SRC_IP.to_string(),
2219 "src_port_or_id": 0u64,
2220 "dst_port_or_id": 0u64,
2221 },
2222 "external_data": {},
2223 "established": false,
2224 "last_packet_time": 1_000_000_000u64,
2225 }
2226 },
2227 });
2228 }
2229
2230 fill_table(
2232 &mut bindings_ctx,
2233 &table,
2234 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2235 EstablishmentLifecycle::Established,
2236 );
2237
2238 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2239
2240 let (packet, reply_packet) =
2243 make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2244 let packet = packet.conntrack_packet().unwrap();
2245 let reply_packet = reply_packet.conntrack_packet().unwrap();
2246 let (conn, _dir) = table
2247 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2248 .expect("packet should be valid")
2249 .expect("packet should be trackable");
2250 assert_matches!(
2251 table
2252 .finalize_connection(&mut bindings_ctx, conn)
2253 .expect("connection finalize should succeed"),
2254 (true, Some(_))
2255 );
2256 let (conn, _dir) = table
2257 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2258 .expect("packet should be valid")
2259 .expect("packet should be trackable");
2260 assert_matches!(
2261 table
2262 .finalize_connection(&mut bindings_ctx, conn)
2263 .expect("connection finalize should succeed"),
2264 (false, Some(_))
2265 );
2266 let (conn, _dir) = table
2267 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2268 .expect("packet should be valid")
2269 .expect("packet should be trackable");
2270 assert_matches!(
2271 table
2272 .finalize_connection(&mut bindings_ctx, conn)
2273 .expect("connection finalize should succeed"),
2274 (false, Some(_))
2275 );
2276
2277 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 + 1).try_into().unwrap());
2280 let packet = packet.conntrack_packet().unwrap();
2281 let (conn, _dir) = table
2282 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2283 .expect("packet should be valid")
2284 .expect("packet should be trackable");
2285 assert_matches!(
2286 table.finalize_connection(&mut bindings_ctx, conn),
2287 Err(FinalizeConnectionError::TableFull)
2288 );
2289
2290 {
2291 let inspector = Inspector::new(Default::default());
2292 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2293 bindings_inspector.delegate_inspectable(&table);
2294
2295 let mut exec = fuchsia_async::TestExecutor::new();
2296 assert_data_tree!(@executor exec, inspector, "root": contains {
2297 "table_limit_drops": 1u64,
2298 "table_limit_hits": 2u64,
2299 "num_entries": MAXIMUM_ENTRIES as u64,
2300 });
2301 }
2302 }
2303
2304 #[ip_test(I)]
2305 fn self_connected_socket<I: IpExt + TestIpExt>() {
2306 let mut bindings_ctx = FakeBindingsCtx::new();
2307 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2308
2309 let packet = PacketMetadata::<I>::new(
2310 I::SRC_IP,
2311 I::SRC_IP,
2312 TransportProtocol::Udp,
2313 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::SRC_PORT },
2314 );
2315
2316 let tuple = packet.tuple();
2317 let reply_tuple = tuple.clone().invert();
2318
2319 assert_eq!(tuple, reply_tuple);
2320
2321 let (conn, _dir) = table
2322 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2323 .expect("packet should be valid")
2324 .expect("packet should be trackable");
2325 let state = conn.state();
2326 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
2329
2330 assert_matches!(conn, Connection::Exclusive(_));
2331 assert!(!table.contains_tuple(&tuple));
2332
2333 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2335 assert!(table.contains_tuple(&tuple));
2336
2337 assert_eq!(table.inner.lock().table.len(), 1);
2340
2341 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP);
2342 table.perform_gc(&mut bindings_ctx);
2343
2344 assert!(table.inner.lock().table.is_empty());
2345 }
2346
2347 #[ip_test(I)]
2348 fn remove_entry_on_update<I: IpExt + TestIpExt>() {
2349 let mut bindings_ctx = FakeBindingsCtx::new();
2350 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2351
2352 let original_packet = PacketMetadata::<I>::new(
2353 I::SRC_IP,
2354 I::DST_IP,
2355 TransportProtocol::Tcp,
2356 TransportPacketData::Tcp {
2357 src_port: I::SRC_PORT,
2358 dst_port: I::DST_PORT,
2359 segment: SegmentHeader {
2360 seq: SeqNum::new(1024),
2361 wnd: UnscaledWindowSize::from(16u16),
2362 control: Some(Control::SYN),
2363 ..Default::default()
2364 },
2365 payload_len: 0,
2366 },
2367 );
2368
2369 let reply_packet = PacketMetadata::<I>::new(
2370 I::DST_IP,
2371 I::SRC_IP,
2372 TransportProtocol::Tcp,
2373 TransportPacketData::Tcp {
2374 src_port: I::DST_PORT,
2375 dst_port: I::SRC_PORT,
2376 segment: SegmentHeader {
2377 seq: SeqNum::new(0),
2378 ack: Some(SeqNum::new(1025)),
2379 wnd: UnscaledWindowSize::from(16u16),
2380 control: Some(Control::RST),
2381 ..Default::default()
2382 },
2383 payload_len: 0,
2384 },
2385 );
2386
2387 let tuple = original_packet.tuple();
2388 let reply_tuple = tuple.clone().invert();
2389
2390 let (conn, _dir) = table
2391 .get_connection_for_packet_and_update(&bindings_ctx, original_packet)
2392 .expect("packet should be valid")
2393 .expect("packet should be trackable");
2394 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2395
2396 assert!(table.contains_tuple(&tuple));
2397 assert!(table.contains_tuple(&reply_tuple));
2398
2399 let (conn, _dir) = table
2402 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2403 .expect("packet should be valid")
2404 .expect("packet should be trackable");
2405
2406 assert!(!table.contains_tuple(&tuple));
2407 assert!(!table.contains_tuple(&reply_tuple));
2408 assert!(table.inner.lock().table.is_empty());
2409
2410 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
2412
2413 assert!(!table.contains_tuple(&tuple));
2414 assert!(!table.contains_tuple(&reply_tuple));
2415 assert!(table.inner.lock().table.is_empty());
2416
2417 bindings_ctx.sleep(Duration::from_secs(60 * 60 * 24 * 6));
2419 table.perform_gc(&mut bindings_ctx);
2420 }
2421
2422 #[ip_test(I)]
2423 fn do_not_insert<I: IpExt + TestIpExt>() {
2424 let mut bindings_ctx = FakeBindingsCtx::new();
2425 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2426
2427 let packet = PacketMetadata::<I>::new(
2428 I::SRC_IP,
2429 I::DST_IP,
2430 TransportProtocol::Udp,
2431 TransportPacketData::Generic { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
2432 );
2433
2434 let tuple = packet.tuple();
2435 let reply_tuple = tuple.clone().invert();
2436
2437 let (conn, _dir) = table
2438 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2439 .expect("packet should be valid")
2440 .expect("packet should be trackable");
2441 let mut conn = assert_matches!(conn, Connection::Exclusive(conn) => conn);
2442 conn.do_not_insert = true;
2443 assert_matches!(
2444 table.finalize_connection(&mut bindings_ctx, Connection::Exclusive(conn)),
2445 Ok((false, None))
2446 );
2447
2448 assert!(!table.contains_tuple(&tuple));
2449 assert!(!table.contains_tuple(&reply_tuple));
2450 }
2451
2452 #[ip_test(I)]
2454 fn tcp_invalid_packet_does_not_advance_lifecycle<I: IpExt + TestIpExt>() {
2455 let mut bindings_ctx = FakeBindingsCtx::new();
2456 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2457
2458 let syn_packet = PacketMetadata::<I>::new(
2461 I::SRC_IP,
2462 I::DST_IP,
2463 TransportProtocol::Tcp,
2464 TransportPacketData::Tcp {
2465 src_port: I::SRC_PORT,
2466 dst_port: I::DST_PORT,
2467 segment: SegmentHeader {
2468 seq: SeqNum::new(1000),
2469 wnd: UnscaledWindowSize::from(1000u16),
2470 control: Some(Control::SYN),
2471 ..Default::default()
2472 },
2473 payload_len: 0,
2474 },
2475 );
2476
2477 let (conn, dir) = table
2478 .get_connection_for_packet_and_update(&bindings_ctx, syn_packet.clone())
2479 .expect("packet should be valid")
2480 .expect("packet should be trackable");
2481
2482 assert_eq!(dir, ConnectionDirection::Original);
2483 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
2484
2485 let (inserted, finalized_conn) =
2486 table.finalize_connection(&mut bindings_ctx, conn).expect("finalize should succeed");
2487 assert!(inserted);
2488 let _finalized_conn = finalized_conn.expect("should have connection");
2489
2490 let syn_ack_packet = PacketMetadata::<I>::new(
2491 I::DST_IP,
2492 I::SRC_IP,
2493 TransportProtocol::Tcp,
2494 TransportPacketData::Tcp {
2495 src_port: I::DST_PORT,
2496 dst_port: I::SRC_PORT,
2497 segment: SegmentHeader {
2498 seq: SeqNum::new(2000),
2499 ack: Some(SeqNum::new(1001)),
2500 wnd: UnscaledWindowSize::from(1000u16),
2501 control: Some(Control::SYN),
2502 ..Default::default()
2503 },
2504 payload_len: 0,
2505 },
2506 );
2507
2508 let (conn, dir) = table
2509 .get_connection_for_packet_and_update(&bindings_ctx, syn_ack_packet)
2510 .expect("packet should be valid")
2511 .expect("packet should be trackable");
2512
2513 assert_eq!(dir, ConnectionDirection::Reply);
2514 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenReply);
2515
2516 let (inserted, _) =
2517 table.finalize_connection(&mut bindings_ctx, conn).expect("finalize should succeed");
2518 assert!(!inserted);
2519
2520 assert_matches!(
2522 table
2523 .get_connection(&syn_packet.tuple())
2524 .expect("should exist")
2525 .state()
2526 .establishment_lifecycle,
2527 EstablishmentLifecycle::SeenReply
2528 );
2529
2530 let invalid_ack_packet = PacketMetadata::<I>::new(
2534 I::SRC_IP,
2535 I::DST_IP,
2536 TransportProtocol::Tcp,
2537 TransportPacketData::Tcp {
2538 src_port: I::SRC_PORT,
2539 dst_port: I::DST_PORT,
2540 segment: SegmentHeader {
2541 seq: SeqNum::new(1001),
2542 ack: Some(SeqNum::new(2002)),
2543 wnd: UnscaledWindowSize::from(1000u16),
2544 ..Default::default()
2545 },
2546 payload_len: 0,
2547 },
2548 );
2549
2550 let update_result =
2551 table.get_connection_for_packet_and_update(&bindings_ctx, invalid_ack_packet);
2552 assert_matches!(update_result, Err(GetConnectionError::InvalidPacket(_, _)));
2553
2554 assert_matches!(
2556 table
2557 .get_connection(&syn_packet.tuple())
2558 .expect("should exist")
2559 .state()
2560 .establishment_lifecycle,
2561 EstablishmentLifecycle::SeenReply
2562 );
2563 }
2564}