net: implement new connect options

This commit is contained in:
Mark Poliakov 2024-11-01 11:44:57 +02:00
parent e43b7ee44b
commit 98fe60bc12
8 changed files with 96 additions and 24 deletions

View File

@ -548,8 +548,15 @@ impl PacketSocket for RawSocket {
}
impl TcpSocket {
pub fn connect(remote: SocketAddr) -> Result<(SocketAddr, Arc<TcpSocket>), Error> {
block!(Self::connect_async(remote).await)?
pub fn connect(
remote: SocketAddr,
timeout: Option<Duration>,
) -> Result<(SocketAddr, Arc<TcpSocket>), Error> {
let future = Self::connect_async(remote);
match timeout {
Some(timeout) => block!(run_with_timeout(timeout, future).await)?.into(),
None => block!(future.await)?,
}
}
pub fn accept_remote(

View File

@ -7,6 +7,7 @@ use core::{
use alloc::vec::Vec;
use futures_util::{future::BoxFuture, Future, FutureExt};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::error::Error;
// 1..32ms, tick every 1ms
static SHORT_TERM_SLEEPS: IrqSafeSpinlock<TimerWheel<32>> =
@ -143,6 +144,15 @@ pub fn run_with_timeout<'a, T: 'a, F: Future<Output = T> + Send + 'a>(
}
}
impl<T> From<FutureTimeout<Result<T, Error>>> for Result<T, Error> {
fn from(value: FutureTimeout<Result<T, Error>>) -> Self {
match value {
FutureTimeout::Ok(res) => res,
FutureTimeout::Timeout => Err(Error::TimedOut),
}
}
}
/// Updates the runtime's time
pub fn tick(_now: Duration) {
SHORT_TERM_SLEEPS.lock().tick();

View File

@ -3,7 +3,7 @@ use core::{mem::MaybeUninit, net::SocketAddr};
use abi::{
error::Error,
io::RawFd,
net::{SocketOption, SocketType},
net::{SocketConnect, SocketOption, SocketType},
};
use libk::{task::thread::Thread, vfs::File};
use ygg_driver_net_core::socket::{RawSocket, TcpListener, TcpSocket, UdpSocket};
@ -12,26 +12,26 @@ use crate::syscall::run_with_io;
// Network
pub(crate) fn connect_socket(
socket_fd: Option<RawFd>,
remote: &SocketAddr,
ty: SocketType,
connect: &mut SocketConnect,
local_result: &mut MaybeUninit<SocketAddr>,
) -> Result<RawFd, Error> {
assert!(socket_fd.is_none());
let thread = Thread::current();
let process = thread.process();
run_with_io(&process, |mut io| {
let (local, file) = match ty {
SocketType::TcpStream => {
let (local, socket) = TcpSocket::connect((*remote).into())?;
(local, File::from_stream_socket(socket))
let (local, fd) = match connect {
&mut SocketConnect::Udp(fd, remote) => {
todo!("UDP socket connect");
}
&mut SocketConnect::Tcp(remote, timeout) => {
let (local, socket) = TcpSocket::connect(remote.into(), timeout)?;
let fd = io
.files
.place_file(File::from_stream_socket(socket), true)?;
(local.into(), fd)
}
_ => return Err(Error::InvalidArgument),
};
let fd = io.files.place_file(file, true)?;
local_result.write(local.into());
local_result.write(local);
Ok(fd)
})
}

View File

@ -27,4 +27,5 @@ enum Error(u32) {
HostUnreachable = 24,
UndefinedSyscall = 25,
DirectoryNotEmpty = 26,
NotConnected = 27,
}

View File

@ -2,7 +2,6 @@
extern {
type Duration = core::time::Duration;
type SocketAddr = core::net::SocketAddr;
type AtomicU32 = core::sync::atomic::AtomicU32;
// TODO "tagged union" enums are not yet implemented
@ -15,7 +14,9 @@ extern {
#[thin]
type ExitCode = yggdrasil_abi::process::ExitCode;
type SocketAddr = core::net::SocketAddr;
type SocketOption = yggdrasil_abi::net::SocketOption;
type SocketConnect = yggdrasil_abi::net::SocketConnect;
type SentMessage = yggdrasil_abi::io::SentMessage;
type ReceivedMessageMetadata = yggdrasil_abi::io::ReceivedMessageMetadata;
@ -122,7 +123,7 @@ syscall receive_message(
) -> Result<()>;
// Network
syscall connect_socket(sock_fd: Option<RawFd>, remote: &SocketAddr, ty: SocketType, local: &mut MaybeUninit<SocketAddr>) -> Result<RawFd>;
syscall connect_socket(connect: &mut SocketConnect, local: &mut MaybeUninit<SocketAddr>) -> Result<RawFd>;
syscall bind_socket(address: &SocketAddr, ty: SocketType) -> Result<RawFd>;
syscall accept(sock_fd: RawFd, remote: &mut MaybeUninit<SocketAddr>) -> Result<RawFd>;

View File

@ -11,6 +11,7 @@ pub mod types;
use core::time::Duration;
pub use crate::generated::SocketType;
use crate::io::RawFd;
pub use types::{
ip_addr::{IpAddr, Ipv4Addr, Ipv6Addr},
@ -19,11 +20,15 @@ pub use types::{
MacAddress,
};
// primitive_enum! {
// #[doc = "Defines a type of a socket"]
// pub enum SocketType: u32 {
// }
// }
/// Describes a socket connect operation
#[derive(Clone, Debug)]
pub enum SocketConnect {
/// Connect a TCP socket with optional timeout.
Tcp(core::net::SocketAddr, Option<Duration>),
/// "Connect" an UDP socket, this just sets the sender's address in the socket so
/// the caller can then use send().
Udp(RawFd, core::net::SocketAddr),
}
/// Describes a method to query an interface
#[derive(Clone, Debug)]
@ -47,6 +52,8 @@ pub enum SocketOption<'a> {
UnbindInterface,
/// (Read-only) Hardware address of the bound interface
BoundHardwareAddress(MacAddress),
/// (Read-only) Remote socket address
PeerAddress(Option<core::net::SocketAddr>),
/// If set, reception will return [crate::error::Error::WouldBlock] if the socket has
/// no data in its queue/buffer.
NonBlocking(bool),

View File

@ -1,5 +1,5 @@
//! Yggdrasil OS application runtime
#![feature(rustc_private)]
#![feature(rustc_private, decl_macro)]
#![no_std]
#![deny(missing_docs)]
#![allow(nonstandard_style)]

View File

@ -1,6 +1,31 @@
//! Network-related functions and types
pub use abi::net::{MacAddress, SocketInterfaceQuery, SocketOption, SocketType};
use core::{mem::MaybeUninit, net::SocketAddr, time::Duration};
pub use abi::net::{MacAddress, SocketConnect, SocketInterfaceQuery, SocketOption, SocketType};
use abi::{error::Error, io::RawFd};
use crate::sys;
macro socket_option_variant {
($opt:ident: bool) => { $crate::net::SocketOption::$opt(false) },
($opt:ident: Option) => { $crate::net::SocketOption::$opt(None) },
($opt:ident: int) => { $crate::net::SocketOption::$opt(0) }
}
/// Helper macro for getting a [SocketOption] with 1 argument
pub macro get_socket_option1($fd:expr, $opt:ident: $ty:tt) {{
let mut option = socket_option_variant!($opt: $ty);
match unsafe { $crate::sys::get_socket_option($fd, &mut option) } {
Ok(()) => {
let $crate::net::SocketOption::$opt(value) = option else { unreachable!() };
Ok(value)
}
Err(error) => {
Err(error)
}
}
}}
#[cfg(any(feature = "alloc", rust_analyzer))]
pub mod dns {
@ -52,3 +77,24 @@ pub mod dns {
Ok(message)
}
}
fn connect_inner(connect: &mut SocketConnect) -> Result<(SocketAddr, RawFd), Error> {
let mut local = MaybeUninit::uninit();
let fd = unsafe { sys::connect_socket(connect, &mut local) }?;
let local = unsafe { local.assume_init() };
Ok((local, fd))
}
/// Connect to a TCP listener
pub fn connect_tcp(
remote: SocketAddr,
timeout: Option<Duration>,
) -> Result<(SocketAddr, RawFd), Error> {
connect_inner(&mut SocketConnect::Tcp(remote.into(), timeout))
}
/// "Connect" an UDP socket
pub fn connect_udp(socket_fd: RawFd, remote: SocketAddr) -> Result<(), Error> {
connect_inner(&mut SocketConnect::Udp(socket_fd, remote.into()))?;
Ok(())
}