Skip to content
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

GNRC extensions #122

Merged
merged 11 commits into from
Sep 16, 2024
12 changes: 12 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ fn main() {
"gnrc",
"gnrc_icmpv6",
"gnrc_ipv6_nib",
"gnrc_netapi_callbacks",
"gnrc_nettype_ccn",
"gnrc_nettype_custom",
"gnrc_nettype_gomach",
"gnrc_nettype_icmpv6",
"gnrc_nettype_ipv6",
"gnrc_nettype_ipv6_ext",
"gnrc_nettype_lwmac",
"gnrc_nettype_ndn",
"gnrc_nettype_sixlowpan",
"gnrc_nettype_tcp",
"gnrc_nettype_udp",
"gnrc_pktbuf",
"gnrc_udp",
"ipv6",
Expand Down
2 changes: 1 addition & 1 deletion src/gnrc/icmpv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ impl From<EchoType> for u32 {
}
}

use super::pktbuf::{NotEnoughSpace, Pktsnip, Writable};
use crate::gnrc_pktbuf::{NotEnoughSpace, Pktsnip, Writable};

impl<'a> Pktsnip<Writable> {
#[doc(alias = "gnrc_icmpv6_echo_build")]
Expand Down
83 changes: 28 additions & 55 deletions src/gnrc/ipv6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

use core::mem::MaybeUninit;

use riot_sys::{ipv6_addr_from_str, ipv6_addr_t, kernel_pid_t};
use riot_sys::{ipv6_addr_t, kernel_pid_t};

use super::pktbuf::{Mode, NotEnoughSpace, Pktsnip, Writable};
use crate::error::{NegativeErrorExt, NumericError};
use crate::gnrc_pktbuf::{Mode, NotEnoughSpace, Pktsnip, Writable};

