Skip to content

Commit 14ea91c

Browse files
committed
Rust NetDevice and NetDeviceOperationsVtable struct
also adds drivers/net/dummy_rs.rs Signed-off-by: Finn Behrens <[email protected]>
1 parent 77c5ca1 commit 14ea91c

14 files changed

+2157
-3
lines changed

Diff for: drivers/net/Kconfig

+16
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,22 @@ config DUMMY
7272
To compile this driver as a module, choose M here: the module
7373
will be called dummy.
7474

75+
config DUMMY_RS
76+
tristate "Dummy net driver support"
77+
depends on HAS_RUST
78+
help
79+
This is essentially a bit-bucket device (i.e. traffic you send to
80+
this device is consigned into oblivion) with a configurable IP
81+
address. It is most commonly used in order to make your currently
82+
inactive SLIP address seem like a real address for local programs.
83+
If you use SLIP or PPP, you might want to say Y here. It won't
84+
enlarge your kernel. What a deal. Read about it in the Network
85+
Administrator's Guide, available from
86+
<http://www.tldp.org/docs.html#guide>.
87+
88+
To compile this driver as a module, choose M here: the module
89+
will be called dummy_rs.
90+
7591
config WIREGUARD
7692
tristate "WireGuard secure network tunnel"
7793
depends on NET && INET

Diff for: drivers/net/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ obj-$(CONFIG_BONDING) += bonding/
1010
obj-$(CONFIG_IPVLAN) += ipvlan/
1111
obj-$(CONFIG_IPVTAP) += ipvlan/
1212
obj-$(CONFIG_DUMMY) += dummy.o
13+
obj-$(CONFIG_DUMMY_RS) += dummy_rs.o
1314
obj-$(CONFIG_WIREGUARD) += wireguard/
1415
obj-$(CONFIG_EQUALIZER) += eql.o
1516
obj-$(CONFIG_IFB) += ifb.o

