From 72cae5504c8834fc6f3403145357d311d36f7fdb Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 11 May 2020 21:02:12 +0000 Subject: [PATCH] wgengine: generate and plumb router.Settings in from ipn. This saves a layer of translation, and saves us having to pass in extra bits and pieces of the netmap and prefs to wgengine. Now it gets one Wireguard config, and one OS network stack config. Signed-off-by: David Anderson --- ipn/local.go | 61 ++++++++++++++++++++++++++++++++++++-- wgengine/userspace.go | 68 ++++--------------------------------------- wgengine/watchdog.go | 5 ++-- wgengine/wgengine.go | 3 +- 4 files changed, 68 insertions(+), 69 deletions(-) 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