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