all: update tailscale.com to v1.74.0

This commit is contained in:
Cuong Manh Le
2024-09-13 20:10:26 +07:00
committed by Cuong Manh Le
parent 7088df58dd
commit 617674ce43
29 changed files with 974 additions and 379 deletions

View File

@@ -37,7 +37,7 @@ import (
"github.com/spf13/pflag"
"github.com/spf13/viper"
"tailscale.com/logtail/backoff"
"tailscale.com/net/interfaces"
"tailscale.com/net/netmon"
"github.com/Control-D-Inc/ctrld"
"github.com/Control-D-Inc/ctrld/internal/clientinfo"
@@ -730,7 +730,7 @@ NOTE: Uninstalling will set DNS to values provided by DHCP.`,
Short: "List network interfaces of the host",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
err := interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
err := netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) {
fmt.Printf("Index : %d\n", i.Index)
fmt.Printf("Name : %s\n", i.Name)
addrs, _ := i.Addrs()
@@ -1662,7 +1662,7 @@ func netInterface(ifaceName string) (*net.Interface, error) {
ifaceName = defaultIfaceName()
}
var iface *net.Interface
err := interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
err := netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) {
if i.Name == ifaceName {
iface = i.Interface
}
@@ -1680,7 +1680,7 @@ func defaultIfaceName() string {
if ifaceName := router.DefaultInterfaceName(); ifaceName != "" {
return ifaceName
}
dri, err := interfaces.DefaultRouteInterface()
dri, err := netmon.DefaultRouteInterface()
if err != nil {
// On WSL 1, the route table does not have any default route. But the fact that
// it only uses /etc/resolv.conf for setup DNS, so we can use "lo" here.

View File

@@ -14,9 +14,10 @@ import (
"sync"
"time"
"tailscale.com/net/netmon"
"github.com/miekg/dns"
"golang.org/x/sync/errgroup"
"tailscale.com/net/interfaces"
"tailscale.com/net/netaddr"
"tailscale.com/net/tsaddr"
@@ -890,7 +891,7 @@ func (p *prog) selfUninstallCoolOfPeriod() {
// queryFromSelf reports whether the input IP is from device running ctrld.
func queryFromSelf(ip string) bool {
netIP := netip.MustParseAddr(ip)
ifaces, err := interfaces.GetList()
ifaces, err := netmon.GetInterfaceList()
if err != nil {
mainLog.Load().Warn().Err(err).Msg("could not get interfaces list")
return false

View File

@@ -5,6 +5,8 @@ import (
"net/netip"
"os/exec"
"tailscale.com/tsd"
"github.com/Control-D-Inc/ctrld/internal/dns"
"github.com/Control-D-Inc/ctrld/internal/resolvconffile"
)
@@ -36,7 +38,8 @@ func setDnsIgnoreUnusableInterface(iface *net.Interface, nameservers []string) e
// set the dns server for the provided network interface
func setDNS(iface *net.Interface, nameservers []string) error {
r, err := dns.NewOSConfigurator(logf, iface.Name)
sys := new(tsd.System)
r, err := dns.NewOSConfigurator(logf, sys.HealthTracker(), sys.ControlKnobs(), iface.Name)
if err != nil {
mainLog.Load().Error().Err(err).Msg("failed to create DNS OS configurator")
return err
@@ -60,7 +63,8 @@ func resetDnsIgnoreUnusableInterface(iface *net.Interface) error {
}
func resetDNS(iface *net.Interface) error {
r, err := dns.NewOSConfigurator(logf, iface.Name)
sys := new(tsd.System)
r, err := dns.NewOSConfigurator(logf, sys.HealthTracker(), sys.ControlKnobs(), iface.Name)
if err != nil {
mainLog.Load().Error().Err(err).Msg("failed to create DNS OS configurator")
return err

View File

@@ -14,6 +14,8 @@ import (
"syscall"
"time"
"tailscale.com/tsd"
"github.com/insomniacslk/dhcp/dhcpv4/nclient4"
"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/insomniacslk/dhcp/dhcpv6/client6"
@@ -54,7 +56,8 @@ func setDnsIgnoreUnusableInterface(iface *net.Interface, nameservers []string) e
}
func setDNS(iface *net.Interface, nameservers []string) error {
r, err := dns.NewOSConfigurator(logf, iface.Name)
sys := new(tsd.System)
r, err := dns.NewOSConfigurator(logf, sys.HealthTracker(), sys.ControlKnobs(), iface.Name)
if err != nil {
mainLog.Load().Error().Err(err).Msg("failed to create DNS OS configurator")
return err
@@ -136,7 +139,8 @@ func resetDNS(iface *net.Interface) (err error) {
if exe, _ := exec.LookPath("/lib/systemd/systemd-networkd"); exe != "" {
_ = exec.Command("systemctl", "start", "systemd-networkd").Run()
}
if r, oerr := dns.NewOSConfigurator(logf, iface.Name); oerr == nil {
sys := new(tsd.System)
if r, oerr := dns.NewOSConfigurator(logf, sys.HealthTracker(), sys.ControlKnobs(), iface.Name); oerr == nil {
_ = r.SetDNS(dns.OSConfig{})
if err := r.Close(); err != nil {
mainLog.Load().Error().Err(err).Msg("failed to rollback DNS setting")

View File

@@ -21,10 +21,11 @@ import (
"syscall"
"time"
"tailscale.com/net/netmon"
"github.com/kardianos/service"
"github.com/rs/zerolog"
"github.com/spf13/viper"
"tailscale.com/net/interfaces"
"tailscale.com/net/tsaddr"
"github.com/Control-D-Inc/ctrld"
@@ -834,7 +835,7 @@ func ifaceFirstPrivateIP(iface *net.Interface) string {
// defaultRouteIP returns private IP string of the default route if present, prefer IPv4 over IPv6.
func defaultRouteIP() string {
dr, err := interfaces.DefaultRoute()
dr, err := netmon.DefaultRoute()
if err != nil {
return ""
}
@@ -854,7 +855,7 @@ func defaultRouteIP() string {
// There could be multiple LAN interfaces with the same Mac address, so we find all private
// IPs then using the smallest one.
var addrs []netip.Addr
interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) {
if i.Name == drNetIface.Name {
return
}
@@ -894,7 +895,7 @@ func canBeLocalUpstream(addr string) bool {
// log message when error happens.
func withEachPhysicalInterfaces(excludeIfaceName, context string, f func(i *net.Interface) error) {
validIfacesMap := validInterfacesMap()
interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) {
// Skip loopback/virtual interface.
if i.IsLoopback() || len(i.HardwareAddr) == 0 {
return

View File

@@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"strings"
"tailscale.com/tsd"
"github.com/kardianos/service"
@@ -14,7 +15,8 @@ import (
)
func init() {
if r, err := dns.NewOSConfigurator(func(format string, args ...any) {}, "lo"); err == nil {
sys := new(tsd.System)
if r, err := dns.NewOSConfigurator(func(format string, args ...any) {}, sys.HealthTracker(), sys.ControlKnobs(), "lo"); err == nil {
useSystemdResolved = r.Mode() == "systemd-resolved"
}
// Disable quic-go's ECN support by default, see https://github.com/quic-go/quic-go/issues/3911

View File

@@ -6,6 +6,7 @@ import (
"net"
"net/netip"
"tailscale.com/tsd"
"tailscale.com/util/dnsname"
"github.com/Control-D-Inc/ctrld/internal/dns"
@@ -13,7 +14,8 @@ import (
// setResolvConf sets the content of resolv.conf file using the given nameservers list.
func setResolvConf(iface *net.Interface, ns []netip.Addr) error {
r, err := dns.NewOSConfigurator(func(format string, args ...any) {}, "lo") // interface name does not matter.
sys := new(tsd.System)
r, err := dns.NewOSConfigurator(func(format string, args ...any) {}, sys.HealthTracker(), sys.ControlKnobs(), "lo") // interface name does not matter.
if err != nil {
return err
}
@@ -27,7 +29,8 @@ func setResolvConf(iface *net.Interface, ns []netip.Addr) error {
// shouldWatchResolvconf reports whether ctrld should watch changes to resolv.conf file with given OS configurator.
func shouldWatchResolvconf() bool {
r, err := dns.NewOSConfigurator(func(format string, args ...any) {}, "lo") // interface name does not matter.
sys := new(tsd.System)
r, err := dns.NewOSConfigurator(func(format string, args ...any) {}, sys.HealthTracker(), sys.ControlKnobs(), "lo") // interface name does not matter.
if err != nil {
return false
}

104
go.mod
View File

@@ -1,95 +1,125 @@
module github.com/Control-D-Inc/ctrld
go 1.21
go 1.23
toolchain go1.23.1
require (
github.com/Masterminds/semver v1.5.0
github.com/coreos/go-systemd/v22 v22.5.0
github.com/cuonglm/osinfo v0.0.0-20230921071424-e0e1b1e0bbbf
github.com/frankban/quicktest v1.14.5
github.com/fsnotify/fsnotify v1.6.0
github.com/frankban/quicktest v1.14.6
github.com/fsnotify/fsnotify v1.7.0
github.com/go-playground/validator/v10 v10.11.1
github.com/godbus/dbus/v5 v5.1.0
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466
github.com/hashicorp/golang-lru/v2 v2.0.1
github.com/illarion/gonotify v1.0.1
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16
github.com/illarion/gonotify/v2 v2.0.3
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
github.com/jaytaylor/go-hostsfile v0.0.0-20220426042432-61485ac1fa6c
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.1
github.com/mdlayher/ndp v1.0.1
github.com/miekg/dns v1.1.55
github.com/miekg/dns v1.1.58
github.com/minio/selfupdate v0.6.0
github.com/olekukonko/tablewriter v0.0.5
github.com/pelletier/go-toml/v2 v2.0.8
github.com/prometheus/client_golang v1.15.1
github.com/prometheus/client_model v0.4.0
github.com/prometheus/client_golang v1.19.1
github.com/prometheus/client_model v0.5.0
github.com/prometheus/prom2json v1.3.3
github.com/quic-go/quic-go v0.42.0
github.com/rs/zerolog v1.28.0
github.com/spf13/cobra v1.7.0
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.16.0
github.com/stretchr/testify v1.8.3
github.com/stretchr/testify v1.9.0
github.com/vishvananda/netlink v1.2.1-beta.2
golang.org/x/net v0.23.0
golang.org/x/sync v0.2.0
golang.org/x/sys v0.18.0
golang.org/x/net v0.27.0
golang.org/x/sync v0.7.0
golang.org/x/sys v0.22.0
golang.zx2c4.com/wireguard/windows v0.5.3
tailscale.com v1.44.0
tailscale.com v1.74.0
)
require (
aead.dev/minisign v0.2.0 // indirect
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/akutz/memconn v0.1.0 // indirect
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
github.com/gaissmai/bart v0.11.1 // indirect
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 // indirect
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jsimonetti/rtnetlink v1.3.2 // indirect
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/mdlayher/packet v1.1.2 // indirect
github.com/mdlayher/socket v0.5.0 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 // indirect
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 // indirect
github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc // indirect
github.com/tcnksm/go-httpstat v0.2.0 // indirect
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.uber.org/mock v0.4.0 // indirect
go4.org/mem v0.0.0-20220726221520-4f986261bf13 // indirect
golang.org/x/crypto v0.21.0 // indirect
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
golang.org/x/mod v0.11.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.9.1 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.23.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 // indirect
)
replace github.com/mr-karan/doggo => github.com/Windscribe/doggo v0.0.0-20220919152748-2c118fc391f8

227
go.sum
View File

@@ -38,52 +38,77 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
filippo.io/mkcert v1.4.4 h1:8eVbbwfVlaqUM7OwuftKc2nuYOoTDQWqsoXmzoXZdbc=
filippo.io/mkcert v1.4.4/go.mod h1:VyvOchVuAye3BoUsPUOOofKygVwLV2KQMVFJNRq+1dA=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Windscribe/zerolog v0.0.0-20230503170159-e6aa153233be h1:qBKVRi7Mom5heOkyZ+NCIu9HZBiNCsRqrRe5t9pooik=
github.com/Windscribe/zerolog v0.0.0-20230503170159-e6aa153233be/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE=
github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6 h1:8h5+bWd7R6AYUslN6c6iuZWTKsKxUFDlpnmilO6R2n0=
github.com/coreos/go-iptables v0.7.1-0.20240112124308-65c67c9f46e6/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cuonglm/osinfo v0.0.0-20230921071424-e0e1b1e0bbbf h1:40DHYsri+d1bnroFDU2FQAeq68f3kAlOzlQ93kCf26Q=
github.com/cuonglm/osinfo v0.0.0-20230921071424-e0e1b1e0bbbf/go.mod h1:G45410zMgmnSjLVKCq4f6GpbYAzoP2plX9rPwgx6C24=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbwwpmHn1J5i43Y0uZP97GqasGCzSRJk=
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
github.com/dsnet/try v0.0.3 h1:ptR59SsrcFUYbT/FhAbKTV6iLkeD6O18qfIWRml2fqI=
github.com/dsnet/try v0.0.3/go.mod h1:WBM8tRpUmnXXhY1U6/S8dt6UWdHTQ7y8A5YSkRCkq40=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/gaissmai/bart v0.11.1 h1:5Uv5XwsaFBRo4E5VBcb9TzY8B7zxFf+U7isDxqOrRfc=
github.com/gaissmai/bart v0.11.1/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0 h1:ymLjT4f35nQbASLnvxEde4XOBL+Sn7rFuV+FOJqkljg=
github.com/go-json-experiment/json v0.0.0-20231102232822-2e55bd4e08b0/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
@@ -95,12 +120,14 @@ github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -122,11 +149,12 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -136,12 +164,14 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI=
github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
@@ -152,8 +182,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -165,28 +195,32 @@ github.com/hashicorp/golang-lru/v2 v2.0.1 h1:5pv5N1lT1fjLg2VQ5KWc7kmucp2x/kvFOnx
github.com/hashicorp/golang-lru/v2 v2.0.1/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/illarion/gonotify v1.0.1 h1:F1d+0Fgbq/sDWjj/r66ekjDG+IDeecQKUFH4wNwsoio=
github.com/illarion/gonotify v1.0.1/go.mod h1:zt5pmDofZpU1f8aqlK0+95eQhoEAn/d4G4B/FjVW4jE=
github.com/illarion/gonotify/v2 v2.0.3 h1:B6+SKPo/0Sw8cRJh1aLzNEeNVFfzE3c6N+o+vyxM+9A=
github.com/illarion/gonotify/v2 v2.0.3/go.mod h1:38oIJTgFqupkEydkkClkbL6i5lXV/bxdH9do5TALPEE=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E=
github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
github.com/jaytaylor/go-hostsfile v0.0.0-20220426042432-61485ac1fa6c h1:kbTQ8oGf+BVFvt/fM+ECI+NbZDCqoi0vtZTfB2p2hrI=
github.com/jaytaylor/go-hostsfile v0.0.0-20220426042432-61485ac1fa6c/go.mod h1:k6+89xKz7BSMJ+DzIerBdtpEUeTlBMugO/hcVSzahog=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
github.com/jsimonetti/rtnetlink v1.3.2 h1:dcn0uWkfxycEEyNy0IGfx3GrhQ38LH7odjxAghimsVI=
github.com/jsimonetti/rtnetlink v1.3.2/go.mod h1:BBu4jZCpTjP6Gk0/wfrO8qcqymnN3g0hoFqObRmUo6U=
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kardianos/service v1.2.1 h1:AYndMsehS+ywIS6RB9KOlcXzteWUzxgMgBymJD7+BYk=
github.com/kardianos/service v1.2.1/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -206,28 +240,29 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 h1:lez6TS6aAau+8wXUP3G9I3TGlmPFEq2CTxBaRqY6AGE=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw=
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
github.com/mdlayher/ndp v1.0.1 h1:+yAD79/BWyFlvAoeG5ncPS0ItlHP/eVbH7bQ6/+LVA4=
github.com/mdlayher/ndp v1.0.1/go.mod h1:rf3wKaWhAYJEXFKpgF8kQ2AxypxVbfNcZbqoAo6fVzk=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065 h1:aFkJ6lx4FPip+S+Uw4aTegFMct9shDvP+79PsSxpm3w=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
github.com/minio/selfupdate v0.6.0 h1:i76PgT0K5xO9+hjzKcacQtO7+MjJ4JKA8Ak8XQ9DDwU=
github.com/minio/selfupdate v0.6.0/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
@@ -239,22 +274,23 @@ github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+q
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/prometheus/prom2json v1.3.3 h1:IYfSMiZ7sSOfliBoo89PcufjWO4eAR0gznGcETyaUgo=
github.com/prometheus/prom2json v1.3.3/go.mod h1:Pv4yIPktEkK7btWsrUTWDDDrnpUrAELaOCj+oFwlgmc=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
@@ -268,16 +304,16 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@@ -286,8 +322,9 @@ github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -295,17 +332,32 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05 h1:4chzWmimtJPxRs2O36yuGRW3f9SYV+bMTTvMBI0EKio=
github.com/tailscale/goupnp v1.0.1-0.20210804011211-c64d0f06ea05/go.mod h1:PdCqy9JzfWMJf1H5UJW2ip33/d4YkoKN0r67yKH1mG8=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc h1:cezaQN9pvKVaw56Ma5qr/G646uKIYP0yQf+OyWN/okc=
github.com/tailscale/wireguard-go v0.0.0-20240905161824-799c1978fafc/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e h1:BA9O3BmlTmpjbvajAwzWx4Wo2TRVdpPXZEeemGQcajw=
github.com/u-root/uio v0.0.0-20240118234441-a3c409a6018e/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -320,6 +372,8 @@ go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go4.org/mem v0.0.0-20220726221520-4f986261bf13 h1:CbZeCBZ0aZj8EfVgnqQcYZgf0lpZ3H9rmp5nkDTAst8=
go4.org/mem v0.0.0-20220726221520-4f986261bf13/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -330,8 +384,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -342,8 +396,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 h1:5llv2sWeaMSnA3w2kS57ouQQ4pudlXrR0dCgw51QK9o=
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -367,15 +421,14 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
@@ -402,8 +455,8 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -423,16 +476,15 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -472,13 +524,16 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -487,8 +542,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -541,12 +596,14 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
golang.zx2c4.com/wireguard/windows v0.5.3/go.mod h1:9TEe8TJmtwyQebdFwAkEWOPr3prrtqm+REGFifP60hI=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@@ -637,8 +694,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -653,6 +708,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987 h1:TU8z2Lh3Bbq77w0t1eG8yRlLcNHzZu3x6mhoH2Mk0c8=
gvisor.dev/gvisor v0.0.0-20240722211153-64c016c92987/go.mod h1:sxc3Uvk/vHcd3tj7/DHVBoR5wvWT/MmRq2pj7HRJnwU=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -660,8 +717,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
tailscale.com v1.44.0 h1:MPos9n30kJvdyfL52045gVFyNg93K+bwgDsr8gqKq2o=
tailscale.com v1.44.0/go.mod h1:+iYwTdeHyVJuNDu42Zafwihq1Uqfh+pW7pRaY1GD328=
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
tailscale.com v1.74.0 h1:J+vRN9o3D4wCqZBiwvDg9kZpQag2mG4Xz5RXNpmV3KE=
tailscale.com v1.74.0/go.mod h1:3iACpCONQ4lauDXvwfoGlwNCpfbVxjdc2j6G9EuFOW8=

View File

@@ -13,8 +13,9 @@ import (
"strings"
"sync"
"tailscale.com/net/netmon"
"github.com/fsnotify/fsnotify"
"tailscale.com/net/interfaces"
"tailscale.com/util/lineread"
"github.com/Control-D-Inc/ctrld"
@@ -356,7 +357,7 @@ func (d *dhcp) addSelf() {
d.ip2name.Store(ipV4Loopback, hostname)
d.ip2name.Store(ipv6Loopback, hostname)
found := false
interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) {
mac := i.HardwareAddr.String()
// Skip loopback interfaces, info was stored above.
if mac == "" {

View File

@@ -1,2 +1,2 @@
This is a fork of https://pkg.go.dev/tailscale.com@v1.34.2/net/dns with modification
This is a fork of https://pkg.go.dev/tailscale.com@v1.74.0/net/dns with modification
to fit ctrld use case.

View File

@@ -1,12 +1,12 @@
// 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux || freebsd || openbsd
package dns
import (
"bufio"
"bytes"
_ "embed"
"fmt"
@@ -33,7 +33,7 @@ var workaroundScript []byte
// resolvconf implementations encourage adding a suffix roughly
// indicating where the config came from, and "inet" is the "none of
// the above" value (rather than, say, "ppp" or "dhcp").
const resolvconfConfigName = "ctrld.inet"
const resolvconfConfigName = "tun-ctrld.inet"
// resolvconfLibcHookPath is the directory containing libc update
// scripts, which are run by Debian resolvconf when /etc/resolv.conf
@@ -53,8 +53,6 @@ type resolvconfManager struct {
scriptInstalled bool // libc update script has been installed
}
var _ OSConfigurator = (*resolvconfManager)(nil)
func newDebianResolvconfManager(logf logger.Logf) (*resolvconfManager, error) {
ret := &resolvconfManager{
logf: logf,
@@ -135,6 +133,43 @@ func (m *resolvconfManager) SetDNS(config OSConfig) error {
return nil
}
func (m *resolvconfManager) SupportsSplitDNS() bool {
return false
}
func (m *resolvconfManager) GetBaseConfig() (OSConfig, error) {
var bs bytes.Buffer
cmd := exec.Command(m.listRecordsPath)
// list-records assumes it's being run with CWD set to the
// interfaces runtime dir, and returns nonsense otherwise.
cmd.Dir = m.interfacesDir
cmd.Stdout = &bs
if err := cmd.Run(); err != nil {
return OSConfig{}, err
}
var conf bytes.Buffer
sc := bufio.NewScanner(&bs)
for sc.Scan() {
if sc.Text() == resolvconfConfigName {
continue
}
bs, err := os.ReadFile(filepath.Join(m.interfacesDir, sc.Text()))
if err != nil {
if os.IsNotExist(err) {
// Probably raced with a deletion, that's okay.
continue
}
return OSConfig{}, err
}
conf.Write(bs)
conf.WriteByte('\n')
}
return readResolv(&conf)
}
func (m *resolvconfManager) Close() error {
if err := m.deleteCtrldConfig(); err != nil {
return err

View File

@@ -1,9 +1,5 @@
// 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.
//lint:file-ignore U1000 Ignore, this file is forked from upstream code.
//lint:file-ignore ST1005 Ignore, this file is forked from upstream code.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package dns
@@ -20,11 +16,13 @@ import (
"os/exec"
"path/filepath"
"runtime"
"slices"
"strings"
"sync"
"time"
"tailscale.com/health"
"tailscale.com/net/tsaddr"
"tailscale.com/types/logger"
"tailscale.com/util/dnsname"
"tailscale.com/version/distro"
@@ -32,11 +30,6 @@ import (
"github.com/Control-D-Inc/ctrld/internal/dns/resolvconffile"
)
const (
backupConf = "/etc/resolv.pre-ctrld-backup.conf"
resolvConf = "/etc/resolv.conf"
)
// writeResolvConf writes DNS configuration in resolv.conf format to the given writer.
func writeResolvConf(w io.Writer, servers []netip.Addr, domains []dnsname.FQDN) error {
c := &resolvconffile.Config{
@@ -60,6 +53,8 @@ func readResolv(r io.Reader) (OSConfig, error) {
// resolvOwner returns the apparent owner of the resolv.conf
// configuration in bs - one of "resolvconf", "systemd-resolved" or
// "NetworkManager", or "" if no known owner was found.
//
//lint:ignore U1000 used in linux and freebsd code
func resolvOwner(bs []byte) string {
likely := ""
b := bytes.NewBuffer(bs)
@@ -123,8 +118,9 @@ func restartResolved() error {
// The caller must call Down before program shutdown
// or as cleanup if the program terminates unexpectedly.
type directManager struct {
logf logger.Logf
fs wholeFileFS
logf logger.Logf
health *health.Tracker
fs wholeFileFS
// renameBroken is set if fs.Rename to or from /etc/resolv.conf
// fails. This can happen in some container runtimes, where
// /etc/resolv.conf is bind-mounted from outside the container,
@@ -140,19 +136,22 @@ type directManager struct {
ctx context.Context // valid until Close
ctxClose context.CancelFunc // closes ctx
mu sync.Mutex
wantResolvConf []byte // if non-nil, what we expect /etc/resolv.conf to contain
mu sync.Mutex
wantResolvConf []byte // if non-nil, what we expect /etc/resolv.conf to contain
//lint:ignore U1000 used in direct_linux.go
lastWarnContents []byte // last resolv.conf contents that we warned about
}
func newDirectManager(logf logger.Logf) *directManager {
return newDirectManagerOnFS(logf, directFS{})
//lint:ignore U1000 used in manager_{freebsd,openbsd}.go
func newDirectManager(logf logger.Logf, health *health.Tracker) *directManager {
return newDirectManagerOnFS(logf, health, directFS{})
}
func newDirectManagerOnFS(logf logger.Logf, fs wholeFileFS) *directManager {
func newDirectManagerOnFS(logf logger.Logf, health *health.Tracker, fs wholeFileFS) *directManager {
ctx, cancel := context.WithCancel(context.Background())
m := &directManager{
logf: logf,
health: health,
fs: fs,
ctx: ctx,
ctxClose: cancel,
@@ -193,13 +192,13 @@ func (m *directManager) ownedByCtrld() (bool, error) {
}
// backupConfig creates or updates a backup of /etc/resolv.conf, if
// resolv.conf does not currently contain a Tailscale-managed config.
// resolv.conf does not currently contain a ctrld-managed config.
func (m *directManager) backupConfig() error {
if _, err := m.fs.Stat(resolvConf); err != nil {
if os.IsNotExist(err) {
// No resolv.conf, nothing to back up. Also get rid of any
// existing backup file, to avoid restoring something old.
_ = m.fs.Remove(backupConf)
m.fs.Remove(backupConf)
return nil
}
return err
@@ -237,7 +236,7 @@ func (m *directManager) restoreBackup() (restored bool, err error) {
if resolvConfExists && !owned {
// There's already a non-ctrld config in place, get rid of
// our backup.
_ = m.fs.Remove(backupConf)
m.fs.Remove(backupConf)
return false, nil
}
@@ -278,6 +277,14 @@ func (m *directManager) rename(old, new string) error {
return fmt.Errorf("writing to %q in rename of %q: %w", new, old, err)
}
// Explicitly set the permissions on the new file. This ensures that
// if we have a umask set which prevents creating world-readable files,
// the file will still have the correct permissions once it's renamed
// into place. See #12609.
if err := m.fs.Chmod(new, 0644); err != nil {
return fmt.Errorf("chmod %q in rename of %q: %w", new, old, err)
}
if err := m.fs.Remove(old); err != nil {
err2 := m.fs.Truncate(old)
if err2 != nil {
@@ -298,53 +305,6 @@ func (m *directManager) setWant(want []byte) {
m.wantResolvConf = want
}
var warnTrample = health.NewWarnable()
// checkForFileTrample checks whether /etc/resolv.conf has been trampled
// by another program on the system. (e.g. a DHCP client)
func (m *directManager) checkForFileTrample() {
m.mu.Lock()
want := m.wantResolvConf
lastWarn := m.lastWarnContents
m.mu.Unlock()
if want == nil {
return
}
cur, err := m.fs.ReadFile(resolvConf)
if err != nil {
m.logf("trample: read error: %v", err)
return
}
if bytes.Equal(cur, want) {
warnTrample.Set(nil)
if lastWarn != nil {
m.mu.Lock()
m.lastWarnContents = nil
m.mu.Unlock()
m.logf("trample: resolv.conf again matches expected content")
}
return
}
if bytes.Equal(cur, lastWarn) {
// We already logged about this, so not worth doing it again.
return
}
m.mu.Lock()
m.lastWarnContents = cur
m.mu.Unlock()
show := cur
if len(show) > 1024 {
show = show[:1024]
}
m.logf("trample: resolv.conf changed from what we expected. did some other program interfere? current contents: %q", show)
//lint:ignore ST1005 This error is for human.
warnTrample.Set(errors.New("Linux DNS config not ideal. /etc/resolv.conf overwritten. See https://tailscale.com/s/dns-fight"))
}
func (m *directManager) SetDNS(config OSConfig) (err error) {
defer func() {
if err != nil && errors.Is(err, fs.ErrPermission) && runtime.GOOS == "linux" &&
@@ -370,7 +330,7 @@ func (m *directManager) SetDNS(config OSConfig) (err error) {
}
buf := new(bytes.Buffer)
_ = writeResolvConf(buf, config.Nameservers, config.SearchDomains)
writeResolvConf(buf, config.Nameservers, config.SearchDomains)
if err := m.atomicWriteFile(m.fs, resolvConf, buf.Bytes(), 0644); err != nil {
return err
}
@@ -411,12 +371,57 @@ func (m *directManager) SetDNS(config OSConfig) (err error) {
return nil
}
func (m *directManager) SupportsSplitDNS() bool {
return false
}
func (m *directManager) GetBaseConfig() (OSConfig, error) {
owned, err := m.ownedByCtrld()
if err != nil {
return OSConfig{}, err
}
fileToRead := resolvConf
if owned {
fileToRead = backupConf
}
oscfg, err := m.readResolvFile(fileToRead)
if err != nil {
return OSConfig{}, err
}
// On some systems, the backup configuration file is actually a
// symbolic link to something owned by another DNS service (commonly,
// resolved). Thus, it can be updated out from underneath us to contain
// the Tailscale service IP, which results in an infinite loop of us
// trying to send traffic to resolved, which sends back to us, and so
// on. To solve this, drop the Tailscale service IP from the base
// configuration; we do this in all situations since there's
// essentially no world where we want to forward to ourselves.
//
// See: https://github.com/tailscale/tailscale/issues/7816
var removed bool
oscfg.Nameservers = slices.DeleteFunc(oscfg.Nameservers, func(ip netip.Addr) bool {
if ip == tsaddr.TailscaleServiceIP() || ip == tsaddr.TailscaleServiceIPv6() {
removed = true
return true
}
return false
})
if removed {
m.logf("[v1] dropped Tailscale IP from base config that was a symlink")
}
return oscfg, nil
}
func (m *directManager) Close() error {
// We used to keep a file for the ctrld config and symlinked
m.ctxClose()
// We used to keep a file for the tailscale config and symlinked
// to it, but then we stopped because /etc/resolv.conf being a
// symlink to surprising places breaks snaps and other sandboxing
// things. Clean it up if it's still there.
_ = m.fs.Remove("/etc/resolv.ctrld.conf")
m.fs.Remove("/etc/resolv.ctrld.conf")
if _, err := m.fs.Stat(backupConf); err != nil {
if os.IsNotExist(err) {
@@ -436,9 +441,9 @@ func (m *directManager) Close() error {
resolvConfExists := !os.IsNotExist(err)
if resolvConfExists && !owned {
// There's already a non-ctrld config in place, get rid of
// There's already a non-tailscale config in place, get rid of
// our backup.
_ = m.fs.Remove(backupConf)
m.fs.Remove(backupConf)
return nil
}
@@ -475,6 +480,14 @@ func (m *directManager) atomicWriteFile(fs wholeFileFS, filename string, data []
if err := fs.WriteFile(tmpName, data, perm); err != nil {
return fmt.Errorf("atomicWriteFile: %w", err)
}
// Explicitly set the permissions on the temporary file before renaming
// it. This ensures that if we have a umask set which prevents creating
// world-readable files, the file will still have the correct
// permissions once it's renamed into place. See #12609.
if err := fs.Chmod(tmpName, perm); err != nil {
return fmt.Errorf("atomicWriteFile: Chmod: %w", err)
}
return m.rename(tmpName, filename)
}
@@ -483,10 +496,11 @@ func (m *directManager) atomicWriteFile(fs wholeFileFS, filename string, data []
//
// All name parameters are absolute paths.
type wholeFileFS interface {
Stat(name string) (isRegular bool, err error)
Rename(oldName, newName string) error
Remove(name string) error
Chmod(name string, mode os.FileMode) error
ReadFile(name string) ([]byte, error)
Remove(name string) error
Rename(oldName, newName string) error
Stat(name string) (isRegular bool, err error)
Truncate(name string) error
WriteFile(name string, contents []byte, perm os.FileMode) error
}
@@ -510,6 +524,10 @@ func (fs directFS) Stat(name string) (isRegular bool, err error) {
return fi.Mode().IsRegular(), nil
}
func (fs directFS) Chmod(name string, mode os.FileMode) error {
return os.Chmod(fs.path(name), mode)
}
func (fs directFS) Rename(oldName, newName string) error {
return os.Rename(fs.path(oldName), fs.path(newName))
}

View File

@@ -1,26 +1,26 @@
// Copyright (c) 2022 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package dns
import (
"bytes"
"context"
"github.com/illarion/gonotify"
"github.com/illarion/gonotify/v2"
"tailscale.com/health"
)
func (m *directManager) runFileWatcher() {
in, err := gonotify.NewInotify()
ctx, cancel := context.WithCancel(m.ctx)
defer cancel()
in, err := gonotify.NewInotify(ctx)
if err != nil {
// Oh well, we tried. This is all best effort for now, to
// surface warnings to users.
m.logf("dns: inotify new: %v", err)
return
}
ctx, cancel := context.WithCancel(m.ctx)
defer cancel()
go m.closeInotifyOnDone(ctx, in)
const events = gonotify.IN_ATTRIB |
gonotify.IN_CLOSE_WRITE |
@@ -56,7 +56,53 @@ func (m *directManager) runFileWatcher() {
}
}
func (m *directManager) closeInotifyOnDone(ctx context.Context, in *gonotify.Inotify) {
<-ctx.Done()
_ = in.Close()
var resolvTrampleWarnable = health.Register(&health.Warnable{
Code: "ctrld-resolv-conf-overwritten",
Severity: health.SeverityMedium,
Title: "Linux DNS configuration issue",
Text: health.StaticMessage("Linux DNS config not ideal. /etc/resolv.conf overwritten. See https://tailscale.com/s/dns-fight"),
})
// checkForFileTrample checks whether /etc/resolv.conf has been trampled
// by another program on the system. (e.g. a DHCP client)
func (m *directManager) checkForFileTrample() {
m.mu.Lock()
want := m.wantResolvConf
lastWarn := m.lastWarnContents
m.mu.Unlock()
if want == nil {
return
}
cur, err := m.fs.ReadFile(resolvConf)
if err != nil {
m.logf("trample: read error: %v", err)
return
}
if bytes.Equal(cur, want) {
m.health.SetHealthy(resolvTrampleWarnable)
if lastWarn != nil {
m.mu.Lock()
m.lastWarnContents = nil
m.mu.Unlock()
m.logf("trample: resolv.conf again matches expected content")
}
return
}
if bytes.Equal(cur, lastWarn) {
// We already logged about this, so not worth doing it again.
return
}
m.mu.Lock()
m.lastWarnContents = cur
m.mu.Unlock()
show := cur
if len(show) > 1024 {
show = show[:1024]
}
m.logf("trample: resolv.conf changed from what we expected. did some other program interfere? current contents: %q", show)
m.health.SetUnhealthy(resolvTrampleWarnable, nil)
}

View File

@@ -1,6 +1,5 @@
// Copyright (c) 2022 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !linux

View File

@@ -1,10 +1,10 @@
// Copyright (c) 2021 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package dns
import (
"context"
"errors"
"fmt"
"io/fs"
@@ -79,7 +79,10 @@ func testDirect(t *testing.T, fs wholeFileFS) {
}
}
m := directManager{logf: t.Logf, fs: fs}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
m := directManager{logf: t.Logf, fs: fs, ctx: ctx, ctxClose: cancel}
if err := m.SetDNS(OSConfig{
Nameservers: []netip.Addr{netip.MustParseAddr("8.8.8.8"), netip.MustParseAddr("8.8.4.4")},
SearchDomains: []dnsname.FQDN{"controld.com."},
@@ -121,7 +124,7 @@ type brokenRemoveFS struct {
directFS
}
func (b brokenRemoveFS) Rename(_, _ string) error {
func (b brokenRemoveFS) Rename(old, new string) error {
return errors.New("nyaaah I'm a silly container!")
}
@@ -178,12 +181,12 @@ func TestReadResolve(t *testing.T) {
SearchDomains: []dnsname.FQDN{"controld.com."},
},
},
{in: `search controld.com # typo`,
{in: `search controld.com # comment`,
want: OSConfig{
SearchDomains: []dnsname.FQDN{"controld.com."},
},
},
{in: `searchcontrold.com`, wantErr: true},
{in: `searchctrld.com`, wantErr: true},
{in: `search`, wantErr: true},
}

