riot_wrappers/
gnrc_util.rs

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
//! Experimental area for GNRC utility functions
//!
//! These are implemented directly 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;

/// 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> {
        let header = incoming.netif_get_header()?;
        Some(NetifRoundtripData {
            pid: header.if_pid(),
        })
    }

    fn wrap(self, payload: Pktsnip<Shared>) -> Result<Pktsnip<Shared>, NotEnoughSpace> {
        Ok(match self.pid {
            None => payload,
            Some(pid) => payload
                .netif_hdr_builder()
                .without_link_layer_addresses()
                .with_if_pid(pid)
                .finish()?
                .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())
    }
}