# How OpenFlux Implements gVisor's Userspace TCP/IP Stack for Network Tunneling

> Discover how OpenFlux leverages gVisor's userspace TCP/IP stack. Learn about virtual stack instantiation, dual NIC configuration, and gonet adapters for efficient network tunneling.

- Repository: [p1neappleXpress/OpenFlux](https://github.com/p1neappleXpress/OpenFlux)
- Tags: deep-dive
- Published: 2026-09-14

---

**OpenFlux implements a complete userspace TCP/IP stack using gVisor's networking library by instantiating a virtual `stack.Stack` in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go), configuring dual NICs for tunnel and Internet traffic, and exposing standard Go `net.Conn` interfaces through the `gonet` adapter to route packets between encrypted transports and raw host sockets.**

OpenFlux is an open-source tunneling solution that leverages gVisor's userspace networking to avoid kernel dependencies for TCP/IP processing. By embedding the `gvisor.dev/gvisor/pkg/tcpip/stack` package, the project creates an isolated networking environment where all protocol handling—from packet parsing to connection state management—runs entirely in userspace.

## Core Architecture: The gVisor Stack in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go)

The foundation of OpenFlux's networking implementation resides in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go), where the repository initializes a self-contained TCP/IP stack using gVisor's library.

### Stack Instantiation

The constructor creates a new stack instance configured specifically for IPv4 and TCP protocols, deliberately omitting unnecessary protocol handlers to minimize memory footprint and attack surface:

```go
t.gvisorStack = stack.New(stack.Options{
    NetworkProtocols:   []stack.NetworkProtocolFactory{ipv4.NewProtocol},
    TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol},
})

```

[Source: tunnel.go line 58‑62](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L58-L62)

### TCP Buffer Configuration

Before accepting connections, OpenFlux tunes the stack's buffer limits via `SetTCPBuffers`, applying minimum, default, and maximum buffer sizes to the TCP transport protocol. This prevents memory exhaustion during high-throughput scenarios and ensures consistent latency characteristics across different deployment environments.

[Source: tunnel.go line 39‑48](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L39-L48)

## Virtual Network Interface Configuration

OpenFlux creates two distinct Network Interface Controllers (NICs) within the gVisor stack to segregate tunnel traffic from Internet-bound traffic.

### Exit Node Setup with Raw Sockets

When operating as an **exit node**, OpenFlux activates `setupExitNode` to create a raw-socket NIC (NIC 2) that bridges the userspace stack to the host operating system's network interface. This function assigns the node's external IP address, enables IPv4 forwarding between NICs, and installs two critical routes: one for the tunnel subnet (`10.10.10.0/24`) directing traffic toward NIC 1, and a default route (`0.0.0.0/0`) for Internet-bound traffic via NIC 2:

```go
t.gvisorStack.CreateNIC(internetNIC, rawEP)
t.gvisorStack.AddProtocolAddress(internetNIC, tcpip.ProtocolAddress{ … })
t.gvisorStack.SetForwardingDefaultAndAllNICs(ipv4.ProtocolNumber, true)

```

[Source: tunnel.go line 95‑127](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L95-L127)

### Client-Side Tunnel Configuration

For **client nodes**, `setupClient` registers only the tunnel NIC (NIC 1) with a static address of `10.10.10.2/24` and establishes a default route that directs all traffic through the encrypted tunnel endpoint. This configuration ensures complete traffic isolation; no packets leak to the host network stack until they traverse the tunnel and reach the exit node.

[Source: tunnel.go line 39‑53](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L39-L53)

## Data Flow and Packet Routing

The userspace TCP/IP stack relies on a custom link endpoint implementation to exchange raw IP packets between gVisor's networking layer and OpenFlux's transport layer.

### Outbound Packet Handling

The `TunnelLinkEndpoint` struct implements an `onOutgoingPacket` callback that intercepts every serialized IP packet produced by the gVisor stack. When an application writes to a TCP connection, the stack encapsulates the data into an IP packet and invokes this callback, which forwards the raw bytes to the configured transport layer via `transport.Transport.Send()`:

```go
tunnelEP.onOutgoingPacket = func(data []byte) { trans.Send(data) }

```

[Source: tunnel.go line 66‑85](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L66-L85)

### Inbound Packet Injection

For incoming traffic, the transport layer's `Receive` handler takes encrypted packets from the network, decrypts them if necessary, and injects the raw IP payloads back into the gVisor stack through `tunnelEP.InjectInbound()`. The stack then processes these packets through its TCP implementation, delivering the payload to the appropriate socket buffer:

```go
trans.Receive(func(data []byte) { tunnelEP.InjectInbound(data) })

```

[Source: tunnel.go line 66‑85](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L66-L85)

## Exposing Standard Go Networking Primitives

OpenFlux abstracts gVisor's internal socket mechanisms using the `gvisor.dev/gvisor/pkg/tcpip/adapters/gonet` package, allowing applications to use familiar `net.Conn` and `net.Listener` interfaces without awareness of the underlying userspace stack.

### TCP Dialing via gonet

The `DialTCP` method utilizes `gonet.DialTCP` to establish connections within the virtual stack. Depending on the node's role, the connection routes through NIC 1 (tunnel) for client nodes or NIC 2 (Internet) for exit nodes:

```go
conn, err := clientTunnel.DialTCP("example.com:443")
if err != nil { log.Fatal(err) }
defer conn.Close()

```

[Source: tunnel.go line 72‑77](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L72-L77)

### TCP Listening via gonet

For accepting inbound connections, `ListenTCP` leverages `gonet.ListenTCP` to bind to the userspace stack rather than the host kernel. This enables exit nodes to accept TCP connections from the Internet while processing them entirely within the gVisor TCP/IP implementation:

```go
ln, err := clientTunnel.ListenTCP(8080)
if err != nil { log.Fatal(err) }
for {
    c, _ := ln.Accept()
    go handle(c) // c is a *net.TCPConn backed by the gVisor stack
}

```

[Source: tunnel.go line 81‑86](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L81-L86)

## Monitoring Stack Statistics

OpenFlux includes a background statistics collector (`printStats`) that periodically queries `t.gvisorStack.Stats()` to log metrics including the number of connected sockets, established connections, and TCP retransmissions. This visibility into the userspace TCP/IP stack's internal state aids in debugging congestion control issues and monitoring tunnel health in production deployments.

[Source: tunnel.go line 88‑101](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go#L88-L101)

## Summary

- **gVisor Integration**: OpenFlux instantiates a complete TCP/IP stack in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go) using `stack.New` with IPv4 and TCP protocol factories, eliminating kernel networking dependencies.
- **Dual NIC Architecture**: The implementation creates NIC 1 for tunnel traffic (via `TunnelLinkEndpoint`) and NIC 2 for Internet traffic (via `RawSocketEndpoint`), enabling flexible routing for both client and exit-node modes.
- **Packet Bridging**: Raw IP packets flow between the transport layer and gVisor through `onOutgoingPacket` callbacks and `InjectInbound` methods, maintaining full userspace control over data paths.
- **Standard Interfaces**: The `gonet` adapter package exposes `net.Conn` and `net.Listener` primitives, allowing existing Go applications to utilize the userspace stack without code modifications.
- **Observability**: Built-in statistics collection via `stack.Stats()` provides real-time visibility into TCP connection states and performance metrics.

## Frequently Asked Questions

### What is the advantage of using gVisor's userspace TCP/IP stack over the host kernel's networking?

Running the TCP/IP stack in userspace provides deterministic network behavior across different operating systems and kernel versions, eliminates the need for elevated privileges to modify routing tables, and enables encrypted tunneling of raw TCP connections without kernel modules. According to the OpenFlux source code, this approach allows the exit node to process Internet traffic entirely within the application process while maintaining isolation between the tunnel subnet (`10.10.10.0/24`) and host network interfaces.

### How does OpenFlux route traffic between the tunnel and the Internet?

OpenFlux configures **forwarding** between two virtual NICs within the gVisor stack using `SetForwardingDefaultAndAllNICs(ipv4.ProtocolNumber, true)`. NIC 1 handles packets to and from the tunnel subnet (`10.10.10.0/24`), while NIC 2 connects to the host's raw socket for Internet access. When operating as an exit node, the stack routes packets received from tunnel clients (NIC 1) out to the Internet (NIC 2) and vice versa, functioning as a userspace IP router.

### Can applications use standard Go networking libraries with OpenFlux's implementation?

Yes. OpenFlux utilizes the **`gonet`** adapter package from gVisor to wrap internal stack sockets into standard `net.Conn` and `net.Listener` objects. This means applications can call `DialTCP` or `ListenTCP` and receive `*net.TCPConn` instances that behave identically to kernel-managed connections, despite actually traversing the userspace TCP/IP stack and custom transport encryption layer.

### Where is the TCP buffer size configured in the OpenFlux stack?

Buffer tuning occurs in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go) within the `SetTCPBuffers` function, which applies minimum, default, and maximum values to the stack's TCP transport protocol immediately after stack initialization. This configuration controls memory allocation for send and receive buffers across all connections managed by the gVisor userspace stack, directly impacting throughput and latency characteristics.