# How LiteBox Provides Network Connectivity: A Deep Dive into the smoltcp Stack

> Discover how LiteBox achieves network connectivity using smoltcp. Explore its three-layer stack, lock-free channels, and IPInterfaceProvider trait for efficient host bridging.

- Repository: [Microsoft/litebox](https://github.com/microsoft/litebox)
- Tags: deep-dive
- Published: 2026-02-16

---

**LiteBox implements network connectivity through a self-contained, three-layer stack built on the smoltcp library, using lock-free ring-buffer channels to decouple user-space I/O from the network worker while bridging to the host via the `IPInterfaceProvider` trait.**

LiteBox provides network connectivity by embedding a lightweight TCP/IP stack directly into the sandboxed environment. Unlike traditional approaches that rely on host kernel networking, LiteBox uses the **smoltcp** library to implement a complete, lock-free network stack in user space. This architecture enables sandboxed applications to establish TCP and UDP connections while maintaining complete isolation from the host operating system.

## The Three-Layer Network Architecture

LiteBox splits its network stack into three logical layers that together provide full TCP/UDP/IP connectivity while keeping user-space code completely lock-free.

### Physical Interface Layer

The **physical interface** bridges smoltcp's `Device` trait to the host's network I/O. In [`litebox/src/net/phy.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/net/phy.rs), the implementation delegates to `IPInterfaceProvider::receive_ip_packet` and `IPInterfaceProvider::send_ip_packet` (lines 30-71). This layer receives raw IP packets from the platform and sends packets back to the platform without processing the payload.

### Network Core Layer

The **network core** manages the smoltcp `Interface`, a `SocketSet`, and the physical device. In [`litebox/src/net/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/net/mod.rs), the `pub struct Network<Platform>` (lines 57-80) encapsulates this state. The core method `perform_platform_interaction` (lines 442-460) drives the platform-interaction loop, allocating local ports, tracking pending closures, and polling smoltcp to process incoming and outgoing packets.

### Socket Proxy Layer

The **socket proxy** provides lock-free ring-buffer channels that decouple user threads from the network worker. In [`litebox/src/net/socket_channel.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/net/socket_channel.rs), the `NetworkProxy` enum wraps `StreamSocketChannel` and `DatagramSocketChannel`. Each socket created by the `Network` gets an `Arc<NetworkProxy>` stored in the descriptor table. The `try_read` and `try_write` methods operate on these ring buffers without locks, enabling concurrent I/O.

## Data Flow Through the Stack

Understanding how packets move through LiteBox requires tracing the six-stage pipeline that connects the host platform to sandboxed applications.

### 1. Platform to LiteBox Ingestion

The host platform implements `platform::IPInterfaceProvider`, typically forwarding packets from a TUN device or virtual NIC. When the platform receives an inbound IP packet, it calls `receive_ip_packet(&mut buf)`. The `phy::Device::receive` method (lines 44-55 in [`phy.rs`](https://github.com/microsoft/litebox/blob/main/phy.rs)) copies the packet into a receive buffer and returns an `RxToken` to smoltcp.

### 2. smoltcp Processing

The `Network::perform_platform_interaction` method calls `self.interface.poll(&mut self.device, timestamp)`. smoltcp parses the packet, matches it to a socket in the `socket_set`, and writes the payload into the socket's RX ring buffer via the associated `NetworkProxy`.

### 3. User-Space Reads

When the sandboxed application calls `socket.read(...)`, the call forwards to `NetworkProxy::try_read`. This method reads from the RX ring buffer of the appropriate `StreamSocketChannel` or `DatagramSocketChannel` (lines 103-124 in [`socket_channel.rs`](https://github.com/microsoft/litebox/blob/main/socket_channel.rs)). The operation is lock-free and updates availability counters to notify observers.

### 4. User-Space Writes

When the application calls `socket.write(...)`, `NetworkProxy::try_write` pushes data into the socket's TX ring buffer (lines 53-65 in [`socket_channel.rs`](https://github.com/microsoft/litebox/blob/main/socket_channel.rs)). The proxy updates the TX-available counter to signal that data awaits transmission.

### 5. LiteBox to Platform Egress

On the next call to `perform_platform_interaction`, smoltcp detects data pending in a socket's TX buffer. It extracts the data via `pop_tx_data_with` or `pop_tx_data` and writes it into the device's `TxToken`. The `TxToken::consume` implementation (lines 91-102 in [`phy.rs`](https://github.com/microsoft/litebox/blob/main/phy.rs)) hands the filled packet back to the platform via `platform.send_ip_packet`.

### 6. Event Notification

Both channel types implement `IOPollable`. When data becomes readable or writable, or when state changes occur (e.g., `Connected`, `Closed`), the channel notifies observers through a `Pollee` (`inner.pollee.notify_observers`). This drives the event-driven API used by the shim layer (`litebox_shim_linux`) to return `POLLIN`, `POLLOUT`, and other readiness events.

## Platform Interaction Modes

LiteBox supports two distinct modes for running the platform-interaction loop, controlled by the `PlatformInteraction` enum (lines 68-74 in [`mod.rs`](https://github.com/microsoft/litebox/blob/main/mod.rs)).

### Automatic Mode

In **automatic** mode (the default), `perform_platform_interaction` is called implicitly on each socket operation. This provides the simplest programming model where network I/O appears synchronous from the application's perspective, with the runtime handling all polling internally.

### Manual Mode

In **manual** mode, the host must invoke `perform_platform_interaction` periodically. This mode is essential for **low-power** or **real-time** environments where the host needs precise control over when network processing occurs. The method returns `PlatformInteractionReinvocationAdvice` (lines 93-100), which tells callers exactly when to invoke it again, optimizing CPU usage by avoiding unnecessary polls.

## Practical Implementation Examples

### Creating a Network Instance and TCP Socket

```rust
use litebox::LiteBox;
use litebox::net::{Network, Protocol};
use litebox::net::socket_channel::NetworkProxy;

// Assume `platform` implements `IPInterfaceProvider + TimeProvider + RawSyncPrimitivesProvider`.
let litebox = LiteBox::new(platform);
let mut net = Network::new(&litebox);

// Allocate a socket descriptor (FD) – the shim layer normally does this.
let fd = net.socket(Protocol::Tcp).expect("socket creation failed");

// Associate a proxy so the user can read/write without blocking.
let proxy = litebox::net::socket_channel::StreamSocketChannel::new();
net.set_socket_proxy(fd, proxy.clone());

// Connect to a remote address (non-blocking; you will later poll for completion).
net.connect(fd, &"10.0.0.3:8080".parse().unwrap())?;

```

### Using Lock-Free Channels for Data Transfer

```rust
// Write data – pushes into the TX ring buffer.
let data = b"GET / HTTP/1.0\r\n\r\n";
proxy.try_write(data).expect("write failed");

// Periodically pump the network stack (required if `Manual` interaction is set).
while net.perform_platform_interaction()
        .call_again_immediately()
{
    // Loop until smoltcp reports no more work.
}

// Read data – pulls from the RX ring buffer.
let mut buf = [0u8; 1500];
let n = proxy.try_read(&mut buf,
                      litebox::net::ReceiveFlags::empty(),
                      None)
          .expect("read failed");
println!("Received {} bytes", n);
println!("{}", core::str::from_utf8(&buf[..n]).unwrap());

```

### Manual Network Loop in Async Context

```rust
async fn net_task(mut net: Network<MyPlatform>) {
    loop {
        // Perform any pending work.
        net.perform_platform_interaction();
        // Sleep or await an event (e.g., platform interrupt) here.
        my_platform.wait_for_packet().await;
    }
}

```

## Key Source Files

Understanding LiteBox network connectivity requires familiarity with these specific files in the repository:

| File | Purpose |
|------|---------|
| [`litebox/src/net/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/net/mod.rs) | Core `Network` type, socket management, and the `perform_platform_interaction` loop |
| [`litebox/src/net/socket_channel.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/net/socket_channel.rs) | Lock-free ring-buffer channels (`StreamSocketChannel`, `DatagramSocketChannel`) and the `NetworkProxy` enum |
| [`litebox/src/net/phy.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/net/phy.rs) | `Device` implementation connecting smoltcp to the platform's IP interface |
| [`litebox/src/platform/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/mod.rs) | `IPInterfaceProvider` trait abstracting raw IP packet I/O from the host |
| [`litebox_shim_linux/src/syscalls/net.rs`](https://github.com/microsoft/litebox/blob/main/litebox_shim_linux/src/syscalls/net.rs) | Linux shim layer creating sockets and forwarding system calls to the `Network` |

## Summary

LiteBox delivers network connectivity through a sophisticated, zero-copy architecture:

* **smoltcp Integration**: The stack leverages the `smoltcp` library for standards-compliant TCP/IP processing while abstracting the physical layer through the `phy::Device` trait.
* **Lock-Free Channels**: User-space I/O occurs through `NetworkProxy` ring buffers (`StreamSocketChannel` and `DatagramSocketChannel`), eliminating locks between application threads and the network worker.
* **Flexible Polling**: The `perform_platform_interaction` method supports both automatic and manual invocation modes via `PlatformInteraction`, accommodating real-time and low-power requirements.
* **Platform Abstraction**: Raw IP packets flow through the `IPInterfaceProvider` trait, enabling LiteBox to run on any host that can provide Ethernet frames or IP packets.

## Frequently Asked Questions

### How does LiteBox handle TCP socket creation without relying on the host kernel?

LiteBox creates TCP sockets entirely within the sandbox using the `Network::socket` method defined in [`litebox/src/net/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/net/mod.rs). This method allocates a socket descriptor and registers it with the internal smoltcp `SocketSet`. The host kernel is only involved at the physical layer through the `IPInterfaceProvider` trait, which exchanges raw IP packets without awareness of TCP connections or socket states.

### What is the difference between automatic and manual platform interaction modes?

Automatic mode (the default) implicitly calls `perform_platform_interaction` on every socket operation, providing a synchronous programming model where the runtime handles all network polling. Manual mode requires the host to explicitly invoke `perform_platform_interaction` and check the `PlatformInteractionReinvocationAdvice` return value to determine when to poll again. Manual mode is essential for real-time systems or low-power environments requiring precise control over CPU usage.

### How does LiteBox achieve lock-free network I/O?

LiteBox uses ring-buffer channels implemented in [`litebox/src/net/socket_channel.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/net/socket_channel.rs). The `NetworkProxy` enum wraps `StreamSocketChannel` and `DatagramSocketChannel`, each containing separate RX and TX ring buffers. When a user thread writes data, `try_write` pushes to the TX buffer without locks. When smoltcp processes packets, it reads from these buffers using `try_read`. The `IOPollable` implementation notifies observers of state changes without requiring mutexes.

### Can LiteBox run on platforms without a traditional network stack?

Yes, LiteBox only requires the host to implement the `IPInterfaceProvider` trait defined in [`litebox/src/platform/mod.rs`](https://github.com/microsoft/litebox/blob/main/litebox/src/platform/mod.rs). This trait abstracts sending and receiving raw IP packets, allowing LiteBox to run on bare-metal environments, embedded systems with custom NIC drivers, or virtualized platforms using TUN/TAP devices. The platform does not need to provide sockets, TCP/IP processing, or a traditional networking stack.