Skip to content

Commit

Permalink
GNRC updates and extensions
Browse files Browse the repository at this point in the history
Merges: #122
  • Loading branch information
chrysn authored Sep 16, 2024
2 parents 28474ca + 37ec4a8 commit 917d545
Show file tree
Hide file tree
Showing 12 changed files with 401 additions and 83 deletions.
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

0 comments on commit 917d545

Please sign in to comment.