vpp_plugin/vlib/
node.rs

1//! VPP node abstractions
2//!
3//! This module contains abstractions around VPP nodes, including node registration,
4//! node runtime data, frames, and error counters.
5
6use std::{cell::UnsafeCell, sync::atomic::AtomicU64};
7
8use arrayvec::ArrayVec;
9
10use crate::{
11    bindings::{
12        _vlib_node_registration, vlib_error_desc_t, vlib_frame_t, vlib_helper_get_global_main,
13        vlib_helper_remove_node_from_registrations, vlib_node_fn_registration_t,
14        vlib_node_registration_t, vlib_node_runtime_t, vlib_node_t,
15    },
16    vlib::buffer::BufferRef,
17    vlib::MainRef,
18};
19
20/// Max number of vector elements to process at once per node
21///
22/// Corresponds to `VLIB_FRAME_SIZE`` in VPP.
23pub const FRAME_SIZE: usize = crate::bindings::VLIB_FRAME_SIZE as usize;
24/// Frame data alignment
25///
26/// Corresponds to `VLIB_FRAME_DATA_ALIGN` in VPP.
27pub const FRAME_DATA_ALIGN: usize = crate::bindings::VLIB_FRAME_DATA_ALIGN as usize;
28
29/// Runtime data alignment
30// Ref: CLIB_ALIGN_MARK (runtime_data_pad, 8) in vlib_node_runtime_t struct definition
31pub const RUNTIME_DATA_ALIGN: usize = 8;
32
33/// Trait for defining next nodes of a VPP node
34///
35/// Typically this trait is implemented automatically using the [`vpp_plugin_macros::NextNodes`]
36/// derive macro.
37///
38/// # Safety
39///
40/// - The length of C_NAMES must be greater than the maximum value that into_u16 can return
41///   (i.e. if implemented for an enum, it should match number of discriminators, and there are no
42///   gaps in the discriminator values).
43/// - Each pointer in C_NAMES must be a valid, nul-terminated string and must stay valid for the
44///   duration of that any nodes using this NextNodes implementation are registered.
45pub unsafe trait NextNodes {
46    /// Array type for [`Self::C_NAMES`]
47    type CNamesArray: AsRef<[*mut ::std::os::raw::c_char]>;
48    /// Array of C names of the next nodes indexed by [`Self::into_u16`]
49    const C_NAMES: Self::CNamesArray;
50
51    /// The u16 value of this next node
52    fn into_u16(self) -> u16;
53}
54
55/// Trait for defining error counters of a VPP node
56///
57/// Typically this trait is implemented automatically using the [`vpp_plugin_macros::ErrorCounters`]
58/// derive macro.
59///
60/// # Safety
61///
62/// - The length of C_DESCRIPTIONS must be greater than the maximum value that into_u16 can return
63///   (i.e. if implemented for an enum, it should match number of discriminators, and there are no
64///   gaps in the discriminator values).
65/// - Each entry in C_DESCRIPTIONS:
66///   - `name` must be a valid nul-terminated string.
67///   - `description` must be either null or a valid nul-terminated string.
68pub unsafe trait ErrorCounters {
69    /// Array type for [`Self::C_DESCRIPTIONS`]
70    type CDescriptionsArray: AsRef<[vlib_error_desc_t]>;
71    /// Array of C descriptions of the errors indexed by [`Self::into_u16`]
72    const C_DESCRIPTIONS: Self::CDescriptionsArray;
73
74    /// The u16 value of this next node
75    fn into_u16(self) -> u16;
76}
77
78impl<N: Node, const N_NEXT_NODES: usize> NodeRegistration<N, N_NEXT_NODES> {
79    /// Creates a new `NodeRegistration` from the given registration data
80    pub const fn new(
81        registration: _vlib_node_registration<[*mut std::os::raw::c_char; N_NEXT_NODES]>,
82    ) -> Self {
83        Self {
84            registration: UnsafeCell::new(registration),
85            _marker: ::std::marker::PhantomData,
86        }
87    }
88
89    /// Registers the node with VPP
90    ///
91    /// # Safety
92    ///
93    /// - Must be called only once for this node registration.
94    /// - Must be called from a constructor function that is invoked before VPP initialises.
95    /// - The following pointers in the registration data must be valid:
96    ///   - `name` (must be a valid, nul-terminated string)
97    ///   - `function` (must point to a valid node function)
98    ///   - `error_descriptions` (must point to an array of `n_errors` valid `vlib_error_desc_t` entries)
99    ///   - `next_nodes` (each entry must be a valid nul-terminated string and length must be at least `n_next_nodes`)
100    /// - Other pointers in the registration data must be either valid or null as appropriate.
101    /// - `vector_size`, `scalar_size`, and `aux_size` must match the sizes of the corresponding types in `N`.
102    pub unsafe fn register(&'static self) {
103        let vgm = vlib_helper_get_global_main();
104        let reg = self.registration.get();
105        (*reg).next_registration = (*vgm).node_registrations;
106        (*vgm).node_registrations = reg as *mut vlib_node_registration_t;
107    }
108
109    /// Unregisters the node from VPP
110    ///
111    /// # Safety
112    ///
113    /// - Must be called only once for this node registration.
114    /// - Must be called from a destructor function that is invoked after VPP uninitialises.
115    /// - The node must have been previously registered with VPP using [`Self::register`].
116    pub unsafe fn unregister(&self) {
117        let vgm = vlib_helper_get_global_main();
118        vlib_helper_remove_node_from_registrations(
119            vgm,
120            self.registration.get() as *mut vlib_node_registration_t,
121        );
122    }
123
124    /// Registers a node function with VPP
125    ///
126    /// # Safety
127    /// - The `node_fn` pointer must be valid and point to a properly initialised
128    ///   `vlib_node_fn_registration_t`.
129    /// - The `node_fn` must not have been previously registered with VPP.
130    pub unsafe fn register_node_fn(&self, node_fn: *mut vlib_node_fn_registration_t) {
131        let reg = self.registration.get();
132        (*node_fn).next_registration = (*reg).node_fn_registrations;
133        (*reg).node_fn_registrations = node_fn;
134    }
135
136    /// Creates a `&mut NodeRuntimeRef` directly from a pointer
137    ///
138    /// This is a convenience method that calls [`NodeRuntimeRef::from_ptr_mut`], for code that
139    /// has an instance of `NodeRegistration`, but doesn't know the name of the type for the node.
140    /// As such, `self` isn't used, it's just taken so that the generic types are known.
141    ///
142    /// # Safety
143    ///
144    /// - The same preconditions as [`NodeRuntimeRef::from_ptr_mut`] apply.
145    pub unsafe fn node_runtime_from_ptr<'a>(
146        &self,
147        ptr: *mut vlib_node_runtime_t,
148    ) -> &'a mut NodeRuntimeRef<N> {
149        NodeRuntimeRef::from_ptr_mut(ptr)
150    }
151
152    /// Creates a `&mut FrameRef` directly from a pointer
153    ///
154    /// This is a convenience method that calls [`FrameRef::from_ptr_mut`], for code that
155    /// has an instance of `NodeRegistration`, but doesn't know the name of the type for the node.
156    /// As such, `self` isn't used, it's just taken so that the generic types are known.
157    ///
158    /// # Safety
159    ///
160    /// - The same preconditions as [`FrameRef::from_ptr_mut`] apply.
161    pub unsafe fn frame_from_ptr<'a>(&self, ptr: *mut vlib_frame_t) -> &'a mut FrameRef<N> {
162        FrameRef::from_ptr_mut(ptr)
163    }
164
165    /// Creates a `&mut NodeRef` directly from a pointer
166    ///
167    /// This is a convenience method that calls [`NodeRef::from_ptr_mut`], for code that
168    /// has an instance of `NodeRegistration`, but doesn't know the name of the type for the node.
169    /// As such, `self` isn't used, it's just taken so that the generic types are known.
170    ///
171    /// # Safety
172    ///
173    /// - The same preconditions as [`NodeRef::from_ptr_mut`] apply.
174    pub unsafe fn node_from_ptr<'a>(&self, ptr: *mut vlib_node_t) -> &'a mut NodeRef<N> {
175        NodeRef::from_ptr_mut(ptr)
176    }
177
178    /// Returns the name of the node as a pointer to a C string
179    ///
180    /// Note: no guarantees are made about the validity of the pointer or the string data.
181    pub const fn name_ptr(&self) -> *const std::os::raw::c_char {
182        // SAFETY: it is safe to access a const pointer to the name as it is not mutated after creation
183        unsafe { (*self.registration.get()).name }
184    }
185}
186
187// SAFETY: there is nothing in vlib_node_registration that is tied to a specific thread or that
188// mutates global state, so it's safe to send between threads.
189unsafe impl<N: Node, const N_NEXT_NODES: usize> Send for NodeRegistration<N, N_NEXT_NODES> {}
190// SAFETY: NodeRegistration doesn't allow any modification after creation (and vpp doesn't
191// modify it afterwards either), so it's safe to access from multiple threads. The only exception
192// to this is the register/unregister/register_node_fn methods, but it's the duty of the caller
193// to ensure they are called at times when no other threads have a reference to the object.
194unsafe impl<N: Node, const N_NEXT_NODES: usize> Sync for NodeRegistration<N, N_NEXT_NODES> {}
195
196/// Registration information for a VPP node
197///
198/// Used for registering and unregistering nodes with VPP.
199///
200/// This is typically created automatically using the [`vpp_plugin_macros::vlib_node`] macro.
201pub struct NodeRegistration<N: Node, const N_NEXT_NODES: usize> {
202    registration: UnsafeCell<_vlib_node_registration<[*mut std::os::raw::c_char; N_NEXT_NODES]>>,
203    _marker: std::marker::PhantomData<N>,
204}
205
206/// Reference to a VPP node runtime data
207///
208/// This is a per-node-instance, per-thread structure containing runtime data about the node.
209///
210/// A `&mut NodeRuntimeRef` corresponds to `vlib_node_runtime_t *` in C.
211#[repr(transparent)]
212pub struct NodeRuntimeRef<N: Node + ?Sized>(foreign_types::Opaque, std::marker::PhantomData<N>);
213
214impl<N: Node> NodeRuntimeRef<N> {
215    /// Creates a `&mut NodeRuntimeRef` directly from a pointer
216    ///
217    /// # Safety
218    /// - The pointer must be valid and a properly initialised `vlib_node_runtime_t`.
219    /// - The pointer must stay valid and the contents must not be mutated for the duration of the
220    ///   lifetime of the returned reference.
221    /// - The `runtime_data` field must be set correctly to point to a valid `RuntimeData` instance.
222    /// - The `node_index` field must be set correctly to point to a valid node in the VPP node main.
223    #[inline(always)]
224    pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_node_runtime_t) -> &'a mut Self {
225        &mut *(ptr as *mut _)
226    }
227
228    /// Returns the raw pointer to the underlying `vlib_node_runtime_t`
229    #[inline(always)]
230    pub fn as_ptr(&self) -> *mut vlib_node_runtime_t {
231        self as *const _ as *mut _
232    }
233
234    /// Returns the node-defined runtime data of the node
235    pub fn runtime_data(&self) -> &N::RuntimeData {
236        // SAFETY: we have a valid pointer to vlib_node_runtime_t, and the runtime_data field is
237        // set correctly
238        unsafe { &*((*self.as_ptr()).runtime_data.as_ptr() as *const N::RuntimeData) }
239    }
240
241    /// Returns the node-defined runtime data of the node as mutable
242    pub fn runtime_data_mut(&mut self) -> &mut N::RuntimeData {
243        // SAFETY: we have a valid pointer to vlib_node_runtime_t, and the runtime_data field is
244        // set correctly
245        unsafe { &mut *((*self.as_ptr()).runtime_data.as_ptr() as *mut N::RuntimeData) }
246    }
247
248    /// Returns the associated node reference
249    pub fn node(&self, vm: &MainRef) -> &NodeRef<N> {
250        // SAFETY: we have a valid pointer to vlib_node_runtime_t, and the node_index field is
251        // set correctly
252        unsafe {
253            let node_index = (*self.as_ptr()).node_index;
254            // TODO: use vec_elt
255            let node_ptr = *(*vm.as_ptr()).node_main.nodes.add(node_index as usize);
256            NodeRef::from_ptr_mut(node_ptr)
257        }
258    }
259
260    /// Increments the given error counter by the specified amount
261    ///
262    /// See also [`NodeRef::increment_error_counter`].
263    pub fn increment_error_counter(&self, vm: &MainRef, counter: N::Errors, increment: u64) {
264        self.node(vm)
265            .increment_error_counter(vm, counter, increment)
266    }
267}
268
269/// Reference to a VPP node frame
270///
271/// A `&mut FrameRef` corresponds to `vlib_frame_t *` in C.
272pub struct FrameRef<N: Node + ?Sized>(foreign_types::Opaque, std::marker::PhantomData<N>);
273
274impl<N: Node + ?Sized> FrameRef<N> {
275    /// Creates a `&mut FrameRef` directly from a pointer
276    ///
277    /// # Safety
278    /// - The pointer must be valid and a properly initialised `vlib_frame_t`.
279    /// - The pointer must stay valid and the contents must not be mutated for the duration of the
280    ///   lifetime of the returned object.
281    /// - `scalar_offset`, `vector_offset`, and `aux_offset` must be set correctly to point to
282    ///   the valid, initialised data areas in the frame, and `n_vectors` must be set correctly
283    ///   to indicate the number of valid vector and aux elements.
284    pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_frame_t) -> &'a mut Self {
285        &mut *(ptr as *mut _)
286    }
287
288    /// Returns the raw pointer to the underlying `vlib_frame_t`
289    pub fn as_ptr(&self) -> *mut vlib_frame_t {
290        self as *const _ as *mut _
291    }
292
293    /// Returns the vector data elements in the frame as a slice
294    pub fn vector(&self) -> &[N::Vector] {
295        // SAFETY: the frame's n_vectors field indicates the number of valid vector elements,
296        // vector_offset is set correctly when creating the frame, and we have a valid pointer to
297        // the frame
298        unsafe {
299            let vec = (self.as_ptr() as *const u8).add((*(self.as_ptr())).vector_offset as usize)
300                as *const N::Vector;
301            std::slice::from_raw_parts(vec, (*(self.as_ptr())).n_vectors as usize)
302        }
303    }
304
305    /// Returns the scalar data in the frame
306    pub fn scalar(&self) -> &N::Scalar {
307        // SAFETY: scalar_offset is set correctly when creating the frame, and we have a valid
308        // pointer to the frame
309        unsafe {
310            &*((self.as_ptr() as *const u8).add((*(self.as_ptr())).scalar_offset as usize)
311                as *const N::Scalar)
312        }
313    }
314
315    /// Returns the auxiliary data elements in the frame as a slice
316    ///
317    /// Note: the length of the returned slice is equal to the number of vectors in the frame.
318    pub fn aux(&self) -> &[N::Aux] {
319        // SAFETY: the frame's n_vectors field indicates the number of valid aux elements,
320        // aux_offset is set correctly when creating the frame, and we have a valid pointer to
321        // the frame
322        unsafe {
323            let aux = (self.as_ptr() as *const u8).add((*(self.as_ptr())).aux_offset as usize)
324                as *const N::Aux;
325            std::slice::from_raw_parts(aux, (*(self.as_ptr())).n_vectors as usize)
326        }
327    }
328}
329
330/// Trait for types that can be used as buffer indices in vector nodes
331pub trait VectorBufferIndex: Send + Copy {
332    /// Converts a slice of Self to a slice of u32
333    fn as_u32_slice(slice: &[Self]) -> &[u32];
334}
335
336impl<N, V> FrameRef<N>
337where
338    N: Node<Vector = V> + ?Sized,
339    V: VectorBufferIndex,
340{
341    /// Get pointers to buffers for the given buffer indices, writing them into the provided `to` arrayvec.
342    ///
343    /// This is similar to `vlib_get_buffers` in the C API.
344    ///
345    /// Note that although it would be more idiomatic to return an `ArrayVec` directly, this
346    /// method takes a mutable reference to an `ArrayVec` to avoid an unnecessary copy when
347    /// returning.
348    ///
349    /// # Safety
350    ///
351    /// - The caller must ensure that `to` has enough capacity to hold all the buffers
352    ///   corresponding to the indices in `from_indices`.
353    /// - Each buffer's `feature_arc_index` and `current_config_index` must be consistent with
354    ///   the `FeatureData` type. If they are not known (i.e. because the caller the node isn't
355    ///   being executed in a feature arc), FeatureData should be a zero-sized type such as `()`.
356    /// - Must not be called more than once without the framing being flushed in between as
357    ///   Rust's reference aliasing rules will be violated.
358    #[inline(always)]
359    pub unsafe fn get_buffers<'me, 'vm, 'buf: 'vm + 'me, const ARRAY_N: usize>(
360        &'me self,
361        vm: &'vm MainRef,
362        to: &mut ArrayVec<&'buf mut BufferRef<N::FeatureData>, ARRAY_N>,
363    ) -> &'me [N::Vector] {
364        let from = self.vector();
365        vm.get_buffers(N::Vector::as_u32_slice(from), to);
366        from
367    }
368}
369
370/// Reference to a VPP node
371///
372/// This is a per-node-instance structure containing metadata about the node and certain node
373/// state.
374///
375/// A `&mut NodeRef` corresponds to `vlib_node_t *` in C.
376#[repr(transparent)]
377pub struct NodeRef<N: Node>(foreign_types::Opaque, std::marker::PhantomData<N>);
378
379impl<N: Node> NodeRef<N> {
380    /// Creates a `&mut NodeRef` directly from a pointer
381    ///
382    /// # Safety
383    ///
384    /// - The pointer must be valid and a properly initialised `vlib_node_t`.
385    /// - The pointer must stay valid and the contents must not be mutated for the duration of the
386    ///   lifetime of the returned object.
387    /// - The `error_heap_index` field must be set correctly to point to the base index of the
388    ///   node's error counters in the VPP error main counters array.
389    pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_node_t) -> &'a mut Self {
390        &mut *(ptr as *mut _)
391    }
392
393    /// Returns the raw pointer to the underlying `vlib_node_t`
394    pub fn as_ptr(&self) -> *mut vlib_node_t {
395        self as *const _ as *mut _
396    }
397
398    /// Increments the given error counter by the specified amount
399    ///
400    /// This corresponds to the VPP C function `vlib_node_increment_counter`.
401    pub fn increment_error_counter(&self, vm: &MainRef, counter: N::Errors, increment: u64) {
402        // SAFETY: we have a valid pointer to vlib_node_t, the error_heap_index field is
403        // set correctly, we are the only writer to counters (because it's per-thread),
404        // and we perform an atomic store to the counter so that concurrent readers cannot see
405        // a partial value.
406        unsafe {
407            let em = &(*vm.as_ptr()).error_main;
408            let node_counter_base_index = (*self.as_ptr()).error_heap_index;
409            let ptr = em
410                .counters
411                .add(node_counter_base_index as usize + counter.into_u16() as usize);
412            AtomicU64::from_ptr(ptr).store(*ptr + increment, std::sync::atomic::Ordering::Relaxed);
413        }
414    }
415}
416
417/// Trait for defining a VPP node
418pub trait Node {
419    /// Type of vector data sent to the node
420    type Vector;
421    /// Type of scalar data sent to the node
422    ///
423    /// The scalar data is shared between all vector elements in a frame.
424    ///
425    /// This is rarely used and can be set to `()` if not needed.
426    type Scalar;
427    /// Type of auxiliary data sent to the node
428    ///
429    /// The auxiliary data is per-vector.
430    ///
431    /// This is rarely used and can be set to `()` if not needed.
432    type Aux;
433
434    /// Type defining the next nodes of this node
435    ///
436    /// Typically an enum using the [`vpp_plugin_macros::NextNodes`] derive macro.
437    type NextNodes: NextNodes;
438    /// Type defining the runtime data of this node
439    ///
440    /// This data is per-node instance and per-thread.
441    // Send + Copy due to:
442    //     if (vec_len (n->runtime_data) > 0)
443    //       clib_memcpy (rt->runtime_data, n->runtime_data,
444    //                    vec_len (n->runtime_data));
445    //     else
446    //       clib_memset (rt->runtime_data, 0, VLIB_NODE_RUNTIME_DATA_SIZE);
447    type RuntimeData: Send + Copy;
448    /// Type defining the trace data of this node
449    // Send due to display from main thread, writing from worker threads.
450    // Copy because no destructor will be called on TraceData (since it's discard inside VPP
451    // code), and Copy is mutually exclusive with implementing the Drop trait. This isn't a
452    // soundness requirement (since Rust doesn't include not leaking memory in its definition
453    // of soundless), so this constraint could be dropped be relaxed if it turns out to be too
454    // much of a burden.
455    type TraceData: Send + Copy;
456    /// Type defining the error counters of this node
457    ///
458    /// Typically an enum using the [`vpp_plugin_macros::ErrorCounters`] derive macro.
459    type Errors: ErrorCounters;
460    /// Type defining the feature data of this node
461    ///
462    /// This is available when the node is used as a feature node invoked from a feature arc.
463    ///
464    /// If the node is not used as a feature node, this type is not used and so can be set to `()`.
465    // Send due to setting on main thread and retrieval on worker threads
466    type FeatureData: Send;
467
468    /// The packet processing function of the node
469    ///
470    /// Returns the number of packets processed from the frame.
471    ///
472    /// # Safety
473    /// - The caller must ensure that precondition assumptions for the state of the buffers
474    ///   in the frame are met, e.g. that the packets are valid and have the expected headers.
475    ///   For example, if the node is expected to be invoked during the ip4-input feature arc, the
476    ///   caller must ensure that all packets in the frame are valid IPv4 packets and the current
477    ///   data offset is pointing to the start of the IPv4 header. In addition, any assumptions
478    ///   about how much of the packet has been linearised must also be upheld.
479    /// - The node's precondition assumptions may also inherit from those of next nodes the node
480    ///   sends the buffers to.
481    unsafe fn function(
482        &self,
483        vm: &mut MainRef,
484        node: &mut NodeRuntimeRef<Self>,
485        frame: &mut FrameRef<Self>,
486    ) -> u16;
487}