Skip to content

Rust NetDevice and NetDeviceOperationsVtable struct #208

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions drivers/net/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ config DUMMY
To compile this driver as a module, choose M here: the module
will be called dummy.

config DUMMY_RS
tristate "Dummy net driver support"
depends on RUST
help
This is essentially a bit-bucket device (i.e. traffic you send to
this device is consigned into oblivion) with a configurable IP
address. It is most commonly used in order to make your currently
inactive SLIP address seem like a real address for local programs.
If you use SLIP or PPP, you might want to say Y here. It won't
enlarge your kernel. What a deal. Read about it in the Network
Administrator's Guide, available from
<http://www.tldp.org/docs.html#guide>.

To compile this driver as a module, choose M here: the module
will be called dummy_rs.

config WIREGUARD
tristate "WireGuard secure network tunnel"
depends on NET && INET
Expand Down
1 change: 1 addition & 0 deletions drivers/net/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_IPVLAN) += ipvlan/
obj-$(CONFIG_IPVTAP) += ipvlan/
obj-$(CONFIG_DUMMY) += dummy.o
obj-$(CONFIG_DUMMY_RS) += dummy_rs.o
obj-$(CONFIG_WIREGUARD) += wireguard/
obj-$(CONFIG_EQUALIZER) += eql.o
obj-$(CONFIG_IFB) += ifb.o
Expand Down
235 changes: 235 additions & 0 deletions drivers/net/dummy_rs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0

//! Rust dummy network driver
//!
//! This is a demonstration of what a small driver looks like in Rust, based on drivers/net/dummy.c.
//! This code is provided as a demonstration only, not as a proposal to mass-rewrite existing drivers in Rust
Comment on lines +5 to +6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment on #254 (comment).

I guess we can talk about this tomorrow in the call :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that was the wording of @joshtriplett, while talking about this at our last call.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not fixated on the exact wording, I just want to make sure that people don't see a Rust version of an existing C driver and assume we're trying to rewrite the world in Rust. In this context, we're providing samples of what drivers would look like; we're not writing replacements.

In the future, some driver maintainers may choose to use Rust in existing drivers, but that's up to those driver maintainers, not us.

//!
//! The purpose of this driver is to provide a device to point a
//! route through, but not to actually transmit packets.
//!
//! Why? If you have a machine whose only connection is an occasional
//! PPP/SLIP/PLIP link, you can only connect to your own hostname
//! when the link is up. Otherwise you have to use localhost.
//! This isn't very consistent.
//!
//! One solution is to set up a dummy link using PPP/SLIP/PLIP,
//! but this seems (to me) too much overhead for too little gain.
//! This driver provides a small alternative. Thus you can do
//!
//! [when not running slip]
//! ifconfig dummy slip.addr.ess.here up
//! [to go to slip]
//! ifconfig dummy down
//! dip whatever
//!
//! This was written by looking at the dummy network driver from Nick
//! Holloway, which was written by looking at Donald Becker's skeleton driver
//! and the loopback driver.
//!
//! Finn Behrens, 30th April 2021
//!
//! rust rewrite of the C version from Nick Holloway, 27th May 1994
//! see [dummy.c](./dummy.c)

#![no_std]
#![feature(allocator_api, global_asm)]

use kernel::net::device;
use kernel::net::prelude::*;
use kernel::net::rtnl;
use kernel::Error;
use kernel::{
net::netlink::{NlAttrVec, NlExtAck},
prelude::*,
};

module! {
type: RustNetDummy,
name: b"dummy_rs",
author: b"Rust for Linux Contributors",
description: b"Rust dummy network driver",
license: b"GPL v2",
alias_rtnl_link: b"dummy_rs",
params: {
numdummies: usize {
default: 0,
permissions: 0,
description: b"Number of dummy_rs pseudo devices",
},
},
}

