1mod tcp;
6
7use alloc::boxed::Box;
8use alloc::fmt::Debug;
9use alloc::sync::{Arc, Weak};
10use alloc::vec::Vec;
11use assert_matches::assert_matches;
12use core::any::Any;
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;
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(Box::new(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::MalformedPacket) => Ok(None),
436 Err(ConnectionUpdateError::InvalidPacket) => {
437 Err(GetConnectionError::InvalidPacket(connection, direction))
438 }
439 }
440 }
441
442 pub(crate) fn perform_gc(&self, bindings_ctx: &mut BC) {
443 let now = bindings_ctx.now();
444 let mut guard = self.inner.lock();
445
446 let to_remove: Vec<_> = guard
455 .table
456 .iter()
457 .filter_map(|(tuple, conn)| {
458 if *tuple == conn.inner.original_tuple && conn.is_expired(now) {
459 Some((conn.inner.original_tuple.clone(), conn.inner.reply_tuple.clone()))
460 } else {
461 None
462 }
463 })
464 .collect();
465
466 for (original_tuple, reply_tuple) in to_remove {
467 assert!(guard.table.remove(&original_tuple).is_some());
468 if reply_tuple != original_tuple {
469 assert!(guard.table.remove(&reply_tuple).is_some());
470 }
471 }
472
473 if !guard.table.is_empty() {
479 schedule_gc(bindings_ctx, &mut guard.gc_timer);
480 }
481 }
482}
483
484impl<I, E, BT> Inspectable for Table<I, E, BT>
485where
486 I: IpExt,
487 E: Inspectable,
488 BT: FilterBindingsTypes,
489{
490 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
491 let guard = self.inner.lock();
492
493 inspector.record_usize("num_entries", guard.table.len());
494 inspector.record_uint("table_limit_hits", guard.table_limit_hits);
495 inspector.record_uint("table_limit_drops", guard.table_limit_drops);
496
497 inspector.record_child("connections", |inspector| {
498 guard
499 .table
500 .iter()
501 .filter_map(|(tuple, connection)| {
502 if *tuple == connection.inner.original_tuple { Some(connection) } else { None }
503 })
504 .for_each(|connection| {
505 inspector.record_unnamed_child(|inspector| {
506 inspector.delegate_inspectable(connection.as_ref())
507 });
508 });
509 });
510 }
511}
512
513#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
515#[allow(missing_docs)]
516pub enum IcmpMessageDirection {
517 Request,
518 Reply,
519}
520
521#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
523#[allow(missing_docs)]
524pub enum TransportTuple {
525 Tcp { src_port: u16, dst_port: u16 },
526 Udp { src_port: u16, dst_port: u16 },
527 IcmpEcho { id: u16, direction: IcmpMessageDirection },
528}
529
530impl TransportTuple {
531 pub(crate) fn invert(self) -> Self {
532 match self {
533 Self::Tcp { src_port, dst_port } => {
534 Self::Tcp { src_port: dst_port, dst_port: src_port }
535 }
536 Self::Udp { src_port, dst_port } => {
537 Self::Udp { src_port: dst_port, dst_port: src_port }
538 }
539 Self::IcmpEcho { id, direction } => Self::IcmpEcho {
540 id,
541 direction: match direction {
542 IcmpMessageDirection::Request => IcmpMessageDirection::Reply,
543 IcmpMessageDirection::Reply => IcmpMessageDirection::Request,
544 },
545 },
546 }
547 }
548
549 pub fn src_port_or_id(&self) -> u16 {
551 match *self {
552 Self::Tcp { src_port, .. } | Self::Udp { src_port, .. } => src_port,
553 Self::IcmpEcho { id, .. } => id,
554 }
555 }
556
557 pub fn dst_port_or_id(&self) -> u16 {
559 match *self {
560 Self::Tcp { dst_port, .. } | Self::Udp { dst_port, .. } => dst_port,
561 Self::IcmpEcho { id, .. } => id,
562 }
563 }
564
565 pub(crate) fn set_src_port_or_id(&mut self, port_or_id: u16) {
566 match self {
567 Self::Tcp { src_port, .. } | Self::Udp { src_port, .. } => {
568 *src_port = port_or_id;
569 }
570 Self::IcmpEcho { id, .. } => {
571 *id = port_or_id;
572 }
573 }
574 }
575
576 pub(crate) fn set_dst_port_or_id(&mut self, port_or_id: u16) {
577 match self {
578 Self::Tcp { dst_port, .. } | Self::Udp { dst_port, .. } => {
579 *dst_port = port_or_id;
580 }
581 Self::IcmpEcho { id, .. } => {
582 *id = port_or_id;
583 }
584 }
585 }
586
587 pub(crate) fn protocol_name(&self) -> &'static str {
588 match self {
589 Self::Tcp { .. } => "TCP",
590 Self::Udp { .. } => "UDP",
591 Self::IcmpEcho { .. } => "ICMP",
592 }
593 }
594}
595
596impl From<&TransportPacketData> for TransportTuple {
597 fn from(transport_data: &TransportPacketData) -> Self {
598 match *transport_data {
599 TransportPacketData::Tcp { src_port, dst_port, .. } => Self::Tcp { src_port, dst_port },
600 TransportPacketData::Udp { src_port, dst_port } => Self::Udp { src_port, dst_port },
601 TransportPacketData::IcmpEcho { id, direction } => Self::IcmpEcho { id, direction },
602 }
603 }
604}
605
606#[derive(Debug, Clone, PartialEq, Eq, Hash, GenericOverIp)]
608#[generic_over_ip(I, Ip)]
609pub struct Tuple<I: IpExt> {
610 pub src_addr: I::Addr,
612 pub dst_addr: I::Addr,
614 pub transport: TransportTuple,
616}
617
618impl<I: IpExt> Tuple<I> {
619 fn new(src_addr: I::Addr, dst_addr: I::Addr, transport: TransportTuple) -> Self {
620 Self { src_addr, dst_addr, transport }
621 }
622
623 pub(crate) fn invert(self) -> Tuple<I> {
628 Self {
629 src_addr: self.dst_addr,
630 dst_addr: self.src_addr,
631 transport: self.transport.invert(),
632 }
633 }
634}
635
636impl<I: IpExt> Inspectable for Tuple<I> {
637 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
638 inspector.record_str("protocol", self.transport.protocol_name());
639 inspector.record_ip_addr("src_addr", self.src_addr);
640 inspector.record_ip_addr("dst_addr", self.dst_addr);
641 inspector.record_usize("src_port_or_id", self.transport.src_port_or_id());
642 inspector.record_usize("dst_port_or_id", self.transport.dst_port_or_id());
643 }
644}
645
646#[derive(Debug, Copy, Clone, PartialEq, Eq)]
648pub enum ConnectionDirection {
649 Original,
652
653 Reply,
656}
657
658#[derive(Debug)]
660pub(crate) enum FinalizeConnectionError {
661 Conflict,
664
665 TableFull,
667}
668
669#[derive(Debug, PartialEq, Eq)]
671enum ConnectionUpdateAction {
672 NoAction,
674
675 RemoveEntry,
677}
678
679#[derive(Debug, PartialEq, Eq)]
681enum ConnectionUpdateError {
682 InvalidPacket,
685
686 MalformedPacket,
688}
689
690#[derive(Derivative)]
692#[derivative(Debug(bound = "E: Debug"))]
693pub(crate) enum GetConnectionError<I: IpExt, E, BT: FilterBindingsTypes> {
694 InvalidPacket(Connection<I, E, BT>, ConnectionDirection),
696}
697
698#[derive(Derivative)]
701#[derivative(Debug(bound = "E: Debug"))]
702pub enum Connection<I: IpExt, E, BT: FilterBindingsTypes> {
703 Exclusive(Box<ConnectionExclusive<I, E, BT>>),
706
707 Shared(Arc<ConnectionShared<I, E, BT>>),
710}
711
712#[derive(Debug)]
715pub enum WeakConnectionError {
716 EntryRemoved,
718 InvalidEntry,
721}
722
723#[derive(Debug, Clone)]
733pub struct WeakConnection(pub(crate) Weak<dyn Any + Send + Sync>);
734
735impl WeakConnection {
736 pub fn new<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
739 conn: &Connection<I, E, BT>,
740 ) -> Option<Self> {
741 let shared = match conn {
742 Connection::Exclusive(_) => return None,
743 Connection::Shared(shared) => shared,
744 };
745 let weak = Arc::downgrade(shared);
746 Some(Self(weak))
747 }
748
749 pub fn into_inner<I: IpExt, BT: FilterBindingsTypes + 'static, E: Send + Sync + 'static>(
758 self,
759 ) -> Result<Connection<I, E, BT>, WeakConnectionError> {
760 let Self(inner) = self;
761 let shared = inner
762 .upgrade()
763 .ok_or(WeakConnectionError::EntryRemoved)?
764 .downcast()
765 .map_err(|_err: Arc<_>| WeakConnectionError::InvalidEntry)?;
766 Ok(Connection::Shared(shared))
767 }
768}
769
770impl<I: IpExt, E, BT: FilterBindingsTypes> Connection<I, E, BT> {
771 pub fn original_tuple(&self) -> &Tuple<I> {
773 match self {
774 Connection::Exclusive(c) => &c.inner.original_tuple,
775 Connection::Shared(c) => &c.inner.original_tuple,
776 }
777 }
778
779 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
781 match self {
782 Connection::Exclusive(c) => &c.inner.reply_tuple,
783 Connection::Shared(c) => &c.inner.reply_tuple,
784 }
785 }
786
787 pub fn external_data(&self) -> &E {
789 match self {
790 Connection::Exclusive(c) => &c.inner.external_data,
791 Connection::Shared(c) => &c.inner.external_data,
792 }
793 }
794
795 pub(crate) fn direction(&self, tuple: &Tuple<I>) -> Option<ConnectionDirection> {
798 let (original, reply) = match self {
799 Connection::Exclusive(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
800 Connection::Shared(c) => (&c.inner.original_tuple, &c.inner.reply_tuple),
801 };
802
803 if tuple == reply {
811 Some(ConnectionDirection::Reply)
812 } else if tuple == original {
813 Some(ConnectionDirection::Original)
814 } else {
815 None
816 }
817 }
818
819 #[cfg(test)]
821 pub(crate) fn state(&self) -> ConnectionState<BT> {
822 match self {
823 Connection::Exclusive(c) => c.state.clone(),
824 Connection::Shared(c) => c.state.lock().clone(),
825 }
826 }
827}
828
829impl<I, E, BC> Connection<I, E, BC>
830where
831 I: IpExt,
832 BC: FilterBindingsTypes + TimerContext,
833{
834 fn update(
835 &mut self,
836 bindings_ctx: &BC,
837 packet: &PacketMetadata<I>,
838 direction: ConnectionDirection,
839 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
840 match packet {
841 PacketMetadata::Full { transport_data, .. } => {
842 let now = bindings_ctx.now();
843 match self {
844 Connection::Exclusive(c) => c.state.update(direction, &transport_data, now),
845 Connection::Shared(c) => c.state.lock().update(direction, &transport_data, now),
846 }
847 }
848 PacketMetadata::IcmpError(_) => Ok(ConnectionUpdateAction::NoAction),
849 }
850 }
851}
852
853#[derive(Derivative)]
855#[derivative(Debug(bound = "E: Debug"), PartialEq(bound = "E: PartialEq"))]
856pub struct ConnectionCommon<I: IpExt, E> {
857 pub(crate) original_tuple: Tuple<I>,
860
861 pub(crate) reply_tuple: Tuple<I>,
864
865 pub(crate) external_data: E,
869}
870
871impl<I: IpExt, E: Inspectable> Inspectable for ConnectionCommon<I, E> {
872 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
873 inspector.record_child("original_tuple", |inspector| {
874 inspector.delegate_inspectable(&self.original_tuple);
875 });
876
877 inspector.record_child("reply_tuple", |inspector| {
878 inspector.delegate_inspectable(&self.reply_tuple);
879 });
880
881 inspector.record_child("external_data", |inspector| {
885 inspector.delegate_inspectable(&self.external_data);
886 });
887 }
888}
889
890#[derive(Debug, Clone)]
891enum ProtocolState {
892 Tcp(tcp::Connection),
893 Udp,
894 IcmpEcho,
895}
896
897impl ProtocolState {
898 fn update(
899 &mut self,
900 dir: ConnectionDirection,
901 transport_data: &TransportPacketData,
902 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
903 match self {
904 ProtocolState::Tcp(tcp_conn) => match transport_data.unwrap_tcp_header_and_len() {
905 None => Err(ConnectionUpdateError::MalformedPacket),
906 Some((header, len)) => tcp_conn.update(&header, len, dir),
907 },
908 ProtocolState::Udp | ProtocolState::IcmpEcho => Ok(ConnectionUpdateAction::NoAction),
909 }
910 }
911}
912
913#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
924enum EstablishmentLifecycle {
925 SeenOriginal,
926 SeenReply,
927 Established,
928}
929
930impl EstablishmentLifecycle {
931 fn update(self, dir: ConnectionDirection) -> Self {
932 match self {
933 EstablishmentLifecycle::SeenOriginal => match dir {
934 ConnectionDirection::Original => self,
935 ConnectionDirection::Reply => EstablishmentLifecycle::SeenReply,
936 },
937 EstablishmentLifecycle::SeenReply => match dir {
938 ConnectionDirection::Original => EstablishmentLifecycle::Established,
939 ConnectionDirection::Reply => self,
940 },
941 EstablishmentLifecycle::Established => self,
942 }
943 }
944}
945
946#[derive(Derivative)]
948#[derivative(Clone(bound = ""), Debug(bound = ""))]
949pub(crate) struct ConnectionState<BT: FilterBindingsTypes> {
950 last_packet_time: BT::Instant,
953
954 establishment_lifecycle: EstablishmentLifecycle,
956
957 protocol_state: ProtocolState,
959}
960
961impl<BT: FilterBindingsTypes> ConnectionState<BT> {
962 fn update(
963 &mut self,
964 dir: ConnectionDirection,
965 transport_data: &TransportPacketData,
966 now: BT::Instant,
967 ) -> Result<ConnectionUpdateAction, ConnectionUpdateError> {
968 let action = self.protocol_state.update(dir, transport_data)?;
969
970 if self.last_packet_time < now {
971 self.last_packet_time = now;
972 }
973
974 self.establishment_lifecycle = self.establishment_lifecycle.update(dir);
975
976 Ok(action)
977 }
978
979 fn is_expired(&self, now: BT::Instant) -> bool {
980 let duration = now.saturating_duration_since(self.last_packet_time);
981
982 let expiry_duration = match &self.protocol_state {
983 ProtocolState::Tcp(tcp_conn) => tcp_conn.expiry_duration(self.establishment_lifecycle),
984 ProtocolState::Udp => CONNECTION_EXPIRY_TIME_UDP,
985 ProtocolState::IcmpEcho => CONNECTION_EXPIRY_OTHER,
990 };
991
992 duration >= expiry_duration
993 }
994}
995
996impl<BT: FilterBindingsTypes> Inspectable for ConnectionState<BT> {
997 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
998 inspector.record_bool(
999 "established",
1000 match self.establishment_lifecycle {
1001 EstablishmentLifecycle::SeenOriginal | EstablishmentLifecycle::SeenReply => false,
1002 EstablishmentLifecycle::Established => true,
1003 },
1004 );
1005 inspector.record_inspectable_value("last_packet_time", &self.last_packet_time);
1006 }
1007}
1008
1009#[derive(Derivative)]
1016#[derivative(Debug(bound = "E: Debug"))]
1017pub struct ConnectionExclusive<I: IpExt, E, BT: FilterBindingsTypes> {
1018 pub(crate) inner: ConnectionCommon<I, E>,
1019 pub(crate) state: ConnectionState<BT>,
1020
1021 do_not_insert: bool,
1026}
1027
1028impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionExclusive<I, E, BT> {
1029 fn make_shared(self) -> Arc<ConnectionShared<I, E, BT>> {
1032 Arc::new(ConnectionShared { inner: self.inner, state: Mutex::new(self.state) })
1033 }
1034
1035 pub(crate) fn reply_tuple(&self) -> &Tuple<I> {
1036 &self.inner.reply_tuple
1037 }
1038
1039 pub(crate) fn rewrite_reply_dst_addr(&mut self, addr: I::Addr) {
1040 self.inner.reply_tuple.dst_addr = addr;
1041 }
1042
1043 pub(crate) fn rewrite_reply_src_addr(&mut self, addr: I::Addr) {
1044 self.inner.reply_tuple.src_addr = addr;
1045 }
1046
1047 pub(crate) fn rewrite_reply_src_port_or_id(&mut self, port_or_id: u16) {
1048 self.inner.reply_tuple.transport.set_src_port_or_id(port_or_id);
1049 }
1050
1051 pub(crate) fn rewrite_reply_dst_port_or_id(&mut self, port_or_id: u16) {
1052 self.inner.reply_tuple.transport.set_dst_port_or_id(port_or_id);
1053 }
1054}
1055
1056impl<I, E, BC> ConnectionExclusive<I, E, BC>
1057where
1058 I: IpExt,
1059 E: Default,
1060 BC: FilterBindingsTypes + TimerContext,
1061{
1062 pub(crate) fn from_deconstructed_packet(
1063 bindings_ctx: &BC,
1064 packet_metadata: &PacketMetadata<I>,
1065 ) -> Option<Self> {
1066 let (tuple, transport_data) = match packet_metadata {
1067 PacketMetadata::Full { tuple, transport_data } => (tuple, transport_data),
1068 PacketMetadata::IcmpError(_) => return None,
1069 };
1070
1071 let reply_tuple = tuple.clone().invert();
1072 let self_connected = reply_tuple == *tuple;
1073
1074 let protocol_state = match transport_data {
1075 TransportPacketData::Tcp { header_and_payload_len: None, .. } => return None,
1076 TransportPacketData::Tcp {
1077 header_and_payload_len: Some((segment, payload_len)),
1078 ..
1079 } => ProtocolState::Tcp(tcp::Connection::new(segment, *payload_len, self_connected)?),
1080 TransportPacketData::Udp { .. } => ProtocolState::Udp,
1081 TransportPacketData::IcmpEcho { direction: IcmpMessageDirection::Request, .. } => {
1082 ProtocolState::IcmpEcho
1083 }
1084 TransportPacketData::IcmpEcho { direction: IcmpMessageDirection::Reply, .. } => {
1087 return None;
1088 }
1089 };
1090
1091 Some(Self {
1092 inner: ConnectionCommon {
1093 original_tuple: tuple.clone(),
1094 reply_tuple,
1095 external_data: E::default(),
1096 },
1097 state: ConnectionState {
1098 last_packet_time: bindings_ctx.now(),
1099 establishment_lifecycle: EstablishmentLifecycle::SeenOriginal,
1100 protocol_state,
1101 },
1102 do_not_insert: false,
1103 })
1104 }
1105}
1106
1107#[derive(Derivative)]
1113#[derivative(Debug(bound = "E: Debug"))]
1114pub struct ConnectionShared<I: IpExt, E, BT: FilterBindingsTypes> {
1115 inner: ConnectionCommon<I, E>,
1116 state: Mutex<ConnectionState<BT>>,
1117}
1118
1119impl<I: IpExt, E, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1120 fn is_expired(&self, now: BT::Instant) -> bool {
1121 self.state.lock().is_expired(now)
1122 }
1123}
1124
1125impl<I: IpExt, E: CompatibleWith, BT: FilterBindingsTypes> ConnectionShared<I, E, BT> {
1126 pub(crate) fn compatible_with(&self, conn: &ConnectionExclusive<I, E, BT>) -> bool {
1130 self.inner.original_tuple == conn.inner.original_tuple
1131 && self.inner.reply_tuple == conn.inner.reply_tuple
1132 && self.inner.external_data.compatible_with(&conn.inner.external_data)
1133 }
1134}
1135
1136impl<I: IpExt, E: Inspectable, BT: FilterBindingsTypes> Inspectable for ConnectionShared<I, E, BT> {
1137 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
1138 inspector.delegate_inspectable(&self.inner);
1139 inspector.delegate_inspectable(&*self.state.lock());
1140 }
1141}
1142
1143pub trait CompatibleWith {
1146 fn compatible_with(&self, other: &Self) -> bool;
1149}
1150
1151#[derive(Debug, Clone, PartialEq, Eq)]
1154pub enum PacketMetadata<I: IpExt> {
1155 Full { tuple: Tuple<I>, transport_data: TransportPacketData },
1156 IcmpError(Tuple<I>),
1157}
1158
1159impl<I: IpExt> PacketMetadata<I> {
1160 pub(crate) fn new(
1161 src_addr: I::Addr,
1162 dst_addr: I::Addr,
1163 transport_data: TransportPacketData,
1164 ) -> Self {
1165 let transport = TransportTuple::from(&transport_data);
1166 Self::Full { tuple: Tuple::new(src_addr, dst_addr, transport), transport_data }
1167 }
1168
1169 pub(crate) fn new_from_icmp_error(
1170 src_addr: I::Addr,
1171 dst_addr: I::Addr,
1172 transport: TransportTuple,
1173 ) -> Self {
1174 Self::IcmpError(Tuple::new(src_addr, dst_addr, transport))
1175 }
1176
1177 pub(crate) fn tuple(&self) -> Tuple<I> {
1178 match self {
1179 PacketMetadata::Full { tuple, .. } => tuple.clone(),
1180 PacketMetadata::IcmpError(tuple) => tuple.clone().invert(),
1208 }
1209 }
1210}
1211
1212#[cfg(test)]
1213pub(crate) mod testutils {
1214 use crate::packets::testutil::internal::{FakeIpPacket, FakeUdpPacket, TestIpExt};
1215
1216 pub(crate) fn make_test_udp_packets<I: TestIpExt>(
1219 index: u32,
1220 ) -> (FakeIpPacket<I, FakeUdpPacket>, FakeIpPacket<I, FakeUdpPacket>) {
1221 let src_port = (index % (u16::MAX as u32)) as u16;
1224 let dst_port = (index / (u16::MAX as u32)) as u16;
1225
1226 let packet = FakeIpPacket::<I, _> {
1227 src_ip: I::SRC_IP,
1228 dst_ip: I::DST_IP,
1229 body: FakeUdpPacket { src_port, dst_port },
1230 };
1231 let reply_packet = FakeIpPacket::<I, _> {
1232 src_ip: I::DST_IP,
1233 dst_ip: I::SRC_IP,
1234 body: FakeUdpPacket { src_port: dst_port, dst_port: src_port },
1235 };
1236
1237 (packet, reply_packet)
1238 }
1239}
1240
1241#[cfg(test)]
1242mod tests {
1243 use assert_matches::assert_matches;
1244 use ip_test_macro::ip_test;
1245 use netstack3_base::testutil::FakeTimerCtxExt;
1246 use netstack3_base::{Control, IntoCoreTimerCtx, SegmentHeader, SeqNum, UnscaledWindowSize};
1247 use test_case::test_case;
1248
1249 use super::testutils::make_test_udp_packets;
1250 use super::*;
1251 use crate::context::testutil::{FakeBindingsCtx, FakeCtx};
1252 use crate::packets::IpPacket;
1253 use crate::packets::testutil::internal::ArbitraryValue;
1254 use crate::state::IpRoutines;
1255 use crate::testutil::TestIpExt;
1256
1257 impl CompatibleWith for () {
1258 fn compatible_with(&self, (): &()) -> bool {
1259 true
1260 }
1261 }
1262
1263 #[test_case(
1264 EstablishmentLifecycle::SeenOriginal,
1265 ConnectionDirection::Original
1266 => EstablishmentLifecycle::SeenOriginal
1267 )]
1268 #[test_case(
1269 EstablishmentLifecycle::SeenOriginal,
1270 ConnectionDirection::Reply
1271 => EstablishmentLifecycle::SeenReply
1272 )]
1273 #[test_case(
1274 EstablishmentLifecycle::SeenReply,
1275 ConnectionDirection::Original
1276 => EstablishmentLifecycle::Established
1277 )]
1278 #[test_case(
1279 EstablishmentLifecycle::SeenReply,
1280 ConnectionDirection::Reply
1281 => EstablishmentLifecycle::SeenReply
1282 )]
1283 #[test_case(
1284 EstablishmentLifecycle::Established,
1285 ConnectionDirection::Original
1286 => EstablishmentLifecycle::Established
1287 )]
1288 #[test_case(
1289 EstablishmentLifecycle::Established,
1290 ConnectionDirection::Reply
1291 => EstablishmentLifecycle::Established
1292 )]
1293 fn establishment_lifecycle_test(
1294 lifecycle: EstablishmentLifecycle,
1295 dir: ConnectionDirection,
1296 ) -> EstablishmentLifecycle {
1297 lifecycle.update(dir)
1298 }
1299
1300 #[ip_test(I)]
1301 #[test_case(
1302 TransportTuple::Udp { src_port: 1000, dst_port: 2000 },
1303 TransportTuple::Udp { src_port: 2000, dst_port: 1000 }
1304 )]
1305 #[test_case(
1306 TransportTuple::Tcp { src_port: 1000, dst_port: 2000 },
1307 TransportTuple::Tcp { src_port: 2000, dst_port: 1000 }
1308 )]
1309 #[test_case(
1310 TransportTuple::IcmpEcho { id: 1000, direction: IcmpMessageDirection::Request },
1311 TransportTuple::IcmpEcho { id: 1000, direction: IcmpMessageDirection::Reply }
1312 )]
1313 fn tuple_invert<I: IpExt + TestIpExt>(
1314 orig_transport: TransportTuple,
1315 expected_transport: TransportTuple,
1316 ) {
1317 let orig_tuple =
1318 Tuple::<I> { src_addr: I::SRC_IP, dst_addr: I::DST_IP, transport: orig_transport };
1319
1320 let expected =
1321 Tuple::<I> { src_addr: I::DST_IP, dst_addr: I::SRC_IP, transport: expected_transport };
1322
1323 let inverted = orig_tuple.invert();
1324
1325 assert_eq!(inverted, expected);
1326 }
1327
1328 #[ip_test(I)]
1329 fn tuple_from_tcp_packet<I: IpExt + TestIpExt>() {
1330 let expected = Tuple::<I> {
1331 src_addr: I::SRC_IP,
1332 dst_addr: I::DST_IP,
1333 transport: TransportTuple::Tcp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1334 };
1335
1336 let packet = PacketMetadata::<I>::new(
1337 I::SRC_IP,
1338 I::DST_IP,
1339 TransportPacketData::Tcp {
1340 src_port: I::SRC_PORT,
1341 dst_port: I::DST_PORT,
1342 header_and_payload_len: Some((SegmentHeader::arbitrary_value(), 4)),
1343 },
1344 );
1345
1346 assert_eq!(packet.tuple(), expected);
1347 }
1348
1349 #[ip_test(I)]
1350 fn connection_from_tuple<I: IpExt + TestIpExt>() {
1351 let bindings_ctx = FakeBindingsCtx::<I>::new();
1352
1353 let packet = PacketMetadata::<I>::new(
1354 I::SRC_IP,
1355 I::DST_IP,
1356 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1357 );
1358 let original_tuple = packet.tuple();
1359 let reply_tuple = packet.tuple().invert();
1360
1361 let connection =
1362 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1363 .unwrap();
1364
1365 assert_eq!(&connection.inner.original_tuple, &original_tuple);
1366 assert_eq!(&connection.inner.reply_tuple, &reply_tuple);
1367 }
1368
1369 #[ip_test(I)]
1370 fn connection_make_shared_has_same_underlying_info<I: IpExt + TestIpExt>() {
1371 let bindings_ctx = FakeBindingsCtx::<I>::new();
1372
1373 let packet = PacketMetadata::<I>::new(
1374 I::SRC_IP,
1375 I::DST_IP,
1376 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1377 );
1378 let original_tuple = packet.tuple();
1379 let reply_tuple = original_tuple.clone().invert();
1380
1381 let mut connection =
1382 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1383 connection.inner.external_data = 1234;
1384 let shared = connection.make_shared();
1385
1386 assert_eq!(shared.inner.original_tuple, original_tuple);
1387 assert_eq!(shared.inner.reply_tuple, reply_tuple);
1388 assert_eq!(shared.inner.external_data, 1234);
1389 }
1390
1391 enum ConnectionKind {
1392 Exclusive,
1393 Shared,
1394 }
1395
1396 #[ip_test(I)]
1397 #[test_case(ConnectionKind::Exclusive)]
1398 #[test_case(ConnectionKind::Shared)]
1399 fn connection_getters<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1400 let bindings_ctx = FakeBindingsCtx::<I>::new();
1401
1402 let packet = PacketMetadata::<I>::new(
1403 I::SRC_IP,
1404 I::DST_IP,
1405 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1406 );
1407 let original_tuple = packet.tuple();
1408 let reply_tuple = original_tuple.clone().invert();
1409
1410 let mut connection =
1411 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1412 connection.inner.external_data = 1234;
1413
1414 let connection = match connection_kind {
1415 ConnectionKind::Exclusive => Connection::Exclusive(Box::new(connection)),
1416 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1417 };
1418
1419 assert_eq!(connection.original_tuple(), &original_tuple);
1420 assert_eq!(connection.reply_tuple(), &reply_tuple);
1421 assert_eq!(connection.external_data(), &1234);
1422 }
1423
1424 #[ip_test(I)]
1425 #[test_case(ConnectionKind::Exclusive)]
1426 #[test_case(ConnectionKind::Shared)]
1427 fn connection_direction<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1428 let bindings_ctx = FakeBindingsCtx::<I>::new();
1429
1430 let packet = PacketMetadata::<I>::new(
1431 I::SRC_IP,
1432 I::DST_IP,
1433 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1434 );
1435 let original_tuple = packet.tuple();
1436 let reply_tuple = original_tuple.clone().invert();
1437
1438 let mut other_tuple = original_tuple.clone();
1439 other_tuple.transport.set_src_port_or_id(I::SRC_PORT + 1);
1440
1441 let connection: ConnectionExclusive<_, (), _> =
1442 ConnectionExclusive::from_deconstructed_packet(&bindings_ctx, &packet).unwrap();
1443 let connection = match connection_kind {
1444 ConnectionKind::Exclusive => Connection::Exclusive(Box::new(connection)),
1445 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1446 };
1447
1448 assert_matches!(connection.direction(&original_tuple), Some(ConnectionDirection::Original));
1449 assert_matches!(connection.direction(&reply_tuple), Some(ConnectionDirection::Reply));
1450 assert_matches!(connection.direction(&other_tuple), None);
1451 }
1452
1453 #[ip_test(I)]
1454 #[test_case(ConnectionKind::Exclusive)]
1455 #[test_case(ConnectionKind::Shared)]
1456 fn connection_update<I: IpExt + TestIpExt>(connection_kind: ConnectionKind) {
1457 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1458 bindings_ctx.sleep(Duration::from_secs(1));
1459
1460 let packet = PacketMetadata::<I>::new(
1461 I::SRC_IP,
1462 I::DST_IP,
1463 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1464 );
1465
1466 let reply_packet = PacketMetadata::<I>::new(
1467 I::DST_IP,
1468 I::SRC_IP,
1469 TransportPacketData::Udp { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1470 );
1471
1472 let connection =
1473 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1474 .unwrap();
1475 let mut connection = match connection_kind {
1476 ConnectionKind::Exclusive => Connection::Exclusive(Box::new(connection)),
1477 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1478 };
1479
1480 assert_matches!(
1481 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1482 Ok(ConnectionUpdateAction::NoAction)
1483 );
1484 let state = connection.state();
1485 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1486 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1487
1488 bindings_ctx.sleep(Duration::from_secs(1));
1491 assert_matches!(
1492 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1493 Ok(ConnectionUpdateAction::NoAction)
1494 );
1495 let state = connection.state();
1496 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1497 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1498 }
1499
1500 #[ip_test(I)]
1501 #[test_case(ConnectionKind::Exclusive)]
1502 #[test_case(ConnectionKind::Shared)]
1503 fn skip_connection_update_for_icmp_error<I: IpExt + TestIpExt>(
1504 connection_kind: ConnectionKind,
1505 ) {
1506 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1507 bindings_ctx.sleep(Duration::from_secs(1));
1508
1509 let packet = PacketMetadata::<I>::new(
1510 I::SRC_IP,
1511 I::DST_IP,
1512 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1513 );
1514
1515 let reply_packet = PacketMetadata::<I>::new_from_icmp_error(
1516 I::DST_IP,
1517 I::SRC_IP,
1518 TransportTuple::Udp { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1519 );
1520
1521 let connection =
1522 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(&bindings_ctx, &packet)
1523 .unwrap();
1524 let mut connection = match connection_kind {
1525 ConnectionKind::Exclusive => Connection::Exclusive(Box::new(connection)),
1526 ConnectionKind::Shared => Connection::Shared(connection.make_shared()),
1527 };
1528
1529 assert_matches!(
1530 connection.update(&bindings_ctx, &packet, ConnectionDirection::Original),
1531 Ok(ConnectionUpdateAction::NoAction)
1532 );
1533 let state = connection.state();
1534 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1535 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1536
1537 bindings_ctx.sleep(Duration::from_secs(1));
1540 assert_matches!(
1541 connection.update(&bindings_ctx, &reply_packet, ConnectionDirection::Reply),
1542 Ok(ConnectionUpdateAction::NoAction)
1543 );
1544 let state = connection.state();
1545 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1546 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1547 }
1548
1549 #[ip_test(I)]
1550 fn skip_connection_creation_for_icmp_error<I: IpExt + TestIpExt>() {
1551 let mut bindings_ctx = FakeBindingsCtx::new();
1552 bindings_ctx.sleep(Duration::from_secs(1));
1553 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1554
1555 let packet = PacketMetadata::<I>::new_from_icmp_error(
1556 I::DST_IP,
1557 I::SRC_IP,
1558 TransportTuple::Udp { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1559 );
1560
1561 assert!(
1564 table
1565 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1566 .expect("packet should be valid")
1567 .is_none()
1568 );
1569 assert!(!table.contains_tuple(&packet.tuple()));
1570 }
1571
1572 #[ip_test(I)]
1573 fn table_get_exclusive_connection_and_finalize_shared<I: IpExt + TestIpExt>() {
1574 let mut bindings_ctx = FakeBindingsCtx::new();
1575 bindings_ctx.sleep(Duration::from_secs(1));
1576 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1577
1578 let packet = PacketMetadata::<I>::new(
1579 I::SRC_IP,
1580 I::DST_IP,
1581 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1582 );
1583
1584 let reply_packet = PacketMetadata::<I>::new(
1585 I::DST_IP,
1586 I::SRC_IP,
1587 TransportPacketData::Udp { src_port: I::DST_PORT, dst_port: I::SRC_PORT },
1588 );
1589
1590 let original_tuple = packet.tuple();
1591 let reply_tuple = reply_packet.tuple();
1592
1593 let (conn, dir) = table
1594 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1595 .expect("packet should be valid")
1596 .expect("connection should be present");
1597 let state = conn.state();
1598 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1599 assert_eq!(state.last_packet_time.offset, Duration::from_secs(1));
1600
1601 assert_matches!(conn, Connection::Exclusive(_));
1605 assert_eq!(dir, ConnectionDirection::Original);
1606 assert!(!table.contains_tuple(&original_tuple));
1607 assert!(!table.contains_tuple(&reply_tuple));
1608
1609 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
1611 assert!(table.contains_tuple(&original_tuple));
1612 assert!(table.contains_tuple(&reply_tuple));
1613
1614 bindings_ctx.sleep(Duration::from_secs(1));
1617 let (conn, dir) = table
1618 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1619 .expect("packet should be valid")
1620 .expect("connection should be present");
1621 assert_eq!(dir, ConnectionDirection::Original);
1622 let state = conn.state();
1623 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
1624 assert_eq!(state.last_packet_time.offset, Duration::from_secs(2));
1625 let conn = assert_matches!(conn, Connection::Shared(conn) => conn);
1626
1627 bindings_ctx.sleep(Duration::from_secs(1));
1628 let (reply_conn, dir) = table
1629 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
1630 .expect("packet should be valid")
1631 .expect("connection should be present");
1632 assert_eq!(dir, ConnectionDirection::Reply);
1633 let state = reply_conn.state();
1634 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1635 assert_eq!(state.last_packet_time.offset, Duration::from_secs(3));
1636 let reply_conn = assert_matches!(reply_conn, Connection::Shared(conn) => conn);
1637
1638 assert!(Arc::ptr_eq(&conn, &reply_conn));
1640
1641 let (conn, _dir) = table
1643 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1644 .expect("packet should be valid")
1645 .unwrap();
1646 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
1647 assert!(table.contains_tuple(&original_tuple));
1648 assert!(table.contains_tuple(&reply_tuple));
1649 }
1650
1651 #[ip_test(I)]
1652 fn table_conflict<I: IpExt + TestIpExt>() {
1653 let mut bindings_ctx = FakeBindingsCtx::new();
1654 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1655
1656 let original_packet = PacketMetadata::<I>::new(
1657 I::SRC_IP,
1658 I::DST_IP,
1659 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1660 );
1661
1662 let nated_original_packet = PacketMetadata::<I>::new(
1663 I::SRC_IP,
1664 I::DST_IP,
1665 TransportPacketData::Udp { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT + 1 },
1666 );
1667
1668 let conn1 = Connection::Exclusive(Box::new(
1669 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1670 &bindings_ctx,
1671 &original_packet,
1672 )
1673 .unwrap(),
1674 ));
1675
1676 let mut conn2 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1679 &bindings_ctx,
1680 &original_packet,
1681 )
1682 .unwrap();
1683 conn2.inner.original_tuple = nated_original_packet.tuple();
1684 let conn2 = Connection::Exclusive(Box::new(conn2));
1685
1686 let mut conn3 = ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1689 &bindings_ctx,
1690 &original_packet,
1691 )
1692 .unwrap();
1693 conn3.inner.reply_tuple = nated_original_packet.tuple().invert();
1694 let conn3 = Connection::Exclusive(Box::new(conn3));
1695
1696 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn1), Ok((true, Some(_))));
1697 assert_matches!(
1698 table.finalize_connection(&mut bindings_ctx, conn2),
1699 Err(FinalizeConnectionError::Conflict)
1700 );
1701 assert_matches!(
1702 table.finalize_connection(&mut bindings_ctx, conn3),
1703 Err(FinalizeConnectionError::Conflict)
1704 );
1705 }
1706
1707 #[ip_test(I)]
1708 fn table_conflict_identical_connection<
1709 I: IpExt + crate::packets::testutil::internal::TestIpExt,
1710 >() {
1711 let mut bindings_ctx = FakeBindingsCtx::new();
1712 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1713
1714 let original_packet = PacketMetadata::<I>::new(
1715 I::SRC_IP,
1716 I::DST_IP,
1717 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1718 );
1719
1720 let conn = Connection::Exclusive(Box::new(
1724 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1725 &bindings_ctx,
1726 &original_packet,
1727 )
1728 .unwrap(),
1729 ));
1730 let finalized = assert_matches!(
1731 table.finalize_connection(&mut bindings_ctx, conn),
1732 Ok((true, Some(conn))) => conn
1733 );
1734
1735 let conn = Connection::Exclusive(Box::new(
1736 ConnectionExclusive::<_, (), _>::from_deconstructed_packet(
1737 &bindings_ctx,
1738 &original_packet,
1739 )
1740 .unwrap(),
1741 ));
1742 let conn = assert_matches!(
1743 table.finalize_connection(&mut bindings_ctx, conn),
1744 Ok((false, Some(conn))) => conn
1745 );
1746 assert!(Arc::ptr_eq(&finalized, &conn));
1747 }
1748
1749 #[derive(Copy, Clone)]
1750 enum GcTrigger {
1751 Direct,
1753 Timer,
1755 }
1756
1757 #[ip_test(I)]
1758 #[test_case(GcTrigger::Direct)]
1759 #[test_case(GcTrigger::Timer)]
1760 fn garbage_collection<I: TestIpExt>(gc_trigger: GcTrigger) {
1761 fn perform_gc<I: TestIpExt>(
1762 core_ctx: &mut FakeCtx<I>,
1763 bindings_ctx: &mut FakeBindingsCtx<I>,
1764 gc_trigger: GcTrigger,
1765 ) {
1766 match gc_trigger {
1767 GcTrigger::Direct => core_ctx.conntrack().perform_gc(bindings_ctx),
1768 GcTrigger::Timer => {
1769 for timer in bindings_ctx
1770 .trigger_timers_until_instant(bindings_ctx.timer_ctx.instant.time, core_ctx)
1771 {
1772 assert_matches!(timer, FilterTimerId::ConntrackGc(_));
1773 }
1774 }
1775 }
1776 }
1777
1778 let mut bindings_ctx = FakeBindingsCtx::new();
1779 let mut core_ctx = FakeCtx::with_ip_routines(&mut bindings_ctx, IpRoutines::default());
1780
1781 let first_packet = PacketMetadata::<I>::new(
1782 I::SRC_IP,
1783 I::DST_IP,
1784 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
1785 );
1786
1787 let second_packet = PacketMetadata::<I>::new(
1788 I::SRC_IP,
1789 I::DST_IP,
1790 TransportPacketData::Udp { src_port: I::SRC_PORT + 1, dst_port: I::DST_PORT },
1791 );
1792 let second_packet_reply = PacketMetadata::<I>::new(
1793 I::DST_IP,
1794 I::SRC_IP,
1795 TransportPacketData::Udp { src_port: I::DST_PORT, dst_port: I::SRC_PORT + 1 },
1796 );
1797
1798 let first_tuple = first_packet.tuple();
1799 let first_tuple_reply = first_tuple.clone().invert();
1800 let second_tuple = second_packet.tuple();
1801 let second_tuple_reply = second_packet_reply.tuple();
1802
1803 let (conn, _dir) = core_ctx
1805 .conntrack()
1806 .get_connection_for_packet_and_update(&bindings_ctx, first_packet)
1807 .expect("packet should be valid")
1808 .expect("packet should be trackable");
1809 assert_matches!(
1810 core_ctx
1811 .conntrack()
1812 .finalize_connection(&mut bindings_ctx, conn)
1813 .expect("connection finalize should succeed"),
1814 (true, Some(_))
1815 );
1816 let (conn, _dir) = core_ctx
1817 .conntrack()
1818 .get_connection_for_packet_and_update(&bindings_ctx, second_packet)
1819 .expect("packet should be valid")
1820 .expect("packet should be trackable");
1821 assert_matches!(
1822 core_ctx
1823 .conntrack()
1824 .finalize_connection(&mut bindings_ctx, conn)
1825 .expect("connection finalize should succeed"),
1826 (true, Some(_))
1827 );
1828 assert!(core_ctx.conntrack().contains_tuple(&first_tuple));
1829 assert!(core_ctx.conntrack().contains_tuple(&second_tuple));
1830 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1831
1832 bindings_ctx.sleep(GC_INTERVAL);
1835 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1836 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1837 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1838 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1839 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1840 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1841
1842 let (conn, _dir) = core_ctx
1844 .conntrack()
1845 .get_connection_for_packet_and_update(&bindings_ctx, second_packet_reply)
1846 .expect("packet should be valid")
1847 .expect("packet should be trackable");
1848 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenReply);
1849 assert_matches!(
1850 core_ctx
1851 .conntrack()
1852 .finalize_connection(&mut bindings_ctx, conn)
1853 .expect("connection finalize should succeed"),
1854 (false, Some(_))
1855 );
1856 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1857 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1858 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1859 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1860 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1861
1862 bindings_ctx.sleep(GC_INTERVAL);
1872 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1873 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), true);
1874 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), true);
1875 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1876 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1877 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 4);
1878
1879 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - 2 * GC_INTERVAL);
1883 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1884 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1885 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1886 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), true);
1887 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), true);
1888 assert_eq!(core_ctx.conntrack().inner.lock().table.len(), 2);
1889
1890 bindings_ctx.sleep(GC_INTERVAL);
1894 perform_gc(&mut core_ctx, &mut bindings_ctx, gc_trigger);
1895 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple), false);
1896 assert_eq!(core_ctx.conntrack().contains_tuple(&first_tuple_reply), false);
1897 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple), false);
1898 assert_eq!(core_ctx.conntrack().contains_tuple(&second_tuple_reply), false);
1899 assert!(core_ctx.conntrack().inner.lock().table.is_empty());
1900 }
1901
1902 fn fill_table<I, E, BC>(
1903 bindings_ctx: &mut BC,
1904 table: &Table<I, E, BC>,
1905 entries: impl Iterator<Item = u32>,
1906 establishment_lifecycle: EstablishmentLifecycle,
1907 ) where
1908 I: IpExt + TestIpExt,
1909 E: Debug + Default + Send + Sync + PartialEq + CompatibleWith + 'static,
1910 BC: FilterBindingsTypes + TimerContext,
1911 {
1912 for i in entries {
1913 let (packet, reply_packet) = make_test_udp_packets(i);
1914 let packet = packet.conntrack_packet().unwrap();
1915 let reply_packet = reply_packet.conntrack_packet().unwrap();
1916
1917 let (conn, _dir) = table
1918 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
1919 .expect("packet should be valid")
1920 .expect("packet should be trackable");
1921 assert_matches!(
1922 table
1923 .finalize_connection(bindings_ctx, conn)
1924 .expect("connection finalize should succeed"),
1925 (true, Some(_))
1926 );
1927
1928 if establishment_lifecycle >= EstablishmentLifecycle::SeenReply {
1929 let (conn, _dir) = table
1930 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
1931 .expect("packet should be valid")
1932 .expect("packet should be trackable");
1933 assert_matches!(
1934 table
1935 .finalize_connection(bindings_ctx, conn)
1936 .expect("connection finalize should succeed"),
1937 (false, Some(_))
1938 );
1939
1940 if establishment_lifecycle >= EstablishmentLifecycle::Established {
1941 let (conn, _dir) = table
1942 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1943 .expect("packet should be valid")
1944 .expect("packet should be trackable");
1945 assert_matches!(
1946 table
1947 .finalize_connection(bindings_ctx, conn)
1948 .expect("connection finalize should succeed"),
1949 (false, Some(_))
1950 );
1951 }
1952 }
1953 }
1954 }
1955
1956 #[ip_test(I)]
1957 #[test_case(EstablishmentLifecycle::SeenOriginal; "existing connections unestablished")]
1958 #[test_case(EstablishmentLifecycle::SeenReply; "existing connections partially established")]
1959 #[test_case(EstablishmentLifecycle::Established; "existing connections established")]
1960 fn table_size_limit_evict_less_established<I: IpExt + TestIpExt>(
1961 existing_lifecycle: EstablishmentLifecycle,
1962 ) {
1963 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
1964 bindings_ctx.sleep(Duration::from_secs(1));
1965 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
1966
1967 fill_table(
1968 &mut bindings_ctx,
1969 &table,
1970 0..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
1971 existing_lifecycle,
1972 );
1973
1974 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
1978
1979 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
1980 let packet = packet.conntrack_packet().unwrap();
1981 let (conn, _dir) = table
1982 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1983 .expect("packet should be valid")
1984 .expect("packet should be trackable");
1985 if existing_lifecycle == EstablishmentLifecycle::Established {
1986 assert_matches!(
1989 table.finalize_connection(&mut bindings_ctx, conn),
1990 Err(FinalizeConnectionError::TableFull)
1991 );
1992
1993 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 - 1).try_into().unwrap());
1996 let packet = packet.conntrack_packet().unwrap();
1997 let (conn, _dir) = table
1998 .get_connection_for_packet_and_update(&bindings_ctx, packet)
1999 .expect("packet should be valid")
2000 .expect("packet should be trackable");
2001 assert_matches!(
2002 table
2003 .finalize_connection(&mut bindings_ctx, conn)
2004 .expect("connection finalize should succeed"),
2005 (false, Some(_))
2006 );
2007 } else {
2008 assert_matches!(
2009 table
2010 .finalize_connection(&mut bindings_ctx, conn)
2011 .expect("connection finalize should succeed"),
2012 (true, Some(_))
2013 );
2014 }
2015 }
2016
2017 #[ip_test(I)]
2018 fn table_size_limit_evict_expired<I: IpExt + TestIpExt>() {
2019 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2020 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2021
2022 let evicted_tuple = {
2024 let (packet, _) = make_test_udp_packets(0);
2025 let packet = packet.conntrack_packet().unwrap();
2026 packet.tuple()
2027 };
2028 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::Established);
2029 bindings_ctx.sleep(Duration::from_secs(1));
2030 fill_table(
2031 &mut bindings_ctx,
2032 &table,
2033 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2034 EstablishmentLifecycle::Established,
2035 );
2036
2037 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2038 assert!(table.contains_tuple(&evicted_tuple));
2039
2040 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2041 let packet = packet.conntrack_packet().unwrap();
2042 let (conn, _dir) = table
2045 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2046 .expect("packet should be valid")
2047 .expect("packet should be trackable");
2048 assert_matches!(
2049 table.finalize_connection(&mut bindings_ctx, conn),
2050 Err(FinalizeConnectionError::TableFull)
2051 );
2052
2053 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP - Duration::from_secs(1));
2056 let (conn, _dir) = table
2057 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2058 .expect("packet should be valid")
2059 .expect("packet should be trackable");
2060 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2061 assert!(!table.contains_tuple(&evicted_tuple));
2062 }
2063
2064 #[ip_test(I)]
2065 fn table_size_limit_less_established<I: IpExt + TestIpExt>() {
2066 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2067 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2068
2069 let evicted_tuple = {
2070 let (packet, _) = make_test_udp_packets(0);
2071 let packet = packet.conntrack_packet().unwrap();
2072 packet.tuple()
2073 };
2074 fill_table(&mut bindings_ctx, &table, 0..=0, EstablishmentLifecycle::SeenOriginal);
2076 bindings_ctx.sleep(Duration::from_secs(1));
2077 fill_table(&mut bindings_ctx, &table, 1..=1, EstablishmentLifecycle::SeenOriginal);
2078 fill_table(
2079 &mut bindings_ctx,
2080 &table,
2081 2..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2082 EstablishmentLifecycle::SeenReply,
2083 );
2084
2085 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2086 assert!(table.contains_tuple(&evicted_tuple));
2087
2088 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2092 let packet = packet.conntrack_packet().unwrap();
2093 let (conn, _dir) = table
2094 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2095 .expect("packet should be valid")
2096 .expect("packet should be trackable");
2097 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok(_));
2098 assert!(!table.contains_tuple(&evicted_tuple));
2099 }
2100
2101 #[cfg(target_os = "fuchsia")]
2102 #[ip_test(I)]
2103 fn inspect<I: IpExt + TestIpExt>() {
2104 use alloc::string::ToString;
2105 use diagnostics_assertions::assert_data_tree;
2106 use diagnostics_traits::FuchsiaInspector;
2107 use fuchsia_inspect::Inspector;
2108
2109 let mut bindings_ctx = FakeBindingsCtx::<I>::new();
2110 bindings_ctx.sleep(Duration::from_secs(1));
2111 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2112
2113 {
2114 let inspector = Inspector::new(Default::default());
2115 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2116 bindings_inspector.delegate_inspectable(&table);
2117
2118 let mut exec = fuchsia_async::TestExecutor::new();
2119
2120 assert_data_tree!(@executor exec, inspector, "root": {
2121 "table_limit_drops": 0u64,
2122 "table_limit_hits": 0u64,
2123 "num_entries": 0u64,
2124 "connections": {},
2125 });
2126 }
2127
2128 let (packet, _) = make_test_udp_packets::<I>(0);
2131 let packet = packet.conntrack_packet().unwrap();
2132 let (conn, _dir) = table
2133 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2134 .expect("packet should be valid")
2135 .expect("packet should be trackable");
2136 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
2137 assert_matches!(
2138 table
2139 .finalize_connection(&mut bindings_ctx, conn)
2140 .expect("connection finalize should succeed"),
2141 (true, Some(_))
2142 );
2143
2144 {
2145 let inspector = Inspector::new(Default::default());
2146 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2147 bindings_inspector.delegate_inspectable(&table);
2148
2149 let mut exec = fuchsia_async::TestExecutor::new();
2150 assert_data_tree!(@executor exec, inspector, "root": {
2151 "table_limit_drops": 0u64,
2152 "table_limit_hits": 0u64,
2153 "num_entries": 2u64,
2154 "connections": {
2155 "0": {
2156 "original_tuple": {
2157 "protocol": "UDP",
2158 "src_addr": I::SRC_IP.to_string(),
2159 "dst_addr": I::DST_IP.to_string(),
2160 "src_port_or_id": 0u64,
2161 "dst_port_or_id": 0u64,
2162 },
2163 "reply_tuple": {
2164 "protocol": "UDP",
2165 "src_addr": I::DST_IP.to_string(),
2166 "dst_addr": I::SRC_IP.to_string(),
2167 "src_port_or_id": 0u64,
2168 "dst_port_or_id": 0u64,
2169 },
2170 "external_data": {},
2171 "established": false,
2172 "last_packet_time": 1_000_000_000u64,
2173 }
2174 },
2175 });
2176 }
2177
2178 fill_table(
2180 &mut bindings_ctx,
2181 &table,
2182 1..(MAXIMUM_ENTRIES / 2).try_into().unwrap(),
2183 EstablishmentLifecycle::Established,
2184 );
2185
2186 assert_eq!(table.inner.lock().table.len(), MAXIMUM_ENTRIES);
2187
2188 let (packet, reply_packet) =
2191 make_test_udp_packets((MAXIMUM_ENTRIES / 2).try_into().unwrap());
2192 let packet = packet.conntrack_packet().unwrap();
2193 let reply_packet = reply_packet.conntrack_packet().unwrap();
2194 let (conn, _dir) = table
2195 .get_connection_for_packet_and_update(&bindings_ctx, packet.clone())
2196 .expect("packet should be valid")
2197 .expect("packet should be trackable");
2198 assert_matches!(
2199 table
2200 .finalize_connection(&mut bindings_ctx, conn)
2201 .expect("connection finalize should succeed"),
2202 (true, Some(_))
2203 );
2204 let (conn, _dir) = table
2205 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2206 .expect("packet should be valid")
2207 .expect("packet should be trackable");
2208 assert_matches!(
2209 table
2210 .finalize_connection(&mut bindings_ctx, conn)
2211 .expect("connection finalize should succeed"),
2212 (false, Some(_))
2213 );
2214 let (conn, _dir) = table
2215 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2216 .expect("packet should be valid")
2217 .expect("packet should be trackable");
2218 assert_matches!(
2219 table
2220 .finalize_connection(&mut bindings_ctx, conn)
2221 .expect("connection finalize should succeed"),
2222 (false, Some(_))
2223 );
2224
2225 let (packet, _) = make_test_udp_packets((MAXIMUM_ENTRIES / 2 + 1).try_into().unwrap());
2228 let packet = packet.conntrack_packet().unwrap();
2229 let (conn, _dir) = table
2230 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2231 .expect("packet should be valid")
2232 .expect("packet should be trackable");
2233 assert_matches!(
2234 table.finalize_connection(&mut bindings_ctx, conn),
2235 Err(FinalizeConnectionError::TableFull)
2236 );
2237
2238 {
2239 let inspector = Inspector::new(Default::default());
2240 let mut bindings_inspector = FuchsiaInspector::<()>::new(inspector.root());
2241 bindings_inspector.delegate_inspectable(&table);
2242
2243 let mut exec = fuchsia_async::TestExecutor::new();
2244 assert_data_tree!(@executor exec, inspector, "root": contains {
2245 "table_limit_drops": 1u64,
2246 "table_limit_hits": 2u64,
2247 "num_entries": MAXIMUM_ENTRIES as u64,
2248 });
2249 }
2250 }
2251
2252 #[ip_test(I)]
2253 fn self_connected_socket<I: IpExt + TestIpExt>() {
2254 let mut bindings_ctx = FakeBindingsCtx::new();
2255 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2256
2257 let packet = PacketMetadata::<I>::new(
2258 I::SRC_IP,
2259 I::SRC_IP,
2260 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::SRC_PORT },
2261 );
2262
2263 let tuple = packet.tuple();
2264 let reply_tuple = tuple.clone().invert();
2265
2266 assert_eq!(tuple, reply_tuple);
2267
2268 let (conn, _dir) = table
2269 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2270 .expect("packet should be valid")
2271 .expect("packet should be trackable");
2272 let state = conn.state();
2273 assert_matches!(state.establishment_lifecycle, EstablishmentLifecycle::SeenReply);
2276
2277 assert_matches!(conn, Connection::Exclusive(_));
2278 assert!(!table.contains_tuple(&tuple));
2279
2280 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2282 assert!(table.contains_tuple(&tuple));
2283
2284 assert_eq!(table.inner.lock().table.len(), 1);
2287
2288 bindings_ctx.sleep(CONNECTION_EXPIRY_TIME_UDP);
2289 table.perform_gc(&mut bindings_ctx);
2290
2291 assert!(table.inner.lock().table.is_empty());
2292 }
2293
2294 #[ip_test(I)]
2295 fn remove_entry_on_update<I: IpExt + TestIpExt>() {
2296 let mut bindings_ctx = FakeBindingsCtx::new();
2297 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2298
2299 let original_packet = PacketMetadata::<I>::new(
2300 I::SRC_IP,
2301 I::DST_IP,
2302 TransportPacketData::Tcp {
2303 src_port: I::SRC_PORT,
2304 dst_port: I::DST_PORT,
2305 header_and_payload_len: Some((
2306 SegmentHeader {
2307 seq: SeqNum::new(1024),
2308 wnd: UnscaledWindowSize::from(16u16),
2309 control: Some(Control::SYN),
2310 ..Default::default()
2311 },
2312 0,
2313 )),
2314 },
2315 );
2316
2317 let reply_packet = PacketMetadata::<I>::new(
2318 I::DST_IP,
2319 I::SRC_IP,
2320 TransportPacketData::Tcp {
2321 src_port: I::DST_PORT,
2322 dst_port: I::SRC_PORT,
2323 header_and_payload_len: Some((
2324 SegmentHeader {
2325 seq: SeqNum::new(0),
2326 ack: Some(SeqNum::new(1025)),
2327 wnd: UnscaledWindowSize::from(16u16),
2328 control: Some(Control::RST),
2329 ..Default::default()
2330 },
2331 0,
2332 )),
2333 },
2334 );
2335
2336 let tuple = original_packet.tuple();
2337 let reply_tuple = tuple.clone().invert();
2338
2339 let (conn, _dir) = table
2340 .get_connection_for_packet_and_update(&bindings_ctx, original_packet)
2341 .expect("packet should be valid")
2342 .expect("packet should be trackable");
2343 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((true, Some(_))));
2344
2345 assert!(table.contains_tuple(&tuple));
2346 assert!(table.contains_tuple(&reply_tuple));
2347
2348 let (conn, _dir) = table
2351 .get_connection_for_packet_and_update(&bindings_ctx, reply_packet)
2352 .expect("packet should be valid")
2353 .expect("packet should be trackable");
2354
2355 assert!(!table.contains_tuple(&tuple));
2356 assert!(!table.contains_tuple(&reply_tuple));
2357 assert!(table.inner.lock().table.is_empty());
2358
2359 assert_matches!(table.finalize_connection(&mut bindings_ctx, conn), Ok((false, Some(_))));
2361
2362 assert!(!table.contains_tuple(&tuple));
2363 assert!(!table.contains_tuple(&reply_tuple));
2364 assert!(table.inner.lock().table.is_empty());
2365
2366 bindings_ctx.sleep(Duration::from_secs(60 * 60 * 24 * 6));
2368 table.perform_gc(&mut bindings_ctx);
2369 }
2370
2371 #[ip_test(I)]
2372 fn do_not_insert<I: IpExt + TestIpExt>() {
2373 let mut bindings_ctx = FakeBindingsCtx::new();
2374 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2375
2376 let packet = PacketMetadata::<I>::new(
2377 I::SRC_IP,
2378 I::DST_IP,
2379 TransportPacketData::Udp { src_port: I::SRC_PORT, dst_port: I::DST_PORT },
2380 );
2381
2382 let tuple = packet.tuple();
2383 let reply_tuple = tuple.clone().invert();
2384
2385 let (conn, _dir) = table
2386 .get_connection_for_packet_and_update(&bindings_ctx, packet)
2387 .expect("packet should be valid")
2388 .expect("packet should be trackable");
2389 let mut conn = assert_matches!(conn, Connection::Exclusive(conn) => conn);
2390 conn.do_not_insert = true;
2391 assert_matches!(
2392 table.finalize_connection(&mut bindings_ctx, Connection::Exclusive(conn)),
2393 Ok((false, None))
2394 );
2395
2396 assert!(!table.contains_tuple(&tuple));
2397 assert!(!table.contains_tuple(&reply_tuple));
2398 }
2399
2400 #[ip_test(I)]
2402 fn tcp_invalid_packet_does_not_advance_lifecycle<I: IpExt + TestIpExt>() {
2403 let mut bindings_ctx = FakeBindingsCtx::new();
2404 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2405
2406 let syn_packet = PacketMetadata::<I>::new(
2409 I::SRC_IP,
2410 I::DST_IP,
2411 TransportPacketData::Tcp {
2412 src_port: I::SRC_PORT,
2413 dst_port: I::DST_PORT,
2414 header_and_payload_len: Some((
2415 SegmentHeader {
2416 seq: SeqNum::new(1000),
2417 wnd: UnscaledWindowSize::from(1000u16),
2418 control: Some(Control::SYN),
2419 ..Default::default()
2420 },
2421 0,
2422 )),
2423 },
2424 );
2425
2426 let (conn, dir) = table
2427 .get_connection_for_packet_and_update(&bindings_ctx, syn_packet.clone())
2428 .expect("packet should be valid")
2429 .expect("packet should be trackable");
2430
2431 assert_eq!(dir, ConnectionDirection::Original);
2432 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenOriginal);
2433
2434 let (inserted, finalized_conn) =
2435 table.finalize_connection(&mut bindings_ctx, conn).expect("finalize should succeed");
2436 assert!(inserted);
2437 let _finalized_conn = finalized_conn.expect("should have connection");
2438
2439 let syn_ack_packet = PacketMetadata::<I>::new(
2440 I::DST_IP,
2441 I::SRC_IP,
2442 TransportPacketData::Tcp {
2443 src_port: I::DST_PORT,
2444 dst_port: I::SRC_PORT,
2445 header_and_payload_len: Some((
2446 SegmentHeader {
2447 seq: SeqNum::new(2000),
2448 ack: Some(SeqNum::new(1001)),
2449 wnd: UnscaledWindowSize::from(1000u16),
2450 control: Some(Control::SYN),
2451 ..Default::default()
2452 },
2453 0,
2454 )),
2455 },
2456 );
2457
2458 let (conn, dir) = table
2459 .get_connection_for_packet_and_update(&bindings_ctx, syn_ack_packet)
2460 .expect("packet should be valid")
2461 .expect("packet should be trackable");
2462
2463 assert_eq!(dir, ConnectionDirection::Reply);
2464 assert_matches!(conn.state().establishment_lifecycle, EstablishmentLifecycle::SeenReply);
2465
2466 let (inserted, _) =
2467 table.finalize_connection(&mut bindings_ctx, conn).expect("finalize should succeed");
2468 assert!(!inserted);
2469
2470 assert_matches!(
2472 table
2473 .get_connection(&syn_packet.tuple())
2474 .expect("should exist")
2475 .state()
2476 .establishment_lifecycle,
2477 EstablishmentLifecycle::SeenReply
2478 );
2479
2480 let invalid_ack_packet = PacketMetadata::<I>::new(
2484 I::SRC_IP,
2485 I::DST_IP,
2486 TransportPacketData::Tcp {
2487 src_port: I::SRC_PORT,
2488 dst_port: I::DST_PORT,
2489 header_and_payload_len: Some((
2490 SegmentHeader {
2491 seq: SeqNum::new(1001),
2492 ack: Some(SeqNum::new(2002)),
2493 wnd: UnscaledWindowSize::from(1000u16),
2494 ..Default::default()
2495 },
2496 0,
2497 )),
2498 },
2499 );
2500
2501 let update_result =
2502 table.get_connection_for_packet_and_update(&bindings_ctx, invalid_ack_packet);
2503 assert_matches!(update_result, Err(GetConnectionError::InvalidPacket(_, _)));
2504
2505 assert_matches!(
2507 table
2508 .get_connection(&syn_packet.tuple())
2509 .expect("should exist")
2510 .state()
2511 .establishment_lifecycle,
2512 EstablishmentLifecycle::SeenReply
2513 );
2514
2515 let malformed_packet = PacketMetadata::<I>::new(
2519 I::SRC_IP,
2520 I::DST_IP,
2521 TransportPacketData::Tcp {
2522 src_port: I::SRC_PORT,
2523 dst_port: I::DST_PORT,
2524 header_and_payload_len: None,
2525 },
2526 );
2527 assert_matches!(
2528 table.get_connection_for_packet_and_update(&bindings_ctx, malformed_packet),
2529 Ok(None)
2530 );
2531 assert_matches!(
2532 table
2533 .get_connection(&syn_packet.tuple())
2534 .expect("should exist")
2535 .state()
2536 .establishment_lifecycle,
2537 EstablishmentLifecycle::SeenReply
2538 );
2539
2540 let malformed_new_conn_packet = PacketMetadata::<I>::new(
2541 I::SRC_IP,
2542 I::DST_IP,
2543 TransportPacketData::Tcp {
2544 src_port: I::SRC_PORT.wrapping_add(1),
2545 dst_port: I::DST_PORT,
2546 header_and_payload_len: None,
2547 },
2548 );
2549 assert_matches!(
2550 table.get_connection_for_packet_and_update(&bindings_ctx, malformed_new_conn_packet),
2551 Ok(None)
2552 );
2553 }
2554
2555 #[ip_test(I)]
2556 fn icmp_echo<I: TestIpExt>() {
2557 let mut bindings_ctx = FakeBindingsCtx::new();
2558 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2559
2560 let echo_packet = |src, dst, direction| {
2561 PacketMetadata::<I>::new(
2562 src,
2563 dst,
2564 TransportPacketData::IcmpEcho { id: I::SRC_PORT, direction },
2565 )
2566 };
2567 let request_a_to_b = echo_packet(I::SRC_IP, I::DST_IP, IcmpMessageDirection::Request);
2568 let reply_b_to_a = echo_packet(I::DST_IP, I::SRC_IP, IcmpMessageDirection::Reply);
2569
2570 assert_matches!(
2572 table.get_connection_for_packet_and_update(&bindings_ctx, reply_b_to_a.clone()),
2573 Ok(None)
2574 );
2575
2576 let (conn_a, dir_a) = table
2577 .get_connection_for_packet_and_update(&bindings_ctx, request_a_to_b)
2578 .expect("packet should be valid")
2579 .expect("connection should be created for echo request");
2580 assert_eq!(dir_a, ConnectionDirection::Original);
2581 let conn_a = assert_matches!(
2582 table.finalize_connection(&mut bindings_ctx, conn_a),
2583 Ok((true, Some(conn))) => conn
2584 );
2585
2586 let (matched_a, dir) = table
2587 .get_connection_for_packet_and_update(&bindings_ctx, reply_b_to_a)
2588 .expect("packet should be valid")
2589 .expect("connection should be present for echo reply");
2590 assert_eq!(dir, ConnectionDirection::Reply);
2591 let matched_a = assert_matches!(matched_a, Connection::Shared(conn) => conn);
2592 assert!(Arc::ptr_eq(&conn_a, &matched_a));
2593 }
2594
2595 #[ip_test(I)]
2596 fn icmp_echo_self<I: TestIpExt>() {
2597 let mut bindings_ctx = FakeBindingsCtx::new();
2598 let table = Table::<_, (), _>::new::<IntoCoreTimerCtx>(&mut bindings_ctx);
2599
2600 let self_request = PacketMetadata::<I>::new(
2603 I::SRC_IP,
2604 I::SRC_IP,
2605 TransportPacketData::IcmpEcho {
2606 id: I::SRC_PORT,
2607 direction: IcmpMessageDirection::Request,
2608 },
2609 );
2610 let self_reply = PacketMetadata::<I>::new(
2611 I::SRC_IP,
2612 I::SRC_IP,
2613 TransportPacketData::IcmpEcho {
2614 id: I::SRC_PORT,
2615 direction: IcmpMessageDirection::Reply,
2616 },
2617 );
2618
2619 let (conn_self, dir) = table
2620 .get_connection_for_packet_and_update(&bindings_ctx, self_request)
2621 .expect("packet should be valid")
2622 .expect("connection should be created");
2623 assert_eq!(dir, ConnectionDirection::Original);
2624 assert_ne!(conn_self.original_tuple(), conn_self.reply_tuple());
2625 let conn_self = assert_matches!(
2626 table.finalize_connection(&mut bindings_ctx, conn_self),
2627 Ok((true, Some(conn))) => conn
2628 );
2629
2630 let (reply_conn, dir) = table
2631 .get_connection_for_packet_and_update(&bindings_ctx, self_reply)
2632 .expect("packet should be valid")
2633 .expect("connection should be present");
2634 assert_eq!(dir, ConnectionDirection::Reply);
2635 let reply_conn = assert_matches!(reply_conn, Connection::Shared(conn) => conn);
2636 assert!(Arc::ptr_eq(&conn_self, &reply_conn));
2637 }
2638}