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}