asterinas/kernel/src/util/net/options/mod.rs

153 lines
5.0 KiB
Rust
Raw Normal View History

2024-01-03 03:22:36 +00:00
// SPDX-License-Identifier: MPL-2.0
2023-12-28 09:24:23 +00:00
//! This module introduces utilities to support Linux get/setsockopt syscalls.
//!
//! These two syscalls are used to get/set options for a socket. These options can be at different
//! socket levels and of different types. To provide a unified interface, the `Socket` trait accepts
//! a `dyn SocketOption` as a parameter. For each socket option, We define a struct that implements
//! the `SocketOption` trait in net module.
//!
//! However, different socket options may have values of different types. For example, the values can
//! be u32, C structs, or byte arrays. Furthermore, some values may have different formats in kernel
//! space and user space. For example, for the option `ReusePort`, the user space may use an i32 while
//! the kernel space may use a bool.
//!
//! We introduce the `RawSocketOption` trait for reading/writing socket options from/to user space. It
//! can read/write values of different types and can convert the user type to the kernel type when
//! reading from the user space and vice versa when writing to the user space. The `RawSocketOption`
//! should not be implemented for a type by hand, and we provide macros to automatically implement the
//! trait.
//!
//! # Example
//!
//! Suppose we want to add a new option `TcpNodelay`.
//!
//! First, the option should be added in the net module for the TCP socket.
//!
2024-03-15 02:27:14 +00:00
//! ```rust no_run
2023-12-28 09:24:23 +00:00
//! impl_socket_option!(TcpNodelay(bool));
//! ```
//!
//! Then, we need to implement the `ReadFromUser` and `WriteFromUser` traits for the bool type
//! in the utils module. These util functions can be shared if multiple options have the value
//! of same type.
//!
2024-03-15 02:27:14 +00:00
//! ```rust compile_fail
//! impl ReadFromUser for bool {
//! // content omitted here
//! }
//! impl WriteFromUser for bool {
//! // content omitted here
//! }
2023-12-28 09:24:23 +00:00
//! ```
//!
//! At last, we can implement `RawSocketOption` for `TcpNodelay` so that it can be read/from
//! user space.
//!
2024-03-15 02:27:14 +00:00
//! ```rust no_run
2023-12-28 09:24:23 +00:00
//! impl_raw_socket_option!(TcpNodeley);
//! ```
//!
//! At the syscall level, the interface is unified for all options and does not need to be modified.
//!
use crate::{net::socket::options::SocketOption, prelude::*};
2023-10-16 03:11:00 +00:00
mod socket;
mod tcp;
mod utils;
use self::{socket::new_socket_option, tcp::new_tcp_option};
2023-10-16 03:11:00 +00:00
2023-12-28 09:24:23 +00:00
pub trait RawSocketOption: SocketOption {
fn read_from_user(&mut self, addr: Vaddr, max_len: u32) -> Result<()>;
2023-10-16 03:11:00 +00:00
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize>;
2023-10-16 03:11:00 +00:00
2023-12-28 09:24:23 +00:00
fn as_sock_option_mut(&mut self) -> &mut dyn SocketOption;
2023-10-16 03:11:00 +00:00
2023-12-28 09:24:23 +00:00
fn as_sock_option(&self) -> &dyn SocketOption;
2023-10-16 03:11:00 +00:00
}
2023-12-28 09:24:23 +00:00
/// Impl `RawSocketOption` for a struct which implements `SocketOption`.
2023-10-16 03:11:00 +00:00
#[macro_export]
2023-12-28 09:24:23 +00:00
macro_rules! impl_raw_socket_option {
2023-10-16 03:11:00 +00:00
($option:ty) => {
2023-12-28 09:24:23 +00:00
impl RawSocketOption for $option {
fn read_from_user(&mut self, addr: Vaddr, max_len: u32) -> Result<()> {
2023-12-28 09:24:23 +00:00
use $crate::util::net::options::utils::ReadFromUser;
2023-10-16 03:11:00 +00:00
let input = ReadFromUser::read_from_user(addr, max_len)?;
2023-12-28 09:24:23 +00:00
self.set(input);
2023-10-16 03:11:00 +00:00
Ok(())
}
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
2023-12-28 09:24:23 +00:00
use $crate::util::net::options::utils::WriteToUser;
2023-10-16 03:11:00 +00:00
2023-12-28 09:24:23 +00:00
let output = self.get().unwrap();
output.write_to_user(addr, max_len)
2023-10-16 03:11:00 +00:00
}
2023-12-28 09:24:23 +00:00
fn as_sock_option_mut(&mut self) -> &mut dyn SocketOption {
2023-10-16 03:11:00 +00:00
self
}
2023-12-28 09:24:23 +00:00
fn as_sock_option(&self) -> &dyn SocketOption {
2023-10-16 03:11:00 +00:00
self
}
}
};
}
2023-12-28 09:24:23 +00:00
/// Impl `RawSocketOption` for a struct which is for only `getsockopt` and implements `SocketOption`.
2023-10-16 03:11:00 +00:00
#[macro_export]
macro_rules! impl_raw_sock_option_get_only {
($option:ty) => {
2023-12-28 09:24:23 +00:00
impl RawSocketOption for $option {
fn read_from_user(&mut self, _addr: Vaddr, _max_len: u32) -> Result<()> {
2023-10-16 03:11:00 +00:00
return_errno_with_message!(Errno::ENOPROTOOPT, "the option is getter-only");
}
fn write_to_user(&self, addr: Vaddr, max_len: u32) -> Result<usize> {
2023-12-28 09:24:23 +00:00
use $crate::util::net::options::utils::WriteToUser;
2023-10-16 03:11:00 +00:00
2023-12-28 09:24:23 +00:00
let output = self.get().unwrap();
output.write_to_user(addr, max_len)
2023-10-16 03:11:00 +00:00
}
2023-12-28 09:24:23 +00:00
fn as_sock_option_mut(&mut self) -> &mut dyn SocketOption {
2023-10-16 03:11:00 +00:00
self
}
2023-12-28 09:24:23 +00:00
fn as_sock_option(&self) -> &dyn SocketOption {
2023-10-16 03:11:00 +00:00
self
}
}
};
}
2023-12-28 09:24:23 +00:00
pub fn new_raw_socket_option(
level: CSocketOptionLevel,
name: i32,
) -> Result<Box<dyn RawSocketOption>> {
2023-10-16 03:11:00 +00:00
match level {
2023-12-28 09:24:23 +00:00
CSocketOptionLevel::SOL_SOCKET => new_socket_option(name),
CSocketOptionLevel::SOL_TCP => new_tcp_option(name),
2023-10-16 03:11:00 +00:00
_ => todo!(),
}
}
/// Sock Opt level. The definition is from https://elixir.bootlin.com/linux/v6.0.9/source/include/linux/socket.h#L343
#[repr(i32)]
#[derive(Debug, Clone, Copy, TryFromInt, PartialEq, Eq)]
#[allow(non_camel_case_types)]
2023-12-28 09:24:23 +00:00
pub enum CSocketOptionLevel {
2023-10-16 03:11:00 +00:00
SOL_IP = 0,
SOL_SOCKET = 1,
SOL_TCP = 6,
SOL_UDP = 17,
SOL_IPV6 = 41,
SOL_RAW = 255,
}