net/dns: return error from NewOSManager, use it to initialize NM.

Signed-off-by: David Anderson <danderson@tailscale.com>
pull/1678/head
David Anderson 2021-04-12 15:51:37 -07:00
parent 4d142ebe06
commit 854d5d36a1
16 changed files with 67 additions and 50 deletions

View File

@ -365,7 +365,11 @@ func tryEngine(logf logger.Logf, linkMon *monitor.Mon, name string) (e wgengine.
dev.Close() dev.Close()
return nil, false, err return nil, false, err
} }
conf.DNS = dns.NewOSConfigurator(logf, devName) d, err := dns.NewOSConfigurator(logf, devName)
if err != nil {
return nil, false, err
}
conf.DNS = d
conf.Router = r conf.Router = r
if wrapNetstack { if wrapNetstack {
conf.Router = netstack.NewSubnetRouterWrapper(conf.Router) conf.Router = netstack.NewSubnetRouterWrapper(conf.Router)

View File

@ -175,10 +175,16 @@ func startIPNServer(ctx context.Context, logid string) error {
if wrapNetstack { if wrapNetstack {
r = netstack.NewSubnetRouterWrapper(r) r = netstack.NewSubnetRouterWrapper(r)
} }
d, err := dns.NewOSConfigurator(logf, devName)
if err != nil {
r.Close()
dev.Close()
return nil, err
}
eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{ eng, err := wgengine.NewUserspaceEngine(logf, wgengine.Config{
Tun: dev, Tun: dev,
Router: r, Router: r,
DNS: dns.NewOSConfigurator(logf, devName), DNS: d,
ListenPort: 41641, ListenPort: 41641,
}) })
if err != nil { if err != nil {

View File

@ -53,7 +53,7 @@ type resolvconfManager struct {
scriptInstalled bool // libc update script has been installed scriptInstalled bool // libc update script has been installed
} }
func newDebianResolvconfManager(logf logger.Logf) *resolvconfManager { func newDebianResolvconfManager(logf logger.Logf) (*resolvconfManager, error) {
ret := &resolvconfManager{ ret := &resolvconfManager{
logf: logf, logf: logf,
listRecordsPath: "/lib/resolvconf/list-records", listRecordsPath: "/lib/resolvconf/list-records",
@ -86,7 +86,7 @@ func newDebianResolvconfManager(logf logger.Logf) *resolvconfManager {
ret.interfacesDir = "/etc/resolvconf/run/interfaces" ret.interfacesDir = "/etc/resolvconf/run/interfaces"
} }
return ret return ret, nil
} }
func (m *resolvconfManager) SetDNS(config OSConfig) error { func (m *resolvconfManager) SetDNS(config OSConfig) error {

View File

@ -120,8 +120,8 @@ func isResolvedRunning() bool {
// or as cleanup if the program terminates unexpectedly. // or as cleanup if the program terminates unexpectedly.
type directManager struct{} type directManager struct{}
func newDirectManager() directManager { func newDirectManager() (directManager, error) {
return directManager{} return directManager{}, nil
} }
// ownedByTailscale reports whether /etc/resolv.conf seems to be a // ownedByTailscale reports whether /etc/resolv.conf seems to be a

View File

@ -240,7 +240,11 @@ func (m *Manager) Down() error {
// in case the Tailscale daemon terminated without closing the router. // in case the Tailscale daemon terminated without closing the router.
// No other state needs to be instantiated before this runs. // No other state needs to be instantiated before this runs.
func Cleanup(logf logger.Logf, interfaceName string) { func Cleanup(logf logger.Logf, interfaceName string) {
oscfg := NewOSConfigurator(logf, interfaceName) oscfg, err := NewOSConfigurator(logf, interfaceName)
if err != nil {
logf("creating dns cleanup: %v", err)
return
}
dns := NewManager(logf, oscfg, nil) dns := NewManager(logf, oscfg, nil)
if err := dns.Down(); err != nil { if err := dns.Down(); err != nil {
logf("dns down: %v", err) logf("dns down: %v", err)

View File

@ -8,7 +8,7 @@ package dns
import "tailscale.com/types/logger" import "tailscale.com/types/logger"
func NewOSConfigurator(logger.Logf, string) OSConfigurator { func NewOSConfigurator(logger.Logf, string) (OSConfigurator, error) {
// TODO(dmytro): on darwin, we should use a macOS-specific method such as scutil. // TODO(dmytro): on darwin, we should use a macOS-specific method such as scutil.
// This is currently not implemented. Editing /etc/resolv.conf does not work, // This is currently not implemented. Editing /etc/resolv.conf does not work,
// as most applications use the system resolver, which disregards it. // as most applications use the system resolver, which disregards it.

View File

@ -6,7 +6,7 @@ package dns
import "tailscale.com/types/logger" import "tailscale.com/types/logger"
func NewOSConfigurator(logf logger.Logf, _ string) OSConfigurator { func NewOSConfigurator(logf logger.Logf, _ string) (OSConfigurator, error) {
switch { switch {
case isResolvconfActive(): case isResolvconfActive():
return newResolvconfManager(logf) return newResolvconfManager(logf)

View File

@ -6,7 +6,7 @@ package dns
import "tailscale.com/types/logger" import "tailscale.com/types/logger"
func NewOSConfigurator(logf logger.Logf, interfaceName string) OSConfigurator { func NewOSConfigurator(logf logger.Logf, interfaceName string) (OSConfigurator, error) {
switch { switch {
case isResolvedActive(): case isResolvedActive():
return newResolvedManager(logf) return newResolvedManager(logf)

View File

@ -6,6 +6,6 @@ package dns
import "tailscale.com/types/logger" import "tailscale.com/types/logger"
func NewOSConfigurator(logger.Logf, string) OSConfigurator { func NewOSConfigurator(logger.Logf, string) (OSConfigurator, error) {
return newDirectManager() return newDirectManager()
} }

View File

@ -39,7 +39,7 @@ type windowsManager struct {
nrptWorks bool nrptWorks bool
} }
func NewOSConfigurator(logf logger.Logf, interfaceName string) OSConfigurator { func NewOSConfigurator(logf logger.Logf, interfaceName string) (OSConfigurator, error) {
ret := windowsManager{ ret := windowsManager{
logf: logf, logf: logf,
guid: interfaceName, guid: interfaceName,
@ -56,7 +56,7 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) OSConfigurator {
ret.delKey(nrptBase) ret.delKey(nrptBase)
} }
return ret return ret, nil
} }
// keyOpenTimeout is how long we wait for a registry key to // keyOpenTimeout is how long we wait for a registry key to

View File

@ -71,42 +71,26 @@ func isNMActive() bool {
// nmManager uses the NetworkManager DBus API. // nmManager uses the NetworkManager DBus API.
type nmManager struct { type nmManager struct {
interfaceName string interfaceName string
canSplit bool manager dbus.BusObject
dnsManager dbus.BusObject
} }
func nmCanSplitDNS() bool { func newNMManager(interfaceName string) (*nmManager, error) {
conn, err := dbus.SystemBus() conn, err := dbus.SystemBus()
if err != nil { if err != nil {
return false return nil, err
} }
var mode string return &nmManager{
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 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 newNMManager(interfaceName string) nmManager {
return nmManager{
interfaceName: interfaceName, interfaceName: interfaceName,
canSplit: nmCanSplitDNS(), manager: conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager")),
} dnsManager: conn.Object("org.freedesktop.NetworkManager", dbus.ObjectPath("/org/freedesktop/NetworkManager/DnsManager")),
}, nil
} }
type nmConnectionSettings map[string]map[string]dbus.Variant type nmConnectionSettings map[string]map[string]dbus.Variant
func (m nmManager) SetDNS(config OSConfig) error { func (m *nmManager) SetDNS(config OSConfig) error {
ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout) ctx, cancel := context.WithTimeout(context.Background(), reconfigTimeout)
defer cancel() defer cancel()
@ -127,7 +111,7 @@ func (m nmManager) SetDNS(config OSConfig) error {
return err return err
} }
func (m nmManager) trySet(ctx context.Context, config OSConfig) error { func (m *nmManager) trySet(ctx context.Context, config OSConfig) error {
conn, err := dbus.SystemBus() conn, err := dbus.SystemBus()
if err != nil { if err != nil {
return fmt.Errorf("connecting to system bus: %w", err) return fmt.Errorf("connecting to system bus: %w", err)
@ -262,9 +246,24 @@ func (m nmManager) trySet(ctx context.Context, config OSConfig) error {
return nil return nil
} }
func (m nmManager) SupportsSplitDNS() bool { return m.canSplit } 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
}
func (m nmManager) GetBaseConfig() (OSConfig, error) { // 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() conn, err := dbus.SystemBus()
if err != nil { if err != nil {
return OSConfig{}, err return OSConfig{}, err
@ -362,7 +361,7 @@ func (m nmManager) GetBaseConfig() (OSConfig, error) {
return ret, nil return ret, nil
} }
func (m nmManager) Close() error { func (m *nmManager) Close() error {
// No need to do anything on close, NetworkManager will delete our // No need to do anything on close, NetworkManager will delete our
// settings when the tailscale interface goes away. // settings when the tailscale interface goes away.
return nil return nil

View File

@ -13,6 +13,6 @@ func (m noopManager) GetBaseConfig() (OSConfig, error) {
return OSConfig{}, ErrGetBaseConfigNotSupported return OSConfig{}, ErrGetBaseConfigNotSupported
} }
func NewNoopManager() noopManager { func NewNoopManager() (noopManager, error) {
return noopManager{} return noopManager{}, nil
} }

View File

@ -15,8 +15,8 @@ import (
// implementation of the `resolvconf` program. // implementation of the `resolvconf` program.
type openresolvManager struct{} type openresolvManager struct{}
func newOpenresolvManager() openresolvManager { func newOpenresolvManager() (openresolvManager, error) {
return openresolvManager{} return openresolvManager{}, nil
} }
func (m openresolvManager) SetDNS(config OSConfig) error { func (m openresolvManager) SetDNS(config OSConfig) error {

View File

@ -42,7 +42,7 @@ func isResolvconfActive() bool {
return false return false
} }
func newResolvconfManager(logf logger.Logf) OSConfigurator { func newResolvconfManager(logf logger.Logf) (OSConfigurator, error) {
_, err := exec.Command("resolvconf", "--version").CombinedOutput() _, err := exec.Command("resolvconf", "--version").CombinedOutput()
if err != nil { if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 99 { if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == 99 {

View File

@ -87,10 +87,10 @@ type resolvedManager struct {
logf logger.Logf logf logger.Logf
} }
func newResolvedManager(logf logger.Logf) resolvedManager { func newResolvedManager(logf logger.Logf) (resolvedManager, error) {
return resolvedManager{ return resolvedManager{
logf: logf, logf: logf,
} }, nil
} }
// Up implements managerImpl. // Up implements managerImpl.

View File

@ -219,7 +219,11 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
} }
if conf.DNS == nil { if conf.DNS == nil {
logf("[v1] using fake (no-op) DNS configurator") logf("[v1] using fake (no-op) DNS configurator")
conf.DNS = dns.NewNoopManager() d, err := dns.NewNoopManager()
if err != nil {
return nil, err
}
conf.DNS = d
} }
tsTUNDev := tstun.Wrap(logf, conf.Tun) tsTUNDev := tstun.Wrap(logf, conf.Tun)