From nobody Thu Apr 9 17:20:15 2026 Received: from mail-wm1-f73.google.com (mail-wm1-f73.google.com [209.85.128.73]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D98B13B5301 for ; Fri, 6 Mar 2026 15:12:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.73 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772809951; cv=none; b=SUHOUiLR5YnlOkTlJsbcQ5G39sLYlRPdQIb/8kC5hEXnWvmpCAzr9kn+Jvk2J8MSivxrcidFemBsqNVkp/eBQY8d652QS8L3Tx8eHIwlew3vGVOQrPKfa6mPiSOs4XVHpiTMZSy9v4zlprhDjTo3NTjbcSMiiXj14WIqzKonr68= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772809951; c=relaxed/simple; bh=2gg0IMeCOonNFxAMRE6I6j2GzytDKOh8FHyYzEqMsFw=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=QYnach9fow5hp6uDXhzhd7qJFDtderiy9xRapzeVmF1Bxn0HjdwMoOlyrYFGRNQYVhpv0w3mSis+EIWnSNCY/tjtlkU0tXwUQ3Sx+dQqSGeecpPggb1Dkb4f8o9WCH6jqScm+VPbPhNzOLtVo5WPj8P24+xJmezj8PiSxEMIxrU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=JGiNMEbp; arc=none smtp.client-ip=209.85.128.73 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="JGiNMEbp" Received: by mail-wm1-f73.google.com with SMTP id 5b1f17b1804b1-4832c4621c2so100986705e9.3 for ; Fri, 06 Mar 2026 07:12:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772809944; x=1773414744; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=550E8GK4gdkLJqbKYXMCXLAWMQ96P9Gx+7hniyLd5m8=; b=JGiNMEbpsFE3ZnjMJJ8xFR2tEIwu6g3TEP9P4DUaCXj+5riK/haDzNEGLolUj2nyd4 cWczkN3iey3kI0P7b+K0OunEZQFppjqR4qLrVQOsku059MfZPlmEuWh1xylMONXCrU2W +8ZreFDaBLQs4Pb1dwEqaThfm+iShFz2bdjlx8O/EPsU39uAcy0pBmIS5lj+ZjfuTLbz D/YRngsDEMjBAmRSPcOaWO8JPoraIgQYf3p7UOo/Yw2bS4bkxOImiayg3Mn3F9tvVM18 XldokK/1XjVSK5FOIfnHwkPKLnXUAMslZverASENs5xuO5i6WJ9TOnJGv7OUek7uF/H4 4KZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772809944; x=1773414744; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=550E8GK4gdkLJqbKYXMCXLAWMQ96P9Gx+7hniyLd5m8=; b=nntj9SJhwWKe+0F+3ZSBNvNgkLmi4CHGKn8FEadgX6DjxtkMFZYChc9AyaZtJtWhTt zYichK5Dm4wEefgCwdr0xtwrbRBLaFtkuaMWZL4RRxoGlanyJQU9GaAiVRzjNEdVnQR4 O80CGs8xA6kPctzh60u/fW4M1MJ+iP5lfd0/msXEXZXg9+mPNzhyOotG5c79Hb8dRCgn qDk5U9ZJugUb6WWSXAscpvmmmLC/phHw/oAkJZK2+wmHEz9CqnEduCvs8fD3wexzpCYI tP0ia/XeHXc2QXxrlJHwJGM0pV+eKoqOmUS4ei8/sqaoy0y36DQPl3YJUjQOK5487Nq4 2ujA== X-Gm-Message-State: AOJu0YwcFoaTONk9M+cUghH5rRINnPHrMTtu3taaSrChkDZiwV/ZQnwq 7NHePWy8ceH4ilqwdc6lUswMw5GV2TDpvYIG0TM7BVL0b2Ctg1r1veJulMWBRBsn6fc/QGr1XmT n8xjNVhRv8YU219q7SA== X-Received: from wrsw18.prod.google.com ([2002:a5d:4b52:0:b0:437:7338:c338]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:500d:b0:483:709e:f238 with SMTP id 5b1f17b1804b1-48526968008mr38754015e9.29.1772809943979; Fri, 06 Mar 2026 07:12:23 -0800 (PST) Date: Fri, 06 Mar 2026 15:12:13 +0000 In-Reply-To: <20260306-binder-netlink-v1-0-daceb5bc83f2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260306-binder-netlink-v1-0-daceb5bc83f2@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=12905; i=aliceryhl@google.com; h=from:subject:message-id; bh=2gg0IMeCOonNFxAMRE6I6j2GzytDKOh8FHyYzEqMsFw=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpqu7UQ2pg3Q1K0jA1mE1WZqjSNpeYUcA0o+2b5 phN/eDOiEeJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaaru1AAKCRAEWL7uWMY5 Ri7lEACKZA4t9mW1PRY2HeVksoFBYDfNwiLO+uWYu3cDyE52mRcT1x8wJxB9Kn7jp/KHw7ZMCS6 WYody/yBBvbeaZNYp8aeeByM/K4jcvsE2b27QfBL50emfO7kTk05oGJUodC/zg61yf1uEuDHz5v g6i7ehVkvxIjXDgINJzEiLCmqrStZLj9/28Wgvu3lTKkj3B2o0jLSbU63Rw6pCyPYRwEfQbukXR tO1gpmEyJeah6o3BI4TBVptImXWhpTMW01FvjTeHkye6YUDaxf8oJNWCoZf4hSSFfr7otXSJNWF X3ExbWPJTOZOSWKSKioGo1nTR7Yp2FfCRhx0/3vBr5oj/5jv1s9FKEvw5GbUE6XttfsmaooCzJ+ +CBNFP9TT91yf0lW6r9Y/fEloeEAr8A1G4mnsLqJ92twmVWxoAaPCI29Y5WBFmyKiKCLGi13QXr 2v/wQrlDTTK/KcI4DlOm6Q5dP92p+E+ioTOweK/XmTF0UsIfO+OBJpEjaMrrj1ZL6UUTEtZjlQj y214iyWC29LlppBtm5BlywLCliuZ90tRrqpA8KeI2Rpm4q3Z5o8/m+Cc2Mn1B6GeBzMVTL1iBiQ noPYGsxFhWsyCAnLpODhp4ZBM3Bem8Hw21W/eyqCwLCU+3nubYVjbMZYAbrPHoGwLLbcEumg9kD f8ACYotbggUUFbg== X-Mailer: b4 0.14.3 Message-ID: <20260306-binder-netlink-v1-1-daceb5bc83f2@google.com> Subject: [PATCH 1/4] rust: netlink: add raw netlink abstraction From: Alice Ryhl To: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Greg Kroah-Hartman , "=?utf-8?q?Arve_Hj=C3=B8nnev=C3=A5g?=" , Todd Kjos , Christian Brauner , Carlos Llamas Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable This implements a safe and relatively simple API over the netlink API, that allows you to add different attributes to a netlink message and broadcast it. As the first user of this API only makes use of broadcast, only broadcast messages are supported here. This API is intended to be safe and to be easy to use in *generated* code. This is because netlink is generally used with yaml files that describe the underlying API, and the python generator outputs C code (or, soon, Rust code) that lets you use the API more easily. So for example, if there is a string field, the code generator will output a method that internall calls `put_string()` with the right attr type. Signed-off-by: Alice Ryhl --- rust/bindings/bindings_helper.h | 3 + rust/helpers/genetlink.c | 46 +++++++ rust/helpers/helpers.c | 1 + rust/kernel/lib.rs | 1 + rust/kernel/netlink.rs | 267 ++++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 318 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 083cc44aa952..8abb626fce6c 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -88,6 +88,8 @@ #include #include #include +#include +#include #include =20 /* @@ -105,6 +107,7 @@ const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN =3D ARCH_SLAB_MINALIGN; const size_t RUST_CONST_HELPER_ARCH_KMALLOC_MINALIGN =3D ARCH_KMALLOC_MINA= LIGN; const size_t RUST_CONST_HELPER_PAGE_SIZE =3D PAGE_SIZE; +const size_t RUST_CONST_HELPER_GENLMSG_DEFAULT_SIZE =3D GENLMSG_DEFAULT_SI= ZE; const gfp_t RUST_CONST_HELPER_GFP_ATOMIC =3D GFP_ATOMIC; const gfp_t RUST_CONST_HELPER_GFP_KERNEL =3D GFP_KERNEL; const gfp_t RUST_CONST_HELPER_GFP_KERNEL_ACCOUNT =3D GFP_KERNEL_ACCOUNT; diff --git a/rust/helpers/genetlink.c b/rust/helpers/genetlink.c new file mode 100644 index 000000000000..99ada80cfa41 --- /dev/null +++ b/rust/helpers/genetlink.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (C) 2026 Google LLC. + */ + +#include + +#ifdef CONFIG_NET + +__rust_helper struct sk_buff *rust_helper_genlmsg_new(size_t payload, gfp_= t flags) +{ + return genlmsg_new(payload, flags); +} + +__rust_helper +int rust_helper_genlmsg_multicast(const struct genl_family *family, + struct sk_buff *skb, u32 portid, + unsigned int group, gfp_t flags) +{ + return genlmsg_multicast(family, skb, portid, group, flags); +} + +__rust_helper void rust_helper_genlmsg_cancel(struct sk_buff *skb, void *h= dr) +{ + return genlmsg_cancel(skb, hdr); +} + +__rust_helper void rust_helper_genlmsg_end(struct sk_buff *skb, void *hdr) +{ + return genlmsg_end(skb, hdr); +} + +__rust_helper void rust_helper_nlmsg_free(struct sk_buff *skb) +{ + return nlmsg_free(skb); +} + +__rust_helper +int rust_helper_genl_has_listeners(const struct genl_family *family, + struct net *net, unsigned int group) +{ + return genl_has_listeners(family, net, group); +} + +#endif diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a3c42e51f00a..0813185d8760 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -32,6 +32,7 @@ #include "err.c" #include "irq.c" #include "fs.c" +#include "genetlink.c" #include "io.c" #include "jump_label.c" #include "kunit.c" diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 3da92f18f4ee..c94658051d42 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -118,6 +118,7 @@ pub mod module_param; #[cfg(CONFIG_NET)] pub mod net; +pub mod netlink; pub mod num; pub mod of; #[cfg(CONFIG_PM_OPP)] diff --git a/rust/kernel/netlink.rs b/rust/kernel/netlink.rs new file mode 100644 index 000000000000..4ac9e6d919fa --- /dev/null +++ b/rust/kernel/netlink.rs @@ -0,0 +1,267 @@ +// 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) +#![cfg(CONFIG_NET)] + +use kernel::{ + alloc::{self, AllocError}, + error::to_result, + prelude::*, + transmute::AsBytes, + types::Opaque, + ThisModule, +}; + +use core::{ + mem::ManuallyDrop, + ptr::NonNull, // +}; + +/// The default netlink message size. +pub const GENLMSG_DEFAULT_SIZE: usize =3D bindings::GENLMSG_DEFAULT_SIZE; + +/// A wrapper around `struct sk_buff` for generic netlink messages. +/// +/// # Invariants +/// +/// The pointer has ownership over a valid `sk_buff`. +pub struct SkBuff { + skb: NonNull, +} + +impl SkBuff { + /// Creates a new `SkBuff` with the given size. + pub fn new(size: usize, flags: alloc::Flags) -> Result { + // SAFETY: `genlmsg_new` only requires its arguments to be valid i= ntegers. + let skb =3D unsafe { bindings::genlmsg_new(size, flags.as_raw()) }; + let skb =3D NonNull::new(skb).ok_or(AllocError)?; + Ok(SkBuff { skb }) + } + + /// Puts a generic netlink header into the `SkBuff`. + pub fn genlmsg_put( + self, + portid: u32, + seq: u32, + family: &'static Family, + cmd: u8, + ) -> Result { + let skb =3D self.skb.as_ptr(); + // SAFETY: The skb and family pointers are valid. + let hdr =3D unsafe { bindings::genlmsg_put(skb, portid, seq, famil= y.as_raw(), 0, cmd) }; + let hdr =3D NonNull::new(hdr).ok_or(AllocError)?; + Ok(GenlMsg { skb: self, hdr }) + } +} + +impl Drop for SkBuff { + 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: SkBuff, + hdr: NonNull, +} + +impl GenlMsg { + /// Puts an attribute into the message. + #[inline] + fn put(&mut self, attrtype: c_int, value: &T) -> Result + where + T: ?Sized + AsBytes, + { + let skb =3D self.skb.skb.as_ptr(); + let len =3D size_of_val(value); + let ptr =3D core::ptr::from_ref(value).cast::(); + // SAFETY: `skb` is valid by `SkBuff` type invariants, and the pro= vided 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 =3D self.skb.skb.as_ptr(); + // SAFETY: `skb` is valid by `SkBuff` 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 =3D ManuallyDrop::new(self); + // SAFETY: The `skb` and `family` pointers are valid. We pass owne= rship 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()) }; + } +} + +/// A generic netlink family. +#[repr(transparent)] +pub struct Family { + inner: Opaque, +} + +// SAFETY: The `Family` type is thread safe. +unsafe impl Sync for Family {} + +impl Family { + /// Creates a new `Family` instance. + pub const fn const_new( + module: &ThisModule, + name: &[u8], + version: u32, + mcgrps: &'static [MulticastGroup], + ) -> Family { + let n_mcgrps =3D mcgrps.len() as u8; + if n_mcgrps as usize !=3D mcgrps.len() { + panic!("too many mcgrps"); + } + let mut genl_family =3D bindings::genl_family { + version, + // SAFETY: This bitfield is represented as an u8. + _bitfield_1: unsafe { + core::mem::transmute::>(0b11u8) + }, + 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 =3D 0; + while i < name.len() { + genl_family.name[i] =3D name[i]; + i +=3D 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) !=3D 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. + pub const fn const_new(name: &CStr) -> MulticastGroup { + let mut group: bindings::genl_multicast_group =3D pin_init::zeroed= (); + + let name =3D name.to_bytes_with_nul(); + if group.name.len() < name.len() { + panic!("genl_multicast_group name too long"); + } + let mut i =3D 0; + while i < name.len() { + group.name[i] =3D name[i]; + i +=3D 1; + } + + MulticastGroup { group } + } +} + +/// A registration of a generic netlink family. +/// +/// This type represents the registration of a [`Family`]. When an instanc= e of this type is +/// dropped, its respective generic netlink family will be unregistered fr= om the system. +/// +/// # Invariants +/// +/// `self.family` always holds a valid reference to an initialized and reg= istered [`genl_family`]. +pub struct Registration { + family: &'static Family, +} + +impl Family { + /// Registers the generic netlink family with the kernel. + pub fn register(&'static self) -> Result { + // SAFETY: `self.as_raw()` is a valid pointer to a `genl_family` s= truct. + // The `genl_family` struct is static, so it will outlive the regi= stration. + 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 register= ed `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()) }; + } +} --=20 2.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 17:20:15 2026 Received: from mail-ej1-f74.google.com (mail-ej1-f74.google.com [209.85.218.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A43073B5844 for ; Fri, 6 Mar 2026 15:12:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772809953; cv=none; b=No0nLWXSjv86xEKSaLTxg1K82sZ0fsypg0A4ZBxiwmVUZR9A4K1ruMXG8FVXj+q7uhU3zENIoHwVzkMbJ3UoEip4z8+ZBnguiSXlmQ9rrIhtE1gb/y1BLA3ZDUvo05/OEY3bsGmB/P+nWAsXTmVlP9434S9xXx1xX2+jKKiU+rQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772809953; c=relaxed/simple; bh=17XHRS4px9KoFXD3y4Fn4cvpcpjxzAIN0p18op7B0go=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=rYY4OiA6MrIrntteBH118xYfpqgNxBEQHwDm+3thnWw4Gt4uanmtroavRbnjDujTGX8/C942FWXRFmep6OTyfqHC5K1FCgwVWcgQ2aZL1S2Q56lXfKFTsm1Sq0zFYBrVYZq2dz5+Wm+61RdaKlrk84HU9Vq9M1q7Dpb142q4JW0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=YAiCcAnM; arc=none smtp.client-ip=209.85.218.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="YAiCcAnM" Received: by mail-ej1-f74.google.com with SMTP id a640c23a62f3a-b941f6209ccso197201666b.0 for ; Fri, 06 Mar 2026 07:12:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772809946; x=1773414746; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=WoYJHkcy80PnGGgEzZEeH4O5cD02mG4V0mk4JEgh3Zc=; b=YAiCcAnMOY9R4MARduJnxwYtMrADa/gOQqTATmqmBbGelRwYuNBV6z5WBUYa+nXMXH 5EJiKeV3CxV90SOBj8LKYKMPXutjNZrlPKGiTXDAMe8NELnZJR2Zkm9ZCUKJKkRNsasz k0h7PgAN7UW7xJ+Gjzr6u8COOp8I+kRGSJOnpwMskMuEH6WKpLyzUdEgIsx4/C11b9ji qN05vy8JUaYe7HDszeGHudMZuDfvB3FH78sfQEiMrvJVOPYnOq4y28mRl/FsX3VONTmL I33BOdG0Dt/2eaPSABiO7J36EeseE/G0pP0qwyoPiAiQwnAWSErgKLBndJ30A9Z5kCkj kcjw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772809946; x=1773414746; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=WoYJHkcy80PnGGgEzZEeH4O5cD02mG4V0mk4JEgh3Zc=; b=Lg2xSjupPPWTrAbkr6o4u9D0TqMygu5EOOHFuwso5cfiGSDy0nSy7qFWEKeyNNFQ3h GPgu9/E87J4tKzZmpoL1ZM9Uk9Avkm/3SousRoCEbxAU+gWwFqtWCcq2Nbz7OqL9nH8b lyhBoIIrHeXlJbRE1yIoSIqvQd1MYCeLioV4yG5ghosJ+2K3x9FKAR27ycWd9QQb9OXP YgyopUsXj08W3EVpzdzu2jMH//u6N30jYQmOQkQKTA5fZbuJ/NZMtmFqeCULQz7WDmom 4W4A04oNkq5bBe725rPZx/RYY8hUhRsAaRZpWcAao5G8MiLLHTEzOp31337fA/LK9ObP Yucg== X-Gm-Message-State: AOJu0YytAgphzlO44vmIXMTi8ed6NgI1MsA2l8CqVqAdmkZFmwADSS7P vv/X4qErEXSHP2RLKTIno8pYISs0Vo9in25vAZ6ZWomFA3c42hjISBiXJ6O/s1Heo44wUKJgfaZ r2403/AJARgKo52Yx9g== X-Received: from ejcff15.prod.google.com ([2002:a17:906:9c0f:b0:b90:4eb7:2c53]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a17:907:86aa:b0:b8e:56d1:be76 with SMTP id a640c23a62f3a-b942da72e4amr128107866b.1.1772809945692; Fri, 06 Mar 2026 07:12:25 -0800 (PST) Date: Fri, 06 Mar 2026 15:12:14 +0000 In-Reply-To: <20260306-binder-netlink-v1-0-daceb5bc83f2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260306-binder-netlink-v1-0-daceb5bc83f2@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7323; i=aliceryhl@google.com; h=from:subject:message-id; bh=17XHRS4px9KoFXD3y4Fn4cvpcpjxzAIN0p18op7B0go=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpqu7U4/HmIeXfFrF24dpNk37B9KD5/AdNewhzY rgs7XErlUeJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaaru1AAKCRAEWL7uWMY5 RtlMD/9R1e230V1ReJ8yX+ZBPPtPtzgsrmUwxEH9FrvFrr92aymlgQ7HP1M9yA4eeNOQ8Jg6f2c P2A6Flzq6n3IdFbvoRn/P1UoLiDKrG0IJWjs33YpMdU+bU1iSH2PuHKh0tvQZFZNnBubkYbJEXf BzLzPuZosMJLOZ/ee5QP/gScbs0AQb1NqG7pKcysYGNOX6mGhCXZyQ5CUyTdTfzD2tWR3tBwn2s QsyH42Ec0zuarjIPOsrrnWf03UgVqmeQDHGtrd8p5R7lqid2NDYTvnb/7p9jWl2HedqoQ5wIGFK aJ82Lx99N7yVFacs6Q53LcRy/M+gmjNkSYRSHhysoisR/qiOyBTvHCqhfXQAR3xQQFqxgeAPG6l THGKP4/XIHuKK7tBIIoRwaVP1K6KiBJGh8Qe4n8NggU4swL/P5BwHbQIFzJVoGdnirHIC54hmX7 uZuTSAbM63dWMJeLB9/iYSCoiPG3BbpfNs/VC1P4OYFUC1Rntd6PTbNRcEGBj2rGkvV5uRg2L3j prTVeLoSX8gz6x/hrgGFu3RvzVj+bKr2SvC7i+fwUZF2mDZXcJObTzO2b+RRyhQaTB3DjdtMiXG iZlSXd0dIHtCM7UJ3pCKBbtoUyzwqdliogj3kOkYBtwoFTzVLoSIcFaJSdZy0M1Z38p12ntep9V 2cI4Maina8yM/cQ== X-Mailer: b4 0.14.3 Message-ID: <20260306-binder-netlink-v1-2-daceb5bc83f2@google.com> Subject: [PATCH 2/4] ynl_gen: generate Rust files from yaml files From: Alice Ryhl To: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Greg Kroah-Hartman , "=?utf-8?q?Arve_Hj=C3=B8nnev=C3=A5g?=" , Todd Kjos , Christian Brauner , Carlos Llamas Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable To generate netlink frames from Rust code easily, generate Rust libraries with methods for generating different netlink messages as appropriate. The new 'rust' type corresponds to a Rust version of the C target 'kernel'. There is no Rust version of the 'uapi' target since Rust code exports its uapi via C headers - choice of language is opaque to userspace. Signed-off-by: Alice Ryhl --- tools/net/ynl/pyynl/ynl_gen_c.py | 132 +++++++++++++++++++++++++++++++++++= +++- tools/net/ynl/ynl-regen.sh | 2 +- 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen= _c.py index 0e1e486c1185..5ddc8f84c4a3 100755 --- a/tools/net/ynl/pyynl/ynl_gen_c.py +++ b/tools/net/ynl/pyynl/ynl_gen_c.py @@ -19,6 +19,7 @@ import pathlib import os import re import shutil +import subprocess import sys import tempfile import yaml as pyyaml @@ -1744,6 +1745,19 @@ class CodeWriter: else: self.p('}' + line) =20 + def array_start(self, line=3D''): + if line: + line =3D line + ' ' + self.p(line + '[') + self._ind +=3D 1 + + def array_end(self, line=3D''): + if line and line[0] not in {';', ','}: + line =3D ' ' + line + self._ind -=3D 1 + self._nl =3D False + self.p(']' + line) + def write_doc_line(self, doc, indent=3DTrue): words =3D doc.split() line =3D ' *' @@ -3415,10 +3429,119 @@ def find_kernel_root(full_path): return full_path, sub_path[:-1] =20 =20 +def render_rust(family, cw): + cw.p('#![allow(unreachable_pub, clippy::wrong_self_convention)]') + cw.p('use kernel::netlink::{Family, MulticastGroup};') + cw.p('use kernel::prelude::*;') + cw.nl() + + family_upper =3D c_upper(family.ident_name) + family_name =3D f'{family_upper}_NL_FAMILY' + mcgrps_name =3D f'{family_name}_MCGRPS' + + cw.p(f'pub static {family_name}: Family =3D Family::const_new(') + cw._ind +=3D 1 + cw.p('&crate::THIS_MODULE,') + cw.p(f'kernel::uapi::{family.fam_key},') + cw.p(f'kernel::uapi::{family.ver_key},') + cw.p(f'&{mcgrps_name},') + cw._ind -=3D 1 + cw.p(');') + cw.nl() + + if family.mcgrps['list']: + cw.array_start(f'static {mcgrps_name}: [MulticastGroup; {len(famil= y.mcgrps["list"])}] =3D ') + for grp in family.mcgrps['list']: + cw.p(f'MulticastGroup::const_new(c"{grp["name"]}"),') + cw.array_end(';') + cw.nl() + + for idx, (op_name, op) in enumerate(item for item in family.msgs.items= () if 'event' in item[1]): + struct_name =3D op_name.capitalize() + + if 'doc' in op: + doc_lines =3D op['doc'].strip().split('\n') + for line in doc_lines: + cw.p(f'/// {line.strip()}') + + cw.block_start(f'pub struct {struct_name}') + cw.p('skb: kernel::netlink::GenlMsg,') + cw.block_end() + cw.nl() + + cw.block_start(f'impl {struct_name}') + cw.p('/// Create a new multicast message.') + cw.p('pub fn new(') + cw._ind +=3D 1 + cw.p('size: usize,') + cw.p('portid: u32,') + cw.p('seq: u32,') + cw.p('flags: kernel::alloc::Flags,') + cw._ind -=3D 1 + cw.block_start(') -> Result') + cw.p(f'const {op.enum_name}: u8 =3D kernel::uapi::{op.enum_name} a= s u8;') + cw.p('let skb =3D kernel::netlink::SkBuff::new(size, flags)?;') + cw.p(f'let skb =3D skb.genlmsg_put(portid, seq, &{family_name}, {o= p.enum_name})?;') + cw.p('Ok(Self { skb })') + cw.block_end() + cw.nl() + + cw.p('/// Broadcast this message.') + cw.block_start('pub fn multicast(self, portid: u32, flags: kernel:= :alloc::Flags) -> Result') + cw.p(f'self.skb.multicast(&{family_name}, portid, {idx}, flags)') + cw.block_end() + cw.nl() + + cw.p('/// Check if this message type has listeners.') + cw.block_start('pub fn has_listeners() -> bool') + cw.p(f'{family_name}.has_listeners({idx})') + cw.block_end() + + attr_set_name =3D op['attribute-set'] + attr_set =3D family.attr_sets[attr_set_name] + event_attrs =3D op['event']['attributes'] + + for attr_name in event_attrs: + attr =3D attr_set[attr_name] + method_name =3D attr_name.replace('-', '_') + + if attr.type =3D=3D 'u32': + put_fn =3D 'put_u32' + arg_str =3D ', val' + method_args =3D '(&mut self, val: u32)' + elif attr.type =3D=3D 'string': + put_fn =3D 'put_string' + arg_str =3D ', val' + method_args =3D '(&mut self, val: &CStr)' + elif attr.type =3D=3D 'flag': + put_fn =3D 'put_flag' + arg_str =3D '' + method_args =3D '(&mut self)' + else: + put_fn =3D 'put_u32' + arg_str =3D ', val' + method_args =3D f'(&mut self, val: {attr.type})' + + cw.nl() + if 'doc' in attr.yaml: + doc_lines =3D attr.yaml['doc'].strip().split('\n') + for line in doc_lines: + cw.p(f'/// {line.strip()}') + + cw.block_start(f'pub fn {method_name}{method_args} -> Result') + cw.p(f'const {attr.enum_name}: c_int =3D kernel::uapi::{attr.e= num_name} as c_int;') + cw.p(f'self.skb.{put_fn}({attr.enum_name}{arg_str})') + cw.block_end() + + cw.block_end() + cw.nl() + cw.p(' ') + + def main(): parser =3D argparse.ArgumentParser(description=3D'Netlink simple parsi= ng generator') parser.add_argument('--mode', dest=3D'mode', type=3Dstr, required=3DTr= ue, - choices=3D('user', 'kernel', 'uapi')) + choices=3D('user', 'kernel', 'uapi', 'rust')) parser.add_argument('--spec', dest=3D'spec', type=3Dstr, required=3DTr= ue) parser.add_argument('--header', dest=3D'header', action=3D'store_true'= , default=3DNone) parser.add_argument('--source', dest=3D'header', action=3D'store_false= ') @@ -3471,6 +3594,13 @@ def main(): render_uapi(parsed, cw) return =20 + if args.mode =3D=3D 'rust': + render_rust(parsed, cw) + cw.close_out_file() + if args.out_file: + subprocess.run(['rustfmt', '--edition', '2021', args.out_file]) + return + hdr_prot =3D f"_LINUX_{parsed.c_name.upper()}_GEN_H" if args.header: cw.p('#ifndef ' + hdr_prot) diff --git a/tools/net/ynl/ynl-regen.sh b/tools/net/ynl/ynl-regen.sh index d9809276db98..4f5ceb4fe147 100755 --- a/tools/net/ynl/ynl-regen.sh +++ b/tools/net/ynl/ynl-regen.sh @@ -17,7 +17,7 @@ done KDIR=3D$(dirname $(dirname $(dirname $(dirname $(realpath $0))))) pushd ${search:-$KDIR} >>/dev/null =20 -files=3D$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\|user= \)') +files=3D$(git grep --files-with-matches '^/\* YNL-GEN \(kernel\|uapi\|user= \|rust\)') for f in $files; do # params: 0 1 2 3 # $YAML YNL-GEN kernel $mode --=20 2.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 17:20:15 2026 Received: from mail-ed1-f74.google.com (mail-ed1-f74.google.com [209.85.208.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6503D3B3C15 for ; Fri, 6 Mar 2026 15:12:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772809953; cv=none; b=VwAsOXBD+o+pMg5VxQME1mdA7vRV9fHtTFLbwdpaIJuRWevM1y99/7XPKUoSZnmDJ2Fm5byFj9BwcqoWnFnir/W9BjsVZnO8E9qGcqUReiOBTqfevn0IscJtXAPI1OQwBdgoXDKD1rMy7Rgb5tIxPqF2wyrqTk2IDiSTTc6hZQw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772809953; c=relaxed/simple; bh=Sk1TAxEEV4GhsxW0UREgc7Kuhcte6geJK72w1Dswhp0=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=m6WMHtS0nfo0NXLcdeYJ1Gnu2M7SqR78yR71P0kFFintJagq4pGvFki4vrlfO6dz+4bFxp2zLNV0JEgO143LWPTGVaxEkuiIsZnWG+154ApCLvUfUvIWWmnXRvLQs7ecQyOlQ5ZT+F20X9kEGrDojcDSDXJvKkstXgke11c7cao= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=vZsa4FqM; arc=none smtp.client-ip=209.85.208.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="vZsa4FqM" Received: by mail-ed1-f74.google.com with SMTP id 4fb4d7f45d1cf-6614741a740so2430349a12.0 for ; Fri, 06 Mar 2026 07:12:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772809948; x=1773414748; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=OKcO5P//MUAi6X8UCN0d3ddOG88u8rL+NGXHISGAiZM=; b=vZsa4FqMK+bK0V4+Bo1bQl3n4tEEkuEIR1C3XKY7x8Q/gONlEEO1w5njlAc2g3xw4x 5BlHbDn+2BiZebZ8hM4+QlZxHriRnaIDljUtUnqV1GdrnOTdcbWJEDT8Qj/Wppb4SIn0 1ikV7l34NYq90CiySAp9QRgvSLoMQgvncVK4RtJtCuaAnkrvkP7ZBHfz2gXQms5rV9A0 Nw8NHQqofqO09bGoPmrY5qVo2hv64TLndZIoXqH3BZOUWSR5h39daaodYa5Nz9YdCG3a OOpcP10okDTU6W797DJG9bCFBdf4AJRTheyU+v9/RUuZtrOawX6lS0E8gspm+aW8vuSl GKUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772809948; x=1773414748; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=OKcO5P//MUAi6X8UCN0d3ddOG88u8rL+NGXHISGAiZM=; b=KEqcXtLcR7lre/MKUO1+UJktBc5vFZO0JapNRjUlj0O2gR1kdbsr3pclaD2UbCakj0 Z21/s32lUhNRYgDem8JxeA0IP742I/a4iNhZvqRTsTv59x6a8+j+eoWfxnL1IjprWjVW MdLUDlRewaELgav/Mmc2UHw+Z7z16fckXmNt4uoI2t+edJJabL+uIpbjXCEJlrKw1EKG bUhmda9oAv52KKhRqd1MIebpfBP+dmqIqblMEao6t8X/IMtF/ciXqdY7rJYAdYrFsqxs QApvsgA2x4H8nIXFKXQ46p322Ps+0q6cQkkJvIhh5tiSltdNyU6xFvplFjv9ojXt9qNA hofg== X-Gm-Message-State: AOJu0Yx9v/hLd1JCTXEEA+QYK6YB1NLL73eaCSXzBFNFObb9/q65dv+r TG+6w0f3jepmoI5DjLmbasBdcoYXftyXfrViATixd8hMNy1cl10sl1CKo0Q8Q1gCKhEadyFvWU/ Hb9onyZQI+0pw/CMBTQ== X-Received: from ejcuh2.prod.google.com ([2002:a17:906:b382:b0:b8f:87e0:aa2b]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a17:907:3da7:b0:b87:1e50:95c2 with SMTP id a640c23a62f3a-b942da27c26mr139764066b.3.1772809947304; Fri, 06 Mar 2026 07:12:27 -0800 (PST) Date: Fri, 06 Mar 2026 15:12:15 +0000 In-Reply-To: <20260306-binder-netlink-v1-0-daceb5bc83f2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260306-binder-netlink-v1-0-daceb5bc83f2@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=7753; i=aliceryhl@google.com; h=from:subject:message-id; bh=Sk1TAxEEV4GhsxW0UREgc7Kuhcte6geJK72w1Dswhp0=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpqu7V1SkNGeGtAl99aCrvoRQqa5WaFdwWxGa6A sYlU5EocSmJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaaru1QAKCRAEWL7uWMY5 RqWSEACpsPjJtTqGr9LbRZAqK0TdOwEL3LxqkFcUggStQFpHcFhIEnIqPonudosX/Npk9XxEtfK VFbNxrRny+tyU92GuA4C0xd0r2StiWliofGelTxDAo78F+J5hOTFNYIskP/H49JqM84lWDoW3gI uxqoKplpRe93kAsQQyU7OSWQ++sUAzVdO0Z+vXakJ8xGPktO546+/SGPdsgh6feIj0khjlfsinF IcfsiSRLgBAnsu34971biAiqXWMY94dYR412L4Y16HfNS0JZ+Z9znQnn//nSfEaNd8SosdE+giZ lLW3yI/Lwe4A85wleV+kWWxPKWr9OeCigGsGeAilYVVk2804nVrUOh0HK6cOJK7lrSFaW6MAa2S ZPqMN8THwytpPo1r6enQD9xVc9TOPBcS9smg8ztIwaTAuU9ZNZFIgPAoN54avZmMVJACBGTPecV TEalNrpdWZqWmsB5l2KnV3xOuvoeSBuDoXwwptlQmrNeHd9Vr3Qu2ALK7EVClQssaO2FlfUQH0w 7W0L4L8hllvhN1U29uDqDY2jG0lUwUKkh0Xv+H5aR/2Uo/Lx9kwKGoJTg7QPCGpkWQLHBsnGZxH FmOboOlr4cKkAPa5TCzd6sxY9pslKIBhXIsG2nNSBf37dO9sRV2DkozKqwAlbEAe+ufd6UnKJbU /DrlwJTXOM4VVzQ== X-Mailer: b4 0.14.3 Message-ID: <20260306-binder-netlink-v1-3-daceb5bc83f2@google.com> Subject: [PATCH 3/4] rust_binder: add generated netlink.rs file From: Alice Ryhl To: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Greg Kroah-Hartman , "=?utf-8?q?Arve_Hj=C3=B8nnev=C3=A5g?=" , Todd Kjos , Christian Brauner , Carlos Llamas Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable To use netlink from Rust Binder, add a new generated netlink file using the new script and Documentation/netlink/specs/binder.yaml. Signed-off-by: Alice Ryhl --- drivers/android/Kconfig | 2 +- drivers/android/binder/netlink.rs | 113 +++++++++++++++++++++++++= ++++ drivers/android/binder/rust_binder_main.rs | 9 ++- rust/uapi/uapi_helper.h | 1 + 4 files changed, 122 insertions(+), 3 deletions(-) diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index e2e402c9d175..606a9d07f774 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC =20 config ANDROID_BINDER_IPC_RUST bool "Rust version of Android Binder IPC Driver" - depends on RUST && MMU && !ANDROID_BINDER_IPC + depends on RUST && MMU && NET && !ANDROID_BINDER_IPC help This enables the Rust implementation of the Binder driver. =20 diff --git a/drivers/android/binder/netlink.rs b/drivers/android/binder/net= link.rs new file mode 100644 index 000000000000..0537be08f4d5 --- /dev/null +++ b/drivers/android/binder/netlink.rs @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Cl= ause) +/* Do not edit directly, auto-generated from: */ +/* Documentation/netlink/specs/binder.yaml */ +/* YNL-GEN rust source */ +/* To regenerate run: tools/net/ynl/ynl-regen.sh */ + +#![allow(unreachable_pub, clippy::wrong_self_convention)] +use kernel::netlink::{Family, MulticastGroup}; +use kernel::prelude::*; + +pub static BINDER_NL_FAMILY: Family =3D Family::const_new( + &crate::THIS_MODULE, + kernel::uapi::BINDER_FAMILY_NAME, + kernel::uapi::BINDER_FAMILY_VERSION, + &BINDER_NL_FAMILY_MCGRPS, +); + +static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] =3D [MulticastGroup::c= onst_new(c"report")]; + +/// A multicast event sent to userspace subscribers to notify them about +/// binder transaction failures. The generated report provides the full +/// details of the specific transaction that failed. The intention is for +/// programs to monitor these events and react to the failures as needed. +pub struct Report { + skb: kernel::netlink::GenlMsg, +} + +impl Report { + /// Create a new multicast message. + pub fn new( + size: usize, + portid: u32, + seq: u32, + flags: kernel::alloc::Flags, + ) -> Result { + const BINDER_CMD_REPORT: u8 =3D kernel::uapi::BINDER_CMD_REPORT as= u8; + let skb =3D kernel::netlink::SkBuff::new(size, flags)?; + let skb =3D skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER= _CMD_REPORT)?; + Ok(Self { skb }) + } + + /// Broadcast this message. + pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Re= sult { + self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags) + } + + /// Check if this message type has listeners. + pub fn has_listeners() -> bool { + BINDER_NL_FAMILY.has_listeners(0) + } + + /// The enum binder_driver_return_protocol returned to the sender. + pub fn error(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_ERROR: c_int =3D kernel::uapi::BINDER_A_REPO= RT_ERROR as c_int; + self.skb.put_u32(BINDER_A_REPORT_ERROR, val) + } + + /// The binder context where the transaction occurred. + pub fn context(&mut self, val: &CStr) -> Result { + const BINDER_A_REPORT_CONTEXT: c_int =3D kernel::uapi::BINDER_A_RE= PORT_CONTEXT as c_int; + self.skb.put_string(BINDER_A_REPORT_CONTEXT, val) + } + + /// The PID of the sender process. + pub fn from_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_PID: c_int =3D kernel::uapi::BINDER_A_R= EPORT_FROM_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val) + } + + /// The TID of the sender thread. + pub fn from_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_TID: c_int =3D kernel::uapi::BINDER_A_R= EPORT_FROM_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val) + } + + /// The PID of the recipient process. This attribute may not be present + /// if the target could not be determined. + pub fn to_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_PID: c_int =3D kernel::uapi::BINDER_A_REP= ORT_TO_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_PID, val) + } + + /// The TID of the recipient thread. This attribute may not be present + /// if the target could not be determined. + pub fn to_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_TID: c_int =3D kernel::uapi::BINDER_A_REP= ORT_TO_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_TID, val) + } + + /// When present, indicates the failed transaction is a reply. + pub fn is_reply(&mut self) -> Result { + const BINDER_A_REPORT_IS_REPLY: c_int =3D kernel::uapi::BINDER_A_R= EPORT_IS_REPLY as c_int; + self.skb.put_flag(BINDER_A_REPORT_IS_REPLY) + } + + /// The bitmask of enum transaction_flags from the transaction. + pub fn flags(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FLAGS: c_int =3D kernel::uapi::BINDER_A_REPO= RT_FLAGS as c_int; + self.skb.put_u32(BINDER_A_REPORT_FLAGS, val) + } + + /// The application-defined code from the transaction. + pub fn code(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_CODE: c_int =3D kernel::uapi::BINDER_A_REPOR= T_CODE as c_int; + self.skb.put_u32(BINDER_A_REPORT_CODE, val) + } + + /// The transaction payload size in bytes. + pub fn data_size(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_DATA_SIZE: c_int =3D kernel::uapi::BINDER_A_= REPORT_DATA_SIZE as c_int; + self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val) + } +} diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/b= inder/rust_binder_main.rs index aa5f2a75adb4..65dae8931676 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -36,6 +36,8 @@ mod deferred_close; mod defs; mod error; +#[allow(dead_code)] +mod netlink; mod node; mod page_range; mod process; @@ -285,7 +287,9 @@ fn ptr_align(value: usize) -> Option { // SAFETY: We call register in `init`. static BINDER_SHRINKER: Shrinker =3D unsafe { Shrinker::new() }; =20 -struct BinderModule {} +struct BinderModule { + _netlink: kernel::netlink::Registration, +} =20 impl kernel::Module for BinderModule { fn init(_module: &'static kernel::ThisModule) -> Result { @@ -294,12 +298,13 @@ fn init(_module: &'static kernel::ThisModule) -> Resu= lt { =20 pr_warn!("Loaded Rust Binder."); =20 + let netlink =3D crate::netlink::BINDER_NL_FAMILY.register()?; BINDER_SHRINKER.register(c"android-binder")?; =20 // SAFETY: The module is being loaded, so we can initialize binder= fs. unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())?= }; =20 - Ok(Self {}) + Ok(Self { _netlink: netlink }) } } =20 diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h index 06d7d1a2e8da..86c7b6b284b0 100644 --- a/rust/uapi/uapi_helper.h +++ b/rust/uapi/uapi_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include --=20 2.53.0.473.g4a7958ca14-goog From nobody Thu Apr 9 17:20:15 2026 Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D54E53AEF39 for ; Fri, 6 Mar 2026 15:12:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772809954; cv=none; b=U4XK/QlyBkkgf3BbkZ40zIy/92nsrf+JM0B/GW2FiAxUzzLFsAyIRKs4PA4TDQ405wVhdtoGyujCY8w9UnSZIdxw1vSDdKGQk7cPfDqsCcSwqCPEaYDTZ2MXYxYUO/l1WUk0fRBaR01lAMlsQ6gBCgLA1atqCF50zlSteAcs/eE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772809954; c=relaxed/simple; bh=YxuNBCXB1ruXIhdHNa9guGwhOCpJAbw7MdxScXE7uVg=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=OV5GeKCuaobmIv6qjmKE/Cfs0sK6BIoDj8CoI+obqVYG4Ofp5Xkio1u4ukj0FRqlYXtjZdaTfZ0q9g1YiBHDowsaQT6udcvd3rsLjSsVCHKVy5WpQuhdWiMnp/IVidhLVAPgGeks2ojRXHZIm2mI7Lf8tLxC7ZSvmVwPeJqk7iY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=CPpVtYzH; arc=none smtp.client-ip=209.85.128.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="CPpVtYzH" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-48529847dedso3198165e9.1 for ; Fri, 06 Mar 2026 07:12:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772809949; x=1773414749; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=V7W6/0L6JR9EvU6fV715H5TPwVjS+u6fq9W7W12hOUQ=; b=CPpVtYzH/0aED2WmQjoU+HCX292Y3RigMl1W2EFDO9AXFy/FpxxbjqXveWRyDf3Nr9 v0rmNCDBsZvE5hVOeJW4eOcPduRmafFp82fw3iNGEnaQ3//ypcdCHhQOBKlgrlMC/v8a uHwEnwPB1HX+x7lPE3Vuu1fUqvqjftd/OucUdYAN9EwfetFnZU6qBKaTjxRiQ+AFAH3B x5ngGLhGrJcwk74K5q/7YDiQYmMwAaDZXcqYKjsMkMwOY7diAKbckVnIivm9zt/Jnx3l olfIo380qP7VpsP+f4Z0l+8fmya5tfvUv1RBuftddfeG1ffGu4UMru7KHWnHHrbQ3AYE 2K0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772809949; x=1773414749; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=V7W6/0L6JR9EvU6fV715H5TPwVjS+u6fq9W7W12hOUQ=; b=dIgFdFyK9QyhH48HpHTtvf8IYGT6vrROb3QpGOnzJNrggtlEiw2vwMJSbAvbgPECqg RLs4nZ5FnMJX6pOFNe6X0ylI1HeVm7wKYsBBFbAmMfaTMCc9Mz5qeKyuUdJOOtoMbsDn 5zQQKIHOIpuNYEOlxSRK7pe5y79s0k0o8SqpOU+8KMGbW4GQJWMCOIhIw8h7WHEc3WnL 048Fqf8CLn9FpO/WZ8yBtPtvwpXoIgPiPUrYd1l3jOaQ88ivqCpI2rlb9aBKObCLrjQr KtEZYzPi0mUrUYUkycRjGCIvTLp/W6YxiZvewr0wlA5B6bLrrf/v73yXfUeDb0RpJi21 ViAg== X-Gm-Message-State: AOJu0YyaZbctQdjDxpZ19+NRicVJ1JRWwT21UB0w54JMHHcg9mR43s9s laShex6FlqFOshSXb8uXuqhiGeNqbWi2nk5DUICRz3AAA+rDBY1p9raDJsM1QF8gC0axm84IFwk N8JDbAMUsF9Jxr+krGQ== X-Received: from wmqy16.prod.google.com ([2002:a05:600c:3650:b0:47e:dc0c:276f]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:609a:b0:477:63a4:88fe with SMTP id 5b1f17b1804b1-48526919691mr41548895e9.2.1772809949104; Fri, 06 Mar 2026 07:12:29 -0800 (PST) Date: Fri, 06 Mar 2026 15:12:16 +0000 In-Reply-To: <20260306-binder-netlink-v1-0-daceb5bc83f2@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260306-binder-netlink-v1-0-daceb5bc83f2@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=3906; i=aliceryhl@google.com; h=from:subject:message-id; bh=B4IqF7dNXJQF1f3lvVpuUKgRecGFsxg/WIFjSG2Bg5s=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpqu7VYn9W4TXw9As8/a5rEo1WV2ZUs9sKk6G0R bBm2CSidn+JAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaaru1QAKCRAEWL7uWMY5 RuefEACHdn+kgRMMwfBVAlPfxB9vQs0RUvm8N2+9VvXmSkylMedWJAejMVAZJoRXZeg2A4B3v1w 6Fj45doXbx8I7fU/+acVjzDzoZJiGoyhu0DhB1/Y9QhY8CklBTWqFLfgHJtxVjaXEMiwoHbkP6p ZyKXm4hERfzOfs3j0reDO6jkaJV4Ac54gKM6Pq/qXyCqq2LZL3zTuS5Vkl33N12IHCl4YsXWHcl iLrWO1MTeGBfa6PS1ruzmptC7gWoBDP8jmy3RIZOSAlCia6do0g9dmv+Chl/57VExVx1/e7mCxm F9XqAjIr68Jth/7aWWhwnzj41DcfilwQJI8tQXwxg9HsbHlBRHiciMxiqwcoATTS8GJoM5hshOp 7ZGc319+n1ZA4moPwrwukSlyzyqTOEIVK4kdohWUTav2t53YvO8qJlZYihcafhayewZMzBlx1eB 7REUfU5C2864Py1xR+YpZbBtXsZ5paSbpXX6XcLWFd0x7ylzmxnYbhUhcjzEiJL47yX2MEW4vsX 4+N1Tm0KGZ2ya5zJqBw+oa41g48dJtlBz0oZl/oZPj4bDYoQGPlF2K+rN/mags4G0tK0e3ZhosI +CD+yDonYZBgr5sCBV1kxv2P+SDHUtHDA4P6QxMeVMsA9Xk8biIq/fV0hjKuHiOcCFk1t6A1wwg S2Nsx/pNpMzof6g== X-Mailer: b4 0.14.3 Message-ID: <20260306-binder-netlink-v1-4-daceb5bc83f2@google.com> Subject: [PATCH 4/4] rust_binder: report netlink transactions From: Alice Ryhl To: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Greg Kroah-Hartman , "=?utf-8?q?Arve_Hj=C3=B8nnev=C3=A5g?=" , Todd Kjos , Christian Brauner , Carlos Llamas Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Carlos Llamas The Android Binder driver supports a netlink API that reports transaction *failures* to a userapce daemon. This allows devices to monitor processes with many failed transactions so that it can e.g. kill misbehaving apps. One very important thing that this monitors is when many oneway messages are sent to a frozen process, so there is special handling to ensure this scenario is surfaced over netlink. Signed-off-by: Carlos Llamas --- drivers/android/binder/rust_binder_main.rs | 1 - drivers/android/binder/thread.rs | 9 +++++++ drivers/android/binder/transaction.rs | 38 ++++++++++++++++++++++++++= ++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/b= inder/rust_binder_main.rs index 65dae8931676..f96264406ef3 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -36,7 +36,6 @@ mod deferred_close; mod defs; mod error; -#[allow(dead_code)] mod netlink; mod node; mod page_range; diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thre= ad.rs index 97a5e4acf64c..49879e615ce5 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1262,6 +1262,15 @@ fn transaction(self: &Arc, cmd: u32, reader: &= mut UserSliceReader) -> Resu } } =20 + if info.oneway_spam_suspect { + // If this is both a oneway spam suspect and a failure, we rep= ort it twice. This is + // useful in case the transaction failed with BR_TRANSACTION_P= ENDING_FROZEN. + info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx); + } + if info.reply !=3D 0 { + info.report_netlink(info.reply, &self.process.ctx); + } + Ok(()) } =20 diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder= /transaction.rs index 5dff3d655c4d..4b3fba76114a 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -3,6 +3,7 @@ // Copyright (C) 2025 Google LLC. =20 use kernel::{ + netlink::GENLMSG_DEFAULT_SIZE, prelude::*, seq_file::SeqFile, seq_print, @@ -17,6 +18,7 @@ allocation::{Allocation, TranslatedFds}, defs::*, error::{BinderError, BinderResult}, + netlink::Report, node::{Node, NodeRef}, process::{Process, ProcessInner}, ptr_align, @@ -49,6 +51,42 @@ impl TransactionInfo { pub(crate) fn is_oneway(&self) -> bool { self.flags & TF_ONE_WAY !=3D 0 } + + pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) { + if let Err(err) =3D self.report_netlink_inner(reply, ctx) { + pr_warn!( + "{}:{} netlink report failed: {err:?}\n", + self.from_pid, + self.from_tid + ); + } + } + + fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> ke= rnel::error::Result { + if !Report::has_listeners() { + return Ok(()); + } + let mut report =3D Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KER= NEL)?; + + report.error(reply)?; + report.context(&ctx.name)?; + report.from_pid(self.from_pid as u32)?; + report.from_tid(self.from_tid as u32)?; + report.to_pid(self.to_pid as u32)?; + if self.to_tid !=3D 0 { + report.to_tid(self.to_tid as u32)?; + } + + if self.is_reply { + report.is_reply()?; + } + report.flags(self.flags)?; + report.code(self.code)?; + report.data_size(self.data_size as u32)?; + + report.multicast(0, GFP_KERNEL)?; + Ok(()) + } } =20 use core::mem::offset_of; --=20 2.53.0.473.g4a7958ca14-goog