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}