mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 02:01:14 +01:00 
			
		
		
		
	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 <github@rei.nal.do>
		
			
				
	
	
		
			157 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// 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 }
 |