mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-19 19:38:40 +00:00
net/dns: support split and unsplit DNS in NetworkManager.
Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
parent
f5742b0647
commit
9aa33b43e6
126
net/dns/nm.go
126
net/dns/nm.go
@ -14,9 +14,12 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/godbus/dbus/v5"
|
"github.com/godbus/dbus/v5"
|
||||||
|
"inet.af/netaddr"
|
||||||
|
"tailscale.com/util/dnsname"
|
||||||
"tailscale.com/util/endian"
|
"tailscale.com/util/endian"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -68,11 +71,36 @@ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
func nmCanSplitDNS() bool {
|
||||||
|
conn, err := dbus.SystemBus()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var mode string
|
||||||
|
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 {
|
func newNMManager(interfaceName string) nmManager {
|
||||||
return nmManager{
|
return nmManager{
|
||||||
interfaceName: interfaceName,
|
interfaceName: interfaceName,
|
||||||
|
canSplit: nmCanSplitDNS(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,10 +262,104 @@ func (m nmManager) trySet(ctx context.Context, config OSConfig) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m nmManager) SupportsSplitDNS() bool { return false }
|
func (m nmManager) SupportsSplitDNS() bool { return m.canSplit }
|
||||||
|
|
||||||
func (m nmManager) GetBaseConfig() (OSConfig, error) {
|
func (m nmManager) GetBaseConfig() (OSConfig, error) {
|
||||||
return OSConfig{}, ErrGetBaseConfigNotSupported
|
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())
|
||||||
|
}
|
||||||
|
|
||||||
|
type dnsPrio struct {
|
||||||
|
resolvers []netaddr.IP
|
||||||
|
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 taislcale 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 := netaddr.ParseIP(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[netaddr.IP]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 {
|
func (m nmManager) Close() error {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user