DHCP Client Script Examples

From Exterior Memory
Jump to: navigation, search

A DHCP client may override /etc/resolv.conf, the hostname and set routes upon receiving. If this is undesired, it can be turned off by overriding a function in the dhclient-script.

Two dhclients

There are two dhclients: the ISC DHCP client, and the OpenBSD DHCP client.

The ISC client is installed by default on Linux, while the OpenBSD client is more common for BSD, including FreeBSD.

Please be aware of the difference. The configuration file for both clients is very similar, but there are differences, so I strongly recommend to make sure you read the correct man pages.

Known differences I've encountered:

  • In ISC, the interface in the config file takes presedence over the interface specified on the command line. In BSD DHClient, the command line takes presedence.
  • The BSD dhclient.conf does not know about adding new names: option vendor-specific-info code 43 = string
  • The BSD dhclient-script and ISC dhclient-script have different functions, so they behave different if you to override them in dhclient-enter-hooks or dhclient-exit-hooks.

More information can be found in the manual pages or source code:

Don't override resolv.conf

resolv.conf is overwritten if the DHCP server send a DHCP message with options domain-name-servers (code 6), domain-name (code 15), or domain-search (code 119).

OpenBSD DHCP client

Create a file called /etc/dhclient-enter-hooks and add this:

add_new_resolv_conf() {
    # We don't want /etc/resolv.conf changed
    # So this is an empty function
    return 0
}

ISC DHCP client

Create a file called /etc/dhcp/dhclient-enter-hooks and add this:

make_resolv_conf() {
    # We don't want /etc/resolv.conf changed
    # So this is an empty function
    return 0
}

Don't ask for name servers

Alternatively, in /etc/dhclient.conf, add a line that only requests certain options, but not domain-name-servers, domain-name, nor domain-search. E.g.:

request routers, subnet-mask, broadcast-address, ntp-servers, host-name

Don't override hostname

The hostname is overwritten if the DHCP server send a DHCP message with option host-name (code 12).

OpenBSD DHCP client

Create a file called /etc/dhclient-enter-hooks and add this:

check_hostname() {
    # We don't want the hostname changed
    # So this is an empty function
    return 0
}

ISC DHCP client

Create a file called /etc/dhcp/dhclient-enter-hooks and add this:

unset new_host_name

Don't ask for a hostname

Alternatively, in /etc/dhclient.conf, add a line that only requests certain options, but not host-name. E.g.:

request routers, subnet-mask, broadcast-address, domain-name-servers, ntp-servers

Do not set a default route

Suppose you have a multihomed FreeBSD, with two interfaces receiving an IP address and default route by DHCP. E.g. interface em0 is for Internet, interface em1 is for IPTV. The default behaviour for the OpenBSD/FreeBSD dhclient-script is to only set the default route for the first interface that receives a DHCP reply. The default behaviour for ISC dhclient-script is to set the default route for all interfaces.

If you want to have more control, e.g. only set the default route for em0, but not for other interface, add the following lines to /etc/dhclient-enter-hooks:

# Do not set default route on any interface except em0
if [ "$interface" != "em0" ]
then
    unset new_routers
fi

Add Routes

Suppose your FreeBSD router receives a default route, but you only want to specify certain routes, you can add the following to /etc/dhclient-enter-hooks:

case "$reason" in
    BOUND|RENEW|REBIND|REBOOT|TIMEOUT)
        # Add static routes on em1
        if [ "$interface" = "em1" -a -n "$new_routers" ]
        then
            # Determine gateway from routers
            # extract first IP address and replace all . with " "
            gateway=`echo "${new_routers%% *}" | sed 's/\./ /g'`
            # route 192.2.0.128/25 and 10.0.0.0/8 to the gateway IP.
            new_classless_routes="25 192 0 2 128 $gateway 8 10 $gateway"
        fi
        
        # Do not set default route on any interface except em0
        if [ "$interface" != "em0" ]
        then
            unset new_routers
        fi
    ;;
esac

This script performs two actions:

  1. If em1 is configured, add two routes.
  2. if any other interface than em0 is configured, do not set a default route.

Rather than calling route directly, the script uses the classless-routes DHCP option to tell the dhclient-script to add a certain route in the routing table. Note that the encoding is a bit odd: the size of the subnet determines how many octets of the IP address are encoded in the DHCP option, and thus in the new_classless_routes variable.


If you use the ISC dhclient instead of the OpenBSD dhclient, note that the FreeBSD dhclient-script supports the classless-routes, but the ISC dhclient-script only supports the older static-routes DHCP option, and will not set any routes. As a work-around, you can set new_static_routes instead of new_classless_routes. Officially, this bad syntax (static-routes is supposed the work on individual IP addresses, not IP ranges), but in practise it should work fine for the ISC dhclient-script.

gateway=${new_routers%% *} # select first IP address
new_static_routes=192.2.0.128/25 $gateway 10.0.0.0/8 $gateway

In case you are wondering why I used sed instead of the following parameter expansion: the second line is a bash-construct, and does not work in sh, which is used by dhclient-script.

gateway=${new_routers%% *} # extract first IP address
gateway=${gateway//./ } # replace all . with " "