diff --git a/wgengine/router_freebsd.go b/wgengine/router_freebsd.go new file mode 100644 index 000000000..6c9b37a00 --- /dev/null +++ b/wgengine/router_freebsd.go @@ -0,0 +1,158 @@ +// 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. + +package wgengine + +import ( + "fmt" + "log" + "net" + "os/exec" + + "github.com/tailscale/wireguard-go/device" + "github.com/tailscale/wireguard-go/tun" + "github.com/tailscale/wireguard-go/wgcfg" + "tailscale.com/types/logger" +) + +// For now this router only supports the userspace WireGuard implementations. +// +// 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 wgcfg.CIDR + routes map[wgcfg.CIDR]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 +} + +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) SetRoutes(rs RouteSettings) error { + var errq error + + // Update the address. + if rs.LocalAddr != r.local { + // If the interface is already set, remove it. + if r.local != (wgcfg.CIDR{}) { + 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", rs.LocalAddr.String(), rs.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[wgcfg.CIDR]struct{}) + for _, peer := range rs.Cfg.Peers { + for _, route := range peer.AllowedIPs { + 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.Mask) + 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.Mask) + 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 = rs.LocalAddr + r.routes = newRoutes + + if err := r.replaceResolvConf(rs.DNS, rs.DNSDomains); err != nil { + errq = fmt.Errorf("replacing resolv.conf failed: %v", err) + } + + return errq +} + +func (r *freebsdRouter) Close() error { + out, err := cmd("ifconfig", r.tunname, "down").CombinedOutput() + if err != nil { + r.logf("running ifconfig failed: %v\n%s", err, out) + } + + if err := r.restoreResolvConf(); err != nil { + r.logf("failed to restore system resolv.conf: %v", err) + } + + 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(_ []net.IP, _ []string) error { return nil } +func (r *freebsdRouter) restoreResolvConf() error { return nil } diff --git a/wgengine/router_bsd.go b/wgengine/router_openbsd.go similarity index 92% rename from wgengine/router_bsd.go rename to wgengine/router_openbsd.go index 36b52f946..2deadc8b6 100644 --- a/wgengine/router_bsd.go +++ b/wgengine/router_openbsd.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build openbsd freebsd - package wgengine import ( @@ -27,7 +25,7 @@ import ( // `ifstated(8)`/`devd(8)`, or become possible with the OpenBSD kernel // implementation. This merits further investigation. -type bsdRouter struct { +type openbsdRouter struct { logf logger.Logf tunname string local wgcfg.CIDR @@ -39,7 +37,7 @@ func newUserspaceRouter(logf logger.Logf, _ *device.Device, tundev tun.Device) ( if err != nil { return nil, err } - return &bsdRouter{ + return &openbsdRouter{ logf: logf, tunname: tunname, }, nil @@ -53,7 +51,7 @@ func cmd(args ...string) *exec.Cmd { return exec.Command(args[0], args[1:]...) } -func (r *bsdRouter) Up() error { +func (r *openbsdRouter) 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) @@ -62,7 +60,7 @@ func (r *bsdRouter) Up() error { return nil } -func (r *bsdRouter) SetRoutes(rs RouteSettings) error { +func (r *openbsdRouter) SetRoutes(rs RouteSettings) error { var errq error if rs.LocalAddr != r.local { @@ -160,7 +158,7 @@ func (r *bsdRouter) SetRoutes(rs RouteSettings) error { return errq } -func (r *bsdRouter) Close() error { +func (r *openbsdRouter) Close() error { out, err := cmd("ifconfig", r.tunname, "down").CombinedOutput() if err != nil { r.logf("running ifconfig failed: %v\n%s", err, out) @@ -177,5 +175,5 @@ func (r *bsdRouter) Close() error { // 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 *bsdRouter) replaceResolvConf(_ []net.IP, _ []string) error { return nil } -func (r *bsdRouter) restoreResolvConf() error { return nil } +func (r *openbsdRouter) replaceResolvConf(_ []net.IP, _ []string) error { return nil } +func (r *openbsdRouter) restoreResolvConf() error { return nil }