fn setup(dev: &mut NetDevice<DummyRsDev>) {
dev.ether_setup();

dev.set_ops();

// Fill in device structure with ethernet-generic values.
dev.add_flag(device::Iff::NOARP);
dev.remove_flag(device::Iff::MULTICAST);

dev.add_private_flag(device::IffPriv::LIVE_ADDR_CHANGE);
dev.add_private_flag(device::IffPriv::NO_QUEUE);

let mut feature = device::feature::NetIF::new();

feature += device::feature::NETIF_F_SG;
feature += device::feature::NETIF_F_FRAGLIST;
feature += device::feature::NETIF_F_GSO_SOFTWARE;
feature += device::feature::NETIF_F_HW_CSUM;
feature += device::feature::NETIF_F_HIGHDMA;
feature += device::feature::NETIF_F_LLTX;
feature += device::feature::NETIF_F_GSO_ENCAP_ALL;

dev.set_features(feature);
dev.set_hw_features(feature);
dev.set_hw_enc_features(feature);

dev.hw_addr_random();
dev.set_mtu(0, 0);
}

fn validate(tb: &NlAttrVec, _data: &NlAttrVec, _ext_ack: &NlExtAck) -> Result {
if let Some(addr) = tb.get(kernel::bindings::IFLA_ADDRESS) {
if Some(kernel::net::netlink::ETH_ALEN) != addr.nla_len() {
return Err(Error::EINVAL);
}
if !addr.is_valid_ether_addr() {
return Err(Error::EADDRNOTAVAIL);
}
}
Ok(())
}

rtnl_link_ops! {
kind: b"dummy_rs",
type: DummyRsDev,
setup: setup,
validate: validate,
}

struct RustNetDummy {
//dev: NetDevice<DummyRsDev>,
}

impl KernelModule for RustNetDummy {
fn init() -> Result<Self> {
let num = *numdummies.read();

unsafe { dummy_rs_link_ops.register() }?;

for _ in 0..(num) {
let dev = NetDevice::new(
DummyRsDev,
kernel::c_str!("dummyrs%d"),
kernel::net::device::NetNameAssingType::Enum,
1,
1,
)?;
dev.set_rtnl_ops(unsafe { &dummy_rs_link_ops });

if let Err(e) = dev.register() {
pr_warn!("could not register: {}", e.to_kernel_errno());
return Err(e);
}
}

Ok(RustNetDummy {
//dev,
})
}
}

impl Drop for RustNetDummy {
fn drop(&mut self) {
// TODO: remove unsafe somehow
unsafe { dummy_rs_link_ops.unregister() };
}
}

struct DummyRsDev;

