Skip to content

About

Linux networking backend for gonnect VPNs, with TUN, policy routing, DNS, process rules, and killswitch integration.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

80 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sysnet-linux

sysnet-linux implements the sysnet.System interface from the gonnect library for Linux.
sysnet-windows provides a windows backend for same interface.

The primary application for this package is Almagest. You can also use it as the Linux backend of another VPN application that needs one cross-platform system-networking abstraction.

Warning

This project is experimental. APIs and behavior can change without notice. Do not use it for production systems without your own review and tests.

Note

Parts of the DNS implementation include code borrowed from the Tailscale project.

Features

  • Creates native Linux TUN interfaces.
  • Configures TUN names, MTUs, IPv4 and IPv6 addresses, and routes.
  • Builds and updates a default-route TUN for full-tunnel VPN operation.
  • Uses separate policy-routing tables instead of adding VPN default routes to the main table.
  • Supports strict, include, and exclude routing modes.
  • Marks VPN transport sockets so that they bypass the VPN and do not create routing loops.
  • Mirrors packet marks through conntrack to support reverse-path filtering.
  • Controls system DNS and restores the previous configuration when the VPN closes.
  • Detects and supports direct /etc/resolv.conf, systemd-resolved, openresolv, and Debian resolvconf setups.
  • Provides process rules for command names, executable paths, command lines, PIDs, users, UIDs, groups, and GIDs.
  • Supports socket-owner matching and optional eBPF-based process marking through p-mark.
  • Integrates with the killswitch daemon through its administration socket.
  • Allocates IPv4 and IPv6 addresses and subnets without conflicting with active local interfaces.
  • Reports the effective feature set at runtime and degrades optional features when the host does not support them.
  • Supports dependency injection for tests and custom integrations.

Requirements

  • Linux
  • Go 1.25.5 or later
  • /dev/net/tun and CAP_NET_ADMIN for TUN and routing operations
  • Permission to control the selected system DNS service for DNS integration
  • nftables support for connection-mark handling
  • A mounted BPF filesystem and a configured pin path for process-based TUN rules
  • A compatible killswitch daemon for killswitch integration

Installation

go get github.com/asciimoth/sysnet-linux

The package name is linux, so it is useful to use an explicit import alias:

import linux "github.com/asciimoth/sysnet-linux"

Quick start

Use New for normal application integration. The zero configuration requests all features and enables the features that are available on the host.

package main

import (
	"log"

	"github.com/asciimoth/gonnect/sysnet"
	linux "github.com/asciimoth/sysnet-linux"
)

func main() {
	var system sysnet.System

	linuxSystem, err := linux.New(linux.SystemConfig{
		Logf: log.Printf,
	})
	if err != nil {
		log.Fatal(err)
	}
	system = linuxSystem
	defer system.Close()

	capabilities := system.Capabilities()
	log.Printf("system capabilities: %+v", capabilities)
}

Set SystemConfig.Pmark.PinPath to enable process-based include and exclude rules. You can also select a DNS backend, configure packet marks, set a killswitch socket path, and register lifecycle callbacks.

Use NewSystem when the application must supply its own DNS provider, routing manager, TUN factory, process marker, killswitch client, or other low-level component. This constructor is also useful for deterministic tests.

Default TUN identity and dynamic updates

BuildDefaultTun returns one stable public object while the default TUN is active. A rebuild returns the same object. A configuration-only rebuild keeps the same native source. If Linux removes the native link, the next rebuild creates a source and changes its source generation.

Use the Linux extension to observe this change:

source := defaultTun.(linux.DefaultTunSource)
generation := source.SourceGeneration()

The generation changes only when the native TUN changes. Replacement closes the old native TUN. A Read or Write that is in progress on that source can return an error that matches os.ErrClosed. A new call uses the new source. An in-progress Write can also return a partial packet count from the old source. Process that count before you retry the unwritten packets. Replacement is rejected if it would change IsNative, MWO, MRO, or BatchSize, because these values must stay stable for the lifetime of the public object. An Events channel is for the source that was current when Events was called. The old channel closes during replacement. Call Events again after the source generation changes.

The dynamic MTU, address, and route methods accept this public object. Address updates keep the main routing table free of routes through the default TUN. Route updates change the routing manager's dedicated VPN-table policy; they do not add routes to the main table. GetTunRoutes returns this route intent for a default TUN. An address update that would change the active DNS address is not supported and returns an error that matches sysnet.ErrNotSupported; use BuildDefaultTun for that change.

After Close, the object is inactive. Its I/O methods return an error that matches os.ErrClosed, and system update methods return sysnet.ErrUnknownTun. A later build creates a new stable public object and a new source generation.

How it fits into a cross-platform VPN

Application code can depend on gonnect/sysnet.System instead of Linux-specific networking APIs. Select the platform implementation at the application boundary:

func runVPN(system sysnet.System) error {
	// The VPN core uses the common gonnect sysnet interface.
	return nil
}

sysnet-linux supplies that interface on Linux. The VPN core can use another sysnet.System implementation on each other operating system without changing its main networking logic.

Packages

  • dns: system DNS detection, configuration, forwarding, and rollback
  • routing: fail-closed Linux policy-routing reconciliation
  • tun: native TUN creation and configuration
  • subnet: Linux-aware address and subnet allocation
  • connmark: nftables packet-mark and connection-mark synchronization
  • killswitch: reconnecting client for temporary killswitch rules

Development

Run the unit tests:

go test ./...

Run all checks and privileged end-to-end tests with just and Docker:

just check

The end-to-end tests create network interfaces, change routes, and test DNS providers in privileged containers.

License

This project is licensed under the GNU General Public License v3.0.

About

Linux networking backend for gonnect VPNs, with TUN, policy routing, DNS, process rules, and killswitch integration.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages