-
Notifications
You must be signed in to change notification settings - Fork 85
/
std_udp.rs
312 lines (293 loc) · 10.9 KB
/
std_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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
use smoltcp::wire::{IpAddress, IpEndpoint};
use ticktimer_server::Ticktimer;
use crate::*;
/// Overall architecture for libstd UDP implementation.
///
/// Sockets are stored in the PID/SocketHandle HashMap `process_sockets` (this is shared with TCP)
/// `recv` requests create `UpdStdState` objects, that are stored in a `udp_rx` Vec.
pub(crate) fn std_udp_bind(
mut msg: xous::MessageEnvelope,
_iface: &mut Interface,
sockets: &mut SocketSet,
our_sockets: &mut Vec<Option<SocketHandle>>,
) {
// Ignore nonblocking and scalar messages
let body = match msg.body.memory_message_mut() {
Some(b) => b,
None => {
log::trace!("invalid message type");
std_failure(msg, NetError::LibraryError);
return;
}
};
let bytes = unsafe { body.buf.as_slice::<u8>() };
let local_port = u16::from_le_bytes([bytes[0], bytes[1]]);
let address = match parse_address(&bytes[2..]) {
Some(addr) => addr,
None => {
log::trace!("couldn't parse address");
std_failure(msg, NetError::LibraryError);
return;
}
};
let udp_rx_buffer =
udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY], vec![0; 65535]);
let udp_tx_buffer =
udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY, udp::PacketMetadata::EMPTY], vec![0; 65535]);
let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer);
let handle = sockets.add(udp_socket);
let udp_socket = sockets.get_mut::<udp::Socket>(handle);
// Attempt to connect, returning the error if there is one
if let Err(e) = udp_socket.bind(IpEndpoint { addr: address, port: local_port }).map_err(|e| match e {
smoltcp::socket::udp::BindError::InvalidState => NetError::SocketInUse,
smoltcp::socket::udp::BindError::Unaddressable => NetError::Unaddressable,
}) {
log::trace!("couldn't connect: {:?}", e);
std_failure(msg, e);
return;
}
// Add the socket into our process' list of sockets, and pass the index back as the `fd` parameter for
// future reference.
let idx = insert_or_append(our_sockets, handle) as u8;
let body = msg.body.memory_message_mut().unwrap();
let bfr = unsafe { body.buf.as_slice_mut::<u8>() };
log::trace!("successfully connected: {} -> {:?}:{}", idx, address, local_port);
bfr[0] = 0;
bfr[1] = idx;
}
pub(crate) fn std_udp_rx(
mut msg: xous::MessageEnvelope,
timer: &Ticktimer,
iface: &mut Interface,
sockets: &mut SocketSet,
udp_rx_waiting: &mut Vec<Option<UdpStdState>>,
our_sockets: &Vec<Option<SocketHandle>>,
) {
let connection_handle_index = (msg.body.id() >> 16) & 0xffff;
let body = match msg.body.memory_message_mut() {
Some(body) => body,
None => {
std_failure(msg, NetError::LibraryError);
return;
}
};
// this is not used
body.valid = None;
let handle = match our_sockets.get(connection_handle_index) {
Some(Some(val)) => val,
_ => {
std_failure(msg, NetError::Invalid);
return;
}
};
let args = unsafe { body.buf.as_slice::<u8>() };
let nonblocking = args[0] == 0;
let expiry = if !nonblocking {
let to = u64::from_le_bytes(args[1..9].try_into().unwrap());
if to == 0 { None } else { Some(to + timer.elapsed_ms()) }
} else {
None
};
let do_peek = body.offset.is_some();
log::debug!("udp rx from fd {}", connection_handle_index);
let local_addr = match iface.ipv4_addr() {
Some(addr) => addr,
None => {
std_failure(msg, NetError::Unaddressable);
return;
}
};
let socket = sockets.get_mut::<udp::Socket>(*handle);
let port = socket.endpoint().port;
// TODO: comment below may be invalid after port to latest smoltcp. Error handler
// is also suspect.
//
// force the local address to correspond to our (one and only) IP address
// the underlying smoltcp library can't handle unspecified source addresses
// because the library itself works with multiple interfaces and has no default resolution mechanism
// this may eventually get fixed see https://github.com/smoltcp-rs/smoltcp/issues/599
if socket.endpoint().addr.expect("UDP endpoint missing") != IpAddress::Ipv4(local_addr) {
if socket.is_open() {
socket.close();
}
if let Err(e) =
socket.bind(IpEndpoint { addr: IpAddress::Ipv4(local_addr), port }).map_err(|e| match e {
smoltcp::socket::udp::BindError::Unaddressable => NetError::WouldBlock,
_ => NetError::LibraryError,
})
{
std_failure(msg, e);
return;
}
}
if socket.can_recv() {
log::debug!("receiving data right away");
if do_peek {
// have to duplicate the code because Endpoint on peek is &, but on recv is not. This
// difference in types means you can't do a pattern match assign to a common variable.
match socket.peek() {
Ok((data, endpoint)) => {
udp_rx_success(unsafe { body.buf.as_slice_mut() }, data, endpoint.endpoint);
}
Err(e) => {
log::error!("unable to receive: {:?}", e);
std_failure(msg, NetError::LibraryError);
}
}
} else {
match socket.recv() {
Ok((data, endpoint)) => {
log::debug!("immediate udp rx");
udp_rx_success(unsafe { body.buf.as_slice_mut() }, data, endpoint.endpoint);
}
Err(e) => {
log::error!("unable to receive: {:?}", e);
std_failure(msg, NetError::LibraryError);
}
}
};
return;
}
if nonblocking {
std_failure(msg, NetError::WouldBlock);
return;
}
log::trace!("UDP socket was not ready to receive, adding it to list of waiting messages");
// Adding the message to the udp_rx_waiting list prevents it from going out of scope and
// thus prevents the .drop() method from being called. Since messages are returned to the sender
// in the .drop() method, this keeps the caller blocked for the lifetime of the message.
insert_or_append(udp_rx_waiting, UdpStdState {
msg, /* <-- msg is inserted into the udp_rx_waiting vector, thus preventing the lend_mut from
* returning. */
handle: *handle,
expiry,
});
}
pub(crate) fn std_udp_tx(
mut msg: xous::MessageEnvelope,
iface: &mut Interface,
sockets: &mut SocketSet,
our_sockets: &Vec<Option<SocketHandle>>,
) {
// unpack meta
let connection_handle_index = (msg.body.id() >> 16) & 0xffff;
let body = match msg.body.memory_message_mut() {
Some(body) => body,
None => {
std_failure(msg, NetError::LibraryError);
return;
}
};
let handle = match our_sockets.get(connection_handle_index) {
Some(Some(val)) => val,
_ => {
std_failure(msg, NetError::Invalid);
return;
}
};
// unpack arguments
let bytes = unsafe { body.buf.as_slice::<u8>() };
let remote_port = u16::from_le_bytes([bytes[0], bytes[1]]);
let address = match parse_address(&bytes[2..]) {
Some(addr) => addr,
None => {
log::trace!("couldn't parse address");
std_failure(msg, NetError::LibraryError);
return;
}
};
let len = u16::from_le_bytes([bytes[19], bytes[20]]);
// attempt the tx
log::debug!(
"udp tx to fd {} -> {:?}:{} {:?}",
connection_handle_index,
address,
remote_port,
&bytes[21..21 + len as usize]
);
let local_addr = match iface.ipv4_addr() {
Some(addr) => addr,
None => {
std_failure(msg, NetError::Unaddressable);
return;
}
};
let socket = sockets.get_mut::<udp::Socket>(*handle);
let port = socket.endpoint().port;
// force the local address to correspond to our (one and only) IP address
// the underlying smoltcp library can't handle unspecified source addresses
// because the library itself works with multiple interfaces and has no default resolution mechanism
// this may eventually get fixed see https://github.com/smoltcp-rs/smoltcp/issues/599
if socket.endpoint().addr.expect("UDP TX endpoint missing") != IpAddress::Ipv4(local_addr) {
if socket.is_open() {
socket.close();
}
if let Err(e) =
socket.bind(IpEndpoint { addr: IpAddress::Ipv4(local_addr), port }).map_err(|e| match e {
smoltcp::socket::udp::BindError::InvalidState => NetError::WouldBlock,
smoltcp::socket::udp::BindError::Unaddressable => NetError::Unaddressable,
})
{
std_failure(msg, e);
return;
}
}
match socket.send_slice(&bytes[21..21 + len as usize], IpEndpoint::new(address, remote_port)) {
Ok(_) => unsafe {
body.buf.as_slice_mut()[0] = 0;
},
Err(_e) => {
// the only type of error returned from smoltcp in this case is if the destination is not
// addressible.
std_failure(msg, NetError::Unaddressable);
return;
}
}
}
pub(crate) fn udp_rx_success(buf: &mut [u8], rx: &[u8], ep: IpEndpoint) {
log::debug!("udp_rx: {:?} -> {:x?}", ep, rx);
buf[0] = 0;
let rx_len = (rx.len() as u16).to_le_bytes();
buf[1] = rx_len[0];
buf[2] = rx_len[1];
match ep.addr {
IpAddress::Ipv4(a) => {
buf[3] = 4;
for (&s, d) in a.0.iter().zip(buf[4..8].iter_mut()) {
*d = s;
}
}
IpAddress::Ipv6(a) => {
buf[3] = 6;
for (&s, d) in a.0.iter().zip(buf[4..20].iter_mut()) {
*d = s;
}
}
}
let port = ep.port.to_le_bytes();
buf[20] = port[0];
buf[21] = port[1];
for (&s, d) in rx.iter().zip(buf[22..].iter_mut()) {
*d = s;
}
}
pub(crate) fn std_failure(mut env: xous::MessageEnvelope, code: NetError) -> Option<()> {
log::trace!("std_failure: {:?}", code);
// If it's not a memory message, don't fill in the return information.
let body = match env.body.memory_message_mut() {
None => {
// But do respond to the scalar message, if it's a BlockingScalar
if env.body.scalar_message().is_some() && env.body.is_blocking() {
xous::return_scalar(env.sender, code as usize).ok();
}
return None;
}
Some(b) => b,
};
body.valid = None;
let s: &mut [u8] = unsafe { body.buf.as_slice_mut() };
let mut i = s.iter_mut();
*i.next()? = 1;
*i.next()? = code as u8;
None
}