impl NetDeviceOps<Self> for DummyRsDev {
kernel::declare_net_device_ops!(
get_stats64,
change_carrier,
validate_addr,
set_mac_addr,
set_rx_mode
);

fn init(dev: &mut NetDevice<Self>) -> Result {
dev.set_new_pcpu_lstats()?;
Ok(())
}

fn uninit(dev: &mut NetDevice<Self>) {
unsafe { dev.free_lstats() };
}

fn start_xmit(skb: SkBuff, dev: &mut NetDevice<Self>) -> kernel::net::device::NetdevTX {
let mut skb = skb;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use fn start_xmit(mut skb: SkBuff, ... and remove the let mut skb = skb;.


dev.lstats_add(skb.len());

skb.tx_timestamp();
drop(skb);

kernel::net::device::NetdevTX::TX_OK
}

fn get_stats64(dev: &NetDevice<Self>, stats: &mut rtnl::RtnlLinkStats64) {
stats.dev_read(dev);
}

fn change_carrier(dev: &mut NetDevice<Self>, new_carrier: bool) -> Result {
dev.carrier_set(new_carrier);

Ok(())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this return a Result if it can't fail?


fn validate_addr(dev: &NetDevice<Self>) -> Result {
device::helpers::eth_validate_addr(dev)
}

fn set_mac_addr(dev: &mut NetDevice<Self>, p: *mut kernel::c_types::c_void) -> Result {
device::helpers::eth_mac_addr(dev, p)
}

// [Someting about faking multicast](https://elixir.bootlin.com/linux/v5.12-rc4/source/drivers/net/dummy.c#L48).
fn set_rx_mode(_dev: &mut NetDevice<Self>) {}
}

impl NetDeviceAdapter for DummyRsDev {
type Inner = Self;

type Ops = Self;

type EthOps = Self;

fn setup(dev: &mut NetDevice<Self>) {
setup(dev);
}
}

impl EthToolOps<Self> for DummyRsDev {
kernel::declare_eth_tool_ops!(get_drvinfo, get_ts_info);

fn get_drvinfo(_dev: &NetDevice<Self>, info: &mut ethtool::EthtoolDrvinfo) {
// TODO: how to do this more efficient without unsafe?
// FIXME: !!
let info: &kernel::bindings::ethtool_drvinfo = info.get_internal();
unsafe {
kernel::bindings::strlcpy(
&(info.driver) as *const _ as *mut i8,
b"dummy_rs\0" as *const _ as *mut i8,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could use the cstr! macro here

32,
);
}
}

fn get_ts_info(dev: &NetDevice<Self>, info: &mut ethtool::EthToolTsInfo) -> Result {
kernel::net::ethtool::helpers::ethtool_op_get_ts_info(dev, info)
}
}
33 changes: 32 additions & 1 deletion rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
#include <linux/highmem.h>
#include <linux/uio.h>
#include <linux/errname.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>

void rust_helper_BUG(void)
{
Expand Down Expand Up @@ -123,6 +126,34 @@ const char *rust_helper_errname(int err)
return errname(err);
}

void *rust_helper_netdev_priv(struct net_device *dev)
{
return netdev_priv(dev);
}
EXPORT_SYMBOL_GPL(rust_helper_netdev_priv);

void rust_helper_eth_hw_addr_random(struct net_device *dev)
{
eth_hw_addr_random(dev);
}
EXPORT_SYMBOL_GPL(rust_helper_eth_hw_addr_random);

int rust_helper_net_device_set_new_lstats(struct net_device *dev)
{
dev->lstats = netdev_alloc_pcpu_stats(struct pcpu_lstats);
if (!dev->lstats)
return -ENOMEM;

return 0;
}
EXPORT_SYMBOL_GPL(rust_helper_net_device_set_new_lstats);

void rust_helper_dev_lstats_add(struct net_device *dev, unsigned int len)
{
dev_lstats_add(dev, len);
}
EXPORT_SYMBOL_GPL(rust_helper_dev_lstats_add);

/* We use bindgen's --size_t-is-usize option to bind the C size_t type
* as the Rust usize type, so we can use it in contexts where Rust
* expects a usize like slice (array) indices. usize is defined to be
Expand All @@ -143,4 +174,4 @@ static_assert(
sizeof(size_t) == sizeof(uintptr_t) &&
__alignof__(size_t) == __alignof__(uintptr_t),
"Rust code expects C size_t to match Rust usize"
);
);
8 changes: 8 additions & 0 deletions rust/kernel/bindings_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
#include <linux/cdev.h>
#include <linux/errname.h>
#include <linux/fs.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/netdev_features.h>
#include <linux/rtnetlink.h>
#include <net/rtnetlink.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/slab.h>
Expand All @@ -21,3 +27,5 @@
// `bindgen` gets confused at certain things
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;
const gfp_t BINDINGS___GFP_ZERO = __GFP_ZERO;

const int BINDINGS_NLA_HDRLEN = NLA_HDRLEN;
5 changes: 4 additions & 1 deletion rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ impl Error {
/// Bad file number.
pub const EBADF: Self = Error(-(bindings::EBADF as i32));

/// Cannot assign requested address
pub const EADDRNOTAVAIL: Self = Error(-(bindings::EADDRNOTAVAIL as i32));

/// Creates an [`Error`] from a kernel error code.
///
/// It is a bug to pass an out-of-range `errno`. `EINVAL` would
Expand Down Expand Up @@ -204,7 +207,7 @@ where
#[macro_export]
macro_rules! from_kernel_result {
($($tt:tt)*) => {{
$crate::error::from_kernel_result_helper((|| {
$crate::from_kernel_result_helper((|| {
$($tt)*
})())
}};
Expand Down
4 changes: 4 additions & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mod error;
pub mod file;
pub mod file_operations;
pub mod miscdev;
pub mod net;
pub mod pages;
pub mod str;
pub mod traits;
Expand Down Expand Up @@ -76,6 +77,9 @@ pub mod user_ptr;
#[doc(hidden)]
pub use build_error::build_error;

#[doc(hidden)]
pub use crate::error::from_kernel_result_helper;

pub use crate::error::{Error, Result};
pub use crate::types::{Mode, ScopeGuard};

Expand Down
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.