Linux – Sending netmask and gateway/route with dhcp for ipv6

ipv6linuxnetworking

I have setup a DHCP server to assign ipv6 to my clients. However, both the "routers" and "subnet" OPTIONS are ignored.

I have read that ipv6 does not get theese from the DHCP, rather from the router broatcast.

However, I find this very strange. Why does it get a static IP from the DHCP, but the netmask from the router? Weird architecture.

Anyways, I am wondering if there is a way to easily add these "missing" features to the DHCPv6 or any other work around. The end result should be that I can set the "ip6", "netmask" and "gateway" from one central place, based on the MAC address of the client, just like I can do with ipv4.

I do not want to mess around with ipv6 auto-configuration and stateless stuff.

The gateway problem I could solve in a "bad" way by setting stuff on the "up" event in the network configuration, but I can't find a way to change the netmask of the interface after it has been brought up.

Best Answer

Normally, IPv6 is suppose to get the ip, netmask and route/gateway information from the router itself through "Router Advertisement" using "Neighbor Discovery Protocol".

This completely differs from the way IPv4 works. There is something called DHCPv6 that is suppose to work as DHCP for ipv6, but lacks lots of features. Most likely because of the way IPv6 is suppose to work.

However, many of us wants to be able to assign IPv6 to our clients just the same way as we would with IPv4 and DHCP. This is possible, but requires a bit of tinkering.

The first thing you have to do is to add 3 custom OPTION parameters to your DHCP server.

If you are using the ISC DHCP server in Linux, your config file should look something like this:

authoritative;
default-lease-time 86400;
max-lease-time 86400;
log-facility local7;
use-host-decl-names on;

option ipv6-address code 214 = ip6-address;
option ipv6-netmask code 215 = unsigned integer 8;
option ipv6-gateway code 216 = ip6-address;

subnet 88.xxx.xxx.128 netmask 255.255.255.224 {
  option domain-name "example.com";
  option domain-name-servers 8.8.8.8, 8.8.4.4;
  option routers 88.xxx.xxx.128;
  option subnet-mask 255.255.255.255;
  option ipv6-netmask 112;
}

host ip-88-xxx-xxx-129.example.com {
  hardware ethernet 16:xx:xx:xx:b3:8d;
  fixed-address 88.xxx.xxx.129;
  option host-name "ip-88-xxx-xxx-129.example.com";
  option ipv6-address 2a01:xxx:xxx:2464::1:1;
  option ipv6-gateway 2a01:xxx:xxx:2464::1:0;
}

host ip-88-xxx-xxx-130.example.com {
  hardware ethernet 16:xx:xx:xx:af:aa;
  fixed-address 88.xxx.xxx.130;
  option host-name "ip-88-xxx-xxx-130.example.com";
  option ipv6-address 2a01:xxx:xxx:2464::2:1;
  option ipv6-gateway 2a01:xxx:xx:2464::2:0;
}

host ip-88-xxx-xxx-131.example.com {
  hardware ethernet 16:xx:xx:xx:7a:73;
  fixed-address 88.xxx.xxx.131;
  option host-name "ip-88-xxx-xxx-131.example.com";
  option ipv6-address 2a01:xxx:xxx:2464::3:1;
  option ipv6-gateway 2a01:xxx:xxx:2464::3:0;
}

As you can see, I have added 3 new OPTIONS called "ipv6-address", "ipv6-netmask" and "ipv6-gateway". You set them the same way as you would normally do with IPv4.

Don't forget to restart the DHCP server after making the changes.

Now, the clients needs to be told to include these 3 custom OPTIONS when they are doing a DHCP-request.

This differs depending on what DHCP-client you are using. Debian/Ubuntu is using "dhclient" and it's configuration needs to look something like this:

option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;

option ipv6-address code 214 = ip6-address;
option ipv6-netmask code 215 = unsigned integer 8;
option ipv6-gateway code 216 = ip6-address;

request subnet-mask, broadcast-address, time-offset, routers,
        domain-name, domain-name-servers, domain-search, host-name,
        netbios-name-servers, netbios-scope, interface-mtu,
        rfc3442-classless-static-routes, ntp-servers,
        dhcp6.domain-search, dhcp6.fqdn,
        dhcp6.name-servers, dhcp6.sntp-servers,
        ipv6-address, ipv6-netmask, ipv6-gateway;

As you can see, I have added the 3 custom OPTION definitions at the top and then told DHCP to include them in the request.

Now try to restart your client and see if it still gets an IPv4 correctly like before (as it should).

After the reboot, you can take a look at the lease-file that DHCP had created. It contains the information gathered from the DHCP-server. On a Debian/Ubuntu system, it can be found in "/var/lib/dhcp/dhclient.eth0.leases". If you look in the file, you should see something like this:

lease {
  interface "eth0";
  fixed-address 88.xxx.xxx.136;
  option subnet-mask 255.255.255.255;
  option routers 88.xxx.xxx.128;
  option dhcp-lease-time 86400;
  option dhcp-message-type 5;
  option domain-name-servers 8.8.8.8,8.8.4.4;
  option dhcp-server-identifier 88.xxx.xxx.128;
  option ipv6-address 2a01:xxx:xxx:2464::8:1;
  option ipv6-netmask 112;
  option host-name "ip-88-xxx-xxx-136.example.com";
  option ipv6-gateway 2a01:xxx:xxx:2464::8:0;
  option domain-name "example.com";
  renew 6 2013/08/03 23:32:53;
  rebind 0 2013/08/04 09:04:30;
  expire 0 2013/08/04 12:04:30;
}

As you can see, the 3 custom OPTIONs has found their way to the client correctly.

Now, the last thing you need to do is to add a script that uses these values to setup your ipv6 interface.

dhclient is using a special hook-system where it runs scripts durring certain stages. We need to make it run when a response from the DHCP-server is recieved.

Create a file at "/etc/dhcp/dhclient-exit-hooks.d/ipv6" with the following contents:

#**************************************
# This script sets ipv6 based on custom options
#**************************************

# To enable this script set the following variable to "yes"
RUN="yes"

if [ "$RUN" != "yes" ]; then
    exit 0
fi

if [ "$reason" != "BOUND" ]; then
    exit 0
fi

if [ ! -n "$new_ipv6_address" ] || [ ! -n "$new_ipv6_netmask" ] || [ ! -n "$new_ipv6_gateway" ]; then
    exit 0
fi

ip addr add $new_ipv6_address/$new_ipv6_netmask dev $interface

ip -6 route add default via $new_ipv6_gateway dev $interface

Now reboot your client and watch the magic happen!

UPDATE 1: Moved the ipv6 from "ifup.d" to run at the DHCP-bound instead to make it cleaner.