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