Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model TCP protocol and implement a client #1130

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions uefi-raw/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
## Added
- Added `TimestampProtocol`.
- Added `DevicePathToTextProtocol` and `DevicePathFromTextProtocol`.
- Added minor utility methods to `Ipv4Address`: `new(u8, u8, u8, u8) -> Self` and `zero() -> Self`.
- Added `Ip4ModeData`, `Ip4ConfigData`, and `Ip4IcmpType`.
- Added `TCPv4Option`, `TCPv4ConnectionState`, and `TCPv4ServiceBindingProtocol`.

# uefi-raw - 0.5.1 (2024-03-17)

Expand Down
10 changes: 10 additions & 0 deletions uefi-raw/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ pub type VirtualAddress = u64;
#[repr(transparent)]
pub struct Ipv4Address(pub [u8; 4]);

impl Ipv4Address {
pub fn new(b1: u8, b2: u8, b3: u8, b4: u8) -> Self {
Self([b1, b2, b3, b4])
}

pub fn zero() -> Self {
Self([0, 0, 0, 0])
}
}

/// An IPv6 internet protocol address.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(transparent)]
Expand Down
41 changes: 41 additions & 0 deletions uefi-raw/src/protocol/network/ip4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,44 @@ pub struct Ip4RouteTable {
pub subnet_mask: Ipv4Address,
pub gateway_addr: Ipv4Address,
}

#[derive(Debug)]
#[repr(C)]
pub struct Ip4ModeData<'a> {
is_started: bool,
max_packet_size: u32,
config_data: Ip4ConfigData,
is_configured: bool,
group_count: bool,
group_table: &'a [Ipv4Address; 0],
route_count: u32,
ip4_route_table: &'a [Ip4RouteTable; 0],
icmp_type_count: u32,
icmp_type_list: &'a [Ip4IcmpType; 0],
}

#[derive(Debug)]
#[repr(C)]
pub struct Ip4ConfigData {
default_protocol: u8,
accept_any_protocol: bool,
accept_icmp_errors: bool,
accept_broadcast: bool,
accept_promiscuous: bool,
use_default_address: bool,
station_address: Ipv4Address,
subnet_mask: Ipv4Address,
type_of_service: u8,
time_to_live: u8,
do_not_fragment: bool,
raw_data: bool,
receive_timeout: u32,
transmit_timeout: u32,
}

#[derive(Debug)]
#[repr(C)]
pub struct Ip4IcmpType {
_type: u8,
code: u8,
}
1 change: 1 addition & 0 deletions uefi-raw/src/protocol/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pub mod http;
pub mod ip4;
pub mod ip4_config2;
pub mod tls;
pub mod tcpv4;
53 changes: 53 additions & 0 deletions uefi-raw/src/protocol/network/tcpv4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::{Handle, Status};

#[derive(Debug)]
#[repr(C)]
pub struct TCPv4Option {
receive_buffer_size: u32,
send_buffer_size: u32,
max_syn_back_log: u32,
connection_timeout: u32,
data_retries: u32,
fin_timeout: u32,
time_wait_timeout: u32,
keep_alive_probes: u32,
keep_alive_time: u32,
keep_alive_interval: u32,
enable_nagle: bool,
enable_time_stamp: bool,
enable_window_scaling: bool,
enable_selective_ack: bool,
enable_path_mtu_discovery: bool,
}

#[derive(Debug)]
#[repr(C)]
pub enum TCPv4ConnectionState {
Closed = 0,
Listen = 1,
SynSent = 2,
SynReceived = 3,
Established = 4,
FinWait1 = 5,
FinWait2 = 6,
Closing = 7,
TimeWait = 8,
CloseWait = 9,
LastAck = 10,
}

#[derive(Debug)]
#[repr(C)]
#[unsafe_protocol("00720665-67EB-4a99-BAF7-D3C33A1C7CC9")]
pub struct TCPv4ServiceBindingProtocol {
pub(crate) create_child: extern "efiapi" fn(
this: &Self,
out_child_handle: &mut Handle,
) -> Status,

destroy_child: extern "efiapi" fn(
this: &Self,
child_handle: Handle,
) -> Status,
}

2 changes: 2 additions & 0 deletions uefi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
- Added `Timestamp` protocol.
- Added `UnalignedSlice::as_ptr`.
- Added common derives for `Event` and `Handle`.
- Added `TCPv4Protocol` and implemented a client that can be used to
connect, transmit, and receive data over TCP.

# uefi - 0.27.0 (2024-03-17)

Expand Down
1 change: 1 addition & 0 deletions uefi/src/proto/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

pub mod pxe;
pub mod snp;
pub mod tcpv4;

/// Represents an IPv4/v6 address.
///
Expand Down
215 changes: 215 additions & 0 deletions uefi/src/proto/network/tcpv4/definitions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
use alloc::format;
use core::alloc::Layout;
use core::ffi::c_void;
use core::fmt::{Debug, Formatter};
use core::ptr::copy_nonoverlapping;
use uefi::{Event, Status};
use uefi_raw::Ipv4Address;
use uefi_raw::protocol::network::tcpv4::TCPv4Option;

use crate::proto::network::tcpv4::managed_event::ManagedEvent;
use crate::proto::network::tcpv4::receive_data::TCPv4ReceiveData;
use crate::proto::network::tcpv4::transmit_data::TCPv4TransmitData;

