netstack3_filter/state.rs
1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod validation;
6
7use alloc::format;
8use alloc::string::ToString as _;
9use alloc::sync::Arc;
10use alloc::vec::Vec;
11use core::fmt::Debug;
12use core::hash::{Hash, Hasher};
13use core::num::NonZeroU16;
14use core::ops::RangeInclusive;
15
16use derivative::Derivative;
17use net_types::ip::{GenericOverIp, Ip};
18use netstack3_base::{
19 CoreTimerContext, GsoInfo, Inspectable, InspectableValue, Inspector as _, MarkDomain, Marks,
20 MatcherBindingsTypes, PacketType, TimerContext,
21};
22use packet_formats::ip::IpExt;
23
24use crate::actions::MarkAction;
25use crate::conntrack::{self, ConnectionDirection};
26use crate::context::{FilterBindingsTypes, SocketInfo};
27use crate::logic::FilterTimerId;
28use crate::logic::nat::NatConfig;
29use crate::matchers::PacketMatcher;
30use crate::state::validation::ValidRoutines;
31
32/// The action to take on a packet.
33#[derive(Derivative)]
34#[derivative(Clone(bound = "RuleInfo: Clone"), Debug(bound = ""))]
35pub enum Action<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
36 /// Accept the packet.
37 ///
38 /// This is a terminal action for the current *installed* routine, i.e. no
39 /// further rules will be evaluated for this packet in the installed routine
40 /// (or any subroutines) in which this rule is installed. Subsequent
41 /// routines installed on the same hook will still be evaluated.
42 Accept,
43 /// Drop the packet.
44 ///
45 /// This is a terminal action for the current hook, i.e. no further rules
46 /// will be evaluated for this packet, even in other routines on the same
47 /// hook.
48 Drop,
49 /// Jump from the current routine to the specified uninstalled routine.
50 Jump(UninstalledRoutine<I, BT, RuleInfo>),
51 /// Stop evaluation of the current routine and return to the calling routine
52 /// (the routine from which the current routine was jumped), continuing
53 /// evaluation at the next rule.
54 ///
55 /// If invoked in an installed routine, equivalent to `Accept`, given
56 /// packets are accepted by default in the absence of any matching rules.
57 Return,
58 /// Redirect the packet to a local socket without changing the packet header
59 /// in any way.
60 ///
61 /// This is a terminal action for the current hook, i.e. no further rules
62 /// will be evaluated for this packet, even in other routines on the same
63 /// hook. However, note that this does not preclude actions on *other* hooks
64 /// from having an effect on this packet; for example, a packet that hits
65 /// TransparentProxy in INGRESS could still be dropped in LOCAL_INGRESS.
66 ///
67 /// This action is only valid in the INGRESS hook. This action is also only
68 /// valid in a rule that ensures the presence of a TCP or UDP header by
69 /// matching on the transport protocol, so that the packet can be properly
70 /// dispatched.
71 ///
72 /// Also note that transparently proxied packets will only be delivered to
73 /// sockets with the transparent socket option enabled.
74 TransparentProxy(TransparentProxy<I>),
75 /// A special case of destination NAT (DNAT) that redirects the packet to
76 /// the local host.
77 ///
78 /// This is a terminal action for all NAT routines on the current hook. The
79 /// packet is redirected by rewriting the destination IP address to one
80 /// owned by the ingress interface (if operating on incoming traffic in
81 /// INGRESS) or the loopback address (if operating on locally-generated
82 /// traffic in LOCAL_EGRESS). If this rule is installed on INGRESS and no IP
83 /// address is assigned to the incoming interface, the packet is dropped.
84 ///
85 /// As with all DNAT actions, this action is only valid in the INGRESS and
86 /// LOCAL_EGRESS hooks. If a destination port is specified, this action is
87 /// only valid in a rule that ensures the presence of a TCP or UDP header by
88 /// matching on the transport protocol, so that the destination port can be
89 /// rewritten.
90 ///
91 /// This is analogous to the `redirect` statement in Netfilter.
92 Redirect {
93 /// The optional range of destination ports used to rewrite the packet.
94 ///
95 /// If specified, the destination port of the packet will be rewritten
96 /// to some randomly chosen port in the range. If absent, the
97 /// destination port of the packet will not be rewritten.
98 dst_port: Option<RangeInclusive<NonZeroU16>>,
99 },
100 /// A special case of source NAT (SNAT) that reassigns the source IP address
101 /// of the packet to an address that is assigned to the outgoing interface.
102 ///
103 /// This is a terminal action for all NAT routines on the current hook. If
104 /// no address is assigned to the outgoing interface, the packet will be
105 /// dropped.
106 ///
107 /// This action is only valid in the EGRESS hook. If a source port range is
108 /// specified, this action is only valid in a rule that ensures the presence
109 /// of a TCP or UDP header by matching on the transport protocol, so that
110 /// the source port can be rewritten.
111 ///
112 /// This is analogous to the `masquerade` statement in Netfilter.
113 Masquerade {
114 /// The optional range of source ports used to rewrite the packet.
115 ///
116 /// The source port will be rewritten if necessary to ensure the
117 /// packet's flow does not conflict with an existing tracked connection.
118 /// Note that the source port may be rewritten whether or not this range
119 /// is specified.
120 ///
121 /// If specified, this overrides the default behavior and restricts the
122 /// range of possible values to which the source port can be rewritten.
123 src_port: Option<RangeInclusive<NonZeroU16>>,
124 },
125 /// Applies the mark action to the given mark domain.
126 ///
127 /// This is a non-terminal action for both routines and hooks. This is also
128 /// only available in [`IpRoutines`] because [`NatRoutines`] only runs on
129 /// the first packet in a connection and it is likely a misconfiguration
130 /// that packets after the first are marked differently or unmarked.
131 ///
132 /// Note: If we find use cases that justify this being in [`NatRoutines`] we
133 /// should relax this limitation and support it.
134 ///
135 /// This is analogous to the `mark` statement in Netfilter.
136 Mark {
137 /// The domain to apply the mark action.
138 domain: MarkDomain,
139 /// The action to apply.
140 action: MarkAction,
141 },
142
143 /// No action.
144 None,
145
146 /// Reject the packet.
147 Reject(RejectType),
148}
149
150/// Transparently intercept the packet and deliver it to a local socket without
151/// changing the packet header.
152///
153/// When a local address is specified, it is the bound address of the local
154/// socket to redirect the packet to. When absent, the destination IP address of
155/// the packet is used for local delivery.
156///
157/// When a local port is specified, it is the bound port of the local socket to
158/// redirect the packet to. When absent, the destination port of the packet is
159/// used for local delivery.
160#[derive(Debug, Clone)]
161#[allow(missing_docs)]
162pub enum TransparentProxy<I: IpExt> {
163 LocalAddr(I::Addr),
164 LocalPort(NonZeroU16),
165 LocalAddrAndPort(I::Addr, NonZeroU16),
166}
167
168impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> Inspectable for Action<I, BT, RuleInfo> {
169 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
170 let value = match self {
171 Self::Accept
172 | Self::Drop
173 | Self::Return
174 | Self::TransparentProxy(_)
175 | Self::Redirect { .. }
176 | Self::Masquerade { .. }
177 | Self::Mark { .. }
178 | Self::None
179 | Self::Reject(_) => {
180 format!("{self:?}")
181 }
182 Self::Jump(UninstalledRoutine { routine: _, id }) => {
183 format!("Jump(UninstalledRoutine({id:?}))")
184 }
185 };
186 inspector.record_string("action", value);
187 }
188}
189
190/// IPv4-specific reject types.
191#[derive(Debug, Clone, Copy, Eq, PartialEq)]
192pub enum RejectType {
193 /// Send a TCP RST packet.
194 TcpReset,
195
196 /// Network unreachable.
197 ///
198 /// IPv4: ICMP Destination network unreachable (type 3, code 0).
199 /// IPv6: ICMPv6 No route to destination (type 1, code 1).
200 NetUnreachable,
201
202 /// Host unreachable.
203 ///
204 /// IPv4: ICMP Host unreachable (type 3, code 1).
205 /// IPv6: ICMPv6 Address unreachable (type 1, code 3).
206 HostUnreachable,
207
208 /// Protocol unreachable.
209 ///
210 /// IPv4: ICMP Protocol unreachable (type 3, code 2).
211 /// IPv6: ICMPv6 Unrecognized Next Header type encountered (type 4, code 1).
212 ProtoUnreachable,
213
214 /// Port unreachable.
215 ///
216 /// IPv4: ICMP Port unreachable (type 3, code 3).
217 /// IPv6: ICMPv6 Port unreachable (type 1, code 4).
218 PortUnreachable,
219
220 /// Route to the destination network is prohibited.
221 ///
222 /// IPv4: ICMP Network administratively prohibited (type 3, code 9).
223 /// IPv6: ICMPv6 Source address failed ingress/egress policy (type 1, code 5).
224 RoutePolicyFail,
225
226 /// Reject route.
227 ///
228 /// IPv4: ICMP Host administratively prohibited (type 3, code 10).
229 /// IPv6: ICMPv6 Reject route to destination (type 1, code 6).
230 RejectRoute,
231
232 /// Communication administratively prohibited.
233 ///
234 /// IPv4: ICMP Communication administratively prohibited (type 3, code 13).
235 /// IPv6: ICMPv6 Communication administratively prohibited (type 1, code 1).
236 AdminProhibited,
237}
238
239/// A handle to a [`Routine`] that is not installed in a particular hook, and
240/// therefore is only run if jumped to from another routine.
241#[derive(Derivative)]
242#[derivative(Clone(bound = ""), Debug(bound = ""))]
243pub struct UninstalledRoutine<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
244 pub(crate) routine: Arc<Routine<I, BT, RuleInfo>>,
245 id: usize,
246}
247
248impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> UninstalledRoutine<I, BT, RuleInfo> {
249 /// Creates a new uninstalled routine with the provided contents.
250 pub fn new(rules: Vec<Rule<I, BT, RuleInfo>>, id: usize) -> Self {
251 Self { routine: Arc::new(Routine { rules }), id }
252 }
253
254 /// Returns the inner routine.
255 pub fn get(&self) -> &Routine<I, BT, RuleInfo> {
256 &*self.routine
257 }
258}
259
260impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> PartialEq
261 for UninstalledRoutine<I, BT, RuleInfo>
262{
263 fn eq(&self, other: &Self) -> bool {
264 let Self { routine: lhs, id: _ } = self;
265 let Self { routine: rhs, id: _ } = other;
266 Arc::ptr_eq(lhs, rhs)
267 }
268}
269
270impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> Eq for UninstalledRoutine<I, BT, RuleInfo> {}
271
272impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> Hash for UninstalledRoutine<I, BT, RuleInfo> {
273 fn hash<H: Hasher>(&self, state: &mut H) {
274 let Self { routine, id: _ } = self;
275 Arc::as_ptr(routine).hash(state)
276 }
277}
278
279impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for UninstalledRoutine<I, BT, ()> {
280 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
281 let Self { routine, id } = self;
282 inspector.record_child(&id.to_string(), |inspector| {
283 inspector.delegate_inspectable(&**routine);
284 });
285 }
286}
287
288/// A set of criteria (matchers) and a resultant action to take if a given
289/// packet matches.
290#[derive(Derivative, GenericOverIp)]
291#[generic_over_ip(I, Ip)]
292#[derivative(Clone(bound = "RuleInfo: Clone"), Debug(bound = ""))]
293pub struct Rule<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
294 /// The criteria that a packet must match for the action to be executed.
295 pub matcher: PacketMatcher<I, BT>,
296 /// The action to take on a matching packet.
297 pub action: Action<I, BT, RuleInfo>,
298 /// Opaque information about this rule for use when validating and
299 /// converting state provided by Bindings into Core filtering state. This is
300 /// only used when installing filtering state, and allows Core to report to
301 /// Bindings which rule caused a particular error. It is zero-sized for
302 /// validated state.
303 #[derivative(Debug = "ignore")]
304 pub validation_info: RuleInfo,
305}
306
307impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for Rule<I, BT, ()> {
308 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
309 let Self { matcher, action, validation_info: () } = self;
310 inspector.record_child("matchers", |inspector| {
311 let PacketMatcher {
312 in_interface,
313 out_interface,
314 src_address,
315 dst_address,
316 transport_protocol,
317 external_matcher,
318 mark_matcher,
319 } = matcher;
320
321 fn record_matcher<Inspector: netstack3_base::Inspector, M: InspectableValue>(
322 inspector: &mut Inspector,
323 name: &str,
324 matcher: &Option<M>,
325 ) {
326 if let Some(matcher) = matcher {
327 inspector.record_inspectable_value(name, matcher);
328 }
329 }
330
331 record_matcher(inspector, "in_interface", in_interface);
332 record_matcher(inspector, "out_interface", out_interface);
333 record_matcher(inspector, "src_address", src_address);
334 record_matcher(inspector, "dst_address", dst_address);
335 record_matcher(inspector, "transport_protocol", transport_protocol);
336 record_matcher(inspector, "external_matcher", external_matcher);
337 record_matcher(inspector, "mark_matcher", mark_matcher);
338 });
339 inspector.delegate_inspectable(action);
340 }
341}
342
343/// A sequence of [`Rule`]s.
344#[derive(Derivative, GenericOverIp)]
345#[generic_over_ip(I, Ip)]
346#[derivative(Clone(bound = "RuleInfo: Clone"), Debug(bound = ""))]
347pub struct Routine<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
348 /// The rules to be executed in order.
349 pub rules: Vec<Rule<I, BT, RuleInfo>>,
350}
351
352impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for Routine<I, BT, ()> {
353 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
354 let Self { rules } = self;
355 inspector.record_usize("rules", rules.len());
356 for rule in rules {
357 inspector.record_unnamed_child(|inspector| inspector.delegate_inspectable(rule));
358 }
359 }
360}
361
362/// A particular entry point for packet processing in which filtering routines
363/// are installed.
364#[derive(Derivative, GenericOverIp)]
365#[generic_over_ip(I, Ip)]
366#[derivative(Default(bound = ""), Debug(bound = ""))]
367pub struct Hook<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
368 /// The routines to be executed in order.
369 pub routines: Vec<Routine<I, BT, RuleInfo>>,
370}
371
372impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for Hook<I, BT, ()> {
373 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
374 let Self { routines } = self;
375 inspector.record_usize("routines", routines.len());
376 for routine in routines {
377 inspector.record_unnamed_child(|inspector| {
378 inspector.delegate_inspectable(routine);
379 });
380 }
381 }
382}
383
384/// Routines that perform ordinary IP filtering.
385#[derive(Derivative)]
386#[derivative(Default(bound = ""), Debug(bound = ""))]
387pub struct IpRoutines<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
388 /// Occurs for incoming traffic before a routing decision has been made.
389 pub ingress: Hook<I, BT, RuleInfo>,
390 /// Occurs for incoming traffic that is destined for the local host.
391 pub local_ingress: Hook<I, BT, RuleInfo>,
392 /// Occurs for incoming traffic that is destined for another node.
393 pub forwarding: Hook<I, BT, RuleInfo>,
394 /// Occurs for locally-generated traffic before a final routing decision has
395 /// been made.
396 pub local_egress: Hook<I, BT, RuleInfo>,
397 /// Occurs for all outgoing traffic after a routing decision has been made.
398 pub egress: Hook<I, BT, RuleInfo>,
399}
400
401impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for IpRoutines<I, BT, ()> {
402 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
403 let Self { ingress, local_ingress, forwarding, local_egress, egress } = self;
404
405 inspector.record_child("ingress", |inspector| inspector.delegate_inspectable(ingress));
406 inspector.record_child("local_ingress", |inspector| {
407 inspector.delegate_inspectable(local_ingress)
408 });
409 inspector
410 .record_child("forwarding", |inspector| inspector.delegate_inspectable(forwarding));
411 inspector
412 .record_child("local_egress", |inspector| inspector.delegate_inspectable(local_egress));
413 inspector.record_child("egress", |inspector| inspector.delegate_inspectable(egress));
414 }
415}
416
417/// Routines that can perform NAT.
418///
419/// Note that NAT routines are only executed *once* for a given connection, for
420/// the first packet in the flow.
421#[derive(Derivative)]
422#[derivative(Default(bound = ""), Debug(bound = ""))]
423pub struct NatRoutines<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
424 /// Occurs for incoming traffic before a routing decision has been made.
425 pub ingress: Hook<I, BT, RuleInfo>,
426 /// Occurs for incoming traffic that is destined for the local host.
427 pub local_ingress: Hook<I, BT, RuleInfo>,
428 /// Occurs for locally-generated traffic before a final routing decision has
429 /// been made.
430 pub local_egress: Hook<I, BT, RuleInfo>,
431 /// Occurs for all outgoing traffic after a routing decision has been made.
432 pub egress: Hook<I, BT, RuleInfo>,
433}
434
435impl<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> NatRoutines<I, BT, RuleInfo> {
436 pub(crate) fn contains_rules(&self) -> bool {
437 let Self { ingress, local_ingress, local_egress, egress } = self;
438
439 let hook_contains_rules =
440 |hook: &Hook<_, _, _>| hook.routines.iter().any(|routine| !routine.rules.is_empty());
441 hook_contains_rules(&ingress)
442 || hook_contains_rules(&local_ingress)
443 || hook_contains_rules(&local_egress)
444 || hook_contains_rules(&egress)
445 }
446}
447
448impl<I: IpExt, BT: MatcherBindingsTypes> Inspectable for NatRoutines<I, BT, ()> {
449 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
450 let Self { ingress, local_ingress, local_egress, egress } = self;
451
452 inspector.record_child("ingress", |inspector| inspector.delegate_inspectable(ingress));
453 inspector.record_child("local_ingress", |inspector| {
454 inspector.delegate_inspectable(local_ingress)
455 });
456 inspector
457 .record_child("local_egress", |inspector| inspector.delegate_inspectable(local_egress));
458 inspector.record_child("egress", |inspector| inspector.delegate_inspectable(egress));
459 }
460}
461
462/// IP version-specific filtering routine state.
463#[derive(Derivative, GenericOverIp)]
464#[generic_over_ip(I, Ip)]
465#[derivative(Default(bound = ""), Debug(bound = ""))]
466pub struct Routines<I: IpExt, BT: MatcherBindingsTypes, RuleInfo> {
467 /// Routines that perform IP filtering.
468 pub ip: IpRoutines<I, BT, RuleInfo>,
469 /// Routines that perform IP filtering and NAT.
470 pub nat: NatRoutines<I, BT, RuleInfo>,
471}
472
473/// A one-way boolean toggle that can only go from `false` to `true`.
474///
475/// Once it has been flipped to `true`, it will remain in that state forever.
476#[derive(Default)]
477pub struct OneWayBoolean(bool);
478
479impl OneWayBoolean {
480 /// A [`OneWayBoolean`] that is enabled on creation.
481 pub const TRUE: Self = Self(true);
482
483 /// Get the value of the boolean.
484 pub fn get(&self) -> bool {
485 let Self(inner) = self;
486 *inner
487 }
488
489 /// Toggle the boolean to `true`.
490 ///
491 /// This operation is idempotent: even though the [`OneWayBoolean`]'s value will
492 /// only ever change from `false` to `true` once, this method can be called any
493 /// number of times safely and the value will remain `true`.
494 pub fn set(&mut self) {
495 let Self(inner) = self;
496 *inner = true;
497 }
498}
499
500/// IP version-specific filtering state.
501pub struct State<I: IpExt, A, BT: FilterBindingsTypes> {
502 /// Routines used for filtering packets that are installed on hooks.
503 pub installed_routines: ValidRoutines<I, BT>,
504 /// Routines that are only executed if jumped to from other routines.
505 ///
506 /// Jump rules refer to their targets by holding a reference counted pointer
507 /// to the inner routine; we hold this index of all uninstalled routines
508 /// that have any references in order to report them in inspect data.
509 pub(crate) uninstalled_routines: Vec<UninstalledRoutine<I, BT, ()>>,
510 /// Connection tracking state.
511 pub conntrack: conntrack::Table<I, NatConfig<I, A>, BT>,
512 /// One-way boolean toggle indicating whether any rules have ever been added to
513 /// an installed NAT routine. If not, performing NAT can safely be skipped.
514 ///
515 /// This is useful because if any NAT is being performed, we have to check
516 /// whether it's necessary to perform implicit NAT for *all* traffic -- even if
517 /// it doesn't match any NAT rules -- to avoid conflicting tracked connections.
518 /// If we know that no NAT is being performed at all, this extra work can be
519 /// avoided.
520 ///
521 /// Note that this value will only ever go from false to true; it does not
522 /// indicate whether any NAT rules are *currently* installed. This avoids a race
523 /// condition where NAT rules are removed but connections are still being NATed
524 /// based on those rules, and therefore must be considered when creating new
525 /// connection tracking entries.
526 pub nat_installed: OneWayBoolean,
527}
528
529impl<I, A, BC> State<I, A, BC>
530where
531 I: IpExt,
532 BC: FilterBindingsTypes + TimerContext,
533{
534 /// Create a new State.
535 pub fn new<CC: CoreTimerContext<FilterTimerId<I>, BC>>(bindings_ctx: &mut BC) -> Self {
536 Self {
537 installed_routines: Default::default(),
538 uninstalled_routines: Default::default(),
539 conntrack: conntrack::Table::new::<CC>(bindings_ctx),
540 nat_installed: OneWayBoolean::default(),
541 }
542 }
543}
544
545impl<I: IpExt, A: InspectableValue, BT: FilterBindingsTypes> Inspectable for State<I, A, BT> {
546 fn record<Inspector: netstack3_base::Inspector>(&self, inspector: &mut Inspector) {
547 let Self { installed_routines, uninstalled_routines, conntrack, nat_installed: _ } = self;
548 let Routines { ip, nat } = installed_routines.get();
549
550 inspector.record_child("IP", |inspector| inspector.delegate_inspectable(ip));
551 inspector.record_child("NAT", |inspector| inspector.delegate_inspectable(nat));
552 inspector.record_child("uninstalled", |inspector| {
553 inspector.record_usize("routines", uninstalled_routines.len());
554 for routine in uninstalled_routines {
555 inspector.delegate_inspectable(routine);
556 }
557 });
558
559 inspector.record_child("conntrack", |inspector| {
560 inspector.delegate_inspectable(conntrack);
561 });
562 }
563}
564
565/// A trait for interacting with the pieces of packet metadata that are
566/// important for filtering.
567pub trait FilterIpMetadata<I: IpExt, A, BT: FilterBindingsTypes>: FilterPacketMetadata {
568 /// Removes the conntrack connection and packet direction, if they exist.
569 fn take_connection_and_direction(
570 &mut self,
571 ) -> Option<(conntrack::Connection<I, NatConfig<I, A>, BT>, ConnectionDirection)>;
572
573 /// Puts a new conntrack connection and packet direction into the metadata
574 /// struct, returning the previous connection value, if one existed.
575 fn replace_connection_and_direction(
576 &mut self,
577 conn: conntrack::Connection<I, NatConfig<I, A>, BT>,
578 direction: ConnectionDirection,
579 ) -> Option<conntrack::Connection<I, NatConfig<I, A>, BT>>;
580}
581
582/// A trait for interacting with packet metadata.
583//
584// The reason why we split this trait from the `FilterIpMetadata` is to avoid
585// introducing trait bounds and type parameters into functions that don't
586// depend on conntrack, for example, all the `check_routine*` methods. This
587// becomes a meaningful simplification for those cases.
588pub trait FilterPacketMetadata {
589 /// Applies the mark action to the metadata.
590 fn apply_mark_action(&mut self, domain: MarkDomain, action: MarkAction);
591 /// Socket info.
592 fn socket_info(&self) -> Option<SocketInfo>;
593 /// Socket marks.
594 fn marks(&self) -> &Marks;
595 /// Packet type.
596 fn packet_type(&self) -> PacketType;
597 /// GSO metadata, if the packet was coalesced from multiple segments.
598 fn gso_info(&self) -> Option<GsoInfo>;
599}
600
601/// Testing utilities for filtering state.
602#[cfg(any(test, feature = "testutils"))]
603pub mod testutil {
604 use super::*;
605
606 /// Fake implementation of `FilterIpMetadata` and `FilterPacketMetadata`
607 /// traits.
608 pub struct FakePacketMetadata {
609 marks: Marks,
610 packet_type: PacketType,
611 }
612
613 impl Default for FakePacketMetadata {
614 fn default() -> Self {
615 Self { marks: Marks::default(), packet_type: PacketType::Host }
616 }
617 }
618
619 impl FakePacketMetadata {
620 /// Creates a new `FakePacketMetadata` with the specified marks.
621 pub fn new(marks: Marks, packet_type: PacketType) -> Self {
622 Self { marks, packet_type }
623 }
624 }
625
626 impl<I: IpExt, A, BT: FilterBindingsTypes> FilterIpMetadata<I, A, BT> for FakePacketMetadata {
627 fn take_connection_and_direction(
628 &mut self,
629 ) -> Option<(conntrack::Connection<I, NatConfig<I, A>, BT>, ConnectionDirection)> {
630 None
631 }
632
633 fn replace_connection_and_direction(
634 &mut self,
635 _conn: conntrack::Connection<I, NatConfig<I, A>, BT>,
636 _direction: ConnectionDirection,
637 ) -> Option<conntrack::Connection<I, NatConfig<I, A>, BT>> {
638 None
639 }
640 }
641
642 impl FilterPacketMetadata for FakePacketMetadata {
643 fn apply_mark_action(&mut self, domain: MarkDomain, action: MarkAction) {
644 action.apply(self.marks.get_mut(domain));
645 }
646
647 fn socket_info(&self) -> Option<SocketInfo> {
648 None
649 }
650
651 fn marks(&self) -> &Marks {
652 &self.marks
653 }
654
655 fn packet_type(&self) -> PacketType {
656 self.packet_type
657 }
658
659 fn gso_info(&self) -> Option<GsoInfo> {
660 None
661 }
662 }
663}