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