During the last weeks, I had an interesting request to publish NTP servers to client systems by using DHCPv6 in an IPv6 only network. Our Fortigate (or me?) had to learn how to publish the information. Hence this post is not only about NTP and IPv6, but a small guide on how to walk through RFCs and how to get out the relevant information. I’m very happy I got the possibility to share my experience here. Thank you, Johannes!
It started with a small question from my co-worker: “Can you send our NTP servers with DHCP in the IPv6 only network?” And my first idea was: “I’m sure, I can configure this within of minutes.” But 10 minutes later I had to change my mind: it takes me more time to understand how I have to configure this. The simple reason for this is the missing option in the system configuration, the vendor didn’t implement (yet)…
I will show how to manage this with a FortiGate (FortiOS 6.0.14, there is no newer version available for this model) operating as a DHCPv6 server. At the time of beginning, the DHCP6 server configuration looks like this:
config system dhcp6 server edit 641 set rapid-commit enable set lease-time 3602 set domain "example.com"IP set subnet 2001:db8:c:641::/64 set interface "IPv6only-01" config ip-range edit 1 set start-ip 2001:db8:c:641::6401 set end-ip 2001:db8:c:641::64ff next end set dns-server1 2001:db8:c:a53::53 set dns-server2 2001:db8:c:b53::53 next end
As you can see, the DHCPv6 server is configured to publish a domain name and two DNS servers for recursive name resolution. Further on there’s an IPv6 range for stateful DHCPv6. But FortiOS didn’t allow me to configure the NTP server directly. But it allows me to send three self-defined options in DHCPv6:
config system dhcp6 server edit < id > set option1 {string} Option 1. set option2 {string} Option 2. set option3 {string} Option 3. next end
Could this be the way to fulfil my co-worker’s request? And how to create the {string} to send an IPv6 address or hostname to the client system? Having a look at the CLI of the FortiGate:
config system dhcp6 server edit < id > (server) # set option1 ? < option-code > [< options >] options must be even number of hexadecimal characters(optional)
This information didn’t help me. Now it looks like nothing else than reading RFCs helps. :-(
Digging through RFCs
In the list of FortiOS 6.0 supported RFCs, RFC 3315 is mentioned. But in RFC 3315 is no useful information about NTP… Reading the new RFC for DHCPv6, RFC 8415, you can find in section 24 a list of DHCPv6 options and the link to the complete list of DHCPv6 parameters assigned by IANA. Looks like that’s the information I need.
There is an option with the name “NTP Server”, number 56 (decimal), but the information about the order and possible values are somewhere else: in RFC 5908.
In RFC typical style there (RFC 5908 on page 4) you can find the required format of the option to send NTP-server information to the client system:
The format of the NTP Server Option is: 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | OPTION_NTP_SERVER | option-len | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | suboption-1 | : : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | suboption-2 | : : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | suboption-n | : : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ option-code: OPTION_NTP_SERVER (56), option-len: Total length of the included suboptions.
For the ones who never had the need to read something like this, a few hints which helped me:
- The headline of the “table” are the bits, starting from 00 to 31 = 32bit.
- The field OPTION_NTP_SERVER is a 16-bit value and the field option-len is a 16bit value, too.
- The next fields suboption-1, suboption-2 to suboption-n has, at this point, no fixed length; but at least 32bit. This is because there is a pipe character on the left and right side, that indicates: this line is required. The next line, which has a colon at both sides, tells us it has a variable length, 0 or more lines.
To find the next piece of the puzzle, we continue reading the next page in RFC 5908 (page 5). Here we find how to format the sub-options:
The format of the NTP Server Address Suboption is:
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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NTP_SUBOPTION_SRV_ADDR | suboption-len = 16 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | | | IPv6 address of NTP server | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ IPv6 address of the NTP server: An IPv6 address, suboption-code: NTP_SUBOPTION_SRV_ADDR (1), suboption-len: 16.
Next hint:
- The field NTP_SUBOPTION_SRV_ADDR is a 16-bit value and the field suboption-len is a 16bit value, too.
- The next field (IPv6 address of NTP server) has a fixed length of 128 bit (4 lines with 32 bit). I hope you already expected this length. :-)
OK, we try to understand the information we have until now: We have to set the option number for the NTP server, which is 56 (dec) and 0x38 (hex). The NTP_SUBOPTION_SRV_ADDR has a sub-option code of 1 (0x0001 as 16bit hex value) and a suboption-length of 16 octets (if it is easier for you: 16 bytes). Then we have to put in the IPv6 (unicast-) address of the server. We take 2001:0db8:2800:0000:0000:0000:0ac3:0123 as an example. In the end, we calculate the complete length of the option and put the value in the field option-len:
- 2 octets for the NTP suboption field
- 2 octets for the NTP suboption length
- 16 octets for the NTP IPv6 server address
In summary 20 octets (0x14 in hex) is the calculated value for the field option-len. If we put in all the data, the fields look like this:
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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | OPTION_NTP_SERVER = 0x0038 | option-len = 0x0014 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |SUBOPTION_NTP_SRV_ADDR = 0x0001| suboption-len = 16dec = 0x0010| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 2001:0db8: | | 2800:0000: | | 0000:0000: IPv6 address of NTP server | | 0ac3:0123 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
But in FortiOS this is different than expected (by me?): we don’t have to calculate and put in the option-length here! We just need the option code (in decimal = 56) and the option value in hexadecimal characters as in the following line:
(server) # set option1 56 '0001001020010db828000000000000000ac30123' NTP_SUBOPTION_SRV_ADDR | | suboption-lenght | IPv6 unicast address of NTP server
It took me a few hours with Wireshark to realize this…
Publishing FQDNs
My co-worker was quite happy about my phone call with the good news. But: is publishing an IPv6 unicast address a good solution for the future? I decided no, I want to publish an FQDN.
Now, as I did understand the format of the NTP-server option, I had a look for how to publish the name of the NTP server. For this we have to read section 4.3 on page 6 of RFC 5908. The format of the NTP Server FQDN Suboption is:
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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | NTP_SUBOPTION_SRV_FQDN | suboption-len | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | FQDN of NTP server | : : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ suboption-code: NTP_SUBOPTION_SRV_FQDN (3), suboption-len: Length of the included FQDN field, FQDN: Fully-Qualified Domain Name of the NTP server or SNTP server. This field MUST be encoded as described in [RFC3315], Section 8. Internationalized domain names are not allowed in this field.
Let’s have a try to send the hostname 3.de.pool.ntp.org to the client systems.
The FQDN must be encoded as described in section 8 of RFC 3315. The few lines there tell us to read section 3.1 of RFC 1035 but MUST NOT use the compressed form as described in section 4.1.4 of RFC 1035. And we MUST NOT use an IDN (internationalized domain name)!
This is how encode the hostname 3.de.pool.ntp.org based on section 3.1 in RFC 1035: First of all, we have to specify the length of the next part of the hostname (also called: label):
- one character which results in value 1 (0x01) followed by the ascii-code of the character 3: 0x33.
- Repeat this for the next label: length of 0x02 followed by 0x6465 for de
- For the next label: length (4 octets) and the word pool results in data 0x04706f6f6c
- And so on: length (3 octets) and data are 0x036e7470 for ntp
- One time similar again: length (3 octets) and data are 0x036f7267 for org
- And now, very important: notification for end of hostname: 0x00
I repeat the whole hexadecimal characters (separated with space characters for a better understanding):
01 33 02 6465 04 706f6f6c 03 6e7470 03 6f7267 00 3 . d e . p o o l . n t p . o r g
This is the FQDN for the NTP suboption. Now we have to calculate the length of the suboption: 19 octets, 0x13 in hex.
My line for configuring the hostname 3.de.pool.ntp.org in FortiOS looks is this one:
(server) # set option2 56 '00030013013302646504706f6f6c036e7470036f726700' suboption type = FQDN | | suboption length = 19dec = 0x0013 | FQDN as described a few lines above
Do you want to publish two FQDNs? For example 2.de.pool.ntp.org and 3.de.pool.ntp.org? No problem, just do it:
(server) # set option2 56 '00030026013202646504706f6f6c036e7470036f726700013302646504706f6f6c036e7470036f726700'
But this wasn’t a good solution for my co-worker. He was not able to configure his client with the information I sent by DHCPv6. (Please don’t ask me for details!) Next, he asked me to send the SNTP address.
What about SNTP?
A fast search and I found the next interesting RFC with the name Simple Network Time Protocol (SNTP) Configuration Option for DHCPv6: RFC 4075. Looking on page 2:
The format of the Simple Network Time Protocol servers option is as shown below: 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | OPTION_SNTP_SERVERS | option-len | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | SNTP server (IPv6 address) | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | | | SNTP server (IPv6 address) | | | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : ... : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ option-code: OPTION_SNTP_SERVERS (31) option-len: Length of the 'SNTP server' fields, in octets; it must be a multiple of 16 SNTP server: IPv6 address of SNTP server
SNTP is option code 31, FortiOS is calculating the option length itself, we just need the IPv6 unicast (only unicast?) address of the SNTP server
(server) # set option3 31 '20010db828000000000000000ac30123'
Do you want to publish two SNTP servers? No problem, do it like this:
(server) # set option3 31 '20010db828000000000000000ac2012320010db828000000000000000ac30123'
FortiOS and RouterOS Configuration
Today, the DHCP6 server configuration of the FortiGate looks like this:
config system dhcp6 server edit 641 set rapid-commit enable set lease-time 3602 set domain "example.com" set subnet 2001:db8:c:641::/64 set interface "IPv6only-01" set option1 56 '0001001020010db828000000000000000ac30123' set option2 56 '00030013013302646504706f6f6c036e7470036f726700' set option3 21 '20010db828000000000000000ac30123' config ip-range edit 1 set start-ip 2001:db8:c:641::6401 set end-ip 2001:db8:c:641::64ff next end set dns-server1 2001:db8:c:a53::53 set dns-server2 2001:db8:c:b53::53 next end
FortiOS in 6.0.14 allows only 3 custom values, I can’t publish more custom options. One more feature request to Fortinet: please allow more (maybe 9?) custom options here. :-)
In the same way, it’s possible to publish other information with DHCPv6, if the operating systems of the server allow the administrator to publish custom values in DHCPv6. In some operating systems, at the time of writing these lines, it’s the only way to send even basic information to a client system. For example, if you have to do a similar configuration in MikroTik RouterOS (tested with 6.48.5), it looks like this:
/ipv6 dhcp-server option add code=23 name=DNSa value="'2001:db8:c:a53::53'" add code=23 name=DNSb value="'2001:db8:c:b53::53'" add code=24 name=DNSsearch value="0x07'example'0x03'com'0x00" add code=31 name=SNTPboth value="'2001:db8:2800::ac2:123''2001:db8:2800::ac3:123'" add code=56 name=NTPaddr value="'000120010db8000c0b53000000000053'" /ipv6 dhcp-server add dhcp-option= DNSa,DNSb,DNSsearch,SNTPboth,NTPaddr interface=eth3 lease-time=29m name=IPv6test
In my experience, RouterOS is sending only requested options, other than FortiOS. In RouterOS, we don’t have to calculate the length of the DHCPv6 option, too.
Please always keep in mind: if you don’t send at least the other-flag in RA packets, most client systems don’t try to get DHCPv6 information from the network.
Photo by Bank Phrom on Unsplash.