How do DUID/IAIDs work? (DHCP)

I believe modern x86 boards all have a UUID (see ansible_product_uuid ),

I’m not sure that’s smart, like what happens with VMs and containers and jails and stuff.


VMs have actual good UUIDs as far as I saw but typically have serial numbers like 12345678. That’s why I’m hashing all of that stuff.

Not sure about jails but I also don’t use them.

Also, I’m not smart enough to know what the result of this code would be. If I was to manipulate the UUID as a string, what characters am I putting where?

Looks like the version goes in the middle, so:


That can’t be right, there are only 4 versions…

Oh I misread it. I believe it’s this:


The a can be between 8 and b (inclusive) for variant 1. Version 4, variant 1 is also what comes out of uuidgen.

1 Like

Ok, so UUID-compliant DUID generated by Ansible:

  - ansible.builtin.set_fact:
      iface: "{{  iface
                  | combine( { 'duid': duid_var } ) }}"
      sys_hash_var: "{{ ( ansible_system_vendor
                          + ansible_product_name
                          + ansible_product_version
                          + ansible_product_uuid
                          + ansible_product_serial )
                        | hash('sha256') }}"
      uuid_var: "{{ sys_hash_var[:12] | string
                    + '4'
                    + sys_hash_var[13:16] | string
                    + ['8', '9', 'a', 'b'] | random | string
                    + sys_hash_var[17:32] | string }}"
      duid_var: "{{ ( '0004'
                      + uuid_var | string )
                    | regex_findall('..')
                    | join(':') }}"

And of course, on macOS, Ansible doesn’t pull those system variables, so I’ll need to pull something out of system_profiler for the initial hash on macOS, but nbd.

DUID is type 4
UUID is type 4, variant 1

1 Like

I think the fact that UUID allots a single hex digit to versioning while DUID allots 2 entire octets while they both have only 4 versions speaks to their respective sanity.

1 Like

Thank you @PhaseLockedLoop and @risk!

missionaccomplished.jpg's all around

1 Like

Sin problema. You were at the end of my deeper knowledge. I learned something too. Risk had more to say :smiley:


Oh also, @PhaseLockedLoop, I think I unintentionally figured out why I was getting weird results with hexdump earlier. If you don’t wrap it in printf (like you did), you get the little endian version. I recognized it looking at the UUID wikipedia:

The binary encoding of UUIDs varies between systems. Variant 1 UUIDs, nowadays the most common variant, are encoded in a big-endian format. For example, 00112233-4455-6677-8899-aabbccddeeff is encoded as the bytes 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff .[9][10]

Variant 2 UUIDs, historically used in Microsoft’s COM/OLE libraries, use a mixed-endian format, whereby the first three components of the UUID are little-endian, and the last two are big-endian. For example, 00112233-4455-6677-c899-aabbccddeeff is encoded as the bytes 33 22 11 00 55 44 77 66 c8 99 aa bb cc dd ee ff .[11][12] See the section on Variants for details on why the ‘88’ byte becomes ‘c8’ in Variant 2.

I can’t imagine why Microsoft would do such a thing but I saw earlier that a leading 00 was becoming 33.

I think this echos way back to when MS had a lot of Itanium servers (BE). when they were initally creating those libraries but dont quote me on their motivation.

Always wrap it in printf hehe. I think I ran into the same issue but forgot printf was my solution


I might be wrong actually, the bad hexdump command was also way longer and I see now that the encoding is just each section reversed…

1 Like

Thats how big endian works

I’ll picture my old student book when learning ARM board support package firmware programming and ARM assembly

Ahh the old 32 bit days

Applies to networking all the same

If you want a copy of this I can make one for you. I own it. I have the rights for “academic” purposes. Exploring this more in depth from the lower level… there is an important distinction to make about endianness is that it only refers to how values are stored in memory and not how we deal with values; for example, 0xdeadbeef is still 0xdeadbeef . There is no concept of endianness here. However, if we were talking about storing this 4-byte value into memory, then and only then would we have to specify endianness. However counter-intuitive it might seem at first, there are valid reasons why little-endian is used over big-endian. The reason for the widespread use of little-endian is not because of the ease of user understanding, but rather for ease of the computer. We will use this 8-byte value 0x0000000000000031 . When we store it in little-endian we have the following.

0x00: 31 00 00 00 00 00 00 00

Big would be:

0x00: 00 00 00 00 00 00 00 31

Right but lets try a down cast pointer

unsigned long long x = 0x0000000000000031;
unsigned long long * x_p = &x;
unsigned int * y_p = (unsigned int *)x_p;
unsigned int y = *y_p;
printf("y = %#.8x\n", y);

Now guess what executing that looks like

0x00: 31 00 00 00
0x04: 00 00 00 00

But the issue is. You have to change this to make it big endian

unsigned long long x = __builtin_bswap64(0x0000000000000031);
unsigned long long * x_p = &x;
unsigned int * y_p = (unsigned int *)x_p;
unsigned int y = __builtin_bswap32(*y_p);
printf("y = %#.8x\n", y);

Only then does it give you a proper big endian output

0x00: 00 00 00 00
0x04: 00 00 00 31

Anyways way off topic… sorry

1 Like

I’m good. I knew what it meant at some point but unused knowledge fades. In general, I try not to get that deep in the weeds. Never expected DHCP of all things to drag me into half a dozen RFCs. Not quite as bad as DNS, but a lot closer than I expected.

1 Like

It sure does. Its hard to keep it all current.

HAHA yeah your telling me. DNS is such a cluster fuck


Some updates here.

  1. @risk was correct about not trusting the system UUIDs on VMs (or maybe just in Linux). Despite a perfectly good UUID showing up in dmidecode, Ansible fails to capture it in ansible_product_uuid on my Linux VMs. However, it does have the systemd machine id ready to go in ansible_machine_id, so there’s enough to work with across the board for generating the DUID.

  2. Turns out OpenBSD adopted a new DHCP client daemon recently called dhcpleased which replaces dhclient. I discovered this after some frustration trying to configure dhclient.conf to no avail.

  3. Other OpenBSD note is that, like NetworkManager, dhcpleased expects the type octet at the beginning of the DUID if you pass it via client id. This is also the case for the static reservation in dhcpd.conf which looks like this:

host mycomputer {
  option dhcp-client-identifier ff:{{iaid+':'+duid}};

It was not clear in the docs that option needed to be included there. I had touted OpenBSD documentation before, but having lived with it for a while now, there are some holes…

1 Like

1 Like

Lol, i can’t believe someone made that.

1 Like

I just made that.

1 Like

Even better

1 Like

Foiled again by Poettering!


The DHCP Identity Association Identifier (IAID) for the interface, a 32-bit unsigned integer.

Why would it need to an integer?!

1 Like