Skip to content

Commit

Permalink
Merge pull request Rust-for-Linux#360 from Rust-for-Linux/alex-patch-1
Browse files Browse the repository at this point in the history
ci: retry add-apt-repository to deal with network issues
  • Loading branch information
ojeda authored and kloenk committed Jun 6, 2021
2 parents bfe9ec6 + a8a369c commit 8185326
Show file tree
Hide file tree
Showing 18 changed files with 2,377 additions and 3 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,11 @@ jobs:
# Setup: LLVM
- run: curl https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
- run: sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main'
# Retry to be resilient to intermittent network issues
- run: |
sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' ||
sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main' ||
sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-12 main'
- run: sudo apt-get update -y
- run: sudo apt-get install -y llvm-12 clang-12 lld-12
- run: echo $(llvm-config-12 --bindir) >> $GITHUB_PATH
Expand Down
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
//!
//! 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;

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(())
}

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,
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

0 comments on commit 8185326

Please sign in to comment.