UNDER CONSTRUCTION
Open Source Software
Remote Netstack
Tunneling layer 2 traffic over layer 4 protocols, and then terminating the tunnel with userspace networking stacks makes it easy to construct arbitrary virtual networks.
Go TCP/IP gvisor
Inspiration

I’m a big fan of tailscale and have used it for quite a while. Tailscale is essentially and managed VPN that uses Wireguard and some fancy NAT traversal techniques to facilitate a zero-config experience. It’s almost magic being able to construct arbitrary virtual networks that are completely isolated from the public internet, but still have full access to any devices I add to my tailnet. Tailscale supports a bunch of very interesting features, but the one that caught my eye was the subnet router or the ability to expose full layer 2 connectivity to an IP address.

Design

This inspired me to explore whether I could build a simple library on top of the gvisor userspace networking stack that would allow me to also tunnel layer 2 traffic over any other connection. Essentially, I was trying to implement a remote network interface card, where I could dial-out from a remote machine and utilize full networking connectivity from a completely different network, so long as an existing connection was already in place.

The easiest and secure way to do tunnel layer 4 traffic is to wrap the raw bytes from a connection in a structure that contains the IP address and port of the device on the other side that you want to connect to, then on the other side, you unpack that wrapping, dial the IP address and port, then forward the raw bytes and handle any bytes that come back. Essentially, you have three different connections that you’re joining together by copying bytes between each of them.

This solution felt like it was a layer too high in the OSI model: I have to send the raw bytes with my own header indicating which IP address and port to connect to, even though the TCP/IP layer already has this information, so why couldn’t I just tunnel the TCP/IP layer itself?

Implementation

I wrote clarkmcc/remotenetstack which provides the utilities that make this happen. At it’s core, it’s simply just a wrapper around the gvisor channel.Endpoint. By wrapping gvisor’s method of sending and receiving packets, we can hot-swap the implementations used to actually transport the packets across the internet.

To illustrate this, I developed a simple libp2p-based transport (working example). The neat thing is that any implementation of net.Conn can be used as the transport for the packets.

To make it easy to remotely connect to IP addresses over an existing connection, I wrote another tool which I called a virtual network interface. This interface gets created on two different machines and accepts a net.Conn that runs between them and that’s it! From one side, you can dial an IP address that only exists on the other side. Here’s an example.