The Missing Bit

Configuring IPv6 with DHCP-PD on OpenBSD
2022-03-15

openbsd network

In this post I will share how I configured my OpenBSD router for IPv6 with DHCP-PD.

Introduction

My ISP provide IPv6 with a static DHCP-PD setup.

This means the ISP router delegate IPv6 prefixes on demand to internal routers.

It took me some time to find the good combinaison of configuration on OpenBSD to get everything working.

Overview

IPv6 is unlike IPv4 in the sense that routing is mostly auto-configured using the "IPv6 Neighbor Discovery Protocol".

This is an ICMP6 based protocol. I won't explain here how it works, you can find that on the net, but I'll just explain what is required for it to work in this particular setup.

What is important, is that NDP doesn't provide a specific IP to a device, instead the device pick the network prefix and happen the address it sees fit behind (can be mac address or something else).

In many environment, this is not desirable and DHCP is used to attribue an additional IP (this is different from IPv4) that has been selected for the device.

I won't linger more and just share the configuration.

Configuration

wan is connected to the ISP router and lan to my local network.

Interfaces

/etc/hostname.lan

In this file, no inet6 configuration is required, dhcp will configure it.

/etc/hostname.wan

In this file, just add a simple ipv6 auto configuration entry.

inet6 autoconf

PF

/etc/pf.conf

It requires a few important rules


# IPv6 REQUIRES ICMP to work, yes, some icmp packet type are not required
# but it is simpler to just allow it
# If you want to be more restrictive, start with that and add exception
# later on
pass on any inet6 proto icmp6 all

# Allow the ISP router to send us DHCP packets
pass in on $wan inet6 proto udp from fe80::/10 port dhcpv6-server \
  to fe80::/10 port dhcpv6-client no state

DHCPCD

Dhcpcd will be the dhcpv6 client:

Install the package:

pkg_add dhcpcd

Modify the start script:

/etc/rc.d/dhcpcd

Changes the flags:

daemon_flags="-Mq -C resolv.conf -c /etc/dhcpcd_up.sh"

This will prevent resolv.conf from being touched and call our own up hook:

/etc/dhcpcd_up.sh

In this file, you can put the following:

route sourceaddr -inet6 <the static dhcp address your router gets>

This will force the router to use the DHCP given address (which, while being DHCP is actually static) for outbound packets.

Of course you also need to configure /etc/resolv.conf manually

And finally:

/etc/dhcpcd.conf


noipv4
noipv4ll
noipv6rs

allowinterfaces lan wan

interface wan
  ia_na 1
  ia_pd 2 lan/0

This will request a prefix from the server and allocate it to the lan interface.

At this stage, the wan interface should be fully configured. The lan interface should have an IP but not act as a router yet.

You notice there is noipv6rs in the configuration as we use the kernel autoconfiguration above (in /etc/hostname.wan). It is important to do it this way otherwise dhcpcd will segfault.

DHCPD v6

Now we need to run a dhcpd 6 server and router advertisement on the lan interface.

For this, we install the isc-dhcp-server package.

pkg_add isc-dhcp-server

And configure it:

/etc/rc.d/isc_dhcpd6 (create it)


#!/bin/ksh

daemon="/usr/local/sbin/dhcpd"
daemon_flags="-6 -cf /etc/dhcpd6.conf -user _isc-dhcp -group _isc-dhcp"

. /etc/rc.d/rc.subr

rc_reload=NO

rc_pre() {
	mkdir -p /var/db/dhcp/
	touch /var/db/dhcp/dhcpd6.leases
}

rc_cmd $1

/etc/dhcp6.conf (create it)


# IPv6 address valid lifetime
#  (at the end the address is no longer usable by the client)
#  (set to 30 days, the usual IPv6 default)
default-lease-time 2592000;

# IPv6 address preferred lifetime
#  (at the end the address is deprecated, i.e., the client should use
#   other addresses for new connections)
#  (set to 7 days, the	usual IPv6 default)
preferred-lifetime 604800;

dhcpv6-lease-file-name "/var/db/dhcp/dhcpd6.leases";


# T1, the delay before Renew
#  (default is 1/2 preferred lifetime)
#  (set to 1 hour)
option dhcp-renewal-time 3600;

# T2, the delay before Rebind (if Renews failed)
#  (default is 3/4 preferred lifetime)
#  (set to 2 hours)
option dhcp-rebinding-time 7200;

# Enable RFC 5007 support (same than for DHCPv4)
allow leasequery;

# Global definitions for name server address(es) and domain search list
option dhcp6.name-servers <dns ipv6>;
option dhcp6.domain-search "example.com", "example2.com";

# Set preference to 255 (maximum) in order to avoid waiting for
# additional servers when there is only one
option dhcp6.preference 255;

# Server side command to enable rapid-commit (2 packet exchange)
option dhcp6.rapid-commit;

# The delay before information-request refresh
#  (minimum is 10 minutes, maximum one day, default is to not refresh)
#  (set to 6 hours)
option dhcp6.info-refresh-time 21600;

subnet6 <delegated prefix>::/64 {
	range6 <delegated prefix>::1000 <delegated prefix>::ffff;
}

RAD

Finally, enable the router advertisement daemon:

/etc/rad.conf


managed address configuration yes
other configuration yes

dns {
  nameserver {
	<dns ip>
  }
}


interface lan

NOTE: The order of the configuration in this file is important. If you do not put managed first, the router advertisement packets will not have the managed flag set, which in turn will not trigger dhcpv6 requests from clients.

Finish notes

Of course, don't forget to enable all those daemon with rcctl and start them.

Also disable dhcpleased and any other dhcp client/server you might have running. If you want to have ipv4 dhcp, also use isc-dhcp with an ipv4 configuration, but other dhcp server/client might break the ipv6 configuration.

If you wish to comment or discuss this post, just mention me on Mastodon or Twitter.