Skip to content

Commit dac9aec

Browse files
committed
network: change implementation of sockopt
The goal is to permit non boolean options in the future, and also to add the keepalive option which is required by some other software
1 parent 95cbe7b commit dac9aec

5 files changed

Lines changed: 212 additions & 91 deletions

File tree

src/executor/network.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ pub(crate) fn init() {
314314
spawn(network_run());
315315
#[cfg(feature = "dhcpv4")]
316316
spawn(dhcpv4_run());
317+
} else {
318+
warn!("Network initialization failed.");
317319
}
318320
}
319321

src/fd/delegate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::fd::socket::vsock;
1717
use crate::fd::stdio::{ConsoleStderr, ConsoleStdin, ConsoleStdout};
1818
#[cfg(feature = "uhyve")]
1919
use crate::fd::stdio::{UhyveStderr, UhyveStdin, UhyveStdout};
20-
use crate::fd::{AccessPermission, ObjectInterface, PollEvent, StatusFlags};
20+
use crate::fd::{AccessPermission, ObjectInterface, PollEvent, SocketOptionValue, StatusFlags};
2121
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
2222
use crate::fd::{Endpoint, ListenEndpoint, SocketOption};
2323
use crate::fs::mem::{MemDirectoryInterface, RamFileInterface, RomFileInterface};
@@ -158,7 +158,7 @@ impl ObjectInterface for Fd {
158158
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
159159
async fn listen(&mut self, _backlog: i32) -> io::Result<()>;
160160
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
161-
async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()>;
161+
async fn setsockopt(&self, _opt: SocketOption, _optval: SocketOptionValue) -> io::Result<()>;
162162
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
163163
async fn getsockopt(&self, _opt: SocketOption) -> io::Result<c_int>;
164164
#[cfg(any(feature = "net", feature = "virtio-vsock"))]

src/fd/mod.rs

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
use alloc::sync::Arc;
2-
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
32
use core::ffi::c_int;
43
use core::future;
54
use core::mem::MaybeUninit;
65
use core::pin::pin;
76
use core::task::Poll::{Pending, Ready};
87
use core::time::Duration;
98

10-
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
11-
use num_enum::TryFromPrimitive;
9+
use num_enum::{IntoPrimitive, TryFromPrimitive};
1210
#[cfg(feature = "net")]
1311
use smoltcp::wire::{IpEndpoint, IpListenEndpoint};
1412

@@ -18,6 +16,8 @@ use crate::errno::Errno;
1816
use crate::executor::block_on;
1917
use crate::fs::{FileAttr, SeekWhence};
2018
use crate::io;
19+
#[cfg(feature = "net")]
20+
use crate::syscalls::socket::{Ipproto, SOL_SOCKET, socklen_t};
2121

2222
mod delegate;
2323
mod eventfd;
@@ -48,12 +48,100 @@ pub(crate) enum ListenEndpoint {
4848
Vsock(socket::vsock::VsockListenEndpoint),
4949
}
5050

51-
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
52-
#[derive(TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
53-
#[repr(i32)]
51+
#[allow(dead_code)]
52+
#[derive(Debug, PartialEq, Eq)]
5453
pub(crate) enum SocketOption {
55-
TcpNodelay = 1,
54+
TcpOption(SocketOptionTcp),
55+
SocketOption(SocketOptionSocket),
56+
}
57+
58+
#[cfg(feature = "net")]
59+
impl SocketOption {
60+
pub fn from_level_optname(level: i32, optname: i32) -> Option<SocketOption> {
61+
if level == SOL_SOCKET {
62+
SocketOptionSocket::try_from(optname)
63+
.ok()
64+
.map(SocketOption::SocketOption)
65+
} else {
66+
let protocol = u8::try_from(level)
67+
.ok()
68+
.and_then(|proto| Ipproto::try_from(proto).ok())?;
69+
70+
match protocol {
71+
Ipproto::Tcp => SocketOptionTcp::try_from(optname)
72+
.ok()
73+
.map(SocketOption::TcpOption),
74+
_ => None,
75+
}
76+
}
77+
}
78+
}
79+
80+
#[cfg(feature = "net")]
81+
pub struct SocketOptionValue {
82+
optval: *const core::ffi::c_void,
83+
optlen: socklen_t,
84+
}
85+
86+
#[cfg(not(feature = "net"))]
87+
pub struct SocketOptionValue;
88+
89+
unsafe impl Send for SocketOptionValue {}
90+
91+
#[cfg(feature = "net")]
92+
impl SocketOptionValue {
93+
pub fn new(optval: *const core::ffi::c_void, optlen: socklen_t) -> Self {
94+
Self { optval, optlen }
95+
}
96+
}
97+
98+
#[cfg(feature = "net")]
99+
impl TryFrom<&SocketOptionValue> for i32 {
100+
type Error = Errno;
101+
102+
fn try_from(value: &SocketOptionValue) -> Result<Self, Self::Error> {
103+
if value.optval.is_null() {
104+
return Err(Errno::Inval);
105+
}
106+
107+
if value.optlen != size_of::<i32>() as u32 {
108+
return Err(Errno::Inval);
109+
}
110+
111+
let value = unsafe { *value.optval.cast::<i32>() };
112+
Ok(value)
113+
}
114+
}
115+
116+
#[cfg(feature = "net")]
117+
impl TryFrom<&SocketOptionValue> for bool {
118+
type Error = Errno;
119+
120+
fn try_from(value: &SocketOptionValue) -> Result<Self, Self::Error> {
121+
let value: i32 = value.try_into()?;
122+
Ok(value != 0)
123+
}
124+
}
125+
126+
#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
127+
#[repr(i32)]
128+
#[non_exhaustive]
129+
pub(crate) enum SocketOptionTcp {
130+
#[doc(alias = "TCP_NODELAY")]
131+
TcpNoDelay = 1,
132+
}
133+
134+
#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)]
135+
#[repr(i32)]
136+
#[non_exhaustive]
137+
pub(crate) enum SocketOptionSocket {
138+
#[doc(alias = "SO_REUSEADDR")]
139+
ReuseAddr = 1,
140+
#[doc(alias = "SO_KEEPALIVE")]
141+
KeepAlive = 8,
142+
#[doc(alias = "SO_SNDBUF")]
56143
SoSndbuf = 0x1001,
144+
#[doc(alias = "SO_RCVBUF")]
57145
SoRcvbuf = 0x1002,
58146
}
59147

@@ -265,7 +353,7 @@ pub(crate) trait ObjectInterface: Sync + Send {
265353

266354
/// `setsockopt` sets options on sockets
267355
#[cfg(any(feature = "net", feature = "virtio-vsock"))]
268-
async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()> {
356+
async fn setsockopt(&self, _opt: SocketOption, _optval: SocketOptionValue) -> io::Result<()> {
269357
Err(Errno::Notsock)
270358
}
271359

src/fd/socket/tcp.rs

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ use smoltcp::wire::{IpEndpoint, Ipv4Address, Ipv6Address};
1313
use crate::errno::Errno;
1414
use crate::executor::block_on;
1515
use crate::executor::network::{Handle, NIC, wake_network_waker};
16-
use crate::fd::{self, Endpoint, Fd, ListenEndpoint, ObjectInterface, PollEvent, SocketOption};
16+
use crate::fd::{
17+
self, Endpoint, Fd, ListenEndpoint, ObjectInterface, PollEvent, SocketOption,
18+
SocketOptionSocket, SocketOptionTcp, SocketOptionValue,
19+
};
1720
use crate::syscalls::socket::Af;
1821
use crate::{DEFAULT_KEEP_ALIVE_INTERVAL, io};
1922

@@ -426,19 +429,38 @@ impl ObjectInterface for Socket {
426429
Ok(())
427430
}
428431

429-
async fn setsockopt(&self, opt: SocketOption, optval: bool) -> io::Result<()> {
430-
if opt == SocketOption::TcpNodelay {
431-
let mut guard = NIC.lock();
432-
let nic = guard.as_nic_mut().unwrap();
432+
async fn setsockopt(&self, opt: SocketOption, optval: SocketOptionValue) -> io::Result<()> {
433+
let mut guard = NIC.lock();
434+
let nic = guard.as_nic_mut().unwrap();
433435

434-
for handle in self.handle.iter().copied() {
435-
let socket = nic.get_mut_socket::<tcp::Socket<'_>>(handle);
436-
socket.set_nagle_enabled(optval);
436+
match opt {
437+
SocketOption::TcpOption(SocketOptionTcp::TcpNoDelay) => {
438+
let is_enabled = (&optval).try_into()?;
439+
for handle in self.handle.iter().copied() {
440+
let socket = nic.get_mut_socket::<tcp::Socket<'_>>(handle);
441+
socket.set_nagle_enabled(is_enabled);
442+
}
443+
Ok(())
437444
}
445+
SocketOption::SocketOption(SocketOptionSocket::KeepAlive) => {
446+
let keepalive: bool = (&optval).try_into()?;
447+
let keepalive = if keepalive {
448+
Some(Duration::from_secs(120))
449+
} else {
450+
None
451+
};
438452

439-
Ok(())
440-
} else {
441-
Err(Errno::Inval)
453+
for i in self.handle.iter() {
454+
let socket = nic.get_mut_socket::<tcp::Socket<'_>>(*i);
455+
socket.set_keep_alive(keepalive);
456+
}
457+
458+
Ok(())
459+
}
460+
other => {
461+
warn!("TCP: unsupported option {other:?}");
462+
Err(Errno::Inval)
463+
}
442464
}
443465
}
444466

@@ -448,9 +470,22 @@ impl ObjectInterface for Socket {
448470
let socket = nic.get_mut_socket::<tcp::Socket<'_>>(*self.handle.first().unwrap());
449471

450472
match opt {
451-
SocketOption::TcpNodelay => Ok(socket.nagle_enabled().into()),
452-
SocketOption::SoSndbuf => Ok(c_int::try_from(socket.send_capacity()).unwrap()),
453-
SocketOption::SoRcvbuf => Ok(c_int::try_from(socket.recv_capacity()).unwrap()),
473+
SocketOption::TcpOption(SocketOptionTcp::TcpNoDelay) => {
474+
Ok(socket.nagle_enabled().into())
475+
}
476+
SocketOption::SocketOption(SocketOptionSocket::KeepAlive) => {
477+
Ok(socket.keep_alive().is_some().into())
478+
}
479+
SocketOption::SocketOption(SocketOptionSocket::SoSndbuf) => {
480+
Ok(c_int::try_from(socket.send_capacity()).unwrap())
481+
}
482+
SocketOption::SocketOption(SocketOptionSocket::SoRcvbuf) => {
483+
Ok(c_int::try_from(socket.recv_capacity()).unwrap())
484+
}
485+
other => {
486+
warn!("TCP: unsupported option {other:?}");
487+
Err(Errno::Inval)
488+
}
454489
}
455490
}
456491

0 commit comments

Comments
 (0)