Skip to main content

netstack3_filter/
context.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
5use core::fmt::Debug;
6
7use net_types::SpecifiedAddr;
8use net_types::ip::{IpVersion, Ipv4, Ipv6};
9
10pub use netstack3_base::socket::{EitherIpProto, SocketInfo};
11pub use netstack3_base::{FrameDestination, LocalFrameDestination, Marks, PacketType};
12use netstack3_base::{
13    GsoInfo, InstantBindingsTypes, InterfaceProperties, IpDeviceAddr, IpDeviceAddressIdContext,
14    MatcherBindingsTypes, RngContext, TimerBindingsTypes, TimerContext, TxMetadataBindingsTypes,
15};
16use packet::FragmentedByteSlice;
17use packet_formats::ip::IpExt;
18
19use crate::FilterIpExt;
20use crate::matchers::BindingsPacketMatcher;
21use crate::packets::FilterIpPacket;
22use crate::state::State;
23
24/// Trait defining required types for filtering provided by bindings.
25pub trait FilterBindingsTypes:
26    InstantBindingsTypes + MatcherBindingsTypes + TimerBindingsTypes + 'static
27{
28}
29
30impl<BT: InstantBindingsTypes + MatcherBindingsTypes + TimerBindingsTypes + 'static>
31    FilterBindingsTypes for BT
32{
33}
34
35/// Trait aggregating functionality required from bindings.
36pub trait FilterBindingsContext<D>:
37    TimerContext + RngContext + FilterBindingsTypes<BindingsPacketMatcher: BindingsPacketMatcher<D>>
38{
39}
40impl<D, BC> FilterBindingsContext<D> for BC
41where
42    BC: TimerContext + RngContext + FilterBindingsTypes,
43    BC::BindingsPacketMatcher: BindingsPacketMatcher<D>,
44{
45}
46
47/// The IP version-specific execution context for packet filtering.
48///
49/// This trait exists to abstract over access to the filtering state. It is
50/// useful to implement filtering logic in terms of this trait, as opposed to,
51/// for example, [`crate::logic::FilterHandler`] methods taking the state
52/// directly as an argument, because it allows Netstack3 Core to use lock
53/// ordering types to enforce that filtering state is only acquired at or before
54/// a given lock level, while keeping test code free of locking concerns.
55pub trait FilterIpContext<I: FilterIpExt, BT: FilterBindingsTypes>:
56    IpDeviceAddressIdContext<I, DeviceId: InterfaceProperties<BT::DeviceClass>>
57{
58    /// The execution context that allows the filtering engine to perform
59    /// Network Address Translation (NAT).
60    type NatCtx<'a>: NatContext<I, BT, DeviceId = Self::DeviceId, WeakAddressId = Self::WeakAddressId>;
61
62    /// Calls the function with a reference to filtering state.
63    fn with_filter_state<O, F: FnOnce(&State<I, Self::WeakAddressId, BT>) -> O>(
64        &mut self,
65        cb: F,
66    ) -> O {
67        self.with_filter_state_and_nat_ctx(|state, _ctx| cb(state))
68    }
69
70    /// Calls the function with a reference to filtering state and the NAT
71    /// context.
72    fn with_filter_state_and_nat_ctx<
73        O,
74        F: FnOnce(&State<I, Self::WeakAddressId, BT>, &mut Self::NatCtx<'_>) -> O,
75    >(
76        &mut self,
77        cb: F,
78    ) -> O;
79}
80
81/// The execution context for Network Address Translation (NAT).
82pub trait NatContext<I: IpExt, BT: FilterBindingsTypes>:
83    IpDeviceAddressIdContext<I, DeviceId: InterfaceProperties<BT::DeviceClass>>
84{
85    /// Returns the best local address for communicating with the remote.
86    fn get_local_addr_for_remote(
87        &mut self,
88        device_id: &Self::DeviceId,
89        remote: Option<SpecifiedAddr<I::Addr>>,
90    ) -> Option<Self::AddressId>;
91
92    /// Returns a strongly-held reference to the provided address, if it is assigned
93    /// to the specified device.
94    fn get_address_id(
95        &mut self,
96        device_id: &Self::DeviceId,
97        addr: IpDeviceAddr<I::Addr>,
98    ) -> Option<Self::AddressId>;
99}
100
101/// A context for mutably accessing all filtering state at once, to allow IPv4
102/// and IPv6 filtering state to be modified atomically.
103pub trait FilterContext<BT: FilterBindingsTypes>:
104    IpDeviceAddressIdContext<Ipv4, DeviceId: InterfaceProperties<BT::DeviceClass>>
105    + IpDeviceAddressIdContext<Ipv6, DeviceId: InterfaceProperties<BT::DeviceClass>>
106{
107    /// Calls the function with a mutable reference to all filtering state.
108    fn with_all_filter_state_mut<
109        O,
110        F: FnOnce(
111            &mut State<Ipv4, <Self as IpDeviceAddressIdContext<Ipv4>>::WeakAddressId, BT>,
112            &mut State<Ipv6, <Self as IpDeviceAddressIdContext<Ipv6>>::WeakAddressId, BT>,
113        ) -> O,
114    >(
115        &mut self,
116        cb: F,
117    ) -> O;
118}
119
120/// Result returned from [`SocketOpsFilter::on_egress`].
121#[derive(Copy, Clone, Debug, Eq, PartialEq)]
122pub enum SocketEgressFilterResult {
123    /// Send the packet normally.
124    Pass {
125        /// Indicates that congestion should be signaled to the higher level protocol.
126        congestion: bool,
127    },
128
129    /// Drop the packet.
130    Drop {
131        /// Indicates that congestion should be signaled to the higher level protocol.
132        congestion: bool,
133    },
134}
135
136/// Result returned from [`SocketOpsFilter::on_ingress`].
137#[derive(Copy, Clone, Debug, Eq, PartialEq)]
138pub enum SocketIngressFilterResult {
139    /// Accept the packet.
140    Accept,
141
142    /// Drop the packet.
143    Drop,
144}
145
146/// Trait for a socket operations filter.
147pub trait SocketOpsFilter<D> {
148    /// Called on every outgoing packet originated from a local socket.
149    fn on_egress<I: FilterIpExt, P: FilterIpPacket<I>>(
150        &self,
151        packet: &P,
152        device: &D,
153        socket_info: SocketInfo,
154        marks: &Marks,
155        gso_info: Option<GsoInfo>,
156    ) -> SocketEgressFilterResult;
157
158    /// Called on every incoming packet handled by a local socket.
159    fn on_ingress(
160        &self,
161        ip_version: IpVersion,
162        packet: FragmentedByteSlice<'_, &[u8]>,
163        header_len: usize,
164        device: &D,
165        socket_info: SocketInfo,
166        marks: &Marks,
167        packet_type: PacketType,
168        gso_info: Option<GsoInfo>,
169    ) -> SocketIngressFilterResult;
170}
171
172/// Implemented by bindings to provide socket operations filtering.
173pub trait SocketOpsFilterBindingContext<D>: TxMetadataBindingsTypes {
174    /// Returns the filter that should be called for socket ops.
175    fn socket_ops_filter(&self) -> impl SocketOpsFilter<D>;
176}
177
178#[cfg(any(test, feature = "testutils"))]
179impl<
180    TimerId: Debug + PartialEq + Clone + Send + Sync + 'static,
181    Event: Debug + 'static,
182    State: 'static,
183    FrameMeta: 'static,
184    D,
185> SocketOpsFilterBindingContext<D>
186    for netstack3_base::testutil::FakeBindingsCtx<TimerId, Event, State, FrameMeta>
187{
188    fn socket_ops_filter(&self) -> impl SocketOpsFilter<D> {
189        crate::testutil::NoOpSocketOpsFilter
190    }
191}
192
193#[cfg(test)]
194pub(crate) mod testutil {
195    use alloc::sync::{Arc, Weak};
196    use alloc::vec::Vec;
197    use core::hash::{Hash, Hasher};
198    use core::ops::Deref;
199    use core::sync::atomic::AtomicUsize;
200    use core::time::Duration;
201
202    use derivative::Derivative;
203    use net_types::ip::{AddrSubnet, GenericOverIp, Ip};
204    use netstack3_base::testutil::{
205        FakeAtomicInstant, FakeCryptoRng, FakeDeviceClass, FakeInstant, FakeMatcherDeviceId,
206        FakeTimerCtx, FakeWeakDeviceId, WithFakeTimerContext,
207    };
208    use netstack3_base::{
209        AnyDevice, AssignedAddrIpExt, DeviceIdContext, InspectableValue, Inspector, InstantContext,
210        IntoCoreTimerCtx, IpAddressId, WeakIpAddressId,
211    };
212    use netstack3_hashmap::HashMap;
213
214    use super::*;
215    use crate::logic::FilterTimerId;
216    use crate::logic::nat::NatConfig;
217    use crate::state::validation::ValidRoutines;
218    use crate::state::{FilterPacketMetadata, IpRoutines, NatRoutines, OneWayBoolean, Routines};
219    use crate::{Interfaces, conntrack};
220
221    pub trait TestIpExt: FilterIpExt + AssignedAddrIpExt {}
222
223    impl<I: FilterIpExt + AssignedAddrIpExt> TestIpExt for I {}
224
225    #[derive(Debug)]
226    pub struct FakePrimaryAddressId<I: AssignedAddrIpExt>(
227        pub Arc<AddrSubnet<I::Addr, I::AssignedWitness>>,
228    );
229
230    #[derive(Clone, Debug, Hash, Eq, PartialEq)]
231    pub struct FakeAddressId<I: AssignedAddrIpExt>(Arc<AddrSubnet<I::Addr, I::AssignedWitness>>);
232
233    #[derive(Clone, Debug)]
234    pub struct FakeWeakAddressId<I: AssignedAddrIpExt>(
235        pub Weak<AddrSubnet<I::Addr, I::AssignedWitness>>,
236    );
237
238    impl<I: AssignedAddrIpExt> PartialEq for FakeWeakAddressId<I> {
239        fn eq(&self, other: &Self) -> bool {
240            let Self(lhs) = self;
241            let Self(rhs) = other;
242            Weak::ptr_eq(lhs, rhs)
243        }
244    }
245
246    impl<I: AssignedAddrIpExt> Eq for FakeWeakAddressId<I> {}
247
248    impl<I: AssignedAddrIpExt> Hash for FakeWeakAddressId<I> {
249        fn hash<H: Hasher>(&self, state: &mut H) {
250            let Self(this) = self;
251            this.as_ptr().hash(state)
252        }
253    }
254
255    impl<I: AssignedAddrIpExt> WeakIpAddressId<I::Addr> for FakeWeakAddressId<I> {
256        type Strong = FakeAddressId<I>;
257
258        fn upgrade(&self) -> Option<Self::Strong> {
259            let Self(inner) = self;
260            inner.upgrade().map(FakeAddressId)
261        }
262
263        fn is_assigned(&self) -> bool {
264            let Self(inner) = self;
265            inner.strong_count() != 0
266        }
267    }
268
269    impl<I: AssignedAddrIpExt> InspectableValue for FakeWeakAddressId<I> {
270        fn record<Inspector: netstack3_base::Inspector>(
271            &self,
272            _name: &str,
273            _inspector: &mut Inspector,
274        ) {
275            unimplemented!()
276        }
277    }
278
279    impl<I: AssignedAddrIpExt> Deref for FakeAddressId<I> {
280        type Target = AddrSubnet<I::Addr, I::AssignedWitness>;
281
282        fn deref(&self) -> &Self::Target {
283            let Self(inner) = self;
284            inner.deref()
285        }
286    }
287
288    impl<I: AssignedAddrIpExt> IpAddressId<I::Addr> for FakeAddressId<I> {
289        type Weak = FakeWeakAddressId<I>;
290
291        fn downgrade(&self) -> Self::Weak {
292            let Self(inner) = self;
293            FakeWeakAddressId(Arc::downgrade(inner))
294        }
295
296        fn addr(&self) -> IpDeviceAddr<I::Addr> {
297            let Self(inner) = self;
298
299            #[derive(GenericOverIp)]
300            #[generic_over_ip(I, Ip)]
301            struct WrapIn<I: AssignedAddrIpExt>(I::AssignedWitness);
302            I::map_ip(
303                WrapIn(inner.addr()),
304                |WrapIn(v4_addr)| IpDeviceAddr::new_from_witness(v4_addr),
305                |WrapIn(v6_addr)| IpDeviceAddr::new_from_ipv6_device_addr(v6_addr),
306            )
307        }
308
309        fn addr_sub(&self) -> AddrSubnet<I::Addr, I::AssignedWitness> {
310            let Self(inner) = self;
311            **inner
312        }
313    }
314
315    pub struct FakeCtx<I: TestIpExt> {
316        state: State<I, FakeWeakAddressId<I>, FakeBindingsCtx<I>>,
317        nat: FakeNatCtx<I>,
318    }
319
320    #[derive(Derivative)]
321    #[derivative(Default(bound = ""))]
322    pub struct FakeNatCtx<I: TestIpExt> {
323        pub(crate) device_addrs: HashMap<FakeMatcherDeviceId, FakePrimaryAddressId<I>>,
324    }
325
326    impl<I: TestIpExt> FakeCtx<I> {
327        pub fn new(bindings_ctx: &mut FakeBindingsCtx<I>) -> Self {
328            Self {
329                state: State {
330                    installed_routines: ValidRoutines::default(),
331                    uninstalled_routines: Vec::default(),
332                    conntrack: conntrack::Table::new::<IntoCoreTimerCtx>(bindings_ctx),
333                    nat_installed: OneWayBoolean::default(),
334                },
335                nat: FakeNatCtx::default(),
336            }
337        }
338
339        pub fn with_ip_routines(
340            bindings_ctx: &mut FakeBindingsCtx<I>,
341            routines: IpRoutines<I, FakeBindingsCtx<I>, ()>,
342        ) -> Self {
343            let (installed_routines, uninstalled_routines) =
344                ValidRoutines::new(Routines { ip: routines, ..Default::default() })
345                    .expect("invalid state");
346            Self {
347                state: State {
348                    installed_routines,
349                    uninstalled_routines,
350                    conntrack: conntrack::Table::new::<IntoCoreTimerCtx>(bindings_ctx),
351                    nat_installed: OneWayBoolean::default(),
352                },
353                nat: FakeNatCtx::default(),
354            }
355        }
356
357        pub fn with_nat_routines_and_device_addrs(
358            bindings_ctx: &mut FakeBindingsCtx<I>,
359            routines: NatRoutines<I, FakeBindingsCtx<I>, ()>,
360            device_addrs: impl IntoIterator<
361                Item = (FakeMatcherDeviceId, AddrSubnet<I::Addr, I::AssignedWitness>),
362            >,
363        ) -> Self {
364            let (installed_routines, uninstalled_routines) =
365                ValidRoutines::new(Routines { nat: routines, ..Default::default() })
366                    .expect("invalid state");
367            Self {
368                state: State {
369                    installed_routines,
370                    uninstalled_routines,
371                    conntrack: conntrack::Table::new::<IntoCoreTimerCtx>(bindings_ctx),
372                    nat_installed: OneWayBoolean::TRUE,
373                },
374                nat: FakeNatCtx {
375                    device_addrs: device_addrs
376                        .into_iter()
377                        .map(|(device, addr)| (device, FakePrimaryAddressId(Arc::new(addr))))
378                        .collect(),
379                },
380            }
381        }
382
383        pub fn conntrack(
384            &mut self,
385        ) -> &conntrack::Table<I, NatConfig<I, FakeWeakAddressId<I>>, FakeBindingsCtx<I>> {
386            &self.state.conntrack
387        }
388    }
389
390    impl<I: TestIpExt> DeviceIdContext<AnyDevice> for FakeCtx<I> {
391        type DeviceId = FakeMatcherDeviceId;
392        type WeakDeviceId = FakeWeakDeviceId<FakeMatcherDeviceId>;
393    }
394
395    impl<I: TestIpExt> IpDeviceAddressIdContext<I> for FakeCtx<I> {
396        type AddressId = FakeAddressId<I>;
397        type WeakAddressId = FakeWeakAddressId<I>;
398    }
399
400    impl<I: TestIpExt> FilterIpContext<I, FakeBindingsCtx<I>> for FakeCtx<I> {
401        type NatCtx<'a> = FakeNatCtx<I>;
402
403        fn with_filter_state_and_nat_ctx<
404            O,
405            F: FnOnce(&State<I, FakeWeakAddressId<I>, FakeBindingsCtx<I>>, &mut Self::NatCtx<'_>) -> O,
406        >(
407            &mut self,
408            cb: F,
409        ) -> O {
410            let Self { state, nat } = self;
411            cb(state, nat)
412        }
413    }
414
415    impl<I: TestIpExt> FakeNatCtx<I> {
416        pub fn new(
417            device_addrs: impl IntoIterator<
418                Item = (FakeMatcherDeviceId, AddrSubnet<I::Addr, I::AssignedWitness>),
419            >,
420        ) -> Self {
421            Self {
422                device_addrs: device_addrs
423                    .into_iter()
424                    .map(|(device, addr)| (device, FakePrimaryAddressId(Arc::new(addr))))
425                    .collect(),
426            }
427        }
428    }
429
430    impl<I: TestIpExt> DeviceIdContext<AnyDevice> for FakeNatCtx<I> {
431        type DeviceId = FakeMatcherDeviceId;
432        type WeakDeviceId = FakeWeakDeviceId<FakeMatcherDeviceId>;
433    }
434
435    impl<I: TestIpExt> IpDeviceAddressIdContext<I> for FakeNatCtx<I> {
436        type AddressId = FakeAddressId<I>;
437        type WeakAddressId = FakeWeakAddressId<I>;
438    }
439
440    impl<I: TestIpExt> NatContext<I, FakeBindingsCtx<I>> for FakeNatCtx<I> {
441        fn get_local_addr_for_remote(
442            &mut self,
443            device_id: &Self::DeviceId,
444            _remote: Option<SpecifiedAddr<I::Addr>>,
445        ) -> Option<Self::AddressId> {
446            let FakePrimaryAddressId(primary) = self.device_addrs.get(device_id)?;
447            Some(FakeAddressId(primary.clone()))
448        }
449
450        fn get_address_id(
451            &mut self,
452            device_id: &Self::DeviceId,
453            addr: IpDeviceAddr<I::Addr>,
454        ) -> Option<Self::AddressId> {
455            let FakePrimaryAddressId(id) = self.device_addrs.get(device_id)?;
456            let id = FakeAddressId(id.clone());
457            if id.addr() == addr { Some(id) } else { None }
458        }
459    }
460
461    pub struct FakeBindingsCtx<I: Ip> {
462        pub timer_ctx: FakeTimerCtx<FilterTimerId<I>>,
463        pub rng: FakeCryptoRng,
464    }
465
466    impl<I: Ip> FakeBindingsCtx<I> {
467        pub(crate) fn new() -> Self {
468            Self { timer_ctx: FakeTimerCtx::default(), rng: FakeCryptoRng::default() }
469        }
470
471        pub(crate) fn sleep(&mut self, time_elapsed: Duration) {
472            self.timer_ctx.instant.sleep(time_elapsed)
473        }
474    }
475
476    impl<I: Ip> InstantBindingsTypes for FakeBindingsCtx<I> {
477        type Instant = FakeInstant;
478        type AtomicInstant = FakeAtomicInstant;
479    }
480
481    #[derive(Debug)]
482    pub struct FakeBindingsPacketMatcher {
483        num_calls: AtomicUsize,
484        result: bool,
485    }
486
487    impl FakeBindingsPacketMatcher {
488        pub fn new(result: bool) -> Arc<Self> {
489            Arc::new(Self { num_calls: AtomicUsize::new(0), result })
490        }
491
492        pub fn num_calls(&self) -> usize {
493            self.num_calls.load(core::sync::atomic::Ordering::SeqCst)
494        }
495    }
496
497    impl BindingsPacketMatcher<FakeMatcherDeviceId> for FakeBindingsPacketMatcher {
498        fn matches<I: FilterIpExt, P: FilterIpPacket<I>>(
499            &self,
500            _packet: &P,
501            _interfaces: Interfaces<'_, FakeMatcherDeviceId>,
502            _socket_info: &impl FilterPacketMetadata,
503        ) -> bool {
504            let _: usize = self.num_calls.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
505            self.result
506        }
507    }
508
509    impl InspectableValue for FakeBindingsPacketMatcher {
510        fn record<I: Inspector>(&self, _name: &str, _inspector: &mut I) {
511            unimplemented!()
512        }
513    }
514
515    impl<I: Ip> MatcherBindingsTypes for FakeBindingsCtx<I> {
516        type DeviceClass = FakeDeviceClass;
517        type BindingsPacketMatcher = Arc<FakeBindingsPacketMatcher>;
518    }
519
520    impl<I: Ip> InstantContext for FakeBindingsCtx<I> {
521        fn now(&self) -> Self::Instant {
522            self.timer_ctx.now()
523        }
524    }
525
526    impl<I: Ip> TimerBindingsTypes for FakeBindingsCtx<I> {
527        type Timer = <FakeTimerCtx<FilterTimerId<I>> as TimerBindingsTypes>::Timer;
528        type DispatchId = <FakeTimerCtx<FilterTimerId<I>> as TimerBindingsTypes>::DispatchId;
529        type UniqueTimerId = <FakeTimerCtx<FilterTimerId<I>> as TimerBindingsTypes>::UniqueTimerId;
530    }
531
532    impl<I: Ip> TimerContext for FakeBindingsCtx<I> {
533        fn new_timer(&mut self, id: Self::DispatchId) -> Self::Timer {
534            self.timer_ctx.new_timer(id)
535        }
536
537        fn schedule_timer_instant(
538            &mut self,
539            time: Self::Instant,
540            timer: &mut Self::Timer,
541        ) -> Option<Self::Instant> {
542            self.timer_ctx.schedule_timer_instant(time, timer)
543        }
544
545        fn cancel_timer(&mut self, timer: &mut Self::Timer) -> Option<Self::Instant> {
546            self.timer_ctx.cancel_timer(timer)
547        }
548
549        fn scheduled_instant(&self, timer: &mut Self::Timer) -> Option<Self::Instant> {
550            self.timer_ctx.scheduled_instant(timer)
551        }
552
553        fn unique_timer_id(&self, timer: &Self::Timer) -> Self::UniqueTimerId {
554            self.timer_ctx.unique_timer_id(timer)
555        }
556    }
557
558    impl<I: Ip> WithFakeTimerContext<FilterTimerId<I>> for FakeBindingsCtx<I> {
559        fn with_fake_timer_ctx<O, F: FnOnce(&FakeTimerCtx<FilterTimerId<I>>) -> O>(
560            &self,
561            f: F,
562        ) -> O {
563            f(&self.timer_ctx)
564        }
565
566        fn with_fake_timer_ctx_mut<O, F: FnOnce(&mut FakeTimerCtx<FilterTimerId<I>>) -> O>(
567            &mut self,
568            f: F,
569        ) -> O {
570            f(&mut self.timer_ctx)
571        }
572    }
573
574    impl<I: Ip> RngContext for FakeBindingsCtx<I> {
575        type Rng<'a>
576            = FakeCryptoRng
577        where
578            Self: 'a;
579
580        fn rng(&mut self) -> Self::Rng<'_> {
581            self.rng.clone()
582        }
583    }
584}