moto_rt/
net.rs

1use super::netc;
2use crate::Error;
3use crate::ErrorCode;
4use crate::Result;
5use crate::RtFd;
6use crate::RtVdsoVtable;
7use crate::into_result;
8use core::sync::atomic::Ordering;
9use core::time::Duration;
10
11#[cfg(not(feature = "rustc-dep-of-std"))]
12extern crate alloc;
13
14// In theory, max UDP payload over IPv4 is
15// 65507 = 65535 - 20 (IP header) - 8 (UDP header).
16//
17// But in practice smoltcp refuses to fragment UDP datagrams
18// larger than 65493 bytes, so our practical MAX UDP payload is
19// this weird number.
20//
21// Some argue that it does not make sense to fragment UDP
22// datagrams, and so UDP payload should be 1472, or less.
23// While smoltcp may well be susceptible to DDOS fragmentation
24// atacks in this case, and so we may have to eventually
25// disable UDP packet (de)fragmentation, for now we try
26// to do our best and allow large UDP payloads.
27pub const MAX_UDP_PAYLOAD: usize = 65493;
28
29pub const SHUTDOWN_READ: u8 = 1;
30pub const SHUTDOWN_WRITE: u8 = 2;
31
32pub const PROTO_TCP: u8 = 1;
33pub const PROTO_UDP: u8 = 2;
34
35pub const SO_RCVTIMEO: u64 = 1;
36pub const SO_SNDTIMEO: u64 = 2;
37pub const SO_SHUTDOWN: u64 = 3;
38pub const SO_NODELAY: u64 = 4;
39pub const SO_TTL: u64 = 5;
40pub const SO_NONBLOCKING: u64 = 6;
41pub const SO_ERROR: u64 = 7;
42pub const SO_ONLY_IPV6: u64 = 8;
43pub const SO_LINGER: u64 = 9;
44pub const SO_BROADCAST: u64 = 10;
45pub const SO_MULTICAST_LOOP_V4: u64 = 11;
46pub const SO_MULTICAST_LOOP_V6: u64 = 12;
47pub const SO_MULTICAST_TTL_V4: u64 = 13;
48
49fn setsockopt(rt_fd: RtFd, opt: u64, ptr: usize, len: usize) -> Result<()> {
50    let vdso_setsockopt: extern "C" fn(RtFd, u64, usize, usize) -> ErrorCode = unsafe {
51        core::mem::transmute(
52            RtVdsoVtable::get().net_setsockopt.load(Ordering::Relaxed) as usize as *const (),
53        )
54    };
55
56    into_result(vdso_setsockopt(rt_fd, opt, ptr, len))
57}
58
59fn getsockopt(rt_fd: RtFd, opt: u64, ptr: usize, len: usize) -> Result<()> {
60    let vdso_getsockopt: extern "C" fn(RtFd, u64, usize, usize) -> ErrorCode = unsafe {
61        core::mem::transmute(
62            RtVdsoVtable::get().net_getsockopt.load(Ordering::Relaxed) as usize as *const (),
63        )
64    };
65
66    into_result(vdso_getsockopt(rt_fd, opt, ptr, len))
67}
68
69fn setsockopt_bool(rt_fd: RtFd, val: bool, opt: u64) -> Result<()> {
70    let val: u8 = if val { 1 } else { 0 };
71    setsockopt(rt_fd, opt, &val as *const _ as usize, 1)
72}
73
74fn getsockopt_bool(rt_fd: RtFd, opt: u64) -> Result<bool> {
75    let mut val = 0_u8;
76    getsockopt(rt_fd, opt, &mut val as *mut _ as usize, 1)?;
77    match val {
78        0 => Ok(false),
79        1 => Ok(true),
80        _ => panic!("bad bool opt val {val} for opt {opt}"),
81    }
82}
83
84pub fn bind(proto: u8, addr: &netc::sockaddr) -> Result<RtFd> {
85    let vdso_bind: extern "C" fn(u8, *const netc::sockaddr) -> RtFd = unsafe {
86        core::mem::transmute(
87            RtVdsoVtable::get().net_bind.load(Ordering::Relaxed) as usize as *const (),
88        )
89    };
90
91    to_result!(vdso_bind(proto, addr))
92}
93
94pub fn listen(rt_fd: RtFd, max_backlog: u32) -> Result<()> {
95    let vdso_listen: extern "C" fn(RtFd, u32) -> ErrorCode = unsafe {
96        core::mem::transmute(
97            RtVdsoVtable::get().net_listen.load(Ordering::Relaxed) as usize as *const (),
98        )
99    };
100
101    into_result(vdso_listen(rt_fd, max_backlog))
102}
103
104pub fn accept(rt_fd: RtFd) -> Result<(RtFd, netc::sockaddr)> {
105    let vdso_accept: extern "C" fn(RtFd, *mut netc::sockaddr) -> RtFd = unsafe {
106        core::mem::transmute(
107            RtVdsoVtable::get().net_accept.load(Ordering::Relaxed) as usize as *const (),
108        )
109    };
110
111    let mut addr: netc::sockaddr = unsafe { core::mem::zeroed() };
112    let res = vdso_accept(rt_fd, &mut addr);
113    if res < 0 {
114        return Err((-res as ErrorCode).into());
115    }
116
117    Ok((res, addr))
118}
119
120/// Create a TCP stream by connecting to a remote addr.
121pub fn tcp_connect(addr: &netc::sockaddr, timeout: Duration, nonblocking: bool) -> Result<RtFd> {
122    let vdso_tcp_connect: extern "C" fn(*const netc::sockaddr, u64, bool) -> RtFd = unsafe {
123        core::mem::transmute(
124            RtVdsoVtable::get().net_tcp_connect.load(Ordering::Relaxed) as usize as *const (),
125        )
126    };
127
128    let timeout = timeout.as_nanos().try_into().unwrap_or(u64::MAX);
129    to_result!(vdso_tcp_connect(addr, timeout, nonblocking))
130}
131
132pub fn udp_connect(rt_fd: RtFd, addr: &netc::sockaddr) -> Result<()> {
133    let vdso_udp_connect: extern "C" fn(RtFd, *const netc::sockaddr) -> ErrorCode = unsafe {
134        core::mem::transmute(
135            RtVdsoVtable::get().net_udp_connect.load(Ordering::Relaxed) as usize as *const (),
136        )
137    };
138
139    into_result(vdso_udp_connect(rt_fd, addr))
140}
141
142pub fn socket_addr(rt_fd: RtFd) -> Result<netc::sockaddr> {
143    let vdso_socket_addr: extern "C" fn(RtFd, *mut netc::sockaddr) -> ErrorCode = unsafe {
144        core::mem::transmute(
145            RtVdsoVtable::get().net_socket_addr.load(Ordering::Relaxed) as usize as *const (),
146        )
147    };
148
149    let mut addr: netc::sockaddr = unsafe { core::mem::zeroed() };
150    into_result(vdso_socket_addr(rt_fd, &mut addr))?;
151    Ok(addr)
152}
153
154pub fn peer_addr(rt_fd: RtFd) -> Result<netc::sockaddr> {
155    let vdso_peer_addr: extern "C" fn(RtFd, *mut netc::sockaddr) -> ErrorCode = unsafe {
156        core::mem::transmute(
157            RtVdsoVtable::get().net_peer_addr.load(Ordering::Relaxed) as usize as *const (),
158        )
159    };
160
161    let mut addr: netc::sockaddr = unsafe { core::mem::zeroed() };
162    into_result(vdso_peer_addr(rt_fd, &mut addr))?;
163    Ok(addr)
164}
165
166pub fn set_ttl(rt_fd: RtFd, ttl: u32) -> Result<()> {
167    setsockopt(rt_fd, SO_TTL, &ttl as *const _ as usize, 4)
168}
169
170pub fn ttl(rt_fd: RtFd) -> Result<u32> {
171    let mut ttl = 0_u32;
172    getsockopt(rt_fd, SO_TTL, &mut ttl as *mut _ as usize, 4)?;
173    Ok(ttl)
174}
175
176pub fn set_only_v6(rt_fd: RtFd, only_v6: bool) -> Result<()> {
177    setsockopt_bool(rt_fd, only_v6, SO_ONLY_IPV6)
178}
179
180pub fn only_v6(rt_fd: RtFd) -> Result<bool> {
181    getsockopt_bool(rt_fd, SO_ONLY_IPV6)
182}
183
184pub fn take_error(rt_fd: RtFd) -> Result<Option<Error>> {
185    let mut error = 0_u16;
186    getsockopt(rt_fd, SO_ERROR, &mut error as *mut _ as usize, 2)?;
187    if error == Error::Ok.into() {
188        Ok(None)
189    } else {
190        Ok(Some(error.into()))
191    }
192}
193
194pub fn set_nonblocking(rt_fd: RtFd, nonblocking: bool) -> Result<()> {
195    setsockopt_bool(rt_fd, nonblocking, SO_NONBLOCKING)
196}
197
198pub fn peek(rt_fd: RtFd, buf: &mut [u8]) -> Result<usize> {
199    let vdso_peek: extern "C" fn(i32, *mut u8, usize) -> i64 = unsafe {
200        core::mem::transmute(
201            RtVdsoVtable::get().net_peek.load(Ordering::Relaxed) as usize as *const (),
202        )
203    };
204
205    to_result!(vdso_peek(rt_fd, buf.as_mut_ptr(), buf.len()))
206}
207
208pub fn set_read_timeout(rt_fd: RtFd, timeout: Option<Duration>) -> Result<()> {
209    let timeout: u64 = match timeout {
210        Some(dur) => dur.as_nanos().try_into().unwrap_or(u64::MAX),
211        None => u64::MAX,
212    };
213
214    if timeout == 0 {
215        // See TcpStream::set_read_timeout() doc in Rust stdlib.
216        return Err(Error::InvalidArgument);
217    }
218
219    setsockopt(
220        rt_fd,
221        SO_RCVTIMEO,
222        &timeout as *const _ as usize,
223        core::mem::size_of::<u64>(),
224    )
225}
226
227pub fn read_timeout(rt_fd: RtFd) -> Result<Option<Duration>> {
228    let mut timeout_ns = 0_u64;
229
230    getsockopt(
231        rt_fd,
232        SO_RCVTIMEO,
233        &mut timeout_ns as *mut _ as usize,
234        core::mem::size_of::<u64>(),
235    )?;
236
237    if timeout_ns == u64::MAX {
238        Ok(None)
239    } else {
240        Ok(Some(Duration::from_nanos(timeout_ns)))
241    }
242}
243
244pub fn set_write_timeout(rt_fd: RtFd, timeout: Option<Duration>) -> Result<()> {
245    let timeout: u64 = match timeout {
246        Some(dur) => dur.as_nanos().try_into().unwrap_or(u64::MAX),
247        None => u64::MAX,
248    };
249
250    if timeout == 0 {
251        // See TcpStream::set_write_timeout() doc in Rust stdlib.
252        return Err(Error::InvalidArgument);
253    }
254
255    setsockopt(
256        rt_fd,
257        SO_SNDTIMEO,
258        &timeout as *const _ as usize,
259        core::mem::size_of::<u64>(),
260    )
261}
262
263pub fn write_timeout(rt_fd: RtFd) -> Result<Option<Duration>> {
264    let mut timeout_ns = 0_u64;
265
266    getsockopt(
267        rt_fd,
268        SO_SNDTIMEO,
269        &mut timeout_ns as *mut _ as usize,
270        core::mem::size_of::<u64>(),
271    )?;
272
273    if timeout_ns == u64::MAX {
274        Ok(None)
275    } else {
276        Ok(Some(Duration::from_nanos(timeout_ns)))
277    }
278}
279
280pub fn shutdown(rt_fd: RtFd, shutdown: u8) -> Result<()> {
281    if 0 != ((shutdown & !SHUTDOWN_READ) & !SHUTDOWN_WRITE) {
282        return Err(Error::InvalidArgument);
283    }
284
285    setsockopt(rt_fd, SO_SHUTDOWN, &shutdown as *const _ as usize, 1)
286}
287
288const MAX_LINGER_MS: u64 = 60_000; // 60 sec.
289pub fn set_linger(rt_fd: RtFd, timeout: Option<Duration>) -> Result<()> {
290    let linger_millis: u64 = if let Some(timo) = timeout {
291        let millis = timo.as_millis();
292        if millis > (MAX_LINGER_MS as u128) {
293            MAX_LINGER_MS
294        } else {
295            millis as u64
296        }
297    } else {
298        u64::MAX
299    };
300    setsockopt(rt_fd, SO_LINGER, &linger_millis as *const u64 as usize, 8)
301}
302
303pub fn linger(rt_fd: RtFd) -> Result<Option<Duration>> {
304    let mut linger_millis = 0_u64;
305    getsockopt(rt_fd, SO_LINGER, &mut linger_millis as *mut _ as usize, 8)?;
306    match linger_millis {
307        val if val <= MAX_LINGER_MS => Ok(Some(Duration::from_millis(val))),
308        u64::MAX => Ok(None),
309        _ => panic!("bad linger {linger_millis}"),
310    }
311}
312
313pub fn set_nodelay(rt_fd: RtFd, nodelay: bool) -> Result<()> {
314    setsockopt_bool(rt_fd, nodelay, SO_NODELAY)
315}
316
317pub fn nodelay(rt_fd: RtFd) -> Result<bool> {
318    getsockopt_bool(rt_fd, SO_NODELAY)
319}
320
321pub fn set_udp_broadcast(rt_fd: RtFd, val: bool) -> Result<()> {
322    setsockopt_bool(rt_fd, val, SO_BROADCAST)
323}
324
325pub fn udp_broadcast(rt_fd: RtFd) -> Result<bool> {
326    getsockopt_bool(rt_fd, SO_BROADCAST)
327}
328
329pub fn udp_recv_from(rt_fd: RtFd, buf: &mut [u8]) -> Result<(usize, netc::sockaddr)> {
330    let mut addr: netc::sockaddr = unsafe { core::mem::zeroed() };
331
332    let vdso_udp_recv_from: extern "C" fn(i32, *mut u8, usize, *mut netc::sockaddr) -> i64 = unsafe {
333        core::mem::transmute(
334            RtVdsoVtable::get()
335                .net_udp_recv_from
336                .load(Ordering::Relaxed) as usize as *const (),
337        )
338    };
339
340    let res = vdso_udp_recv_from(rt_fd, buf.as_mut_ptr(), buf.len(), &mut addr as *mut _);
341    if res < 0 {
342        Err(((-res) as ErrorCode).into())
343    } else {
344        Ok(((res as usize), addr))
345    }
346}
347
348pub fn udp_peek_from(rt_fd: RtFd, buf: &mut [u8]) -> Result<(usize, netc::sockaddr)> {
349    let mut addr: netc::sockaddr = unsafe { core::mem::zeroed() };
350
351    let vdso_udp_peek_from: extern "C" fn(i32, *mut u8, usize, *mut netc::sockaddr) -> i64 = unsafe {
352        core::mem::transmute(
353            RtVdsoVtable::get()
354                .net_udp_peek_from
355                .load(Ordering::Relaxed) as usize as *const (),
356        )
357    };
358
359    let res = vdso_udp_peek_from(rt_fd, buf.as_mut_ptr(), buf.len(), &mut addr as *mut _);
360    if res < 0 {
361        Err(((-res) as ErrorCode).into())
362    } else {
363        Ok(((res as usize), addr))
364    }
365}
366
367pub fn udp_send_to(rt_fd: RtFd, buf: &[u8], addr: &netc::sockaddr) -> Result<usize> {
368    let vdso_udp_send_to: extern "C" fn(i32, *const u8, usize, *const netc::sockaddr) -> i64 = unsafe {
369        core::mem::transmute(
370            RtVdsoVtable::get().net_udp_send_to.load(Ordering::Relaxed) as usize as *const (),
371        )
372    };
373
374    to_result!(vdso_udp_send_to(
375        rt_fd,
376        buf.as_ptr(),
377        buf.len(),
378        addr as *const _
379    ))
380}
381
382pub fn set_udp_multicast_loop_v4(rt_fd: RtFd, val: bool) -> Result<()> {
383    setsockopt_bool(rt_fd, val, SO_MULTICAST_LOOP_V4)
384}
385
386pub fn udp_multicast_loop_v4(rt_fd: RtFd) -> Result<bool> {
387    getsockopt_bool(rt_fd, SO_MULTICAST_LOOP_V4)
388}
389
390pub fn set_udp_multicast_ttl_v4(rt_fd: RtFd, val: u32) -> Result<()> {
391    setsockopt(rt_fd, SO_MULTICAST_TTL_V4, &val as *const _ as usize, 4)
392}
393
394pub fn udp_multicast_ttl_v4(rt_fd: RtFd) -> Result<u32> {
395    let mut ttl = 0_u32;
396    getsockopt(rt_fd, SO_MULTICAST_TTL_V4, &mut ttl as *mut _ as usize, 4)?;
397    Ok(ttl)
398}
399
400pub fn set_udp_multicast_loop_v6(rt_fd: RtFd, val: bool) -> Result<()> {
401    setsockopt_bool(rt_fd, val, SO_MULTICAST_LOOP_V6)
402}
403
404pub fn udp_multicast_loop_v6(rt_fd: RtFd) -> Result<bool> {
405    getsockopt_bool(rt_fd, SO_MULTICAST_LOOP_V6)
406}
407
408pub const JOIN_MULTICAST_OP: u64 = 1;
409pub const LEAVE_MULTICAST_OP: u64 = 2;
410
411pub fn join_udp_multicast_v4(
412    rt_fd: RtFd,
413    addr: &netc::in_addr,
414    iface: &netc::in_addr,
415) -> Result<()> {
416    let vdso_multicast_op_v4: extern "C" fn(
417        i32,
418        u64,
419        *const netc::in_addr,
420        *const netc::in_addr,
421    ) -> ErrorCode = unsafe {
422        core::mem::transmute(
423            RtVdsoVtable::get()
424                .net_udp_multicast_op_v4
425                .load(Ordering::Relaxed) as usize as *const (),
426        )
427    };
428
429    into_result(vdso_multicast_op_v4(
430        rt_fd,
431        JOIN_MULTICAST_OP,
432        addr as *const _,
433        iface as *const _,
434    ))
435}
436
437pub fn leave_udp_multicast_v4(
438    rt_fd: RtFd,
439    addr: &netc::in_addr,
440    iface: &netc::in_addr,
441) -> Result<()> {
442    let vdso_multicast_op_v4: extern "C" fn(
443        i32,
444        u64,
445        *const netc::in_addr,
446        *const netc::in_addr,
447    ) -> ErrorCode = unsafe {
448        core::mem::transmute(
449            RtVdsoVtable::get()
450                .net_udp_multicast_op_v4
451                .load(Ordering::Relaxed) as usize as *const (),
452        )
453    };
454
455    into_result(vdso_multicast_op_v4(
456        rt_fd,
457        LEAVE_MULTICAST_OP,
458        addr as *const _,
459        iface as *const _,
460    ))
461}
462
463pub fn join_udp_multicast_v6(rt_fd: RtFd, addr: &netc::in6_addr, iface: u32) -> Result<()> {
464    let vdso_multicast_op_v6: extern "C" fn(i32, u64, *const netc::in6_addr, u32) -> ErrorCode = unsafe {
465        core::mem::transmute(
466            RtVdsoVtable::get()
467                .net_udp_multicast_op_v6
468                .load(Ordering::Relaxed) as usize as *const (),
469        )
470    };
471
472    into_result(vdso_multicast_op_v6(
473        rt_fd,
474        JOIN_MULTICAST_OP,
475        addr as *const _,
476        iface,
477    ))
478}
479
480pub fn leave_udp_multicast_v6(rt_fd: RtFd, addr: &netc::in6_addr, iface: u32) -> Result<()> {
481    let vdso_multicast_op_v6: extern "C" fn(i32, u64, *const netc::in6_addr, u32) -> ErrorCode = unsafe {
482        core::mem::transmute(
483            RtVdsoVtable::get()
484                .net_udp_multicast_op_v6
485                .load(Ordering::Relaxed) as usize as *const (),
486        )
487    };
488
489    into_result(vdso_multicast_op_v6(
490        rt_fd,
491        LEAVE_MULTICAST_OP,
492        addr as *const _,
493        iface,
494    ))
495}
496
497pub fn lookup_host(
498    host: &str,
499    port: u16,
500) -> Result<(u16, alloc::collections::VecDeque<netc::sockaddr>)> {
501    let vdso_lookup: extern "C" fn(
502        /* host_bytes */ *const u8,
503        /* host_bytes_sz */ usize,
504        /* port */ u16,
505        /* result_addr */ *mut usize,
506        /* result_len */ *mut usize,
507    ) -> ErrorCode = unsafe {
508        core::mem::transmute(
509            RtVdsoVtable::get().dns_lookup.load(Ordering::Relaxed) as usize as *const (),
510        )
511    };
512
513    let mut result_addr: usize = 0;
514    let mut result_num: usize = 0;
515
516    into_result(vdso_lookup(
517        host.as_bytes().as_ptr(),
518        host.len(),
519        port,
520        &mut result_addr,
521        &mut result_num,
522    ))?;
523
524    let addresses: &[netc::sockaddr] =
525        unsafe { core::slice::from_raw_parts(result_addr as *const netc::sockaddr, result_num) };
526
527    let mut vecdec = alloc::collections::VecDeque::new();
528    for addr in addresses {
529        vecdec.push_back(*addr);
530    }
531
532    let layout = core::alloc::Layout::from_size_align(
533        core::mem::size_of::<netc::sockaddr>() * result_num,
534        16,
535    )
536    .unwrap();
537    unsafe { crate::alloc::dealloc(result_addr as *mut u8, layout) };
538
539    Ok((port, vecdec))
540}