Dynamic Management of the ISC BIND named Forwarding Table with D-BUS Jason Vas Dias, Red Hat Inc., May 2005 Overview: Red Hat has developed an extension to named that is enabled during rpmbuild of the bind SRPM with the option --define 'WITH_DBUS=1', and at named runtime with the -D named option. You can obtain the latest version of the source code for the BIND D-BUS extensions from: http://people.redhat.com/~jvdias/bind-dbus/ The Red Hat BIND D-BUS extensions allow services such as Red Hat's NetworkManager and dhcdbd (the DHCP Client controller D-Bus daemon) to tell named which name servers to forward requests to dynamically, instead of only with the "forward" and "forwarders" named.conf options. Dynamic forwarding table management allows named to be an effective and efficient caching nameserver for configurations with multiple wireless or VPN IP interfaces that are not always active, and whose name service parameters are typically configured with DHCP. Problems with trying to configure such systems automatically using only the libc resolver, causing conflicts over the contents of the /etc/resolv.conf file, are avoided; the resolv.conf file can contain only the users chosen search path and the single "nameserver 127.0.0.1" entry. named also provides a much more efficient, both in terms of caching performance and resolving time, and much more feature rich DNS resolver than does the libc resolver library and nscd, and has the benefit of existing improved IPv6 and DNSSEC support over glibc and nscd. Operation Guide for Developers: Programs can access named's dynamic forward table management services using D-BUS, the "service messagebus" sysv-init service that is started by default at boot (see the D-BUS documentation for details). When named is started with the -D option (by adding -D to the $OPTIONS variable in /etc/sysconfig/named), named provides two D-BUS methods: These D-BUS names are common to all named D-BUS methods: D-BUS Destination D-BUS Path D-BUS interface ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~~~~~~ com.redhat.named /com/redhat/named com.redhat.named D-BUS Members: ~~~~~~~~~~~~~~ SetForwarders ( { [ string:, ~~~~~~~~~~~~~ [ ( uint32: | array of 4 bytes : | array of 16 bytes : | string: ) [ uint16: , ] [ uint8: ] ] ] } , ... ) SetForwarders will create or delete members of the forwarding table. It accepts a list of tuples of up to 4 members: only the is required. If ONLY the is specified, the forwarding entry for EXACTLY that domain name is deleted if it exists. Only a specification of at least one is required to create a forwarding entry. The IP address can be IPv4: ( 32-bit integer OR array of 4 bytes OR dotted-quad string ) Or IPv6: ( array of 16 bytes OR RFC 2373/4 ascii string of 8 ':' separate hex quads as supported by inet_pton(3) ) 32 and 16-bit integer parameters MUST be given in network byte order; ie the IPv4 address 192.168.2.1 would be specified as uint32:16951488 on an i386 and port 53 would be uint16:13568. There are an optional 16-bit integer parameter, to specify the name server socket address port associated with the preceding IP address, and a parameter, which sets the forward policy as follows: 0: "none" : never forward to this nameserver for this domain. 1: "first": forward first to this server, and then search its authoritative data. 2: "only" : always forward to this nameserver for this domain. If not specified, will have the value 53, and will be "2": "only". named's default forward policy is "first" . Creation of forwarding domains is not "exact", as is deletion, but is "inclusive": creating forwarding entry for the '.' domain sets the default set of nameservers named will query for ALL domains, and creating an entry for "redhat.com" creates a set of nameservers to be queried for all names suffixed by "redhat.com." . If both are specified, the "redhat.com" servers will be tried first, followed by the "." servers. Forwarding entries are ONLY created in the first DNS View that matches the "localhost" client (127.0.0.1) and destination. The default view, which exists if no views have been specified in named.conf, matches ALL clients and destinations. If the user has configured views, none of which match the localhost client, then no forwarding will be dynamically configurable. Users are also free to configure a view that matches the localhost, for which forwarding will be dynamically configurable, and other views which do not match the localhost, so that other, remote clients can be served that will not be subject to dynamic forwarding. So it is a fully supported configuration that users can serve authoritative data to external clients and still use named's forwarding features for their localhost resolver. SetForwarders returns uint32:0 on success or a DBUS_ERROR message on failure . GetForwarders ( [ string: ] ) ~~~~~~~~~~~~~ Using the default "com.redhat.named" interface, returns the EXACT forwarding entry for as binary D-BUS types; there is also a com.redhat.named.text interface supported by GetForwarders which returns all values as string: text . If a is not specified, all forwarding table entries are dumped. Examples: ~~~~~~~~ Suppose we start out with the named.conf configuration: options { ... forwarders { 172.16.80.118; }; ... }; zone "redhat.com" { forward only; forwarders { 172.16.76.10; 172.16.52.28; }; }; Using a "dbus-send" trivially modified to support uint16 parameters (!) : $ dbus-send --system --type=method_call --print-reply --reply-timeout=20000 \ --dest=com.redhat.named /com/redhat/named com.redhat.named.GetForwarders method return sender=:1.367 -> dest=:1.368 0 string "redhat.com" 1 byte 2 2 uint32 172757164 3 uint16 13568 4 uint32 473174188 5 uint16 13568 6 string "." 7 byte 1 8 uint32 1984958636 9 uint16 13568 ie. GetForwarders always returns a list of tuples of ( , , , ) If the "text" interface was specified: $ dbus-send --system --type=method_call --print-reply --reply-timeout=20000 \ --dest=com.redhat.named /com/redhat/named com.redhat.named.text.GetForwarders method return sender=:1.367 -> dest=:1.370 0 string "redhat.com" 1 string "only" 2 string "172.16.76.10" 3 string "53" 4 string "172.16.52.28" 5 string "53" 6 string "." 7 string "first" 8 string "172.16.80.118" 9 string "53" So we could set the default nameserver for the root zone as follows: $ dbus-send --system --type=method_call --print-reply --reply-timeout=20000 \ --dest=com.redhat.named /com/redhat/named com.redhat.named.SetForwarders \ string:'.' string:'192.33.14.30' string:'2001:503:231d::2:30' method return sender=:1.367 -> dest=:1.371 0 uint32 0 $ dbus-send --system --type=method_call --print-reply --reply-timeout=20000 \ --dest=com.redhat.named /com/redhat/named com.redhat.named.text.GetForwarders method return sender=:1.367 -> dest=:1.372 0 string "redhat.com" 1 string "only" 2 string "172.16.76.10" 3 string "53" 4 string "172.16.52.28" 5 string "53" 6 string "." 7 string "only" 8 string "192.33.14.30" 9 string "53" 10 string "2001:503:231d::2:30" 11 string "53" Using tcpdump one can verify that named will attempt to contact 192.33.14.30, then 2001:503:231d::2:30, for all zones not in redhat.com; for redhat.com zones, 172.16.76.10 and 192.33.14.30 will be tried in that order. If the D-BUS driver dbus-daemon should shut down, named will emit the syslog message: "D-BUS service disabled." And will retry connecting to D-BUS every 10 seconds - once it has connected, the message: "D-BUS service enabled." will be logged. NOTE: there are the "SetForwarders" and "GetForwarders" scripts in the contrib/dbus directory of the BIND source code distribution which are wrappers around the dbus-send commands above. Usage: SetForwarders [ -t first | only ] [ [...] ] GetForwarders [ ] DHCP Integration ~~~~~~~~~~~~~~~~ With the -D option, named will try to subscribe to dhcdbd, the DHCP Client D-BUS Daemon, to be notified of DHCP "reason", "domain-name", "domain-name-server", "ip-address", and "subnet-mask" DHCP options when the dhclient program has received them from a DHCP server . If it cannot subscribe to dhcdbd, named will emit the message : "D-BUS dhcdbd subscription disabled." and will monitor D-BUS "NameOwnerChanged" messages for the appearance of a new owner for "com.redhat.dhcp". When the name is owned, named will send a "com.redhat.dhcp.subscribe.binary" message to dhcdbd to subscribe to the above options for all interfaces (provided by dhcdbd-1.5+), and emit the log message: "D-BUS dhcdbd subscription enabled." named will match on signals from the com.redhat.dhcp.subscribe.binary interface for those option settings, and , when the last option is received (indicated by a "reason" of 15: END_OPTIONS), it will configure the forwarding table . For each whitespace separated member of "domain-name-servers", AND for the reverse IPv4 in-addr.arpa class C or less domain of the ip-address masked by the subnet-mask, it will create a forwarding entry to query each "domain-server" . To support CIDR-based reverse subnet forwarding, Views would have to be configured dynamically, a possible future direction which is not yet implemented. (It would perhaps be easier to add a "match-queries" ACL to the forwarders table). When dhclient acquires a lease, named will configure forwarding, and emit the message: "D-BUS: dhclient for interface eth0 acquired new lease - creating forwarders." When a lease expires or the interface is brought down (dhclient is stopped with dhcdbd), it will revert any forwarding entries from the initial, static configuration that were modified by the DHCP subscription to their initial values; ie. if redhat.com had a forwarder configured in named.conf, and then an DHCP session specified forwarders for redhat.com, when the DHCP session ends the forwarders for redhat.com are reverted to their named.conf values; thus when all DHCP interfaces have released their leases, and if no SetForwarders commands were issued, the forwarding configuration will be identical to that at named startup. To Do: - Sending signals when any Forwarding entry is changed (easy to implement if it would be desirable). - CIDR based reverse Forwarding