Page MenuHomeVyOS Platform

dhcpv6-pd: randomly generated DUID is not persisted
Closed, ResolvedPublic

Description

The default behavior of the dhcpv6-pd client contains a bit of a footgun. If you do not specify a duid in the config, dhcp6c will generate a random DUID on first start and store it into /var/lib/dhcpv6/dhcp6c_duid.

The problem is this file does not survive image upgrades. As such, after every upgrade, a new DUID will be generated and the ISP will almost certainly issue a different prefix.

There are a few ways out of this...

  1. We could symlink this file into the /config dir (or patch dhcp6c to write there to begin with)
  2. We could require a duid to always be specified in the config, since specifying a DUID by hand avoids dealing with that file entirely

For option 2 we could implement by either auto-generating a DUID and adding it to the config for you if you commit a dhcpv6-pd node without one, or erroring out (we should probably offer a generate dhcpv6 duid or some other operational mode command to make it easy for people to come up with DUIDs).

Details

Difficulty level
Unknown (require assessment)
Version
yOS 1.4-rolling-202110020217
Why the issue appeared?
Will be filled on close
Is it a breaking change?
Perfectly compatible
Issue type
Improvement (missing useful functionality)

Event Timeline

lucasec created this object in space S1 VyOS Public.

Hi @lucasec,

what about the following:

  • Add a new system ipv6 duid CLI node which acts as the general DUID used on the system and renders /var/lib/dhcpv6/dhcp6c_duid, if not overwritten at the "interface" level set interfaces ethernet eth0 dhcpv6-options duid
  • If system ipv6 duid is not configured, we "generate" the DUID using the eth0 MAC address automatically and store it in /var/lib/dhcpv6/dhcp6c_duid - thus no issue on image upgrades anymore

That seems fair, basically make the DUID generation deterministic. There is some defined structure to the DUID format, I think this would be a "type 3 DUID" per this document: https://www.juniper.net/documentation/en_US/junose15.1/topics/concept/dhcp-unique-id-servers-clients-overview.html.

Should we expose a system-level DUID at all? If a user wants to customize it, they could always set it on a per-interface basis using the existing configuration node.

Should we expose a system-level DUID at all? If a user wants to customize it, they could always set it on a per-interface basis using the existing configuration node.

You ara actually right about that - no need to make it over complicated. It will just be a deterministic system duid with the ability to change it on the interface level.

c-po changed the task status from Open to Confirmed.Tue, Oct 5, 6:03 AM
c-po claimed this task.
c-po triaged this task as Normal priority.
c-po changed Is it a breaking change? from Config syntax change (migratable) to Perfectly compatible.
c-po edited projects, added VyOS 1.3 Equuleus (1.3.0-epa2); removed VyOS 1.3 Equuleus.

Yeah, that seems reasonable to me. I would prefer not add clutter to the system node if it can be avoided.

Reading a bit further https://datatracker.ietf.org/doc/html/rfc8415#section-11 has a lot of details on the format of DUIDs. I suppose the strategy of generating a type 3 from eth0 wouldn't quite be following the format to the letter since "A DUID-LL SHOULD NOT be used by DHCP clients or servers that cannot tell whether or not a network interface is permanently attached to the device", but I would suspect most of us are not hotplugging NICs all the time. Still feels better than the current behavior and if someone comes along later and finds edge cases where it causes problems in, say, a virtualized application then a more complicated solution involving persistence could be implemented then.

Only other thing that could be nice is an op mode command to generate new DUIDs to paste into the config for the per-interface use case. Especially if we already have the code for generating the system default file. Maybe something like:

generate dhcpv6 duid type 1 interface eth0   # good sane default if the user wants some value to persist
generate dhcpv6 duid                         # logical alias for the above ^
generate dhcpv6 duid type 3 interface eth0   # equivalent to what is used to generate system default

None of the other types would be sensible to use in VyOS.

The DUID is presented in binary inside /var/lib/dhcpv6/dhcp6c_duid to read it back into ASCII use: hexdump -e '"%07.7_ax " 1/2 "%04x" " " 14/1 "%02x:" "\n"' /var/lib/dhcpv6/dhcp6c_duid

To convert from ASCII to a binary DUID use:

echo 00:01:00:01:28:e4:c1:2f:00:50:56:bf:c5:6d | awk '{ gsub(":"," "); printf "0: 0a 00 %s\n", $0 }' | xxd -r > /var/lib/dhcpv6/dhcp6c_duid

4.  DUID-UUID Format

   The DUID-UUID is carried within Client Identifier or Server
   Identifier options.  It has the following format:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          DUID-Type (4)        |    UUID (128 bits)            |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                               |
   |                                                               |
   |                                                               |
   |                                -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                                |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-

                        Figure 1: DUID-UUID Format

   DUID-Type -  DUID-UUID (4) - (16 bits)

   UUID -  An [RFC4122] UUID (128 bits)

The code to generate a static - per system Type4 DUID is:

#!/bin/bash

# Retrieve the system boards UUID number to form a Type4 DUID (UUID based)
UUID=$(cat /sys/class/dmi/id/product_uuid | tr -d -)
# Add DUID-Type 4 (UUID) information
DUID_TYPE4="0004"
DUID=$(echo -n ${DUID_TYPE4}${UUID} | sed 's/../&:/g; s/:$//')
echo -n $DUID | awk '{ gsub(":"," "); printf "0: 0a 00 %s\n", $0 }' | xxd -r
c-po moved this task from Need Triage to Finished on the VyOS 1.3 Equuleus (1.3.0-epa2) board.
c-po moved this task from Backlog to Finished on the VyOS 1.4 Sagitta board.

I surveyed all the hardware I have to see what kind of UUIDs they report:

  • Supermicro A2SDI-TP8F: 00000000-0000-0000-0000-ac1f6bb016a4 (equivalent to MAC of first onboard LAN)
  • Lanner NCA-1510b: 03000200-0400-0500-0006-000700080009 (obviously a garbage value)
  • AWS EC2 t3.micro: ec214f77-ede9-e424-e658-6dfc9b7f00fc (likely randomly-assigned value)
  • VirtualBox VM: 661d927d-92c4-bf4a-be03-c084548d798f (likely randomly-assigned value)

The Lanner platform is a bit disappointing. Otherwise feels like we get fairly good guarantees of uniqueness everywhere else.

@lucasec the reason for switching to the platform UUID instead of building up out own one was that it was not "unique".

The idea was to use a mixture of CPU-ID + BLKID of the VyOS partition + the first MAC. The UUID would have been changed on CPU upgrade, Maybe some future partition re-layouting or exchange of the NICs - thus the BIOS/UEFI UUID value seemed to be the only logical choice. Thanks for also looking around and helping on this!

Obviously in a perfect world we get "unique" and "stable". I do think giving stability priority makes sense.

And again the UUID format should also serve well for people that want to do a PXE boot. I also checked some settings for the BIOS on my SuperMicro board and at least when using the UEFI network drivers it defaults to using UUID as the DUID. Pretty cool!

I don't really know how we could deal with devices like the Lanner board. If I had to guess, all of their boards probably report 03000200-0400-0500-0006-000700080009 as the UUID so as soon as two of these devices coexist on a network there would be conflicts. I don't have a good solution for this though, other than building a "blocklist" of known garbage UUIDs and switching to another method when those are detected. That feels like it would be a pain to maintain.