NetworkManager DNS handling

Current status

Each connection contains static IPv4 and IPv6 DNS parameters in the ipv{4,6}.dns, ipv{4,6}.dns-search options. When the connection is activated, such parameters are merged together, as well as parameters received dynamically through DHCP or pushed by VPNs. Dynamic parameters are ignored if the ipv{4,6}.ignore-auto-dns property is set.

When there are multiple active connections, the resulting DNS configuration is the union of all connections' configurations. How they are merged together depends on the dns mode, selected by the dns option in NetworkManager.conf.

For dns=default, i.e. when NetworkManager updates resolv.conf, name servers from all connections are added to resolv.conf ordered by the value of ipv{4,6}.dns-priority. All search domains are also added to resolv.conf as 'search domain1 domain2 ...", as well as options from ipv{4,6}.dns-options.

The dnsmasq configuration backend allows more flexibility, as it can restrict nameservers to queries for specific domains (this is called 'split DNS'). Currently the dnsmasq backend does split DNS only (and unconditionally) for VPNs that push a list of domains to clients. So, non-VPN connections are always used for all queries, while VPNs are only used for the domains they push (even if the VPN has the default route). The dnsmasq backend also enforces that queries to a name server are only forwarded through the interface associated to such server.

The systemd-resolved backend is conceptually similar to the dnsmasq one, with the difference that it always does split DNS, and so name servers for a connection are used only for the domains in the connection's search list. Queries that don't match any domain are forwarded to all interfaces. If a connection does not have a default route, domains in the search list are considered "routing only", i.e. they are not used to complete single-label names but only to select the outgoing interface.

For all backends, the ipv{4,6}.dns-priority property can be used to tweak the priority of name server entries. When all connections have a non-negative DNS priority, values are used to determine the order of name servers in resolv.conf (and thus the value only really matters when using the default backend, because other backends query name servers in parallel). If a connection has a negative DNS priority value, its DNS configuration overrides all other configurations. In presence of multiple negative values, the most negative ones win.

Problems

The current implementation has some problems.

If we exclude the default backend, which has very limited functionality, the remaining two have inconsistent behaviors as systemd-resolved always uses split DNS while dnsmasq uses it only for VPNs.

Also, there is lack of flexibility as some basic scenarios can't be achieved. For example it's not possible to have a VPN that gets all the traffic, while still directing queries for a local domain to the local network. Or, when using the systemd-resolved backend, a VPN without the default route can't have a search domain that is used to complete names (bug)

The general problem is that the policy is hardwired in the code and it's not possible to change it.

The plan

The dnsmasq and systemd-resolved backends should behave in the same way, except when the functionalities offered by the two are not compatible.

Both plugins will do split DNS for all connections; all domains provided by a connection will be used as a search list (i.e. also to complete unqualified names) by default.

When all connections specify a domain, queries not matching any of them will be directed to all interfaces, excluding VPNs. This preserves the current behavior.

A new ipv{4,6}.dns-default boolean property is be added to connections, to specify which connections will get queries for non-matching domains.

Domains in ipv{4,6}.dns-search can have a ~ prefix to specify that the domain is routing-only. When using systemd-resolved, the prefix will be used to set the routing-only flag for the domain. When using dnsmasq, the prefix will prevent the addition of the domain to the search list in resolv.conf.

Test cases

Here some test scenarios and the resulting configuration.

  1. Single connection
    Setup
    eth0: nsA/domainA
    Result
    search domainA
    nsA@eth0/domainA
    nsA@eth0/*
  2. Single connection, routing only
    Setup
    eth0: nsA/~domainA
    Result
    nsA@eth0/domainA
    nsA@eth0/*
  3. Multiple connections
    Setup
    eth0: nsA/domainA
    eth1: nsB/domainB
    Result
    search domainA domainB
    nsA@eth0/domainA
    nsB@eth1/domainB
    nsA@eth0/*
    nsB@eth1/*
  4. Multiple connections, DNS default
    Setup
    eth0: nsA/domainA
    eth1: nsB, dns-default=yes
    Result
    search domainA
    nsA@eth0/domainA
    nsB@eth1/*
  5. Multiple connections, DNS default override
    Setup
    eth0: nsA
    eth1: nsB, dns-default=yes
    Result
    nsB/*
  6. VPN with search domain and default gateway
    Setup
    eth0: nsA/domainA
    vpn0: nsB/domainB(pushed), default gateway
    Result
    nsA@eth0/*
    nsA@eth0/domainA
    nsB@vpn0/domainB
  7. VPN with search domain, default gateway and DNS default
    Setup
    eth0: nsA
    vpn0: nsB/domainB(pushed), dns-default=yes, default gateway
    Result
    nsB@vpn0/domainB
    nsB@vpn0/*
  8. VPN without search domain
    Setup
    eth0: nsA
    vpn0: nsB
    Result
    nsA@eth0/*
    nsB@eth1/*

Projects/NetworkManager/DNS (last edited 2017-06-23 08:57:05 by BeniaminoGalvani)