]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
ntp: limit number of pool sources
authorMiroslav Lichvar <mlichvar@redhat.com>
Wed, 26 Nov 2014 15:40:33 +0000 (16:40 +0100)
committerMiroslav Lichvar <mlichvar@redhat.com>
Wed, 26 Nov 2014 16:56:36 +0000 (17:56 +0100)
A new option can be now used in the pool directive: maxsources sets the
maximum number of sources that can be used from the pool, the default
value is 4.

On start, when the pool name is resolved, chronyd will add up to 16
sources, one for each resolved address. When the number of sources from
which at least one valid reply was received reaches maxsources, the
other sources will be removed.

chrony.texi.in
client.c
cmdparse.c
cmdparse.h
conf.c
ntp_sources.c
srcparams.h

index 441ad0d55aa8b76a808730d5be62a9ff49ceb504..6f195f311a387c03891c32f1fa65800105f1c5fc 100644 (file)
@@ -2492,21 +2492,28 @@ pidfile /var/tmp/chronyd.pid
 @c {{{ pool
 @node pool directive
 @subsection pool
-The syntax of this directive is identical to that for the @code{server}
+The syntax of this directive is similar to that for the @code{server}
 directive (@pxref{server directive}), except that it is used to specify a pool
 of NTP servers rather than a single NTP server.  The pool name is expected to
-resolve to multiple addresses which change over time.
+resolve to multiple addresses which may change over time.
+
+All options valid in the @code{server} directive can be used in this directive
+too.  There is one option specific to @code{pool} directive: @code{maxsources}
+sets the maximum number of sources that can be used from the pool, the default
+value is 4.
 
-On start, a source will be added for each resolved address.  When a source from
-the pool is unreachable or marked as falseticker, @code{chronyd} will try to
-replace the source with a newly resolved address of the pool.
+On start, when the pool name is resolved, @code{chronyd} will add up to 16
+sources, one for each resolved address.  When the number of sources from which
+at least one valid reply was received reaches @code{maxsources}, the other
+sources will be removed.  When a pool source is unreachable or marked as
+falseticker, @code{chronyd} will try to replace the source with a newly
+resolved address of the pool.
 
 An example of the pool directive is
 
 @example
-pool pool.ntp.org iburst
+pool pool.ntp.org iburst maxsources 3
 @end example
-
 @c }}}
 @c {{{ port
 @node port directive
index 2e1866c833f31cfd77b3e28b2e2434d493bafea5..0a390c058cfd3985f1c2834be0f1275bc693360a 100644 (file)
--- a/client.c
+++ b/client.c
@@ -960,6 +960,11 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
         break;
       }
 
+      if (data.params.max_sources != SRC_DEFAULT_MAXSOURCES) {
+        fprintf(stderr, "Option maxsources not supported\n");
+        break;
+      }
+
       msg->data.ntp_source.port = htonl((unsigned long) data.port);
       UTI_IPHostToNetwork(&ip_addr, &msg->data.ntp_source.ip_addr);
       msg->data.ntp_source.minpoll = htonl(data.params.minpoll);
@@ -1016,6 +1021,9 @@ process_cmd_add_server_or_peer(CMD_Request *msg, char *line)
     case CPS_BadVersion:
       fprintf(stderr, "Unreadable version value\n");
       break;
+    case CPS_BadMaxsources:
+      fprintf(stderr, "Unreadable maxsources value\n");
+      break;
   }
 
   return result;
index 5e589ef7736c48b0a2ec972dffdcc96b2375d938..88f98c23ca7fa9c3ec918a525186bcd4ba2768a7 100644 (file)
@@ -60,6 +60,7 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
   src->params.min_stratum = SRC_DEFAULT_MINSTRATUM;
   src->params.poll_target = SRC_DEFAULT_POLLTARGET;
   src->params.version = NTP_VERSION;
+  src->params.max_sources = SRC_DEFAULT_MAXSOURCES;
   src->params.sel_option = SRC_SelectNormal;
 
   result = CPS_Success;
@@ -175,6 +176,14 @@ CPS_ParseNTPSourceAdd(char *line, CPS_NTP_Source *src)
             line += n;
           }
 
+        } else if (!strcasecmp(cmd, "maxsources")) {
+          if (sscanf(line, "%d%n", &src->params.max_sources, &n) != 1) {
+            result = CPS_BadMaxsources;
+            done = 1;
+          } else {
+            line += n;
+          }
+
         } else {
           result = CPS_BadOption;
           done = 1;
index df8484a4cdcfeb0058ff363afac669c179d11e34..ae863b119e54b5d876b6fd61b488b2818bb4bb3e 100644 (file)
@@ -44,7 +44,8 @@ typedef enum {
   CPS_BadKey,
   CPS_BadMinstratum,
   CPS_BadPolltarget,
-  CPS_BadVersion
+  CPS_BadVersion,
+  CPS_BadMaxsources,
 } CPS_Status;
 
 typedef struct {
diff --git a/conf.c b/conf.c
index 8967dc895d3e655669dacecbf507e45ffd44cbdf..3c263420b1466d72b3358a60a76580bf407917fc 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -632,6 +632,9 @@ parse_source(char *line, NTP_Source_Type type, int pool)
     case CPS_BadVersion:
       other_parse_error("Unreadable version");
       break;
+    case CPS_BadMaxsources:
+      other_parse_error("Unreadable maxsources");
+      break;
   }
 }
 
