arblant
firefox-vpn-client / core / internal / tunstack / options_linux.go
options_linux.go53 lines · 1.6 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
package tunstack

import (
	"errors"

	"github.com/sagernet/netlink"
	tun "github.com/sagernet/sing-tun"
)

// The routing table and the rules' first priority of the TUN's routes on
// Linux: sing-tun puts the routes into a table of their own and sends the
// computer's traffic to it with policy rules (ip rule), which keep the
// local networks' routes, send DNS to any address into the TUN, and leave
// sockets bound to an interface (the core's, internal/egress) and replies
// to connections from outside (SSH to this computer) on the network they
// came from. Not sing-box's 2022 and 9000, so both can run.
const (
	routeTable = 2026
	ruleIndex  = 9100
)

func deviceName(name string) string { return name }

func platformOptions(o *tun.Options, cfg Config) {
	o.IPRoute2TableIndex = routeTable
	o.IPRoute2RuleIndex = ruleIndex
	// Without an IPv6 address on the device, an unreachable rule keeps
	// IPv6 from leaving outside it.
	o.StrictRoute = cfg.BlockIPv6
}

// CleanupStale removes the rules a TUN left when its process ended without
// closing it (the device and its routes go with the process; the rules
// stay, and with IPv6 blocked they would keep blocking it).
func CleanupStale() error {
	rules, err := netlink.RuleList(netlink.FAMILY_ALL)
	if err != nil {
		return err
	}
	var errs []error
	for _, r := range rules {
		if r.Priority < ruleIndex || r.Priority > ruleIndex+10 {
			continue
		}
		del := netlink.NewRule()
		del.Family = r.Family
		del.Priority = r.Priority
		if err := netlink.RuleDel(del); err != nil {
			errs = append(errs, err)
		}
	}
	return errors.Join(errs...)
}