# How OpenFlux Handles Cross-Platform Raw Sockets for Exit Nodes

> Discover how OpenFlux manages cross-platform raw sockets for exit nodes. Learn about its Linux specific implementation and fallback to proxy mode on Windows and macOS.

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

---

**OpenFlux implements raw socket support exclusively on Linux through platform-specific source files with build tags, while Windows and macOS use stub implementations that automatically fall back to proxy mode.**

OpenFlux is an open-source networking tunnel that demonstrates how to handle **cross-platform raw sockets** for exit nodes using Go's build constraints. The architecture cleanly separates Linux-specific raw IP socket handling from portable proxy-based operation, ensuring functional exit node behavior across all supported operating systems while maximizing performance on Linux through kernel-level packet control.

## Platform-Specific Raw Socket Implementations

OpenFlux uses Go build tags (`//go:build linux`, `//go:build windows`, `//go:build darwin`) to compile platform-specific implementations of the `RawSocketEndpoint` interface. This design isolates privileged raw socket operations to Linux only, where root access and `IP_HDRINCL` capabilities are available.

### Linux Raw Socket Support

In [`tunnel/rawsocket_linux.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/rawsocket_linux.go), the `NewRawSocketEndpoint` function creates a pair of raw sockets using `syscall.Socket` with `syscall.SOCK_RAW`. The implementation enables `IP_HDRINCL` to allow manual IP header construction, then spawns a background `readLoop` goroutine for packet ingress.

The `readLoop` receives inbound IP packets via `syscall.Recvfrom`, rewrites the destination IP address to the exit node's local egress address (e.g., `10.10.10.2`), recomputes IP and transport-layer checksums, and forwards the packet into the gVisor stack through a callback registered via `SetTransportSender`. For egress traffic, `WritePackets` rewrites source addresses to the local egress IP, recalculates checksums, and transmits via `syscall.Sendto`.

### Windows and macOS Stub Implementations

On non-Linux platforms, [`tunnel/rawsocket_windows.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/rawsocket_windows.go) and [`tunnel/rawsocket_darwin.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/rawsocket_darwin.go) contain stub implementations. The `NewRawSocketEndpoint` function in these files immediately returns an error stating that raw socket mode is unsupported, while all other interface methods are no-ops. This compile-time isolation ensures the binary builds successfully on Windows and macOS without requiring platform-specific socket privileges.

## Exit Mode Selection and Architecture

The `ExitMode` type defined in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go) enumerates the two operational modes: `ExitModeProxy` (default, cross-platform) and `ExitModeRaw` (Linux only). The command-line flag `--mode` is parsed by `ParseExitMode`, which maps the string "raw" to `ExitModeRaw`.

When initializing a `TCPTunnel` with `isExitNode` set to `true`, the `NewTCPTunnelMode` function in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go) selects between `setupExitNodeRaw` and `setupExitNodeProxy` based on the parsed exit mode.

## Setting Up a Raw Socket Exit Node

The `setupExitNodeRaw` function in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go) orchestrates the Linux-specific raw socket initialization through the following sequence:

1. **Endpoint instantiation**: Calls `NewRawSocketEndpoint` to create the raw socket pair.
2. **Transport wiring**: Sets the endpoint's transport-sender callback to `tunnel.transport.Send` using `SetTransportSender`.
3. **Dual-NIC configuration**: Creates a second Network Interface Card (NIC ID 2) in the gVisor stack bound to the raw endpoint, while NIC ID 1 remains dedicated to the internal tunnel link.
4. **Protocol address assignment**: Obtains the local egress IP via `getLocalIP()` and adds it to the stack as a protocol address with a `/24` subnet (IPv4).
5. **Routing configuration**: Enables IPv4 forwarding and installs routes directing Internet-bound traffic through NIC 2 (raw socket) while maintaining the internal tunnel subnet (`10.10.10.0/24`) on NIC 1.

## Fallback to Proxy Mode

If `NewRawSocketEndpoint` fails due to insufficient privileges or platform incompatibility, `setupExitNodeRaw` catches the error, logs a warning, and automatically invokes `setupExitNodeProxy`. This fallback mechanism guarantees that exit nodes function correctly on Windows, macOS, or unprivileged Linux environments by using standard Go `net.Dial` and TCP proxying instead of raw sockets.

In proxy mode, the gVisor stack terminates TCP connections normally, and outgoing traffic is proxied through conventional socket operations without kernel-level packet manipulation.

## Code Examples

### Creating a Raw Socket Exit Node (Linux)

```go
// Initialize a tunnel with raw socket mode on Linux
tunnel := tunnel.NewTCPTunnelMode(myTransport, true, tunnel.ExitModeRaw)
defer tunnel.Close()

// Dial through the exit node
conn, err := tunnel.DialTCP("93.184.216.34:80")
if err != nil {
    log.Fatalf("dial failed: %v", err)
}
defer conn.Close()

```

### Automatic Fallback on Unsupported Platforms

```go
// On Windows or macOS, the same code automatically switches to proxy mode
tunnel := tunnel.NewTCPTunnelMode(myTransport, true, tunnel.ExitModeRaw)
fmt.Println("Running in mode:", tunnel.ExitMode) // prints "proxy"

```

### Raw Packet Processing Loop

The background packet processing in [`tunnel/rawsocket_linux.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/rawsocket_linux.go) demonstrates the IP rewriting logic:

