Skip to main content

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