/* * ntp_peer.c - management of data maintained for peer associations */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "ntpd.h" #include "ntp_lists.h" #include "ntp_stdlib.h" #include "ntp_control.h" #include /* * 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 */ const s_char 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[CLAMP((x), 0, AM_MODES)][CLAMP((y), 0, AM_MODES)]) /* * 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; /* actually next poss. ID */ static associd_t initial_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 name+family or address. */ 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 addresses 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, TRUE, 0); peer->flags |= FLAG_DISABLED; 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; /* * Do not change the local address of a link-local * peer address. */ if ( p->dstadr != NULL && is_linklocal(&p->dstadr->sin) && dstadr != NULL) { return; } /* * Do not set the local address for a link-local IPv6 peer * to one with a different scope ID. */ if ( dstadr != NULL && IS_IPV6(&p->srcadr) && SCOPE(&dstadr->sin) != SCOPE(&p->srcadr)) { 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); } if ( !IS_MCAST(&p->srcadr) && !(FLAG_DISABLED & p->flags) && !initializing) { msyslog(LOG_INFO, "%s local addr %s -> %s", stoa(&p->srcadr), eptoa(p->dstadr), eptoa(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 ? "" : 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, ("\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 peers a chance to find a better interface. */ for (p = peer_list; p != NULL; p = p->p_link) { /* * Bug 2849 XOR 2043 * Change local address only if the peer doesn't * have a local address already or if the one * they have hasn't worked for a while. */ if (p->dstadr != NULL && (p->reach & 0x3)) { continue; } 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, "Rejecting Autokey with %s," " built without support.", stoa(srcadr)); 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); } /* * In any case, do not create an association with a duplicate * remote address (srcadr) except for undefined (zero) address. * Arguably this should be part of the logic above but * [Bug 3888] exposed a situation with manycastclient where * duplicate associations happened. */ if (NULL == peer) { for (peer = peer_list; peer != NULL; peer = peer->p_link) { if ( SOCK_EQ(srcadr, &peer->srcadr) && !SOCK_UNSPEC(srcadr) && !SOCK_UNSPEC(&peer->srcadr)) { /* leave peer non-NULL */ break; } } } /* * 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, ("%s(%s) found existing association\n", __func__, (hostname) ? hostname : stoa(srcadr))); return NULL; } #if 0 DPRINTF(1, ("newpeer(%s) found no existing and %d other associations\n", (hostname) ? hostname : stoa(srcadr), ip_count)); #endif /* 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 available association ID. Zero is reserved. */ do { while (0 == ++current_association_ID) { /* EMPTY */ } } while (NULL != findpeerbyassoc(current_association_ID)); peer->associd = 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)); /* * Zero for minpoll or maxpoll means use defaults. */ peer->maxpoll = (0 == maxpoll) ? NTP_MAXDPOLL : maxpoll; peer->minpoll = (0 == minpoll) ? NTP_MINDPOLL : minpoll; /* * Clamp maxpoll and minpoll within NTP_MINPOLL and NTP_MAXPOLL, * and further clamp minpoll less than or equal maxpoll. */ peer->maxpoll = CLAMP(peer->maxpoll, NTP_MINPOLL, NTP_MAXPOLL); peer->minpoll = CLAMP(peer->minpoll, NTP_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 addr unavailable\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 (!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, FALSE, 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; struct peer *nextpeer; for (peer = peer_list; peer != NULL; peer = nextpeer) { nextpeer = peer->p_link; unpeer(peer); } }