1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
//! Experimental area for GNRC utility functions
//!
//! These are implemented direclty in Rust and do not wrap any RIOT libraries, but seem useful at
//! least for the purpose of the author's experiments. It may turn out that they'd make nice
//! additions to the RIOT API, or are completely misguided.
#[cfg(riot_module_ipv6)]
use crate::gnrc::ipv6;
use crate::gnrc::pktbuf::{NotEnoughSpace, Pktsnip, Shared};
use crate::thread::KernelPID;
#[cfg(riot_module_gnrc_udp)]
use riot_sys::gnrc_nettype_t_GNRC_NETTYPE_UDP as GNRC_NETTYPE_UDP;
use riot_sys::{gnrc_netif_hdr_t, gnrc_nettype_t_GNRC_NETTYPE_NETIF as GNRC_NETTYPE_NETIF};
/// Trait of data structures that store all the information needed to respond to a Pktsnip in some
/// way; the data (typically address and port information) is copied into the trait implementation
/// and persisted there while the original snip is dropped and a new one is written.
///
/// This trait, in future, might also participate in the re-use of snips that are not dropped and
/// re-allocated but turned into responses in-place, but whether that makes sense here remains to
/// be seen.
pub trait RoundtripData: Sized {
/// Read round trip data from an incoming packet.
///
/// This returns something if the packet can fundamentally be responded to, which is usually
/// the case.
fn from_incoming(incoming: &Pktsnip<Shared>) -> Option<Self>;
fn wrap(self, payload: Pktsnip<Shared>) -> Result<Pktsnip<Shared>, NotEnoughSpace>;
}
#[derive(Debug)]
pub struct NetifRoundtripData {
// This would be more compact if we promised to never make UNKNOWN or ISR into KernelPIDs.
pid: Option<KernelPID>,
}
impl RoundtripData for NetifRoundtripData {
fn from_incoming(incoming: &Pktsnip<Shared>) -> Option<Self> {
Some(Self {
pid: incoming.search_type(GNRC_NETTYPE_NETIF).map(|s| {
let netif_hdr: &gnrc_netif_hdr_t = unsafe { &*(s.data.as_ptr() as *const _) };
KernelPID::new(netif_hdr.if_pid).unwrap()
}),
})
}
fn wrap(self, payload: Pktsnip<Shared>) -> Result<Pktsnip<Shared>, NotEnoughSpace> {
match self.pid {
None => Ok(payload),
Some(pid) => unsafe {
let mut netif = payload.netif_hdr_build(None, None)?;
let data: &mut gnrc_netif_hdr_t = ::core::mem::transmute(netif.data_mut().as_ptr());
data.if_pid = pid.into();
Ok(netif.into())
},
}
}
}
#[cfg(riot_module_ipv6)]
#[derive(Debug)]
pub struct IPv6RoundtripDataFull<N: RoundtripData> {
remote: ipv6::Address,
local: ipv6::Address,
// We "only" need the NetifRoundtripData if our destination address has a %interface part in it
// -- which fortunately is a typical case during development, for otherwise that step might
// easily be forgotten and IPv6RoundtripDataFull might be missing its next.
next: N,
}
#[cfg(riot_module_ipv6)]
impl<N: RoundtripData> RoundtripData for IPv6RoundtripDataFull<N> {
fn from_incoming(incoming: &Pktsnip<Shared>) -> Option<Self> {
let ip = incoming.ipv6_get_header().unwrap();
Some(Self {
remote: *ip.src(),
local: *ip.dst(),
next: N::from_incoming(incoming)?,
})
}
fn wrap(self, payload: Pktsnip<Shared>) -> Result<Pktsnip<Shared>, NotEnoughSpace> {
self.next.wrap(
payload
.ipv6_hdr_build(Some(&self.local), Some(&self.remote))?
.into(),
)
}
}
// It'd be nice to have a UDPRoundtripData (not full) that wouldn't need to store the local port
// (b/c it's usually known in the context), but how would that get past a .wrap()-ish API?
#[cfg(riot_module_gnrc_udp)]
#[derive(Debug)]
pub struct UDPRoundtripDataFull<N: RoundtripData> {
remote: core::num::NonZeroU16,
local: core::num::NonZeroU16,
next: N,
}
#[cfg(riot_module_gnrc_udp)]
impl<N: RoundtripData> RoundtripData for UDPRoundtripDataFull<N> {
fn from_incoming(incoming: &Pktsnip<Shared>) -> Option<Self> {
let (src, dst) = incoming
.search_type(GNRC_NETTYPE_UDP)
.map(|s| {
let hdr: &riot_sys::udp_hdr_t = unsafe { &*(s.data.as_ptr() as *const _) };
(
u16::from_be_bytes(unsafe { (*hdr).src_port.u8_ }),
u16::from_be_bytes(unsafe { (*hdr).dst_port.u8_ }),
)
})
.unwrap();
Some(Self {
// Taking RFC 768 literally, the remote port can easily be 0 if no responses are
// expected. (Treating the local port the same for practical reasons; it won't be zero
// at least if the data is from listening on a concrete UDP port).
remote: src.try_into().ok()?,
local: dst.try_into().ok()?,
next: N::from_incoming(incoming)?,
})
}
fn wrap(self, payload: Pktsnip<Shared>) -> Result<Pktsnip<Shared>, NotEnoughSpace> {
self.next
.wrap(payload.udp_hdr_build(self.local, self.remote)?.into())
}
}