Anyone who’s followed this blog or gone back and read my past posts has probably picked up on my somewhat unhealthy obsession with time. Every time I get my hands on a new display or microcontroller, the first thing I do is build a clock out of it (one, two, three, four, five, six, seven, eight, nine). I’ve also dabbled in high precision time with my rubidium frequency standard and the multitude of frequency counters in my apartment. One area that I haven’t had as much success with time in the past is keeping all of my Linux systems synchronized, and that’s been bothering me.

I finally sat down this week and spent a few hours figuring out how to get multicast network time protocol working on Debian, since I’ve already put so much work into getting multicast routing working on my apartment’s LAN and want to actually use it for something. There were a few critical pieces that all came from different places online, so I figured I’d collect them all in one place.

Typically, NTP operates by configuring each client with a server address to periodically poll for the current time. During each poll, the client tries to determine the remote server’s time and how long it took for the server’s response to get back to the client. For remote Internet servers, this time delay can be in the order of 20-100ms and change wildly based on network conditions. The traditional solution to this is to have a single local system poll several remote NTP servers, and then manually configure each local client to poll just this one local server, which in my case is my WNDR3800 router. This works acceptably well, but just isn’t complicated enough to meet my desire to make life hard on myself.

Multicast NTP is based on multiple servers beaconing their local time towards a multicast address, which clients listen for to discover these servers. When a client discovers a new server, it first performs a standard unicast query with the server to determine network delay between the two systems, and then sits back and silently listens for future multicast beacons from the server to keep the client’s local time in sync. All this does for me is allow my NTP clients to dynamically find new and forget old NTP servers on my network when I swap in and out computers. Another advantage where multicast really shines on huge computer networks is that after the initial propagation measurement, clients don’t generate any additional network traffic, but only passively listen for the multicast beacons from the servers.


Configuring NTP Multicast Servers

On my systems that I’ve designated as my NTP servers, they do the typical pulling of four unique servers from *.pool.ntp.org DNS entries, but then act as both multicast servers and clients so they can discover and synchronize with each other.

Sample Config and ntpq output:

multicast.ntp.conf

# /etc/ntp.conf for multicast servers
# Kenneth Finnegan, 2014


# Logging of stats, etc
driftfile /var/lib/ntp/ntp.drift
statsdir /var/log/ntpstats/
statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable

# Pull multiple ntp.org pool NTP servers as the basis for local time
server 0.ubuntu.pool.ntp.org
server 1.ubuntu.pool.ntp.org
server 2.ubuntu.pool.ntp.org
server 3.ubuntu.pool.ntp.org

# Access control configuration
# For multicast, MAKE SURE TO REMOVE nopeer
restrict -4 default kod notrap nomodify noquery
restrict -6 default kod notrap nomodify noquery

# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1

# Beacon to this locally admin address
# Note that ttl is an index into the ttl array, not an actual ttl value
# By default, it seems to be 0, which maps to 1, which doesn't cross subnets
# ttl 7 sets it to the maximum value of 224, since the default array is multiples of 32
broadcast 239.192.1.1 ttl 7

# Disable authentication and listen for the other peer servers
disable auth
multicastclient 239.192.1.1

ntpq_output.txt

kenneth@KWF3:~$ ntpq -pn -c rv
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
-10.44.1.1       132.163.4.102    3 u  521 1024  377    0.302   -9.587   5.805
-198.199.111.124 216.218.192.202  2 u   34 1024  377   24.987   13.977   1.416
*166.70.136.41   .GPS.            1 u  773 1024  377   77.533   -5.500   3.148
-2001:4f8:fff7:1 216.218.254.202  2 u 1014 1024  377   24.295   11.447   1.555
+64.246.132.14   .CDMA.           1 u  253 1024  377   97.612    9.846   0.523
+91.189.89.199   193.79.237.14    2 u   52 1024  377  152.725    8.163   1.508
 239.192.1.1     .MCST.          16 u    -   64    0    0.000    0.000   0.002
-10.44.1.188     10.44.1.13       3 m   19   64  376    0.760   -4.619   0.013
-10.44.1.142     10.44.1.13       3 m   23   64  377    0.092   -4.532   0.044

associd=0 status=0615 leap_none, sync_ntp, 1 event, clock_sync,
version="ntpd 4.2.6p3@1.2290-o Tue Jun  5 20:12:08 UTC 2012 (1)",
processor="x86_64", system="Linux/3.2.0-58-generic", leap=00, stratum=2,
precision=-19, rootdelay=77.533, rootdisp=47.586, refid=166.70.136.41,
reftime=d67d7a9a.b7d316b7  Sun, Jan 12 2014 12:33:30.718,
clock=d67d7d9f.b5ac8204  Sun, Jan 12 2014 12:46:23.709, peer=36371,
tc=10, mintc=3, offset=2.949, frequency=-56.460, sys_jitter=12.017,
clk_jitter=1.622, clk_wander=0.241

A few tripping points in the configuration file:

  • The default restrict statements prevent discovering peers, so you need to remove the nopeer statement from the IPv4 and/or IPv6 restrict statements.
  • The broadcast statement defaults to “ttl 0”, which means the 0th entry in the eight value ttl array used by manycasting. This means that the argument to ttl isn’t actually a time to live value, but an index into the default array of ttl values {1, 32, 64, 96, 128, 160, 192, 224}, which you can re-declare if that doesn’t meet your needs.
  • Unlike broadcast clients, for multicastclient you need to specify the address to listen to.
  • /etc/ntp.conf isn’t the config file that NTP actually uses. If you look at /etc/init.d/ntp, you’ll discover that by default, ntpd uses the config file /var/lib/ntp/ntp.conf.dhcp, which is built by appending /etc/ntp.conf to any NTP server advertisements from the system’s DHCP server. /var/lib/ntp/ntp.conf.dhcp isn’t rebuilt when you run “service ntp restart”, which is why your changes to /etc/ntp.conf don’t appear to do anything until you reboot the entire system! I haven’t bothered to figure out which init script triggers this config rebuild, but have been doing testing on the /var/lib/… file and then manually copying the config back to /etc/ntp.config

The selection of the multicast address is pretty arbitrary. What I have shown isn’t the actual address I use, but anything in the 239.192.0.0/14 or ffx8::/16 organizationally-local multicast address subnets are technically correct and should work fine as long as all of your systems match.