From: Michal Schmidt <mschmidt@redhat.com>
Date: Wed, 10 Sep 2014 19:13:40 +0200
Subject: timesyncd: check root distance

Upstream commit:
commit 3af0442c52090f34ae7a1c8e6b6587c540c06896
Author: Miroslav Lichvar <mlichvar@redhat.com>
Date:   Wed Aug 27 16:47:20 2014 +0200

    timesyncd: check root distance

    NTPv4 servers don't reply with unsynchronized status when they lost
    synchronization, they only keep increasing the root dispersion and it's
    up to the client to decide at which point they no longer consider it
    synchronized.

    Ignore replies with root distance over 5 seconds.
---
 src/timesync/timesyncd.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 436aeae..5187df5 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -91,6 +91,9 @@
 #define NTP_FIELD_MODE(f)               ((f) & 7)
 #define NTP_FIELD(l, v, m)              (((l) << 6) | ((v) << 3) | (m))
 
+/* Maximum acceptable root distance in seconds. */
+#define NTP_MAX_ROOT_DISTANCE           5.0
+
 /*
  * "NTP timestamps are represented as a 64-bit unsigned fixed-point number,
  * in seconds relative to 0h on 1 January 1900."
@@ -136,6 +139,10 @@ static int manager_clock_watch_setup(Manager *m);
 static int manager_connect(Manager *m);
 static void manager_disconnect(Manager *m);
 
+static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) {
+        return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0);
+}
+
 static double ntp_ts_to_d(const struct ntp_ts *ts) {
         return be32toh(ts->sec) + ((double)be32toh(ts->frac) / UINT_MAX);
 }
@@ -565,6 +572,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
         ssize_t len;
         double origin, receive, trans, dest;
         double delay, offset;
+        double root_distance;
         bool spike;
         int leap_sec;
         int r;
@@ -650,6 +658,12 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                 return manager_connect(m);
         }
 
+        root_distance = ntp_ts_short_to_d(&ntpmsg.root_delay) / 2 + ntp_ts_short_to_d(&ntpmsg.root_dispersion);
+        if (root_distance > NTP_MAX_ROOT_DISTANCE) {
+                log_debug("Server has too large root distance. Disconnecting.");
+                return manager_connect(m);
+        }
+
         /* valid packet */
         m->pending = false;
         m->retry_interval = 0;
@@ -691,6 +705,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                   "  mode         : %u\n"
                   "  stratum      : %u\n"
                   "  precision    : %.6f sec (%d)\n"
+                  "  root distance: %.6f sec\n"
                   "  reference    : %.4s\n"
                   "  origin       : %.3f\n"
                   "  receive      : %.3f\n"
@@ -706,6 +721,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re
                   NTP_FIELD_MODE(ntpmsg.field),
                   ntpmsg.stratum,
                   exp2(ntpmsg.precision), ntpmsg.precision,
+                  root_distance,
                   ntpmsg.stratum == 1 ? ntpmsg.refid : "n/a",
                   origin - OFFSET_1900_1970,
                   receive - OFFSET_1900_1970,
