embedded_nal_async/stack/
udp.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
//! Traits for using UDP on embedded devices
//!
//! ## Notes for implementers
//!
//! * At several places, the APIs expect to provide a local address. Backends that can not obtain
//!   it, such as some AT-command based stacks, <!-- should question whether they may really call
//!   themselves UDP and --> may pretend to have performed some form of network address
//!   translation, and present invalid addresses as the local address.
//!
//! * Implementing [`UdpStack::UniquelyBound`] and [`UdpStack::MultiplyBound`] unconnected sockets
//!   separately allows discarding the local addresses in the bound case. With LTO enabled, all the
//!   overhead compared with a third trait variant between [ConnectedUdp] and [UnconnectedUdp] (in
//!   which the local address is static but the remote address is flexible) should optimized out.
//!   Implementing `UniquelyBound` and `MultiplyBound` with the same type is expected to be a
//!   common choice.

use crate::SocketAddr;

/// This trait is implemented by UDP sockets.
///
/// The socket it represents is both bound (has a local IP address, port and interface) and
/// connected (has a remote IP address and port).
///
/// The term "connected" here refers to the semantics of POSIX datagram sockets, through which datagrams
/// are sent and received without having a remote address per call. It does not imply any process
/// of establishing a connection (which is absent in UDP). While there is typically no POSIX
/// `bind()` call in the creation of such sockets, these are implicitly bound to a suitable local
/// address at connect time.
pub trait ConnectedUdp {
	/// Error type returned by send and receive operations.
	type Error: embedded_io_async::Error;

	/// Send the provided data to the connected peer
	async fn send(&mut self, data: &[u8]) -> Result<(), Self::Error>;

	/// Receive a datagram into the provided buffer.
	///
	/// If the received datagram exceeds the buffer's length, it is received regardless, and the
	/// remaining bytes are discarded. The full datagram size is still indicated in the result,
	/// allowing the recipient to detect that truncation.
	///
	/// ## Compatibility note
	///
	/// This deviates from the sync/nb equivalent trait in that it describes the overflow behavior
	/// (a possibility not considered there). The name deviates from the original `receive()` to
	/// make room for a version that is more zero-copy friendly.
	async fn receive_into(&mut self, buffer: &mut [u8]) -> Result<usize, Self::Error>;

	// WIP to allow zero-copy operation
	// The plain receive is simple and can be provided -- implementations that don't populate
	// receive calls from scatter-gather can just return a slice of the raw data instead, and rely
	// on the socket still being exclusively owned. receive_oned is harder as providing it requires
	// alloc.
	//
	// async fn receive(&mut self, buffer: &mut [u8]) -> utput = Result<impl AsRef<u8> + '_, Self::Error>;
	// async fn receive_owned(&mut self) -> Result<impl AsRef<u8> + 'static, Self::Error>;
}

/// This trait is implemented by UDP sockets.
///
/// The socket it represents is not necessarily bound (may not have a single local IP address, port
/// and interface), and is typically not connected (has no remote IP address and port). Both are
/// addresses are explicitly given in every call.
///
/// If there were constraints in place at socket creation time (typically on the local side), the
/// caller MUST pass in the same (or compatible) values, MAY and pass in unspecified values where
/// applicable. The implementer MAY check them for compatibility, and SHOULD do that in debug mode.
pub trait UnconnectedUdp {
	/// Error type returned by send and receive operations.
	type Error: embedded_io_async::Error;

	/// Send the provided data to a peer
	///
	/// ## Sending initial messages
	///
	/// The local address can be left unspecified by leaving any of its component zero -- that
	/// gives the "any" address (`[::]` / `0.0.0.0`), the uncspecified port (0) or the unspecified
	/// zone identifier (0). Unless the operating system provides facilities exceeding this crate's traits for
	/// enumerating local interfaces and addresses, this is the only way to initiate outbound
	/// traffic.
	///
	/// ## Responding to messages
	///
	/// Users who have previously received data from a peer and want to respond have a choice of
	/// sending from the address to which the original datagram was addressed, or from an unbound
	/// address. Both are valid choices in some situations, and the right choice depends on the
	/// protocol used.
	///
	/// Note that users of sockets created through [`UdpStack::bind_single()`] should always pass
	/// in that single address -- even though they've made their intention clear at construction.
	/// They can pass either the one obtained at socket creation time, or the one obtained at
	/// receive time; these should be equal. This allows implementations of the trait to use a
	/// single kind of socket for both sockets bound to a single and sockets bound to multiple
	/// addresses.
	async fn send(
		&mut self,
		local: SocketAddr,
		remote: SocketAddr,
		data: &[u8],
	) -> Result<(), Self::Error>;

