From 598c53724f1dc8eeec4dd4087865a377b4cac4e0 Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 2 Jul 2024 22:40:23 +0200 Subject: [PATCH 01/10] refactor(coap): Move server/client mechanism from example into coapcore --- Cargo.lock | 5 ++- examples/coap/Cargo.toml | 1 - examples/coap/src/main.rs | 89 +++++++++++++++++-------------------- src/lib/coapcore/Cargo.toml | 8 ++++ src/lib/coapcore/src/lib.rs | 42 +++++++++++++++-- 5 files changed, 90 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d78519177..0a9af9bd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -449,7 +449,6 @@ dependencies = [ "coap-scroll-ring-server", "coapcore", "embassy-executor", - "embassy-futures", "embassy-net", "embassy-time", "embedded-io-async", @@ -598,6 +597,9 @@ dependencies = [ "coap-message-implementations", "coap-message-utils", "coap-numbers", + "embassy-futures", + "embedded-nal-async", + "embedded-nal-coap", "heapless 0.8.0", "hexlit", "lakers", @@ -605,6 +607,7 @@ dependencies = [ "liboscore", "liboscore-msgbackend", "minicbor 0.23.0", + "rand_core", ] [[package]] diff --git a/examples/coap/Cargo.toml b/examples/coap/Cargo.toml index 6db682ec5..b6dc44ca3 100644 --- a/examples/coap/Cargo.toml +++ b/examples/coap/Cargo.toml @@ -28,7 +28,6 @@ smoltcp = { version = "0.11", default-features = false } embedded-nal-coap = "0.1.0-alpha.2" coap-request = "0.2.0-alpha.2" coap-message = "0.3.2" -embassy-futures = "0.1.1" coap-message-demos = { version = "0.4.0", default-features = false } coap-request-implementations = "0.1.0-alpha.4" lakers = { version = "0.6.0", default-features = false } diff --git a/examples/coap/src/main.rs b/examples/coap/src/main.rs index 201a9806d..c7ec871ad 100644 --- a/examples/coap/src/main.rs +++ b/examples/coap/src/main.rs @@ -107,57 +107,48 @@ where println!("Server is ready."); - let coap = embedded_nal_coap::CoAPShared::<3>::new(); - let (client, server) = coap.split(); - - // going with an embassy_futures join instead of an async_std::task::spawn b/c CoAPShared is not - // Sync, and async_std expects to work in multiple threads - embassy_futures::join::join( - async { - server - .run(&mut sock, &mut handler, &mut riot_rs::random::fast_rng()) - .await - .expect("UDP error") - }, - run_client_operations(client), - ) - .await; + // FIXME: We may want to have a very slim wrapper around this for riot-rs that provides our RNG + // … or maybe less slim and it handles the sock creation as well. + let mut rng = riot_rs::random::fast_rng(); + coapcore::coap_task(&mut sock, &mut handler, &mut rng, Client).await; } -/// In parallel to server operation, this function performs some operations as a client. -/// -/// This doubles as an experimentation ground for the client side of embedded_nal_coap and -/// coap-request in general. -async fn run_client_operations( - client: embedded_nal_coap::CoAPRuntimeClient<'_, N>, -) { - // shame - let demoserver = "10.42.0.1:1234".parse().unwrap(); - - use coap_request::Stack; - println!("Sending GET to {}...", demoserver); - let response = client - .to(demoserver) - .request( - coap_request_implementations::Code::get() - .with_path("/other/separate") +struct Client; + +impl coapcore::ClientRunner<3> for Client { + /// In parallel to server operation, this function performs some operations as a client. + /// + /// This doubles as an experimentation ground for the client side of embedded_nal_coap and + /// coap-request in general. + async fn run(self, client: embedded_nal_coap::CoAPRuntimeClient<'_, 3>) { + // shame + let demoserver = "10.42.0.1:1234".parse().unwrap(); + + use coap_request::Stack; + println!("Sending GET to {}...", demoserver); + let response = client + .to(demoserver) + .request( + coap_request_implementations::Code::get() + .with_path("/other/separate") + .processing_response_payload_through(|p| { + println!("Got payload {:?}", p); + }), + ) + .await; + println!("Response {:?}", response); + + let req = coap_request_implementations::Code::post().with_path("/uppercase"); + + println!("Sending POST..."); + let mut response = client.to(demoserver); + let response = response.request( + req.with_request_payload_slice(b"Set time to 1955-11-05") .processing_response_payload_through(|p| { - println!("Got payload {:?}", p); + println!("Uppercase is {}", core::str::from_utf8(p).unwrap()) }), - ) - .await; - println!("Response {:?}", response); - - let req = coap_request_implementations::Code::post().with_path("/uppercase"); - - println!("Sending POST..."); - let mut response = client.to(demoserver); - let response = response.request( - req.with_request_payload_slice(b"Set time to 1955-11-05") - .processing_response_payload_through(|p| { - println!("Uppercase is {}", core::str::from_utf8(p).unwrap()) - }), - ); - let response = response.await; - println!("Response {:?}", response); + ); + let response = response.await; + println!("Response {:?}", response); + } } diff --git a/src/lib/coapcore/Cargo.toml b/src/lib/coapcore/Cargo.toml index 3b7f3a113..1fbd868bf 100644 --- a/src/lib/coapcore/Cargo.toml +++ b/src/lib/coapcore/Cargo.toml @@ -14,7 +14,14 @@ description = "A CoAP stack for embedded devices with built-in OSCORE/EDHOC supp # public coap-handler = "0.2.0" coap-message = "0.3.2" +# public because we take a credential lakers = { version = "0.6.0", default-features = false } +# public because the callback provides a type from there +embedded-nal-coap = "0.1.0-alpha.2" +# public because we take a socket +embedded-nal-async = "0.7" +# public because we take a RngCor +rand_core = "0.6.4" # private arrayvec = { version = "0.7.4", default-features = false } @@ -29,3 +36,4 @@ liboscore-msgbackend = { git = "https://gitlab.com/oscore/liboscore/", features ], rev = "e7a4ecd037cbb9c7f085047fec5896f4bdc68d50" } minicbor = "0.23.0" heapless = "0.8.0" +embassy-futures = "0.1.1" diff --git a/src/lib/coapcore/src/lib.rs b/src/lib/coapcore/src/lib.rs index 049a19300..0b0470546 100644 --- a/src/lib/coapcore/src/lib.rs +++ b/src/lib/coapcore/src/lib.rs @@ -5,11 +5,9 @@ //! the application as an `embedded-nal` socket, and processes CoAP along with its security //! components OSCORE and EDHOC before passing on authorized requests to the application. //! -//! The crate is under heavy development: Its API is in flux, and so far it does not yet provide -//! the CoAP server itself, but merely a middleware. (Providing the full CoAP will be a requirement -//! for at least as long as the OSCORE component is tightly coupled to a particular implementation -//! of [`coap-message`]). +//! The crate is under heavy development. #![no_std] +#![feature(lint_reasons)] // Might warrant a standalone crate at some point // @@ -17,3 +15,39 @@ // anyway) pub mod oluru; pub mod seccontext; + +// This is a trait because I haven't managed to work out any lifetimes for a single client +// callback; tried +// pub async fn coap_task( +// sock: &mut impl embedded_nal_async::UnconnectedUdp, +// handler: &mut impl coap_handler::Handler, +// rng: &mut impl rand_core::RngCore, +// run_client_operations: ClientOps, +// ) where +// for<'a> ClientOps: Fn(embedded_nal_coap::CoAPRuntimeClient<'a, 3>) -> ClientFuture, +// ClientFuture: core::future::Future, +// { +// which typechecks on its own, but won't take a static async fn (or closure that returns an async +// block) as input. +pub trait ClientRunner { + #[allow(async_fn_in_trait, reason = "We explicitly expect this to not be send")] + async fn run(self, client: embedded_nal_coap::CoAPRuntimeClient<'_, N>); +} + +pub async fn coap_task( + sock: &mut impl embedded_nal_async::UnconnectedUdp, + handler: &mut impl coap_handler::Handler, + rng: &mut impl rand_core::RngCore, + client_runner: impl ClientRunner, +) { + let coap = embedded_nal_coap::CoAPShared::::new(); + let (client, server) = coap.split(); + + // going with an embassy_futures join instead of an async_std::task::spawn b/c CoAPShared is not + // Sync, and async_std expects to work in multiple threads + embassy_futures::join::join( + async { server.run(sock, handler, rng).await.expect("UDP error") }, + client_runner.run(client), + ) + .await; +} From 65021053233e4f8dd68ee16437819a3a5580cff6 Mon Sep 17 00:00:00 2001 From: chrysn Date: Tue, 2 Jul 2024 22:46:15 +0200 Subject: [PATCH 02/10] fix(coap): Minor lint fixes --- examples/coap/src/main.rs | 2 +- examples/coap/src/udp_nal/mod.rs | 2 +- src/lib/coapcore/src/seccontext.rs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/coap/src/main.rs b/examples/coap/src/main.rs index c7ec871ad..253ce43f9 100644 --- a/examples/coap/src/main.rs +++ b/examples/coap/src/main.rs @@ -94,7 +94,7 @@ where R, ); - let mut handler = coap_message_demos::full_application_tree(log) + let handler = coap_message_demos::full_application_tree(log) .at( &["stdout"], coap_scroll_ring_server::BufferHandler::new(&buffer), diff --git a/examples/coap/src/udp_nal/mod.rs b/examples/coap/src/udp_nal/mod.rs index bd0ad3871..8464ef08b 100644 --- a/examples/coap/src/udp_nal/mod.rs +++ b/examples/coap/src/udp_nal/mod.rs @@ -78,7 +78,7 @@ impl<'a> ConnectedUdp<'a> { /// unconnected. pub async fn connect(socket: udp::UdpSocket<'a>, /*, ... */) -> Result { // This is really just a copy of the provided `embedded_nal::udp::UdpStack::connect` method - todo!() + todo!("use {:p}", &socket) } } diff --git a/src/lib/coapcore/src/seccontext.rs b/src/lib/coapcore/src/seccontext.rs index 396c75786..e4d1f5d84 100644 --- a/src/lib/coapcore/src/seccontext.rs +++ b/src/lib/coapcore/src/seccontext.rs @@ -615,7 +615,8 @@ impl<'a, H: coap_handler::Handler, L: Write, Crypto: lakers::Crypto> coap_handle self.log, "Got credential by value: {:?}..", &id_cred_i.value.get_slice(0, 5) - ); + ) + .unwrap(); cred_i = lakers::CredentialRPK::new(id_cred_i.value) // FIXME What kind of error do we send here? From 30f1716e9abdf10f2982b9561b9b03ca4a31e1e4 Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 3 Jul 2024 10:23:44 +0200 Subject: [PATCH 03/10] build: Add riot-rs{-,::}coap --- Cargo.lock | 8 ++++++++ Cargo.toml | 1 + examples/coap/Cargo.toml | 2 ++ src/riot-rs-coap/Cargo.toml | 12 ++++++++++++ src/riot-rs-coap/src/lib.rs | 8 ++++++++ src/riot-rs/Cargo.toml | 3 +++ src/riot-rs/src/lib.rs | 3 +++ 7 files changed, 37 insertions(+) create mode 100644 src/riot-rs-coap/Cargo.toml create mode 100644 src/riot-rs-coap/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0a9af9bd2..f31af7d0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3363,6 +3363,7 @@ dependencies = [ "linkme", "riot-rs-bench", "riot-rs-boards", + "riot-rs-coap", "riot-rs-debug", "riot-rs-embassy", "riot-rs-macros", @@ -3409,6 +3410,13 @@ dependencies = [ "stm32f4xx", ] +[[package]] +name = "riot-rs-coap" +version = "0.1.0" +dependencies = [ + "coapcore", +] + [[package]] name = "riot-rs-debug" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 3a2bdbe24..a1256adc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "src/riot-rs-boards/nrf52840dk", "src/riot-rs-boards/nucleo-f401re", "src/riot-rs-chips", + "src/riot-rs-coap", "src/riot-rs-debug", "src/riot-rs-macros", "src/riot-rs-random", diff --git a/examples/coap/Cargo.toml b/examples/coap/Cargo.toml index b6dc44ca3..f20f8105a 100644 --- a/examples/coap/Cargo.toml +++ b/examples/coap/Cargo.toml @@ -13,6 +13,8 @@ embedded-io-async = "0.6.1" heapless = { workspace = true } riot-rs = { path = "../../src/riot-rs", features = [ "override-network-config", + "coap", + # to be removed later once coap pulls them in "random", "csprng", ] } diff --git a/src/riot-rs-coap/Cargo.toml b/src/riot-rs-coap/Cargo.toml new file mode 100644 index 000000000..cfd8900b6 --- /dev/null +++ b/src/riot-rs-coap/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "riot-rs-coap" +version.workspace = true +authors.workspace = true +edition.workspace = true +repository.workspace = true + +[dependencies] +coapcore.path = "../lib/coapcore" + +[lints] +workspace = true diff --git a/src/riot-rs-coap/src/lib.rs b/src/riot-rs-coap/src/lib.rs new file mode 100644 index 000000000..04ead5746 --- /dev/null +++ b/src/riot-rs-coap/src/lib.rs @@ -0,0 +1,8 @@ +//! A CoAP stack preconfigured for RIOT-rs +//! ====================================== +//! +//! This crate mainly provides easy-to-use wrappers around the [`coapcore`] crate, with presets +//! tailored towards RIOT-rs: It utilizes [`embassy-net`] to open a network accessible CoAP socket, +//! [`riot-rs-random`] as a source of randomness, and [`lakers-crypto-rustcrypto`] for the +//! cryptographic algorithm implementations. +#![no_std] diff --git a/src/riot-rs/Cargo.toml b/src/riot-rs/Cargo.toml index 9b3154651..1e453af32 100644 --- a/src/riot-rs/Cargo.toml +++ b/src/riot-rs/Cargo.toml @@ -12,6 +12,7 @@ document-features = { workspace = true } linkme = { workspace = true } riot-rs-bench = { workspace = true, optional = true } riot-rs-boards = { path = "../riot-rs-boards" } +riot-rs-coap = { path = "../riot-rs-coap", optional = true } riot-rs-debug = { workspace = true } riot-rs-embassy = { path = "../riot-rs-embassy" } riot-rs-macros = { path = "../riot-rs-macros" } @@ -40,6 +41,8 @@ random = ["riot-rs-random"] csprng = ["riot-rs-random/csprng"] ## Enables seeding the random number generator from hardware. hwrng = ["riot-rs-embassy/hwrng"] +## Enables the [`coap`] module +coap = ["riot-rs-coap", "random", "csprng"] #! ## Wired communication ## Enables USB support. diff --git a/src/riot-rs/src/lib.rs b/src/riot-rs/src/lib.rs index a9093d316..564131e1f 100644 --- a/src/riot-rs/src/lib.rs +++ b/src/riot-rs/src/lib.rs @@ -16,6 +16,9 @@ pub mod buildinfo; #[cfg(feature = "bench")] #[doc(inline)] pub use riot_rs_bench as bench; +#[cfg(feature = "coap")] +#[doc(inline)] +pub use riot_rs_coap as coap; #[doc(inline)] pub use riot_rs_debug as debug; #[doc(inline)] From 09a3cb05ce2df2bb22cee46df03064425f05e8af Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 3 Jul 2024 10:44:27 +0200 Subject: [PATCH 04/10] refactor: Move CoAP stack setup into riot-rs-coap module The security setup is not moved in yet. Choices of address families are propagated into smoltcp to avoid mismatches. --- Cargo.lock | 10 ++-- examples/coap/Cargo.toml | 12 ----- examples/coap/src/main.rs | 51 ++----------------- src/riot-rs-coap/Cargo.toml | 20 ++++++++ src/riot-rs-coap/src/lib.rs | 37 ++++++++++++++ .../riot-rs-coap}/src/udp_nal/mod.rs | 2 +- .../riot-rs-coap}/src/udp_nal/util.rs | 2 +- 7 files changed, 71 insertions(+), 63 deletions(-) rename {examples/coap => src/riot-rs-coap}/src/udp_nal/mod.rs (99%) rename {examples/coap => src/riot-rs-coap}/src/udp_nal/util.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index f31af7d0b..b283186f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -451,8 +451,6 @@ dependencies = [ "embassy-executor", "embassy-net", "embassy-time", - "embedded-io-async", - "embedded-nal-async", "embedded-nal-coap", "heapless 0.8.0", "hexlit", @@ -461,7 +459,6 @@ dependencies = [ "riot-rs", "riot-rs-boards", "scroll-ring", - "smoltcp", "static-alloc", ] @@ -3414,7 +3411,14 @@ dependencies = [ name = "riot-rs-coap" version = "0.1.0" dependencies = [ + "coap-handler", "coapcore", + "embedded-io-async", + "embedded-nal-async", + "embedded-nal-coap", + "riot-rs-embassy", + "riot-rs-random", + "smoltcp", ] [[package]] diff --git a/examples/coap/Cargo.toml b/examples/coap/Cargo.toml index f20f8105a..5bcfa7d15 100644 --- a/examples/coap/Cargo.toml +++ b/examples/coap/Cargo.toml @@ -9,7 +9,6 @@ publish = false embassy-executor = { workspace = true, default-features = false } embassy-net = { workspace = true, features = ["udp"] } embassy-time = { workspace = true, default-features = false } -embedded-io-async = "0.6.1" heapless = { workspace = true } riot-rs = { path = "../../src/riot-rs", features = [ "override-network-config", @@ -22,11 +21,6 @@ riot-rs-boards = { path = "../../src/riot-rs-boards" } coapcore.path = "../../src/lib/coapcore/" -# for the udp_nal mod -embedded-nal-async = "0.7" -# actually patched with https://github.com/smoltcp-rs/smoltcp/pull/904 but -# patch.crates-io takes care of that -smoltcp = { version = "0.11", default-features = false } embedded-nal-coap = "0.1.0-alpha.2" coap-request = "0.2.0-alpha.2" coap-message = "0.3.2" @@ -41,9 +35,3 @@ hexlit = "0.5.5" static-alloc = "0.2.5" coap-scroll-ring-server = "0.2.0" scroll-ring = "0.1.1" - -[features] -default = ["proto-ipv4"] # shame -# actually embedded-nal features, we have to match them here while developing udp_nal in here -proto-ipv4 = [] -proto-ipv6 = [] diff --git a/examples/coap/src/main.rs b/examples/coap/src/main.rs index 253ce43f9..bb2d89c56 100644 --- a/examples/coap/src/main.rs +++ b/examples/coap/src/main.rs @@ -5,10 +5,7 @@ use riot_rs::{debug::println, embassy::network}; -use embassy_net::udp::{PacketMetadata, UdpSocket}; - -// Moving work from https://github.com/embassy-rs/embassy/pull/2519 in here for the time being -mod udp_nal; +use riot_rs::embassy::embassy_net; use coapcore::seccontext; @@ -19,36 +16,6 @@ use static_alloc::Bump; #[global_allocator] static A: Bump<[u8; 1 << 16]> = Bump::uninit(); -#[riot_rs::task(autostart)] -async fn coap_run() { - let stack = network::network_stack().await.unwrap(); - - // FIXME trim to CoAP requirements - let mut rx_meta = [PacketMetadata::EMPTY; 16]; - let mut rx_buffer = [0; 4096]; - let mut tx_meta = [PacketMetadata::EMPTY; 16]; - let mut tx_buffer = [0; 4096]; - - let socket = UdpSocket::new( - stack, - &mut rx_meta, - &mut rx_buffer, - &mut tx_meta, - &mut tx_buffer, - ); - - println!("Starting up CoAP server"); - - // Can't that even bind to the Any address?? - // let local_any = "0.0.0.0:5683".parse().unwrap(); // shame - let local_any = "10.42.0.61:5683".parse().unwrap(); // shame - let unconnected = udp_nal::UnconnectedUdp::bind_multiple(socket, local_any) - .await - .unwrap(); - - run(unconnected).await; -} - // FIXME: So far, this is necessary boiler plate; see ../README.md#networking for details #[riot_rs::config(network)] fn network_config() -> embassy_net::Config { @@ -61,15 +28,10 @@ fn network_config() -> embassy_net::Config { }) } -// Rest is from coap-message-demos/examples/std_embedded_nal_coap.rs +// This is adjusted from coap-message-demos/examples/std_embedded_nal_coap.rs -/// This function works on *any* UdpFullStack, including embedded ones -- only main() is what makes -/// this use POSIX sockets. (It does make use of a std based RNG, but that could be passed in just -/// as well for no_std operation). -async fn run(mut sock: S) -where - S: embedded_nal_async::UnconnectedUdp, -{ +#[riot_rs::task(autostart)] +async fn run() { use coap_handler_implementations::{HandlerBuilder, ReportingHandlerBuilder}; let log = None; @@ -107,10 +69,7 @@ where println!("Server is ready."); - // FIXME: We may want to have a very slim wrapper around this for riot-rs that provides our RNG - // … or maybe less slim and it handles the sock creation as well. - let mut rng = riot_rs::random::fast_rng(); - coapcore::coap_task(&mut sock, &mut handler, &mut rng, Client).await; + riot_rs::coap::coap_task(&mut handler, Client).await; } struct Client; diff --git a/src/riot-rs-coap/Cargo.toml b/src/riot-rs-coap/Cargo.toml index cfd8900b6..513d08cfb 100644 --- a/src/riot-rs-coap/Cargo.toml +++ b/src/riot-rs-coap/Cargo.toml @@ -7,6 +7,26 @@ repository.workspace = true [dependencies] coapcore.path = "../lib/coapcore" +riot-rs-random.path = "../riot-rs-random" +riot-rs-embassy.path = "../riot-rs-embassy" + +# actually patched with https://github.com/smoltcp-rs/smoltcp/pull/904 but +# patch.crates-io takes care of that +smoltcp = { version = "0.11", default-features = false } + +embedded-nal-coap = "0.1.0-alpha.2" +coap-handler = "0.2.0" + +# for the udp_nal mod +embedded-nal-async = "0.7" +embedded-io-async = "0.6.1" [lints] workspace = true + +[features] +# all for udp_nal +default = ["proto-ipv4"] # shame +# actually embedded-nal features, we have to match them here while developing udp_nal in here +proto-ipv4 = ["smoltcp/proto-ipv4"] +proto-ipv6 = ["smoltcp/proto-ipv6"] diff --git a/src/riot-rs-coap/src/lib.rs b/src/riot-rs-coap/src/lib.rs index 04ead5746..2bb6c3f82 100644 --- a/src/riot-rs-coap/src/lib.rs +++ b/src/riot-rs-coap/src/lib.rs @@ -6,3 +6,40 @@ //! [`riot-rs-random`] as a source of randomness, and [`lakers-crypto-rustcrypto`] for the //! cryptographic algorithm implementations. #![no_std] + +// Moving work from https://github.com/embassy-rs/embassy/pull/2519 in here for the time being +mod udp_nal; + +use riot_rs_embassy::embassy_net::udp::{PacketMetadata, UdpSocket}; + +pub async fn coap_task( + handler: &mut impl coap_handler::Handler, + client_runner: impl coapcore::ClientRunner, +) { + let stack = riot_rs_embassy::network::network_stack().await.unwrap(); + + // FIXME trim to CoAP requirements + let mut rx_meta = [PacketMetadata::EMPTY; 16]; + let mut rx_buffer = [0; 4096]; + let mut tx_meta = [PacketMetadata::EMPTY; 16]; + let mut tx_buffer = [0; 4096]; + + let socket = UdpSocket::new( + stack, + &mut rx_meta, + &mut rx_buffer, + &mut tx_meta, + &mut tx_buffer, + ); + + // Can't that even bind to the Any address?? + // let local_any = "0.0.0.0:5683".parse().unwrap(); // shame + let local_any = "10.42.0.61:5683".parse().unwrap(); // shame + let mut sock = udp_nal::UnconnectedUdp::bind_multiple(socket, local_any) + .await + .unwrap(); + + let mut rng = riot_rs_random::fast_rng(); + + coapcore::coap_task(&mut sock, handler, &mut rng, client_runner).await; +} diff --git a/examples/coap/src/udp_nal/mod.rs b/src/riot-rs-coap/src/udp_nal/mod.rs similarity index 99% rename from examples/coap/src/udp_nal/mod.rs rename to src/riot-rs-coap/src/udp_nal/mod.rs index 8464ef08b..b468933b3 100644 --- a/examples/coap/src/udp_nal/mod.rs +++ b/src/riot-rs-coap/src/udp_nal/mod.rs @@ -21,7 +21,7 @@ use core::future::poll_fn; use embedded_nal_async as nal; use smoltcp::wire::{IpAddress, IpEndpoint}; -use embassy_net::udp; +use riot_rs_embassy::embassy_net::udp; mod util; pub use util::Error; diff --git a/examples/coap/src/udp_nal/util.rs b/src/riot-rs-coap/src/udp_nal/util.rs similarity index 99% rename from examples/coap/src/udp_nal/util.rs rename to src/riot-rs-coap/src/udp_nal/util.rs index fc8b532f4..5c0e0a3a6 100644 --- a/examples/coap/src/udp_nal/util.rs +++ b/src/riot-rs-coap/src/udp_nal/util.rs @@ -1,7 +1,7 @@ //! Helpers for udp_nal -- conversion and error types -use embassy_net::udp; use embedded_nal_async as nal; +use riot_rs_embassy::embassy_net::udp; use smoltcp::wire::{IpAddress, IpEndpoint}; pub(super) fn sockaddr_nal2smol(sockaddr: nal::SocketAddr) -> Result { From bf57daf810a5102153c5d69611578ff06b10bb6b Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 3 Jul 2024 10:56:59 +0200 Subject: [PATCH 05/10] refactor(coap): Move crypto setup into riot-rs-coap --- Cargo.lock | 6 +++--- examples/coap/Cargo.toml | 3 --- examples/coap/src/main.rs | 17 ++--------------- src/riot-rs-coap/Cargo.toml | 5 ++++- src/riot-rs-coap/src/lib.rs | 18 ++++++++++++++++-- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b283186f7..7e5045982 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -453,9 +453,6 @@ dependencies = [ "embassy-time", "embedded-nal-coap", "heapless 0.8.0", - "hexlit", - "lakers", - "lakers-crypto-rustcrypto", "riot-rs", "riot-rs-boards", "scroll-ring", @@ -3416,6 +3413,9 @@ dependencies = [ "embedded-io-async", "embedded-nal-async", "embedded-nal-coap", + "hexlit", + "lakers", + "lakers-crypto-rustcrypto", "riot-rs-embassy", "riot-rs-random", "smoltcp", diff --git a/examples/coap/Cargo.toml b/examples/coap/Cargo.toml index 5bcfa7d15..5cb825b89 100644 --- a/examples/coap/Cargo.toml +++ b/examples/coap/Cargo.toml @@ -26,11 +26,8 @@ coap-request = "0.2.0-alpha.2" coap-message = "0.3.2" coap-message-demos = { version = "0.4.0", default-features = false } coap-request-implementations = "0.1.0-alpha.4" -lakers = { version = "0.6.0", default-features = false } -lakers-crypto-rustcrypto = "0.6.0" coap-handler = "0.2.0" coap-handler-implementations = "0.5.0" -hexlit = "0.5.5" static-alloc = "0.2.5" coap-scroll-ring-server = "0.2.0" diff --git a/examples/coap/src/main.rs b/examples/coap/src/main.rs index bb2d89c56..0bb8b83a4 100644 --- a/examples/coap/src/main.rs +++ b/examples/coap/src/main.rs @@ -3,12 +3,10 @@ #![feature(type_alias_impl_trait)] #![feature(used_with_arg)] -use riot_rs::{debug::println, embassy::network}; +use riot_rs::debug::println; use riot_rs::embassy::embassy_net; -use coapcore::seccontext; - // because coapcore depends on it temporarily extern crate alloc; use static_alloc::Bump; @@ -49,13 +47,6 @@ async fn run() { writeln!(stdout, "We have our own stdout now.").unwrap(); writeln!(stdout, "With rings and atomics.").unwrap(); - use hexlit::hex; - const R: &[u8] = &hex!("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac"); - let own_identity = ( - &lakers::CredentialRPK::new(lakers::EdhocMessageBuffer::new_from_slice(&hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072")).expect("Credential should be small enough")).expect("Credential should be processable"), - R, - ); - let handler = coap_message_demos::full_application_tree(log) .at( &["stdout"], @@ -63,13 +54,9 @@ async fn run() { ) .with_wkc(); - let mut handler = seccontext::OscoreEdhocHandler::new(own_identity, handler, stdout, || { - lakers_crypto_rustcrypto::Crypto::new(riot_rs::random::crypto_rng()) - }); - println!("Server is ready."); - riot_rs::coap::coap_task(&mut handler, Client).await; + riot_rs::coap::coap_task(handler, Client, &mut stdout).await; } struct Client; diff --git a/src/riot-rs-coap/Cargo.toml b/src/riot-rs-coap/Cargo.toml index 513d08cfb..349548b4d 100644 --- a/src/riot-rs-coap/Cargo.toml +++ b/src/riot-rs-coap/Cargo.toml @@ -7,7 +7,7 @@ repository.workspace = true [dependencies] coapcore.path = "../lib/coapcore" -riot-rs-random.path = "../riot-rs-random" +riot-rs-random = { path = "../riot-rs-random", features = ["csprng"] } riot-rs-embassy.path = "../riot-rs-embassy" # actually patched with https://github.com/smoltcp-rs/smoltcp/pull/904 but @@ -16,6 +16,9 @@ smoltcp = { version = "0.11", default-features = false } embedded-nal-coap = "0.1.0-alpha.2" coap-handler = "0.2.0" +hexlit = "0.5.5" +lakers = { version = "0.6.0", default-features = false } +lakers-crypto-rustcrypto = "0.6.0" # for the udp_nal mod embedded-nal-async = "0.7" diff --git a/src/riot-rs-coap/src/lib.rs b/src/riot-rs-coap/src/lib.rs index 2bb6c3f82..1bb26a679 100644 --- a/src/riot-rs-coap/src/lib.rs +++ b/src/riot-rs-coap/src/lib.rs @@ -12,9 +12,12 @@ mod udp_nal; use riot_rs_embassy::embassy_net::udp::{PacketMetadata, UdpSocket}; +use coapcore::seccontext; + pub async fn coap_task( - handler: &mut impl coap_handler::Handler, + handler: impl coap_handler::Handler, client_runner: impl coapcore::ClientRunner, + logger: &mut impl core::fmt::Write, ) { let stack = riot_rs_embassy::network::network_stack().await.unwrap(); @@ -41,5 +44,16 @@ pub async fn coap_task( let mut rng = riot_rs_random::fast_rng(); - coapcore::coap_task(&mut sock, handler, &mut rng, client_runner).await; + use hexlit::hex; + const R: &[u8] = &hex!("72cc4761dbd4c78f758931aa589d348d1ef874a7e303ede2f140dcf3e6aa4aac"); + let own_identity = ( + &lakers::CredentialRPK::new(lakers::EdhocMessageBuffer::new_from_slice(&hex!("A2026008A101A5010202410A2001215820BBC34960526EA4D32E940CAD2A234148DDC21791A12AFBCBAC93622046DD44F02258204519E257236B2A0CE2023F0931F1F386CA7AFDA64FCDE0108C224C51EABF6072")).expect("Credential should be small enough")).expect("Credential should be processable"), + R, + ); + + let mut handler = seccontext::OscoreEdhocHandler::new(own_identity, handler, logger, || { + lakers_crypto_rustcrypto::Crypto::new(riot_rs_random::crypto_rng()) + }); + + coapcore::coap_task(&mut sock, &mut handler, &mut rng, client_runner).await; } From 83d39d953bc18ec800e09d565cb4e17ba5a3bab8 Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 3 Jul 2024 11:37:43 +0200 Subject: [PATCH 06/10] ci(doc): Enable CoAP module --- .github/workflows/build-deploy-docs.yml | 2 +- .github/workflows/main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-deploy-docs.yml b/.github/workflows/build-deploy-docs.yml index 1487d1f62..c00738b4c 100644 --- a/.github/workflows/build-deploy-docs.yml +++ b/.github/workflows/build-deploy-docs.yml @@ -48,7 +48,7 @@ jobs: - name: Build rustdoc docs run: | - cargo doc -p riot-rs --features no-boards,bench,threading,random,csprng,hwrng + cargo doc -p riot-rs --features no-boards,bench,threading,random,csprng,hwrng,coap echo "" > target/doc/index.html mkdir -p ./_site/dev/docs/api && mv target/doc/* ./_site/dev/docs/api diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 93a87cd13..5fe59ded9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -141,7 +141,7 @@ jobs: args: --verbose --locked --features no-boards -p riot-rs -p riot-rs-boards -p riot-rs-chips -p riot-rs-debug -p riot-rs-embassy -p riot-rs-macros -p riot-rs-random -p riot-rs-rt -p riot-rs-utils - name: rustdoc - run: RUSTDOCFLAGS='-D warnings' cargo doc -p riot-rs --features no-boards,bench,threading,random,csprng,hwrng + run: RUSTDOCFLAGS='-D warnings' cargo doc -p riot-rs --features no-boards,bench,threading,random,csprng,hwrng,coap - name: rustfmt run: cargo fmt --check --all From ed56c4189ec2def9f83b4e4f752b7f4d0d6c366e Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 3 Jul 2024 13:20:28 +0200 Subject: [PATCH 07/10] fix(coap): Split and simplify example client code to avoid unwraps and enhance error handling --- examples/coap/src/main.rs | 87 ++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/examples/coap/src/main.rs b/examples/coap/src/main.rs index 0bb8b83a4..1063f9c97 100644 --- a/examples/coap/src/main.rs +++ b/examples/coap/src/main.rs @@ -3,7 +3,7 @@ #![feature(type_alias_impl_trait)] #![feature(used_with_arg)] -use riot_rs::debug::println; +use core::fmt::Write; use riot_rs::embassy::embassy_net; @@ -28,22 +28,23 @@ fn network_config() -> embassy_net::Config { // This is adjusted from coap-message-demos/examples/std_embedded_nal_coap.rs +// FIXME: Why doesn't scroll_ring provide that? +#[derive(Clone)] +struct Stdout<'a>(&'a scroll_ring::Buffer<512>); +impl<'a> Write for Stdout<'a> { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + self.0.write(s.as_bytes()); + Ok(()) + } +} + #[riot_rs::task(autostart)] async fn run() { use coap_handler_implementations::{HandlerBuilder, ReportingHandlerBuilder}; let log = None; let buffer = scroll_ring::Buffer::<512>::default(); - // FIXME: Why doesn't scroll_ring provide that? - struct Stdout<'a>(&'a scroll_ring::Buffer<512>); - impl<'a> core::fmt::Write for Stdout<'a> { - fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { - self.0.write(s.as_bytes()); - Ok(()) - } - } let mut stdout = Stdout(&buffer); - use core::fmt::Write; writeln!(stdout, "We have our own stdout now.").unwrap(); writeln!(stdout, "With rings and atomics.").unwrap(); @@ -54,47 +55,59 @@ async fn run() { ) .with_wkc(); - println!("Server is ready."); + writeln!(stdout, "Server is ready.").unwrap(); - riot_rs::coap::coap_task(handler, Client, &mut stdout).await; + riot_rs::coap::coap_task(handler, Client(stdout.clone()), &mut stdout).await; } -struct Client; +struct Client<'s>(Stdout<'s>); -impl coapcore::ClientRunner<3> for Client { - /// In parallel to server operation, this function performs some operations as a client. - /// - /// This doubles as an experimentation ground for the client side of embedded_nal_coap and - /// coap-request in general. - async fn run(self, client: embedded_nal_coap::CoAPRuntimeClient<'_, 3>) { +impl<'s> Client<'s> { + async fn run_logging( + mut self, + client: embedded_nal_coap::CoAPRuntimeClient<'_, 3>, + ) -> Result<(), &'static str> { // shame - let demoserver = "10.42.0.1:1234".parse().unwrap(); + let demoserver = "10.42.0.1:1234" + .parse() + .map_err(|_| "Error parsing demo server address")?; use coap_request::Stack; - println!("Sending GET to {}...", demoserver); + writeln!(self.0, "Sending GET to {}...", demoserver).unwrap(); + let response = client .to(demoserver) .request( coap_request_implementations::Code::get() .with_path("/other/separate") .processing_response_payload_through(|p| { - println!("Got payload {:?}", p); + writeln!( + self.0, + "Got payload {:?} length {}", + &p[..core::cmp::min(10, p.len())], + p.len() + ) + .unwrap(); }), ) - .await; - println!("Response {:?}", response); - - let req = coap_request_implementations::Code::post().with_path("/uppercase"); - - println!("Sending POST..."); - let mut response = client.to(demoserver); - let response = response.request( - req.with_request_payload_slice(b"Set time to 1955-11-05") - .processing_response_payload_through(|p| { - println!("Uppercase is {}", core::str::from_utf8(p).unwrap()) - }), - ); - let response = response.await; - println!("Response {:?}", response); + .await + .map_err(|_| "Error while trying to GET /other/separate")?; + writeln!(self.0, "Response {:?}", response).unwrap(); + + Ok(()) + } +} + +impl<'s> coapcore::ClientRunner<3> for Client<'s> { + /// In parallel to server operation, this function performs some operations as a client. + /// + /// This doubles as an experimentation ground for the client side of embedded_nal_coap and + /// coap-request in general. + async fn run(self, client: embedded_nal_coap::CoAPRuntimeClient<'_, 3>) { + let mut stdout = self.0.clone(); + match self.run_logging(client).await { + Ok(_) => writeln!(stdout, "Client process completed").unwrap(), + Err(e) => writeln!(stdout, "Client process erred out: {e}").unwrap(), + } } } From 000d917f090a41575df328c194131c6831703a64 Mon Sep 17 00:00:00 2001 From: chrysn Date: Wed, 3 Jul 2024 13:43:06 +0200 Subject: [PATCH 08/10] fix(coap): Restore building with IPv6 enabled --- src/riot-rs-coap/src/udp_nal/util.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/riot-rs-coap/src/udp_nal/util.rs b/src/riot-rs-coap/src/udp_nal/util.rs index 5c0e0a3a6..1048510d3 100644 --- a/src/riot-rs-coap/src/udp_nal/util.rs +++ b/src/riot-rs-coap/src/udp_nal/util.rs @@ -39,7 +39,8 @@ pub(super) fn sockaddr_smol2nal(endpoint: IpEndpoint) -> nal::SocketAddr { } #[cfg(feature = "proto-ipv6")] IpAddress::Ipv6(addr) => { - embedded_nal_async::SocketAddrV6::new(addr.0.into(), endpoint.port).into() + // FIXME: Where is smoltcp's zone identifier? + embedded_nal_async::SocketAddrV6::new(addr.0.into(), endpoint.port, 0, 0).into() } } } From 764adb28f1f72e6b202ddfc62313331ad2812de3 Mon Sep 17 00:00:00 2001 From: chrysn Date: Fri, 16 Aug 2024 22:11:25 +0200 Subject: [PATCH 09/10] refactor(coap): Generalize to make buffer size more flexible --- examples/coap/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/coap/src/main.rs b/examples/coap/src/main.rs index 1063f9c97..b3efd412c 100644 --- a/examples/coap/src/main.rs +++ b/examples/coap/src/main.rs @@ -30,8 +30,8 @@ fn network_config() -> embassy_net::Config { // FIXME: Why doesn't scroll_ring provide that? #[derive(Clone)] -struct Stdout<'a>(&'a scroll_ring::Buffer<512>); -impl<'a> Write for Stdout<'a> { +struct Stdout<'a, const N: usize>(&'a scroll_ring::Buffer); +impl<'a, const N: usize> Write for Stdout<'a, N> { fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { self.0.write(s.as_bytes()); Ok(()) @@ -60,9 +60,9 @@ async fn run() { riot_rs::coap::coap_task(handler, Client(stdout.clone()), &mut stdout).await; } -struct Client<'s>(Stdout<'s>); +struct Client(W); -impl<'s> Client<'s> { +impl Client { async fn run_logging( mut self, client: embedded_nal_coap::CoAPRuntimeClient<'_, 3>, @@ -98,7 +98,7 @@ impl<'s> Client<'s> { } } -impl<'s> coapcore::ClientRunner<3> for Client<'s> { +impl coapcore::ClientRunner<3> for Client { /// In parallel to server operation, this function performs some operations as a client. /// /// This doubles as an experimentation ground for the client side of embedded_nal_coap and From 796181a3173e54770a123f7bfa5fd4c825e62dcd Mon Sep 17 00:00:00 2001 From: chrysn Date: Fri, 16 Aug 2024 22:23:18 +0200 Subject: [PATCH 10/10] fix(build): Close gaps that resulted in lint stage failures --- .github/workflows/main.yml | 2 +- src/riot-rs-coap/Cargo.toml | 2 +- src/riot-rs-embassy/Cargo.toml | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3384a52dd..7b0be0c01 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -141,7 +141,7 @@ jobs: args: --verbose --locked --features no-boards -p riot-rs -p riot-rs-boards -p riot-rs-chips -p riot-rs-debug -p riot-rs-embassy -p riot-rs-macros -p riot-rs-random -p riot-rs-rt -p riot-rs-threads -p riot-rs-utils - name: rustdoc - run: RUSTDOCFLAGS='-D warnings' cargo doc -p riot-rs --features no-boards,bench,external-interrupts,threading,random,csprng,hwrng,coap + run: RUSTDOCFLAGS='-D warnings' cargo doc -p riot-rs --features no-boards,bench,external-interrupts,threading,random,csprng,hwrng,coap,net,usb-ethernet - name: rustfmt run: cargo fmt --check --all diff --git a/src/riot-rs-coap/Cargo.toml b/src/riot-rs-coap/Cargo.toml index 349548b4d..879925744 100644 --- a/src/riot-rs-coap/Cargo.toml +++ b/src/riot-rs-coap/Cargo.toml @@ -8,7 +8,7 @@ repository.workspace = true [dependencies] coapcore.path = "../lib/coapcore" riot-rs-random = { path = "../riot-rs-random", features = ["csprng"] } -riot-rs-embassy.path = "../riot-rs-embassy" +riot-rs-embassy = { path = "../riot-rs-embassy", features = ["udp"] } # actually patched with https://github.com/smoltcp-rs/smoltcp/pull/904 but # patch.crates-io takes care of that diff --git a/src/riot-rs-embassy/Cargo.toml b/src/riot-rs-embassy/Cargo.toml index 5fe769e15..8d834e4f9 100644 --- a/src/riot-rs-embassy/Cargo.toml +++ b/src/riot-rs-embassy/Cargo.toml @@ -166,3 +166,7 @@ defmt = [ # usb peripheral support. stm32-usb = [] stm32-usb-synopsis = [] + +# Made available so that downsteram crates such as riot-rs-coap don't need to +# pull in embassy-net just to enable a feature they use through a pub-use. +udp = ["net", "embassy-net/udp"]