vpp_plugin/vlibapi/mod.rs
1//! VPP API library
2//!
3//! Traits, types and helpers for working with API messages and client registrations.
4
5use std::{
6 marker::PhantomData,
7 mem::{self, MaybeUninit},
8 ops::{Deref, DerefMut},
9 ptr::{self, NonNull},
10};
11
12use crate::{
13 bindings::{
14 vl_api_helper_client_index_to_registration, vl_api_helper_send_msg, vl_api_registration_t,
15 vl_msg_api_alloc, vl_msg_api_free,
16 },
17 vlib::BarrierHeldMainRef,
18};
19
20/// An owned VPP message buffer containing a `T`.
21///
22/// The message can be sent to a client using [`Registration::send_message`].
23///
24/// Important invariant:
25///
26/// - `T` must have an alignment of 1 (e.g. by `#[repr(packed)]`)
27pub struct Message<T> {
28 pointer: NonNull<T>,
29}
30
31impl<T> Message<T> {
32 /// Allocate a VPP message and initialise it by copying `value` into the
33 /// newly-allocated buffer.
34 ///
35 /// # Panics
36 ///
37 /// Panics if `align_of::<T>() != 1` because the VPP API message allocator does not provide
38 /// alignment guarantees; generated message structs are expected to be packed.
39 pub fn new(value: T) -> Self {
40 if std::mem::align_of::<T>() != 1 {
41 // It's unclear what alignment guarantees vpp gives. Possibly the memory is aligned
42 // to align_of(msghdr_t), but play it safe and required packed types -
43 // vpp-plugin-api-gen generated types always have this anyway.
44 panic!("Messages must only contain #[repr(packed)] types");
45 }
46
47 // SAFETY: `vl_msg_api_alloc` returns a pointer to at least `size_of::<T>()` bytes (or
48 // null on allocation failure). We have asserted `align_of::<T>() == 1` so the cast to
49 // `*mut T` is valid. It is safe to use `NonNull::new_unchecked` because the VPP
50 // allocation cannot fail and instead aborts on allocation failures.
51 unsafe {
52 let mut me = Self {
53 pointer: NonNull::new_unchecked(
54 vl_msg_api_alloc(std::mem::size_of::<T>() as i32) as *mut T
55 ),
56 };
57 ptr::copy_nonoverlapping(&value, me.pointer.as_mut(), 1);
58 me
59 }
60 }
61
62 /// Allocate an uninitialised VPP message buffer for `T`.
63 ///
64 /// This returns a `Message<MaybeUninit<T>>`. Use [`Self::write`] or [`Self::assume_init`]
65 /// after manually initialising the contents.
66 ///
67 ///
68 /// # Panics
69 ///
70 /// Panics if `align_of::<T>() != 1` for the same reason as `new`.
71 pub fn new_uninit() -> Message<MaybeUninit<T>> {
72 if std::mem::align_of::<T>() != 1 {
73 // It's unclear what alignment guarantees vpp gives. Possibly the memory is aligned
74 // to align_of(msghdr_t), but play it safe and required packed types -
75 // vpp-plugin-api-gen generated types always have this anyway.
76 panic!("Messages must only contain #[repr(packed)] types");
77 }
78
79 // SAFETY: `vl_msg_api_alloc` returns a pointer to at least `size_of::<MaybeUninit<T>>()`
80 // bytes. Casting that pointer to `*mut MaybeUninit<T>` is valid as the buffer is
81 // uninitialised but suitably sized. It is safe to use `NonNull::new_unchecked` because
82 // the VPP allocation cannot fail and instead aborts on allocation failures.
83 unsafe {
84 Message {
85 pointer: NonNull::new_unchecked(vl_msg_api_alloc(
86 std::mem::size_of::<MaybeUninit<T>>() as i32,
87 ) as *mut MaybeUninit<T>),
88 }
89 }
90 }
91
92 /// Consume the `Message` and return the raw pointer to the underlying buffer
93 ///
94 /// The returned pointer becomes the caller's responsibility. The `Message` destructor will
95 /// not run for `m` and the underlying buffer will not be freed by Rust; callers must ensure
96 /// the buffer is eventually freed (for example by passing it to VPP or calling
97 /// `vl_msg_api_free`).
98 ///
99 /// Not a method on `Message` to avoid clashing with application methods of the same name on
100 /// the underlying type.
101 pub fn into_raw(m: Self) -> *mut T {
102 let m = mem::ManuallyDrop::new(m);
103 m.pointer.as_ptr()
104 }
105}
106
107impl<T> Message<MaybeUninit<T>> {
108 /// Convert a `Message<MaybeUninit<T>>` into a `Message<T>` without performing any
109 /// initialisation checks
110 ///
111 /// # Safety
112 ///
113 /// The caller must ensure that the underlying buffer is fully initialised for `T`. If the
114 /// memory is not properly initialised, using the resulting `Message<T>` is undefined
115 /// behaviour.
116 pub unsafe fn assume_init(self) -> Message<T> {
117 let pointer = Message::into_raw(self);
118 Message {
119 pointer: NonNull::new_unchecked(pointer as *mut T),
120 }
121 }
122
123 /// Initialise the previously-uninitialised buffer with `value` and return the initialised
124 /// `Message<T>`
125 pub fn write(mut self, value: T) -> Message<T> {
126 // SAFETY: We have exclusive ownership of the allocated buffer for
127 // `self`. Writing `value` into the `MaybeUninit<T>` buffer
128 // initialises it, after which `assume_init` converts the message to
129 // `Message<T>`.
130 unsafe {
131 (*self).write(value);
132 self.assume_init()
133 }
134 }
135}
136
137impl<T> Deref for Message<T> {
138 type Target = T;
139
140 fn deref(&self) -> &Self::Target {
141 // SAFETY: `self.pointer` was allocated by `vl_msg_api_alloc` and points to a valid,
142 // initialised `T` for the lifetime of `&self`.
143 unsafe { self.pointer.as_ref() }
144 }
145}
146
147impl<T> DerefMut for Message<T> {
148 fn deref_mut(&mut self) -> &mut Self::Target {
149 // SAFETY: `self.pointer` was allocated by `vl_msg_api_alloc` and we hold exclusive access
150 // via `&mut self`, so returning a mutable reference to the inner `T` is valid.
151 unsafe { self.pointer.as_mut() }
152 }
153}
154
155impl<T: Default> Default for Message<T> {
156 fn default() -> Self {
157 Self::new_uninit().write(Default::default())
158 }
159}
160
161impl<T> Drop for Message<T> {
162 fn drop(&mut self) {
163 // SAFETY: We own the underlying buffer and the memory is considered initialised for `T`
164 // at time of drop. It's therefore safe to drop the contained `T` and free the buffer
165 // with the VPP message API free function.
166 unsafe {
167 ptr::drop_in_place(self.pointer.as_ptr());
168 vl_msg_api_free(self.pointer.as_ptr().cast());
169 }
170 }
171}
172
173impl<T> From<T> for Message<T> {
174 fn from(value: T) -> Self {
175 Self::new(value)
176 }
177}
178
179/// Trait used by generated message types that require endian conversions.
180///
181/// Implementations should swap fields between host and network byte order.
182pub trait EndianSwap {
183 /// Swap the endianness of the message in-place.
184 ///
185 /// `to_net == true` indicates conversion from host to network order.
186 fn endian_swap(&mut self, to_net: bool);
187}
188
189/// Registration state for the VPP side of an API client
190///
191/// A `&mut Registration` corresponds to a C `vl_api_registration *`.
192///
193/// Use [`RegistrationScope::from_client_index`] to obtain a mutable reference.
194#[repr(transparent)]
195pub struct Registration(foreign_types::Opaque);
196
197impl Registration {
198 /// Construct a `&mut Registration` from a raw `vl_api_registration_t` pointer.
199 ///
200 /// # Safety
201 ///
202 /// - `ptr` must be a valid, non-null pointer to a `vl_api_registration_t`.
203 /// - The caller must ensure exclusive mutable access for the returned lifetime `'a` (no other
204 /// references or concurrent uses may alias the same underlying registration for the
205 /// duration of the returned borrow).
206 /// - The pointer must remain valid for the returned lifetime and must not be freed or
207 /// invalidated while the borrow is active.
208 pub unsafe fn from_ptr_mut<'a>(ptr: *mut vl_api_registration_t) -> &'a mut Self {
209 &mut *(ptr as *mut _)
210 }
211
212 /// Return the raw `vl_api_registration_t` pointer for this `Registration`.
213 pub fn as_ptr(&self) -> *mut vl_api_registration_t {
214 self as *const _ as *mut _
215 }
216
217 /// Send a message to the registration.
218 ///
219 /// This consumes `message` and transfers ownership of the underlying buffer to VPP.
220 pub fn send_message<T>(&mut self, message: Message<T>) {
221 // SAFETY: `self.as_ptr()` returns a raw `vl_api_registration_t` pointer that is valid
222 // for the duration of this call. `Message::into_raw` transfers ownership of the message
223 // buffer and yields a pointer that is safe to pass to the C API; the C API takes
224 // ownership of the buffer. `vl_api_helper_send_msg` is called with valid pointers.
225 unsafe {
226 vl_api_helper_send_msg(self.as_ptr(), Message::into_raw(message).cast());
227 }
228 }
229}
230
231/// Scope helper used to obtain short-lived `&mut Registration` borrows.
232///
233/// This enforces that `Registration` references obtained cannot be retained beyond the
234/// `registration_scope` function call
235pub struct RegistrationScope<'scope>(PhantomData<&'scope ()>);
236
237impl<'scope> RegistrationScope<'scope> {
238 /// Look up a `Registration` by VPP client index.
239 ///
240 /// Returns `Some(&mut Registration)` when the client index corresponds to a current
241 /// registration, or `None` if no registration exists for that index.
242 // FIXME: change &mut to & since we cannot prevent aliases here
243 pub fn from_client_index(
244 &self,
245 _vm: &BarrierHeldMainRef,
246 client_index: u32,
247 ) -> Option<&'scope mut Registration> {
248 // SAFETY: `vl_api_helper_client_index_to_registration` returns either a null pointer or
249 // a valid pointer to a `vl_api_registration_t` that lives as long as the corresponding
250 // client registration in VPP. The lifetime of the returned reference ensures the caller
251 // cannot retain that reference beyond the intended scope.
252 unsafe {
253 let ptr = vl_api_helper_client_index_to_registration(client_index.to_be());
254 if ptr.is_null() {
255 None
256 } else {
257 Some(Registration::from_ptr_mut(ptr))
258 }
259 }
260 }
261}
262
263/// Execute a closure with a temporary `RegistrationScope`.
264///
265/// Used to ensure any `&mut Registration` borrows that are obtained are tied to the lifetime of
266/// the closure and cannot accidentally escape.
267pub fn registration_scope<F, T>(f: F) -> T
268where
269 F: for<'scope> FnOnce(&'scope RegistrationScope<'scope>) -> T,
270{
271 let scope = RegistrationScope(PhantomData);
272 f(&scope)
273}