Skip to content
Merged
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
16 changes: 16 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
/// Error types for the IP stack.
///
/// This enum represents all possible errors that can occur when working with the IP stack.
#[derive(thiserror::Error, Debug)]
pub enum IpStackError {
/// The transport protocol is not supported.
#[error("The transport protocol is not supported")]
UnsupportedTransportProtocol,

/// The packet is invalid or malformed.
#[error("The packet is invalid")]
InvalidPacket,

/// A value is too large to fit in a u16.
#[error("ValueTooBigError<u16> {0}")]
ValueTooBigErrorU16(#[from] etherparse::err::ValueTooBigError<u16>),

/// A value is too large to fit in a usize.
#[error("ValueTooBigError<usize> {0}")]
ValueTooBigErrorUsize(#[from] etherparse::err::ValueTooBigError<usize>),

/// The TCP packet is invalid.
#[error("Invalid Tcp packet")]
InvalidTcpPacket,

/// An I/O error occurred.
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),

/// Error accepting a new stream.
#[error("Accept Error")]
AcceptError,

/// Error sending data through a channel.
#[error("Send Error {0}")]
SendError(#[from] Box<tokio::sync::mpsc::error::SendError<crate::stream::IpStackStream>>),
}
Expand Down Expand Up @@ -48,4 +59,9 @@ impl From<IpStackError> for std::io::Error {
}
}

/// A specialized [`Result`] type for IP stack operations.
///
/// This type is used throughout the IP stack for any operation which may produce an error.
///
/// [`Result`]: std::result::Result
pub type Result<T, E = IpStackError> = std::result::Result<T, E>;
184 changes: 183 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,34 @@ const TUN_PROTO_IP6: [u8; 2] = [0x00, 0x0A];
#[cfg(any(target_os = "macos", target_os = "ios"))]
const TUN_PROTO_IP4: [u8; 2] = [0x00, 0x02];

/// Configuration for the IP stack.
///
/// This structure holds configuration parameters that control the behavior of the IP stack,
/// including network settings and protocol-specific timeouts.
///
/// # Examples
///
/// ```
/// use ipstack::IpStackConfig;
/// use std::time::Duration;
///
/// let mut config = IpStackConfig::default();
/// config.mtu(1500)
/// .udp_timeout(Duration::from_secs(60))
/// .packet_information(false);
/// ```
#[non_exhaustive]
pub struct IpStackConfig {
/// Maximum Transmission Unit (MTU) size in bytes.
/// Default is `u16::MAX` (65535).
pub mtu: u16,
/// Whether to include packet information headers (Unix platforms only).
/// Default is `false`.
pub packet_information: bool,
/// TCP-specific configuration parameters.
pub tcp_config: Arc<TcpConfig>,
/// Timeout for UDP connections.
/// Default is 30 seconds.
pub udp_timeout: Duration,
}

Expand All @@ -62,31 +85,157 @@ impl Default for IpStackConfig {
}

impl IpStackConfig {
/// Set custom TCP configuration
/// Set custom TCP configuration.
///
/// # Arguments
///
/// * `config` - The TCP configuration to use
///
/// # Examples
///
/// ```
/// use ipstack::{IpStackConfig, TcpConfig};
///
/// let mut config = IpStackConfig::default();
/// config.with_tcp_config(TcpConfig::default());
/// ```
pub fn with_tcp_config(&mut self, config: TcpConfig) -> &mut Self {
self.tcp_config = Arc::new(config);
self
}

/// Set the UDP connection timeout.
///
/// # Arguments
///
/// * `timeout` - The timeout duration for UDP connections
///
/// # Examples
///
/// ```
/// use ipstack::IpStackConfig;
/// use std::time::Duration;
///
/// let mut config = IpStackConfig::default();
/// config.udp_timeout(Duration::from_secs(60));
/// ```
pub fn udp_timeout(&mut self, timeout: Duration) -> &mut Self {
self.udp_timeout = timeout;
self
}

/// Set the Maximum Transmission Unit (MTU) size.
///
/// # Arguments
///
/// * `mtu` - The MTU size in bytes
///
/// # Examples
///
/// ```
/// use ipstack::IpStackConfig;
///
/// let mut config = IpStackConfig::default();
/// config.mtu(1500);
/// ```
pub fn mtu(&mut self, mtu: u16) -> &mut Self {
self.mtu = mtu;
self
}

/// Enable or disable packet information headers (Unix platforms only).
///
/// When enabled on Unix platforms, the TUN device will include 4-byte packet
/// information headers.
///
/// # Arguments
///
/// * `packet_information` - Whether to include packet information headers
///
/// # Examples
///
/// ```
/// use ipstack::IpStackConfig;
///
/// let mut config = IpStackConfig::default();
/// config.packet_information(true);
/// ```
pub fn packet_information(&mut self, packet_information: bool) -> &mut Self {
self.packet_information = packet_information;
self
}
}

/// The main IP stack instance.
///
/// `IpStack` provides a userspace TCP/IP stack implementation for TUN devices.
/// It processes network packets and creates stream abstractions for TCP, UDP, and
/// unknown transport protocols.
///
/// # Examples
///
/// ```no_run
/// use ipstack::{IpStack, IpStackConfig, IpStackStream};
/// use std::net::Ipv4Addr;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// // Configure TUN device
/// let mut config = tun::Configuration::default();
/// config
/// .address(Ipv4Addr::new(10, 0, 0, 1))
/// .netmask(Ipv4Addr::new(255, 255, 255, 0))
/// .up();
///
/// // Create IP stack
/// let ipstack_config = IpStackConfig::default();
/// let mut ip_stack = IpStack::new(ipstack_config, tun::create_as_async(&config)?);
///
/// // Accept incoming streams
/// while let Ok(stream) = ip_stack.accept().await {
/// match stream {
/// IpStackStream::Tcp(tcp) => {
/// // Handle TCP connection
/// }
/// IpStackStream::Udp(udp) => {
/// // Handle UDP connection
/// }
/// _ => {}
/// }
/// }
/// Ok(())
/// }
/// ```
pub struct IpStack {
accept_receiver: UnboundedReceiver<IpStackStream>,
handle: JoinHandle<Result<()>>,
}

impl IpStack {
/// Create a new IP stack instance.
///
/// # Arguments
///
/// * `config` - Configuration for the IP stack
/// * `device` - An async TUN device implementing `AsyncRead` + `AsyncWrite`
///
/// # Examples
///
/// ```no_run
/// use ipstack::{IpStack, IpStackConfig};
/// use std::net::Ipv4Addr;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let mut tun_config = tun::Configuration::default();
/// tun_config.address(Ipv4Addr::new(10, 0, 0, 1))
/// .netmask(Ipv4Addr::new(255, 255, 255, 0))
/// .up();
///
/// let ipstack_config = IpStackConfig::default();
/// let ip_stack = IpStack::new(ipstack_config, tun::create_as_async(&tun_config)?);
/// # Ok(())
/// # }
/// ```
pub fn new<Device>(config: IpStackConfig, device: Device) -> IpStack
where
Device: AsyncRead + AsyncWrite + Unpin + Send + 'static,
Expand All @@ -98,6 +247,39 @@ impl IpStack {
}
}

/// Accept an incoming network stream.
///
/// This method waits for and returns the next incoming network connection or packet.
/// The returned `IpStackStream` enum indicates the type of stream (TCP, UDP, or unknown).
///
/// # Returns
///
/// * `Ok(IpStackStream)` - The next incoming stream
/// * `Err(IpStackError::AcceptError)` - If the IP stack has been shut down
///
/// # Examples
///
/// ```no_run
/// use ipstack::{IpStack, IpStackConfig, IpStackStream};
///
/// # async fn example(mut ip_stack: IpStack) -> Result<(), Box<dyn std::error::Error>> {
/// match ip_stack.accept().await? {
/// IpStackStream::Tcp(tcp) => {
/// println!("New TCP connection from {}", tcp.peer_addr());
/// }
/// IpStackStream::Udp(udp) => {
/// println!("New UDP stream from {}", udp.peer_addr());
/// }
/// IpStackStream::UnknownTransport(unknown) => {
/// println!("Unknown transport protocol: {:?}", unknown.ip_protocol());
/// }
/// IpStackStream::UnknownNetwork(data) => {
/// println!("Unknown network packet: {} bytes", data.len());
/// }
/// }
/// # Ok(())
/// # }
/// ```
pub async fn accept(&mut self) -> Result<IpStackStream, IpStackError> {
self.accept_receiver.recv().await.ok_or(IpStackError::AcceptError)
}
Expand Down
46 changes: 45 additions & 1 deletion src/stream/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,43 @@ mod tcp;
mod udp;
mod unknown;

/// A network stream accepted by the IP stack.
///
/// This enum represents different types of network streams that can be accepted from the TUN device.
/// Each variant provides appropriate abstractions for handling specific protocol types.
///
/// # Variants
///
/// * `Tcp` - A TCP connection stream implementing `AsyncRead` + `AsyncWrite`
/// * `Udp` - A UDP stream implementing `AsyncRead` + `AsyncWrite`
/// * `UnknownTransport` - A stream for unknown transport layer protocols (e.g., ICMP, IGMP)
/// * `UnknownNetwork` - Raw network layer packets that couldn't be parsed
pub enum IpStackStream {
/// A TCP connection stream.
Tcp(IpStackTcpStream),
/// A UDP stream.
Udp(IpStackUdpStream),
/// A stream for unknown transport protocols.
UnknownTransport(IpStackUnknownTransport),
/// Raw network packets that couldn't be parsed.
UnknownNetwork(Vec<u8>),
}

impl IpStackStream {
/// Returns the local socket address for this stream.
///
/// For TCP and UDP streams, this returns the source address of the connection.
/// For unknown transport and network streams, this returns an unspecified address.
///
/// # Examples
///
/// ```no_run
/// # use ipstack::{IpStack, IpStackStream};
/// # async fn example(stream: IpStackStream) {
/// let local_addr = stream.local_addr();
/// println!("Local address: {}", local_addr);
/// # }
/// ```
pub fn local_addr(&self) -> SocketAddr {
match self {
IpStackStream::Tcp(tcp) => tcp.local_addr(),
Expand All @@ -30,6 +59,21 @@ impl IpStackStream {
},
}
}

/// Returns the remote socket address for this stream.
///
/// For TCP and UDP streams, this returns the destination address of the connection.
/// For unknown transport and network streams, this returns an unspecified address.
///
/// # Examples
///
/// ```no_run
/// # use ipstack::{IpStack, IpStackStream};
/// # async fn example(stream: IpStackStream) {
/// let peer_addr = stream.peer_addr();
/// println!("Peer address: {}", peer_addr);
/// # }
/// ```
pub fn peer_addr(&self) -> SocketAddr {
match self {
IpStackStream::Tcp(tcp) => tcp.peer_addr(),
Expand All @@ -42,7 +86,7 @@ impl IpStackStream {
}
}

pub fn stream_sender(&self) -> Result<crate::PacketSender, std::io::Error> {
pub(crate) fn stream_sender(&self) -> Result<crate::PacketSender, std::io::Error> {
match self {
IpStackStream::Tcp(tcp) => Ok(tcp.stream_sender()),
IpStackStream::Udp(udp) => Ok(udp.stream_sender()),
Expand Down
Loading
Loading