[RFC PATCH 5/6] net: add Rust reference driver for nlmon

From: Wenzhao Liao

Date: Thu Apr 02 2026 - 19:31:16 EST


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 <wenzhaoliao@xxxxxxxxxx>
---
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.

+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) += vxlan/
obj-$(CONFIG_GENEVE) += geneve.o
obj-$(CONFIG_BAREUDP) += bareudp.o
obj-$(CONFIG_GTP) += gtp.o
-obj-$(CONFIG_NLMON) += nlmon.o
+ifdef CONFIG_NLMON_RUST
+ obj-$(CONFIG_NLMON) += nlmon_rust.o
+else
+ obj-$(CONFIG_NLMON) += nlmon.o
+endif
obj-$(CONFIG_PFCP) += pfcp.o
obj-$(CONFIG_NET_VRF) += vrf.o
obj-$(CONFIG_VSOCKMON) += 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 <dborkman@xxxxxxxxxx>",
+ "Mathieu Geli <geli@xxxxxxxxxx>",
+ ],
+ description: "Rust netlink monitoring device",
+ license: "GPL v2",
+ alias: ["rtnl-link-nlmon"],
+}
+
+#[pin_data]
+struct NlmonModule {
+ #[pin]
+ registration: rtnl::Registration<NlmonDriver>,
+}
+
+impl kernel::InPlaceModule for NlmonModule {
+ fn init(_module: &'static ThisModule) -> impl PinInit<Self, Error> {
+ 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 = 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) -> netdevice::TxOutcome {
+ stats::dev_lstats_add(dev, skb.len());
+ netdevice::TxOutcome::Ok
+ }
+}
+
+impl rtnl::Driver for NlmonDriver {
+ const KIND: &'static CStr = c"nlmon";
+
+ fn setup(dev: &mut netdevice::Device) {
+ let features = 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(())
+ }
+}
--
2.34.1