From nobody Sun Jun 21 16:16:43 2026 Received: from mail-m128153.netease.com (mail-m128153.netease.com [103.209.128.153]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 49479304BDC; Thu, 2 Apr 2026 17:52:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=103.209.128.153 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775152370; cv=none; b=iI8UjXvwBgTVu/HIayl6vjPZ3gOLcsgpq0vnF3NdMh4p8NEH4vFvWu4n1XPHQiVKB009tgfWZRC/cVSDRq8+HjJkyXiJ+YE3or0oSroj8DkwN6Xca87bRG7myHH6EnqNOzS8nZLEx/FIxnVsrPeBUqma8UMEOJCcEaYQVxvCmdQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775152370; c=relaxed/simple; bh=56UAlDrw2MVJbfQhHM8iVveF06uJGnsbg6eCsCsjn2Q=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=u4LbWamH/ZwOM21BkTKoVpHXXKWZJeTLmFy/yja8RI4LLCA8KvEMf9oQtrj9dICi8/AU64IQbfwpmIg7IEfToRy88sfq+vlhd26snO0tVm93cZQ31bT87w6IfCHAB/+pzYwyPoPO0qpq3BNpv+BvSlS+p9a55RLrCTvmrOmLGqg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn; spf=pass smtp.mailfrom=ruc.edu.cn; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b=lhuT3Ia0; arc=none smtp.client-ip=103.209.128.153 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b="lhuT3Ia0" Received: from lwz.tail698a0e.ts.net (gy-adaptive-ssl-proxy-4-entmail-virt151.gy.ntes [36.112.3.244]) by smtp.qiye.163.com (Hmail) with ESMTP id 3956a5591; Fri, 3 Apr 2026 00:36:57 +0800 (GMT+08:00) From: Wenzhao Liao To: rust-for-linux@vger.kernel.org, netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com Subject: [RFC PATCH 1/6] rust: bindings: expose networking headers needed by nlmon Date: Thu, 2 Apr 2026 12:36:35 -0400 Message-Id: <20260402163640.1079056-2-wenzhaoliao@ruc.edu.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> References: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-HM-Tid: 0a9d4f0e189103a2kunm6754def456bf19 X-HM-MType: 10 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFITzdXWS1ZQUlXWQ8JGhUIEh9ZQVlCS0pKVkkZTxpISB0ZShpPHVYeHw5VEwETFhoSFy QUDg9ZV1kYEgtZQVlITVVKSklVSFVJT09ZV1kWGg8SFR0UWUFZT0tIVUpLSEpOTE5VSktLVUpCS0 tZBg++ DKIM-Signature: a=rsa-sha256; b=lhuT3Ia02h5qaCmBbzBmWtwx9H8A2NQvan9sQMTJ3JPO0C2UHjF2m1boDz0zPPOm4tSU7uei3V0xjk30b6JSbqlOfQQEutv75FHzTZQFxPTUO6DgVJyV6SWdk8sqPbaoawJ1yI0PU0B7ut0SMjP4FXMgaAZcHxJmtBn2k4f2qrQ=; c=relaxed/relaxed; s=default; d=ruc.edu.cn; v=1; bh=EtrAAqKgIFaObfmYMvH+CIpV3k5p6IY+gei6OvVIHxo=; h=date:mime-version:subject:message-id:from; Content-Type: text/plain; charset="utf-8" Expose the networking declarations consumed by the minimal Rust nlmon reference driver before higher level wrappers are introduced. This includes the core net_device, rtnl_link_ops, statistics, sk_buff, and netlink tap declarations needed by the planned abstractions. Signed-off-by: Wenzhao Liao --- rust/bindings/bindings_helper.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 083cc44aa952..ee56505e03f9 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,8 @@ #include #include #include +#include +#include #include #include #include @@ -88,6 +91,7 @@ #include #include #include +#include #include =20 /* --=20 2.34.1 From nobody Sun Jun 21 16:16:43 2026 Received: from mail-m155101.qiye.163.com (mail-m155101.qiye.163.com [101.71.155.101]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 040D839E6C9; Thu, 2 Apr 2026 23:26:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=101.71.155.101 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775172410; cv=none; b=q0IMZaf4XHHDk/n+7RwDeCVyixizPJgqQTflZWjNk3n8fj4ZL4BTeVLUsbPcwmXX5ElOecdK/KhHZ2RLHHSGLbMerYt5BmDD9b1ryiinjkS6hEY30Vmz633QSdDiC//4qkXcOIMmnsVSJxDK9PjVts4eMduBZS5d2AzkYvyllGk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775172410; c=relaxed/simple; bh=wzqSeaql+9f3bTlG6r1Ms527UU1vR+/m3RmzbC6VoJs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ZS/UuX4ticoYkhWcT3APvhpFPRzcQ8wU8D0NkdpEbYredJB+t0g6E0yK/pzs6STNFSwIlDBxi4uXYGl1vjY/EleB/uShTF8lsOQC53ja1v9Gmtu5lJa1RZH0SnRJdo7X8mHjZk0a/2zpkcp/FnBjOt894U5GMNOU5OzNssn9AQ8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn; spf=pass smtp.mailfrom=ruc.edu.cn; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b=gmV6miW3; arc=none smtp.client-ip=101.71.155.101 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b="gmV6miW3" Received: from lwz.tail698a0e.ts.net (gy-adaptive-ssl-proxy-4-entmail-virt151.gy.ntes [36.112.3.244]) by smtp.qiye.163.com (Hmail) with ESMTP id 3956a5594; Fri, 3 Apr 2026 00:37:03 +0800 (GMT+08:00) From: Wenzhao Liao To: rust-for-linux@vger.kernel.org, netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com Subject: [RFC PATCH 2/6] rust: helpers: add net_device and sk_buff helper wrappers Date: Thu, 2 Apr 2026 12:36:36 -0400 Message-Id: <20260402163640.1079056-3-wenzhaoliao@ruc.edu.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> References: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-HM-Tid: 0a9d4f0e313303a2kunm6754def456bf2f X-HM-MType: 10 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFITzdXWS1ZQUlXWQ8JGhUIEh9ZQVkaGUgZVkwZSk8ZS0lPHklMSlYeHw5VEwETFhoSFy QUDg9ZV1kYEgtZQVlITVVKSklVSFVJT09ZV1kWGg8SFR0UWUFZT0tIVUpLSEpOTE5VSktLVUpCS0 tZBg++ DKIM-Signature: a=rsa-sha256; b=gmV6miW3WhKwZlXqFRnWXjHFyYY3hBwDUtBnsuq9gvIEwZHSlC+V2aprYItxyu/nTe2GwrgDgmmWi/+7Kyq6Zr1XrW8KTHGbMYDRgZH0jAb9vGQIj2/Qz15hCqTjRRKvyk2aqlP0cUn0AX//aOZt3NkopXwIlu+5iaGVdcFqZew=; c=relaxed/relaxed; s=default; d=ruc.edu.cn; v=1; bh=CfXxbgNMw9+HoS60QIpz3BngK0AlOEjcX5CqGTKZmNE=; h=date:mime-version:subject:message-id:from; Content-Type: text/plain; charset="utf-8" Add C helper entry points for the small networking helpers that are not exposed through bindgen-friendly interfaces. This covers netdev_priv() and dev_lstats_add(), which are used by the reference driver and remain hidden behind Rust-side safe wrappers. Signed-off-by: Wenzhao Liao --- rust/helpers/helpers.c | 1 + rust/helpers/net.c | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 rust/helpers/net.c diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index a3c42e51f00a..90fe1c7cbadc 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -38,6 +38,7 @@ #include "maple_tree.c" #include "mm.c" #include "mutex.c" +#include "net.c" #include "of.c" #include "page.c" #include "pci.c" diff --git a/rust/helpers/net.c b/rust/helpers/net.c new file mode 100644 index 000000000000..da04c718acd4 --- /dev/null +++ b/rust/helpers/net.c @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +__rust_helper void rust_helper_dev_kfree_skb(struct sk_buff *skb) +{ + dev_kfree_skb(skb); +} + +__rust_helper void rust_helper_dev_lstats_add(struct net_device *dev, unsi= gned int len) +{ + dev_lstats_add(dev, len); +} + +__rust_helper void *rust_helper_netdev_priv(const struct net_device *dev) +{ + return netdev_priv(dev); +} --=20 2.34.1 From nobody Sun Jun 21 16:16:43 2026 Received: from mail-m155101.qiye.163.com (mail-m155101.qiye.163.com [101.71.155.101]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6576D38C41A; Thu, 2 Apr 2026 23:26:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=101.71.155.101 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775172418; cv=none; b=sBjKhbyrebPc4LbvaS4TP3c1jllgV7Wx/zElXFSOclzfmO/WznowrPLgrDia3kK4WsWjOm0ArYAS6pZw+YeI5B8hK7vWtgf4pN8l/CkIyq9W8aUD5rt1ti7rj3UE5nQ2BOlT5nRLMRVz17OsC1+4EmN2axMy0guBUKq7xCzn/wI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775172418; c=relaxed/simple; bh=kJlR2Ns8kF7cTAUqt1+wsSt4O9jfhLCxG0g+f8scNsM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=TA/yUi/1WlE+QERZfRNsBc3Nbklows3ESLQ3DGibGa0TA3aMhMLm4GZPpSS2fV87dNLpfagyV95wlOyhHx6V3vTayby8BjRD/0ZEFtt7DJRbKulTvPmG/0HWM6dyedm8WBKPOlp0b0G47hV5FXvgQgLtOuL9plj54sLGZxkRkt8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn; spf=pass smtp.mailfrom=ruc.edu.cn; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b=SsQbp6Of; arc=none smtp.client-ip=101.71.155.101 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b="SsQbp6Of" Received: from lwz.tail698a0e.ts.net (gy-adaptive-ssl-proxy-4-entmail-virt151.gy.ntes [36.112.3.244]) by smtp.qiye.163.com (Hmail) with ESMTP id 3956a5597; Fri, 3 Apr 2026 00:37:09 +0800 (GMT+08:00) From: Wenzhao Liao To: rust-for-linux@vger.kernel.org, netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com Subject: [RFC PATCH 3/6] rust: net: add minimal skbuff, netdevice, and stats abstractions Date: Thu, 2 Apr 2026 12:36:37 -0400 Message-Id: <20260402163640.1079056-4-wenzhaoliao@ruc.edu.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> References: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-HM-Tid: 0a9d4f0e49d303a2kunm6754def456bf3b X-HM-MType: 10 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFITzdXWS1ZQUlXWQ8JGhUIEh9ZQVlCSxkZVhhNTx8fHkhOTRodTlYeHw5VEwETFhoSFy QUDg9ZV1kYEgtZQVlITVVKSklVSFVJT09ZV1kWGg8SFR0UWUFZT0tIVUpLSEpOTE5VSktLVUpCS0 tZBg++ DKIM-Signature: a=rsa-sha256; b=SsQbp6Ofx6tbC278bWXlNQ6nG9kdmGKiDUE8BCfpMlPftXPsyJ8yJgP1M4i/VE5pspJyEIXVP7amkY8K/TizYSGuanfrUGyQCxk/i28RWlBBXlZ+BqNhyGwxxOnqnB0jxeB2nfycZk0SJT/GAY91nqQfps7ylTsPQVl0H9nfHxo=; c=relaxed/relaxed; s=default; d=ruc.edu.cn; v=1; bh=3yj724qbxmPZznRzy+XcCgyvPzeCQ0S4i2y7mbpIovU=; h=date:mime-version:subject:message-id:from; Content-Type: text/plain; charset="utf-8" Add narrow Rust wrappers for sk_buff access, net_device private state, and lightweight stats updates. These APIs keep ownership with kernel-managed networking objects while confining unsafe operations to the abstraction boundary. Signed-off-by: Wenzhao Liao --- rust/kernel/net.rs | 3 + rust/kernel/net/netdevice.rs | 319 +++++++++++++++++++++++++++++++++++ rust/kernel/net/skbuff.rs | 67 ++++++++ rust/kernel/net/stats.rs | 17 ++ 4 files changed, 406 insertions(+) create mode 100644 rust/kernel/net/netdevice.rs create mode 100644 rust/kernel/net/skbuff.rs create mode 100644 rust/kernel/net/stats.rs diff --git a/rust/kernel/net.rs b/rust/kernel/net.rs index fe415cb369d3..a61bc76f4499 100644 --- a/rust/kernel/net.rs +++ b/rust/kernel/net.rs @@ -4,3 +4,6 @@ =20 #[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)] pub mod phy; +pub mod netdevice; +pub mod skbuff; +pub mod stats; diff --git a/rust/kernel/net/netdevice.rs b/rust/kernel/net/netdevice.rs new file mode 100644 index 000000000000..1e6a63741422 --- /dev/null +++ b/rust/kernel/net/netdevice.rs @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Network device support. +//! +//! C headers: [`include/linux/netdevice.h`](srctree/include/linux/netdevi= ce.h) + +use crate::{ + bindings, + error::from_result, + net::skbuff, + prelude::*, + types::Opaque, +}; + +/// Network device feature flags. +pub type Features =3D bindings::netdev_features_t; + +/// `features` bits used by the MVP link-type setup path. +pub mod features { + use super::{bindings, Features}; + + const fn bit(index: u32) -> Features { + 1u64 << index + } + + /// Equivalent to `NETIF_F_SG`. + pub const SG: Features =3D bit(bindings::NETIF_F_SG_BIT as u32); + + /// Equivalent to `NETIF_F_FRAGLIST`. + pub const FRAGLIST: Features =3D bit(bindings::NETIF_F_FRAGLIST_BIT as= u32); + + /// Equivalent to `NETIF_F_HIGHDMA`. + pub const HIGHDMA: Features =3D bit(bindings::NETIF_F_HIGHDMA_BIT as u= 32); +} + +/// MTU-related constants and helpers used by the MVP link-type setup path. +pub mod mtu { + use super::bindings; + + const fn align(value: usize, alignment: usize) -> usize { + (value + alignment - 1) & !(alignment - 1) + } + + /// Equivalent to `NLMSG_GOODSIZE`. + pub const fn nlmsg_goodsize() -> u32 { + let page =3D if bindings::PAGE_SIZE < 8192 { + bindings::PAGE_SIZE + } else { + 8192 + }; + let overhead =3D align( + core::mem::size_of::(), + bindings::SMP_CACHE_BYTES as usize, + ); + + (page - overhead) as u32 + } + + /// Equivalent to `sizeof(struct nlmsghdr)`. + pub const NLMSGHDR: u32 =3D core::mem::size_of::()= as u32; +} + +/// `priv_flags` bits used by the MVP link-type setup path. +pub mod priv_flags { + use super::bindings; + + /// Equivalent to `IFF_NO_QUEUE`. + pub const NO_QUEUE: u32 =3D bindings::netdev_priv_flags_IFF_NO_QUEUE; +} + +/// `flags` bits used by the MVP link-type setup path. +pub mod flags { + use super::bindings; + + /// Equivalent to `IFF_NOARP`. + pub const NO_ARP: u32 =3D bindings::net_device_flags_IFF_NOARP; +} + +/// Device type constants used by the MVP link-type setup path. +pub mod device_type { + use super::bindings; + + /// Equivalent to `ARPHRD_NETLINK`. + pub const NETLINK: u16 =3D bindings::ARPHRD_NETLINK as u16; +} + +/// Per-cpu stat type constants used by the MVP link-type setup path. +pub mod pcpu_stat_type { + use super::bindings; + + /// Equivalent to `NETDEV_PCPU_STAT_LSTATS`. + pub const LSTATS: bindings::netdev_stat_type =3D + bindings::netdev_stat_type_NETDEV_PCPU_STAT_LSTATS; +} + +/// Result of an `ndo_start_xmit` callback. +pub enum TxOutcome { + /// The skb has been consumed and may be dropped by the abstraction. + Ok, + + /// The device is temporarily busy; ownership of the skb goes back to = the networking core. + Busy(skbuff::SkBuff), +} + +/// Operations exposed by a network device for the MVP `nlmon` loop. +/// +/// The callback trampolines live in this abstraction layer so that driver= code does not have to +/// touch raw kernel pointers or private storage casts directly. +pub trait Operations: Send + Sync + 'static { + /// The type stored in `net_device` private storage. + /// + /// The RTNL/netdevice allocation path zero-initializes private storag= e before any driver + /// callback observes it, so implementations must accept the all-zero = bit pattern. + type Private: Zeroable; + + /// Device open callback. + fn open(dev: &mut Device, private: Pin<&mut Self::Private>) -> Result; + + /// Device stop callback. + fn stop(dev: &mut Device, private: Pin<&mut Self::Private>) -> Result; + + /// Packet transmit callback. + /// + /// This only exposes a shared device view: TX callbacks may run concu= rrently, so the + /// abstraction must not synthesize an exclusive borrow of either `net= _device` or private + /// driver state here. + fn start_xmit(skb: skbuff::SkBuff, dev: &Device) -> TxOutcome; +} + +/// A Rust wrapper over `struct net_device`. +/// +/// # Invariants +/// +/// - The wrapped pointer always points to a valid `struct net_device`. +/// - The caller is responsible for only creating references in contexts w= here the kernel allows +/// the accessed fields to be mutated. +#[repr(transparent)] +pub struct Device(Opaque); + +impl Device { + /// Creates a mutable wrapper from a raw `net_device` pointer. + /// + /// # Safety + /// + /// The pointer must point to a valid `struct net_device` for the life= time of the returned + /// reference, and the caller must ensure that it is safe to mutate th= rough it. + pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::net_device) -> &= 'a mut Self { + let ptr =3D ptr.cast::(); + // SAFETY: The caller guarantees the pointer is valid for the retu= rned lifetime. + unsafe { &mut *ptr } + } + + /// Creates a shared wrapper from a raw `net_device` pointer. + /// + /// # Safety + /// + /// The pointer must point to a valid `struct net_device` for the life= time of the returned + /// reference. + pub(crate) unsafe fn from_raw_ref<'a>(ptr: *mut bindings::net_device) = -> &'a Self { + let ptr =3D ptr.cast::(); + // SAFETY: The caller guarantees the pointer is valid for the retu= rned lifetime. + unsafe { &*ptr } + } + + /// Returns the wrapped raw `net_device` pointer. + pub(crate) fn as_ptr(&self) -> *mut bindings::net_device { + self.0.get() + } + + /// Sets `dev->type`. + pub fn set_type(&mut self, device_type: u16) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`. + unsafe { (*dev).type_ =3D device_type }; + } + + /// ORs a bit into `dev->priv_flags`. + pub fn add_priv_flag(&mut self, flag: u32) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`, and the + // bindgen-generated accessors preserve the layout of the `priv_fl= ags/lltx` union. + unsafe { + let flags =3D &mut (*dev).__bindgen_anon_1.__bindgen_anon_1; + let current =3D flags.priv_flags(); + let flag =3D flag as usize; + flags.set_priv_flags(current | flag); + } + } + + /// Sets `dev->lltx`. + pub fn set_lltx(&mut self, enabled: bool) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`, and the + // bindgen-generated accessors preserve the layout of the `priv_fl= ags/lltx` union. + unsafe { + let flags =3D &mut (*dev).__bindgen_anon_1.__bindgen_anon_1; + flags.set_lltx(if enabled { 1 } else { 0 }); + } + } + + /// Stores a typed `net_device_ops` vtable pointer into `dev->netdev_o= ps`. + pub fn set_netdevice_ops(&mut self) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`. + unsafe { (*dev).netdev_ops =3D OperationsVTable::::build() }; + } + + /// Sets `dev->needs_free_netdev`. + pub fn set_needs_free_netdev(&mut self, enabled: bool) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`. + unsafe { (*dev).needs_free_netdev =3D enabled }; + } + + /// Sets `dev->features`. + pub fn set_features(&mut self, features: Features) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`. + unsafe { (*dev).features =3D features }; + } + + /// Sets `dev->flags`. + pub fn set_flags(&mut self, flags: u32) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`. + unsafe { (*dev).flags =3D flags }; + } + + /// Sets `dev->mtu`. + pub fn set_mtu(&mut self, mtu: u32) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`. + unsafe { (*dev).mtu =3D mtu }; + } + + /// Sets `dev->min_mtu`. + pub fn set_min_mtu(&mut self, mtu: u32) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`. + unsafe { (*dev).min_mtu =3D mtu }; + } + + /// Sets `dev->pcpu_stat_type`. + pub fn set_pcpu_stat_type(&mut self, stat_type: bindings::netdev_stat_= type) { + let dev =3D self.as_ptr(); + // SAFETY: The type invariant guarantees that `dev` points to a va= lid `net_device`. + unsafe { (*dev).set_pcpu_stat_type(stat_type) }; + } + + /// Returns a typed raw pointer to `net_device` private storage. + /// + /// # Safety + /// + /// The caller must ensure that the current `net_device` was allocated= with private storage for + /// `T`. Dereferencing the returned pointer additionally requires excl= usive access to that + /// storage. + pub(crate) unsafe fn private_ptr(&self) -> *mut T { + // SAFETY: The caller guarantees that the private storage really c= ontains a `T`, and the + // helper/exported wrapper preserves the C `netdev_priv` semantics. + unsafe { bindings::netdev_priv(self.as_ptr() as *const bindings::n= et_device).cast::() } + } +} + +struct OperationsVTable(core::marker::PhantomData); + +impl OperationsVTable { + extern "C" fn open_callback(dev: *mut bindings::net_device) -> c_int { + from_result(|| { + // SAFETY: The networking core only calls this callback with a= valid `net_device`. + let dev =3D unsafe { Device::from_raw(dev) }; + // SAFETY: The `rtnl::Registration` abstraction ties `priv_= size` to `T::Private`. + let private =3D unsafe { Pin::new_unchecked(&mut *dev.private_= ptr::()) }; + T::open(dev, private)?; + Ok(0) + }) + } + + extern "C" fn stop_callback(dev: *mut bindings::net_device) -> c_int { + from_result(|| { + // SAFETY: The networking core only calls this callback with a= valid `net_device`. + let dev =3D unsafe { Device::from_raw(dev) }; + // SAFETY: The `rtnl::Registration` abstraction ties `priv_= size` to `T::Private`. + let private =3D unsafe { Pin::new_unchecked(&mut *dev.private_= ptr::()) }; + T::stop(dev, private)?; + Ok(0) + }) + } + + extern "C" fn start_xmit_callback( + skb: *mut bindings::sk_buff, + dev: *mut bindings::net_device, + ) -> bindings::netdev_tx { + // SAFETY: The networking core only calls this callback with a val= id `net_device`. + let dev =3D unsafe { Device::from_raw_ref(dev) }; + // SAFETY: The networking core transfers ownership of the callback= skb to the driver. + let skb =3D unsafe { skbuff::SkBuff::from_raw_owned(skb) }; + + match T::start_xmit(skb, dev) { + TxOutcome::Ok =3D> bindings::netdev_tx_NETDEV_TX_OK, + TxOutcome::Busy(skb) =3D> { + let _ =3D skb.into_raw(); + bindings::netdev_tx_NETDEV_TX_BUSY + } + } + } + + const VTABLE: bindings::net_device_ops =3D bindings::net_device_ops { + ndo_open: Some(Self::open_callback), + ndo_stop: Some(Self::stop_callback), + ndo_start_xmit: Some(Self::start_xmit_callback), + // SAFETY: A zeroed `net_device_ops` is valid because omitted call= backs are represented by + // null pointers and all remaining fields are plain data/pointers. + ..unsafe { core::mem::zeroed() } + }; + + pub(crate) const fn build() -> &'static bindings::net_device_ops { + &Self::VTABLE + } +} diff --git a/rust/kernel/net/skbuff.rs b/rust/kernel/net/skbuff.rs new file mode 100644 index 000000000000..32ca2bb24a75 --- /dev/null +++ b/rust/kernel/net/skbuff.rs @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Socket-buffer ownership wrappers. +//! +//! C header: [`include/linux/skbuff.h`](srctree/include/linux/skbuff.h) + +use crate::bindings; + +use core::ptr::NonNull; + +/// Owns a single `struct sk_buff` passed into Rust from `ndo_start_xmit`. +/// +/// # Invariants +/// +/// - `ptr` is either `None` after ownership was handed back to the networ= king core, or it points +/// to a valid `struct sk_buff` exclusively owned by this value. +/// - Dropping this wrapper releases the skb exactly once via `dev_kfree_s= kb`. +pub struct SkBuff { + ptr: Option>, +} + +impl SkBuff { + /// Creates an owned skb wrapper from the callback argument. + /// + /// # Safety + /// + /// `ptr` must be a valid non-null skb pointer whose ownership is tran= sferred to Rust for the + /// duration of the callback. + pub(crate) unsafe fn from_raw_owned(ptr: *mut bindings::sk_buff) -> Se= lf { + Self { + // SAFETY: The caller guarantees ownership of a valid non-null= skb pointer. + ptr: Some(unsafe { NonNull::new_unchecked(ptr) }), + } + } + + fn ptr(&self) -> *mut bindings::sk_buff { + self.ptr + .expect("SkBuff ownership already transferred back to the core= ") + .as_ptr() + } + + /// Returns `skb->len`. + pub fn len(&self) -> u32 { + // SAFETY: `Self` owns a valid skb until ownership is explicitly r= eturned with `into_raw`. + unsafe { (*self.ptr()).len } + } + + /// Hands ownership back to the networking core without freeing the sk= b. + pub(crate) fn into_raw(mut self) -> *mut bindings::sk_buff { + self.ptr + .take() + .expect("SkBuff ownership already transferred back to the core= ") + .as_ptr() + } +} + +impl Drop for SkBuff { + fn drop(&mut self) { + let Some(ptr) =3D self.ptr.take() else { + return; + }; + + // SAFETY: `Self` owns the skb exactly once unless ownership has b= een transferred back to + // the networking core via `into_raw`. + unsafe { bindings::dev_kfree_skb(ptr.as_ptr()) }; + } +} diff --git a/rust/kernel/net/stats.rs b/rust/kernel/net/stats.rs new file mode 100644 index 000000000000..3e17f3b19c05 --- /dev/null +++ b/rust/kernel/net/stats.rs @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Network-device statistics helpers. +//! +//! C header: [`include/linux/netdevice.h`](srctree/include/linux/netdevic= e.h) + +use crate::{ + bindings, + net::netdevice, +}; + +/// Equivalent to `dev_lstats_add(dev, len)`. +pub fn dev_lstats_add(dev: &netdevice::Device, len: u32) { + // SAFETY: The helper expects a valid `net_device *`; `netdevice::Devi= ce` maintains that + // invariant and the helper handles the required per-cpu synchronizati= on internally. + unsafe { bindings::dev_lstats_add(dev.as_ptr(), len) }; +} --=20 2.34.1 From nobody Sun Jun 21 16:16:43 2026 Received: from mail-m24124.xmail.ntesmail.com (mail-m24124.xmail.ntesmail.com [45.195.24.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 58D1E3F7885; Thu, 2 Apr 2026 16:42:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.195.24.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775148170; cv=none; b=cu0+1EiIXTTTx8FUVNne7LPh9e8j5IYb7AKCwo+/2w4VBGtbtcr8FMeMK9lcvHJG8RxbWenwkGKmWbyrFZUCodFmhpoU3OyeZpkuEI7Sb9n4hOrsOTq6e76F+PPTDOlNuTmo2HI7GxmtZUG2bPQPMyujIKwet5FPYHRZtQHQXUY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775148170; c=relaxed/simple; bh=tNI/lzfNVMFJLGZUOC7zw3RzUqFKt/DhWPHxoVwk7Zw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=ebPtD+dYcAu4/S82lHjIeVTL91VS6yrrOK1CS7DupNqN41nL2c7KSmb5gH2+oFA9uqOKI/+gf780HrhUGsNR6i1X/KCyD9tPQH2l0xlPtFSmR4buOU4otPIhUJ8lEiUjtMbbMBVANxDLadFKzLppx2OXsqkLRW4VdAKwRUQTqZ8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn; spf=pass smtp.mailfrom=ruc.edu.cn; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b=UBFS7DnH; arc=none smtp.client-ip=45.195.24.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b="UBFS7DnH" Received: from lwz.tail698a0e.ts.net (gy-adaptive-ssl-proxy-4-entmail-virt151.gy.ntes [36.112.3.244]) by smtp.qiye.163.com (Hmail) with ESMTP id 3956a559a; Fri, 3 Apr 2026 00:37:16 +0800 (GMT+08:00) From: Wenzhao Liao To: rust-for-linux@vger.kernel.org, netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com Subject: [RFC PATCH 4/6] rust: net: add minimal rtnl registration and netlink tap support Date: Thu, 2 Apr 2026 12:36:38 -0400 Message-Id: <20260402163640.1079056-5-wenzhaoliao@ruc.edu.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> References: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-HM-Tid: 0a9d4f0e62a503a2kunm6754def456bf48 X-HM-MType: 10 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFITzdXWS1ZQUlXWQ8JGhUIEh9ZQVlDSkpCVhlCGE8fTBhPQk1NT1YeHw5VEwETFhoSFy QUDg9ZV1kYEgtZQVlITVVKSklVSFVJT09ZV1kWGg8SFR0UWUFZT0tIVUpLSUhOQ0NVSktLVUtZBg ++ DKIM-Signature: a=rsa-sha256; b=UBFS7DnHV82/lmeO6h5lzCGpw80eIx1OJjnjmlDGIa4Ai1okxOs9FQjUqHcIVrY4CMyMT4vymR+binmd1Qjy6I17ezcsYQC0i+Ue3b0QPXgZOiiv2yZOdAx+XkQDbrpbHGV0C4NohaHJMYxOGCZjb/5vd59IbU6mFWegJFmvMR0=; c=relaxed/relaxed; s=default; d=ruc.edu.cn; v=1; bh=hENdfJYTaCz+DNkQzaLUlsMdCmLD4GifK7zE2H7w848=; h=date:mime-version:subject:message-id:from; Content-Type: text/plain; charset="utf-8" Add the minimal pieces needed to register an rtnl link driver and to attach or detach a netlink tap from a net_device. The abstractions model kernel-owned registration lifetime and keep the driver layer free of unsafe blocks. Signed-off-by: Wenzhao Liao --- rust/kernel/net.rs | 2 + rust/kernel/net/netlink_tap.rs | 89 +++++++++++++ rust/kernel/net/rtnl.rs | 221 +++++++++++++++++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 rust/kernel/net/netlink_tap.rs create mode 100644 rust/kernel/net/rtnl.rs diff --git a/rust/kernel/net.rs b/rust/kernel/net.rs index a61bc76f4499..35459816c518 100644 --- a/rust/kernel/net.rs +++ b/rust/kernel/net.rs @@ -5,5 +5,7 @@ #[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)] pub mod phy; pub mod netdevice; +pub mod netlink_tap; +pub mod rtnl; pub mod skbuff; pub mod stats; diff --git a/rust/kernel/net/netlink_tap.rs b/rust/kernel/net/netlink_tap.rs new file mode 100644 index 000000000000..b26461937c6c --- /dev/null +++ b/rust/kernel/net/netlink_tap.rs @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Netlink tap lifecycle helpers. +//! +//! C header: [`include/linux/netlink.h`](srctree/include/linux/netlink.h) + +use crate::{ + bindings, + error::to_result, + net::netdevice, + prelude::*, + types::Opaque, + ThisModule, +}; + +/// Owns a `struct netlink_tap` plus its registration state. +#[pin_data] +#[derive(Zeroable)] +pub struct Tap { + #[pin] + inner: Opaque, + registered: bool, +} + +impl Tap { + /// Creates an unregistered tap. + pub const fn new() -> Self { + Self { + inner: Opaque::zeroed(), + registered: false, + } + } + + /// Returns whether the tap is currently registered. + pub fn is_registered(&self) -> bool { + self.registered + } + + /// Registers the tap for the provided device. + pub fn add( + self: Pin<&mut Self>, + dev: &netdevice::Device, + module: &'static ThisModule, + ) -> Result { + // SAFETY: The caller pinned `self`, so accessing the interior thr= ough the stable address is + // valid for the duration of this method. + let this =3D unsafe { self.get_unchecked_mut() }; + + if this.registered { + return Err(EBUSY); + } + + let tap =3D this.inner.get(); + + // SAFETY: `tap` points to valid storage for `struct netlink_tap`. + unsafe { + (*tap).dev =3D dev.as_ptr(); + (*tap).module =3D module.as_ptr(); + } + + // SAFETY: `tap` points to a valid `struct netlink_tap`. + to_result(unsafe { bindings::netlink_add_tap(tap) })?; + this.registered =3D true; + Ok(()) + } + + /// Unregisters the tap if it is currently active. + pub fn remove(self: Pin<&mut Self>) -> Result { + // SAFETY: The caller pinned `self`, so accessing the interior thr= ough the stable address is + // valid for the duration of this method. + let this =3D unsafe { self.get_unchecked_mut() }; + + if !this.registered { + return Ok(()); + } + + let tap =3D this.inner.get(); + + // SAFETY: `self.inner` contains a valid `struct netlink_tap` prev= iously passed to + // `netlink_add_tap`. + to_result(unsafe { bindings::netlink_remove_tap(tap) })?; + this.registered =3D false; + + // SAFETY: The tap has been removed and `netlink_remove_tap` waite= d for in-flight users via + // `synchronize_net`, so restoring the zeroed unregistered state i= s valid. + unsafe { tap.write(core::mem::zeroed()) }; + Ok(()) + } +} diff --git a/rust/kernel/net/rtnl.rs b/rust/kernel/net/rtnl.rs new file mode 100644 index 000000000000..f3bddd29874f --- /dev/null +++ b/rust/kernel/net/rtnl.rs @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! RTNL link-type registration support. +//! +//! C headers: +//! - [`include/net/rtnetlink.h`](srctree/include/net/rtnetlink.h) +//! - [`include/uapi/linux/if_link.h`](srctree/include/uapi/linux/if_link.= h) + +use crate::{ + bindings, + error::{from_result, to_result}, + net::netdevice, + prelude::*, + types::Opaque, +}; + +use core::{ + marker::PhantomData, + mem::size_of, +}; + +/// Typed link-level netlink attribute selector. +#[derive(Clone, Copy)] +pub struct LinkAttr(usize); + +impl LinkAttr { + /// Equivalent to `IFLA_ADDRESS`. + pub const ADDRESS: Self =3D Self(bindings::IFLA_ADDRESS as usize); + + const fn as_index(self) -> usize { + self.0 + } +} + +/// Typed info-level netlink attribute selector. +#[derive(Clone, Copy)] +pub struct InfoAttr(usize); + +impl InfoAttr { + /// Equivalent to `IFLA_INFO_KIND`. + pub const KIND: Self =3D Self(bindings::IFLA_INFO_KIND as usize); + + /// Equivalent to `IFLA_INFO_DATA`. + pub const DATA: Self =3D Self(bindings::IFLA_INFO_DATA as usize); + + const fn as_index(self) -> usize { + self.0 + } +} + +const LINK_ATTR_TABLE_LEN: usize =3D bindings::__IFLA_MAX as usize; +const INFO_ATTR_TABLE_LEN: usize =3D bindings::__IFLA_INFO_MAX as usize; + +/// Validation context for `rtnl_link_ops::validate`. +pub struct ValidateContext<'a> { + link_attrs: NlAttrTable, + data_attrs: NlAttrTable, + extack: Option<&'a mut ExtAck>, +} + +impl<'a> ValidateContext<'a> { + fn new( + link_attrs: NlAttrTable, + data_attrs: NlAttrTable, + extack: Option<&'a mut ExtAck>, + ) -> Self { + Self { + link_attrs, + data_attrs, + extack, + } + } + + /// Returns whether a link-level netlink attribute is present. + pub fn has_link_attr(&self, attr: LinkAttr) -> bool { + self.link_attrs.has_attr_index(attr.as_index()) + } + + /// Returns whether an info-level netlink attribute is present. + pub fn has_info_attr(&self, attr: InfoAttr) -> bool { + self.data_attrs.has_attr_index(attr.as_index()) + } + + /// Returns the optional extack wrapper. + pub fn extack(&mut self) -> Option<&mut ExtAck> { + self.extack.as_deref_mut() + } +} + +/// Safe view over the `struct nlattr *tb[]` / `data[]` arrays passed to v= alidate callbacks. +#[derive(Clone, Copy)] +pub struct NlAttrTable { + raw: *mut *mut bindings::nlattr, + len: usize, +} + +impl NlAttrTable { + fn new(raw: *mut *mut bindings::nlattr, len: usize) -> Self { + Self { raw, len } + } + + /// Returns `true` if the attribute slot contains a non-null pointer. + fn has_attr_index(&self, attr: usize) -> bool { + if self.raw.is_null() || attr >=3D self.len { + return false; + } + + // SAFETY: The RTNL core provides these arrays for validate callba= cks. Indexing is kept in + // the abstraction layer so driver code does not perform raw point= er arithmetic. + unsafe { !(*self.raw.add(attr)).is_null() } + } + +} + +/// Wrapper over `struct netlink_ext_ack`. +#[repr(transparent)] +pub struct ExtAck(Opaque); + +impl ExtAck { + /// Creates a mutable wrapper from a raw extack pointer. + /// + /// # Safety + /// + /// The pointer must be valid for the returned lifetime. + pub unsafe fn from_raw<'a>(ptr: *mut bindings::netlink_ext_ack) -> &'a= mut Self { + let ptr =3D ptr.cast::(); + // SAFETY: The caller guarantees validity for the returned lifetim= e. + unsafe { &mut *ptr } + } +} + +/// A Rust RTNL link-type driver. +pub trait Driver: netdevice::Operations { + /// The RTNL link kind, e.g. `"nlmon"`. + const KIND: &'static CStr; + + /// Performs link-type setup. + fn setup(dev: &mut netdevice::Device); + + /// Optional netlink validation. + fn validate(_ctx: &mut ValidateContext<'_>) -> Result { + Ok(()) + } +} + +/// Owns an RTNL link-type registration. +#[pin_data(PinnedDrop)] +pub struct Registration { + #[pin] + ops: Opaque, + _driver: PhantomData, +} + +// SAFETY: Shared references do not expose interior mutation beyond drop s= emantics. +unsafe impl Sync for Registration {} + +// SAFETY: Registration and unregistration are handled by RTNL core code a= nd can be performed from +// the module init/exit path. +unsafe impl Send for Registration {} + +impl Registration { + extern "C" fn setup_callback(dev: *mut bindings::net_device) { + // SAFETY: The RTNL core only calls setup with a valid `net_device= `. + let dev =3D unsafe { netdevice::Device::from_raw(dev) }; + dev.set_netdevice_ops::(); + T::setup(dev); + } + + extern "C" fn validate_callback( + tb: *mut *mut bindings::nlattr, + data: *mut *mut bindings::nlattr, + extack: *mut bindings::netlink_ext_ack, + ) -> c_int { + from_result(|| { + let extack =3D if extack.is_null() { + None + } else { + // SAFETY: The RTNL core passes a valid extack pointer whe= n non-null. + Some(unsafe { ExtAck::from_raw(extack) }) + }; + let mut ctx =3D ValidateContext::new( + NlAttrTable::new(tb, LINK_ATTR_TABLE_LEN), + NlAttrTable::new(data, INFO_ATTR_TABLE_LEN), + extack, + ); + T::validate(&mut ctx)?; + Ok(0) + }) + } + + /// Creates a new RTNL registration object. + pub fn new() -> impl PinInit { + build_assert!(!core::mem::needs_drop::()); + try_pin_init!(Self { + ops <- Opaque::try_ffi_init(|ptr: *mut bindings::rtnl_link_ops= | { + // SAFETY: All-zero is a valid initial state for the opaqu= e RTNL ops structure. + unsafe { ptr.write(core::mem::zeroed()) }; + + // SAFETY: `ptr` is valid for writes for the duration of t= his initializer. + unsafe { + (*ptr).kind =3D T::KIND.as_char_ptr(); + (*ptr).priv_size =3D size_of::(); + (*ptr).setup =3D Some(Self::setup_callback); + (*ptr).validate =3D Some(Self::validate_callback); + } + + // SAFETY: `ptr` now points to a fully initialized `rtnl_l= ink_ops`. + to_result(unsafe { bindings::rtnl_link_register(ptr) }) + }), + _driver: PhantomData, + }) + } +} + +#[pinned_drop] +impl PinnedDrop for Registration { + fn drop(self: Pin<&mut Self>) { + // SAFETY: The existence of `self` guarantees a successful earlier= registration. + unsafe { bindings::rtnl_link_unregister(self.ops.get()) }; + } +} --=20 2.34.1 From nobody Sun Jun 21 16:16:43 2026 Received: from mail-m49197.qiye.163.com (mail-m49197.qiye.163.com [45.254.49.197]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id F1D563803DA; Thu, 2 Apr 2026 23:27:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=45.254.49.197 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775172433; cv=none; b=SFi4ygHxptGe6hN3CpF1uWTqq9tVgJN8T/BWFkBjbr5nyTS+tgSvnBAWzeN3CWNZFx758aIlrSrUCc7OIrbj+ASXlkKD3yup4ZmBH7ovPGxzeV/MARLzs+239GhsVhasTlHmnNA7ynF3tirmnzlqwRHFVEScMUh7UXzeG9K1zlE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775172433; c=relaxed/simple; bh=d0FCQBwWQcmetfvZotdvIkyZucvxpGqFc03v86p1wio=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=QtgoUfTxxVu2FzZS9S1/OmWNrhTjbOGkjFRdLxt1frONDJT2RA8yOAWhvNdx2jmmh4bGbAj1aa9/RW0f4aEysgOd9DKbXTegmjklkIUAVqvMBZvFsjI82BIyqm5trLFBmOTq8YfSIGNzC2w6HJSbgolaxBRZKZzfIoQzB9aqb/w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn; spf=pass smtp.mailfrom=ruc.edu.cn; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b=XIY/dR7M; arc=none smtp.client-ip=45.254.49.197 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b="XIY/dR7M" Received: from lwz.tail698a0e.ts.net (gy-adaptive-ssl-proxy-4-entmail-virt151.gy.ntes [36.112.3.244]) by smtp.qiye.163.com (Hmail) with ESMTP id 3956a559c; Fri, 3 Apr 2026 00:37:22 +0800 (GMT+08:00) From: Wenzhao Liao To: rust-for-linux@vger.kernel.org, netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com Subject: [RFC PATCH 5/6] net: add Rust reference driver for nlmon Date: Thu, 2 Apr 2026 12:36:39 -0400 Message-Id: <20260402163640.1079056-6-wenzhaoliao@ruc.edu.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> References: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-HM-Tid: 0a9d4f0e7b4603a2kunm6754def456bf61 X-HM-MType: 10 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFITzdXWS1ZQUlXWQ8JGhUIEh9ZQVlCGUhOVh5IQ0oeT09JTUkYHVYeHw5VEwETFhoSFy QUDg9ZV1kYEgtZQVlITVVKSklVSFVJT09ZV1kWGg8SFR0UWUFZT0tIVUpLSEpOTE5VSktLVUpCS0 tZBg++ DKIM-Signature: a=rsa-sha256; b=XIY/dR7MMs7qOrRoufyt5GJaJxx1M1V1MhmUy7v3PXBNK5kORbZCoW/sIbl0CJ9whQ0+86ucuCKp099KWq40Ou0Hf0L1l5YI4LlG6INtLkQj7a225NgNgBrDv2zQ11zOBrchRMUWPXN3zjhmEBxdS2LH6zbW6U7FwF1seACy4gs=; c=relaxed/relaxed; s=default; d=ruc.edu.cn; v=1; bh=MwU5JMyxAfcaZO4WV+hoTg6Dq0CRM52eh9oDblRZKtg=; h=date:mime-version:subject:message-id:from; Content-Type: text/plain; charset="utf-8" Implement nlmon as a small Rust reference driver on top of the new networking abstractions. nlmon is a good validation target because it is netlink-centric and exercises rtnl registration, netdevice setup, tap lifecycle, and packet stats without hardware dependencies. Signed-off-by: Wenzhao Liao --- drivers/net/Kconfig | 9 ++++ drivers/net/Makefile | 6 ++- drivers/net/nlmon_rust.rs | 93 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 drivers/net/nlmon_rust.rs diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 17108c359216..0eccd0af8b75 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -466,6 +466,15 @@ config NLMON diagnostics, etc. This is mostly intended for developers or support to debug netlink issues. If unsure, say N. =20 +config NLMON_RUST + bool "Rust implementation of nlmon" + depends on RUST && NLMON + help + Select this option to build the Rust implementation of + the nlmon driver instead of the original C version. + This keeps a small reference driver available while the + supporting Rust networking abstractions are reviewed. + config NETKIT bool "BPF-programmable network device" depends on BPF_SYSCALL diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 5b01215f6829..5d3357d56cc8 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -38,7 +38,11 @@ obj-$(CONFIG_VXLAN) +=3D vxlan/ obj-$(CONFIG_GENEVE) +=3D geneve.o obj-$(CONFIG_BAREUDP) +=3D bareudp.o obj-$(CONFIG_GTP) +=3D gtp.o -obj-$(CONFIG_NLMON) +=3D nlmon.o +ifdef CONFIG_NLMON_RUST + obj-$(CONFIG_NLMON) +=3D nlmon_rust.o +else + obj-$(CONFIG_NLMON) +=3D nlmon.o +endif obj-$(CONFIG_PFCP) +=3D pfcp.o obj-$(CONFIG_NET_VRF) +=3D vrf.o obj-$(CONFIG_VSOCKMON) +=3D vsockmon.o diff --git a/drivers/net/nlmon_rust.rs b/drivers/net/nlmon_rust.rs new file mode 100644 index 000000000000..d10e320c4635 --- /dev/null +++ b/drivers/net/nlmon_rust.rs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 + +#![forbid(unsafe_code)] + +//! Rust netlink monitoring device. +//! +//! C version of this driver: [`drivers/net/nlmon.c`](./nlmon.c) + +use kernel::{ + net::{netdevice, netlink_tap, rtnl, skbuff, stats}, + prelude::*, +}; + +module! { + type: NlmonModule, + name: "nlmon_rust", + authors: [ + "Daniel Borkmann ", + "Mathieu Geli ", + ], + description: "Rust netlink monitoring device", + license: "GPL v2", + alias: ["rtnl-link-nlmon"], +} + +#[pin_data] +struct NlmonModule { + #[pin] + registration: rtnl::Registration, +} + +impl kernel::InPlaceModule for NlmonModule { + fn init(_module: &'static ThisModule) -> impl PinInit { + try_pin_init!(Self { + registration <- rtnl::Registration::new(), + }) + } +} + +struct NlmonDriver; + +#[pin_data] +#[derive(Zeroable)] +#[repr(C)] +struct NlmonPriv { + #[pin] + tap: netlink_tap::Tap, +} + +impl netdevice::Operations for NlmonDriver { + type Private =3D NlmonPriv; + + fn open(dev: &mut netdevice::Device, private: Pin<&mut Self::Private>)= -> Result { + private.project().tap.add(dev, &THIS_MODULE) + } + + fn stop(_dev: &mut netdevice::Device, private: Pin<&mut Self::Private>= ) -> Result { + private.project().tap.remove() + } + + fn start_xmit(skb: skbuff::SkBuff, dev: &netdevice::Device) -> netdevi= ce::TxOutcome { + stats::dev_lstats_add(dev, skb.len()); + netdevice::TxOutcome::Ok + } +} + +impl rtnl::Driver for NlmonDriver { + const KIND: &'static CStr =3D c"nlmon"; + + fn setup(dev: &mut netdevice::Device) { + let features =3D netdevice::features::SG + | netdevice::features::FRAGLIST + | netdevice::features::HIGHDMA; + + dev.set_type(netdevice::device_type::NETLINK); + dev.add_priv_flag(netdevice::priv_flags::NO_QUEUE); + dev.set_lltx(true); + dev.set_needs_free_netdev(true); + dev.set_features(features); + dev.set_flags(netdevice::flags::NO_ARP); + dev.set_pcpu_stat_type(netdevice::pcpu_stat_type::LSTATS); + dev.set_mtu(netdevice::mtu::nlmsg_goodsize()); + dev.set_min_mtu(netdevice::mtu::NLMSGHDR); + } + + fn validate(ctx: &mut rtnl::ValidateContext<'_>) -> Result { + if ctx.has_link_attr(rtnl::LinkAttr::ADDRESS) { + return Err(EINVAL); + } + + Ok(()) + } +} --=20 2.34.1 From nobody Sun Jun 21 16:16:43 2026 Received: from mail-m83187.xmail.ntesmail.com (mail-m83187.xmail.ntesmail.com [156.224.83.187]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BA1413A5431; Thu, 2 Apr 2026 21:13:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=156.224.83.187 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775164425; cv=none; b=CHCPmjGX6kFZ34O5vq9/dJ10oLR/Juu1pNN6xevDSPqbnTih/Z+Qb1WDpsHbRhTao+9YabJMJGRfTtoGz+Ar2qowCsB5cBTgp3FHunkyOAOyGxUd0A1MvhplhCJ04eWX61GynsU4NGddZMD1XJzlT26DgZCVEqmWOCSVoB7yStw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1775164425; c=relaxed/simple; bh=mq1qNMND5KoaoFkHmrroSqLwyeymKan5QEYrdceyQKw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=XmwxfICWFj6v/cForY5ErEmp//6XA1PJ51NoXCidbhQd+WcSMQd2YKJyIKvD+V/Mbaj+4t7DJ/63SH5x+fS3gyv+gO00LS1NVTwiSwBE6M0JOofbeuiP4sEYiR+pmLeqMMyWvPS+La2PdiIaBO1cnGDgNiGKaVZDNx3YhpDGAI8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn; spf=pass smtp.mailfrom=ruc.edu.cn; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b=RFna+KE+; arc=none smtp.client-ip=156.224.83.187 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ruc.edu.cn Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=ruc.edu.cn header.i=@ruc.edu.cn header.b="RFna+KE+" Received: from lwz.tail698a0e.ts.net (gy-adaptive-ssl-proxy-4-entmail-virt151.gy.ntes [36.112.3.244]) by smtp.qiye.163.com (Hmail) with ESMTP id 3956a559d; Fri, 3 Apr 2026 00:37:28 +0800 (GMT+08:00) From: Wenzhao Liao To: rust-for-linux@vger.kernel.org, netdev@vger.kernel.org Cc: linux-kernel@vger.kernel.org, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com Subject: [RFC PATCH 6/6] MAINTAINERS: add Rust net and nlmon entries Date: Thu, 2 Apr 2026 12:36:40 -0400 Message-Id: <20260402163640.1079056-7-wenzhaoliao@ruc.edu.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> References: <20260402163640.1079056-1-wenzhaoliao@ruc.edu.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-HM-Tid: 0a9d4f0e93eb03a2kunm6754def456bf74 X-HM-MType: 10 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFITzdXWS1ZQUlXWQ8JGhUIEh9ZQVkaTR1JVhgeSkhDThgeHRhJQ1YeHw5VEwETFhoSFy QUDg9ZV1kYEgtZQVlITVVKSklVSFVJT09ZV1kWGg8SFR0UWUFZT0tIVUpLSEpOTE5VSktLVUpCS0 tZBg++ DKIM-Signature: a=rsa-sha256; b=RFna+KE+V472iTrrDSAC/I/BvoC/Z3mD4LVKkw/c/WUSrdG8vy7sKm+S7aOF/Mje+ldx4jb00cKodLuruv6tkQLRpp8zeb1m3nbBsh0ZNyK9EkolV9OeChsFPw+njE/xQ/TsjArlYVH9FTNgAdsPlykRVnF9vZvAvc2A/oj4cNQ=; c=relaxed/relaxed; s=default; d=ruc.edu.cn; v=1; bh=0RXe8FftK9+WQ4CtVqc+7/yKc01o6MTUrQjVs8zTSg4=; h=date:mime-version:subject:message-id:from; Content-Type: text/plain; charset="utf-8" Add scoped maintainer entries for the new Rust networking abstraction files and for the Rust nlmon reference driver. This keeps future changes routed to the networking and Rust mailing lists while also giving the new code paths a direct maintainer contact. Signed-off-by: Wenzhao Liao --- MAINTAINERS | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 77fdfcb55f06..d1c3eb61ea46 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18335,6 +18335,25 @@ X: Documentation/devicetree/bindings/net/wireless/ X: drivers/net/can/ X: drivers/net/wireless/ =20 +NETWORKING LINK-TYPE ABSTRACTIONS [RUST] +M: Wenzhao Liao +L: netdev@vger.kernel.org +L: rust-for-linux@vger.kernel.org +S: Maintained +F: rust/kernel/net.rs +F: rust/kernel/net/netdevice.rs +F: rust/kernel/net/netlink_tap.rs +F: rust/kernel/net/rtnl.rs +F: rust/kernel/net/skbuff.rs +F: rust/kernel/net/stats.rs + +NLMON DRIVER [RUST] +M: Wenzhao Liao +L: netdev@vger.kernel.org +L: rust-for-linux@vger.kernel.org +S: Maintained +F: drivers/net/nlmon_rust.rs + NETWORKING DRIVERS (WIRELESS) M: Johannes Berg L: linux-wireless@vger.kernel.org --=20 2.34.1