Diff for: drivers/net/dummy_rs.rs

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Rust dummy network driver
4+
//!
5+
//! This is a demonstration of what a small driver looks like in Rust, based on drivers/net/dummy.c.
6+
//! This code is provided as a demonstration only, not as a proposal to mass-rewrite existing drivers in Rust
7+
8+
// TODO: copyright (see ./dummy.c)
9+
10+
11+
#![no_std]
12+
#![feature(allocator_api, global_asm)]
13+
14+
use core::ops::Deref;
15+
16+
use kernel::{net::netlink::{NlAttrVec, NlExtAck}, prelude::*};
17+
use kernel::net::prelude::*;
18+
use kernel::net::device;
19+
use kernel::net::rtnl;
20+
use kernel::Error;
21+
22+
module! {
23+
type: RustNetDummy,
24+
name: b"dummy_rs",
25+
author: b"Rust for Linux Contributors",
26+
description: b"Rust dummy network driver",
27+
license: b"GPL v2",
28+
alias: b"rtnl-link-dummy_rs",
29+
params: {
30+
numdummies: usize {
31+
default: 0,
32+
permissions: 0o644,
33+
description: b"Number of dummy_rs pseudo devices",
34+
},
35+
},
36+
}
37+
38+
fn setup(dev: &mut NetDevice<DummyRsDev>) {
39+
pr_info!("called rtnl_setup");
40+
dev.ether_setup();
41+
42+
// Fill in device structure with ethernet-generic values.
43+
dev.add_flag(device::Iff::NOARP);
44+
dev.remove_flag(device::Iff::MULTICAST);
45+
46+
dev.add_private_flag(device::IffPriv::LIVE_ADDR_CHANGE);
47+
dev.add_private_flag(device::IffPriv::NO_QUEUE);
48+
49+
let mut feature = device::feature::NetIF::new();
50+
51+
//feature.add(device::feature::NETIF_F_SG);
52+
//feature.add(device::feature::NETIF_F_FRAGLIST_BIT as u64);
53+
feature += device::feature::NETIF_F_SG;
54+
feature += device::feature::NETIF_F_FRAGLIST;
55+
feature += device::feature::NETIF_F_GSO_SOFTWARE;
56+
feature += device::feature::NETIF_F_HW_CSUM;
57+
feature += device::feature::NETIF_F_HIGHDMA;
58+
feature += device::feature::NETIF_F_LLTX;
59+
feature += device::feature::NETIF_F_GSO_ENCAP_ALL;
60+
61+
dev.set_features(feature);
62+
dev.set_hw_features(feature);
63+
dev.set_hw_enc_features(feature);
64+
65+
dev.hw_addr_random();
66+
dev.set_mtu(0, 0);
67+
}
68+
69+
fn validate(tb: &NlAttrVec, data: &NlAttrVec, ext_ack: &NlExtAck) -> KernelResult<()> {
70+
pr_info!("validate nlattr");
71+
if let Some(addr) = tb.get(kernel::bindings::IFLA_ADDRESS) {
72+
if addr.nla_len() != kernel::net::netlink::ETH_ALEN {
73+
return Err(Error::EINVAL);
74+
}
75+
if !addr.is_valid_ether_addr() {
76+
return Err(Error::EADDRNOTAVAIL);
77+
}
78+
} else {
79+
pr_info!("no IFLA_ADDRESS in nlattr");
80+
}
81+
pr_info!("valid nlattr");
82+
Ok(())
83+
}
84+
85+
rtnl_link_ops! {
86+
kind: b"dummy_rs",
87+
type: DummyRsDev,
88+
setup: setup,
89+
validate: validate,
90+
}
91+
92+
struct RustNetDummy {
93+
dev: NetDevice<DummyRsDev>,
94+
}
95+
96+
97+
impl KernelModule for RustNetDummy {
98+
fn init() -> KernelResult<Self> {
99+
{
100+
let lock =THIS_MODULE.kernel_param_lock();
101+
pr_info!("Rust Network Dummy with {} pseudo devices\n", numdummies.read(&lock));
102+
}
103+
104+
unsafe { dummy_rs_rtnl_link_ops.register() }?;
105+
106+
let mut dev = NetDevice::new(DummyRsDev, kernel::cstr!("dummyrs%d"), kernel::net::device::NetNameAssingType::Enum, 1, 1)?;
107+
dev.set_rtnl_ops( unsafe { &dummy_rs_rtnl_link_ops });
108+
109+
if let Err(e) = dev.register() {
110+
pr_warn!("could not register: {}", e.to_kernel_errno());
111+
return Err(e);
112+
}
113+
pr_info!("device registered");
114+
115+
Ok(RustNetDummy {
116+
dev,
117+
})
118+
}
119+
}
120+
121+
impl Drop for RustNetDummy {
122+
fn drop(&mut self) {
123+
pr_info!("remove rust net dummy");
124+
125+
126+
unsafe {
127+
//let ptr = &dummy_rs_rtnl_link_ops as *const _ as *mut kernel::bindings::rtnl_link_ops;
128+
pr_info!("rtnl: {:#?}", &dummy_rs_rtnl_link_ops.0);
129+
let ptr = &dummy_rs_rtnl_link_ops.get_ptr();
130+
pr_info!("rtnl_link_ops_ptr: {:?}", ptr);
131+
}
132+
// TODO rtnl_link_unregister
133+
// TODO: remove unsafe somehow
134+
unsafe { dummy_rs_rtnl_link_ops.unregister() };
135+
}
136+
}
137+
138+
139+
struct DummyRsDev;
140+
141+
impl NetDeviceOps<Self> for DummyRsDev {
142+
kernel::declare_net_device_ops!(
143+
get_stats64,
144+
change_carrier,
145+
validate_addr,
146+
set_mac_addr,
147+
set_rx_mode
148+
);
149+
150+
fn init(dev: &mut NetDevice<Self>) -> KernelResult<()> {
151+
dev.set_new_pcpu_lstats()?;
152+
Ok(())
153+
}
154+
155+
fn uninit(dev: &mut NetDevice<Self>) {
156+
unsafe { dev.free_lstats() };
157+
}
158+
159+
fn start_xmit(skb: SkBuff, dev: &mut NetDevice<Self>) -> kernel::net::device::NetdevTX {
160+
let mut skb = skb;
161+
162+
// TODO: dev_lstatt_add(dev, skb->len)
163+
dev.lstats_add(skb.len());
164+
165+
skb.tx_timestamp();
166+
drop(skb);
167+
168+
pr_info!("start_xmit called");
169+
170+
kernel::net::device::NetdevTX::TX_OK
171+
}
172+
173+
fn get_stats64(dev: &NetDevice<Self>, stats: &mut rtnl::RtnlLinkStats64) {
174+
pr_info!("get stats64");
175+
stats.dev_read(dev);
176+
}
177+
178+
fn change_carrier(dev: &mut NetDevice<Self>, new_carrier: bool) -> KernelResult<()> {
179+
dev.carrier_set(new_carrier);
180+
181+
Ok(())
182+
}
183+
184+
fn validate_addr(dev: &NetDevice<Self>) -> KernelResult<()> {
185+
pr_info!("eth_validate_addr");
186+
device::helpers::eth_validate_addr(dev)
187+
}
188+
189+
fn set_mac_addr(dev: &mut NetDevice<Self>, p: *mut kernel::c_types::c_void) -> KernelResult<()> {
190+
device::helpers::eth_mac_addr(dev, p)
191+
}
192+
193+
// Someting about faking multicast
194+
fn set_rx_mode(dev: &mut NetDevice<Self>) {
195+
pr_info!("set_rx_mode");
196+
}
197+
}
198+
199+
impl NetDeviceAdapter for DummyRsDev {
200+
type Inner = Self;
201+
202+
type Ops = Self;
203+
204+
type EthOps = Self;
205+
206+
fn setup(dev: &mut NetDevice<Self>) {
207+
pr_info!("called netdev_setup");
208+
setup(dev);
209+
//dev.set_rtnl_ops( unsafe { &dummy_rs_rtnl_link_ops });
210+
}
211+
}
212+
213+
impl EthToolOps<Self> for DummyRsDev {
214+
kernel::declare_eth_tool_ops!(get_drvinfo, get_ts_info);
215+
216+
fn get_drvinfo(_dev: &NetDevice<Self>, info: &mut ethtool::EthtoolDrvinfo) {
217+
// TODO: how to do this more efficient without unsafe?
218+
// FIXME: !!
219+
let info: &kernel::bindings::ethtool_drvinfo = info.deref();
220+
unsafe {
221+
kernel::bindings::strlcpy(&(info.driver) as *const _ as *mut i8, b"dummy_rs\0" as *const _ as *mut i8, 32);
222+
}
223+
}
224+
225+
fn get_ts_info(dev: &NetDevice<Self>, info: &mut ethtool::EthToolTsInfo) -> KernelResult<()> {
226+
kernel::net::ethtool::helpers::ethtool_op_get_ts_info(dev, info)
227+
}
228+
}