index 746bc0fc96196cd73a2601ec98c413468beca731..6fc05c4515acbc44b38ce631173156d35b8b9ecc 100644 (file)
@@ -51,6 +51,9 @@ typedef struct {
   NCR_Instance data;            /* Data for the protocol engine for this source */
   int pool;                     /* Number of the pool from which was this source
                                    added or INVALID_POOL */
+  int tentative;                /* Flag indicating there was no valid response
+                                   yet and the source may be removed if other
+                                   sources from the pool respond first */
 } SourceRecord;
 
 /* Hash table of SourceRecord, the size should be a power of two */
@@ -98,6 +101,10 @@ static NSR_SourceResolvingEndHandler resolving_end_handler = NULL;
 struct SourcePool {
   char *name;
   int port;
+  /* Number of sources added from this pool (ignoring tentative sources) */
+  int sources;
+  /* Maximum number of sources */
+  int max_sources;
 };
 
 /* Array of SourcePool */
@@ -317,6 +324,7 @@ add_source(NTP_Remote_Address *remote_addr, NTP_Source_Type type, SourceParamete
       record->data = NCR_GetInstance(remote_addr, type, params);
       record->remote_addr = NCR_GetRemoteAddress(record->data);
       record->pool = pool;
+      record->tentative = pool != INVALID_POOL ? 1 : 0;
 
       if (auto_start_sources)
         NCR_StartInstance(record->data);
@@ -512,6 +520,8 @@ NSR_AddSourceByName(char *name, int port, int pool, NTP_Source_Type type, Source
     sp = (struct SourcePool *)ARR_GetNewElement(pools);
     sp->name = Strdup(name);
     sp->port = port;
+    sp->sources = 0;
+    sp->max_sources = params->max_sources;
     us->new_source.pool = ARR_GetSize(pools) - 1;
     us->new_source.max_new_sources = MAX_POOL_SOURCES;
   }
@@ -682,19 +692,62 @@ NSR_HandleBadSource(IPAddr *address)
 
 /* ================================================== */
 
+static void remove_tentative_pool_sources(int pool)
+{
+  SourceRecord *record;
+  unsigned int i, removed;
+
+  for (i = removed = 0; i < ARR_GetSize(records); i++) {
+    record = get_record(i);
+
+    if (!record->remote_addr || record->pool != pool || !record->tentative)
+      continue;
+
+    DEBUG_LOG(LOGF_NtpSources, "removing tentative source %s",
+              UTI_IPToString(&record->remote_addr->ip_addr));
+
+    clean_source_record(record);
+    removed++;
+  }
+
+  if (removed)
+    rehash_records();
+}
+
 /* This routine is called by ntp_io when a new packet arrives off the network,
    possibly with an authentication tail */
 void
 NSR_ProcessReceive(NTP_Packet *message, struct timeval *now, double now_err, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
 {
+  SourceRecord *record;
+  struct SourcePool *pool;
   int slot, found;
 
   assert(initialised);
 
   find_slot(remote_addr, &slot, &found);
   if (found == 2) { /* Must match IP address AND port number */
-    NCR_ProcessKnown(message, now, now_err, get_record(slot)->data,
-                     local_addr, length);
+    record = get_record(slot);
+
+    if (!NCR_ProcessKnown(message, now, now_err, record->data, local_addr, length))
+      return;
+
+    if (record->tentative) {
+      /* First reply from a pool source */
+      record->tentative = 0;
+
+      assert(record->pool != INVALID_POOL);
+      pool = (struct SourcePool *)ARR_GetElement(pools, record->pool);
+      pool->sources++;
+
+      DEBUG_LOG(LOGF_NtpSources, "pool %s has %d confirmed sources",
+                pool->name, pool->sources);
+
+      /* If the number of sources reached the configured maximum, remove
+         the tentative sources added from this pool */
+      if (pool->sources >= pool->max_sources)
+        remove_tentative_pool_sources(record->pool);
+    }
   } else {
     NCR_ProcessUnknown(message, now, now_err, remote_addr, local_addr, length);
   }
index 5fd7517896b0902fc2ade9b37278a3b146c540ab..fdf0abbc067e0c0c8c2c5180dca955040d9a1fa0 100644 (file)
@@ -39,6 +39,7 @@ typedef struct {
   int min_stratum;
   int poll_target;
   int version;
+  int max_sources;
   uint32_t authkey;
   double max_delay;
   double max_delay_ratio;
@@ -55,6 +56,7 @@ typedef struct {
 #define SRC_DEFAULT_MAXDELAYDEVRATIO 10.0
 #define SRC_DEFAULT_MINSTRATUM 0
 #define SRC_DEFAULT_POLLTARGET 6
+#define SRC_DEFAULT_MAXSOURCES 4
 #define INACTIVE_AUTHKEY 0
 
 #endif /* GOT_SRCPARAMS_H */