impl super::Netif {
pub fn ipv6_addrs(
Expand Down Expand Up @@ -55,74 +55,47 @@ impl<'a, const MAX: usize> core::iter::IntoIterator for &'a AddrList<MAX> {
}
}

/// An IPv6 address
///
/// This is strictly equivalent and convertible with a [core::net::Ipv6Addr], but can not be
/// guaranteed the same memory layout (mostly alignment).
///
/// Method implementations mixedly use what RIOT offers and what Rust's standard library offers,
/// depending on what is easiest to use, trusting that the compiler will elide the memory copying
/// that is required for conversion in case the copy is not necessary for alignment purposes.
#[repr(transparent)] // which allows the AddrList addresss to be passed to gnrc_netif_ipv6_addrs_get
#[derive(Copy, Clone)]
pub struct Address {
inner: ipv6_addr_t,
}

// When no_std_net / embedded_nal is present, it may be a good idea to run through there (or allow
// configuration to optimize which route to take for best deduplication of code)
impl ::core::str::FromStr for Address {
type Err = ();

fn from_str(s: &str) -> Result<Self, Self::Err> {
// It'd be nice to use std::net::Address::from_str, but the parser is generic over
// families (maybe at some point we'll need that here too, but not now), and it's in std
// rather then core for reasons I can't really follow.

let s = s.as_bytes();

let mut with_null = [0u8; 32 + 7 + 1]; // 32 nibbles + 7 colons + null byte
if s.len() > with_null.len() - 1 {
// Obviously too long to be a valid plain address
return Err(());
impl From<&core::net::Ipv6Addr> for Address {
fn from(addr: &core::net::Ipv6Addr) -> Self {
Self {
inner: ipv6_addr_t { u8_: addr.octets() },
}
with_null[..s.len()].copy_from_slice(s);
}
}

let mut inner: MaybeUninit<ipv6_addr_t> = MaybeUninit::uninit();
impl From<&Address> for core::net::Ipv6Addr {
fn from(addr: &Address) -> Self {
// unsafe: All fields are equivalently initialized
core::net::Ipv6Addr::from(unsafe { addr.inner.u8_ })
}
}

let conversion_result = unsafe {
ipv6_addr_from_str(
inner.as_mut_ptr(),
core::ffi::CStr::from_bytes_with_nul_unchecked(&with_null).as_ptr() as _,
)
};
impl ::core::str::FromStr for Address {
type Err = ();

match conversion_result as usize {
0 => Err(()),
_ => Ok(Self {
inner: unsafe { inner.assume_init() },
}),
}
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok((&core::net::Ipv6Addr::from_str(s).map_err(|_| ())?).into())
}
}

// When no_std_net / embedded_nal is present, it may be a good idea to run through there.
impl ::core::fmt::Debug for Address {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let as_u8 = self.raw();
write!(
f,
"{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:\
{:02x}{:02x}:{:02x}{:02x}",
as_u8[0],
as_u8[1],
as_u8[2],
as_u8[3],
as_u8[4],
as_u8[5],
as_u8[6],
as_u8[7],
as_u8[8],
as_u8[9],
as_u8[10],
as_u8[11],
as_u8[12],
as_u8[13],
as_u8[14],
as_u8[15],
)
let converted = core::net::Ipv6Addr::from(self);
write!(f, "{:?}", converted)
}
}

Expand Down
48 changes: 46 additions & 2 deletions src/gnrc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,63 @@
pub mod icmpv6;
#[cfg(riot_module_ipv6)]
pub mod ipv6;
pub mod netif;

pub mod netapi;
pub mod netreg;
#[cfg(riot_module_gnrc_ipv6_nib)]
pub mod nib;
#[deprecated(note = "Internally, use gnrc_pktbuf directly")]
pub(crate) use crate::gnrc_pktbuf as pktbuf;

use riot_sys::{gnrc_netif_iter, gnrc_netif_t};

use crate::thread::KernelPID;
use core::iter::Iterator;

// Could be made public on the long run, but will need proper constructors and is_... functions.
// Right now, this is just for pretty-printing.
pub(crate) struct NetType(pub(crate) riot_sys::gnrc_nettype_t);

impl core::fmt::Debug for NetType {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self.0 {
// To be updated from the gnrc_nettype_t enum definition on demand
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_TX_SYNC => write!(f, "TX_SYNC"),
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_NETIF => write!(f, "NETIF"),
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_UNDEF => write!(f, "undefined"),
#[cfg(riot_module_gnrc_nettype_gomach)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_GOMACH => write!(f, "GOMACH"),
#[cfg(riot_module_gnrc_nettype_lwmac)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_LWMAC => write!(f, "LWMAC"),
#[cfg(riot_module_gnrc_nettype_custom)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_CUSTOM => write!(f, "CUSTOM"),
#[cfg(riot_module_gnrc_nettype_sixlowpan)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_SIXLOWPAN => write!(f, "SIXLOWPAN"),
#[cfg(riot_module_gnrc_nettype_ipv6)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_IPV6 => write!(f, "IPV6"),
#[cfg(riot_module_gnrc_nettype_ipv6_ext)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_IPV6_EXT => write!(f, "IPV6_EXT"),
#[cfg(riot_module_gnrc_nettype_icmpv6)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_ICMPV6 => write!(f, "ICMPV6"),
#[cfg(riot_module_gnrc_nettype_ccn)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_CCN => write!(f, "CCN"),
#[cfg(riot_module_gnrc_nettype_ccn)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_CCN_CHUNK => write!(f, "CCN_CHUNK"),
#[cfg(riot_module_gnrc_nettype_ndn)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_NDN => write!(f, "NDN"),
#[cfg(riot_module_gnrc_nettype_tcp)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_TCP => write!(f, "TCP"),
#[cfg(riot_module_gnrc_nettype_udp)]
riot_sys::gnrc_nettype_t_GNRC_NETTYPE_UDP => write!(f, "UDP"),
x if riot_sys::gnrc_nettype_t_GNRC_NETTYPE_UNDEF < x
&& x < riot_sys::gnrc_nettype_t_GNRC_NETTYPE_NUMOF =>
{
write!(f, "unknown ({})", x)
}
x => write!(f, "invalid ({})", x),
}
}
}

struct NetifIter {
current: *const gnrc_netif_t,
}
Expand Down
4 changes: 3 additions & 1 deletion src/gnrc/netapi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![cfg(riot_module_gnrc_pktbuf)]

use riot_sys::gnrc_nettype_t;

use crate::gnrc::pktbuf::{Pktsnip, Shared};
use crate::gnrc_pktbuf::{Pktsnip, Shared};

/// Dispatch a packet to all listeners of the given nettype and demux context.
///
Expand Down
119 changes: 119 additions & 0 deletions src/gnrc/netif.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//! Components acting on the netif pktsnip layer
#![cfg(riot_module_gnrc_pktbuf)]

// FIXME: Move some of mod.rs in here

use crate::gnrc_pktbuf::{Mode, NotEnoughSpace, Pktsnip, Writable};
use crate::thread::KernelPID;
use riot_sys::{gnrc_netif_hdr_t, gnrc_nettype_t_GNRC_NETTYPE_NETIF as GNRC_NETTYPE_NETIF};

/// A transparent wrapper around ``gnrc_netif_hdr_t`` that provides idiomatically typed fields
#[doc(alias = "gnrc_netif_hdr_t")]
#[derive(Copy, Clone)]
pub struct Header<'a>(&'a gnrc_netif_hdr_t);

