mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-11-04 10:11:18 +01:00 
			
		
		
		
	Updates #5162 Change-Id: If99a3f0000bce0c01bdf44da1d513f236fd7cdf8 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			162 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.4 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.
 | 
						|
 | 
						|
package monitor
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"strings"
 | 
						|
	"sync"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
 | 
						|
	"tailscale.com/net/tsaddr"
 | 
						|
	"tailscale.com/types/logger"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	errClosed = errors.New("closed")
 | 
						|
)
 | 
						|
 | 
						|
type eventMessage struct {
 | 
						|
	eventType string
 | 
						|
}
 | 
						|
 | 
						|
func (eventMessage) ignore() bool { return false }
 | 
						|
 | 
						|
type winMon struct {
 | 
						|
	logf                  logger.Logf
 | 
						|
	ctx                   context.Context
 | 
						|
	cancel                context.CancelFunc
 | 
						|
	messagec              chan eventMessage
 | 
						|
	addressChangeCallback *winipcfg.UnicastAddressChangeCallback
 | 
						|
	routeChangeCallback   *winipcfg.RouteChangeCallback
 | 
						|
 | 
						|
	mu      sync.Mutex
 | 
						|
	lastLog time.Time // time we last logged about any windows change event
 | 
						|
 | 
						|
	// noDeadlockTicker exists just to have something scheduled as
 | 
						|
	// far as the Go runtime is concerned. Otherwise "tailscaled
 | 
						|
	// debug --monitor" thinks it's deadlocked with nothing to do,
 | 
						|
	// as Go's runtime doesn't know about callbacks registered with
 | 
						|
	// Windows.
 | 
						|
	noDeadlockTicker *time.Ticker
 | 
						|
}
 | 
						|
 | 
						|
func newOSMon(logf logger.Logf, _ *Mon) (osMon, error) {
 | 
						|
	m := &winMon{
 | 
						|
		logf:             logf,
 | 
						|
		messagec:         make(chan eventMessage, 1),
 | 
						|
		noDeadlockTicker: time.NewTicker(5000 * time.Hour), // arbitrary
 | 
						|
	}
 | 
						|
 | 
						|
	var err error
 | 
						|
	m.addressChangeCallback, err = winipcfg.RegisterUnicastAddressChangeCallback(m.unicastAddressChanged)
 | 
						|
	if err != nil {
 | 
						|
		m.logf("winipcfg.RegisterUnicastAddressChangeCallback error: %v", err)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	m.routeChangeCallback, err = winipcfg.RegisterRouteChangeCallback(m.routeChanged)
 | 
						|
	if err != nil {
 | 
						|
		m.addressChangeCallback.Unregister()
 | 
						|
		m.logf("winipcfg.RegisterRouteChangeCallback error: %v", err)
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	m.ctx, m.cancel = context.WithCancel(context.Background())
 | 
						|
 | 
						|
	return m, nil
 | 
						|
}
 | 
						|
 | 
						|
func (m *winMon) IsInterestingInterface(iface string) bool { return true }
 | 
						|
 | 
						|
func (m *winMon) Close() (ret error) {
 | 
						|
	m.cancel()
 | 
						|
	m.noDeadlockTicker.Stop()
 | 
						|
 | 
						|
	if m.addressChangeCallback != nil {
 | 
						|
		if err := m.addressChangeCallback.Unregister(); err != nil {
 | 
						|
			m.logf("addressChangeCallback.Unregister error: %v", err)
 | 
						|
			ret = err
 | 
						|
		} else {
 | 
						|
			m.addressChangeCallback = nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if m.routeChangeCallback != nil {
 | 
						|
		if err := m.routeChangeCallback.Unregister(); err != nil {
 | 
						|
			m.logf("routeChangeCallback.Unregister error: %v", err)
 | 
						|
			ret = err
 | 
						|
		} else {
 | 
						|
			m.routeChangeCallback = nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 | 
						|
 | 
						|
func (m *winMon) Receive() (message, error) {
 | 
						|
	if m.ctx.Err() != nil {
 | 
						|
		m.logf("Receive call on closed monitor")
 | 
						|
		return nil, errClosed
 | 
						|
	}
 | 
						|
 | 
						|
	t0 := time.Now()
 | 
						|
 | 
						|
	select {
 | 
						|
	case msg := <-m.messagec:
 | 
						|
		now := time.Now()
 | 
						|
		m.mu.Lock()
 | 
						|
		sinceLast := now.Sub(m.lastLog)
 | 
						|
		m.lastLog = now
 | 
						|
		m.mu.Unlock()
 | 
						|
		// If it's either been awhile since we last logged
 | 
						|
		// anything, or if this some route/addr that's not
 | 
						|
		// about a Tailscale IP ("ts" prefix), then log. This
 | 
						|
		// is mainly limited to suppress the flood about our own
 | 
						|
		// route updates after connecting to a large tailnet
 | 
						|
		// and all the IPv4 /32 routes.
 | 
						|
		if sinceLast > 5*time.Second || !strings.HasPrefix(msg.eventType, "ts") {
 | 
						|
			m.logf("got windows change event after %v: evt=%s", time.Since(t0).Round(time.Millisecond), msg.eventType)
 | 
						|
		}
 | 
						|
		return msg, nil
 | 
						|
	case <-m.ctx.Done():
 | 
						|
		return nil, errClosed
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// unicastAddressChanged is the callback we register with Windows to call when unicast address changes.
 | 
						|
func (m *winMon) unicastAddressChanged(_ winipcfg.MibNotificationType, row *winipcfg.MibUnicastIPAddressRow) {
 | 
						|
	what := "addr"
 | 
						|
	if ip := row.Address.Addr(); ip.IsValid() && tsaddr.IsTailscaleIP(ip.Unmap()) {
 | 
						|
		what = "tsaddr"
 | 
						|
	}
 | 
						|
 | 
						|
	// start a goroutine to finish our work, to return to Windows out of this callback
 | 
						|
	go m.somethingChanged(what)
 | 
						|
}
 | 
						|
 | 
						|
// routeChanged is the callback we register with Windows to call when route changes.
 | 
						|
func (m *winMon) routeChanged(_ winipcfg.MibNotificationType, row *winipcfg.MibIPforwardRow2) {
 | 
						|
	what := "route"
 | 
						|
	ip := row.DestinationPrefix.Prefix().Addr().Unmap()
 | 
						|
	if ip.IsValid() && tsaddr.IsTailscaleIP(ip) {
 | 
						|
		what = "tsroute"
 | 
						|
	}
 | 
						|
	// start a goroutine to finish our work, to return to Windows out of this callback
 | 
						|
	go m.somethingChanged(what)
 | 
						|
}
 | 
						|
 | 
						|
// somethingChanged gets called from OS callbacks whenever address or route changes.
 | 
						|
func (m *winMon) somethingChanged(evt string) {
 | 
						|
	select {
 | 
						|
	case <-m.ctx.Done():
 | 
						|
		return
 | 
						|
	case m.messagec <- eventMessage{eventType: evt}:
 | 
						|
		return
 | 
						|
	}
 | 
						|
}
 |