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