mirror of https://github.com/tailscale/tailscale/
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
4.4 KiB
Go
162 lines
4.4 KiB
Go
2 years ago
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||
4 years ago
|
|
||
2 years ago
|
package netmon
|
||
4 years ago
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
3 years ago
|
"strings"
|
||
|
"sync"
|
||
4 years ago
|
"time"
|
||
|
|
||
4 years ago
|
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||
3 years ago
|
"tailscale.com/net/tsaddr"
|
||
4 years ago
|
"tailscale.com/types/logger"
|
||
|
)
|
||
|
|
||
|
var (
|
||
4 years ago
|
errClosed = errors.New("closed")
|
||
4 years ago
|
)
|
||
|
|
||
4 years ago
|
type eventMessage struct {
|
||
|
eventType string
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
4 years ago
|
func (eventMessage) ignore() bool { return false }
|
||
4 years ago
|
|
||
4 years ago
|
type winMon struct {
|
||
|
logf logger.Logf
|
||
|
ctx context.Context
|
||
|
cancel context.CancelFunc
|
||
|
messagec chan eventMessage
|
||
|
addressChangeCallback *winipcfg.UnicastAddressChangeCallback
|
||
|
routeChangeCallback *winipcfg.RouteChangeCallback
|
||
|
|
||
3 years ago
|
mu sync.Mutex
|
||
|
lastLog time.Time // time we last logged about any windows change event
|
||
|
|
||
4 years ago
|
// 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
|
||
4 years ago
|
}
|
||
|
|
||
2 years ago
|
func newOSMon(logf logger.Logf, _ *Monitor) (osMon, error) {
|
||
4 years ago
|
m := &winMon{
|
||
|
logf: logf,
|
||
|
messagec: make(chan eventMessage, 1),
|
||
|
noDeadlockTicker: time.NewTicker(5000 * time.Hour), // arbitrary
|
||
4 years ago
|
}
|
||
2 years ago
|
m.ctx, m.cancel = context.WithCancel(context.Background())
|
||
4 years ago
|
|
||
4 years ago
|
var err error
|
||
|
m.addressChangeCallback, err = winipcfg.RegisterUnicastAddressChangeCallback(m.unicastAddressChanged)
|
||
|
if err != nil {
|
||
|
m.logf("winipcfg.RegisterUnicastAddressChangeCallback error: %v", err)
|
||
2 years ago
|
m.cancel()
|
||
4 years ago
|
return nil, err
|
||
4 years ago
|
}
|
||
4 years ago
|
|
||
4 years ago
|
m.routeChangeCallback, err = winipcfg.RegisterRouteChangeCallback(m.routeChanged)
|
||
|
if err != nil {
|
||
|
m.addressChangeCallback.Unregister()
|
||
|
m.logf("winipcfg.RegisterRouteChangeCallback error: %v", err)
|
||
2 years ago
|
m.cancel()
|
||
4 years ago
|
return nil, err
|
||
|
}
|
||
4 years ago
|
|
||
4 years ago
|
return m, nil
|
||
4 years ago
|
}
|
||
|
|
||
3 years ago
|
func (m *winMon) IsInterestingInterface(iface string) bool { return true }
|
||
|
|
||
4 years ago
|
func (m *winMon) Close() (ret error) {
|
||
|
m.cancel()
|
||
|
m.noDeadlockTicker.Stop()
|
||
4 years ago
|
|
||
4 years ago
|
if m.addressChangeCallback != nil {
|
||
|
if err := m.addressChangeCallback.Unregister(); err != nil {
|
||
|
m.logf("addressChangeCallback.Unregister error: %v", err)
|
||
|
ret = err
|
||
|
} else {
|
||
|
m.addressChangeCallback = nil
|
||
4 years ago
|
}
|
||
|
}
|
||
|
|
||
4 years ago
|
if m.routeChangeCallback != nil {
|
||
|
if err := m.routeChangeCallback.Unregister(); err != nil {
|
||
|
m.logf("routeChangeCallback.Unregister error: %v", err)
|
||
|
ret = err
|
||
|
} else {
|
||
|
m.routeChangeCallback = nil
|
||
4 years ago
|
}
|
||
|
}
|
||
4 years ago
|
|
||
|
return
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
func (m *winMon) Receive() (message, error) {
|
||
4 years ago
|
if m.ctx.Err() != nil {
|
||
4 years ago
|
m.logf("Receive call on closed monitor")
|
||
4 years ago
|
return nil, errClosed
|
||
|
}
|
||
|
|
||
|
t0 := time.Now()
|
||
4 years ago
|
|
||
4 years ago
|
select {
|
||
|
case msg := <-m.messagec:
|
||
3 years ago
|
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)
|
||
|
}
|
||
4 years ago
|
return msg, nil
|
||
|
case <-m.ctx.Done():
|
||
|
return nil, errClosed
|
||
4 years ago
|
}
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
// unicastAddressChanged is the callback we register with Windows to call when unicast address changes.
|
||
3 years ago
|
func (m *winMon) unicastAddressChanged(_ winipcfg.MibNotificationType, row *winipcfg.MibUnicastIPAddressRow) {
|
||
|
what := "addr"
|
||
2 years ago
|
if ip := row.Address.Addr(); ip.IsValid() && tsaddr.IsTailscaleIP(ip.Unmap()) {
|
||
3 years ago
|
what = "tsaddr"
|
||
|
}
|
||
|
|
||
4 years ago
|
// start a goroutine to finish our work, to return to Windows out of this callback
|
||
3 years ago
|
go m.somethingChanged(what)
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
// routeChanged is the callback we register with Windows to call when route changes.
|
||
3 years ago
|
func (m *winMon) routeChanged(_ winipcfg.MibNotificationType, row *winipcfg.MibIPforwardRow2) {
|
||
|
what := "route"
|
||
2 years ago
|
ip := row.DestinationPrefix.Prefix().Addr().Unmap()
|
||
|
if ip.IsValid() && tsaddr.IsTailscaleIP(ip) {
|
||
3 years ago
|
what = "tsroute"
|
||
|
}
|
||
4 years ago
|
// start a goroutine to finish our work, to return to Windows out of this callback
|
||
3 years ago
|
go m.somethingChanged(what)
|
||
4 years ago
|
}
|
||
|
|
||
4 years ago
|
// 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
|
||
4 years ago
|
}
|
||
|
}
|