Skip to main content

vpp_plugin/vnet/
feature.rs

1//! VPP networking features
2
3use std::{cell::UnsafeCell, marker::PhantomData};
4
5use crate::{
6    bindings::{
7        feature_main, vlib_helper_remove_feature_from_registrations, vnet_feature_enable_disable,
8        vnet_feature_registration_t,
9    },
10    vlib::{
11        BarrierHeldMainRef,
12        node::{Node, NodeRegistration},
13    },
14    vnet::{error::VnetError, types::SwIfIndex},
15};
16
17/// Registration information for a VPP feature
18///
19/// This is typically created automatically using the [`crate::vnet_feature_init`] macro.
20pub struct FeatureRegistration<FeatureConfig> {
21    feat: UnsafeCell<vnet_feature_registration_t>,
22    _marker: PhantomData<FeatureConfig>,
23}
24
25impl<FeatureConfig> FeatureRegistration<FeatureConfig> {
26    /// Creates a new `FeatureRegistration` from the given registration data
27    ///
28    /// The `node_name` field will be filled in from the name of the `node` parameter.
29    ///
30    /// # Safety
31    ///
32    /// - The `arc_name` field must point to a valid nul-terminated C string that lives for the
33    ///   lifetime of the registration.
34    /// - The `runs_before` and `runs_after` fields must either be a valid null-terminated array
35    ///   of valid pointers to nul-terminated C string that live for the lifetime of the
36    ///   registration, or null.
37    /// - Other pointers may be left null.
38    /// - [`Self::register`] must be called before calling other methods.
39    pub const unsafe fn new<N: Node<FeatureData = FeatureConfig>, const N_NEXT_NODES: usize>(
40        mut feat: vnet_feature_registration_t,
41        node: &'static NodeRegistration<N, N_NEXT_NODES>,
42    ) -> Self {
43        feat.node_name = node.name_ptr().cast_mut();
44        Self {
45            feat: UnsafeCell::new(feat),
46            _marker: PhantomData,
47        }
48    }
49
50    /// Registers the feature with VPP
51    ///
52    /// # Safety
53    ///
54    /// - Must be called only once for this node registration.
55    /// - Must be called from a constructor function that is invoked before VPP initialises.
56    pub unsafe fn register(&'static self) {
57        // SAFETY: it is safe for this method to take a non-mutable self and mutates the
58        // vnet_feature_registration as it is done through an UnsafeCell and method
59        // preconditions mean this is done at startup when there are no outstanding references,
60        // including from other threads. The updating of the feature_main feature linked list is
61        // in accordance with vpp expectations.
62        unsafe {
63            let fm = std::ptr::addr_of_mut!(feature_main);
64            let r = self.feat.get();
65            (*r).next = (*fm).next_feature;
66            (*fm).next_feature = r;
67        }
68    }
69
70    /// Unregisters the node with VPP
71    ///
72    /// # Safety
73    ///
74    /// - Must be called from a destructor function.
75    /// - Must be called only once for this node registration.
76    /// - The node must have been previously registered with VPP using [`Self::register`].
77    pub unsafe fn unregister(&self) {
78        // SAFETY: it is safe for this method to take a non-mutable self and mutates the
79        // vnet_feature_registration as it is done through an UnsafeCell and method
80        // preconditions mean this is done during a destructor when there are no outstanding
81        // references, including from other threads. The updating of the feature_main feature
82        // linked list is in accordance with vpp expectations.
83        unsafe {
84            let fm = std::ptr::addr_of_mut!(feature_main);
85            let r = self.feat.get();
86            vlib_helper_remove_feature_from_registrations(fm, r);
87        }
88    }
89
90    /// Enables the feature on the given software interface index
91    ///
92    /// If `feature_config` is provided, it can be obtained by feature node when processing
93    /// packets.
94    ///
95    /// If the feature arc doesn't operate on a per-interface basis, `sw_if_index` can be
96    /// set to `SwIfIndex::new(0)`.
97    pub fn enable(
98        &self,
99        _vm: &BarrierHeldMainRef,
100        sw_if_index: SwIfIndex,
101        feature_config: FeatureConfig,
102    ) -> Result<(), VnetError> {
103        // SAFETY: arc_name and node_name pointers are valid nul-terminated strings, the
104        // function validates sw_if_index and returns an error if not valid, and non-concurrent
105        // access is ensured by the fact that the method takes a &BarrierHeldMainRef, which can
106        // only be obtained on the main thread and with no packets flowing (and potentially
107        // using the state being modified).
108        let ret = unsafe {
109            let feat = self.feat.get().cast_const();
110            vnet_feature_enable_disable(
111                (*feat).arc_name,
112                (*feat).node_name,
113                sw_if_index.into(),
114                1,
115                std::ptr::addr_of!(feature_config).cast_mut().cast(),
116                std::mem::size_of::<FeatureConfig>() as u32,
117            )
118        };
119        if ret == 0 { Ok(()) } else { Err(ret.into()) }
120    }
121
122    /// Disables the feature on the given software interface index
123    ///
124    /// If the feature arc doesn't operate on a per-interface basis, `sw_if_index` can be
125    /// set to `SwIfIndex::new(0)`.
126    pub fn disable(
127        &self,
128        _vm: &BarrierHeldMainRef,
129        sw_if_index: SwIfIndex,
130    ) -> Result<(), VnetError> {
131        // SAFETY: arc_name and node_name pointers are valid nul-terminated strings, the
132        // function validates sw_if_index and returns an error if not valid, and non-concurrent
133        // access is ensured by the fact that the method takes a &BarrierHeldMainRef, which can
134        // only be obtained on the main thread and with no packets flowing (and potentially
135        // using the state being modified).
136        let ret = unsafe {
137            let feat = self.feat.get().cast_const();
138            vnet_feature_enable_disable(
139                (*feat).arc_name,
140                (*feat).node_name,
141                sw_if_index.into(),
142                0,
143                std::ptr::null_mut(),
144                0,
145            )
146        };
147        if ret == 0 { Ok(()) } else { Err(ret.into()) }
148    }
149}
150
151// SAFETY: all safe methods that mutate state should take a &VlibMainRef which can't be created by safe code or sent between thread, and which guarantees that we are running on either the main or worker threads. Then methods either use per-thread data, or restrict themselves to being able to run on a single thread (typically the main thread).
152unsafe impl<T> Sync for FeatureRegistration<T> {}