My network is running IPv6 with SLAAC and the JunOS version on my firewall does not support RDNSS, so I needed a full-featured DHCP server to hand out DNS information.
I already have a Raspberry Pi running isc-dhcp-server for my IPv4 network so I thought it would be simple to add IPv6 support. Unfortunately it was not – the ISC DHCP server does not support dual-stack natively so you need to run two independent services in parallel.
The official documentation for this is very limited and outdated, so it took a lot of blogs and forum posts to get it working, so perhaps this post will help others in their quest for DHCPv6 support 🙂
I am assuming that you have already installed and configured the DHCP server for IPv4. If not, you can do so by entering the following command.
pi@pi ~ $ sudo apt-get install isc-dhcp-server
After installation, you can start the service manually, or reboot.
pi@pi ~ $ sudo service isc-dhcp-server start [ ok ] Starting ISC DHCP server: dhcpd.
This post focuses on the DHCPv6 part. For the actual configuration for standard DHCP, there are plenty of good guides available online.
Configuring a static IPv6 address
In case you haven’t done so, be sure to configure your device with a static IPv6 address.
pi@pi ~ $ cat /etc/network/interfaces # iface eth0 inet dhcp # Loopback interface auto lo iface lo inet loopback auto eth0 iface eth0 inet static # Static IPv4 address 10.6.0.36 gateway 10.6.0.1 network 10.6.0.0 netmask 255.255.255.0 broadcast 10.6.0.255 # Static IPv6 iface eth0 inet6 static address 2a02:1234:420a:100b::10 netmask 64 gateway 2a02:1234:420a:100b::1
Restart the network interface or reboot the device to apply it.
INIT file – /etc/init.d/isc-dhcp-server6
First step is to copy the working init script, which is used by the v4 server, to a new file.
pi@pi ~ $ sudo cp /etc/init.d/isc-dhcp-server /etc/init.d/isc-dhcp-server6
This will create a new file isc-dhcp-server6 – use nano or vi to edit it.
Some of the values in the ### INIT INFO ### section need to modified to represent the new dhcpv6 service. I’ve also modified the value for the default config file, DHCPD_DEFAULT, to be /etc/default/isc-dhcp-server6 – more on that file later.
The script below shows only the first part of the script so don’t copy-paste.
#!/bin/sh # # ### BEGIN INIT INFO # Provides: isc-dhcp-server6 # Required-Start: $remote_fs $network $syslog # Required-Stop: $remote_fs $network $syslog # Should-Start: $local_fs slapd $named # Should-Stop: $local_fs slapd # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: DHCPv6 server # Description: Dynamic Host Configuration Protocol Server for IPv6 ### END INIT INFO PATH=/sbin:/bin:/usr/sbin:/usr/bin test -f /usr/sbin/dhcpd || exit 0 DHCPD_DEFAULT="${DHCPD_DEFAULT:-/etc/default/isc-dhcp-server6}" # It is not safe to start if we don't have a default configuration... if [ ! -f "$DHCPD_DEFAULT" ]; then echo "$DHCPD_DEFAULT does not exist! - Aborting..." if [ "$DHCPD_DEFAULT" = "/etc/default/isc-dhcp-server" ]; then echo "Run 'dpkg-reconfigure isc-dhcp-server' to fix the problem." fi exit 0 fi . /lib/lsb/init-functions # Read init script configuration [ -f "$DHCPD_DEFAULT" ] && . "$DHCPD_DEFAULT" NAME=dhcpd DESC="ISC DHCPv6 server" # fallback to default config file DHCPD_CONF=${DHCPD_CONF:-/etc/dhcp/dhcpd6.conf} # try to read pid file name from config file, with fallback to /var/run/dhcpd.pid if [ -z "$DHCPD_PID" ]; then DHCPD_PID=$(sed -n -e 's/^[ \t]*pid-file-name[ \t]*"(.*)"[ \t]*;.*$/\1/p' < "$DHCPD_CONF" 2>/dev/null | head -n 1) fi DHCPD_PID="${DHCPD_PID:-/var/run/dhcpd.pid}" ... rest of script omitted for brevity ...
Defaults file – /etc/default/isc-dhcp-server6
This file stores the values used by the INIT script, so it’s absolutely essential that they are correct, or the service will refuse to start or will not work as expected.
Again, you can copy the existing file and modify those parameters.
sudo cp /etc/default/isc-dhcp-server /etc/default/isc-dhcp-server6
The configuration file for this example has four different values.
- DHCPD_CONF – this value identifies the configuration file which will hold our DHCPv6 attributes.
- DHCPD_PID – when the service is started, the process ID for the service will be written to this file
- OPTIONS – here we need to specify the server to run in v6 mode by using the -6 knob
- INTERFACES – the interface(s) on which the DHCP server will be listening, in my case eth0
# Defaults for isc-dhcp-server6 initscript # sourced by /etc/init.d/isc-dhcp-server6 # installed at /etc/default/isc-dhcp-server6 by the maintainer scripts # # This is a POSIX shell fragment # # Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf). DHCPD_CONF=/etc/dhcp/dhcpd6.conf # Path to dhcpd's PID file (default: /var/run/dhcpd.pid). DHCPD_PID=/var/run/dhcpd6.pid # Additional options to start dhcpd with. # Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead OPTIONS="-6" # On what interfaces should the DHCP server (dhcpd) serve DHCP requests? # Separate multiple interfaces with spaces, e.g. "eth0 eth1". INTERFACES="eth0"
Config file – /etc/dhcp/dhcpd6.conf
This file, just like in the IPv4 version, contains the global DHCP options and the subnet statements, reservations, etc. I am using it in conjunction with SLAAC, so DHCPv6 is only used to hand out the DNS info.
Note – For IPv6 subnets, you need to use the subnet6 statement.
# The ddns-updates-style parameter controls whether or not the server will # attempt to do a DNS update when a lease is confirmed. ddns-update-style none; # Option definitions common to all supported networks... default-lease-time 600; max-lease-time 7200; # This DHCP server is the official DHCP server for the local network authoritative; # Use this to send dhcp log messages to a different log file (you also # have to hack syslog.conf to complete the redirection). log-facility local7; # Subnet declaration subnet6 2a02:1234:420a:112d::/64 { option dhcp6.name-servers 2a02:1234:420a:100b::10, 2001:4860:4860::8888; option dhcp6.domain-search "netprobe.local"; } subnet6 2a02:1234:420a:100b::/64 { option dhcp6.name-servers 2a02:1234:420a:100b::10, 2001:4860:4860::8888; option dhcp6.domain-search "netprobe.local"; }
The DHCP6 leases file
It’s possible the service will not start because this file does not exist:
Dec 5 21:37:00 pi dhcpd: Can't open lease database /var/lib/dhcp/dhcpd6.leases: No such file or directory -- Dec 5 21:37:00 pi dhcpd: check for failed database rewrite attempt!
Just create an empty file and it should be OK.
pi@pi / $ sudo touch /var/lib/dhcp/dhcpd6.leases
Starting the Service and verifying it
If the configuration files are correct, we can now manually start the service.
pi@pi /etc/default $ sudo service isc-dhcp-server6 start [ ok ] Starting ISC DHCPv6 server: dhcpd.
If you are getting an error, you will usually find a clue in the /var/log/syslog files…
Verify that the second service is now running:
pi@pi /etc/default $ ps aux | grep dhcp root 2091 0.0 1.2 6824 5368 ? Ss 20:52 0:00 /usr/sbin/dhcpd -q -cf /etc/dhcp/dhcpd.conf -pf /var/run/dhcpd.pid eth0 root 2751 0.0 0.6 5484 2868 ? Ss 21:18 0:00 /usr/sbin/dhcpd -q -6 -cf /etc/dhcp/dhcpd6.conf -pf /var/run/dhcpd6.pid eth0 pi 2772 0.0 0.4 3572 1944 pts/0 S+ 21:19 0:00 grep --color=auto dhcp
To validate the process ID as written at service start time.
pi@pi /etc/default $ cat /var/run/dhcpd6.pid 2751
To verify that the service is listening on UDP port 547:
pi@pi /etc/default $ netstat -an | grep 547 udp6 0 0 :::547 :::*
If you have your DHCP relays set up correctly, you can also verify communication with tcpdump. Here we can see a client is requesting information from DHCP.
pi@pi /etc/default $ sudo tcpdump -i eth0 port 547 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes 21:21:36.935479 IP6 2a02:1234:420a:112d::1.dhcpv6-server > 2a02:1234:420a:100b::10.dhcpv6-server: dhcp6 relay-fwd 21:21:36.942854 IP6 2a02:1234:420a:100b::10.dhcpv6-server > 2a02:1234:420a:112d::1.dhcpv6-server: dhcp6 relay-reply 21:21:36.951406 IP6 2a02:1234:420a:112d::1.dhcpv6-server > 2a02:1234:420a:100b::10.dhcpv6-server: dhcp6 relay-fwd 21:21:36.955437 IP6 2a02:1234:420a:100b::10.dhcpv6-server > 2a02:1234:420a:112d::1.dhcpv6-server: dhcp6 relay-reply 21:22:16.092006 IP6 2a02:1234:420a:112d::1.dhcpv6-server > 2a02:1234:420a:100b::10.dhcpv6-server: dhcp6 relay-fwd 21:22:16.094353 IP6 2a02:1234:420a:100b::10.dhcpv6-server > 2a02:1234:420a:112d::1.dhcpv6-server: dhcp6 relay-reply 21:22:16.169193 IP6 2a02:1234:420a:112d::1.dhcpv6-server > 2a02:1234:420a:100b::10.dhcpv6-server: dhcp6 relay-fwd 21:22:16.171748 IP6 2a02:1234:420a:100b::10.dhcpv6-server > 2a02:1234:420a:112d::1.dhcpv6-server: dhcp6 relay-reply
Auto-starting the service at boot
To add the new service to the initialization scripts, enter the following command.
pi@pi ~ $ sudo update-rc.d isc-dhcp-server6 defaults update-rc.d: using dependency based boot sequencing pi@pi ~ $
After a reboot, you should again have two running instances of the dhcpd service – one with the -6 option.
pi@pi ~ $ ps aux | grep dhcp root 2100 0.0 1.1 6824 5120 ? Ss 21:30 0:00 /usr/sbin/dhcpd -q -cf /etc/dhcp/dhcpd.conf -pf /var/run/dhcpd.pid eth0 root 2135 0.0 0.6 5484 3048 ? Ss 21:30 0:00 /usr/sbin/dhcpd -q -6 -cf /etc/dhcp/dhcpd6.conf -pf /var/run/dhcpd6.pid eth0 pi 2356 0.0 0.4 3568 1836 pts/0 S+ 21:32 0:00 grep --color=auto dhcp
If everything went well, your clients should now be picking up the DNS servers (or other DHCPv6 information) from your new DHCPv6 server.
If this guide does not work for you, please let me know in the comments!
Hello,
How do i set a a static IPv6 adress for a client?
Hi Simon,
Thanks for the detailed post. It worked like a charm.
One question I have is, how to set ntp-servers option in the dhcpv6 case?
I want know both client and server configuration.
I tried this in “/etc/dhcp/dhclient.conf”,
====================
option dhcp6.use-ntp code 56 = unsigned integer 16;
request dhcp6.use-ntp;
=====================
then client started sending the option 56 in the request packet, but server is not offering the ntp-servers in the reply message.
For example, try:
“`ip addr add 2001:db8:1::1/48 dev eth0“`
(per https://kb.isc.org/docs/aa-00368)