From a8635784bc0bee2232b320986425ac7327a5b044 Mon Sep 17 00:00:00 2001 From: Reinaldo de Souza Date: Sun, 14 Jun 2020 14:18:38 +0200 Subject: [PATCH] wgengine: add BSD userspace router to darwin Darwin and FreeBSD are compatible enough to share the userspace router. The OSX router delegates to the BSD userspace router unless `SetRoutesFunc` is set. That preserves the mechanism that allows `ipn-go-bridge` to specify its own routing behavior. Fixes #177 Signed-off-by: Reinaldo de Souza --- wgengine/router/router_darwin.go | 24 ++-- wgengine/router/router_freebsd.go | 141 +-------------------- wgengine/router/router_userspace_bsd.go | 156 ++++++++++++++++++++++++ 3 files changed, 170 insertions(+), 151 deletions(-) create mode 100644 wgengine/router/router_userspace_bsd.go diff --git a/wgengine/router/router_darwin.go b/wgengine/router/router_darwin.go index 36b718248..5e9cb14a1 100644 --- a/wgengine/router/router_darwin.go +++ b/wgengine/router/router_darwin.go @@ -13,6 +13,7 @@ type darwinRouter struct { logf logger.Logf tunname string + Router } func newUserspaceRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) (Router, error) { @@ -20,26 +21,27 @@ func newUserspaceRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) ( if err != nil { return nil, err } + + userspaceRouter, err := newUserspaceBSDRouter(logf, nil, tundev) + if err != nil { + return nil, err + } + return &darwinRouter{ logf: logf, tunname: tunname, + Router: userspaceRouter, }, nil } -func (r *darwinRouter) Up() error { - return nil -} - func (r *darwinRouter) Set(cfg *Config) error { - if SetRoutesFunc == nil { - return nil - } if cfg == nil { cfg = &shutdownConfig } - return SetRoutesFunc(cfg) -} -func (r *darwinRouter) Close() error { - return nil + if SetRoutesFunc != nil { + return SetRoutesFunc(cfg) + } + + return r.Router.Set(cfg) } diff --git a/wgengine/router/router_freebsd.go b/wgengine/router/router_freebsd.go index 86fd04a42..e7113223f 100644 --- a/wgengine/router/router_freebsd.go +++ b/wgengine/router/router_freebsd.go @@ -5,14 +5,8 @@ package router import ( - "errors" - "fmt" - "log" - "os/exec" - "github.com/tailscale/wireguard-go/device" "github.com/tailscale/wireguard-go/tun" - "inet.af/netaddr" "tailscale.com/types/logger" ) @@ -21,139 +15,6 @@ // Work is currently underway for an in-kernel FreeBSD implementation of wireguard // https://svnweb.freebsd.org/base?view=revision&revision=357986 -type freebsdRouter struct { - logf logger.Logf - tunname string - local netaddr.IPPrefix - routes map[netaddr.IPPrefix]struct{} -} - func newUserspaceRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) (Router, error) { - tunname, err := tundev.Name() - if err != nil { - return nil, err - } - return &freebsdRouter{ - logf: logf, - tunname: tunname, - }, nil + return newUserspaceBSDRouter(logf, nil, tundev) } - -func cmd(args ...string) *exec.Cmd { - if len(args) == 0 { - log.Fatalf("exec.Cmd(%#v) invalid; need argv[0]\n", args) - } - return exec.Command(args[0], args[1:]...) -} - -func (r *freebsdRouter) 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 (r *freebsdRouter) Set(cfg *Config) error { - if cfg == nil { - cfg = &shutdownConfig - } - if len(cfg.LocalAddrs) == 0 { - return nil - } - // TODO: support configuring multiple local addrs on interface. - if len(cfg.LocalAddrs) != 1 { - return errors.New("freebsd doesn't support setting multiple local addrs yet") - } - localAddr := cfg.LocalAddrs[0] - - var errq error - - // Update the address. - if localAddr != r.local { - // If the interface is already set, remove it. - if r.local != (netaddr.IPPrefix{}) { - addrdel := []string{"ifconfig", r.tunname, - "inet", r.local.String(), "-alias"} - out, err := cmd(addrdel...).CombinedOutput() - if err != nil { - r.logf("addr del failed: %v: %v\n%s", addrdel, err, out) - if errq == nil { - errq = err - } - } - } - - // Add the interface. - addradd := []string{"ifconfig", r.tunname, - "inet", localAddr.String(), localAddr.IP.String()} - out, err := cmd(addradd...).CombinedOutput() - if err != nil { - r.logf("addr add failed: %v: %v\n%s", addradd, err, out) - if errq == nil { - errq = err - } - } - } - - newRoutes := make(map[netaddr.IPPrefix]struct{}) - for _, route := range cfg.Routes { - newRoutes[route] = struct{}{} - } - // Delete any pre-existing routes. - for route := range r.routes { - if _, keep := newRoutes[route]; !keep { - net := route.IPNet() - nip := net.IP.Mask(net.Mask) - nstr := fmt.Sprintf("%v/%d", nip, route.Bits) - routedel := []string{"route", "-q", "-n", - "del", "-inet", nstr, - "-iface", r.tunname} - out, err := cmd(routedel...).CombinedOutput() - if err != nil { - r.logf("route del failed: %v: %v\n%s", routedel, err, out) - if errq == nil { - errq = err - } - } - } - } - // Add the routes. - for route := range newRoutes { - if _, exists := r.routes[route]; !exists { - net := route.IPNet() - nip := net.IP.Mask(net.Mask) - nstr := fmt.Sprintf("%v/%d", nip, route.Bits) - routeadd := []string{"route", "-q", "-n", - "add", "-inet", nstr, - "-iface", r.tunname} - out, err := cmd(routeadd...).CombinedOutput() - if err != nil { - r.logf("addr add failed: %v: %v\n%s", routeadd, err, out) - if errq == nil { - errq = err - } - } - } - } - - // Store the interface and routes so we know what to change on an update. - r.local = localAddr - r.routes = newRoutes - - if err := r.replaceResolvConf(cfg.DNS, cfg.DNSDomains); err != nil { - errq = fmt.Errorf("replacing resolv.conf failed: %v", err) - } - - return errq -} - -func (r *freebsdRouter) Close() error { - return nil -} - -// TODO(mbaillie): these are no-ops for now. They could re-use the Linux funcs -// (sans systemd parts), but I note Linux DNS is disabled(?) so leaving for now. -func (r *freebsdRouter) replaceResolvConf(_ []netaddr.IP, _ []string) error { return nil } -func (r *freebsdRouter) restoreResolvConf() error { return nil } diff --git a/wgengine/router/router_userspace_bsd.go b/wgengine/router/router_userspace_bsd.go new file mode 100644 index 000000000..108b79161 --- /dev/null +++ b/wgengine/router/router_userspace_bsd.go @@ -0,0 +1,156 @@ +// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin freebsd + +package router + +import ( + "errors" + "fmt" + "log" + "os/exec" + + "github.com/tailscale/wireguard-go/device" + "github.com/tailscale/wireguard-go/tun" + "inet.af/netaddr" + "tailscale.com/types/logger" +) + +type userspaceBSDRouter struct { + logf logger.Logf + tunname string + local netaddr.IPPrefix + routes map[netaddr.IPPrefix]struct{} +} + +func newUserspaceBSDRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) (Router, error) { + tunname, err := tundev.Name() + if err != nil { + return nil, err + } + return &userspaceBSDRouter{ + logf: logf, + tunname: tunname, + }, nil +} + +func (r *userspaceBSDRouter) cmd(args ...string) *exec.Cmd { + if len(args) == 0 { + log.Fatalf("exec.Cmd(%#v) invalid; need argv[0]\n", args) + } + return exec.Command(args[0], args[1:]...) +} + +func (r *userspaceBSDRouter) Up() error { + ifup := []string{"ifconfig", r.tunname, "up"} + if out, err := r.cmd(ifup...).CombinedOutput(); err != nil { + r.logf("running ifconfig failed: %v\n%s", err, out) + return err + } + return nil +} + +func (r *userspaceBSDRouter) Set(cfg *Config) error { + if cfg == nil { + cfg = &shutdownConfig + } + if len(cfg.LocalAddrs) == 0 { + return nil + } + // TODO: support configuring multiple local addrs on interface. + if len(cfg.LocalAddrs) != 1 { + return errors.New("freebsd doesn't support setting multiple local addrs yet") + } + localAddr := cfg.LocalAddrs[0] + + var errq error + + // Update the address. + if localAddr != r.local { + // If the interface is already set, remove it. + if r.local != (netaddr.IPPrefix{}) { + addrdel := []string{"ifconfig", r.tunname, + "inet", r.local.String(), "-alias"} + out, err := r.cmd(addrdel...).CombinedOutput() + if err != nil { + r.logf("addr del failed: %v: %v\n%s", addrdel, err, out) + if errq == nil { + errq = err + } + } + } + + // Add the interface. + addradd := []string{"ifconfig", r.tunname, + "inet", localAddr.String(), localAddr.IP.String()} + out, err := r.cmd(addradd...).CombinedOutput() + if err != nil { + r.logf("addr add failed: %v: %v\n%s", addradd, err, out) + if errq == nil { + errq = err + } + } + } + + newRoutes := make(map[netaddr.IPPrefix]struct{}) + for _, route := range cfg.Routes { + newRoutes[route] = struct{}{} + } + // Delete any pre-existing routes. + for route := range r.routes { + if _, keep := newRoutes[route]; !keep { + net := route.IPNet() + nip := net.IP.Mask(net.Mask) + nstr := fmt.Sprintf("%v/%d", nip, route.Bits) + routedel := []string{"route", "-q", "-n", + "del", "-inet", nstr, + "-iface", r.tunname} + out, err := r.cmd(routedel...).CombinedOutput() + if err != nil { + r.logf("route del failed: %v: %v\n%s", routedel, err, out) + if errq == nil { + errq = err + } + } + } + } + // Add the routes. + for route := range newRoutes { + if _, exists := r.routes[route]; !exists { + net := route.IPNet() + nip := net.IP.Mask(net.Mask) + nstr := fmt.Sprintf("%v/%d", nip, route.Bits) + routeadd := []string{"route", "-q", "-n", + "add", "-inet", nstr, + "-iface", r.tunname} + out, err := r.cmd(routeadd...).CombinedOutput() + if err != nil { + r.logf("addr add failed: %v: %v\n%s", routeadd, err, out) + if errq == nil { + errq = err + } + } + } + } + + // Store the interface and routes so we know what to change on an update. + r.local = localAddr + r.routes = newRoutes + + if err := r.replaceResolvConf(cfg.DNS, cfg.DNSDomains); err != nil { + errq = fmt.Errorf("replacing resolv.conf failed: %v", err) + } + + return errq +} + +func (r *userspaceBSDRouter) Close() error { + return nil +} + +// TODO(mbaillie): these are no-ops for now. They could re-use the Linux funcs +// (sans systemd parts), but I note Linux DNS is disabled(?) so leaving for now. +func (r *userspaceBSDRouter) replaceResolvConf(_ []netaddr.IP, _ []string) error { return nil } +func (r *userspaceBSDRouter) restoreResolvConf() error { return nil }