net/dns: improve NetworkManager detection, using more DBus.

Signed-off-by: David Anderson <danderson@tailscale.com>
pull/1691/head
David Anderson 3 years ago
parent 9a48bac8ad
commit 84430cdfa1

@ -8,6 +8,8 @@ import "tailscale.com/types/logger"
func NewOSConfigurator(logf logger.Logf, interfaceName string) OSConfigurator {
switch {
case isNMActive():
return newNMManager(interfaceName)
// TODO: rework NetworkManager and resolved support.
// case isResolvedActive():
// return newResolvedManager()

@ -14,7 +14,7 @@ import (
"context"
"fmt"
"os"
"os/exec"
"time"
"github.com/godbus/dbus/v5"
"tailscale.com/util/endian"
@ -22,11 +22,21 @@ import (
// isNMActive determines if NetworkManager is currently managing system DNS settings.
func isNMActive() bool {
// This is somewhat tricky because NetworkManager supports a number
// of DNS configuration modes. In all cases, we expect it to be installed
// and /etc/resolv.conf to contain a mention of NetworkManager in the comments.
_, err := exec.LookPath("NetworkManager")
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
defer cancel()
conn, err := dbus.SystemBus()
if err != nil {
// Probably no DBus on this system. Either way, we can't
// control NM without DBus.
return false
}
// Try to ping NetworkManager's DnsManager object. If it responds,
// NM is running and we're allowed to touch it.
nm := conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager/DnsManager"))
call := nm.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0)
if call.Err != nil {
return false
}
@ -67,8 +77,24 @@ func (m nmManager) SetDNS(config OSConfig) error {
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
defer cancel()
// conn is a shared connection whose lifecycle is managed by the dbus package.
// We should not interfere with that by closing it.
// NetworkManager only lets you set DNS settings on "active"
// connections, which requires an assigned IP address. This got
// configured before the DNS manager was invoked, but it might
// take a little time for the netlink notifications to propagate
// up. So, keep retrying for the duration of the reconfigTimeout.
var err error
for ctx.Err() == nil {
err = m.trySet(ctx, config)
if err == nil {
break
}
time.Sleep(10 * time.Millisecond)
}
return err
}
func (m nmManager) trySet(ctx context.Context, config OSConfig) error {
conn, err := dbus.SystemBus()
if err != nil {
return fmt.Errorf("connecting to system bus: %w", err)

@ -1010,15 +1010,18 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
}
if routerChanged {
e.logf("wgengine: Reconfig: configuring DNS")
err := e.dns.Set(*dnsCfg)
health.SetDNSHealth(err)
e.logf("wgengine: Reconfig: configuring router")
err := e.router.Set(routerCfg)
health.SetRouterHealth(err)
if err != nil {
return err
}
e.logf("wgengine: Reconfig: configuring router")
err = e.router.Set(routerCfg)
health.SetRouterHealth(err)
// Keep DNS configuration after router configuration, as some
// DNS managers refuse to apply settings if the device has no
// assigned address.
e.logf("wgengine: Reconfig: configuring DNS")
err = e.dns.Set(*dnsCfg)
health.SetDNSHealth(err)
if err != nil {
return err
}

Loading…
Cancel
Save