	/// Receive a datagram into the provided buffer.
	///
	/// If the received datagram exceeds the buffer's length, it is received regardless, and the
	/// remaining bytes are discarded. The full datagram size is still indicated in the result,
	/// allowing the recipient to detect that truncation.
	///
	/// The local and remote address are given, in that order, in the result along with the number
	/// of bytes.
	async fn receive_into(
		&mut self,
		buffer: &mut [u8],
	) -> Result<(usize, SocketAddr, SocketAddr), Self::Error>;
}

/// This trait is implemented by UDP/IP stacks. The trait allows the underlying driver to
/// construct multiple connections that implement the I/O traits from embedded-io-async.
///
/// Note that stacks with exotic connection creation methods may still not implement this, yet have
/// objects that implement [`ConnectedUdp`] or similar.
pub trait UdpStack {
	/// Error type returned on socket creation failure.
	type Error: embedded_io_async::Error;

	/// Eventual socket return type of the [`.connect()`] method
	type Connected: ConnectedUdp<Error = Self::Error>;
	/// Eventual socket return type of the [`.bind_single()`] method
	type UniquelyBound: UnconnectedUdp<Error = Self::Error>;
	/// Eventual return type of the [`.bind_multiple()`] method
	type MultiplyBound: UnconnectedUdp<Error = Self::Error>;

	/// Create a socket that has a fixed remote address.
	///
	/// The local address is chosen automatically.
	///
	/// There is a provided implementation that implements this from the maximally unspecified
	/// local address and [`.connect_from()`], but may be provided more efficiently by
	/// implementers.
	async fn connect(
		&self,
		remote: SocketAddr,
	) -> Result<(SocketAddr, Self::Connected), Self::Error> {
		use crate::{Ipv4Addr, Ipv6Addr, SocketAddr::*, SocketAddrV4, SocketAddrV6};

		let local = match remote {
			V4(_) => V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)),
			V6(_) => V6(SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0)),
		};
		self.connect_from(local, remote).await
	}

	/// Create a socket that has a fixed remote address.
	///
	/// The local address is given explicitly, but may be partially unspecified; it is fixed by the
	/// network stack at connection time. The full local address is returned along with the
	/// connected socket, primarily for debugging purposes.
	async fn connect_from(
		&self,
		local: SocketAddr,
		remote: SocketAddr,
	) -> Result<(SocketAddr, Self::Connected), Self::Error>;

	/// Create a socket that has a fixed local address.
	///
	/// Note that the giving an unspecified address here is *not* the same as a POSIX `bind()` --
	/// if the underlying stack supports multiple local addresses, it will pick *one* of the
	/// applicable addresses, rather than binding to all of them.
	///
	/// The full local address is returned along with the bound socket; it may then be passed on to
	/// other protocols for advertising purposes.
	async fn bind_single(
		&self,
		local: SocketAddr,
	) -> Result<(SocketAddr, Self::UniquelyBound), Self::Error>;

	/// Create a socket that has no single fixed local address.
	///
	/// The IP address part of the local address is typically left unspecified, and the port is
	/// given. There are use cases for other constellations, and this interface does not rule out
	/// that they can be used, but they are rare (e.g. using the same IP address on different
	/// network interfaces, and listening to datagrams arriving at any of them) or not well
	/// supported by operating systems (e.g., binding to all ports at the same is not possible on
	/// POSIX systems, where giving port 0 to a bind makes the OS pick *some* suitable port).
	///
	/// Caveats:
	///
	/// * There is currently no way to pass in a local address that has an unspecified address
	///   family (which would effectively create a single socket that servers both IPv4 and IPv6);
	///   it is not specified whether stacks that use V6MAPPED IPv4 addresses could simply used
	///   that mechanism.
	///
	/// * It is currently not specified whether this mechanism can be used to join multicast
	///   groups.
	///
	/// * There is currently no hybrid binding that allows emulating what POSIX systems do when
	///   binding to `[::]:0`, that is, picking some available port but then still leaving the
	///   interface and IP address unspecified.
	async fn bind_multiple(&self, local: SocketAddr) -> Result<Self::MultiplyBound, Self::Error>;
}