Time Enough for NetworkManager

When one is traveling, NetworkManager is a great convenience. However, one thing it does not do on its own is update your computer's time service server.

ntpd has been the standard Linux network time protocol (NTP) daemon. It listens to servers out there, and sets the system's time accordingly. It is also a server. Many networks provide their own time servers. To minimize external traffic, such networks encourage hosts to use their own time server.

One way to encourage hosts on a network to use the network's own time server is to provide its address or domain name with DHCP. This is easily done. In dhcpd.conf, add something like:

    option ntp-servers 192.168.100.31; # freeman, AKA ntp.localdomain

or

    option ntp-servers ntp.localdomain;  # freeman, AKA ntp.localdomain

to appropriate places, such as subnet declarations.

(It helps to have npt as a cname, or alias, to the ntp server's name in case you decide to move it to another host. But that's a whole 'nother can of lawyers.)

The Debian ntp package comes with a short script that drops into place transparently. That's great if you are using ntpd. However systemd, the kudzu of init systems, comes with its own time client, systemd-timesyncd. It is lightweight: a client only. So if you want an NTP server, you will want ntp on at least one machine.

On every other machine, systemd-timesyncd will do the job. One problem: systemd-timesyncd has no NetworkManager integration. We are going to provide that.

systemd-timesyncd has a configuration file, found at /etc/systemd/timesyncd.conf, and provided by the systemd package. To provide a new time server, we edit the NTP entry like so, from

[Time]
#NTP=
#FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org

to

[Time]
NTP=192.168.10.31 
#FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org

(See line 74, sed -e ... below.)

The fallback NTP servers are what systemd-timesyncd uses if it can't get to the main time server, specified by the NTP line.

So where do we get that address for the NTP value? From DHCP. NetworkManager places it in the environment of the scripts it finds (and runs) in /etc/NetworkManager/dispatch.d.

Two caveats:

  • This script only handles IPV4. I don't have an IPV6 network on which to experiment, so I will leave that as an exercise for the student.

  • The log file is one I use for my own Network Manager script. Maybe one of these days I'll document that. Meanwhile you can either use that for debugging, or send output to syslog (or equivalent) with 'logger`.

With that explanation, the script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/bin/bash

# Time-stamp: <2021-02-06 14:48 charles 60ntp>

# A script to get the DHCP option for ntp servers into the system and
# hand it to systemd-timesyncd (not ntp). Adapted from
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=537358#37.

# This script resides in /etc/NetworkManager/dispatcher.d, is owned
# by root:root, and should have permissions of 0744.

# Note that this script only handles IPV4.

# Note that this script creates and logs to its own log file. Once you
# are satisfied that it works, you can comment out the rest of the
# debug statements.

LOG=/var/log/NetworkManager

TSC=/etc/systemd/timesyncd.conf
GOLDENTSC=/etc/systemd/timesyncd.conf.au

# The first thing to do is copy /etc/systemd/timesyncd.conf to
# /etc/systemd/timesyncd.conf.au if it isn't already there.

if [ ! -e $GOLDENTSC ]; then
    cp -p $TSC $GOLDENTSC
    chmod a-wx $GOLDENTSC
fi

# /bin/echo Running 60ntp >> $LOG
/bin/echo "$# arguments: |$*|" >> $LOG

if [ -z "$1" ]; then
    # /bin/echo "$0: called with no interface" >> $LOG
    # /bin/echo Leaving 60ntp >> $LOG
    exit 0;
fi

ntp_server_restart() {
    systemctl restart systemd-timesyncd >> $LOG
}

ntp_servers_setup_remove() {
    echo "Removing any local NTP servers." >> $LOG
    cp -rp ${GOLDENTSC} ${TSC}
    ntp_server_restart
}

# Run the right scripts
case "$2" in
    up|vpn-up)

        /bin/echo "\$DHCP4_NTP_SERVERS is |$DHCP4_NTP_SERVERS|" >> $LOG

    if [ -z "$DHCP4_NTP_SERVERS" ]; then
            # we have no ntp server via DHCP, so we revert to the
            # fallback servers.
            /bin/echo "Removing any old NTP servers." >> $LOG
        ntp_servers_setup_remove
        exit 0;
    fi

        # I'm not sure you need this loop; it depends on what exactly
        # is in the variable $DHCP4_NTP_SERVERS. I haven't
        # experimented with it.
        TIMESERVERS='';
        for server in $DHCP4_NTP_SERVERS; do
            TIMESERVERS="$TIMESERVERS$server ";
        done

        /bin/echo "New time servers are |$TIMESERVERS|" >>$LOG

        sed -e "s/#NTP=/NTP=$TIMESERVERS/g" ${GOLDENTSC} > ${TSC}

        ntp_server_restart

        # /bin/echo Leaving 60ntp >> $LOG
        exit 0;

    ntp_server_restart
    ;;
    down|vpn-down)
        ntp_servers_setup_remove
    ;;
    hostname|dhcp4-change|dhcp6-change)
        # Do nothing
    ;;
    *)
    # /bin/echo "$0: called with unknown action \`$2'" >> $LOG
        # /bin/echo Leaving 60ntp >> $LOG
    exit 0
    ;;
esac

# /bin/echo Leaving 60ntp >> $LOG

exit 0

blogroll

social