diff --git a/ipn/local.go b/ipn/local.go index 85e46378a..8745b799f 100644 --- a/ipn/local.go +++ b/ipn/local.go @@ -14,6 +14,7 @@ import ( "time" "github.com/tailscale/wireguard-go/wgcfg" + "inet.af/netaddr" "tailscale.com/control/controlclient" "tailscale.com/ipn/ipnstate" "tailscale.com/ipn/policy" @@ -25,6 +26,7 @@ import ( "tailscale.com/version" "tailscale.com/wgengine" "tailscale.com/wgengine/filter" + "tailscale.com/wgengine/router" ) // LocalBackend is the scaffolding between the Tailscale cloud control @@ -709,13 +711,66 @@ func (b *LocalBackend) authReconfig() { log.Fatalf("WGCfg: %v", err) } - err = b.e.Reconfig(cfg, dom, uc.AdvertiseRoutes, uc.NoSNAT) + err = b.e.Reconfig(cfg, routerSettings(cfg, uc, dom)) if err == wgengine.ErrNoChanges { return } b.logf("authReconfig: ra=%v dns=%v 0x%02x: %v", uc.RouteAll, uc.CorpDNS, uflags, err) } +// routerSettings produces a router.Settings from a wireguard config, +// IPN prefs, and the dnsDomains pulled from control's network map. +func routerSettings(cfg *wgcfg.Config, prefs *Prefs, dnsDomains []string) router.Settings { + var addrs []wgcfg.CIDR + for _, addr := range cfg.Addresses { + addrs = append(addrs, wgcfg.CIDR{ + IP: addr.IP, + // TODO(apenwarr): this shouldn't be hardcoded in the client + // TODO(danderson): fairly sure we can make this a /32 or + // /128 based on address family. Need to check behavior on + // !linux OSes. + Mask: 10, + }) + } + + rs := router.Settings{ + LocalAddrs: wgCIDRToNetaddr(addrs), + DNS: wgIPToNetaddr(cfg.DNS), + DNSDomains: dnsDomains, + SubnetRoutes: wgCIDRToNetaddr(prefs.AdvertiseRoutes), + NoSNAT: prefs.NoSNAT, + } + + for _, peer := range cfg.Peers { + rs.Routes = append(rs.Routes, wgCIDRToNetaddr(peer.AllowedIPs)...) + } + + return rs +} + +func wgIPToNetaddr(ips []wgcfg.IP) (ret []netaddr.IP) { + for _, ip := range ips { + nip, ok := netaddr.FromStdIP(ip.IP()) + if !ok { + panic(fmt.Sprintf("conversion of %s from wgcfg to netaddr IP failed", ip)) + } + ret = append(ret, nip.Unmap()) + } + return ret +} + +func wgCIDRToNetaddr(cidrs []wgcfg.CIDR) (ret []netaddr.IPPrefix) { + for _, cidr := range cidrs { + ncidr, ok := netaddr.FromStdIPNet(cidr.IPNet()) + if !ok { + panic(fmt.Sprintf("conversion of %s from wgcfg to netaddr IPNet failed", cidr)) + } + ncidr.IP = ncidr.IP.Unmap() + ret = append(ret, ncidr) + } + return ret +} + func (b *LocalBackend) enterState(newState State) { b.mu.Lock() state := b.state @@ -738,7 +793,7 @@ func (b *LocalBackend) enterState(newState State) { b.blockEngineUpdates(true) fallthrough case Stopped: - err := b.e.Reconfig(&wgcfg.Config{}, nil, nil, false) + err := b.e.Reconfig(&wgcfg.Config{}, router.Settings{}) if err != nil { b.logf("Reconfig(down): %v", err) } @@ -814,7 +869,7 @@ func (b *LocalBackend) stateMachine() { func (b *LocalBackend) stopEngineAndWait() { b.logf("stopEngineAndWait...") - b.e.Reconfig(&wgcfg.Config{}, nil, nil, false) + b.e.Reconfig(&wgcfg.Config{}, router.Settings{}) b.requestEngineStatusAndWait() b.logf("stopEngineAndWait: done.") } diff --git a/wgengine/userspace.go b/wgengine/userspace.go index d21da466b..ce7090ba3 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -23,7 +23,6 @@ import ( "github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/wgcfg" "go4.org/mem" - "inet.af/netaddr" "tailscale.com/ipn/ipnstate" "tailscale.com/net/interfaces" "tailscale.com/tailcfg" @@ -327,26 +326,17 @@ func (e *userspaceEngine) pinger(peerKey wgcfg.Key, ips []wgcfg.IP) { } } -func configSignature(cfg *wgcfg.Config, dnsDomains []string, localRoutes []wgcfg.CIDR, noSNAT bool) (string, error) { +func configSignature(cfg *wgcfg.Config, routerCfg router.Settings) (string, error) { // TODO(apenwarr): get rid of uapi stuff for in-process comms uapi, err := cfg.ToUAPI() if err != nil { return "", err } - return fmt.Sprintf("%s %v %v %v", uapi, dnsDomains, localRoutes, noSNAT), nil + return fmt.Sprintf("%s %v", uapi, routerCfg), nil } -// TODO(apenwarr): dnsDomains really ought to be in wgcfg.Config. -// However, we don't actually ever provide it to wireguard and it's not in -// the traditional wireguard config format. On the other hand, wireguard -// itself doesn't use the traditional 'dns =' setting either. -// -// TODO(danderson): this function signature is starting to get out of -// hand. Feels like we either need a wgengine.Config type, or make -// router and wgengine siblings of each other that interact via glue -// in ipn. -func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, dnsDomains []string, localRoutes []wgcfg.CIDR, noSNAT bool) error { +func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg router.Settings) error { e.wgLock.Lock() defer e.wgLock.Unlock() @@ -359,7 +349,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, dnsDomains []string, local } e.mu.Unlock() - rc, err := configSignature(cfg, dnsDomains, localRoutes, noSNAT) + rc, err := configSignature(cfg, routerCfg) if err != nil { return err } @@ -386,32 +376,7 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, dnsDomains []string, local e.magicConn.UpdatePeers(peerSet) - // TODO(apenwarr): only handling the first local address. - // Currently we never use more than one anyway. - var addrs []wgcfg.CIDR - for _, addr := range cfg.Addresses { - addrs = append(addrs, wgcfg.CIDR{ - IP: addr.IP, - // TODO(apenwarr): this shouldn't be hardcoded in the client - // TODO(danderson): fairly sure we can make this a /32 or - // /128 based on address family. Need to check behavior on - // !linux OSes. - Mask: 10, - }) - } - - rs := router.Settings{ - LocalAddrs: wgCIDRToNetaddr(addrs), - DNS: wgIPToNetaddr(cfg.DNS), - DNSDomains: dnsDomains, - SubnetRoutes: wgCIDRToNetaddr(localRoutes), - NoSNAT: noSNAT, - } - for _, peer := range cfg.Peers { - rs.Routes = append(rs.Routes, wgCIDRToNetaddr(peer.AllowedIPs)...) - } - - if err := e.router.Set(rs); err != nil { + if err := e.router.Set(routerCfg); err != nil { return err } @@ -419,29 +384,6 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, dnsDomains []string, local return nil } -func wgIPToNetaddr(ips []wgcfg.IP) (ret []netaddr.IP) { - for _, ip := range ips { - nip, ok := netaddr.FromStdIP(ip.IP()) - if !ok { - panic(fmt.Sprintf("conversion of %s from wgcfg to netaddr IP failed", ip)) - } - ret = append(ret, nip.Unmap()) - } - return ret -} - -func wgCIDRToNetaddr(cidrs []wgcfg.CIDR) (ret []netaddr.IPPrefix) { - for _, cidr := range cidrs { - ncidr, ok := netaddr.FromStdIPNet(cidr.IPNet()) - if !ok { - panic(fmt.Sprintf("conversion of %s from wgcfg to netaddr IPNet failed", cidr)) - } - ncidr.IP = ncidr.IP.Unmap() - ret = append(ret, ncidr) - } - return ret -} - func (e *userspaceEngine) GetFilter() *filter.Filter { e.mu.Lock() defer e.mu.Unlock() diff --git a/wgengine/watchdog.go b/wgengine/watchdog.go index 9a6f9a12b..2c60839ab 100644 --- a/wgengine/watchdog.go +++ b/wgengine/watchdog.go @@ -13,6 +13,7 @@ import ( "github.com/tailscale/wireguard-go/wgcfg" "tailscale.com/ipn/ipnstate" "tailscale.com/wgengine/filter" + "tailscale.com/wgengine/router" ) // NewWatchdog wraps an Engine and makes sure that all methods complete @@ -61,8 +62,8 @@ func (e *watchdogEngine) watchdog(name string, fn func()) { }) } -func (e *watchdogEngine) Reconfig(cfg *wgcfg.Config, dnsDomains []string, localRoutes []wgcfg.CIDR, noSNAT bool) error { - return e.watchdogErr("Reconfig", func() error { return e.wrap.Reconfig(cfg, dnsDomains, localRoutes, noSNAT) }) +func (e *watchdogEngine) Reconfig(cfg *wgcfg.Config, routerCfg router.Settings) error { + return e.watchdogErr("Reconfig", func() error { return e.wrap.Reconfig(cfg, routerCfg) }) } func (e *watchdogEngine) GetFilter() *filter.Filter { var x *filter.Filter diff --git a/wgengine/wgengine.go b/wgengine/wgengine.go index 196ecc95c..a6d194fad 100644 --- a/wgengine/wgengine.go +++ b/wgengine/wgengine.go @@ -12,6 +12,7 @@ import ( "tailscale.com/ipn/ipnstate" "tailscale.com/tailcfg" "tailscale.com/wgengine/filter" + "tailscale.com/wgengine/router" ) // ByteCount is the number of bytes that have been sent or received. @@ -59,7 +60,7 @@ type Engine interface { // sends an updated network map. // // The returned error is ErrNoChanges if no changes were made. - Reconfig(cfg *wgcfg.Config, dnsDomains []string, localSubnets []wgcfg.CIDR, noSNAT bool) error + Reconfig(cfg *wgcfg.Config, routerCfg router.Settings) error // GetFilter returns the current packet filter, if any. GetFilter() *filter.Filter