owned this note
owned this note
Published
Linked with GitHub
# `smoltcp` + metadata support = :boom:
## Motivation
In our work with IEEE1588 (Precision Time Protocol) support for STM32s we noticed that there is a need to be able to associate and retrieve metadata about RX and TX packet events. In our case it was to read out and forward timestamps from both events, which requires that you know which timestamp event is connected to which Ethernet packet (containing a specific UDP packet). This connection between Ethernet packets and `send/receive` operations does not exist today.
This is similarly true for other mediums such as radio and 6LoWPAN where one might want to get RSSI information or other performance metrics.
With this in mind we propose the following API extension to `smoltcp`.
## API extensions
The aim was to add this support without having `smoltcp` be dependent on the associated data, this to keep it simple. Instead `smoltcp` is extended with a `PacketId { usize }` that can be used to uniquely identify a high-level packet at the `Device` level. It's the responsibility of an implementor of `Device` to be able to lookup the `PacketId` and produce the relevant information (if any).
Working implementation example using the extensions (extracting RX and TX timestamps):
* [`smoltcp changes`](https://github.com/smoltcp-rs/smoltcp/compare/master...datdenkikniet:unique_packet_id)
* [`stm32-eth`](https://github.com/datdenkikniet/stm32-eth/tree/ptp-support) (currently only tested on `stm32f745`)
### `Device` trait extension
```rust
pub trait Device<'a> {
type RxToken: RxToken + 'a;
type TxToken: TxToken + 'a;
/// Construct a token pair consisting of one receive token and one
/// transmit token.
///
/// The additional transmit token makes it possible to generate a
/// reply packet based on the contents of the received packet. For
/// example, this makes it possible to handle arbitrarily large ICMP
/// echo ("ping") requests, where all the received bytes need to be
/// sent back, without heap allocation.
fn receive(
&'a mut self,
rx_packet_id: PacketId,
tx_packet_id: PacketId,
) -> Option<(Self::RxToken, Self::TxToken)>;
/// Construct a transmit token.
fn transmit(&'a mut self, packet_id: PacketId)
-> Option<Self::TxToken>;
/// Get a description of device capabilities.
fn capabilities(&self) -> DeviceCapabilities;
}
```
The proposed solution places the burden of adoption with implementors of `Device`.
Alternatively, a new `MarkingDevice` trait that has the proposed signatures could be created. We could then `impl<'a> MarkingDevice<'a> for Device<'a>`, use `MarkingDevice` internally, and leave it up to implementors to switch over to the new `MarkingDevice`. [Code differences between two proposals](https://github.com/smoltcp-rs/smoltcp/compare/datdenkikniet:unique_packet_id...datdenkikniet:unique_packet_id_trait)
### `udp::Socket`
Addition to the `udp::Socket` API was minimal which allows for sending a packet and getting the `PacketId`:
```rust
pub fn send_marked<'b>(
&mut self,
interface: &mut Interface<'b>,
size: usize,
remote_endpoint: IpEndpoint,
) -> Result<(&mut [u8], PacketId), SendError> {
// ...
}
```
`interface` is used to generate of the next `PacketId`.
However the storage type was update from an `IpEndPoint` to `UdpMetadata`, where `UdpMetadata` is:
```rust
pub struct UdpMetadata {
packet_id: Option<PacketId>,
endpoint: IpEndpoint,
}
```
Here, using an `Option<PacketId>` is effectively required. If it's not an `Option`, the signature for `send` could not remain the same, as that currently does not have any way to generate an ID that is guaranteed to be unique for a given `Device`.
### Other socket types
Currently, there aren't plans for adding support for this feature to other sockets. Doing so, however, should be as straightforward as adding it to UDP sockets.
## Open questions
Please expand!
* For `udp::Socket`: how should the maximum payload size be determined? I.e.: if the mtu is `1522`, how do we determine that a data-packet of `1800` bytes cannot be sent in a single frame? A "reasonable" upper bound determined by the minimum ethernet `MTU` and a "bad case" of many IPv6 headers? How do we deal with UDP sockets that don't use Ethernet as the underlying transmission medium?
* ...