Diff for: rust/helpers.c

+32-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include <linux/gfp.h>
88
#include <linux/highmem.h>
99
#include <linux/uio.h>
10+
#include <linux/netdevice.h>
11+
#include <linux/etherdevice.h>
12+
#include <linux/rtnetlink.h>
1013

1114
void rust_helper_BUG(void)
1215
{
@@ -105,8 +108,36 @@ size_t rust_helper_copy_to_iter(const void *addr, size_t bytes, struct iov_iter
105108
}
106109
EXPORT_SYMBOL_GPL(rust_helper_copy_to_iter);
107110

108-
#if !defined(CONFIG_ARM)
111+
void *rust_helper_netdev_priv(struct net_device *dev)
112+
{
113+
return netdev_priv(dev);
114+
}
115+
EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);
116+
117+
void rust_helper_eth_hw_addr_random(struct net_device *dev)
118+
{
119+
eth_hw_addr_random(dev);
120+
}
121+
EXPORT_SYMBOL_GPL(rust_helper_eth_hw_addr_random);
122+
123+
int rust_helper_net_device_set_new_lstats(struct net_device *dev)
124+
{
125+
dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats);
126+
if (!dev->lstats)
127+
return -ENOMEM;
128+
129+
return 0;
130+
}
131+
EXPORT_SYMBOL_GPL(rust_helper_net_device_set_new_lstats);
132+
133+
void rust_helper_dev_lstats_add(struct net_device *dev, unsigned int len)
134+
{
135+
dev_lstats_add(dev, len);
136+
}
137+
EXPORT_SYMBOL_GPL(rust_helper_dev_lstats_add);
138+
109139
// See https://github.com/rust-lang/rust-bindgen/issues/1671
140+
#if !defined(CONFIG_ARM)
110141
static_assert(__builtin_types_compatible_p(size_t, uintptr_t),
111142
"size_t must match uintptr_t, what architecture is this??");
112143
#endif

