My danish ISP, Kviknet, provide native ipv6. All is good in this brave new world until you remember I built my own OpenBSD router and really love it.
So I am not going to use the router they provided me (which we will call CPE, for Customer Premises Equipment, to differentiate from their network routers, which are actually bigger and more powerful).

I actually asked to set the CPE on bridge mode, that means it is acting like a modem on my point of view.

The trick is, in order to get ipv6 addresses, I have to make a specific dhcpv6 request that would correspond exactly to my ISP big routers' expectations (that means, I have to copy or impersonate the request their CPE makes). I have no idea if those routers (ZTE routers) have a generic setup or not (point being : I don't know if this article and the configuration below is valid for all ZTE routers' networks or only valid for my ISP).

When I asked My ISP for the options I needed, they provided me with a pcap file, meaning the capture of a dhcpv6 request from one of their own CPE.

First act

Here I saw that, indeed, I would not have guessed the correct configuration I need on my own.

This is the capture, analyzed with wireshark.

Let's begin the analyse. I had a vague idea of how DHCP(v4 or 6) works, but this was the first time I really had to dig in the internals, and I am not a professionnal, so I had to do a lot of deduction and guessing.

The first line indicates the router is releasing its lease. This line is useless to us, but it's the start of the process. OK.

The interesting line is actually the 9th, where the true request is made (SOLICIT). You can see line 10 that the server answers (ADVERTISE).

Here is the 9th line's packet and you see all the options the dhcpv6 client (the client's router, so the CPE) is requesting. And you see precisely what request you have to create again to pass through the ISP's routers and gain the Graal: an ipv6 prefix delegation.

So here is the first Lesson of the day : ask your ISP for their specifications.

Second act

Analyzing the capture, I saw options I have never heard of. Some options are implemented by softwares, others (the new ones) not.

I thought also that the majority of options are utterly useless to me. I run my own DNSSEC resolver and my own NTP server. Why would I ask about it (options "DNS Recursive name server (23)" and "NTP Server (56)") ? But these are usefull to ordinary people who just wish plug and play. And I have to include those in my requests to shape it perfectly so that it passes the ISP routers on the network.

So I wrote to Roy Marples, the developer of dhcpcd, about it and first asked him how to enable those options. Eventually dhcpcd had to get patched.

Notice that this step was actually a lot of "try and fail, redo again", with discussions in between with Roy Marples who provided three small patches (progressive improvement too on his side, all those patches come in the next release). I made a lot of DHCPv6 requests, captured them directly on my router, compared the capture with the one I had from Kviknet, adjusted the dhcpcd conf' and requested again. A lot failures. But each time closer to the goal.

The capture command line should be (for re0 as the internet interface on your router):

# tcpdump -i re0 -D out -n -vv ip6 and port dhcpv6-server

For GNU/Linux, it might be eth or eno.

At one moment I felt great because the "Option request" part was identical to the reference: its value (00:15:00:16:00:17:00:18:00:1f:00:38:00:40:00:43:00:52:00:53:00:5e:00:5f:00:60) was exactly the same as Kviknet's one. Meaning that part was secured. There was other parts but each step closer is good.

Second lesson : ask and discuss with the dev’, third lesson : patch or upgrade your software.

Third act : Configuration.

Here is the final dhcpcd configuration :

# Persist interface configuration when dhcpcd exits.
persistent

# I don't use those, but you can do.
nohook lookup-hostname
nohook resolv.conf
nohook ntp.conf

interface re0
    nooption dhcp6_vivco

    option dhcp6_sip_servers_names
    option dhcp6_sip_servers_addresses
    option dhcp6_name_servers
    option dhcp6_domain_search
    option dhcp6_sntp_servers
    option dhcp6_ntp_server
    option dhcp6_aftr_name
    option dhcp6_pd_exclude

    # SOL_MAX and INF_MAX should already be requested by default
    option dhcp6_sol_max_rt
    option dhcp6_inf_max_rt
    # Request 94, 95 and 96
    option dhcp6_s46_cont_mape
    option dhcp6_s46_cont_mapt
    option dhcp6_s46_cont_lw

    ia_na 1

    # re2 = ethernet interface
    # athn0 = wifi interface
    # 226 = e2 en hexa; 160 = a0 en hexa
    ia_pd 1 re2/226/64 athn0/160/64

    fqdn ptr

Here you have my configuration. It's fully functional. Of course, if you want to use it, you have to adapt to your case (interfaces names, etc). I also have my interfaces with addresses like that :

re2     $prefix48:e2::1
athn0   $prefix48:a0::1

This means that my two networks will have addresses that shows whether they are wifi or eth. Might be handy later, you never know.

Finally, a reminder, this only takes care of dhcpv6 request. It provides you with a prefix delegation for your inside network. But you still have to check your firewall, be it PF or Ip6tables. You need rules to let pass the dhcpv6 requests themselves plus all the icmp used by ipv6 for routing. And you still have to setup your router in order to forward packets properly.