```go
func (e *RawSocketEndpoint) readLoop() {
    buf := make([]byte, 65535)
    for {
        n, _, err := syscall.Recvfrom(e.recvFd, buf, 0)
        if err != nil {
            continue
        }
        pktCopy := make([]byte, n)
        copy(pktCopy, buf[:n])
        
        // Rewrite destination to local egress IP
        copy(pktCopy[16:20], []byte{10, 10, 10, 2})
        
        // Recompute checksums and forward to stack
        // ...
        e.sendToTransport(pktCopy)
    }
}

```

## Summary

- OpenFlux implements **raw sockets exclusively on Linux** through build-tagged files ([`rawsocket_linux.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/rawsocket_linux.go)), while Windows and macOS use stubs ([`rawsocket_windows.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/rawsocket_windows.go), [`rawsocket_darwin.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/rawsocket_darwin.go)).
- The `ExitMode` enum in [`tunnel/tunnel.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/tunnel/tunnel.go) selects between raw mode (Linux-only) and proxy mode (universal).
- Raw mode creates dual-NIC gVisor stacks where NIC 2 handles raw IP packets and NIC 1 manages the tunnel link, enabling true IP-level SNAT.
- Automatic fallback to proxy mode ensures **cross-platform compatibility** when raw socket creation fails or is unsupported.
- Packet processing includes IP header rewriting, checksum recalculation, and integration with the gVisor network stack via transport callbacks.

## Frequently Asked Questions

### Why does OpenFlux only support raw sockets on Linux?

Raw IP sockets require `IP_HDRINCL` capability and root privileges to create sockets with `syscall.SOCK_RAW`. While Linux exposes these primitives directly, Windows and macOS impose additional restrictions and different APIs that would require platform-specific kernel drivers or complex workarounds. OpenFlux prioritizes a clean, secure implementation that uses standard Go networking on non-Linux platforms.

### What happens if I try to use raw mode on Windows or macOS?

On Windows and macOS, the stub implementations in [`rawsocket_windows.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/rawsocket_windows.go) and [`rawsocket_darwin.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/rawsocket_darwin.go) cause `NewRawSocketEndpoint` to return an error. The tunnel initialization code catches this error and automatically falls back to `setupExitNodeProxy`, allowing the exit node to operate using standard TCP proxying without raw socket privileges.

### How does OpenFlux handle packet rewriting in raw socket mode?

The `readLoop` in [`rawsocket_linux.go`](https://github.com/p1neappleXpress/OpenFlux/blob/main/rawsocket_linux.go) receives raw IP packets from the kernel, validates them, rewrites the destination IP address to the local egress interface (e.g., `10.10.10.2`), recalculates the IP and TCP/UDP checksums, and injects the modified packet into the gVisor stack. For outbound traffic, `WritePackets` rewrites the source IP to the public egress address before transmission.

### Can I force proxy mode even on Linux?

Yes. You can explicitly set the exit mode to `ExitModeProxy` when calling `NewTCPTunnelMode`, or omit the `--mode raw` command-line flag to use the default proxy mode. This is useful when running without root privileges or when the additional overhead of raw socket handling is unnecessary for your use case.