net/dns: print systemd-resolved ResolvConfMode

The ResolvConfMode property is documented to return how systemd-resolved
is currently managing /etc/resolv.conf. Include that information in the
debug line, when available, to assist in debugging DNS issues.

Signed-off-by: Andrew Dunham <andrew@du.nham.ca>
Change-Id: I1ae3a257df1d318d0193a8c7f135c458ec45093e
This commit is contained in:
Andrew Dunham 2022-10-14 20:25:22 +02:00
parent 223126fe5b
commit e966f024b0
2 changed files with 100 additions and 15 deletions

View File

@ -31,6 +31,7 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
env := newOSConfigEnv{ env := newOSConfigEnv{
fs: directFS{}, fs: directFS{},
dbusPing: dbusPing, dbusPing: dbusPing,
dbusReadString: dbusReadString,
nmIsUsingResolved: nmIsUsingResolved, nmIsUsingResolved: nmIsUsingResolved,
nmVersionBetween: nmVersionBetween, nmVersionBetween: nmVersionBetween,
resolvconfStyle: resolvconfStyle, resolvconfStyle: resolvconfStyle,
@ -60,6 +61,7 @@ func NewOSConfigurator(logf logger.Logf, interfaceName string) (ret OSConfigurat
type newOSConfigEnv struct { type newOSConfigEnv struct {
fs wholeFileFS fs wholeFileFS
dbusPing func(string, string) error dbusPing func(string, string) error
dbusReadString func(string, string, string, string) (string, error)
nmIsUsingResolved func() error nmIsUsingResolved func() error
nmVersionBetween func(v1, v2 string) (safe bool, err error) nmVersionBetween func(v1, v2 string) (safe bool, err error)
resolvconfStyle func() string resolvconfStyle func() string
@ -78,6 +80,25 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
logf("dns: %v", debug) logf("dns: %v", debug)
}() }()
// In all cases that we detect systemd-resolved, try asking it what it
// thinks the current resolv.conf mode is so we can add it to our logs.
defer func() {
if ret != "systemd-resolved" {
return
}
// Try to ask systemd-resolved what it thinks the current
// status of resolv.conf is. This is documented at:
// https://www.freedesktop.org/software/systemd/man/org.freedesktop.resolve1.html
mode, err := env.dbusReadString("org.freedesktop.resolve1", "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", "ResolvConfMode")
if err != nil {
logf("dns: ResolvConfMode error: %v", err)
dbg("resolv-conf-mode", "error")
} else {
dbg("resolv-conf-mode", mode)
}
}()
// Before we read /etc/resolv.conf (which might be in a broken // Before we read /etc/resolv.conf (which might be in a broken
// or symlink-dangling state), try to ping the D-Bus service // or symlink-dangling state), try to ping the D-Bus service
// for systemd-resolved. If it's active on the machine, this // for systemd-resolved. If it's active on the machine, this
@ -102,6 +123,7 @@ func dnsMode(logf logger.Logf, env newOSConfigEnv) (ret string, err error) {
switch resolvOwner(bs) { switch resolvOwner(bs) {
case "systemd-resolved": case "systemd-resolved":
dbg("rc", "resolved") dbg("rc", "resolved")
// Some systems, for reasons known only to them, have a // Some systems, for reasons known only to them, have a
// resolv.conf that has the word "systemd-resolved" in its // resolv.conf that has the word "systemd-resolved" in its
// header, but doesn't actually point to resolved. We mustn't // header, but doesn't actually point to resolved. We mustn't
@ -327,3 +349,29 @@ func dbusPing(name, objectPath string) error {
call := obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0) call := obj.CallWithContext(ctx, "org.freedesktop.DBus.Peer.Ping", 0)
return call.Err 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
}

View File

@ -71,7 +71,7 @@ func TestLinuxDNSMode(t *testing.T) {
{ {
name: "resolved_alone_without_ping", name: "resolved_alone_without_ping",
env: env(resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53")), env: env(resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53")),
wantLog: "dns: [rc=resolved nm=no ret=systemd-resolved]", wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=resolved nm=no resolv-conf-mode=error ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -79,7 +79,7 @@ func TestLinuxDNSMode(t *testing.T) {
env: env( env: env(
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"), resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning()), resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=resolved nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -88,7 +88,7 @@ func TestLinuxDNSMode(t *testing.T) {
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"), resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning(), resolvedRunning(),
nmRunning("1.2.3", false)), nmRunning("1.2.3", false)),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -106,7 +106,7 @@ func TestLinuxDNSMode(t *testing.T) {
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"), resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning(), resolvedRunning(),
nmRunning("1.27.0", true)), nmRunning("1.27.0", true)),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -115,7 +115,7 @@ func TestLinuxDNSMode(t *testing.T) {
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"), resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedRunning(), resolvedRunning(),
nmRunning("1.22.0", true)), nmRunning("1.22.0", true)),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=resolved nm=yes nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
// Regression tests for extreme corner cases below. // Regression tests for extreme corner cases below.
@ -141,7 +141,7 @@ func TestLinuxDNSMode(t *testing.T) {
"nameserver 127.0.0.53", "nameserver 127.0.0.53",
"nameserver 127.0.0.53"), "nameserver 127.0.0.53"),
resolvedRunning()), resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=resolved nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -156,7 +156,7 @@ func TestLinuxDNSMode(t *testing.T) {
"# run \"systemd-resolve --status\" to see details about the actual nameservers.", "# run \"systemd-resolve --status\" to see details about the actual nameservers.",
"nameserver 127.0.0.53"), "nameserver 127.0.0.53"),
resolvedRunning()), resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=resolved nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -171,7 +171,7 @@ func TestLinuxDNSMode(t *testing.T) {
"# 127.0.0.53 is the systemd-resolved stub resolver.", "# 127.0.0.53 is the systemd-resolved stub resolver.",
"# run \"systemd-resolve --status\" to see details about the actual nameservers.", "# run \"systemd-resolve --status\" to see details about the actual nameservers.",
"nameserver 127.0.0.53")), "nameserver 127.0.0.53")),
wantLog: "dns: [rc=resolved nm=no ret=systemd-resolved]", wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=resolved nm=no resolv-conf-mode=error ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -183,7 +183,7 @@ func TestLinuxDNSMode(t *testing.T) {
"options edns0 trust-ad"), "options edns0 trust-ad"),
resolvedRunning(), resolvedRunning(),
nmRunning("1.32.12", true)), nmRunning("1.32.12", true)),
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm-safe=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm-safe=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -194,7 +194,7 @@ func TestLinuxDNSMode(t *testing.T) {
"nameserver 127.0.0.53", "nameserver 127.0.0.53",
"options edns0 trust-ad"), "options edns0 trust-ad"),
nmRunning("1.32.12", true)), nmRunning("1.32.12", true)),
wantLog: "dns: [rc=nm nm-resolved=yes nm-safe=no ret=systemd-resolved]", wantLog: "dns: ResolvConfMode error: dbus property not found\ndns: [rc=nm nm-resolved=yes nm-safe=no resolv-conf-mode=error ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -217,7 +217,7 @@ func TestLinuxDNSMode(t *testing.T) {
"nameserver 127.0.0.53", "nameserver 127.0.0.53",
"options edns0 trust-ad"), "options edns0 trust-ad"),
resolvedRunning()), resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -228,7 +228,7 @@ func TestLinuxDNSMode(t *testing.T) {
"search lan", "search lan",
"nameserver 127.0.0.53"), "nameserver 127.0.0.53"),
resolvedRunning()), resolvedRunning()),
wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=nm nm-resolved=yes nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
{ {
@ -236,8 +236,9 @@ func TestLinuxDNSMode(t *testing.T) {
// before we read its file. // before we read its file.
env: env(resolvedStartOnPingAndThen( env: env(resolvedStartOnPingAndThen(
resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"), resolvDotConf("# Managed by systemd-resolved", "nameserver 127.0.0.53"),
resolvedDbusProperty(),
)), )),
wantLog: "dns: [resolved-ping=yes rc=resolved nm=no ret=systemd-resolved]", wantLog: "dns: [resolved-ping=yes rc=resolved nm=no resolv-conf-mode=fortests ret=systemd-resolved]",
want: "systemd-resolved", want: "systemd-resolved",
}, },
} }
@ -306,9 +307,16 @@ type dbusService struct {
hook func() // if non-nil, run on ping hook func() // if non-nil, run on ping
} }
type dbusProperty struct {
name, path string
iface, member string
hook func() (string, error) // what to return
}
type envBuilder struct { type envBuilder struct {
fs memFS fs memFS
dbus []dbusService dbus []dbusService
dbusProperties []dbusProperty
nmUsingResolved bool nmUsingResolved bool
nmVersion string nmVersion string
resolvconfStyle string resolvconfStyle string
@ -345,6 +353,14 @@ func env(opts ...envOption) newOSConfigEnv {
} }
return errors.New("dbus service not found") return errors.New("dbus service not found")
}, },
dbusReadString: func(name, path, iface, member string) (string, error) {
for _, svc := range b.dbusProperties {
if svc.name == name && svc.path == path && svc.iface == iface && svc.member == member {
return svc.hook()
}
}
return "", errors.New("dbus property not found")
},
nmIsUsingResolved: func() error { nmIsUsingResolved: func() error {
if !b.nmUsingResolved { if !b.nmUsingResolved {
return errors.New("networkmanager not using resolved") return errors.New("networkmanager not using resolved")
@ -365,9 +381,16 @@ func resolvDotConf(ss ...string) envOption {
}) })
} }
// resolvedRunning returns an option that makes resolved reply to a dbusPing. // resolvedRunning returns an option that makes resolved reply to a dbusPing
// and the ResolvConfMode property.
func resolvedRunning() envOption { func resolvedRunning() envOption {
return resolvedStartOnPingAndThen( /* nothing */ ) return resolvedStartOnPingAndThen(resolvedDbusProperty())
}
// resolvedDbusProperty returns an option that responds to the ResolvConfMode
// property that resolved exposes.
func resolvedDbusProperty() envOption {
return setDbusProperty("org.freedesktop.resolve1", "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", "ResolvConfMode", "fortests")
} }
// resolvedStartOnPingAndThen returns an option that makes resolved be // resolvedStartOnPingAndThen returns an option that makes resolved be
@ -400,3 +423,17 @@ func resolvconf(s string) envOption {
b.resolvconfStyle = s b.resolvconfStyle = s
}) })
} }
func setDbusProperty(name, path, iface, member, value string) envOption {
return envOpt(func(b *envBuilder) {
b.dbusProperties = append(b.dbusProperties, dbusProperty{
name: name,
path: path,
iface: iface,
member: member,
hook: func() (string, error) {
return value, nil
},
})
})
}