embedded_nal_async/stack/udp.rs
1//! Traits for using UDP on embedded devices
2//!
3//! ## Notes for implementers
4//!
5//! * At several places, the APIs expect to provide a local address. Backends that can not obtain
6//! it, such as some AT-command based stacks, <!-- should question whether they may really call
7//! themselves UDP and --> may pretend to have performed some form of network address
8//! translation, and present invalid addresses as the local address.
9//!
10//! * Implementing [`UdpStack::UniquelyBound`] and [`UdpStack::MultiplyBound`] unconnected sockets
11//! separately allows discarding the local addresses in the bound case. With LTO enabled, all the
12//! overhead compared with a third trait variant between [ConnectedUdp] and [UnconnectedUdp] (in
13//! which the local address is static but the remote address is flexible) should optimized out.
14//! Implementing `UniquelyBound` and `MultiplyBound` with the same type is expected to be a
15//! common choice.
16
17use core::net::SocketAddr;
18
19/// This trait is implemented by UDP sockets.
20///
21/// The socket it represents is both bound (has a local IP address, port and interface) and
22/// connected (has a remote IP address and port).
23///
24/// The term "connected" here refers to the semantics of POSIX datagram sockets, through which datagrams
25/// are sent and received without having a remote address per call. It does not imply any process
26/// of establishing a connection (which is absent in UDP). While there is typically no POSIX
27/// `bind()` call in the creation of such sockets, these are implicitly bound to a suitable local
28/// address at connect time.
29pub trait ConnectedUdp {
30 /// Error type returned by send and receive operations.
31 type Error: embedded_io_async::Error;
32
33 /// Send the provided data to the connected peer
34 async fn send(&mut self, data: &[u8]) -> Result<(), Self::Error>;
35
36 /// Receive a datagram into the provided buffer.
37 ///
38 /// If the received datagram exceeds the buffer's length, it is received regardless, and the
39 /// remaining bytes are discarded. The full datagram size is still indicated in the result,
40 /// allowing the recipient to detect that truncation.
41 ///
42 /// ## Compatibility note
43 ///
44 /// This deviates from the sync/nb equivalent trait in that it describes the overflow behavior
45 /// (a possibility not considered there). The name deviates from the original `receive()` to
46 /// make room for a version that is more zero-copy friendly.
47 async fn receive_into(&mut self, buffer: &mut [u8]) -> Result<usize, Self::Error>;
48
49 // WIP to allow zero-copy operation
50 // The plain receive is simple and can be provided -- implementations that don't populate
51 // receive calls from scatter-gather can just return a slice of the raw data instead, and rely
52 // on the socket still being exclusively owned. receive_oned is harder as providing it requires
53 // alloc.
54 //
55 // async fn receive(&mut self, buffer: &mut [u8]) -> utput = Result<impl AsRef<u8> + '_, Self::Error>;
56 // async fn receive_owned(&mut self) -> Result<impl AsRef<u8> + 'static, Self::Error>;
57}
58
59/// This trait is implemented by UDP sockets.
60///
61/// The socket it represents is not necessarily bound (may not have a single local IP address, port
62/// and interface), and is typically not connected (has no remote IP address and port). Both are
63/// addresses are explicitly given in every call.
64///
65/// If there were constraints in place at socket creation time (typically on the local side), the
66/// caller MUST pass in the same (or compatible) values, MAY and pass in unspecified values where
67/// applicable. The implementer MAY check them for compatibility, and SHOULD do that in debug mode.
68pub trait UnconnectedUdp {
69 /// Error type returned by send and receive operations.
70 type Error: embedded_io_async::Error;
71
72 /// Send the provided data to a peer
73 ///
74 /// ## Sending initial messages
75 ///
76 /// The local address can be left unspecified by leaving any of its component zero -- that
77 /// gives the "any" address (`[::]` / `0.0.0.0`), the uncspecified port (0) or the unspecified
78 /// zone identifier (0). Unless the operating system provides facilities exceeding this crate's traits for
79 /// enumerating local interfaces and addresses, this is the only way to initiate outbound
80 /// traffic.
81 ///
82 /// ## Responding to messages
83 ///
84 /// Users who have previously received data from a peer and want to respond have a choice of
85 /// sending from the address to which the original datagram was addressed, or from an unbound
86 /// address. Both are valid choices in some situations, and the right choice depends on the
87 /// protocol used.
88 ///
89 /// Note that users of sockets created through [`UdpStack::bind_single()`] should always pass
90 /// in that single address -- even though they've made their intention clear at construction.
91 /// They can pass either the one obtained at socket creation time, or the one obtained at
92 /// receive time; these should be equal. This allows implementations of the trait to use a
93 /// single kind of socket for both sockets bound to a single and sockets bound to multiple
94 /// addresses.
95 async fn send(
96 &mut self,
97 local: SocketAddr,
98 remote: SocketAddr,
99 data: &[u8],
100 ) -> Result<(), Self::Error>;
101
102 /// Receive a datagram into the provided buffer.
103 ///
104 /// If the received datagram exceeds the buffer's length, it is received regardless, and the
105 /// remaining bytes are discarded. The full datagram size is still indicated in the result,
106 /// allowing the recipient to detect that truncation.
107 ///
108 /// The local and remote address are given, in that order, in the result along with the number
109 /// of bytes.
110 async fn receive_into(
111 &mut self,
112 buffer: &mut [u8],
113 ) -> Result<(usize, SocketAddr, SocketAddr), Self::Error>;
114}
115
116/// This trait is implemented by UDP/IP stacks. The trait allows the underlying driver to
117/// construct multiple connections that implement the I/O traits from embedded-io-async.
118///
119/// Note that stacks with exotic connection creation methods may still not implement this, yet have
120/// objects that implement [`ConnectedUdp`] or similar.
121pub trait UdpStack {
122 /// Error type returned on socket creation failure.
123 type Error: embedded_io_async::Error;
124
125 /// Eventual socket return type of the [`.connect()`] method
126 type Connected: ConnectedUdp<Error = Self::Error>;
127 /// Eventual socket return type of the [`.bind_single()`] method
128 type UniquelyBound: UnconnectedUdp<Error = Self::Error>;
129 /// Eventual return type of the [`.bind_multiple()`] method
130 type MultiplyBound: UnconnectedUdp<Error = Self::Error>;
131
132 /// Create a socket that has a fixed remote address.
133 ///
134 /// The local address is chosen automatically.
135 ///
136 /// There is a provided implementation that implements this from the maximally unspecified
137 /// local address and [`.connect_from()`], but may be provided more efficiently by
138 /// implementers.
139 async fn connect(
140 &self,
141 remote: SocketAddr,
142 ) -> Result<(SocketAddr, Self::Connected), Self::Error> {
143 use core::net::{Ipv4Addr, Ipv6Addr, SocketAddr::*, SocketAddrV4, SocketAddrV6};
144
145 let local = match remote {
146 V4(_) => V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)),
147 V6(_) => V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)),
148 };
149 self.connect_from(local, remote).await
150 }
151
152 /// Create a socket that has a fixed remote address.
153 ///
154 /// The local address is given explicitly, but may be partially unspecified; it is fixed by the
155 /// network stack at connection time. The full local address is returned along with the
156 /// connected socket, primarily for debugging purposes.
157 async fn connect_from(
158 &self,
159 local: SocketAddr,
160 remote: SocketAddr,
161 ) -> Result<(SocketAddr, Self::Connected), Self::Error>;
162
163 /// Create a socket that has a fixed local address.
164 ///
165 /// Note that the giving an unspecified address here is *not* the same as a POSIX `bind()` --
166 /// if the underlying stack supports multiple local addresses, it will pick *one* of the
167 /// applicable addresses, rather than binding to all of them.
168 ///
169 /// The full local address is returned along with the bound socket; it may then be passed on to
170 /// other protocols for advertising purposes.
171 async fn bind_single(
172 &self,
173 local: SocketAddr,
174 ) -> Result<(SocketAddr, Self::UniquelyBound), Self::Error>;
175
176 /// Create a socket that has no single fixed local address.
177 ///
178 /// The IP address part of the local address is typically left unspecified, and the port is
179 /// given. There are use cases for other constellations, and this interface does not rule out
180 /// that they can be used, but they are rare (e.g. using the same IP address on different
181 /// network interfaces, and listening to datagrams arriving at any of them) or not well
182 /// supported by operating systems (e.g., binding to all ports at the same is not possible on
183 /// POSIX systems, where giving port 0 to a bind makes the OS pick *some* suitable port).
184 ///
185 /// Caveats:
186 ///
187 /// * There is currently no way to pass in a local address that has an unspecified address
188 /// family (which would effectively create a single socket that servers both IPv4 and IPv6);
189 /// it is not specified whether stacks that use V6MAPPED IPv4 addresses could simply used
190 /// that mechanism.
191 ///
192 /// * It is currently not specified whether this mechanism can be used to join multicast
193 /// groups.
194 ///
195 /// * There is currently no hybrid binding that allows emulating what POSIX systems do when
196 /// binding to `[::]:0`, that is, picking some available port but then still leaving the
197 /// interface and IP address unspecified.
198 async fn bind_multiple(&self, local: SocketAddr) -> Result<Self::MultiplyBound, Self::Error>;
199}