Skip to main content

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;
9use bitflags::bitflags;
10
11#[cfg(feature = "process-node")]
12use crate::vlib::ProcessNode;
13use crate::{
14    bindings::{
15        _vlib_node_registration, VLIB_NODE_FLAG_ADAPTIVE_MODE,
16        VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES, VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH,
17        VLIB_NODE_FLAG_IS_DROP, VLIB_NODE_FLAG_IS_HANDOFF, VLIB_NODE_FLAG_IS_OUTPUT,
18        VLIB_NODE_FLAG_IS_PUNT, VLIB_NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE,
19        VLIB_NODE_FLAG_SWITCH_FROM_POLLING_TO_INTERRUPT_MODE, VLIB_NODE_FLAG_TRACE,
20        VLIB_NODE_FLAG_TRACE_SUPPORTED, vlib_error_desc_t, vlib_frame_t,
21        vlib_helper_get_global_main, vlib_helper_remove_node_from_registrations,
22        vlib_node_fn_registration_t, vlib_node_registration_t, vlib_node_runtime_t, vlib_node_t,
23    },
24    vlib::{MainRef, buffer::BufferRef},
25    vppinfra::VecRef,
26};
27
28/// Max number of vector elements to process at once per node
29///
30/// Corresponds to `VLIB_FRAME_SIZE`` in VPP.
31pub const FRAME_SIZE: usize = crate::bindings::VLIB_FRAME_SIZE as usize;
32/// Frame data alignment
33///
34/// Corresponds to `VLIB_FRAME_DATA_ALIGN` in VPP.
35pub const FRAME_DATA_ALIGN: usize = crate::bindings::VLIB_FRAME_DATA_ALIGN as usize;
36
37/// Runtime data alignment
38// Ref: CLIB_ALIGN_MARK (runtime_data_pad, 8) in vlib_node_runtime_t struct definition
39pub const RUNTIME_DATA_ALIGN: usize = 8;
40
41/// Trait for defining next nodes of a VPP node
42///
43/// Typically this trait is implemented automatically using the [`vpp_plugin_macros::NextNodes`]
44/// derive macro.
45///
46/// # Safety
47///
48/// - The length of C_NAMES must be greater than the maximum value that into_u16 can return
49///   (i.e. if implemented for an enum, it should match number of discriminators, and there are no
50///   gaps in the discriminator values).
51/// - Each pointer in C_NAMES must be a valid, nul-terminated string and must stay valid for the
52///   duration of that any nodes using this NextNodes implementation are registered.
53pub unsafe trait NextNodes {
54    /// Array type for [`Self::C_NAMES`]
55    type CNamesArray: AsRef<[*mut ::std::os::raw::c_char]>;
56    /// Array of C names of the next nodes indexed by [`Self::into_u16`]
57    const C_NAMES: Self::CNamesArray;
58
59    /// The u16 value of this next node
60    fn into_u16(self) -> u16;
61}
62
63/// Trait for defining error counters of a VPP node
64///
65/// Typically this trait is implemented automatically using the [`vpp_plugin_macros::ErrorCounters`]
66/// derive macro.
67///
68/// # Safety
69///
70/// - The length of C_DESCRIPTIONS must be greater than the maximum value that into_u16 can return
71///   (i.e. if implemented for an enum, it should match number of discriminators, and there are no
72///   gaps in the discriminator values).
73/// - Each entry in C_DESCRIPTIONS:
74///   - `name` must be a valid nul-terminated string.
75///   - `description` must be either null or a valid nul-terminated string.
76pub unsafe trait ErrorCounters {
77    /// Array type for [`Self::C_DESCRIPTIONS`]
78    type CDescriptionsArray: AsRef<[vlib_error_desc_t]>;
79    /// Array of C descriptions of the errors indexed by [`Self::into_u16`]
80    const C_DESCRIPTIONS: Self::CDescriptionsArray;
81
82    /// The u16 value of this next node
83    fn into_u16(self) -> u16;
84}
85
86impl<N: Node, const N_NEXT_NODES: usize> NodeRegistration<N, N_NEXT_NODES> {
87    /// Creates a new `NodeRegistration` from the given registration data
88    pub const fn new(
89        registration: _vlib_node_registration<[*mut std::os::raw::c_char; N_NEXT_NODES]>,
90    ) -> Self {
91        Self {
92            registration: UnsafeCell::new(registration),
93            _marker: ::std::marker::PhantomData,
94        }
95    }
96
97    /// Registers the node with VPP
98    ///
99    /// # Safety
100    ///
101    /// - Must be called only once for this node registration.
102    /// - Must be called from a constructor function that is invoked before VPP initialises.
103    /// - The following pointers in the registration data must be valid:
104    ///   - `name` (must be a valid, nul-terminated string)
105    ///   - `function` (must point to a valid node function)
106    ///   - `error_descriptions` (must point to an array of `n_errors` valid `vlib_error_desc_t` entries)
107    ///   - `next_nodes` (each entry must be a valid nul-terminated string and length must be at least `n_next_nodes`)
108    /// - Other pointers in the registration data must be either valid or null as appropriate.
109    /// - `vector_size`, `scalar_size`, and `aux_size` must match the sizes of the corresponding types in `N`.
110    /// - `n_errors` must match the discriminants in N::Errors
111    /// - `n_next_nodes` must match the discriminants in N::NextNodes
112    pub unsafe fn register(&'static self) {
113        // SAFETY: The safety requirements are documented in the function's safety comment.
114        unsafe {
115            let vgm = vlib_helper_get_global_main();
116            let reg = self.registration.get();
117            (*reg).next_registration = (*vgm).node_registrations;
118            (*vgm).node_registrations = reg as *mut vlib_node_registration_t;
119        }
120    }
121
122    /// Unregisters the node from VPP
123    ///
124    /// # Safety
125    ///
126    /// - Must be called only once for this node registration.
127    /// - Must be called from a destructor function that is invoked after VPP uninitialises.
128    /// - The node must have been previously registered with VPP using [`Self::register`].
129    pub unsafe fn unregister(&self) {
130        // SAFETY: The safety requirements are documented in the function's safety comment.
131        unsafe {
132            let vgm = vlib_helper_get_global_main();
133            vlib_helper_remove_node_from_registrations(
134                vgm,
135                self.registration.get() as *mut vlib_node_registration_t,
136            );
137        }
138    }
139
140    /// Registers a node function with VPP
141    ///
142    /// # Safety
143    /// - The `node_fn` pointer must be valid and point to a properly initialised
144    ///   `vlib_node_fn_registration_t`.
145    /// - The `node_fn` must not have been previously registered with VPP.
146    pub unsafe fn register_node_fn(&self, node_fn: *mut vlib_node_fn_registration_t) {
147        // SAFETY: The safety requirements are documented in the function's safety comment.
148        unsafe {
149            let reg = self.registration.get();
150            (*node_fn).next_registration = (*reg).node_fn_registrations;
151            (*reg).node_fn_registrations = node_fn;
152        }
153    }
154
155    /// Creates a `&mut NodeRuntimeRef` directly from a pointer
156    ///
157    /// This is a convenience method that calls [`NodeRuntimeRef::from_ptr_mut`], for code that
158    /// has an instance of `NodeRegistration`, but doesn't know the name of the type for the node.
159    /// As such, `self` isn't used, it's just taken so that the generic types are known.
160    ///
161    /// # Safety
162    ///
163    /// - The same preconditions as [`NodeRuntimeRef::from_ptr_mut`] apply.
164    pub unsafe fn node_runtime_from_ptr<'a>(
165        &self,
166        ptr: *mut vlib_node_runtime_t,
167    ) -> &'a mut NodeRuntimeRef<N> {
168        // SAFETY: The safety requirements are documented in the function's safety comment.
169        unsafe { NodeRuntimeRef::from_ptr_mut(ptr) }
170    }
171
172    /// Creates a `&mut FrameRef` directly from a pointer
173    ///
174    /// This is a convenience method that calls [`FrameRef::from_ptr_mut`], for code that
175    /// has an instance of `NodeRegistration`, but doesn't know the name of the type for the node.
176    /// As such, `self` isn't used, it's just taken so that the generic types are known.
177    ///
178    /// # Safety
179    ///
180    /// - The same preconditions as [`FrameRef::from_ptr_mut`] apply.
181    pub unsafe fn frame_from_ptr<'a>(&self, ptr: *mut vlib_frame_t) -> &'a mut FrameRef<N> {
182        // SAFETY: The safety requirements are documented in the function's safety comment.
183        unsafe { FrameRef::from_ptr_mut(ptr) }
184    }
185
186    /// Creates a `&mut NodeRef` directly from a pointer
187    ///
188    /// This is a convenience method that calls [`NodeRef::from_ptr_mut`], for code that
189    /// has an instance of `NodeRegistration`, but doesn't know the name of the type for the node.
190    /// As such, `self` isn't used, it's just taken so that the generic types are known.
191    ///
192    /// # Safety
193    ///
194    /// - The same preconditions as [`NodeRef::from_ptr_mut`] apply.
195    pub unsafe fn node_from_ptr<'a>(&self, ptr: *mut vlib_node_t) -> &'a mut NodeRef<N> {
196        // SAFETY: The safety requirements are documented in the function's safety comment.
197        unsafe { NodeRef::from_ptr_mut(ptr) }
198    }
199
200    /// Returns the name of the node as a pointer to a C string
201    ///
202    /// Note: no guarantees are made about the validity of the pointer or the string data.
203    pub const fn name_ptr(&self) -> *const std::os::raw::c_char {
204        // SAFETY: it is safe to access a const pointer to the name as it is not mutated after creation
205        unsafe { (*self.registration.get()).name }
206    }
207}
208
209bitflags! {
210    /// Node flags
211    #[repr(transparent)]
212    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
213    pub struct NodeFlags: u16 {
214        /// Don't free the frame after dispatch - processing function keeps the frame
215        const FRAME_NO_FREE_AFTER_DISPATCH = VLIB_NODE_FLAG_FRAME_NO_FREE_AFTER_DISPATCH as u16;
216        /// Internal node counts as output node for stats purposes
217        const IS_OUTPUT = VLIB_NODE_FLAG_IS_OUTPUT as u16;
218        /// Internal node counts as drop node for stats purposes
219        const IS_DROP = VLIB_NODE_FLAG_IS_DROP as u16;
220        /// Internal node counts as punt node for stats purposes
221        const IS_PUNT = VLIB_NODE_FLAG_IS_PUNT as u16;
222        /// Internal node counts as handoff node for stats purposes
223        const IS_HANDOFF = VLIB_NODE_FLAG_IS_HANDOFF as u16;
224        /// Current node runtime has traced vectors
225        const TRACE = VLIB_NODE_FLAG_TRACE as u16;
226        /// The node is in the process of switching from interrupt to polling mode
227        const SWITCH_FROM_INTERRUPT_TO_POLLING_MODE = VLIB_NODE_FLAG_SWITCH_FROM_INTERRUPT_TO_POLLING_MODE as u16;
228        /// The node is in the process of switching from polling to interrupt mode
229        const SWITCH_FROM_POLLING_TO_INTERRUPT_MODE = VLIB_NODE_FLAG_SWITCH_FROM_POLLING_TO_INTERRUPT_MODE as u16;
230        /// The node can initiate a per-node packet trace
231        const TRACE_SUPPORTED = VLIB_NODE_FLAG_TRACE_SUPPORTED as u16;
232        /// The node supports automatic switching between interrupt and polling mode
233        const ADAPTIVE_MODE =  VLIB_NODE_FLAG_ADAPTIVE_MODE as u16;
234        /// Allows the node registration to refer to a next node that doesn't exist at
235        /// registration time
236        const ALLOW_LAZY_NEXT_NODES =  VLIB_NODE_FLAG_ALLOW_LAZY_NEXT_NODES as u16;
237    }
238}
239
240// SAFETY: there is nothing in vlib_node_registration that is tied to a specific thread or that
241// mutates global state, so it's safe to send between threads.
242unsafe impl<N: Node, const N_NEXT_NODES: usize> Send for NodeRegistration<N, N_NEXT_NODES> {}
243// SAFETY: NodeRegistration doesn't allow any modification after creation (and vpp doesn't
244// modify it afterwards either), so it's safe to access from multiple threads. The only exception
245// to this is the register/unregister/register_node_fn methods, but it's the duty of the caller
246// to ensure they are called at times when no other threads have a reference to the object.
247unsafe impl<N: Node, const N_NEXT_NODES: usize> Sync for NodeRegistration<N, N_NEXT_NODES> {}
248
249/// Registration information for a VPP node
250///
251/// Used for registering and unregistering nodes with VPP.
252///
253/// This is typically created automatically using the [`vpp_plugin_macros::vlib_node`] macro.
254pub struct NodeRegistration<N: Node, const N_NEXT_NODES: usize> {
255    registration: UnsafeCell<_vlib_node_registration<[*mut std::os::raw::c_char; N_NEXT_NODES]>>,
256    _marker: std::marker::PhantomData<N>,
257}
258
259/// Reference to a VPP node runtime data
260///
261/// This is a per-node-instance, per-thread structure containing runtime data about the node.
262///
263/// A `&mut NodeRuntimeRef` corresponds to `vlib_node_runtime_t *` in C.
264#[repr(transparent)]
265pub struct NodeRuntimeRef<N: ?Sized>(foreign_types::Opaque, std::marker::PhantomData<N>);
266
267impl<N> NodeRuntimeRef<N> {
268    /// Creates a `&mut NodeRuntimeRef` directly from a pointer
269    ///
270    /// # Safety
271    /// - The pointer must be valid and a properly initialised `vlib_node_runtime_t`.
272    /// - The pointer must stay valid and the contents must not be mutated for the duration of the
273    ///   lifetime of the returned reference.
274    /// - The `runtime_data` field must be set correctly to point to a valid `RuntimeData` instance.
275    /// - The `node_index` field must be set correctly to point to a valid node in the VPP node main.
276    #[inline(always)]
277    pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_node_runtime_t) -> &'a mut Self {
278        // SAFETY: The safety requirements are documented in the function's safety comment.
279        unsafe { &mut *(ptr as *mut _) }
280    }
281
282    /// Returns the raw pointer to the underlying `vlib_node_runtime_t`
283    #[inline(always)]
284    pub fn as_ptr(&self) -> *mut vlib_node_runtime_t {
285        self as *const _ as *mut _
286    }
287
288    /// Node flags
289    #[inline(always)]
290    pub fn flags(&self) -> NodeFlags {
291        // SAFETY: we have a valid pointer to vlib_node_runtime_t
292        unsafe { NodeFlags::from_bits_truncate((*self.as_ptr()).flags) }
293    }
294
295    /// Return the node index VPP has assigned this node
296    pub fn node_index(&self) -> u32 {
297        // SAFETY: we have a valid pointer to vlib_node_runtime_t
298        unsafe { (*self.as_ptr()).node_index }
299    }
300
301    /// Returns the associated node reference
302    pub fn node(&self, vm: &MainRef) -> &NodeRef<N> {
303        // SAFETY: we have a valid pointer to vlib_node_runtime_t, and the node_index field is
304        // set correctly
305        unsafe {
306            let nodes = VecRef::from_raw_mut((*vm.as_ptr()).node_main.nodes);
307            NodeRef::from_ptr_mut(*nodes.get_unchecked(self.node_index() as usize) as *mut _)
308        }
309    }
310}
311
312impl<N: Node> NodeRuntimeRef<N> {
313    /// Returns the node-defined runtime data of the node
314    pub fn runtime_data(&self) -> &N::RuntimeData {
315        // SAFETY: we have a valid pointer to vlib_node_runtime_t, and the runtime_data field is
316        // set correctly
317        unsafe { &*((*self.as_ptr()).runtime_data.as_ptr() as *const N::RuntimeData) }
318    }
319
320    /// Returns the node-defined runtime data of the node as mutable
321    pub fn runtime_data_mut(&mut self) -> &mut N::RuntimeData {
322        // SAFETY: we have a valid pointer to vlib_node_runtime_t, and the runtime_data field is
323        // set correctly
324        unsafe { &mut *((*self.as_ptr()).runtime_data.as_ptr() as *mut N::RuntimeData) }
325    }
326
327    /// Increments the given error counter by the specified amount
328    ///
329    /// See also [`NodeRef::increment_error_counter`].
330    pub fn increment_error_counter(&self, vm: &MainRef, counter: N::Errors, increment: u64) {
331        self.node(vm)
332            .increment_error_counter(vm, counter, increment)
333    }
334}
335
336#[cfg(feature = "process-node")]
337impl<N: ProcessNode> NodeRuntimeRef<N> {
338    // Note runtime_data/runtime_data_mut not implemented as they have little benefit for process nodes
339
340    /// Increments the given error counter by the specified amount for process nodes
341    ///
342    /// See also [`NodeRef::increment_process_error_counter`].
343    pub fn increment_process_error_counter(
344        &self,
345        vm: &MainRef,
346        counter: N::Errors,
347        increment: u64,
348    ) {
349        self.node(vm)
350            .increment_process_error_counter(vm, counter, increment)
351    }
352}
353
354/// Reference to a VPP node frame
355///
356/// A `&mut FrameRef` corresponds to `vlib_frame_t *` in C.
357pub struct FrameRef<N: Node + ?Sized>(foreign_types::Opaque, std::marker::PhantomData<N>);
358
359impl<N: Node + ?Sized> FrameRef<N> {
360    /// Creates a `&mut FrameRef` directly from a pointer
361    ///
362    /// # Safety
363    /// - The pointer must be valid and a properly initialised `vlib_frame_t`.
364    /// - The pointer must stay valid and the contents must not be mutated for the duration of the
365    ///   lifetime of the returned object.
366    /// - `scalar_offset`, `vector_offset`, and `aux_offset` must be set correctly to point to
367    ///   the valid, initialised data areas in the frame, and `n_vectors` must be set correctly
368    ///   to indicate the number of valid vector and aux elements.
369    pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_frame_t) -> &'a mut Self {
370        // SAFETY: The safety requirements are documented in the function's safety comment.
371        unsafe { &mut *(ptr as *mut _) }
372    }
373
374    /// Returns the raw pointer to the underlying `vlib_frame_t`
375    pub fn as_ptr(&self) -> *mut vlib_frame_t {
376        self as *const _ as *mut _
377    }
378
379    /// Returns the vector data elements in the frame as a slice
380    pub fn vector(&self) -> &[N::Vector] {
381        // SAFETY: the frame's n_vectors field indicates the number of valid vector elements,
382        // vector_offset is set correctly when creating the frame, and we have a valid pointer to
383        // the frame
384        unsafe {
385            let vec = (self.as_ptr() as *const u8).add((*(self.as_ptr())).vector_offset as usize)
386                as *const N::Vector;
387            std::slice::from_raw_parts(vec, (*(self.as_ptr())).n_vectors as usize)
388        }
389    }
390
391    /// Returns the scalar data in the frame
392    pub fn scalar(&self) -> &N::Scalar {
393        // SAFETY: scalar_offset is set correctly when creating the frame, and we have a valid
394        // pointer to the frame
395        unsafe {
396            &*((self.as_ptr() as *const u8).add((*(self.as_ptr())).scalar_offset as usize)
397                as *const N::Scalar)
398        }
399    }
400
401    /// Returns the auxiliary data elements in the frame as a slice
402    ///
403    /// Note: the length of the returned slice is equal to the number of vectors in the frame.
404    pub fn aux(&self) -> &[N::Aux] {
405        // SAFETY: the frame's n_vectors field indicates the number of valid aux elements,
406        // aux_offset is set correctly when creating the frame, and we have a valid pointer to
407        // the frame
408        unsafe {
409            let aux = (self.as_ptr() as *const u8).add((*(self.as_ptr())).aux_offset as usize)
410                as *const N::Aux;
411            std::slice::from_raw_parts(aux, (*(self.as_ptr())).n_vectors as usize)
412        }
413    }
414}
415
416/// Trait for types that can be used as buffer indices in vector nodes
417pub trait VectorBufferIndex: Send + Copy {
418    /// Converts a slice of Self to a slice of u32
419    fn as_u32_slice(slice: &[Self]) -> &[u32];
420}
421
422impl<N, V> FrameRef<N>
423where
424    N: Node<Vector = V> + ?Sized,
425    V: VectorBufferIndex,
426{
427    /// Get pointers to buffers for the given buffer indices, writing them into the provided `to` arrayvec.
428    ///
429    /// This is similar to `vlib_get_buffers` in the C API.
430    ///
431    /// Note that although it would be more idiomatic to return an `ArrayVec` directly, this
432    /// method takes a mutable reference to an `ArrayVec` to avoid an unnecessary copy when
433    /// returning.
434    ///
435    /// # Safety
436    ///
437    /// - The caller must ensure that `to` has enough capacity to hold all the buffers
438    ///   corresponding to the indices in `from_indices`.
439    /// - Each buffer's `feature_arc_index` and `current_config_index` must be consistent with
440    ///   the `FeatureData` type. If they are not known (i.e. because the caller the node isn't
441    ///   being executed in a feature arc), FeatureData should be a zero-sized type such as `()`.
442    /// - Must not be called more than once without the framing being flushed in between as
443    ///   Rust's reference aliasing rules will be violated.
444    #[inline(always)]
445    pub unsafe fn get_buffers<'me, 'vm, 'buf: 'vm + 'me, const ARRAY_N: usize>(
446        &'me self,
447        vm: &'vm MainRef,
448        to: &mut ArrayVec<&'buf mut BufferRef<N::FeatureData>, ARRAY_N>,
449    ) -> &'me [N::Vector] {
450        // SAFETY: The safety requirements are documented in the function's safety comment.
451        unsafe {
452            let from = self.vector();
453            vm.get_buffers(N::Vector::as_u32_slice(from), to);
454            from
455        }
456    }
457}
458
459/// Reference to a VPP node
460///
461/// This is a per-node-instance structure containing metadata about the node and certain node
462/// state.
463///
464/// A `&mut NodeRef` corresponds to `vlib_node_t *` in C.
465#[repr(transparent)]
466pub struct NodeRef<N>(foreign_types::Opaque, std::marker::PhantomData<N>);
467
468impl<N> NodeRef<N> {
469    /// Creates a `&mut NodeRef` directly from a pointer
470    ///
471    /// # Safety
472    ///
473    /// - The pointer must be valid and a properly initialised `vlib_node_t`.
474    /// - The pointer must stay valid and the contents must not be mutated for the duration of the
475    ///   lifetime of the returned object.
476    /// - The `error_heap_index` field must be set correctly to point to the base index of the
477    ///   node's error counters in the VPP error main counters array.
478    pub unsafe fn from_ptr_mut<'a>(ptr: *mut vlib_node_t) -> &'a mut Self {
479        // SAFETY: The safety requirements are documented in the function's safety comment.
480        unsafe { &mut *(ptr as *mut _) }
481    }
482
483    /// Returns the raw pointer to the underlying `vlib_node_t`
484    pub fn as_ptr(&self) -> *mut vlib_node_t {
485        self as *const _ as *mut _
486    }
487}
488
489impl<N: Node> NodeRef<N> {
490    /// Increments the given error counter by the specified amount
491    ///
492    /// This corresponds to the VPP C function `vlib_node_increment_counter`.
493    pub fn increment_error_counter(&self, vm: &MainRef, counter: N::Errors, increment: u64) {
494        // SAFETY: we have a valid pointer to vlib_node_t, the error_heap_index field is
495        // set correctly, we are the only writer to counters (because it's per-thread),
496        // and we perform an atomic store to the counter so that concurrent readers cannot see
497        // a partial value.
498        unsafe {
499            let em = &(*vm.as_ptr()).error_main;
500            let node_counter_base_index = (*self.as_ptr()).error_heap_index;
501            let ptr = em
502                .counters
503                .add(node_counter_base_index as usize + counter.into_u16() as usize);
504            AtomicU64::from_ptr(ptr).store(*ptr + increment, std::sync::atomic::Ordering::Relaxed);
505        }
506    }
507}
508
509#[cfg(feature = "process-node")]
510impl<N: ProcessNode> NodeRef<N> {
511    /// Increments the given error counter by the specified amount for process nodes
512    ///
513    /// This corresponds to the VPP C function `vlib_node_increment_counter`.
514    pub fn increment_process_error_counter(
515        &self,
516        vm: &MainRef,
517        counter: N::Errors,
518        increment: u64,
519    ) {
520        // SAFETY: we have a valid pointer to vlib_node_t, the error_heap_index field is
521        // set correctly, we are the only writer to counters (because it's per-thread),
522        // and we perform an atomic store to the counter so that concurrent readers cannot see
523        // a partial value.
524        unsafe {
525            let em = &(*vm.as_ptr()).error_main;
526            let node_counter_base_index = (*self.as_ptr()).error_heap_index;
527            let ptr = em
528                .counters
529                .add(node_counter_base_index as usize + counter.into_u16() as usize);
530            AtomicU64::from_ptr(ptr).store(*ptr + increment, std::sync::atomic::Ordering::Relaxed);
531        }
532    }
533}
534
535/// Trait for defining a VPP node
536pub trait Node {
537    /// Type of vector data sent to the node
538    type Vector;
539    /// Type of scalar data sent to the node
540    ///
541    /// The scalar data is shared between all vector elements in a frame.
542    ///
543    /// This is rarely used and can be set to `()` if not needed.
544    type Scalar;
545    /// Type of auxiliary data sent to the node
546    ///
547    /// The auxiliary data is per-vector.
548    ///
549    /// This is rarely used and can be set to `()` if not needed.
550    type Aux;
551
552    /// Type defining the next nodes of this node
553    ///
554    /// Typically an enum using the [`vpp_plugin_macros::NextNodes`] derive macro.
555    type NextNodes: NextNodes;
556    /// Type defining the runtime data of this node
557    ///
558    /// This data is per-node instance and per-thread.
559    // Send + Copy due to:
560    //     if (vec_len (n->runtime_data) > 0)
561    //       clib_memcpy (rt->runtime_data, n->runtime_data,
562    //                    vec_len (n->runtime_data));
563    //     else
564    //       clib_memset (rt->runtime_data, 0, VLIB_NODE_RUNTIME_DATA_SIZE);
565    type RuntimeData: Send + Copy;
566    /// Type defining the trace data of this node
567    // Send due to display from main thread, writing from worker threads.
568    // Copy because no destructor will be called on TraceData (since it's discard inside VPP
569    // code), and Copy is mutually exclusive with implementing the Drop trait. This isn't a
570    // soundness requirement (since Rust doesn't include not leaking memory in its definition
571    // of soundless), so this constraint could be dropped be relaxed if it turns out to be too
572    // much of a burden.
573    type TraceData: Send + Copy;
574    /// Type defining the error counters of this node
575    ///
576    /// Typically an enum using the [`vpp_plugin_macros::ErrorCounters`] derive macro.
577    type Errors: ErrorCounters;
578    /// Type defining the feature data of this node
579    ///
580    /// This is available when the node is used as a feature node invoked from a feature arc.
581    ///
582    /// If the node is not used as a feature node, this type is not used and so can be set to `()`.
583    // Send due to setting on main thread and retrieval on worker threads
584    type FeatureData: Send;
585
586    /// The packet processing function of the node
587    ///
588    /// Returns the number of packets processed from the frame.
589    ///
590    /// # Safety
591    /// - The caller must ensure that precondition assumptions for the state of the buffers
592    ///   in the frame are met, e.g. that the packets are valid and have the expected headers.
593    ///   For example, if the node is expected to be invoked during the ip4-input feature arc, the
594    ///   caller must ensure that all packets in the frame are valid IPv4 packets and the current
595    ///   data offset is pointing to the start of the IPv4 header. In addition, any assumptions
596    ///   about how much of the packet has been linearised must also be upheld.
597    /// - The node's precondition assumptions may also inherit from those of next nodes the node
598    ///   sends the buffers to.
599    unsafe fn function(
600        &self,
601        vm: &mut MainRef,
602        node: &mut NodeRuntimeRef<Self>,
603        frame: &mut FrameRef<Self>,
604    ) -> u16;
605}
606
607#[cfg(test)]
608mod tests {
609    use crate::{
610        bindings::{vlib_error_desc_t, vlib_node_registration_t},
611        vlib::{self, node::NodeRegistration},
612    };
613
614    enum Errors {}
615    // SAFETY: C_DESCRIPTIONS is a valid array that matches number of discriminants in Errors enum
616    unsafe impl vlib::node::ErrorCounters for Errors {
617        type CDescriptionsArray = [vlib_error_desc_t; 0];
618
619        const C_DESCRIPTIONS: Self::CDescriptionsArray = [];
620
621        fn into_u16(self) -> u16 {
622            todo!()
623        }
624    }
625
626    #[derive(Copy, Clone)]
627    enum NextNodes {
628        _Drop,
629    }
630
631    // SAFETY: C_NAMES is a valid array that matches number of discriminants in NextNodes enum
632    unsafe impl vlib::node::NextNodes for NextNodes {
633        type CNamesArray = [*mut ::std::os::raw::c_char; 1];
634
635        const C_NAMES: Self::CNamesArray = [c"drop".as_ptr().cast_mut()];
636
637        fn into_u16(self) -> u16 {
638            self as u16
639        }
640    }
641
642    struct Node;
643    impl vlib::node::Node for Node {
644        type Vector = vlib::BufferIndex;
645        type Scalar = ();
646        type Aux = ();
647
648        type NextNodes = NextNodes;
649        type RuntimeData = ();
650        type TraceData = ();
651        type Errors = Errors;
652        type FeatureData = ();
653
654        unsafe fn function(
655            &self,
656            _vm: &mut vlib::MainRef,
657            _node: &mut vlib::NodeRuntimeRef<Self>,
658            _frame: &mut vlib::FrameRef<Self>,
659        ) -> u16 {
660            unreachable!()
661        }
662    }
663
664    #[test]
665    fn test_node_reg() {
666        // NodeRegistration::new is a const function and Rust doesn't generate coverage data
667        // when such functions are evaluated at compile time
668        // (https://github.com/rust-lang/rust/issues/124732), so force it to be evaluated at
669        // runtime.
670        let node: NodeRegistration<Node, 0> =
671            std::hint::black_box(NodeRegistration::new(vlib_node_registration_t::default()));
672
673        let node = Box::new(node);
674        // Get a raw pointer so we can ignore the static lifetime requirement of register
675        let node = Box::into_raw(node);
676
677        // SAFETY: preconditions of register/unregister don't have to be met because we are
678        // calling this outside of the VPP application, meaning that pointers in the
679        // vlib_node_registration_t won't be dereferenced.
680        unsafe {
681            (*node).register();
682            (*node).unregister();
683
684            let _ = Box::from_raw(node);
685        }
686    }
687}