mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-03 19:41:34 +02:00
net/dns, feature/featuretags: make NetworkManager, systemd-resolved, and DBus modular
Saves 360 KB (19951800 => 19591352 on linux/amd64 --extra-small --box binary) Updates #12614 Updates #17206 Change-Id: Iafd5b2536dd735111b447546cba335a7a64379ed Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
parent
b54cdf9f38
commit
b3e9a128af
@ -161,3 +161,16 @@ func TestOmitOutboundProxy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}.Check(t)
|
}.Check(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOmitDBus(t *testing.T) {
|
||||||
|
deptest.DepChecker{
|
||||||
|
GOOS: "linux",
|
||||||
|
GOARCH: "amd64",
|
||||||
|
Tags: "ts_omit_networkmanager,ts_omit_dbus,ts_omit_resolved,ts_omit_systray,ts_omit_ssh,ts_include_cli",
|
||||||
|
OnDep: func(dep string) {
|
||||||
|
if strings.Contains(dep, "dbus") {
|
||||||
|
t.Errorf("unexpected DBus dep: %q", dep)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}.Check(t)
|
||||||
|
}
|
||||||
|
13
feature/buildfeatures/feature_dbus_disabled.go
Normal file
13
feature/buildfeatures/feature_dbus_disabled.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_dbus
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasDBus is whether the binary was built with support for modular feature "Linux DBus support".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_dbus" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasDBus = false
|
13
feature/buildfeatures/feature_dbus_enabled.go
Normal file
13
feature/buildfeatures/feature_dbus_enabled.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_dbus
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasDBus is whether the binary was built with support for modular feature "Linux DBus support".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_dbus" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasDBus = true
|
13
feature/buildfeatures/feature_networkmanager_disabled.go
Normal file
13
feature/buildfeatures/feature_networkmanager_disabled.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_networkmanager
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasNetworkManager is whether the binary was built with support for modular feature "Linux NetworkManager integration".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_networkmanager" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasNetworkManager = false
|
13
feature/buildfeatures/feature_networkmanager_enabled.go
Normal file
13
feature/buildfeatures/feature_networkmanager_enabled.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_networkmanager
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasNetworkManager is whether the binary was built with support for modular feature "Linux NetworkManager integration".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_networkmanager" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasNetworkManager = true
|
13
feature/buildfeatures/feature_resolved_disabled.go
Normal file
13
feature/buildfeatures/feature_resolved_disabled.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build ts_omit_resolved
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasResolved is whether the binary was built with support for modular feature "Linux systemd-resolved integration".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_resolved" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasResolved = false
|
13
feature/buildfeatures/feature_resolved_enabled.go
Normal file
13
feature/buildfeatures/feature_resolved_enabled.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Code generated by gen.go; DO NOT EDIT.
|
||||||
|
|
||||||
|
//go:build !ts_omit_resolved
|
||||||
|
|
||||||
|
package buildfeatures
|
||||||
|
|
||||||
|
// HasResolved is whether the binary was built with support for modular feature "Linux systemd-resolved integration".
|
||||||
|
// Specifically, it's whether the binary was NOT built with the "ts_omit_resolved" build tag.
|
||||||
|
// It's a const so it can be used for dead code elimination.
|
||||||
|
const HasResolved = true
|
@ -97,6 +97,7 @@ var Features = map[FeatureTag]FeatureMeta{
|
|||||||
"capture": {"Capture", "Packet capture", nil},
|
"capture": {"Capture", "Packet capture", nil},
|
||||||
"cli": {"CLI", "embed the CLI into the tailscaled binary", nil},
|
"cli": {"CLI", "embed the CLI into the tailscaled binary", nil},
|
||||||
"completion": {"Completion", "CLI shell completion", nil},
|
"completion": {"Completion", "CLI shell completion", nil},
|
||||||
|
"dbus": {"DBus", "Linux DBus support", nil},
|
||||||
"debugeventbus": {"DebugEventBus", "eventbus debug support", nil},
|
"debugeventbus": {"DebugEventBus", "eventbus debug support", nil},
|
||||||
"debugportmapper": {
|
"debugportmapper": {
|
||||||
Sym: "DebugPortMapper",
|
Sym: "DebugPortMapper",
|
||||||
@ -115,7 +116,17 @@ var Features = map[FeatureTag]FeatureMeta{
|
|||||||
},
|
},
|
||||||
"portmapper": {"PortMapper", "NAT-PMP/PCP/UPnP port mapping support", nil},
|
"portmapper": {"PortMapper", "NAT-PMP/PCP/UPnP port mapping support", nil},
|
||||||
"netstack": {"Netstack", "gVisor netstack (userspace networking) support (TODO; not yet omittable)", nil},
|
"netstack": {"Netstack", "gVisor netstack (userspace networking) support (TODO; not yet omittable)", nil},
|
||||||
|
"networkmanager": {
|
||||||
|
Sym: "NetworkManager",
|
||||||
|
Desc: "Linux NetworkManager integration",
|
||||||
|
Deps: []FeatureTag{"dbus"},
|
||||||
|
},
|
||||||
"relayserver": {"RelayServer", "Relay server", nil},
|
"relayserver": {"RelayServer", "Relay server", nil},
|
||||||
|
"resolved": {
|
||||||
|
Sym: "Resolved",
|
||||||
|
Desc: "Linux systemd-resolved integration",
|
||||||
|
Deps: []FeatureTag{"dbus"},
|
||||||
|
},
|
||||||
"serve": {
|
"serve": {
|
||||||
Sym: "Serve",
|
Sym: "Serve",
|
||||||
Desc: "Serve and Funnel support",
|
Desc: "Serve and Funnel support",
|
||||||
@ -124,10 +135,14 @@ var Features = map[FeatureTag]FeatureMeta{
|
|||||||
"ssh": {
|
"ssh": {
|
||||||
Sym: "SSH",
|
Sym: "SSH",
|
||||||
Desc: "Tailscale SSH support",
|
Desc: "Tailscale SSH support",
|
||||||
Deps: []FeatureTag{"netstack"},
|
Deps: []FeatureTag{"dbus", "netstack"},
|
||||||
},
|
},
|
||||||
"syspolicy": {"SystemPolicy", "System policy configuration (MDM) support", nil},
|
"syspolicy": {"SystemPolicy", "System policy configuration (MDM) support", nil},
|
||||||
"systray": {"SysTray", "Linux system tray", nil},
|
"systray": {
|
||||||
|
Sym: "SysTray",
|
||||||
|
Desc: "Linux system tray",
|
||||||
|
Deps: []FeatureTag{"dbus"},
|
||||||
|
},
|
||||||
"taildrop": {"Taildrop", "Taildrop (file sending) support", nil},
|
"taildrop": {"Taildrop", "Taildrop (file sending) support", nil},
|
||||||
"tailnetlock": {"TailnetLock", "Tailnet Lock support", nil},
|
"tailnetlock": {"TailnetLock", "Tailnet Lock support", nil},
|
||||||
"tap": {"Tap", "Experimental Layer 2 (ethernet) support", nil},
|
"tap": {"Tap", "Experimental Layer 2 (ethernet) support", nil},
|
||||||
|
59
net/dns/dbus.go
Normal file
59
net/dns/dbus.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
//go:build linux && !android && !ts_omit_dbus
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/godbus/dbus/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
optDBusPing.Set(dbusPing)
|
||||||
|
optDBusReadString.Set(dbusReadString)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbusPing(name, objectPath string) error {
|
||||||
|
conn, err := dbus.SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
// DBus probably not running.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
obj := conn.Object(name, dbus.ObjectPath(objectPath))
|
||||||
|
call := obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0)
|
||||||
|
return call.Err
|
||||||
|
}
|
||||||
|
|
||||||
|
// dbusReadString reads a string property from the provided name and object
|
||||||
|
// path. property must be in "interface.member" notation.
|
||||||
|
func dbusReadString(name, objectPath, iface, member string) (string, error) {
|
||||||
|
conn, err := dbus.SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
// DBus probably not running.
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
obj := conn.Object(name, dbus.ObjectPath(objectPath))
|
||||||
|
|
||||||
|
var result dbus.Variant
|
||||||
|
err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, iface, member).Store(&result)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s, ok := result.Value().(string); ok {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
return result.String(), nil
|
||||||
|
}
|
@ -7,7 +7,6 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -15,13 +14,12 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
|
||||||
"tailscale.com/control/controlknobs"
|
"tailscale.com/control/controlknobs"
|
||||||
|
"tailscale.com/feature"
|
||||||
"tailscale.com/health"
|
"tailscale.com/health"
|
||||||
"tailscale.com/net/netaddr"
|
"tailscale.com/net/netaddr"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
"tailscale.com/util/cmpver"
|
|
||||||
"tailscale.com/util/syspolicy/policyclient"
|
"tailscale.com/util/syspolicy/policyclient"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
)
|
)
|
||||||
@ -36,6 +34,31 @@ func (kv kv) String() string {
|
|||||||
|
|
||||||
var publishOnce sync.Once
|
var publishOnce sync.Once
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Set unless ts_omit_networkmanager
|
||||||
|
var (
|
||||||
|
optNewNMManager feature.Hook[func(ifName string) (OSConfigurator, error)]
|
||||||
|
optNMIsUsingResolved feature.Hook[func() error]
|
||||||
|
optNMVersionBetween feature.Hook[func(v1, v2 string) (bool, error)]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set unless ts_omit_resolved
|
||||||
|
var (
|
||||||
|
optNewResolvedManager feature.Hook[func(logf logger.Logf, health *health.Tracker, interfaceName string) (OSConfigurator, error)]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set unless ts_omit_dbus
|
||||||
|
var (
|
||||||
|
optDBusPing feature.Hook[func(name, objectPath string) error]
|
||||||
|
optDBusReadString feature.Hook[func(name, objectPath, iface, member string) (string, error)]
|
||||||
|
)
|
||||||
|
|
||||||
// NewOSConfigurator created a new OS configurator.
|
// NewOSConfigurator created a new OS configurator.
|
||||||
//
|
//
|
||||||
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
|
// The health tracker may be nil; the knobs may be nil and are ignored on this platform.
|
||||||
@ -46,12 +69,24 @@ func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ policyclient.
|
|||||||
|
|
||||||
env := newOSConfigEnv{
|
env := newOSConfigEnv{
|
||||||
fs: directFS{},
|
fs: directFS{},
|
||||||
dbusPing: dbusPing,
|
|
||||||
dbusReadString: dbusReadString,
|
|
||||||
nmIsUsingResolved: nmIsUsingResolved,
|
|
||||||
nmVersionBetween: nmVersionBetween,
|
|
||||||
resolvconfStyle: resolvconfStyle,
|
resolvconfStyle: resolvconfStyle,
|
||||||
}
|
}
|
||||||
|
if f, ok := optDBusPing.GetOk(); ok {
|
||||||
|
env.dbusPing = f
|
||||||
|
} else {
|
||||||
|
env.dbusPing = func(_, _ string) error { return errors.ErrUnsupported }
|
||||||
|
}
|
||||||
|
if f, ok := optDBusReadString.GetOk(); ok {
|
||||||
|
env.dbusReadString = f
|
||||||
|
} else {
|
||||||
|
env.dbusReadString = func(_, _, _, _ string) (string, error) { return "", errors.ErrUnsupported }
|
||||||
|
}
|
||||||
|
if f, ok := optNMIsUsingResolved.GetOk(); ok {
|
||||||
|
env.nmIsUsingResolved = f
|
||||||
|
} else {
|
||||||
|
env.nmIsUsingResolved = func() error { return errors.ErrUnsupported }
|
||||||
|
}
|
||||||
|
env.nmVersionBetween, _ = optNMVersionBetween.GetOk() // GetOk to not panic if nil; unused if optNMIsUsingResolved returns an error
|
||||||
mode, err := dnsMode(logf, health, env)
|
mode, err := dnsMode(logf, health, env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -66,17 +101,24 @@ func NewOSConfigurator(logf logger.Logf, health *health.Tracker, _ policyclient.
|
|||||||
case "direct":
|
case "direct":
|
||||||
return newDirectManagerOnFS(logf, health, env.fs), nil
|
return newDirectManagerOnFS(logf, health, env.fs), nil
|
||||||
case "systemd-resolved":
|
case "systemd-resolved":
|
||||||
return newResolvedManager(logf, health, interfaceName)
|
if f, ok := optNewResolvedManager.GetOk(); ok {
|
||||||
|
return f(logf, health, interfaceName)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("tailscaled was built without DNS %q support", mode)
|
||||||
case "network-manager":
|
case "network-manager":
|
||||||
return newNMManager(interfaceName)
|
if f, ok := optNewNMManager.GetOk(); ok {
|
||||||
|
return f(interfaceName)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("tailscaled was built without DNS %q support", mode)
|
||||||
case "debian-resolvconf":
|
case "debian-resolvconf":
|
||||||
return newDebianResolvconfManager(logf)
|
return newDebianResolvconfManager(logf)
|
||||||
case "openresolv":
|
case "openresolv":
|
||||||
return newOpenresolvManager(logf)
|
return newOpenresolvManager(logf)
|
||||||
default:
|
default:
|
||||||
logf("[unexpected] detected unknown DNS mode %q, using direct manager as last resort", mode)
|
logf("[unexpected] detected unknown DNS mode %q, using direct manager as last resort", mode)
|
||||||
return newDirectManagerOnFS(logf, health, env.fs), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return newDirectManagerOnFS(logf, health, env.fs), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newOSConfigEnv are the funcs newOSConfigurator needs, pulled out for testing.
|
// newOSConfigEnv are the funcs newOSConfigurator needs, pulled out for testing.
|
||||||
@ -292,50 +334,6 @@ func dnsMode(logf logger.Logf, health *health.Tracker, env newOSConfigEnv) (ret
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func nmVersionBetween(first, last string) (bool, error) {
|
|
||||||
conn, err := dbus.SystemBus()
|
|
||||||
if err != nil {
|
|
||||||
// DBus probably not running.
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nm := conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager"))
|
|
||||||
v, err := nm.GetProperty("org.freedesktop.NetworkManager.Version")
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
version, ok := v.Value().(string)
|
|
||||||
if !ok {
|
|
||||||
return false, fmt.Errorf("unexpected type %T for NM version", v.Value())
|
|
||||||
}
|
|
||||||
|
|
||||||
outside := cmpver.Compare(version, first) < 0 || cmpver.Compare(version, last) > 0
|
|
||||||
return !outside, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func nmIsUsingResolved() error {
|
|
||||||
conn, err := dbus.SystemBus()
|
|
||||||
if err != nil {
|
|
||||||
// DBus probably not running.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nm := conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager/DnsManager"))
|
|
||||||
v, err := nm.GetProperty("org.freedesktop.NetworkManager.DnsManager.Mode")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("getting NM mode: %w", err)
|
|
||||||
}
|
|
||||||
mode, ok := v.Value().(string)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("unexpected type %T for NM DNS mode", v.Value())
|
|
||||||
}
|
|
||||||
if mode != "systemd-resolved" {
|
|
||||||
return errors.New("NetworkManager is not using systemd-resolved for DNS")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolvedIsActuallyResolver reports whether the system is using
|
// resolvedIsActuallyResolver reports whether the system is using
|
||||||
// systemd-resolved as the resolver. There are two different ways to
|
// systemd-resolved as the resolver. There are two different ways to
|
||||||
// use systemd-resolved:
|
// use systemd-resolved:
|
||||||
@ -396,44 +394,3 @@ func isLibnssResolveUsed(env newOSConfigEnv) error {
|
|||||||
}
|
}
|
||||||
return fmt.Errorf("libnss_resolve not used")
|
return fmt.Errorf("libnss_resolve not used")
|
||||||
}
|
}
|
||||||
|
|
||||||
func dbusPing(name, objectPath string) error {
|
|
||||||
conn, err := dbus.SystemBus()
|
|
||||||
if err != nil {
|
|
||||||
// DBus probably not running.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
obj := conn.Object(name, dbus.ObjectPath(objectPath))
|
|
||||||
call := obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0)
|
|
||||||
return call.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
// dbusReadString reads a string property from the provided name and object
|
|
||||||
// path. property must be in "interface.member" notation.
|
|
||||||
func dbusReadString(name, objectPath, iface, member string) (string, error) {
|
|
||||||
conn, err := dbus.SystemBus()
|
|
||||||
if err != nil {
|
|
||||||
// DBus probably not running.
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
obj := conn.Object(name, dbus.ObjectPath(objectPath))
|
|
||||||
|
|
||||||
var result dbus.Variant
|
|
||||||
err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, iface, member).Store(&result)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if s, ok := result.Value().(string); ok {
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
return result.String(), nil
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
//go:build linux && !android
|
//go:build linux && !android && !ts_omit_networkmanager
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@ -16,6 +17,7 @@ import (
|
|||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
"tailscale.com/net/tsaddr"
|
"tailscale.com/net/tsaddr"
|
||||||
|
"tailscale.com/util/cmpver"
|
||||||
"tailscale.com/util/dnsname"
|
"tailscale.com/util/dnsname"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,13 +27,6 @@ const (
|
|||||||
lowerPriority = int32(200) // lower than all builtin auto priorities
|
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.
|
// nmManager uses the NetworkManager DBus API.
|
||||||
type nmManager struct {
|
type nmManager struct {
|
||||||
interfaceName string
|
interfaceName string
|
||||||
@ -39,7 +34,13 @@ type nmManager struct {
|
|||||||
dnsManager dbus.BusObject
|
dnsManager dbus.BusObject
|
||||||
}
|
}
|
||||||
|
|
||||||
func newNMManager(interfaceName string) (*nmManager, error) {
|
func init() {
|
||||||
|
optNewNMManager.Set(newNMManager)
|
||||||
|
optNMIsUsingResolved.Set(nmIsUsingResolved)
|
||||||
|
optNMVersionBetween.Set(nmVersionBetween)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNMManager(interfaceName string) (OSConfigurator, error) {
|
||||||
conn, err := dbus.SystemBus()
|
conn, err := dbus.SystemBus()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -389,3 +390,47 @@ func (m *nmManager) Close() error {
|
|||||||
// settings when the tailscale interface goes away.
|
// settings when the tailscale interface goes away.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nmVersionBetween(first, last string) (bool, error) {
|
||||||
|
conn, err := dbus.SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
// DBus probably not running.
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nm := conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager"))
|
||||||
|
v, err := nm.GetProperty("org.freedesktop.NetworkManager.Version")
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
version, ok := v.Value().(string)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Errorf("unexpected type %T for NM version", v.Value())
|
||||||
|
}
|
||||||
|
|
||||||
|
outside := cmpver.Compare(version, first) < 0 || cmpver.Compare(version, last) > 0
|
||||||
|
return !outside, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func nmIsUsingResolved() error {
|
||||||
|
conn, err := dbus.SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
// DBus probably not running.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nm := conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager/DnsManager"))
|
||||||
|
v, err := nm.GetProperty("org.freedesktop.NetworkManager.DnsManager.Mode")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getting NM mode: %w", err)
|
||||||
|
}
|
||||||
|
mode, ok := v.Value().(string)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type %T for NM DNS mode", v.Value())
|
||||||
|
}
|
||||||
|
if mode != "systemd-resolved" {
|
||||||
|
return errors.New("NetworkManager is not using systemd-resolved for DNS")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
//go:build linux && !android
|
//go:build linux && !android && !ts_omit_resolved
|
||||||
|
|
||||||
package dns
|
package dns
|
||||||
|
|
||||||
@ -70,7 +70,11 @@ type resolvedManager struct {
|
|||||||
configCR chan changeRequest // tracks OSConfigs changes and error responses
|
configCR chan changeRequest // tracks OSConfigs changes and error responses
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResolvedManager(logf logger.Logf, health *health.Tracker, interfaceName string) (*resolvedManager, error) {
|
func init() {
|
||||||
|
optNewResolvedManager.Set(newResolvedManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResolvedManager(logf logger.Logf, health *health.Tracker, interfaceName string) (OSConfigurator, error) {
|
||||||
iface, err := net.InterfaceByName(interfaceName)
|
iface, err := net.InterfaceByName(interfaceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
x
Reference in New Issue
Block a user