#[derive(Debug)]
#[repr(C)]
pub struct UnmodelledPointer(pub *mut c_void);

#[derive(Debug)]
#[repr(C)]
pub struct TCPv4AccessPoint {
use_default_address: bool,
station_address: Ipv4Address,
subnet_mask: Ipv4Address,
station_port: u16,
remote_address: Ipv4Address,
remote_port: u16,
active_flag: bool,
}

impl TCPv4AccessPoint {
fn new(connection_mode: TCPv4ConnectionMode) -> Self {
let (remote_ip, remote_port, is_client) = match connection_mode {
TCPv4ConnectionMode::Client(params) => {
(params.remote_ip, params.remote_port, true)
}
TCPv4ConnectionMode::Server => {
(Ipv4Address::zero(), 0, false)
}
};
Self {
use_default_address: true,
// These two fields are meaningless because we set use_default_address above
station_address: Ipv4Address::zero(),
subnet_mask: Ipv4Address::zero(),
// Chosen on-demand
station_port: 0,
remote_address: remote_ip,
remote_port,
active_flag: is_client,

}
}
}

#[derive(Debug)]
#[repr(C)]
pub struct TCPv4ConfigData<'a> {
type_of_service: u8,
time_to_live: u8,
access_point: TCPv4AccessPoint,
option: Option<&'a TCPv4Option>,
}

#[derive(Debug)]
#[repr(C)]
pub struct TCPv4IoToken<'a> {
pub completion_token: TCPv4CompletionToken,
packet: TCPv4Packet<'a>,
}

impl<'a> TCPv4IoToken<'a> {
pub fn new(
event: &ManagedEvent,
tx: Option<&'a TCPv4TransmitData>,
rx: Option<&'a TCPv4ReceiveData>,
) -> Self {
let packet = {
if tx.is_some() {
TCPv4Packet { tx_data: tx }
}
else {
let rx_ref = rx.as_ref();
rx_ref.expect("Either RX or TX data handles must be provided");
TCPv4Packet { rx_data: rx }
}
};
Self {
completion_token: TCPv4CompletionToken::new(event),
packet,
}
}
}

impl Drop for TCPv4IoToken<'_> {
fn drop(&mut self) {
// TODO(PT): I'm unsure offhand whether this empty Drop implementation is important,
// or if it can just be... dropped.
}
}

#[derive(Debug)]
pub struct TCPv4ClientConnectionModeParams {
remote_ip: Ipv4Address,
remote_port: u16,
}

impl TCPv4ClientConnectionModeParams {
pub fn new(
remote_ip: Ipv4Address,
remote_port: u16,
) -> Self {
Self {
remote_ip,
remote_port,
}
}
}

#[derive(Debug)]
pub enum TCPv4ConnectionMode {
Client(TCPv4ClientConnectionModeParams),
// TODO(PT): There may be parameters we need to model when operating as a server
Server,
}

impl<'a> TCPv4ConfigData<'a> {
pub(crate) fn new(
connection_mode: TCPv4ConnectionMode,
options: Option<&'a TCPv4Option>,
) -> Self {
Self {
type_of_service: 0,
time_to_live: 255,
access_point: TCPv4AccessPoint::new(connection_mode),
option: options,
}
}
}

#[repr(C)]
union TCPv4Packet<'a> {
rx_data: Option<&'a TCPv4ReceiveData>,
tx_data: Option<&'a TCPv4TransmitData>,
}

impl Debug for TCPv4Packet<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
unsafe {
let rx_data = self.rx_data;
let tx_data = self.tx_data;
f.write_str(&format!("<TCPv4Packet {rx_data:?} {tx_data:?}"))?;
}
Ok(())
}
}

#[derive(Debug)]
#[repr(C)]
pub struct TCPv4CompletionToken {
pub event: Event,
status: Status,
}

impl TCPv4CompletionToken {
pub fn new(event: &ManagedEvent) -> Self {
// Safety: The lifetime of this token is bound by the lifetime of the ManagedEvent.
let event_clone = unsafe { event.event.unsafe_clone() };
Self {
event: event_clone,
status: Status::SUCCESS,
}
}
}

#[derive(Debug)]
#[repr(C)]
pub struct TCPv4FragmentData {
pub(crate) fragment_length: u32,
pub(crate) fragment_buf: *const c_void,
}

impl TCPv4FragmentData {
pub fn with_buffer_len(len: usize) -> Self {
unsafe {
let layout = Layout::array::<u8>(len).unwrap();
let buffer = alloc::alloc::alloc(layout);
Self {
fragment_length: len as u32,
fragment_buf: buffer as *const c_void,
}
}
}
pub fn with_data(data: &[u8]) -> Self {
unsafe {
let data_len = data.len();
let _self = Self::with_buffer_len(data_len);
let buffer = _self.fragment_buf as *mut u8;
copy_nonoverlapping(
data.as_ptr(),
buffer,
data_len,
);
_self
}
}
}

impl Drop for TCPv4FragmentData {
fn drop(&mut self) {
unsafe {
let layout = Layout::array::<u8>(self.fragment_length as usize).unwrap();
alloc::alloc::dealloc(self.fragment_buf as *mut u8, layout);
}
}
}
Loading