impl Header<'_> {
pub fn if_pid(&self) -> Option<KernelPID> {
KernelPID::new(self.0.if_pid)
}
}

/// The root of building a Netif pktsnip
pub struct HeaderBuilder<M: Mode>(Pktsnip<M>);

impl<M: Mode> HeaderBuilder<M> {
#[inline]
pub fn without_link_layer_addresses(self) -> HeaderBuilt {
self.with_src_and_dst(&[], &[])
}

#[inline]
pub fn with_src(self, src: &[u8]) -> HeaderBuilt {
self.with_src_and_dst(src, &[])
}

#[inline]
pub fn with_dst(self, dst: &[u8]) -> HeaderBuilt {
self.with_src_and_dst(&[], dst)
}

pub fn with_src_and_dst(self, src: &[u8], dst: &[u8]) -> HeaderBuilt {
let Ok(src_len) = src.len().try_into() else {
return HeaderBuilt(Err(NotEnoughSpace));
};
let Ok(dst_len) = dst.len().try_into() else {
return HeaderBuilt(Err(NotEnoughSpace));
};
// "as *mut": I'm assuming the C signature is just missing its const
let snip = unsafe {
riot_sys::gnrc_netif_hdr_build(
src.as_ptr() as *mut _,
src_len,
dst.as_ptr() as *mut _,
dst_len,
)
};
if snip == 0 as *mut _ {
return HeaderBuilt(Err(NotEnoughSpace));
} else {
// unsafe: snip is initialized, and to_ptr takes care of the refcounts
// FIXME can use add?
unsafe { (*snip).next = self.0.to_ptr() };
HeaderBuilt(Ok(unsafe { Pktsnip::<Writable>::from_ptr(snip) }))
}
}
}

pub struct HeaderBuilt(Result<Pktsnip<Writable>, NotEnoughSpace>);

impl HeaderBuilt {
pub fn finish(self) -> Result<Pktsnip<Writable>, NotEnoughSpace> {
self.0
}

fn access(&mut self) -> Option<&mut riot_sys::gnrc_netif_hdr_t> {
match &mut self.0 {
Ok(snip) => Some(
// Unsafe: Valid by construction of our type
unsafe {
(snip.data_mut().as_mut_ptr() as *mut riot_sys::gnrc_netif_hdr_t).as_mut()
}
.expect("Non-null by construction"),
),
Err(_) => None,
}
}

pub fn with_if_pid(mut self, pid: KernelPID) -> HeaderBuilt {
if let Some(h) = self.access() {
h.if_pid = pid.into();
}
self
}
}

impl<M: Mode> Pktsnip<M> {
/// Get the Netif header of the snip, if there is any thusly typed snip present
// Note that we can *not* just implement this with &mut on a Writable Pktsnip, because
// writability is only ever about the first snip
pub fn netif_get_header(&self) -> Option<Header<'_>> {
let netif_snip = self.search_type(GNRC_NETTYPE_NETIF)?;
// unsafe: Following GNRC conventions
// unwrap: pointer comes from as_ptr and is thus non-null (but doesn't tell that)
let header =
unsafe { (netif_snip.data.as_ptr() as *const gnrc_netif_hdr_t).as_ref() }.unwrap();
// unsafe: Using C API as documented
debug_assert!(
netif_snip.data.len()
== unsafe { riot_sys::inline::gnrc_netif_hdr_sizeof(crate::inline_cast(header)) }
as _
);
Some(Header(header))
}

/// Build a netif header around the Pktsnip
#[doc(alias = "gnrc_netif_hdr_build")]
pub fn netif_hdr_builder(self) -> HeaderBuilder<M> {
HeaderBuilder(self)
}
}
Loading
Loading