From 0249236cc0f1ce7dab0b6c198548771d59cc3019 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 27 Jul 2020 20:25:25 +0000 Subject: [PATCH] ipn/ipnstate: record assigned Tailscale IPs. wgengine/magicsock: use ipnstate to find assigned Tailscale IPs. Signed-off-by: David Anderson --- ipn/ipnstate/ipnstate.go | 20 ++++++++++++ wgengine/magicsock/magicsock.go | 16 +++++++-- wgengine/magicsock/magicsock_test.go | 49 +++++++++++++--------------- 3 files changed, 55 insertions(+), 30 deletions(-) diff --git a/ipn/ipnstate/ipnstate.go b/ipn/ipnstate/ipnstate.go index e8f41c9ca..8210d518e 100644 --- a/ipn/ipnstate/ipnstate.go +++ b/ipn/ipnstate/ipnstate.go @@ -18,6 +18,7 @@ import ( "sync" "time" + "inet.af/netaddr" "tailscale.com/tailcfg" "tailscale.com/types/key" ) @@ -25,6 +26,7 @@ import ( // Status represents the entire state of the IPN network. type Status struct { BackendState string + TailscaleIPs []netaddr.IP // Tailscale IP(s) assigned to this node Peer map[key.Public]*PeerStatus User map[tailcfg.UserID]tailcfg.UserProfile } @@ -109,6 +111,18 @@ func (sb *StatusBuilder) AddUser(id tailcfg.UserID, up tailcfg.UserProfile) { sb.st.User[id] = up } +// AddIP adds a Tailscale IP address to the status. +func (sb *StatusBuilder) AddTailscaleIP(ip netaddr.IP) { + sb.mu.Lock() + defer sb.mu.Unlock() + if sb.locked { + log.Printf("[unexpected] ipnstate: AddIP after Locked") + return + } + + sb.st.TailscaleIPs = append(sb.st.TailscaleIPs, ip) +} + // AddPeer adds a peer node to the status. // // Its PeerStatus is mixed with any previous status already added. @@ -218,6 +232,12 @@ table tbody tr:nth-child(even) td { background-color: #f5f5f5; } //f("

logid: %s

\n", logid) //f("

opts: %s

\n", html.EscapeString(fmt.Sprintf("%+v", opts))) + ips := make([]string, 0, len(st.TailscaleIPs)) + for _, ip := range st.TailscaleIPs { + ips = append(ips, ip.String()) + } + f("

Tailscale IP: %s", strings.Join(ips, ", ")) + f("\n\n") f("\n") f("\n\n") diff --git a/wgengine/magicsock/magicsock.go b/wgengine/magicsock/magicsock.go index 478a431e5..149c1ae09 100644 --- a/wgengine/magicsock/magicsock.go +++ b/wgengine/magicsock/magicsock.go @@ -81,9 +81,8 @@ var ( debugReSTUNStopOnIdle, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_RESTUN_STOP_ON_IDLE")) ) -// inTest binds magicsock to 127.0.0.1 instead of its usual 0.0.0.0, -// to avoid macOS prompting for firewall permissions during -// interactive tests. +// inTest reports whether the running program is a test that set the +// IN_TS_TEST environment variable. // // Unlike the other debug tweakables above, this one needs to be // checked every time at runtime, because tests set this after program @@ -2860,6 +2859,17 @@ func (c *Conn) UpdateStatus(sb *ipnstate.StatusBuilder) { c.mu.Lock() defer c.mu.Unlock() + if c.netMap != nil { + for _, addr := range c.netMap.Addresses { + if (addr.IP.Is4() && addr.Mask != 32) || (addr.IP.Is6() && addr.Mask != 128) { + continue + } + if ip, ok := netaddr.FromStdIP(addr.IP.IP()); ok { + sb.AddTailscaleIP(ip) + } + } + } + for dk, n := range c.nodeOfDisco { ps := &ipnstate.PeerStatus{InMagicSock: true} ps.Addrs = append(ps.Addrs, n.Endpoints...) diff --git a/wgengine/magicsock/magicsock_test.go b/wgengine/magicsock/magicsock_test.go index 00af5e142..70386b811 100644 --- a/wgengine/magicsock/magicsock_test.go +++ b/wgengine/magicsock/magicsock_test.go @@ -118,7 +118,6 @@ type magicStack struct { tun *tuntest.ChannelTUN // tuntap device to send/receive packets tsTun *tstun.TUN // wrapped tun that implements filtering and wgengine hooks dev *device.Device // the wireguard-go Device that connects the previous things - tsIP chan netaddr.IP // buffered, guaranteed to yield at least 1 value } // newMagicStack builds and initializes an idle magicsock and @@ -181,7 +180,6 @@ func newMagicStack(t *testing.T, logf logger.Logf, l nettype.PacketListener, der tun: tun, tsTun: tsTun, dev: dev, - tsIP: make(chan netaddr.IP, 1), } } @@ -205,18 +203,20 @@ func (s *magicStack) Status() *ipnstate.Status { return sb.Status() } -// AwaitIP waits for magicStack to receive a Tailscale IP address on -// tsIP, and returns the IP. It's intended for use with magicStacks -// that have been meshed with meshStacks, to wait for configs to have -// propagated enough that everyone has a Tailscale IP that should -// work. -func (s *magicStack) AwaitIP() netaddr.IP { - select { - case ip := <-s.tsIP: - return ip - case <-time.After(2 * time.Second): - panic("timed out waiting for magicStack to get an IP") +// IP returns the Tailscale IP address assigned to this magicStack. +// +// Something external needs to provide a NetworkMap and WireGuard +// configs to the magicStack in order for it to acquire an IP +// address. See meshStacks for one possible source of netmaps and IPs. +func (s *magicStack) IP(t *testing.T) netaddr.IP { + for deadline := time.Now().Add(5 * time.Second); time.Now().Before(deadline); time.Sleep(10 * time.Millisecond) { + st := s.Status() + if len(st.TailscaleIPs) > 0 { + return st.TailscaleIPs[0] + } } + t.Fatal("timed out waiting for magicstack to get an IP assigned") + panic("unreachable") // compiler doesn't know t.Fatal panics } // meshStacks monitors epCh on all given ms, and plumbs network maps @@ -271,11 +271,6 @@ func meshStacks(logf logger.Logf, ms []*magicStack) (cleanup func()) { for i, m := range ms { netmap := buildNetmapLocked(i) - nip, _ := netaddr.FromStdIP(netmap.Addresses[0].IP.IP()) - select { - case m.tsIP <- nip: - default: - } m.conn.SetNetworkMap(netmap) peerSet := make(map[key.Public]struct{}, len(netmap.Peers)) for _, peer := range netmap.Peers { @@ -707,7 +702,7 @@ type devices struct { // newPinger starts continuously sending test packets from srcM to // dstM, until cleanup is invoked to stop it. Each ping has 1 second // to transit the network. It is a test failure to lose a ping. -func newPinger(t *testing.T, logf logger.Logf, srcM, dstM *magicStack, srcIP, dstIP netaddr.IP) (cleanup func()) { +func newPinger(t *testing.T, logf logger.Logf, src, dst *magicStack) (cleanup func()) { ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) one := func() bool { @@ -717,10 +712,10 @@ func newPinger(t *testing.T, logf logger.Logf, srcM, dstM *magicStack, srcIP, ds // failure). Figure out what kind of thing would be // acceptable to test instead of "every ping must // transit". - pkt := tuntest.Ping(dstIP.IPAddr().IP, srcIP.IPAddr().IP) - srcM.tun.Outbound <- pkt + pkt := tuntest.Ping(dst.IP(t).IPAddr().IP, src.IP(t).IPAddr().IP) + src.tun.Outbound <- pkt select { - case <-dstM.tun.Inbound: + case <-dst.tun.Inbound: return true case <-time.After(10 * time.Second): // Very generous timeout here because depending on @@ -737,7 +732,7 @@ func newPinger(t *testing.T, logf logger.Logf, srcM, dstM *magicStack, srcIP, ds // natlab may still be delivering a packet to us from a // goroutine. select { - case <-dstM.tun.Inbound: + case <-dst.tun.Inbound: case <-time.After(time.Second): } return false @@ -758,7 +753,7 @@ func newPinger(t *testing.T, logf logger.Logf, srcM, dstM *magicStack, srcIP, ds } go func() { - logf("sending ping stream from %s (%s) to %s (%s)", srcM, srcIP, dstM, dstIP) + logf("sending ping stream from %s (%s) to %s (%s)", src, src.IP(t), dst, dst.IP(t)) defer close(done) for one() { } @@ -792,11 +787,11 @@ func testActiveDiscovery(t *testing.T, d *devices) { cleanup = meshStacks(logf, []*magicStack{m1, m2}) defer cleanup() - m1IP := m1.AwaitIP() - m2IP := m2.AwaitIP() + m1IP := m1.IP(t) + m2IP := m2.IP(t) logf("IPs: %s %s", m1IP, m2IP) - cleanup = newPinger(t, logf, m1, m2, m1IP, m2IP) + cleanup = newPinger(t, logf, m1, m2) defer cleanup() // Everything is now up and running, active discovery should find
PeerNodeOwnerRxTxActivityEndpoints