Diff for: rust/kernel/bindings_helper.h

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
#include <linux/cdev.h>
44
#include <linux/fs.h>
5+
#include <linux/netdevice.h>
6+
#include <linux/ethtool.h>
7+
#include <linux/etherdevice.h>
8+
#include <linux/netdev_features.h>
9+
#include <linux/rtnetlink.h>
10+
#include <net/rtnetlink.h>
511
#include <linux/module.h>
612
#include <linux/random.h>
713
#include <linux/slab.h>
@@ -17,3 +23,5 @@
1723
// `bindgen` gets confused at certain things
1824
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
1925
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;
26+
27+
const int BINDINGS_NLA_HDRLEN = NLA_HDRLEN;

Diff for: rust/kernel/error.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
77
use crate::{bindings, c_types};
88
use alloc::{alloc::AllocError, collections::TryReserveError};
9-
use core::{num::TryFromIntError, str::Utf8Error};
9+
use core::{convert::TryFrom, num::TryFromIntError, str::Utf8Error};
1010

1111
/// Generic integer kernel error.
1212
///
@@ -48,6 +48,9 @@ impl Error {
4848
/// Interrupted system call.
4949
pub const EINTR: Self = Error(-(bindings::EINTR as i32));
5050

51+
/// Cannot assign requested address
52+
pub const EADDRNOTAVAIL: Self = Error(-(bindings::EADDRNOTAVAIL as i32));
53+
5154
/// Creates an [`Error`] from a kernel error code.
5255
pub fn from_kernel_errno(errno: c_types::c_int) -> Error {
5356
Error(errno)
@@ -104,3 +107,24 @@ impl From<AllocError> for Error {
104107
Error::ENOMEM
105108
}
106109
}
110+
111+
/// Used by the rtnl_link_ops macro to interface with C
112+
pub fn c_from_kernel_result<T>(r: KernelResult<T>) -> T
113+
where
114+
T: TryFrom<c_types::c_int>,
115+
T::Error: core::fmt::Debug,
116+
{
117+
match r {
118+
Ok(v) => v,
119+
Err(e) => T::try_from(e.to_kernel_errno()).unwrap(),
120+
}
121+
}
122+
123+
#[macro_export]
124+
macro_rules! c_from_kernel_result {
125+
($($tt:tt)*) => {{
126+
$crate::c_from_kernel_result((|| {
127+
$($tt)*
128+
})())
129+
}};
130+
}

Diff for: rust/kernel/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ pub mod c_types;
4141
pub mod chrdev;
4242
mod error;
4343
pub mod file_operations;
44+
pub mod net;
4445
pub mod miscdev;
4546
pub mod pages;
4647

@@ -64,7 +65,7 @@ pub mod iov_iter;
6465
mod types;
6566
pub mod user_ptr;
6667

67-
pub use crate::error::{Error, KernelResult};
68+
pub use crate::error::{Error, KernelResult, c_from_kernel_result};
6869
pub use crate::types::{CStr, Mode};
6970

7071
/// Page size defined in terms of the `PAGE_SHIFT` macro from C.

0 commit comments

Comments
 (0)