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 node::{Node, NodeRegistration},
12 BarrierHeldMainRef,
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 {
120 Ok(())
121 } else {
122 Err(ret.into())
123 }
124 }
125
126 /// Disables the feature on the given software interface index
127 ///
128 /// If the feature arc doesn't operate on a per-interface basis, `sw_if_index` can be
129 /// set to `SwIfIndex::new(0)`.
130 pub fn disable(
131 &self,
132 _vm: &BarrierHeldMainRef,
133 sw_if_index: SwIfIndex,
134 ) -> Result<(), VnetError> {
135 // SAFETY: arc_name and node_name pointers are valid nul-terminated strings, the
136 // function validates sw_if_index and returns an error if not valid, and non-concurrent
137 // access is ensured by the fact that the method takes a &BarrierHeldMainRef, which can
138 // only be obtained on the main thread and with no packets flowing (and potentially
139 // using the state being modified).
140 let ret = unsafe {
141 let feat = self.feat.get().cast_const();
142 vnet_feature_enable_disable(
143 (*feat).arc_name,
144 (*feat).node_name,
145 sw_if_index.into(),
146 0,
147 std::ptr::null_mut(),
148 0,
149 )
150 };
151 if ret == 0 {
152 Ok(())
153 } else {
154 Err(ret.into())
155 }
156 }
157}
158
159// 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).
160unsafe impl<T> Sync for FeatureRegistration<T> {}