summaryrefslogtreecommitdiffstats
path: root/sebhbsd/freebsd/contrib/ntp/ntpd/ntp_peer.c
diff options
context:
space:
mode:
Diffstat (limited to 'sebhbsd/freebsd/contrib/ntp/ntpd/ntp_peer.c')
-rw-r--r--sebhbsd/freebsd/contrib/ntp/ntpd/ntp_peer.c1119
1 files changed, 1119 insertions, 0 deletions
diff --git a/sebhbsd/freebsd/contrib/ntp/ntpd/ntp_peer.c b/sebhbsd/freebsd/contrib/ntp/ntpd/ntp_peer.c
new file mode 100644
index 0000000..e2c2152
--- /dev/null
+++ b/sebhbsd/freebsd/contrib/ntp/ntpd/ntp_peer.c
@@ -0,0 +1,1119 @@
+#include <machine/rtems-bsd-user-space.h>
+
+/*
+ * ntp_peer.c - management of data maintained for peer associations
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_lists.h"
+#include "ntp_stdlib.h"
+#include "ntp_control.h"
+#include <ntp_random.h>
+
+/*
+ * Table of valid association combinations
+ * ---------------------------------------
+ *
+ * packet->mode
+ * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST
+ * ---------- | ---------------------------------------------
+ * NO_PEER | e 1 0 1 1 1
+ * ACTIVE | e 1 1 0 0 0
+ * PASSIVE | e 1 e 0 0 0
+ * CLIENT | e 0 0 0 1 0
+ * SERVER | e 0 0 0 0 0
+ * BCAST | e 0 0 0 0 0
+ * BCLIENT | e 0 0 0 e 1
+ *
+ * One point to note here: a packet in BCAST mode can potentially match
+ * a peer in CLIENT mode, but we that is a special case and we check for
+ * that early in the decision process. This avoids having to keep track
+ * of what kind of associations are possible etc... We actually
+ * circumvent that problem by requiring that the first b(m)roadcast
+ * received after the change back to BCLIENT mode sets the clock.
+ */
+#define AM_MODES 7 /* number of rows and columns */
+#define NO_PEER 0 /* action when no peer is found */
+
+int AM[AM_MODES][AM_MODES] = {
+/* packet->mode */
+/* peer { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */
+/* mode */
+/*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL},
+
+/*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_NOMATCH},
+
+/*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
+
+/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT},
+};
+
+#define MATCH_ASSOC(x, y) AM[(x)][(y)]
+
+/*
+ * These routines manage the allocation of memory to peer structures
+ * and the maintenance of three data structures involving all peers:
+ *
+ * - peer_list is a single list with all peers, suitable for scanning
+ * operations over all peers.
+ * - peer_adr_hash is an array of lists indexed by hashed peer address.
+ * - peer_aid_hash is an array of lists indexed by hashed associd.
+ *
+ * They also maintain a free list of peer structures, peer_free.
+ *
+ * The three main entry points are findpeer(), which looks for matching
+ * peer structures in the peer list, newpeer(), which allocates a new
+ * peer structure and adds it to the list, and unpeer(), which
+ * demobilizes the association and deallocates the structure.
+ */
+/*
+ * Peer hash tables
+ */
+struct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */
+int peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */
+struct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */
+int assoc_hash_count[NTP_HASH_SIZE];/* peers in each bucket */
+struct peer *peer_list; /* peer structures list */
+static struct peer *peer_free; /* peer structures free list */
+int peer_free_count; /* count of free structures */
+
+/*
+ * Association ID. We initialize this value randomly, then assign a new
+ * value every time an association is mobilized.
+ */
+static associd_t current_association_ID; /* association ID */
+static associd_t initial_association_ID; /* association ID */
+
+/*
+ * Memory allocation watermarks.
+ */
+#define INIT_PEER_ALLOC 8 /* static preallocation */
+#define INC_PEER_ALLOC 4 /* add N more when empty */
+
+/*
+ * Miscellaneous statistic counters which may be queried.
+ */
+u_long peer_timereset; /* time stat counters zeroed */
+u_long findpeer_calls; /* calls to findpeer */
+u_long assocpeer_calls; /* calls to findpeerbyassoc */
+u_long peer_allocations; /* allocations from free list */
+u_long peer_demobilizations; /* structs freed to free list */
+int total_peer_structs; /* peer structs */
+int peer_associations; /* mobilized associations */
+int peer_preempt; /* preemptable associations */
+static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */
+
+static struct peer * findexistingpeer_name(const char *, u_short,
+ struct peer *, int);
+static struct peer * findexistingpeer_addr(sockaddr_u *,
+ struct peer *, int,
+ u_char, int *);
+static void free_peer(struct peer *, int);
+static void getmorepeermem(void);
+static int score(struct peer *);
+
+
+/*
+ * init_peer - initialize peer data structures and counters
+ *
+ * N.B. We use the random number routine in here. It had better be
+ * initialized prior to getting here.
+ */
+void
+init_peer(void)
+{
+ int i;
+
+ /*
+ * Initialize peer free list from static allocation.
+ */
+ for (i = COUNTOF(init_peer_alloc) - 1; i >= 0; i--)
+ LINK_SLIST(peer_free, &init_peer_alloc[i], p_link);
+ total_peer_structs = COUNTOF(init_peer_alloc);
+ peer_free_count = COUNTOF(init_peer_alloc);
+
+ /*
+ * Initialize our first association ID
+ */
+ do
+ current_association_ID = ntp_random() & ASSOCID_MAX;
+ while (!current_association_ID);
+ initial_association_ID = current_association_ID;
+}
+
+
+/*
+ * getmorepeermem - add more peer structures to the free list
+ */
+static void
+getmorepeermem(void)
+{
+ int i;
+ struct peer *peers;
+
+ peers = eallocarray(INC_PEER_ALLOC, sizeof(*peers));
+
+ for (i = INC_PEER_ALLOC - 1; i >= 0; i--)
+ LINK_SLIST(peer_free, &peers[i], p_link);
+
+ total_peer_structs += INC_PEER_ALLOC;
+ peer_free_count += INC_PEER_ALLOC;
+}
+
+
+static struct peer *
+findexistingpeer_name(
+ const char * hostname,
+ u_short hname_fam,
+ struct peer * start_peer,
+ int mode
+ )
+{
+ struct peer *p;
+
+ if (NULL == start_peer)
+ p = peer_list;
+ else
+ p = start_peer->p_link;
+ for (; p != NULL; p = p->p_link)
+ if (p->hostname != NULL
+ && (-1 == mode || p->hmode == mode)
+ && (AF_UNSPEC == hname_fam
+ || AF_UNSPEC == AF(&p->srcadr)
+ || hname_fam == AF(&p->srcadr))
+ && !strcasecmp(p->hostname, hostname))
+ break;
+ return p;
+}
+
+
+static
+struct peer *
+findexistingpeer_addr(
+ sockaddr_u * addr,
+ struct peer * start_peer,
+ int mode,
+ u_char cast_flags,
+ int * ip_count
+ )
+{
+ struct peer *peer;
+
+ DPRINTF(2, ("findexistingpeer_addr(%s, %s, %d, 0x%x, %p)\n",
+ sptoa(addr),
+ (start_peer)
+ ? sptoa(&start_peer->srcadr)
+ : "NULL",
+ mode, (u_int)cast_flags, ip_count));
+
+ /*
+ * start_peer is included so we can locate instances of the
+ * same peer through different interfaces in the hash table.
+ * Without MDF_BCLNT, a match requires the same mode and remote
+ * address. MDF_BCLNT associations start out as MODE_CLIENT
+ * if broadcastdelay is not specified, and switch to
+ * MODE_BCLIENT after estimating the one-way delay. Duplicate
+ * associations are expanded in definition to match any other
+ * MDF_BCLNT with the same srcadr (remote, unicast address).
+ */
+ if (NULL == start_peer)
+ peer = peer_hash[NTP_HASH_ADDR(addr)];
+ else
+ peer = start_peer->adr_link;
+
+ while (peer != NULL) {
+ DPRINTF(3, ("%s %s %d %d 0x%x 0x%x ", sptoa(addr),
+ sptoa(&peer->srcadr), mode, peer->hmode,
+ (u_int)cast_flags, (u_int)peer->cast_flags));
+ if (ip_count) {
+ if (SOCK_EQ(addr, &peer->srcadr)) {
+ (*ip_count)++;
+ }
+ }
+ if ((-1 == mode || peer->hmode == mode ||
+ ((MDF_BCLNT & peer->cast_flags) &&
+ (MDF_BCLNT & cast_flags))) &&
+ ADDR_PORT_EQ(addr, &peer->srcadr)) {
+ DPRINTF(3, ("found.\n"));
+ break;
+ }
+ DPRINTF(3, ("\n"));
+ peer = peer->adr_link;
+ }
+
+ return peer;
+}
+
+
+/*
+ * findexistingpeer - search by address and return a pointer to a peer.
+ */
+struct peer *
+findexistingpeer(
+ sockaddr_u * addr,
+ const char * hostname,
+ struct peer * start_peer,
+ int mode,
+ u_char cast_flags,
+ int * ip_count
+ )
+{
+ if (hostname != NULL)
+ return findexistingpeer_name(hostname, AF(addr),
+ start_peer, mode);
+ else
+ return findexistingpeer_addr(addr, start_peer, mode,
+ cast_flags, ip_count);
+}
+
+
+/*
+ * findpeer - find and return a peer match for a received datagram in
+ * the peer_hash table.
+ *
+ * [Bug 3072] To faciliate a faster reorganisation after routing changes
+ * the original code re-assigned the peer address to be the destination
+ * of the received packet and initiated another round on a mismatch.
+ * Unfortunately this leaves us wide open for a DoS attack where the
+ * attacker directs a packet with forged destination address to us --
+ * this results in a wrong interface assignment, actually creating a DoS
+ * situation.
+ *
+ * This condition would persist until the next update of the interface
+ * list, but a continued attack would put us out of business again soon
+ * enough. Authentication alone does not help here, since it does not
+ * protect the UDP layer and leaves us open for a replay attack.
+ *
+ * So we do not update the adresses and wait until the next interface
+ * list update does the right thing for us.
+ */
+struct peer *
+findpeer(
+ struct recvbuf *rbufp,
+ int pkt_mode,
+ int * action
+ )
+{
+ struct peer * p;
+ sockaddr_u * srcadr;
+ u_int hash;
+ struct pkt * pkt;
+ l_fp pkt_org;
+
+ findpeer_calls++;
+ srcadr = &rbufp->recv_srcadr;
+ hash = NTP_HASH_ADDR(srcadr);
+ for (p = peer_hash[hash]; p != NULL; p = p->adr_link) {
+
+ /* [Bug 3072] ensure interface of peer matches */
+ /* [Bug 3356] ... if NOT a broadcast peer! */
+ if (p->hmode != MODE_BCLIENT && p->dstadr != rbufp->dstadr)
+ continue;
+
+ /* ensure peer source address matches */
+ if ( ! ADDR_PORT_EQ(srcadr, &p->srcadr))
+ continue;
+
+ /* If the association matching rules determine that this
+ * is not a valid combination, then look for the next
+ * valid peer association.
+ */
+ *action = MATCH_ASSOC(p->hmode, pkt_mode);
+
+ /* A response to our manycastclient solicitation might
+ * be misassociated with an ephemeral peer already spun
+ * for the server. If the packet's org timestamp
+ * doesn't match the peer's, check if it matches the
+ * ACST prototype peer's. If so it is a redundant
+ * solicitation response, return AM_ERR to discard it.
+ * [Bug 1762]
+ */
+ if (MODE_SERVER == pkt_mode && AM_PROCPKT == *action) {
+ pkt = &rbufp->recv_pkt;
+ NTOHL_FP(&pkt->org, &pkt_org);
+ if (!L_ISEQU(&p->aorg, &pkt_org) &&
+ findmanycastpeer(rbufp))
+ *action = AM_ERR;
+ }
+
+ /* if an error was returned, exit back right here. */
+ if (*action == AM_ERR)
+ return NULL;
+
+ /* if a match is found, we stop our search. */
+ if (*action != AM_NOMATCH)
+ break;
+ }
+
+ /* If no matching association is found... */
+ if (NULL == p)
+ *action = MATCH_ASSOC(NO_PEER, pkt_mode);
+
+ return p;
+}
+
+/*
+ * findpeerbyassoc - find and return a peer using his association ID
+ */
+struct peer *
+findpeerbyassoc(
+ associd_t assoc
+ )
+{
+ struct peer *p;
+ u_int hash;
+
+ assocpeer_calls++;
+ hash = assoc & NTP_HASH_MASK;
+ for (p = assoc_hash[hash]; p != NULL; p = p->aid_link)
+ if (assoc == p->associd)
+ break;
+ return p;
+}
+
+
+/*
+ * clear_all - flush all time values for all associations
+ */
+void
+clear_all(void)
+{
+ struct peer *p;
+
+ /*
+ * This routine is called when the clock is stepped, and so all
+ * previously saved time values are untrusted.
+ */
+ for (p = peer_list; p != NULL; p = p->p_link)
+ if (!(MDF_TXONLY_MASK & p->cast_flags))
+ peer_clear(p, "STEP");
+
+ DPRINTF(1, ("clear_all: at %lu\n", current_time));
+}
+
+
+/*
+ * score_all() - determine if an association can be demobilized
+ */
+int
+score_all(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ struct peer *speer;
+ int temp, tamp;
+ int x;
+
+ /*
+ * This routine finds the minimum score for all preemptible
+ * associations and returns > 0 if the association can be
+ * demobilized.
+ */
+ tamp = score(peer);
+ temp = 100;
+ for (speer = peer_list; speer != NULL; speer = speer->p_link)
+ if (speer->flags & FLAG_PREEMPT) {
+ x = score(speer);
+ if (x < temp)
+ temp = x;
+ }
+ DPRINTF(1, ("score_all: at %lu score %d min %d\n",
+ current_time, tamp, temp));
+
+ if (tamp != temp)
+ temp = 0;
+
+ return temp;
+}
+
+
+/*
+ * score() - calculate preemption score
+ */
+static int
+score(
+ struct peer *peer /* peer structure pointer */
+ )
+{
+ int temp;
+
+ /*
+ * This routine calculates the premption score from the peer
+ * error bits and status. Increasing values are more cherished.
+ */
+ temp = 0;
+ if (!(peer->flash & TEST10))
+ temp++; /* 1 good synch and stratum */
+ if (!(peer->flash & TEST13))
+ temp++; /* 2 reachable */
+ if (!(peer->flash & TEST12))
+ temp++; /* 3 no loop */
+ if (!(peer->flash & TEST11))
+ temp++; /* 4 good distance */
+ if (peer->status >= CTL_PST_SEL_SELCAND)
+ temp++; /* 5 in the hunt */
+ if (peer->status != CTL_PST_SEL_EXCESS)
+ temp++; /* 6 not spare tire */
+ return (temp); /* selection status */
+}
+
+
+/*
+ * free_peer - internal routine to free memory referred to by a struct
+ * peer and return it to the peer free list. If unlink is
+ * nonzero, unlink from the various lists.
+ */
+static void
+free_peer(
+ struct peer * p,
+ int unlink_peer
+ )
+{
+ struct peer * unlinked;
+ int hash;
+
+ if (unlink_peer) {
+ hash = NTP_HASH_ADDR(&p->srcadr);
+ peer_hash_count[hash]--;
+
+ UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link,
+ struct peer);
+ if (NULL == unlinked) {
+ peer_hash_count[hash]++;
+ msyslog(LOG_ERR, "peer %s not in address table!",
+ stoa(&p->srcadr));
+ }
+
+ /*
+ * Remove him from the association hash as well.
+ */
+ hash = p->associd & NTP_HASH_MASK;
+ assoc_hash_count[hash]--;
+
+ UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link,
+ struct peer);
+ if (NULL == unlinked) {
+ assoc_hash_count[hash]++;
+ msyslog(LOG_ERR,
+ "peer %s not in association ID table!",
+ stoa(&p->srcadr));
+ }
+
+ /* Remove him from the overall list. */
+ UNLINK_SLIST(unlinked, peer_list, p, p_link,
+ struct peer);
+ if (NULL == unlinked)
+ msyslog(LOG_ERR, "%s not in peer list!",
+ stoa(&p->srcadr));
+ }
+
+ if (p->hostname != NULL)
+ free(p->hostname);
+
+ if (p->ident != NULL)
+ free(p->ident);
+
+ if (p->addrs != NULL)
+ free(p->addrs); /* from copy_addrinfo_list() */
+
+ /* Add his corporeal form to peer free list */
+ ZERO(*p);
+ LINK_SLIST(peer_free, p, p_link);
+ peer_free_count++;
+}
+
+
+/*
+ * unpeer - remove peer structure from hash table and free structure
+ */
+void
+unpeer(
+ struct peer *peer
+ )
+{
+ mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd);
+ restrict_source(&peer->srcadr, 1, 0);
+ set_peerdstadr(peer, NULL);
+ peer_demobilizations++;
+ peer_associations--;
+ if (FLAG_PREEMPT & peer->flags)
+ peer_preempt--;
+#ifdef REFCLOCK
+ /*
+ * If this peer is actually a clock, shut it down first
+ */
+ if (FLAG_REFCLOCK & peer->flags)
+ refclock_unpeer(peer);
+#endif
+
+ free_peer(peer, TRUE);
+}
+
+
+/*
+ * peer_config - configure a new association
+ */
+struct peer *
+peer_config(
+ sockaddr_u * srcadr,
+ const char * hostname,
+ endpt * dstadr,
+ int ippeerlimit,
+ u_char hmode,
+ u_char version,
+ u_char minpoll,
+ u_char maxpoll,
+ u_int flags,
+ u_int32 ttl,
+ keyid_t key,
+ const char * ident /* autokey group */
+ )
+{
+ u_char cast_flags;
+
+ /*
+ * We do a dirty little jig to figure the cast flags. This is
+ * probably not the best place to do this, at least until the
+ * configure code is rebuilt. Note only one flag can be set.
+ */
+ switch (hmode) {
+ case MODE_BROADCAST:
+ if (IS_MCAST(srcadr))
+ cast_flags = MDF_MCAST;
+ else
+ cast_flags = MDF_BCAST;
+ break;
+
+ case MODE_CLIENT:
+ if (hostname != NULL && SOCK_UNSPEC(srcadr))
+ cast_flags = MDF_POOL;
+ else if (IS_MCAST(srcadr))
+ cast_flags = MDF_ACAST;
+ else
+ cast_flags = MDF_UCAST;
+ break;
+
+ default:
+ cast_flags = MDF_UCAST;
+ }
+
+ /*
+ * Mobilize the association and initialize its variables. If
+ * emulating ntpdate, force iburst. For pool and manycastclient
+ * strip FLAG_PREEMPT as the prototype associations are not
+ * themselves preemptible, though the resulting associations
+ * are.
+ */
+ flags |= FLAG_CONFIG;
+ if (mode_ntpdate)
+ flags |= FLAG_IBURST;
+ if ((MDF_ACAST | MDF_POOL) & cast_flags)
+ flags &= ~FLAG_PREEMPT;
+ return newpeer(srcadr, hostname, dstadr, ippeerlimit, hmode, version,
+ minpoll, maxpoll, flags, cast_flags, ttl, key, ident);
+}
+
+/*
+ * setup peer dstadr field keeping it in sync with the interface
+ * structures
+ */
+void
+set_peerdstadr(
+ struct peer * p,
+ endpt * dstadr
+ )
+{
+ struct peer * unlinked;
+
+ DEBUG_INSIST(p != NULL);
+
+ if (p == NULL)
+ return;
+
+ /* check for impossible or identical assignment */
+ if (p->dstadr == dstadr)
+ return;
+
+ /*
+ * Don't accept updates to a separate multicast receive-only
+ * endpt while a BCLNT peer is running its unicast protocol.
+ */
+ if (dstadr != NULL && (FLAG_BC_VOL & p->flags) &&
+ (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) {
+ return;
+ }
+
+ /* unlink from list if we have an address prior to assignment */
+ if (p->dstadr != NULL) {
+ p->dstadr->peercnt--;
+ UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink,
+ struct peer);
+ msyslog(LOG_INFO, "%s local addr %s -> %s",
+ stoa(&p->srcadr), latoa(p->dstadr),
+ latoa(dstadr));
+ }
+
+ p->dstadr = dstadr;
+
+ /* link to list if we have an address after assignment */
+ if (p->dstadr != NULL) {
+ LINK_SLIST(dstadr->peers, p, ilink);
+ dstadr->peercnt++;
+ }
+}
+
+/*
+ * attempt to re-rebind interface if necessary
+ */
+static void
+peer_refresh_interface(
+ struct peer *p
+ )
+{
+ endpt * niface;
+ endpt * piface;
+
+ niface = select_peerinterface(p, &p->srcadr, NULL);
+
+ DPRINTF(4, (
+ "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ",
+ p->dstadr == NULL ? "<null>" :
+ stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode,
+ p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags,
+ p->ttl, p->keyid));
+ if (niface != NULL) {
+ DPRINTF(4, (
+ "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s",
+ niface->fd, niface->bfd, niface->name,
+ niface->flags, niface->ifindex,
+ stoa(&niface->sin)));
+ if (niface->flags & INT_BROADCAST)
+ DPRINTF(4, (", bcast=%s",
+ stoa(&niface->bcast)));
+ DPRINTF(4, (", mask=%s\n", stoa(&niface->mask)));
+ } else {
+ DPRINTF(4, ("<NONE>\n"));
+ }
+
+ piface = p->dstadr;
+ set_peerdstadr(p, niface);
+ if (p->dstadr != NULL) {
+ /*
+ * clear crypto if we change the local address
+ */
+ if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags)
+ && MODE_BROADCAST != p->pmode)
+ peer_clear(p, "XFAC");
+
+ /*
+ * Broadcast needs the socket enabled for broadcast
+ */
+ if (MDF_BCAST & p->cast_flags)
+ enable_broadcast(p->dstadr, &p->srcadr);
+
+ /*
+ * Multicast needs the socket interface enabled for
+ * multicast
+ */
+ if (MDF_MCAST & p->cast_flags)
+ enable_multicast_if(p->dstadr, &p->srcadr);
+ }
+}
+
+
+/*
+ * refresh_all_peerinterfaces - see that all interface bindings are up
+ * to date
+ */
+void
+refresh_all_peerinterfaces(void)
+{
+ struct peer *p;
+
+ /*
+ * this is called when the interface list has changed
+ * give all peers a chance to find a better interface
+ * but only if either they don't have an address already
+ * or if the one they have hasn't worked for a while.
+ */
+ for (p = peer_list; p != NULL; p = p->p_link) {
+ if (!(p->dstadr && (p->reach & 0x3))) // Bug 2849 XOR 2043
+ peer_refresh_interface(p);
+ }
+}
+
+
+/*
+ * newpeer - initialize a new peer association
+ */
+struct peer *
+newpeer(
+ sockaddr_u * srcadr,
+ const char * hostname,
+ endpt * dstadr,
+ int ippeerlimit,
+ u_char hmode,
+ u_char version,
+ u_char minpoll,
+ u_char maxpoll,
+ u_int flags,
+ u_char cast_flags,
+ u_int32 ttl,
+ keyid_t key,
+ const char * ident
+ )
+{
+ struct peer * peer;
+ u_int hash;
+ int ip_count = 0;
+
+
+ DEBUG_REQUIRE(srcadr);
+
+#ifdef AUTOKEY
+ /*
+ * If Autokey is requested but not configured, complain loudly.
+ */
+ if (!crypto_flags) {
+ if (key > NTP_MAXKEY) {
+ return (NULL);
+
+ } else if (flags & FLAG_SKEY) {
+ msyslog(LOG_ERR, "Autokey not configured");
+ return (NULL);
+ }
+ }
+#endif /* AUTOKEY */
+
+ /*
+ * For now only pool associations have a hostname.
+ */
+ INSIST(NULL == hostname || (MDF_POOL & cast_flags));
+
+ /*
+ * First search from the beginning for an association with given
+ * remote address and mode. If an interface is given, search
+ * from there to find the association which matches that
+ * destination. If the given interface is "any", track down the
+ * actual interface, because that's what gets put into the peer
+ * structure.
+ */
+ if (dstadr != NULL) {
+ peer = findexistingpeer(srcadr, hostname, NULL, hmode,
+ cast_flags, &ip_count);
+ while (peer != NULL) {
+ if ( peer->dstadr == dstadr
+ || ( (MDF_BCLNT & cast_flags)
+ && (MDF_BCLNT & peer->cast_flags)))
+ break;
+
+ if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) &&
+ peer->dstadr == findinterface(srcadr))
+ break;
+
+ peer = findexistingpeer(srcadr, hostname, peer,
+ hmode, cast_flags, &ip_count);
+ }
+ } else {
+ /* no endpt address given */
+ peer = findexistingpeer(srcadr, hostname, NULL, hmode,
+ cast_flags, &ip_count);
+ }
+
+ /*
+ * If a peer is found, this would be a duplicate and we don't
+ * allow that. This avoids duplicate ephemeral (broadcast/
+ * multicast) and preemptible (manycast and pool) client
+ * associations.
+ */
+ if (peer != NULL) {
+ DPRINTF(2, ("newpeer(%s) found existing association\n",
+ (hostname)
+ ? hostname
+ : stoa(srcadr)));
+ return NULL;
+ }
+
+DPRINTF(1, ("newpeer(%s) found no existing and %d other associations\n",
+ (hostname)
+ ? hostname
+ : stoa(srcadr),
+ ip_count));
+
+ /* Check ippeerlimit wrt ip_count */
+ if (ippeerlimit > -1) {
+ if (ip_count + 1 > ippeerlimit) {
+ DPRINTF(2, ("newpeer(%s) denied - ippeerlimit %d\n",
+ (hostname)
+ ? hostname
+ : stoa(srcadr),
+ ippeerlimit));
+ return NULL;
+ }
+ } else {
+ DPRINTF(1, ("newpeer(%s) - ippeerlimit %d ignored\n",
+ (hostname)
+ ? hostname
+ : stoa(srcadr),
+ ippeerlimit));
+ }
+
+ /*
+ * Allocate a new peer structure. Some dirt here, since some of
+ * the initialization requires knowlege of our system state.
+ */
+ if (peer_free_count == 0)
+ getmorepeermem();
+ UNLINK_HEAD_SLIST(peer, peer_free, p_link);
+ INSIST(peer != NULL);
+ peer_free_count--;
+ peer_associations++;
+ if (FLAG_PREEMPT & flags)
+ peer_preempt++;
+
+ /*
+ * Assign an association ID and increment the system variable.
+ */
+ peer->associd = current_association_ID;
+ if (++current_association_ID == 0)
+ ++current_association_ID;
+
+ peer->srcadr = *srcadr;
+ if (hostname != NULL)
+ peer->hostname = estrdup(hostname);
+ peer->hmode = hmode;
+ peer->version = version;
+ peer->flags = flags;
+ peer->cast_flags = cast_flags;
+ set_peerdstadr(peer,
+ select_peerinterface(peer, srcadr, dstadr));
+
+ /*
+ * It is an error to set minpoll less than NTP_MINPOLL or to
+ * set maxpoll greater than NTP_MAXPOLL. However, minpoll is
+ * clamped not greater than NTP_MAXPOLL and maxpoll is clamped
+ * not less than NTP_MINPOLL without complaint. Finally,
+ * minpoll is clamped not greater than maxpoll.
+ */
+ if (minpoll == 0)
+ peer->minpoll = NTP_MINDPOLL;
+ else
+ peer->minpoll = min(minpoll, NTP_MAXPOLL);
+ if (maxpoll == 0)
+ peer->maxpoll = NTP_MAXDPOLL;
+ else
+ peer->maxpoll = max(maxpoll, NTP_MINPOLL);
+ if (peer->minpoll > peer->maxpoll)
+ peer->minpoll = peer->maxpoll;
+
+ if (peer->dstadr != NULL)
+ DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n",
+ stoa(srcadr), peer->dstadr->fd,
+ stoa(&peer->dstadr->sin)));
+ else
+ DPRINTF(3, ("newpeer(%s): local interface currently not bound\n",
+ stoa(srcadr)));
+
+ /*
+ * Broadcast needs the socket enabled for broadcast
+ */
+ if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL)
+ enable_broadcast(peer->dstadr, srcadr);
+
+ /*
+ * Multicast needs the socket interface enabled for multicast
+ */
+ if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL)
+ enable_multicast_if(peer->dstadr, srcadr);
+
+#ifdef AUTOKEY
+ if (key > NTP_MAXKEY)
+ peer->flags |= FLAG_SKEY;
+#endif /* AUTOKEY */
+ peer->ttl = ttl;
+ peer->keyid = key;
+ if (ident != NULL)
+ peer->ident = estrdup(ident);
+ peer->precision = sys_precision;
+ peer->hpoll = peer->minpoll;
+ if (cast_flags & MDF_ACAST)
+ peer_clear(peer, "ACST");
+ else if (cast_flags & MDF_POOL)
+ peer_clear(peer, "POOL");
+ else if (cast_flags & MDF_MCAST)
+ peer_clear(peer, "MCST");
+ else if (cast_flags & MDF_BCAST)
+ peer_clear(peer, "BCST");
+ else
+ peer_clear(peer, "INIT");
+ if (mode_ntpdate)
+ peer_ntpdate++;
+
+ /*
+ * Note time on statistics timers.
+ */
+ peer->timereset = current_time;
+ peer->timereachable = current_time;
+ peer->timereceived = current_time;
+
+ if (ISREFCLOCKADR(&peer->srcadr)) {
+#ifdef REFCLOCK
+ /*
+ * We let the reference clock support do clock
+ * dependent initialization. This includes setting
+ * the peer timer, since the clock may have requirements
+ * for this.
+ */
+ if (maxpoll == 0)
+ peer->maxpoll = peer->minpoll;
+ if (!refclock_newpeer(peer)) {
+ /*
+ * Dump it, something screwed up
+ */
+ set_peerdstadr(peer, NULL);
+ free_peer(peer, 0);
+ return NULL;
+ }
+#else /* REFCLOCK */
+ msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.",
+ stoa(&peer->srcadr));
+ set_peerdstadr(peer, NULL);
+ free_peer(peer, 0);
+ return NULL;
+#endif /* REFCLOCK */
+ }
+
+ /*
+ * Put the new peer in the hash tables.
+ */
+ hash = NTP_HASH_ADDR(&peer->srcadr);
+ LINK_SLIST(peer_hash[hash], peer, adr_link);
+ peer_hash_count[hash]++;
+ hash = peer->associd & NTP_HASH_MASK;
+ LINK_SLIST(assoc_hash[hash], peer, aid_link);
+ assoc_hash_count[hash]++;
+ LINK_SLIST(peer_list, peer, p_link);
+
+ restrict_source(&peer->srcadr, 0, 0);
+ mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd);
+ DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n",
+ latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode,
+ peer->version, peer->minpoll, peer->maxpoll, peer->flags,
+ peer->cast_flags, peer->ttl, peer->keyid));
+ return peer;
+}
+
+
+/*
+ * peer_clr_stats - clear peer module statistics counters
+ */
+void
+peer_clr_stats(void)
+{
+ findpeer_calls = 0;
+ assocpeer_calls = 0;
+ peer_allocations = 0;
+ peer_demobilizations = 0;
+ peer_timereset = current_time;
+}
+
+
+/*
+ * peer_reset - reset statistics counters
+ */
+void
+peer_reset(
+ struct peer *peer
+ )
+{
+ if (peer == NULL)
+ return;
+
+ peer->timereset = current_time;
+ peer->sent = 0;
+ peer->received = 0;
+ peer->processed = 0;
+ peer->badauth = 0;
+ peer->bogusorg = 0;
+ peer->oldpkt = 0;
+ peer->seldisptoolarge = 0;
+ peer->selbroken = 0;
+}
+
+
+/*
+ * peer_all_reset - reset all peer statistics counters
+ */
+void
+peer_all_reset(void)
+{
+ struct peer *peer;
+
+ for (peer = peer_list; peer != NULL; peer = peer->p_link)
+ peer_reset(peer);
+}
+
+
+/*
+ * findmanycastpeer - find and return a manycastclient or pool
+ * association matching a received response.
+ */
+struct peer *
+findmanycastpeer(
+ struct recvbuf *rbufp /* receive buffer pointer */
+ )
+{
+ struct peer *peer;
+ struct pkt *pkt;
+ l_fp p_org;
+
+ /*
+ * This routine is called upon arrival of a server-mode response
+ * to a manycastclient multicast solicitation, or to a pool
+ * server unicast solicitation. Search the peer list for a
+ * manycastclient association where the last transmit timestamp
+ * matches the response packet's originate timestamp. There can
+ * be multiple manycastclient associations, or multiple pool
+ * solicitation assocations, so this assumes the transmit
+ * timestamps are unique for such.
+ */
+ pkt = &rbufp->recv_pkt;
+ for (peer = peer_list; peer != NULL; peer = peer->p_link)
+ if (MDF_SOLICIT_MASK & peer->cast_flags) {
+ NTOHL_FP(&pkt->org, &p_org);
+ if (L_ISEQU(&p_org, &peer->aorg))
+ break;
+ }
+
+ return peer;
+}
+
+/* peer_cleanup - clean peer list prior to shutdown */
+void peer_cleanup(void)
+{
+ struct peer *peer;
+ associd_t assoc;
+
+ for (assoc = initial_association_ID; assoc != current_association_ID; assoc++) {
+ if (assoc != 0U) {
+ peer = findpeerbyassoc(assoc);
+ if (peer != NULL)
+ unpeer(peer);
+ }
+ }
+ peer = findpeerbyassoc(current_association_ID);
+ if (peer != NULL)
+ unpeer(peer);
+}