Skip to main content

kernel/net/
netlink.rs

1// SPDX-License-Identifier: GPL-2.0
2
3// Copyright (C) 2026 Google LLC.
4
5//! Rust support for generic netlink.
6//!
7//! Currently only supports exposing multicast groups.
8//!
9//! C header: [`include/net/genetlink.h`](srctree/include/net/genetlink.h)
10
11use kernel::{
12    alloc::{self, AllocError},
13    error::to_result,
14    prelude::*,
15    types::Opaque,
16    ThisModule,
17};
18
19use core::{
20    mem::ManuallyDrop,
21    ptr::NonNull, //
22};
23
24use zerocopy::{
25    Immutable,
26    IntoBytes, //
27};
28
29/// The default netlink message size.
30pub const GENLMSG_DEFAULT_SIZE: usize = bindings::GENLMSG_DEFAULT_SIZE;
31
32/// A wrapper around `struct sk_buff` for generic netlink messages.
33///
34/// This type is intended to be specific for buffers used with netlink only, and other usecases for
35/// `struct sk_buff` are out-of-scope for this abstraction.
36///
37/// # Invariants
38///
39/// The pointer has ownership over a valid `sk_buff`.
40pub struct NetlinkSkBuff {
41    skb: NonNull<kernel::bindings::sk_buff>,
42}
43
44impl NetlinkSkBuff {
45    /// Creates a new `NetlinkSkBuff` with the given size.
46    pub fn new(size: usize, flags: alloc::Flags) -> Result<NetlinkSkBuff, AllocError> {
47        // SAFETY: `genlmsg_new` only requires its arguments to be valid integers.
48        let skb = unsafe { bindings::genlmsg_new(size, flags.as_raw()) };
49        let skb = NonNull::new(skb).ok_or(AllocError)?;
50        Ok(NetlinkSkBuff { skb })
51    }
52
53    /// Puts a generic netlink header into the `NetlinkSkBuff`.
54    pub fn genlmsg_put(
55        self,
56        portid: u32,
57        seq: u32,
58        family: &'static Family,
59        cmd: u8,
60    ) -> Result<GenlMsg, AllocError> {
61        let skb = self.skb.as_ptr();
62        // SAFETY: The skb and family pointers are valid.
63        let hdr = unsafe { bindings::genlmsg_put(skb, portid, seq, family.as_raw(), 0, cmd) };
64        let hdr = NonNull::new(hdr).ok_or(AllocError)?;
65        Ok(GenlMsg { skb: self, hdr })
66    }
67}
68
69impl Drop for NetlinkSkBuff {
70    fn drop(&mut self) {
71        // SAFETY: We have ownership over the `sk_buff`, so we may free it.
72        unsafe { bindings::nlmsg_free(self.skb.as_ptr()) }
73    }
74}
75
76/// A generic netlink message being constructed.
77///
78/// # Invariants
79///
80/// `hdr` references the header in this netlink message.
81pub struct GenlMsg {
82    skb: NetlinkSkBuff,
83    hdr: NonNull<c_void>,
84}
85
86impl GenlMsg {
87    /// Puts an attribute into the message.
88    #[inline]
89    fn put<T>(&mut self, attrtype: c_int, value: &T) -> Result
90    where
91        T: ?Sized + IntoBytes + Immutable,
92    {
93        let skb = self.skb.skb.as_ptr();
94        let len = size_of_val(value);
95        let ptr = core::ptr::from_ref(value).cast::<c_void>();
96        // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and the provided value is
97        // readable and initialized for its `size_of` bytes.
98        to_result(unsafe { bindings::nla_put(skb, attrtype, len as c_int, ptr) })
99    }
100
101    /// Puts a `u32` attribute into the message.
102    #[inline]
103    pub fn put_u32(&mut self, attrtype: c_int, value: u32) -> Result {
104        self.put(attrtype, &value)
105    }
106
107    /// Puts a string attribute into the message.
108    #[inline]
109    pub fn put_string(&mut self, attrtype: c_int, value: &CStr) -> Result {
110        self.put(attrtype, value.to_bytes_with_nul())
111    }
112
113    /// Puts a flag attribute into the message.
114    #[inline]
115    pub fn put_flag(&mut self, attrtype: c_int) -> Result {
116        let skb = self.skb.skb.as_ptr();
117        // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and a null pointer is valid
118        // when the length is zero.
119        to_result(unsafe { bindings::nla_put(skb, attrtype, 0, core::ptr::null()) })
120    }
121
122    /// Sends the generic netlink message as a multicast message.
123    #[inline]
124    pub fn multicast(
125        self,
126        family: &'static Family,
127        portid: u32,
128        group: u32,
129        flags: alloc::Flags,
130    ) -> Result {
131        let me = ManuallyDrop::new(self);
132        // SAFETY: The `skb` and `family` pointers are valid. We pass ownership of the `skb` to
133        // `genlmsg_multicast` by not dropping `self`.
134        unsafe {
135            bindings::genlmsg_end(me.skb.skb.as_ptr(), me.hdr.as_ptr());
136            to_result(bindings::genlmsg_multicast(
137                family.as_raw(),
138                me.skb.skb.as_ptr(),
139                portid,
140                group,
141                flags.as_raw(),
142            ))
143        }
144    }
145}
146impl Drop for GenlMsg {
147    fn drop(&mut self) {
148        // SAFETY: The `hdr` pointer references the header of this generic netlink message.
149        unsafe { bindings::genlmsg_cancel(self.skb.skb.as_ptr(), self.hdr.as_ptr()) };
150    }
151}
152
153/// Flags for a generic netlink family.
154struct FamilyFlags {
155    /// Whether the family supports network namespaces.
156    netnsok: bool,
157    /// Whether the family supports parallel operations.
158    parallel_ops: bool,
159}
160
161impl FamilyFlags {
162    /// Converts the flags to the bitfield representation used by `genl_family`.
163    const fn into_bitfield(self) -> bindings::__BindgenBitfieldUnit<[u8; 1]> {
164        // The below shifts are verified correct by test_family_flags_bitfield() below.
165        //
166        // Although bindgen generates helpers to change bitfields based on the C headers, these
167        // helpers unfortunately can't be used in const context. Since `Family` needs to be filled
168        // out at build-time, we use this helper instead.
169        let mut bits = 0;
170        if self.netnsok {
171            bits |= 1 << 0;
172        }
173        if self.parallel_ops {
174            bits |= 1 << 1;
175        }
176        // Convert from little endian to the target's endianness.
177        bits = u8::from_le(bits);
178        // SAFETY: This bitfield is represented as an u8.
179        unsafe { core::mem::transmute::<u8, bindings::__BindgenBitfieldUnit<[u8; 1]>>(bits) }
180    }
181}
182
183/// A generic netlink family.
184#[repr(transparent)]
185pub struct Family {
186    inner: Opaque<bindings::genl_family>,
187}
188
189// SAFETY: The `Family` type is thread safe.
190unsafe impl Sync for Family {}
191
192impl Family {
193    /// Creates a new `Family` instance.
194    ///
195    /// Intended to be used from const context only. Will panic if provided with invalid arguments.
196    ///
197    /// The name must be a nul-terminated string, but it is taken as `&[u8]` so that it can be used
198    /// more conveniently with the strings generated by bindgen.
199    pub const fn const_new(
200        module: &ThisModule,
201        name: &[u8],
202        version: u32,
203        mcgrps: &'static [MulticastGroup],
204    ) -> Family {
205        let n_mcgrps = mcgrps.len() as u8;
206        if n_mcgrps as usize != mcgrps.len() {
207            panic!("too many mcgrps");
208        }
209        let mut genl_family = bindings::genl_family {
210            version,
211            _bitfield_1: FamilyFlags {
212                netnsok: true,
213                parallel_ops: true,
214            }
215            .into_bitfield(),
216            module: module.as_ptr(),
217            mcgrps: mcgrps.as_ptr().cast(),
218            n_mcgrps,
219            ..pin_init::zeroed()
220        };
221        if CStr::from_bytes_with_nul(name).is_err() {
222            panic!("genl_family name not nul-terminated");
223        }
224        if genl_family.name.len() < name.len() {
225            panic!("genl_family name too long");
226        }
227        let mut i = 0;
228        while i < name.len() {
229            genl_family.name[i] = name[i];
230            i += 1;
231        }
232        Family {
233            inner: Opaque::new(genl_family),
234        }
235    }
236
237    /// Checks if there are any listeners for the given multicast group.
238    pub fn has_listeners(&self, group: u32) -> bool {
239        // SAFETY: The family and init_net pointers are valid.
240        unsafe {
241            bindings::genl_has_listeners(self.as_raw(), &raw mut bindings::init_net, group) != 0
242        }
243    }
244
245    /// Returns a raw pointer to the underlying `genl_family` structure.
246    pub fn as_raw(&self) -> *mut bindings::genl_family {
247        self.inner.get()
248    }
249}
250
251/// A generic netlink multicast group.
252#[repr(transparent)]
253pub struct MulticastGroup {
254    // No Opaque because fully immutable
255    group: bindings::genl_multicast_group,
256}
257
258// SAFETY: Pure data so thread safe.
259unsafe impl Sync for MulticastGroup {}
260
261impl MulticastGroup {
262    /// Creates a new `MulticastGroup` instance.
263    ///
264    /// Intended to be used from const context only. Will panic if provided with invalid arguments.
265    pub const fn const_new(name: &CStr) -> MulticastGroup {
266        let mut group: bindings::genl_multicast_group = pin_init::zeroed();
267
268        let name = name.to_bytes_with_nul();
269        if group.name.len() < name.len() {
270            panic!("genl_multicast_group name too long");
271        }
272        let mut i = 0;
273        while i < name.len() {
274            group.name[i] = name[i];
275            i += 1;
276        }
277
278        MulticastGroup { group }
279    }
280}
281
282/// A registration of a generic netlink family.
283///
284/// This type represents the registration of a [`Family`]. When an instance of this type is
285/// dropped, its respective generic netlink family will be unregistered from the system.
286///
287/// # Invariants
288///
289/// `self.family` always holds a valid reference to an initialized and registered [`Family`].
290pub struct Registration {
291    family: &'static Family,
292}
293
294impl Family {
295    /// Registers the generic netlink family with the kernel.
296    pub fn register(&'static self) -> Result<Registration> {
297        // SAFETY: `self.as_raw()` is a valid pointer to a `genl_family` struct.
298        // The `genl_family` struct is static, so it will outlive the registration.
299        to_result(unsafe { bindings::genl_register_family(self.as_raw()) })?;
300        Ok(Registration { family: self })
301    }
302}
303
304impl Drop for Registration {
305    fn drop(&mut self) {
306        // SAFETY: `self.family.as_raw()` is a valid pointer to a registered `genl_family` struct.
307        // The `Registration` struct ensures that `genl_unregister_family` is called exactly once
308        // for this family when it goes out of scope.
309        unsafe { bindings::genl_unregister_family(self.family.as_raw()) };
310    }
311}
312
313#[macros::kunit_tests(rust_netlink)]
314mod tests {
315    use super::*;
316
317    #[test]
318    fn test_family_flags_bitfield() {
319        for netnsok in [false, true] {
320            for parallel_ops in [false, true] {
321                let mut b_fam = bindings::genl_family {
322                    ..Default::default()
323                };
324                b_fam.set_netnsok(if netnsok { 1 } else { 0 });
325                b_fam.set_parallel_ops(if parallel_ops { 1 } else { 0 });
326
327                let c_bitfield = FamilyFlags {
328                    netnsok,
329                    parallel_ops,
330                }
331                .into_bitfield();
332
333                // SAFETY: The bit field is stored as u8.
334                let b_val: u8 = unsafe { core::mem::transmute(b_fam._bitfield_1) };
335                // SAFETY: The bit field is stored as u8.
336                let c_val: u8 = unsafe { core::mem::transmute(c_bitfield) };
337                assert_eq!(b_val, c_val);
338            }
339        }
340    }
341}