View File

@@ -1,6 +1,5 @@
// 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package dns
@@ -8,13 +7,18 @@ import (
"fmt"
"os"
"tailscale.com/control/controlknobs"
"tailscale.com/health"
"tailscale.com/types/logger"
)
func NewOSConfigurator(logf logger.Logf, _ string) (OSConfigurator, error) {
// NewOSConfigurator creates a new OS configurator.
//
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ *controlknobs.Knobs, _ string) (OSConfigurator, error) {
bs, err := os.ReadFile("/etc/resolv.conf")
if os.IsNotExist(err) {
return newDirectManager(logf), nil
return newDirectManager(logf, health), nil
}
if err != nil {
return nil, fmt.Errorf("reading /etc/resolv.conf: %w", err)
@@ -24,16 +28,16 @@ func NewOSConfigurator(logf logger.Logf, _ string) (OSConfigurator, error) {
case "resolvconf":
switch resolvconfStyle() {
case "":
return newDirectManager(logf), nil
return newDirectManager(logf, health), nil
case "debian":
return newDebianResolvconfManager(logf)
case "openresolv":
return newOpenresolvManager()
return newOpenresolvManager(logf)
default:
logf("[unexpected] got unknown flavor of resolvconf %q, falling back to direct manager", resolvconfStyle())
return newDirectManager(logf), nil
return newDirectManager(logf, health), nil
}
default:
return newDirectManager(logf), nil
return newDirectManager(logf, health), nil
}
}

View File

@@ -1,8 +1,5 @@
// 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.
//lint:file-ignore U1000 Ignore this file, it's a copy.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package dns
@@ -17,6 +14,7 @@ import (
"time"
"github.com/godbus/dbus/v5"
"tailscale.com/control/controlknobs"
"tailscale.com/health"
"tailscale.com/net/netaddr"
"tailscale.com/types/logger"
@@ -38,7 +36,10 @@ func (kv kv) String() string {
var publishOnce sync.Once
func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurator, err error) {
// NewOSConfigurator created a new OS configurator.
//
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ *controlknobs.Knobs, interfaceName string) (ret OSConfigurator, err error) {
env := newOSConfigEnv{
fs: directFS{},
dbusPing: dbusPing,
@@ -47,7 +48,7 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
nmVersionBetween: nmVersionBetween,
resolvconfStyle: resolvconfStyle,
}
mode, err := dnsMode(logf, env)
mode, err := dnsMode(logf, health, env)
if err != nil {
return nil, err
}
@@ -59,18 +60,18 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
logf("dns: using %q mode", mode)
switch mode {
case "direct":
return newDirectManagerOnFS(logf, env.fs), nil
return newDirectManagerOnFS(logf, health, env.fs), nil
case "systemd-resolved":
return newResolvedManager(logf, interfaceName)
return newResolvedManager(logf, health, interfaceName)
case "network-manager":
return newNMManager(interfaceName)
case "debian-resolvconf":
return newDebianResolvconfManager(logf)
case "openresolv":
return newOpenresolvManager()
return newOpenresolvManager(logf)
default:
logf("[unexpected] detected unknown DNS mode %q, using direct manager as last resort", mode)
return newDirectManagerOnFS(logf, env.fs), nil
return newDirectManagerOnFS(logf, health, env.fs), nil
}
}
@@ -84,7 +85,7 @@ type newOSConfigEnv struct {
resolvconfStyle func() string
}
func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
func dnsMode(logf logger.Logf, health *health.Tracker, env newOSConfigEnv) (ret string, err error) {
var debug []kv
dbg := func(k, v string) {
debug = append(debug, kv{k, v})
@@ -145,7 +146,7 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
// header, but doesn't actually point to resolved. We mustn't
// try to program resolved in that case.
// https://github.com/tailscale/tailscale/issues/2136
if err := resolvedIsActuallyResolver(bs); err != nil {
if err := resolvedIsActuallyResolver(logf, env, dbg, bs); err != nil {
logf("dns: resolvedIsActuallyResolver error: %v", err)
dbg("resolved", "not-in-use")
return "direct", nil
@@ -231,7 +232,7 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
dbg("rc", "nm")
// Sometimes, NetworkManager owns the configuration but points
// it at systemd-resolved.
if err := resolvedIsActuallyResolver(bs); err != nil {
if err := resolvedIsActuallyResolver(logf, env, dbg, bs); err != nil {
logf("dns: resolvedIsActuallyResolver error: %v", err)
dbg("resolved", "not-in-use")
// You'd think we would use newNMManager here. However, as
@@ -271,6 +272,14 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
dbg("nm-safe", "yes")
return "network-manager", nil
}
if err := env.nmIsUsingResolved(); err != nil {
// If systemd-resolved is not running at all, then we don't have any
// other choice: we take direct control of DNS.
dbg("nm-resolved", "no")
return "direct", nil
}
//lint:ignore SA1019 upstream code still use it.
health.SetDNSManagerHealth(errors.New("systemd-resolved and NetworkManager are wired together incorrectly; MagicDNS will probably not work. For more info, see https://tailscale.com/s/resolved-nm"))
dbg("nm-safe", "no")
return "systemd-resolved", nil
@@ -324,14 +333,23 @@ func nmIsUsingResolved() error {
return nil
}
// resolvedIsActuallyResolver reports whether the given resolv.conf
// bytes describe a configuration where systemd-resolved (127.0.0.53)
// is the only configured nameserver.
// resolvedIsActuallyResolver reports whether the system is using
// systemd-resolved as the resolver. There are two different ways to
// use systemd-resolved:
// - libnss_resolve, which requires adding `resolve` to the "hosts:"
// line in /etc/nsswitch.conf
// - setting the only nameserver configured in `resolv.conf` to
// systemd-resolved IP (127.0.0.53)
//
// Returns an error if the configuration is something other than
// exclusively systemd-resolved, or nil if the config is only
// systemd-resolved.
func resolvedIsActuallyResolver(bs []byte) error {
func resolvedIsActuallyResolver(logf logger.Logf, env newOSConfigEnv, dbg func(k, v string), bs []byte) error {
if err := isLibnssResolveUsed(env); err == nil {
dbg("resolved", "nss")
return nil
}
cfg, err := readResolv(bytes.NewBuffer(bs))
if err != nil {
return err
@@ -348,9 +366,34 @@ func resolvedIsActuallyResolver(bs []byte) error {
return fmt.Errorf("resolv.conf doesn't point to systemd-resolved; points to %v", cfg.Nameservers)
}
}
dbg("resolved", "file")
return nil
}
// isLibnssResolveUsed reports whether libnss_resolve is used
// for resolving names. Returns nil if it is, and an error otherwise.
func isLibnssResolveUsed(env newOSConfigEnv) error {
bs, err := env.fs.ReadFile("/etc/nsswitch.conf")
if err != nil {
return fmt.Errorf("reading /etc/resolv.conf: %w", err)
}
for _, line := range strings.Split(string(bs), "\n") {
fields := strings.Fields(line)
if len(fields) < 2 || fields[0] != "hosts:" {
continue
}
for _, module := range fields[1:] {
if module == "dns" {
return fmt.Errorf("dns with a higher priority than libnss_resolve")
}
if module == "resolve" {
return nil
}
}
}
return fmt.Errorf("libnss_resolve not used")
}
func dbusPing(name, objectPath string) error {
conn, err := dbus.SystemBus()
if err != nil {

View File

@@ -1,6 +1,5 @@
// Copyright (c) 2021 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package dns
@@ -71,7 +70,7 @@ func TestLinuxDNSMode(t *testing.T) {
{
name: "resolved_alone_without_ping",
env: env(resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53")),
wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=resolved nm=no resolv-conf-mode=error ret=systemd-resolved]",
wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=resolved resolved=file nm=no resolv-conf-mode=error ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -79,16 +78,46 @@ func TestLinuxDNSMode(t *testing.T) {
env: env(
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
name: "resolved_and_nsswitch_resolve",
env: env(
resolvDotConf("# Managed by systemd-resolved", "nameserver 1.1.1.1"),
resolvedRunning(),
nsswitchDotConf("hosts: files resolve [!UNAVAIL=return] dns"),
),
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=nss nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
name: "resolved_and_nsswitch_dns",
env: env(
resolvDotConf("# Managed by systemd-resolved", "nameserver 1.1.1.1"),
resolvedRunning(),
nsswitchDotConf("hosts: files dns resolve [!UNAVAIL=return]"),
),
wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [1.1.1.1]\ndns: [resolved-ping=yes rc=resolved resolved=not-in-use ret=direct]",
want: "direct",
},
{
name: "resolved_and_nsswitch_none",
env: env(
resolvDotConf("# Managed by systemd-resolved", "nameserver 1.1.1.1"),
resolvedRunning(),
nsswitchDotConf("hosts:"),
),
wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [1.1.1.1]\ndns: [resolved-ping=yes rc=resolved resolved=not-in-use ret=direct]",
want: "direct",
},
{
name: "resolved_and_networkmanager_not_using_resolved",
env: env(
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning(),
nmRunning("1.2.3", false)),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=yes nm-resolved=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -97,7 +126,7 @@ func TestLinuxDNSMode(t *testing.T) {
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning(),
nmRunning("1.26.2", true)),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=yes ret=network-manager]",
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=yes nm-resolved=yes nm-safe=yes ret=network-manager]",
want: "network-manager",
},
{
@@ -106,7 +135,7 @@ func TestLinuxDNSMode(t *testing.T) {
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning(),
nmRunning("1.27.0", true)),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=yes nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -115,7 +144,7 @@ func TestLinuxDNSMode(t *testing.T) {
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning(),
nmRunning("1.22.0", true)),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=yes nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
// Regression tests for extreme corner cases below.
@@ -141,7 +170,7 @@ func TestLinuxDNSMode(t *testing.T) {
"nameserver 127.0.0.53",
"nameserver 127.0.0.53"),
resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -156,7 +185,7 @@ func TestLinuxDNSMode(t *testing.T) {
"# run \"systemd-resolve --status\" to see details about the actual nameservers.",
"nameserver 127.0.0.53"),
resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -171,7 +200,7 @@ func TestLinuxDNSMode(t *testing.T) {
"# 127.0.0.53 is the systemd-resolved stub resolver.",
"# run \"systemd-resolve --status\" to see details about the actual nameservers.",
"nameserver 127.0.0.53")),
wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=resolved nm=no resolv-conf-mode=error ret=systemd-resolved]",
wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=resolved resolved=file nm=no resolv-conf-mode=error ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -183,7 +212,7 @@ func TestLinuxDNSMode(t *testing.T) {
"options edns0 trust-ad"),
resolvedRunning(),
nmRunning("1.32.12", true)),
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=nm resolved=file nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -194,7 +223,7 @@ func TestLinuxDNSMode(t *testing.T) {
"nameserver 127.0.0.53",
"options edns0 trust-ad"),
nmRunning("1.32.12", true)),
wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=nm nm-resolved=yes nm-safe=no resolv-conf-mode=error ret=systemd-resolved]",
wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=nm resolved=file nm-resolved=yes nm-safe=no resolv-conf-mode=error ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -206,7 +235,7 @@ func TestLinuxDNSMode(t *testing.T) {
"options edns0 trust-ad"),
resolvedRunning(),
nmRunning("1.26.3", true)),
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm-safe=yes ret=network-manager]",
wantLog: "dns: [resolved-ping=yes rc=nm resolved=file nm-resolved=yes nm-safe=yes ret=network-manager]",
want: "network-manager",
},
{
@@ -217,7 +246,7 @@ func TestLinuxDNSMode(t *testing.T) {
"nameserver 127.0.0.53",
"options edns0 trust-ad"),
resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=nm resolved=file nm-resolved=yes nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -228,7 +257,7 @@ func TestLinuxDNSMode(t *testing.T) {
"search lan",
"nameserver 127.0.0.53"),
resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=nm resolved=file nm-resolved=yes nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
@@ -238,14 +267,26 @@ func TestLinuxDNSMode(t *testing.T) {
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedDbusProperty(),
)),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
wantLog: "dns: [resolved-ping=yes rc=resolved resolved=file nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved",
},
{
// regression test for https://github.com/tailscale/tailscale/issues/9687
name: "networkmanager_endeavouros",
env: env(resolvDotConf(
"# Generated by NetworkManager",
"search example.com localdomain",
"nameserver 10.0.0.1"),
nmRunning("1.44.2", false)),
wantLog: "dns: resolvedIsActuallyResolver error: resolv.conf doesn't point to systemd-resolved; points to [10.0.0.1]\n" +
"dns: [rc=nm resolved=not-in-use ret=direct]",
want: "direct",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var logBuf tstest.MemLogger
got, err := dnsMode(logBuf.Logf, tt.env)
got, err := dnsMode(logBuf.Logf, nil, tt.env)
if err != nil {
t.Fatal(err)
}
@@ -272,8 +313,9 @@ func (m memFS) Stat(name string) (isRegular bool, err error) {
return false, nil
}
func (m memFS) Rename(_, _ string) error { panic("TODO") }
func (m memFS) Remove(_ string) error { panic("TODO") }
func (m memFS) Chmod(name string, mode os.FileMode) error { panic("TODO") }
func (m memFS) Rename(oldName, newName string) error { panic("TODO") }
func (m memFS) Remove(name string) error { panic("TODO") }
func (m memFS) ReadFile(name string) ([]byte, error) {
v, ok := m[name]
if !ok {
@@ -297,7 +339,7 @@ func (m memFS) Truncate(name string) error {
return nil
}
func (m memFS) WriteFile(name string, contents []byte, _ os.FileMode) error {
func (m memFS) WriteFile(name string, contents []byte, perm os.FileMode) error {
m[name] = string(contents)
return nil
}
@@ -381,6 +423,12 @@ func resolvDotConf(ss ...string) envOption {
})
}
func nsswitchDotConf(ss ...string) envOption {
return envOpt(func(b *envBuilder) {
b.fs["/etc/nsswitch.conf"] = strings.Join(ss, "\n")
})
}
// resolvedRunning returns an option that makes resolved reply to a dbusPing
// and the ResolvConfMode property.
func resolvedRunning() envOption {

View File

@@ -1,6 +1,5 @@
// 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux
@@ -11,6 +10,7 @@ import (
"fmt"
"net"
"net/netip"
"sort"
"time"
"github.com/godbus/dbus/v5"
@@ -24,6 +24,13 @@ const (
lowerPriority = int32(200) // lower than all builtin auto priorities
)
// reconfigTimeout is the time interval within which Manager.{Up,Down} should complete.
//
// This is particularly useful because certain conditions can cause indefinite hangs
// (such as improper dbus auth followed by contextless dbus.Object.Call).
// Such operations should be wrapped in a timeout context.
const reconfigTimeout = time.Second
// nmManager uses the NetworkManager DBus API.
type nmManager struct {
interfaceName string
@@ -31,8 +38,6 @@ type nmManager struct {
dnsManager dbus.BusObject
}
var _ OSConfigurator = (*nmManager)(nil)
func newNMManager(interfaceName string) (*nmManager, error) {
conn, err := dbus.SystemBus()
if err != nil {
@@ -141,18 +146,17 @@ func (m *nmManager) trySet(ctx context.Context, config OSConfig) error {
// tell it explicitly to keep it. Read out the current interface
// settings and mirror them out to NetworkManager.
var addrs6 []map[string]any
if netIface, err := net.InterfaceByName(m.interfaceName); err == nil {
if addrs, err := netIface.Addrs(); err == nil {
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok {
nip, ok := netip.AddrFromSlice(ipnet.IP)
nip = nip.Unmap()
if ok && nip.Is6() {
addrs6 = append(addrs6, map[string]any{
"address": nip.String(),
"prefix": uint32(128),
})
}
if tsIf, err := net.InterfaceByName(m.interfaceName); err == nil {
addrs, _ := tsIf.Addrs()
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok {
nip, ok := netip.AddrFromSlice(ipnet.IP)
nip = nip.Unmap()
if ok && nip.Is6() {
addrs6 = append(addrs6, map[string]any{
"address": nip.String(),
"prefix": uint32(128),
})
}
}
}
@@ -260,6 +264,125 @@ func (m *nmManager) trySet(ctx context.Context, config OSConfig) error {
return nil
}
func (m *nmManager) SupportsSplitDNS() bool {
var mode string
v, err := m.dnsManager.GetProperty("org.freedesktop.NetworkManager.DnsManager.Mode")
if err != nil {
return false
}
mode, ok := v.Value().(string)
if !ok {
return false
}
// Per NM's documentation, it only does split-DNS when it's
// programming dnsmasq or systemd-resolved. All other modes are
// primary-only.
return mode == "dnsmasq" || mode == "systemd-resolved"
}
func (m *nmManager) GetBaseConfig() (OSConfig, error) {
conn, err := dbus.SystemBus()
if err != nil {
return OSConfig{}, err
}
nm := conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager/DnsManager"))
v, err := nm.GetProperty("org.freedesktop.NetworkManager.DnsManager.Configuration")
if err != nil {
return OSConfig{}, err
}
cfgs, ok := v.Value().([]map[string]dbus.Variant)
if !ok {
return OSConfig{}, fmt.Errorf("unexpected NM config type %T", v.Value())
}
if len(cfgs) == 0 {
return OSConfig{}, nil
}
type dnsPrio struct {
resolvers []netip.Addr
domains []string
priority int32
}
order := make([]dnsPrio, 0, len(cfgs)-1)
for _, cfg := range cfgs {
if name, ok := cfg["interface"]; ok {
if s, ok := name.Value().(string); ok && s == m.interfaceName {
// Config for the tailscale interface, skip.
continue
}
}
var p dnsPrio
if v, ok := cfg["nameservers"]; ok {
if ips, ok := v.Value().([]string); ok {
for _, s := range ips {
ip, err := netip.ParseAddr(s)
if err != nil {
// hmm, what do? Shouldn't really happen.
continue
}
p.resolvers = append(p.resolvers, ip)
}
}
}
if v, ok := cfg["domains"]; ok {
if domains, ok := v.Value().([]string); ok {
p.domains = domains
}
}
if v, ok := cfg["priority"]; ok {
if prio, ok := v.Value().(int32); ok {
p.priority = prio
}
}
order = append(order, p)
}
sort.Slice(order, func(i, j int) bool {
return order[i].priority < order[j].priority
})
var (
ret OSConfig
seenResolvers = map[netip.Addr]bool{}
seenSearch = map[string]bool{}
)
for _, cfg := range order {
for _, resolver := range cfg.resolvers {
if seenResolvers[resolver] {
continue
}
ret.Nameservers = append(ret.Nameservers, resolver)
seenResolvers[resolver] = true
}
for _, dom := range cfg.domains {
if seenSearch[dom] {
continue
}
fqdn, err := dnsname.ToFQDN(dom)
if err != nil {
continue
}
ret.SearchDomains = append(ret.SearchDomains, fqdn)
seenSearch[dom] = true
}
if cfg.priority < 0 {
// exclusive configurations preempt all other
// configurations, so we're done.
break
}
}
return ret, nil
}
func (m *nmManager) Close() error {
// No need to do anything on close, NetworkManager will delete our
// settings when the tailscale interface goes away.

View File

@@ -1,6 +1,5 @@
// Copyright (c) 2021 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux || freebsd || openbsd
@@ -10,22 +9,41 @@ import (
"bytes"
"fmt"
"os/exec"
"strings"
"tailscale.com/types/logger"
)
// openresolvManager manages DNS configuration using the openresolv
// implementation of the `resolvconf` program.
type openresolvManager struct{}
type openresolvManager struct {
logf logger.Logf
}
var _ OSConfigurator = (*openresolvManager)(nil)
func newOpenresolvManager(logf logger.Logf) (openresolvManager, error) {
return openresolvManager{logf}, nil
}
func newOpenresolvManager() (openresolvManager, error) {
return openresolvManager{}, nil
func (m openresolvManager) logCmdErr(cmd *exec.Cmd, err error) {
if err == nil {
return
}
commandStr := fmt.Sprintf("path=%q args=%q", cmd.Path, cmd.Args)
exerr, ok := err.(*exec.ExitError)
if !ok {
m.logf("error running command %s: %v", commandStr, err)
return
}
m.logf("error running command %s stderr=%q exitCode=%d: %v", commandStr, exerr.Stderr, exerr.ExitCode(), err)
}
func (m openresolvManager) deleteTailscaleConfig() error {
cmd := exec.Command("resolvconf", "-f", "-d", "ctrld")
out, err := cmd.CombinedOutput()
if err != nil {
m.logCmdErr(cmd, err)
return fmt.Errorf("running %s: %s", cmd, out)
}
return nil
@@ -43,11 +61,55 @@ func (m openresolvManager) SetDNS(config OSConfig) error {
cmd.Stdin = &stdin
out, err := cmd.CombinedOutput()
if err != nil {
m.logCmdErr(cmd, err)
return fmt.Errorf("running %s: %s", cmd, out)
}
return nil
}
func (m openresolvManager) SupportsSplitDNS() bool {
return false
}
func (m openresolvManager) GetBaseConfig() (OSConfig, error) {
// List the names of all config snippets openresolv is aware
// of. Snippets get listed in priority order (most to least),
// which we'll exploit later.
bs, err := exec.Command("resolvconf", "-i").CombinedOutput()
if err != nil {
return OSConfig{}, err
}
// Remove the "tailscale" snippet from the list.
args := []string{"-l"}
for _, f := range strings.Split(strings.TrimSpace(string(bs)), " ") {
if f == "tailscale" {
continue
}
args = append(args, f)
}
// List all resolvconf snippets except our own, and parse that as
// a resolv.conf. This effectively generates a blended config of
// "everyone except tailscale", which is what would be in use if
// tailscale hadn't set exclusive mode.
//
// Note that this is not _entirely_ true. To be perfectly correct,
// we should be looking for other interfaces marked exclusive that
// predated tailscale, and stick to only those. However, in
// practice, openresolv uses are generally quite limited, and boil
// down to 1-2 DHCP leases, for which the correct outcome is a
// blended config like the one we produce here.
var buf bytes.Buffer
cmd := exec.Command("resolvconf", args...)
cmd.Stdout = &buf
if err := cmd.Run(); err != nil {
m.logCmdErr(cmd, err)
return OSConfig{}, err
}
return readResolv(&buf)
}
func (m openresolvManager) Close() error {
return m.deleteTailscaleConfig()
}

View File

@@ -1,20 +1,20 @@
// Copyright (c) 2021 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package dns
import (
"bufio"
"errors"
"fmt"
"net/netip"
"slices"
"strings"
"tailscale.com/types/logger"
"tailscale.com/util/dnsname"
)
var _ OSConfigurator = (*directManager)(nil)
// An OSConfigurator applies DNS settings to the operating system.
type OSConfigurator interface {
// SetDNS updates the OS's DNS configuration to match cfg.
@@ -23,8 +23,21 @@ type OSConfigurator interface {
// SetDNS must not be called after Close.
// SetDNS takes ownership of cfg.
SetDNS(cfg OSConfig) error
// SupportsSplitDNS reports whether the configurator is capable of
// installing a resolver only for specific DNS suffixes. If false,
// the configurator can only set a global resolver.
SupportsSplitDNS() bool
// GetBaseConfig returns the OS's "base" configuration, i.e. the
// resolver settings the OS would use without Tailscale
// contributing any configuration.
// GetBaseConfig must return the tailscale-free base config even
// after SetDNS has been called to set a Tailscale configuration.
// Only works when SupportsSplitDNS=false.
// Close removes ctrld-related DNS configuration from the OS.
// Implementations that don't support getting the base config must
// return ErrGetBaseConfigNotSupported.
GetBaseConfig() (OSConfig, error)
// Close removes Tailscale-related DNS configuration from the OS.
Close() error
Mode() string
@@ -50,14 +63,59 @@ type OSConfig struct {
SearchDomains []dnsname.FQDN
// MatchDomains are the DNS suffixes for which Nameservers should
// be used. If empty, Nameservers is installed as the "primary" resolver.
// A non-empty MatchDomains requests a "split DNS" configuration
// from the OS, which will only work with OSConfigurators that
// report SupportsSplitDNS()=true.
MatchDomains []dnsname.FQDN
}
func (o *OSConfig) WriteToBufioWriter(w *bufio.Writer) {
if o == nil {
w.WriteString("<nil>")
return
}
w.WriteString("{")
if len(o.Hosts) > 0 {
fmt.Fprintf(w, "Hosts:%v ", o.Hosts)
}
if len(o.Nameservers) > 0 {
fmt.Fprintf(w, "Nameservers:%v ", o.Nameservers)
}
if len(o.SearchDomains) > 0 {
fmt.Fprintf(w, "SearchDomains:%v ", o.SearchDomains)
}
if len(o.MatchDomains) > 0 {
w.WriteString("MatchDomains:[")
sp := ""
var numARPA int
for _, s := range o.MatchDomains {
if strings.HasSuffix(string(s), ".arpa.") {
numARPA++
continue
}
w.WriteString(sp)
w.WriteString(string(s))
sp = " "
}
w.WriteString("]")
if numARPA > 0 {
fmt.Fprintf(w, "+%darpa", numARPA)
}
}
w.WriteString("}")
}
func (o OSConfig) IsZero() bool {
return len(o.Nameservers) == 0 && len(o.SearchDomains) == 0 && len(o.MatchDomains) == 0
return len(o.Hosts) == 0 &&
len(o.Nameservers) == 0 &&
len(o.SearchDomains) == 0 &&
len(o.MatchDomains) == 0
}
func (a OSConfig) Equal(b OSConfig) bool {
if len(a.Hosts) != len(b.Hosts) {
return false
}
if len(a.Nameservers) != len(b.Nameservers) {
return false
}
@@ -68,6 +126,15 @@ func (a OSConfig) Equal(b OSConfig) bool {
return false
}
for i := range a.Hosts {
ha, hb := a.Hosts[i], b.Hosts[i]
if ha.Addr != hb.Addr {
return false
}
if !slices.Equal(ha.Hosts, hb.Hosts) {
return false
}
}
for i := range a.Nameservers {
if a.Nameservers[i] != b.Nameservers[i] {
return false
@@ -93,34 +160,39 @@ func (a OSConfig) Equal(b OSConfig) bool {
// Fixes https://github.com/tailscale/tailscale/issues/5669
func (a OSConfig) Format(f fmt.State, verb rune) {
logger.ArgWriter(func(w *bufio.Writer) {
_, _ = w.WriteString(`{Nameservers:[`)
w.WriteString(`{Nameservers:[`)
for i, ns := range a.Nameservers {
if i != 0 {
_, _ = w.WriteString(" ")
w.WriteString(" ")
}
_, _ = fmt.Fprintf(w, "%+v", ns)
fmt.Fprintf(w, "%+v", ns)
}
_, _ = w.WriteString(`] SearchDomains:[`)
w.WriteString(`] SearchDomains:[`)
for i, domain := range a.SearchDomains {
if i != 0 {
_, _ = w.WriteString(" ")
w.WriteString(" ")
}
_, _ = fmt.Fprintf(w, "%+v", domain)
fmt.Fprintf(w, "%+v", domain)
}
_, _ = w.WriteString(`] MatchDomains:[`)
w.WriteString(`] MatchDomains:[`)
for i, domain := range a.MatchDomains {
if i != 0 {
_, _ = w.WriteString(" ")
w.WriteString(" ")
}
_, _ = fmt.Fprintf(w, "%+v", domain)
fmt.Fprintf(w, "%+v", domain)
}
_, _ = w.WriteString(`] Hosts:[`)
w.WriteString(`] Hosts:[`)
for i, host := range a.Hosts {
if i != 0 {
_, _ = w.WriteString(" ")
w.WriteString(" ")
}
_, _ = fmt.Fprintf(w, "%+v", host)
fmt.Fprintf(w, "%+v", host)
}
_, _ = w.WriteString(`]}`)
w.WriteString(`]}`)
}).Format(f, verb)
}
// ErrGetBaseConfigNotSupported is the error
// OSConfigurator.GetBaseConfig returns when the OSConfigurator
// doesn't support reading the underlying configuration out of the OS.
var ErrGetBaseConfigNotSupported = errors.New("getting OS base config is not supported")

View File

@@ -1,14 +1,15 @@
// Copyright (c) 2022 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package dns
import (
"fmt"
"net/netip"
"reflect"
"testing"
"tailscale.com/tstest"
"tailscale.com/util/dnsname"
)
@@ -42,3 +43,13 @@ func TestOSConfigPrintable(t *testing.T) {
t.Errorf("format mismatch:\n got: %s\n want: %s", s, expected)
}
}
func TestIsZero(t *testing.T) {
tstest.CheckIsZero[OSConfig](t, map[reflect.Type]any{
reflect.TypeFor[dnsname.FQDN](): dnsname.FQDN("foo.bar."),
reflect.TypeFor[*HostEntry](): &HostEntry{
Addr: netip.AddrFrom4([4]byte{100, 1, 2, 3}),
Hosts: []string{"foo", "bar"},
},
})
}

View File

@@ -1,7 +1,6 @@
#!/bin/sh
# Copyright (c) 2021 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.
# Copyright (c) Ctrld Inc & AUTHORS
# SPDX-License-Identifier: BSD-3-Clause
#
# This script is a workaround for a vpn-unfriendly behavior of the
# original resolvconf by Thomas Hood. Unlike the `openresolv`
@@ -29,7 +28,7 @@ if [ -n "$CTRLD_RESOLVCONF_HOOK_LOOP" ]; then
exit 0
fi
if [ ! -f ctrld.inet ]; then
if [ ! -f tun-ctrld.inet ]; then
# Ctrld isn't trying to manage DNS, do nothing.
exit 0
fi
@@ -60,4 +59,4 @@ if [ -d /etc/resolvconf/update-libc.d ] ; then
# Re-notify libc watchers that we've changed resolv.conf again.
export CTRLD_RESOLVCONF_HOOK_LOOP=1
exec run-parts /etc/resolvconf/update-libc.d
fi
fi

View File

@@ -1,12 +1,12 @@
// Copyright (c) 2021 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux || freebsd || openbsd
package dns
import (
"bytes"
"os/exec"
)
@@ -14,13 +14,17 @@ func resolvconfStyle() string {
if _, err := exec.LookPath("resolvconf"); err != nil {
return ""
}
if _, err := exec.Command("resolvconf", "--version").CombinedOutput(); err != nil {
output, err := exec.Command("resolvconf", "--version").CombinedOutput()
if err != nil {
// Debian resolvconf doesn't understand --version, and
// exits with a specific error code.
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 99 {
return "debian"
}
}
if bytes.HasPrefix(output, []byte("Debian resolvconf")) {
return "debian"
}
// Treat everything else as openresolv, by far the more popular implementation.
return "openresolv"
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build !gokrazy
package dns
const (
resolvConf = "/etc/resolv.conf"
backupConf = "/etc/resolv.pre-ctrld-backup.conf"
)

View File

@@ -0,0 +1,11 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build gokrazy
package dns
const (
resolvConf = "/tmp/resolv.conf"
backupConf = "/tmp/resolv.pre-ctrld-backup.conf"
)

View File

@@ -1,6 +1,5 @@
// 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.
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux
@@ -21,8 +20,6 @@ import (
"tailscale.com/util/dnsname"
)
const reconfigTimeout = time.Second
// DBus entities we talk to.
//
// DBus is an RPC bus. In particular, the bus we're talking to is the
@@ -97,16 +94,14 @@ type resolvedManager struct {
ctx context.Context
cancel func() // terminate the context, for close
logf logger.Logf
ifidx int
logf logger.Logf
health *health.Tracker
ifidx int
configCR chan changeRequest // tracks OSConfigs changes and error responses
revertCh chan struct{}
}
var _ OSConfigurator = (*resolvedManager)(nil)
func newResolvedManager(logf logger.Logf, interfaceName string) (*resolvedManager, error) {
func newResolvedManager(logf logger.Logf, health *health.Tracker, interfaceName string) (*resolvedManager, error) {
iface, err := net.InterfaceByName(interfaceName)
if err != nil {
return nil, err
@@ -119,11 +114,11 @@ func newResolvedManager(logf logger.Logf, interfaceName string) (*resolvedManage
ctx: ctx,
cancel: cancel,
logf: logf,
ifidx: iface.Index,
logf: logf,
health: health,
ifidx: iface.Index,
configCR: make(chan changeRequest),
revertCh: make(chan struct{}),
}
go mgr.run(ctx)
@@ -132,8 +127,10 @@ func newResolvedManager(logf logger.Logf, interfaceName string) (*resolvedManage
}
func (m *resolvedManager) SetDNS(config OSConfig) error {
// NOTE: don't close this channel, since it's possible that the SetDNS
// call will time out and return before the run loop answers, at which
// point it will send on the now-closed channel.
errc := make(chan error, 1)
defer close(errc)
select {
case <-m.ctx.Done():
@@ -221,14 +218,12 @@ func (m *resolvedManager) run(ctx context.Context) {
if err = conn.AddMatchSignal(dbus.WithMatchObjectPath(dbusPath), dbus.WithMatchInterface(dbusInterface), dbus.WithMatchMember(dbusOwnerSignal), dbus.WithMatchArg(0, dbusResolvedObject)); err != nil {
m.logf("[v1] Setting DBus signal filter failed: %v", err)
}
if err = conn.AddMatchSignal(dbus.WithMatchObjectPath(dbusPath), dbus.WithMatchInterface(dbusInterface), dbus.WithMatchMember(dbusOwnerSignal), dbus.WithMatchArg(0, dbusNetworkdObject)); err != nil {
m.logf("[v1] Setting DBus signal filter failed: %v", err)
}
conn.Signal(signals)
// Reset backoff and SetNSOSHealth after successful on reconnect.
bo.BackOff(ctx, nil)
health.SetDNSOSHealth(nil)
//lint:ignore SA1019 upstream code still use it.
m.health.SetDNSOSHealth(nil)
return nil
}
@@ -243,15 +238,13 @@ func (m *resolvedManager) run(ctx context.Context) {
if rManager == nil {
return
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
// RevertLink resets all per-interface settings on systemd-resolved to defaults.
// When ctx goes away systemd-resolved auto reverts.
// Keeping for potential use in future refactor.
if call := rManager.CallWithContext(ctx, dbusRevertLink, 0, m.ifidx); call.Err != nil {
m.logf("[v1] RevertLink: %v", call.Err)
return
}
cancel()
close(m.revertCh)
return
case configCR := <-m.configCR:
// Track and update sync with latest config change.
@@ -308,7 +301,8 @@ func (m *resolvedManager) run(ctx context.Context) {
// Set health while holding the lock, because this will
// graciously serialize the resync's health outcome with a
// concurrent SetDNS call.
health.SetDNSOSHealth(err)
//lint:ignore SA1019 upstream code still use it.
m.health.SetDNSOSHealth(err)
if err != nil {
m.logf("failed to configure systemd-resolved: %v", err)
}
@@ -426,18 +420,22 @@ func (m *resolvedManager) setConfigOverDBus(ctx context.Context, rManager dbus.B
m.logf("[v1] failed to disable DoT: %v", call.Err)
}
if rManager.Path() == dbusResolvedPath {
if call := rManager.CallWithContext(ctx, dbusFlushCaches, 0); call.Err != nil {
m.logf("failed to flush resolved DNS cache: %v", call.Err)
}
if call := rManager.CallWithContext(ctx, dbusFlushCaches, 0); call.Err != nil {
m.logf("failed to flush resolved DNS cache: %v", call.Err)
}
return nil
}
func (m *resolvedManager) SupportsSplitDNS() bool {
return true
}
func (m *resolvedManager) GetBaseConfig() (OSConfig, error) {
return OSConfig{}, ErrGetBaseConfigNotSupported
}
func (m *resolvedManager) Close() error {
m.cancel() // stops the 'run' method goroutine
<-m.revertCh
return nil
}

View File

@@ -10,8 +10,9 @@ import (
"sync"
"time"
"tailscale.com/net/netmon"
"github.com/miekg/dns"
"tailscale.com/net/interfaces"
)
const (
@@ -346,7 +347,7 @@ func NewResolverWithNameserver(nameservers []string) Resolver {
// Rfc1918Addresses returns the list of local interfaces private IP addresses
func Rfc1918Addresses() []string {
var res []string
interfaces.ForeachInterface(func(i interfaces.Interface, prefixes []netip.Prefix) {
netmon.ForeachInterface(func(i netmon.Interface, prefixes []netip.Prefix) {
addrs, _ := i.Addrs()
for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet)