mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-30 05:25:35 +00:00
d46a4eced5
* util/linuxfw, wgengine: allow ingress to magicsock UDP port on Linux Updates #9084. Currently, we have to tell users to manually open UDP ports on Linux when certain firewalls (like ufw) are enabled. This change automates the process of adding and updating those firewall rules as magicsock changes what port it listens on. Signed-off-by: Naman Sood <mail@nsood.in>
209 lines
5.1 KiB
Go
209 lines
5.1 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
//go:build darwin || freebsd
|
|
|
|
package router
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"net/netip"
|
|
"os/exec"
|
|
"runtime"
|
|
|
|
"github.com/tailscale/wireguard-go/tun"
|
|
"go4.org/netipx"
|
|
"tailscale.com/net/netmon"
|
|
"tailscale.com/net/tsaddr"
|
|
"tailscale.com/types/logger"
|
|
"tailscale.com/version"
|
|
)
|
|
|
|
type userspaceBSDRouter struct {
|
|
logf logger.Logf
|
|
netMon *netmon.Monitor
|
|
tunname string
|
|
local []netip.Prefix
|
|
routes map[netip.Prefix]bool
|
|
}
|
|
|
|
func newUserspaceBSDRouter(logf logger.Logf, tundev tun.Device, netMon *netmon.Monitor) (Router, error) {
|
|
tunname, err := tundev.Name()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &userspaceBSDRouter{
|
|
logf: logf,
|
|
netMon: netMon,
|
|
tunname: tunname,
|
|
}, nil
|
|
}
|
|
|
|
func (r *userspaceBSDRouter) addrsToRemove(newLocalAddrs []netip.Prefix) (remove []netip.Prefix) {
|
|
for _, cur := range r.local {
|
|
found := false
|
|
for _, v := range newLocalAddrs {
|
|
found = (v == cur)
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
remove = append(remove, cur)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (r *userspaceBSDRouter) addrsToAdd(newLocalAddrs []netip.Prefix) (add []netip.Prefix) {
|
|
for _, cur := range newLocalAddrs {
|
|
found := false
|
|
for _, v := range r.local {
|
|
found = (v == cur)
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
add = append(add, cur)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func cmd(args ...string) *exec.Cmd {
|
|
if len(args) == 0 {
|
|
log.Fatalf("exec.Cmd(%#v) invalid; need argv[0]", args)
|
|
}
|
|
return exec.Command(args[0], args[1:]...)
|
|
}
|
|
|
|
func (r *userspaceBSDRouter) Up() error {
|
|
ifup := []string{"ifconfig", r.tunname, "up"}
|
|
if out, err := cmd(ifup...).CombinedOutput(); err != nil {
|
|
r.logf("running ifconfig failed: %v\n%s", err, out)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func inet(p netip.Prefix) string {
|
|
if p.Addr().Is6() {
|
|
return "inet6"
|
|
}
|
|
return "inet"
|
|
}
|
|
|
|
func (r *userspaceBSDRouter) Set(cfg *Config) (reterr error) {
|
|
if cfg == nil {
|
|
cfg = &shutdownConfig
|
|
}
|
|
|
|
setErr := func(err error) {
|
|
if reterr == nil {
|
|
reterr = err
|
|
}
|
|
}
|
|
addrsToRemove := r.addrsToRemove(cfg.LocalAddrs)
|
|
|
|
// If we're removing all addresses, we need to remove and re-add all
|
|
// routes.
|
|
resetRoutes := len(r.local) > 0 && len(addrsToRemove) == len(r.local)
|
|
|
|
// Update the addresses.
|
|
for _, addr := range addrsToRemove {
|
|
arg := []string{"ifconfig", r.tunname, inet(addr), addr.String(), "-alias"}
|
|
out, err := cmd(arg...).CombinedOutput()
|
|
if err != nil {
|
|
r.logf("addr del failed: %v => %v\n%s", arg, err, out)
|
|
setErr(err)
|
|
}
|
|
}
|
|
for _, addr := range r.addrsToAdd(cfg.LocalAddrs) {
|
|
var arg []string
|
|
if runtime.GOOS == "freebsd" && addr.Addr().Is6() && addr.Bits() == 128 {
|
|
// FreeBSD rejects tun addresses of the form fc00::1/128 -> fc00::1,
|
|
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=218508
|
|
// Instead add our whole /48, which works because we use a /48 route.
|
|
// Full history: https://github.com/tailscale/tailscale/issues/1307
|
|
tmp := netip.PrefixFrom(addr.Addr(), 48)
|
|
arg = []string{"ifconfig", r.tunname, inet(tmp), tmp.String()}
|
|
} else {
|
|
arg = []string{"ifconfig", r.tunname, inet(addr), addr.String(), addr.Addr().String()}
|
|
}
|
|
out, err := cmd(arg...).CombinedOutput()
|
|
if err != nil {
|
|
r.logf("addr add failed: %v => %v\n%s", arg, err, out)
|
|
setErr(err)
|
|
}
|
|
}
|
|
|
|
newRoutes := make(map[netip.Prefix]bool)
|
|
for _, route := range cfg.Routes {
|
|
if runtime.GOOS != "darwin" && route == tsaddr.TailscaleULARange() {
|
|
// Because we added the interface address as a /48 above,
|
|
// the kernel already created the Tailscale ULA route
|
|
// implicitly. We mustn't try to add/delete it ourselves.
|
|
continue
|
|
}
|
|
newRoutes[route] = true
|
|
}
|
|
// Delete any preexisting routes.
|
|
for route := range r.routes {
|
|
if resetRoutes || !newRoutes[route] {
|
|
net := netipx.PrefixIPNet(route)
|
|
nip := net.IP.Mask(net.Mask)
|
|
nstr := fmt.Sprintf("%v/%d", nip, route.Bits())
|
|
del := "del"
|
|
if version.OS() == "macOS" {
|
|
del = "delete"
|
|
}
|
|
routedel := []string{"route", "-q", "-n",
|
|
del, "-" + inet(route), nstr,
|
|
"-iface", r.tunname}
|
|
out, err := cmd(routedel...).CombinedOutput()
|
|
if err != nil {
|
|
r.logf("route del failed: %v: %v\n%s", routedel, err, out)
|
|
setErr(err)
|
|
}
|
|
}
|
|
}
|
|
// Add the routes.
|
|
for route := range newRoutes {
|
|
if resetRoutes || !r.routes[route] {
|
|
net := netipx.PrefixIPNet(route)
|
|
nip := net.IP.Mask(net.Mask)
|
|
nstr := fmt.Sprintf("%v/%d", nip, route.Bits())
|
|
routeadd := []string{"route", "-q", "-n",
|
|
"add", "-" + inet(route), nstr,
|
|
"-iface", r.tunname}
|
|
out, err := cmd(routeadd...).CombinedOutput()
|
|
if err != nil {
|
|
r.logf("addr add failed: %v: %v\n%s", routeadd, err, out)
|
|
setErr(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Store the interface and routes so we know what to change on an update.
|
|
if reterr == nil {
|
|
r.local = append([]netip.Prefix{}, cfg.LocalAddrs...)
|
|
}
|
|
r.routes = newRoutes
|
|
|
|
return reterr
|
|
}
|
|
|
|
// UpdateMagicsockPort implements the Router interface. This implementation
|
|
// does nothing and returns nil because this router does not currently need
|
|
// to know what the magicsock UDP port is.
|
|
func (r *userspaceBSDRouter) UpdateMagicsockPort(_ uint16, _ string) error {
|
|
return nil
|
|
}
|
|
|
|
func (r *userspaceBSDRouter) Close() error {
|
|
return nil
|
|
}
|