From 17e0ddcee309284250bef51b5c1e56341f6d84b8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 11 Oct 2021 20:19:23 +0200 Subject: [PATCH] Add rand module. On `std` targets, `OsRng` is used by default. The user can supply a custom impl by enabling the `rand-custom-impl` Cargo feature and using the `rand_custom_impl!()` macro. Specifying a custom impl is mandatory when `std` is not enabled. --- .github/workflows/test.yml | 6 ++-- Cargo.toml | 4 ++- examples/loopback.rs | 8 +++++ src/lib.rs | 4 +++ src/rand.rs | 67 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 src/rand.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7df5416..9f3ca51 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,7 +48,7 @@ jobs: include: # Test alloc feature which requires nightly. - rust: nightly - features: alloc medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp + features: alloc rand-custom-impl medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 @@ -73,8 +73,8 @@ jobs: features: # These feature sets cannot run tests, so we only check they build. - - medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async - - defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - rand-custom-impl medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async + - rand-custom-impl defmt defmt-trace medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async steps: - uses: actions/checkout@v2 diff --git a/Cargo.toml b/Cargo.toml index 54eb6c3..170760a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ log = { version = "0.4.4", default-features = false, optional = true } libc = { version = "0.2.18", optional = true } bitflags = { version = "1.0", default-features = false } defmt = { version = "0.2.0", optional = true } +rand_core = { version = "0.6.3", optional = true, default-features = false } [dev-dependencies] env_logger = "0.5" @@ -30,9 +31,10 @@ rand = "0.3" url = "1.0" [features] -std = ["managed/std"] +std = ["managed/std", "rand_core/std"] alloc = ["managed/alloc"] verbose = [] +rand-custom-impl = [] "medium-ethernet" = ["socket"] "medium-ip" = ["socket"] "phy-raw_socket" = ["std", "libc", "medium-ethernet"] diff --git a/examples/loopback.rs b/examples/loopback.rs index 15f13ac..dfbb773 100644 --- a/examples/loopback.rs +++ b/examples/loopback.rs @@ -37,6 +37,14 @@ mod mock { self.0.get() } } + + struct Rand; + smoltcp::rand_custom_impl!(Rand); + impl smoltcp::Rand for Rand { + fn rand_bytes(buf: &mut [u8]) { + buf.fill(0x42); + } + } } #[cfg(feature = "std")] diff --git a/src/lib.rs b/src/lib.rs index 3537088..0d0d1ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -126,6 +126,10 @@ use core::fmt; mod macros; mod parsers; +mod rand; +#[cfg(feature = "rand-custom-impl")] +pub use crate::rand::Rand; + pub mod iface; pub mod phy; #[cfg(feature = "socket")] diff --git a/src/rand.rs b/src/rand.rs new file mode 100644 index 0000000..93f5a00 --- /dev/null +++ b/src/rand.rs @@ -0,0 +1,67 @@ +#![allow(unsafe_code)] +#![allow(unused)] + +#[cfg(not(any(test, feature = "std", feature = "rand-custom-impl")))] +compile_error!("None of the Cargo features `std` or `rand-custom-impl` is enabled. smoltcp needs a `rand` implementation to work. If your target supports `std`, enable the `std` feature to use the OS's RNG. Otherwise, you must enable the `rand-custom-impl` Cargo feature, and supply your own custom implementation using the `smoltcp::rand_custom_impl!()` macro"); + +pub fn rand_u32() -> u32 { + let mut val = [0; 4]; + rand_bytes(&mut val); + u32::from_ne_bytes(val) +} + +/// Fill `buf` with random bytes. +pub fn rand_bytes(buf: &mut [u8]) { + extern "Rust" { + fn _smoltcp_rand(buf: &mut [u8]); + } + + unsafe { _smoltcp_rand(buf) } +} + +/// Methods required for a custom rand implementation. +/// +/// This trait is not intended to be used directly, just to supply a custom rand implementation to smoltcp. +#[cfg(feature = "rand-custom-impl")] +pub trait Rand { + /// Fill `buf` with random bytes. + fn rand_bytes(buf: &mut [u8]); +} + +/// Set the custom rand implementation. +/// +/// # Example +/// +/// ``` +/// struct Rand; +/// smoltcp::rand_custom_impl!(Rand); +/// impl smoltcp::Rand for Rand { +/// fn rand_bytes(buf: &mut [u8]) { +/// // TODO +/// } +/// } +/// +#[macro_export] +#[cfg(feature = "rand-custom-impl")] +macro_rules! rand_custom_impl { + ($t: ty) => { + #[no_mangle] + fn _smoltcp_rand(buf: &mut [u8]) { + <$t as $crate::Rand>::rand_bytes(buf) + } + }; +} + +#[cfg(all(feature = "std", not(feature = "rand-custom-impl"), not(test)))] +#[no_mangle] +fn _smoltcp_rand(buf: &mut [u8]) { + use rand_core::RngCore; + + rand_core::OsRng.fill_bytes(buf) +} + +#[cfg(test)] +#[no_mangle] +fn _smoltcp_rand(buf: &mut [u8]) { + panic!("Rand should not be used when testing"); +}