154359Sroberto/*
254359Sroberto * ntp_peer.c - management of data maintained for peer associations
354359Sroberto */
454359Sroberto#ifdef HAVE_CONFIG_H
554359Sroberto#include <config.h>
654359Sroberto#endif
754359Sroberto
854359Sroberto#include <stdio.h>
954359Sroberto#include <sys/types.h>
1054359Sroberto
1154359Sroberto#include "ntpd.h"
12285612Sdelphij#include "ntp_lists.h"
1354359Sroberto#include "ntp_stdlib.h"
14285612Sdelphij#include "ntp_control.h"
15182007Sroberto#include <ntp_random.h>
1654359Sroberto
1754359Sroberto/*
18285612Sdelphij *		    Table of valid association combinations
19285612Sdelphij *		    ---------------------------------------
2054359Sroberto *
2154359Sroberto *                             packet->mode
2254359Sroberto * peer->mode      | UNSPEC  ACTIVE PASSIVE  CLIENT  SERVER  BCAST
2354359Sroberto * ----------      | ---------------------------------------------
24182007Sroberto * NO_PEER         |   e       1       0       1       1       1
2554359Sroberto * ACTIVE          |   e       1       1       0       0       0
2654359Sroberto * PASSIVE         |   e       1       e       0       0       0
27285612Sdelphij * CLIENT          |   e       0       0       0       1       0
2854359Sroberto * SERVER          |   e       0       0       0       0       0
29182007Sroberto * BCAST           |   e       0       0       0       0       0
3054359Sroberto * BCLIENT         |   e       0       0       0       e       1
3154359Sroberto *
3282498Sroberto * One point to note here: a packet in BCAST mode can potentially match
3382498Sroberto * a peer in CLIENT mode, but we that is a special case and we check for
3482498Sroberto * that early in the decision process.  This avoids having to keep track
3582498Sroberto * of what kind of associations are possible etc...  We actually
3682498Sroberto * circumvent that problem by requiring that the first b(m)roadcast
3782498Sroberto * received after the change back to BCLIENT mode sets the clock.
3854359Sroberto */
39182007Sroberto#define AM_MODES	7	/* number of rows and columns */
40182007Sroberto#define NO_PEER		0	/* action when no peer is found */
4154359Sroberto
4254359Srobertoint AM[AM_MODES][AM_MODES] = {
43285612Sdelphij/*			packet->mode					    */
44285612Sdelphij/* peer { UNSPEC,   ACTIVE,     PASSIVE,    CLIENT,     SERVER,     BCAST } */
45285612Sdelphij/* mode */
46182007Sroberto/*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT,   AM_MANYCAST, AM_NEWBCL},
4754359Sroberto
4854359Sroberto/*A*/	{ AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH,  AM_NOMATCH},
4954359Sroberto
5054359Sroberto/*P*/	{ AM_ERR, AM_PROCPKT, AM_ERR,     AM_NOMATCH, AM_NOMATCH,  AM_NOMATCH},
5154359Sroberto
52285612Sdelphij/*C*/	{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT,  AM_NOMATCH},
5354359Sroberto
5454359Sroberto/*S*/	{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH,  AM_NOMATCH},
5554359Sroberto
5654359Sroberto/*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH,  AM_NOMATCH},
5754359Sroberto
58182007Sroberto/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH,  AM_PROCPKT},
5954359Sroberto};
6054359Sroberto
61285612Sdelphij#define MATCH_ASSOC(x, y)	AM[(x)][(y)]
6254359Sroberto
6354359Sroberto/*
6454359Sroberto * These routines manage the allocation of memory to peer structures
65285612Sdelphij * and the maintenance of three data structures involving all peers:
66285612Sdelphij *
67285612Sdelphij * - peer_list is a single list with all peers, suitable for scanning
68285612Sdelphij *   operations over all peers.
69285612Sdelphij * - peer_adr_hash is an array of lists indexed by hashed peer address.
70285612Sdelphij * - peer_aid_hash is an array of lists indexed by hashed associd.
71285612Sdelphij *
72285612Sdelphij * They also maintain a free list of peer structures, peer_free.
73285612Sdelphij *
74285612Sdelphij * The three main entry points are findpeer(), which looks for matching
75285612Sdelphij * peer structures in the peer list, newpeer(), which allocates a new
76285612Sdelphij * peer structure and adds it to the list, and unpeer(), which
77285612Sdelphij * demobilizes the association and deallocates the structure.
7854359Sroberto */
7954359Sroberto/*
8082498Sroberto * Peer hash tables
8154359Sroberto */
82182007Srobertostruct peer *peer_hash[NTP_HASH_SIZE];	/* peer hash table */
83285612Sdelphijint	peer_hash_count[NTP_HASH_SIZE];	/* peers in each bucket */
84182007Srobertostruct peer *assoc_hash[NTP_HASH_SIZE];	/* association ID hash table */
85285612Sdelphijint	assoc_hash_count[NTP_HASH_SIZE];/* peers in each bucket */
86285612Sdelphijstruct peer *peer_list;			/* peer structures list */
8782498Srobertostatic struct peer *peer_free;		/* peer structures free list */
88285612Sdelphijint	peer_free_count;		/* count of free structures */
8954359Sroberto
9054359Sroberto/*
9182498Sroberto * Association ID.  We initialize this value randomly, then assign a new
92285612Sdelphij * value every time an association is mobilized.
9354359Sroberto */
9482498Srobertostatic associd_t current_association_ID; /* association ID */
95285612Sdelphijstatic associd_t initial_association_ID; /* association ID */
9654359Sroberto
9754359Sroberto/*
9854359Sroberto * Memory allocation watermarks.
9954359Sroberto */
100285612Sdelphij#define	INIT_PEER_ALLOC		8	/* static preallocation */
101285612Sdelphij#define	INC_PEER_ALLOC		4	/* add N more when empty */
10254359Sroberto
10354359Sroberto/*
10454359Sroberto * Miscellaneous statistic counters which may be queried.
10554359Sroberto */
106285612Sdelphiju_long	peer_timereset;			/* time stat counters zeroed */
107285612Sdelphiju_long	findpeer_calls;			/* calls to findpeer */
108285612Sdelphiju_long	assocpeer_calls;		/* calls to findpeerbyassoc */
109285612Sdelphiju_long	peer_allocations;		/* allocations from free list */
110285612Sdelphiju_long	peer_demobilizations;		/* structs freed to free list */
111285612Sdelphijint	total_peer_structs;		/* peer structs */
112285612Sdelphijint	peer_associations;		/* mobilized associations */
113285612Sdelphijint	peer_preempt;			/* preemptable associations */
11482498Srobertostatic struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */
11554359Sroberto
116285612Sdelphijstatic struct peer *	findexistingpeer_name(const char *, u_short,
117285612Sdelphij					      struct peer *, int);
118285612Sdelphijstatic struct peer *	findexistingpeer_addr(sockaddr_u *,
119285612Sdelphij					      struct peer *, int,
120285612Sdelphij					      u_char);
121285612Sdelphijstatic void		free_peer(struct peer *, int);
122285612Sdelphijstatic void		getmorepeermem(void);
123285612Sdelphijstatic int		score(struct peer *);
12454359Sroberto
125285612Sdelphij
12654359Sroberto/*
12754359Sroberto * init_peer - initialize peer data structures and counters
12854359Sroberto *
12982498Sroberto * N.B. We use the random number routine in here. It had better be
13082498Sroberto * initialized prior to getting here.
13154359Sroberto */
13254359Srobertovoid
13354359Srobertoinit_peer(void)
13454359Sroberto{
135285612Sdelphij	int i;
13654359Sroberto
13754359Sroberto	/*
138285612Sdelphij	 * Initialize peer free list from static allocation.
13954359Sroberto	 */
140285612Sdelphij	for (i = COUNTOF(init_peer_alloc) - 1; i >= 0; i--)
141285612Sdelphij		LINK_SLIST(peer_free, &init_peer_alloc[i], p_link);
142285612Sdelphij	total_peer_structs = COUNTOF(init_peer_alloc);
143285612Sdelphij	peer_free_count = COUNTOF(init_peer_alloc);
14454359Sroberto
14554359Sroberto	/*
14654359Sroberto	 * Initialize our first association ID
14754359Sroberto	 */
148285612Sdelphij	do
149285612Sdelphij		current_association_ID = ntp_random() & ASSOCID_MAX;
150285612Sdelphij	while (!current_association_ID);
151285612Sdelphij	initial_association_ID = current_association_ID;
15254359Sroberto}
15354359Sroberto
15454359Sroberto
15554359Sroberto/*
15654359Sroberto * getmorepeermem - add more peer structures to the free list
15754359Sroberto */
15854359Srobertostatic void
15954359Srobertogetmorepeermem(void)
16054359Sroberto{
161285612Sdelphij	int i;
162285612Sdelphij	struct peer *peers;
16354359Sroberto
164285612Sdelphij	peers = emalloc_zero(INC_PEER_ALLOC * sizeof(*peers));
16554359Sroberto
166285612Sdelphij	for (i = INC_PEER_ALLOC - 1; i >= 0; i--)
167285612Sdelphij		LINK_SLIST(peer_free, &peers[i], p_link);
168285612Sdelphij
16954359Sroberto	total_peer_structs += INC_PEER_ALLOC;
17054359Sroberto	peer_free_count += INC_PEER_ALLOC;
17154359Sroberto}
17254359Sroberto
17354359Sroberto
174285612Sdelphijstatic struct peer *
175285612Sdelphijfindexistingpeer_name(
176285612Sdelphij	const char *	hostname,
177285612Sdelphij	u_short		hname_fam,
178285612Sdelphij	struct peer *	start_peer,
179285612Sdelphij	int		mode
18054359Sroberto	)
18154359Sroberto{
182285612Sdelphij	struct peer *p;
18354359Sroberto
184285612Sdelphij	if (NULL == start_peer)
185285612Sdelphij		p = peer_list;
186285612Sdelphij	else
187285612Sdelphij		p = start_peer->p_link;
188285612Sdelphij	for (; p != NULL; p = p->p_link)
189285612Sdelphij		if (p->hostname != NULL
190285612Sdelphij		    && (-1 == mode || p->hmode == mode)
191285612Sdelphij		    && (AF_UNSPEC == hname_fam
192285612Sdelphij			|| AF_UNSPEC == AF(&p->srcadr)
193285612Sdelphij			|| hname_fam == AF(&p->srcadr))
194285612Sdelphij		    && !strcasecmp(p->hostname, hostname))
195285612Sdelphij			break;
196285612Sdelphij	return p;
197285612Sdelphij}
198285612Sdelphij
199285612Sdelphij
200285612Sdelphijstatic
201285612Sdelphijstruct peer *
202285612Sdelphijfindexistingpeer_addr(
203285612Sdelphij	sockaddr_u *	addr,
204285612Sdelphij	struct peer *	start_peer,
205285612Sdelphij	int		mode,
206285612Sdelphij	u_char		cast_flags
207285612Sdelphij	)
208285612Sdelphij{
209285612Sdelphij	struct peer *peer;
210285612Sdelphij
211285612Sdelphij	DPRINTF(2, ("findexistingpeer_addr(%s, %s, %d, 0x%x)\n",
212285612Sdelphij		sptoa(addr),
213285612Sdelphij		(start_peer)
214285612Sdelphij		    ? sptoa(&start_peer->srcadr)
215285612Sdelphij		    : "NULL",
216285612Sdelphij		mode, (u_int)cast_flags));
217285612Sdelphij
21854359Sroberto	/*
21954359Sroberto	 * start_peer is included so we can locate instances of the
22054359Sroberto	 * same peer through different interfaces in the hash table.
221285612Sdelphij	 * Without MDF_BCLNT, a match requires the same mode and remote
222285612Sdelphij	 * address.  MDF_BCLNT associations start out as MODE_CLIENT
223285612Sdelphij	 * if broadcastdelay is not specified, and switch to
224285612Sdelphij	 * MODE_BCLIENT after estimating the one-way delay.  Duplicate
225285612Sdelphij	 * associations are expanded in definition to match any other
226285612Sdelphij	 * MDF_BCLNT with the same srcadr (remote, unicast address).
22754359Sroberto	 */
228285612Sdelphij	if (NULL == start_peer)
229182007Sroberto		peer = peer_hash[NTP_HASH_ADDR(addr)];
23054359Sroberto	else
231285612Sdelphij		peer = start_peer->adr_link;
23254359Sroberto
233285612Sdelphij	while (peer != NULL) {
234285612Sdelphij		DPRINTF(3, ("%s %s %d %d 0x%x 0x%x ", sptoa(addr),
235285612Sdelphij			sptoa(&peer->srcadr), mode, peer->hmode,
236285612Sdelphij			(u_int)cast_flags, (u_int)peer->cast_flags));
237285612Sdelphij 		if ((-1 == mode || peer->hmode == mode ||
238285612Sdelphij		     ((MDF_BCLNT & peer->cast_flags) &&
239285612Sdelphij		      (MDF_BCLNT & cast_flags))) &&
240285612Sdelphij		    ADDR_PORT_EQ(addr, &peer->srcadr)) {
241285612Sdelphij			DPRINTF(3, ("found.\n"));
242285612Sdelphij			break;
24354359Sroberto		}
244285612Sdelphij		DPRINTF(3, ("\n"));
245285612Sdelphij		peer = peer->adr_link;
24654359Sroberto	}
247285612Sdelphij
248285612Sdelphij	return peer;
24954359Sroberto}
25054359Sroberto
25154359Sroberto
25254359Sroberto/*
253285612Sdelphij * findexistingpeer - search by address and return a pointer to a peer.
25454359Sroberto */
25554359Srobertostruct peer *
256285612Sdelphijfindexistingpeer(
257285612Sdelphij	sockaddr_u *	addr,
258285612Sdelphij	const char *	hostname,
259285612Sdelphij	struct peer *	start_peer,
260285612Sdelphij	int		mode,
261285612Sdelphij	u_char		cast_flags
262285612Sdelphij	)
263285612Sdelphij{
264285612Sdelphij	if (hostname != NULL)
265285612Sdelphij		return findexistingpeer_name(hostname, AF(addr),
266285612Sdelphij					     start_peer, mode);
267285612Sdelphij	else
268285612Sdelphij		return findexistingpeer_addr(addr, start_peer, mode,
269285612Sdelphij					     cast_flags);
270285612Sdelphij}
271285612Sdelphij
272285612Sdelphij
273285612Sdelphij/*
274285612Sdelphij * findpeer - find and return a peer match for a received datagram in
275285612Sdelphij *	      the peer_hash table.
276310419Sdelphij *
277310419Sdelphij * [Bug 3072] To faciliate a faster reorganisation after routing changes
278310419Sdelphij * the original code re-assigned the peer address to be the destination
279310419Sdelphij * of the received packet and initiated another round on a mismatch.
280310419Sdelphij * Unfortunately this leaves us wide open for a DoS attack where the
281310419Sdelphij * attacker directs a packet with forged destination address to us --
282310419Sdelphij * this results in a wrong interface assignment, actually creating a DoS
283310419Sdelphij * situation.
284310419Sdelphij *
285310419Sdelphij * This condition would persist until the next update of the interface
286310419Sdelphij * list, but a continued attack would put us out of business again soon
287310419Sdelphij * enough. Authentication alone does not help here, since it does not
288310419Sdelphij * protect the UDP layer and leaves us open for a replay attack.
289310419Sdelphij *
290310419Sdelphij * So we do not update the adresses and wait until the next interface
291310419Sdelphij * list update does the right thing for us.
292285612Sdelphij */
293285612Sdelphijstruct peer *
29454359Srobertofindpeer(
295285612Sdelphij	struct recvbuf *rbufp,
296285612Sdelphij	int		pkt_mode,
297285612Sdelphij	int *		action
29854359Sroberto	)
29954359Sroberto{
300285612Sdelphij	struct peer *	p;
301285612Sdelphij	sockaddr_u *	srcadr;
302285612Sdelphij	u_int		hash;
303285612Sdelphij	struct pkt *	pkt;
304285612Sdelphij	l_fp		pkt_org;
30554359Sroberto
30654359Sroberto	findpeer_calls++;
307285612Sdelphij	srcadr = &rbufp->recv_srcadr;
308182007Sroberto	hash = NTP_HASH_ADDR(srcadr);
309285612Sdelphij	for (p = peer_hash[hash]; p != NULL; p = p->adr_link) {
31082498Sroberto
311310419Sdelphij		/* [Bug 3072] ensure interface of peer matches */
312310419Sdelphij		if (p->dstadr != rbufp->dstadr)
313310419Sdelphij			continue;
31454359Sroberto
315310419Sdelphij		/* ensure peer source address matches */
316310419Sdelphij		if ( ! ADDR_PORT_EQ(srcadr, &p->srcadr))
317310419Sdelphij			continue;
318310419Sdelphij
319310419Sdelphij		/* If the association matching rules determine that this
320310419Sdelphij		 * is not a valid combination, then look for the next
321310419Sdelphij		 * valid peer association.
322310419Sdelphij		 */
323310419Sdelphij		*action = MATCH_ASSOC(p->hmode, pkt_mode);
324285612Sdelphij
325310419Sdelphij		/* A response to our manycastclient solicitation might
326310419Sdelphij		 * be misassociated with an ephemeral peer already spun
327310419Sdelphij		 * for the server.  If the packet's org timestamp
328310419Sdelphij		 * doesn't match the peer's, check if it matches the
329310419Sdelphij		 * ACST prototype peer's.  If so it is a redundant
330310419Sdelphij		 * solicitation response, return AM_ERR to discard it.
331310419Sdelphij		 * [Bug 1762]
332310419Sdelphij		 */
333310419Sdelphij		if (MODE_SERVER == pkt_mode && AM_PROCPKT == *action) {
334310419Sdelphij			pkt = &rbufp->recv_pkt;
335310419Sdelphij			NTOHL_FP(&pkt->org, &pkt_org);
336310419Sdelphij			if (!L_ISEQU(&p->aorg, &pkt_org) &&
337310419Sdelphij			    findmanycastpeer(rbufp))
338310419Sdelphij				*action = AM_ERR;
339310419Sdelphij		}
34054359Sroberto
341310419Sdelphij		/* if an error was returned, exit back right here. */
342310419Sdelphij		if (*action == AM_ERR)
343310419Sdelphij			return NULL;
344310419Sdelphij
345310419Sdelphij		/* if a match is found, we stop our search. */
346310419Sdelphij		if (*action != AM_NOMATCH)
347310419Sdelphij			break;
34854359Sroberto	}
34954359Sroberto
350310419Sdelphij	/* If no matching association is found... */
351310419Sdelphij	if (NULL == p)
35254359Sroberto		*action = MATCH_ASSOC(NO_PEER, pkt_mode);
353310419Sdelphij
354285612Sdelphij	return p;
35554359Sroberto}
35654359Sroberto
35754359Sroberto/*
358285612Sdelphij * findpeerbyassoc - find and return a peer using his association ID
35954359Sroberto */
36054359Srobertostruct peer *
36154359Srobertofindpeerbyassoc(
362285612Sdelphij	associd_t assoc
36354359Sroberto	)
36454359Sroberto{
365285612Sdelphij	struct peer *p;
366285612Sdelphij	u_int hash;
36754359Sroberto
36854359Sroberto	assocpeer_calls++;
369182007Sroberto	hash = assoc & NTP_HASH_MASK;
370285612Sdelphij	for (p = assoc_hash[hash]; p != NULL; p = p->aid_link)
371285612Sdelphij		if (assoc == p->associd)
372285612Sdelphij			break;
373285612Sdelphij	return p;
37454359Sroberto}
37554359Sroberto
37654359Sroberto
37754359Sroberto/*
37882498Sroberto * clear_all - flush all time values for all associations
37954359Sroberto */
38054359Srobertovoid
38182498Srobertoclear_all(void)
38254359Sroberto{
383285612Sdelphij	struct peer *p;
38454359Sroberto
38582498Sroberto	/*
38682498Sroberto	 * This routine is called when the clock is stepped, and so all
38782498Sroberto	 * previously saved time values are untrusted.
38882498Sroberto	 */
389285612Sdelphij	for (p = peer_list; p != NULL; p = p->p_link)
390285612Sdelphij		if (!(MDF_TXONLY_MASK & p->cast_flags))
391285612Sdelphij			peer_clear(p, "STEP");
392285612Sdelphij
393285612Sdelphij	DPRINTF(1, ("clear_all: at %lu\n", current_time));
39454359Sroberto}
39582498Sroberto
39682498Sroberto
39754359Sroberto/*
398285612Sdelphij * score_all() - determine if an association can be demobilized
39954359Sroberto */
400285612Sdelphijint
401285612Sdelphijscore_all(
402285612Sdelphij	struct peer *peer	/* peer structure pointer */
40354359Sroberto	)
40454359Sroberto{
405285612Sdelphij	struct peer *speer;
406285612Sdelphij	int	temp, tamp;
407285612Sdelphij	int	x;
40854359Sroberto
409285612Sdelphij	/*
410285612Sdelphij	 * This routine finds the minimum score for all preemptible
411285612Sdelphij	 * associations and returns > 0 if the association can be
412285612Sdelphij	 * demobilized.
413285612Sdelphij	 */
414285612Sdelphij	tamp = score(peer);
415285612Sdelphij	temp = 100;
416285612Sdelphij	for (speer = peer_list; speer != NULL; speer = speer->p_link)
417285612Sdelphij		if (speer->flags & FLAG_PREEMPT) {
418285612Sdelphij			x = score(speer);
419285612Sdelphij			if (x < temp)
420285612Sdelphij				temp = x;
421285612Sdelphij		}
422285612Sdelphij	DPRINTF(1, ("score_all: at %lu score %d min %d\n",
423285612Sdelphij		    current_time, tamp, temp));
424182007Sroberto
425285612Sdelphij	if (tamp != temp)
426285612Sdelphij		temp = 0;
427182007Sroberto
428285612Sdelphij	return temp;
429285612Sdelphij}
430285612Sdelphij
431285612Sdelphij
432285612Sdelphij/*
433285612Sdelphij * score() - calculate preemption score
434285612Sdelphij */
435285612Sdelphijstatic int
436285612Sdelphijscore(
437285612Sdelphij	struct peer *peer	/* peer structure pointer */
438285612Sdelphij	)
439285612Sdelphij{
440285612Sdelphij	int	temp;
441285612Sdelphij
44254359Sroberto	/*
443285612Sdelphij	 * This routine calculates the premption score from the peer
444285612Sdelphij	 * error bits and status. Increasing values are more cherished.
44554359Sroberto	 */
446285612Sdelphij	temp = 0;
447285612Sdelphij	if (!(peer->flash & TEST10))
448285612Sdelphij		temp++;			/* 1 good synch and stratum */
449285612Sdelphij	if (!(peer->flash & TEST13))
450285612Sdelphij		temp++;			/* 2 reachable */
451285612Sdelphij	if (!(peer->flash & TEST12))
452285612Sdelphij		temp++;			/* 3 no loop */
453285612Sdelphij	if (!(peer->flash & TEST11))
454285612Sdelphij		temp++;			/* 4 good distance */
455285612Sdelphij	if (peer->status >= CTL_PST_SEL_SELCAND)
456285612Sdelphij		temp++;			/* 5 in the hunt */
457285612Sdelphij	if (peer->status != CTL_PST_SEL_EXCESS)
458285612Sdelphij		temp++;			/* 6 not spare tire */
459285612Sdelphij	return (temp);			/* selection status */
460285612Sdelphij}
46154359Sroberto
462285612Sdelphij
463285612Sdelphij/*
464285612Sdelphij * free_peer - internal routine to free memory referred to by a struct
465285612Sdelphij *	       peer and return it to the peer free list.  If unlink is
466285612Sdelphij *	       nonzero, unlink from the various lists.
467285612Sdelphij */
468285612Sdelphijstatic void
469285612Sdelphijfree_peer(
470285612Sdelphij	struct peer *	p,
471285612Sdelphij	int		unlink_peer
472285612Sdelphij	)
473285612Sdelphij{
474285612Sdelphij	struct peer *	unlinked;
475285612Sdelphij	int		hash;
476285612Sdelphij
477285612Sdelphij	if (unlink_peer) {
478285612Sdelphij		hash = NTP_HASH_ADDR(&p->srcadr);
479285612Sdelphij		peer_hash_count[hash]--;
480285612Sdelphij
481285612Sdelphij		UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link,
482285612Sdelphij			     struct peer);
483285612Sdelphij		if (NULL == unlinked) {
48454359Sroberto			peer_hash_count[hash]++;
485285612Sdelphij			msyslog(LOG_ERR, "peer %s not in address table!",
486285612Sdelphij				stoa(&p->srcadr));
48754359Sroberto		}
48854359Sroberto
489285612Sdelphij		/*
490285612Sdelphij		 * Remove him from the association hash as well.
491285612Sdelphij		 */
492285612Sdelphij		hash = p->associd & NTP_HASH_MASK;
493285612Sdelphij		assoc_hash_count[hash]--;
49454359Sroberto
495285612Sdelphij		UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link,
496285612Sdelphij			     struct peer);
497285612Sdelphij		if (NULL == unlinked) {
49854359Sroberto			assoc_hash_count[hash]++;
49954359Sroberto			msyslog(LOG_ERR,
500285612Sdelphij				"peer %s not in association ID table!",
501285612Sdelphij				stoa(&p->srcadr));
50254359Sroberto		}
503285612Sdelphij
504285612Sdelphij		/* Remove him from the overall list. */
505285612Sdelphij		UNLINK_SLIST(unlinked, peer_list, p, p_link,
506285612Sdelphij			     struct peer);
507285612Sdelphij		if (NULL == unlinked)
508285612Sdelphij			msyslog(LOG_ERR, "%s not in peer list!",
509285612Sdelphij				stoa(&p->srcadr));
51054359Sroberto	}
511285612Sdelphij
512285612Sdelphij	if (p->hostname != NULL)
513285612Sdelphij		free(p->hostname);
514285612Sdelphij
515285612Sdelphij	if (p->ident != NULL)
516285612Sdelphij		free(p->ident);
517285612Sdelphij
518285612Sdelphij	if (p->addrs != NULL)
519285612Sdelphij		free(p->addrs);		/* from copy_addrinfo_list() */
520285612Sdelphij
521285612Sdelphij	/* Add his corporeal form to peer free list */
522285612Sdelphij	ZERO(*p);
523285612Sdelphij	LINK_SLIST(peer_free, p, p_link);
52454359Sroberto	peer_free_count++;
52554359Sroberto}
52654359Sroberto
52754359Sroberto
52854359Sroberto/*
529285612Sdelphij * unpeer - remove peer structure from hash table and free structure
530285612Sdelphij */
531285612Sdelphijvoid
532285612Sdelphijunpeer(
533285612Sdelphij	struct peer *peer
534285612Sdelphij	)
535285612Sdelphij{
536285612Sdelphij	mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd);
537285612Sdelphij	restrict_source(&peer->srcadr, 1, 0);
538285612Sdelphij	set_peerdstadr(peer, NULL);
539285612Sdelphij	peer_demobilizations++;
540285612Sdelphij	peer_associations--;
541285612Sdelphij	if (FLAG_PREEMPT & peer->flags)
542285612Sdelphij		peer_preempt--;
543285612Sdelphij#ifdef REFCLOCK
544285612Sdelphij	/*
545285612Sdelphij	 * If this peer is actually a clock, shut it down first
546285612Sdelphij	 */
547285612Sdelphij	if (FLAG_REFCLOCK & peer->flags)
548285612Sdelphij		refclock_unpeer(peer);
549285612Sdelphij#endif
550285612Sdelphij
551285612Sdelphij	free_peer(peer, TRUE);
552285612Sdelphij}
553285612Sdelphij
554285612Sdelphij
555285612Sdelphij/*
55682498Sroberto * peer_config - configure a new association
55754359Sroberto */
55854359Srobertostruct peer *
55954359Srobertopeer_config(
560285612Sdelphij	sockaddr_u *	srcadr,
561285612Sdelphij	const char *	hostname,
562285612Sdelphij	endpt *		dstadr,
563285612Sdelphij	u_char		hmode,
564285612Sdelphij	u_char		version,
565285612Sdelphij	u_char		minpoll,
566285612Sdelphij	u_char		maxpoll,
567285612Sdelphij	u_int		flags,
568285612Sdelphij	u_int32		ttl,
569285612Sdelphij	keyid_t		key,
570285612Sdelphij	const char *	ident		/* autokey group */
57154359Sroberto	)
57254359Sroberto{
573132451Sroberto	u_char cast_flags;
57454359Sroberto
57554359Sroberto	/*
57682498Sroberto	 * We do a dirty little jig to figure the cast flags. This is
57782498Sroberto	 * probably not the best place to do this, at least until the
57882498Sroberto	 * configure code is rebuilt. Note only one flag can be set.
57954359Sroberto	 */
58082498Sroberto	switch (hmode) {
58182498Sroberto	case MODE_BROADCAST:
582285612Sdelphij		if (IS_MCAST(srcadr))
583285612Sdelphij			cast_flags = MDF_MCAST;
584285612Sdelphij		else
585285612Sdelphij			cast_flags = MDF_BCAST;
586285612Sdelphij		break;
58782498Sroberto
58882498Sroberto	case MODE_CLIENT:
589285612Sdelphij		if (hostname != NULL && SOCK_UNSPEC(srcadr))
590285612Sdelphij			cast_flags = MDF_POOL;
591285612Sdelphij		else if (IS_MCAST(srcadr))
592285612Sdelphij			cast_flags = MDF_ACAST;
593285612Sdelphij		else
594285612Sdelphij			cast_flags = MDF_UCAST;
595285612Sdelphij		break;
59682498Sroberto
59782498Sroberto	default:
59882498Sroberto		cast_flags = MDF_UCAST;
59982498Sroberto	}
60082498Sroberto
60182498Sroberto	/*
602285612Sdelphij	 * Mobilize the association and initialize its variables. If
603285612Sdelphij	 * emulating ntpdate, force iburst.  For pool and manycastclient
604285612Sdelphij	 * strip FLAG_PREEMPT as the prototype associations are not
605285612Sdelphij	 * themselves preemptible, though the resulting associations
606285612Sdelphij	 * are.
60782498Sroberto	 */
608285612Sdelphij	flags |= FLAG_CONFIG;
609132451Sroberto	if (mode_ntpdate)
610132451Sroberto		flags |= FLAG_IBURST;
611285612Sdelphij	if ((MDF_ACAST | MDF_POOL) & cast_flags)
612285612Sdelphij		flags &= ~FLAG_PREEMPT;
613285612Sdelphij	return newpeer(srcadr, hostname, dstadr, hmode, version,
614285612Sdelphij	    minpoll, maxpoll, flags, cast_flags, ttl, key, ident);
61554359Sroberto}
61654359Sroberto
617182007Sroberto/*
618285612Sdelphij * setup peer dstadr field keeping it in sync with the interface
619285612Sdelphij * structures
620182007Sroberto */
621182007Srobertovoid
622285612Sdelphijset_peerdstadr(
623285612Sdelphij	struct peer *	p,
624285612Sdelphij	endpt *		dstadr
625285612Sdelphij	)
626182007Sroberto{
627285612Sdelphij	struct peer *	unlinked;
62854359Sroberto
629310419Sdelphij	DEBUG_INSIST(p != NULL);
630310419Sdelphij
631310419Sdelphij	if (p == NULL)
632310419Sdelphij		return;
633310419Sdelphij
634310419Sdelphij	/* check for impossible or identical assignment */
635285612Sdelphij	if (p->dstadr == dstadr)
636285612Sdelphij		return;
637182007Sroberto
638285612Sdelphij	/*
639285612Sdelphij	 * Don't accept updates to a separate multicast receive-only
640285612Sdelphij	 * endpt while a BCLNT peer is running its unicast protocol.
641285612Sdelphij	 */
642285612Sdelphij	if (dstadr != NULL && (FLAG_BC_VOL & p->flags) &&
643285612Sdelphij	    (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) {
644285612Sdelphij		return;
645182007Sroberto	}
646310419Sdelphij
647310419Sdelphij	/* unlink from list if we have an address prior to assignment */
648285612Sdelphij	if (p->dstadr != NULL) {
649285612Sdelphij		p->dstadr->peercnt--;
650285612Sdelphij		UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink,
651285612Sdelphij			     struct peer);
652285612Sdelphij		msyslog(LOG_INFO, "%s local addr %s -> %s",
653285612Sdelphij			stoa(&p->srcadr), latoa(p->dstadr),
654285612Sdelphij			latoa(dstadr));
655285612Sdelphij	}
656310419Sdelphij
657285612Sdelphij	p->dstadr = dstadr;
658310419Sdelphij
659310419Sdelphij	/* link to list if we have an address after assignment */
660310419Sdelphij	if (p->dstadr != NULL) {
661285612Sdelphij		LINK_SLIST(dstadr->peers, p, ilink);
662285612Sdelphij		dstadr->peercnt++;
663285612Sdelphij	}
664182007Sroberto}
665182007Sroberto
66654359Sroberto/*
667182007Sroberto * attempt to re-rebind interface if necessary
668182007Sroberto */
669182007Srobertostatic void
670285612Sdelphijpeer_refresh_interface(
671285612Sdelphij	struct peer *p
672285612Sdelphij	)
673182007Sroberto{
674285612Sdelphij	endpt *	niface;
675285612Sdelphij	endpt *	piface;
676182007Sroberto
677285612Sdelphij	niface = select_peerinterface(p, &p->srcadr, NULL);
678182007Sroberto
679285612Sdelphij	DPRINTF(4, (
680285612Sdelphij	    "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ",
681285612Sdelphij	    p->dstadr == NULL ? "<null>" :
682285612Sdelphij	    stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode,
683285612Sdelphij	    p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags,
684285612Sdelphij	    p->ttl, p->keyid));
685285612Sdelphij	if (niface != NULL) {
686285612Sdelphij		DPRINTF(4, (
687285612Sdelphij		    "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s",
688285612Sdelphij		    niface->fd,  niface->bfd, niface->name,
689285612Sdelphij		    niface->flags, niface->ifindex,
690285612Sdelphij		    stoa(&niface->sin)));
691285612Sdelphij		if (niface->flags & INT_BROADCAST)
692285612Sdelphij			DPRINTF(4, (", bcast=%s",
693285612Sdelphij				stoa(&niface->bcast)));
694285612Sdelphij		DPRINTF(4, (", mask=%s\n", stoa(&niface->mask)));
695285612Sdelphij	} else {
696285612Sdelphij		DPRINTF(4, ("<NONE>\n"));
697182007Sroberto	}
698182007Sroberto
699285612Sdelphij	piface = p->dstadr;
700285612Sdelphij	set_peerdstadr(p, niface);
701285612Sdelphij	if (p->dstadr != NULL) {
702285612Sdelphij		/*
703285612Sdelphij		 * clear crypto if we change the local address
704285612Sdelphij		 */
705285612Sdelphij		if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags)
706285612Sdelphij		    && MODE_BROADCAST != p->pmode)
707285612Sdelphij			peer_clear(p, "XFAC");
708182007Sroberto
709182007Sroberto		/*
710182007Sroberto	 	 * Broadcast needs the socket enabled for broadcast
711182007Sroberto	 	 */
712285612Sdelphij		if (MDF_BCAST & p->cast_flags)
713285612Sdelphij			enable_broadcast(p->dstadr, &p->srcadr);
714182007Sroberto
715182007Sroberto		/*
716285612Sdelphij	 	 * Multicast needs the socket interface enabled for
717285612Sdelphij		 * multicast
718182007Sroberto	 	 */
719285612Sdelphij		if (MDF_MCAST & p->cast_flags)
720285612Sdelphij			enable_multicast_if(p->dstadr, &p->srcadr);
721182007Sroberto	}
722182007Sroberto}
723182007Sroberto
724285612Sdelphij
725182007Sroberto/*
726285612Sdelphij * refresh_all_peerinterfaces - see that all interface bindings are up
727285612Sdelphij * to date
728182007Sroberto */
729182007Srobertovoid
730182007Srobertorefresh_all_peerinterfaces(void)
731182007Sroberto{
732285612Sdelphij	struct peer *p;
733182007Sroberto
734182007Sroberto	/*
735182007Sroberto	 * this is called when the interface list has changed
736182007Sroberto	 * give all peers a chance to find a better interface
737289999Sglebius	 * but only if either they don't have an address already
738289999Sglebius	 * or if the one they have hasn't worked for a while.
739182007Sroberto	 */
740289999Sglebius	for (p = peer_list; p != NULL; p = p->p_link) {
741289999Sglebius		if (!(p->dstadr && (p->reach & 0x3)))	// Bug 2849 XOR 2043
742289999Sglebius			peer_refresh_interface(p);
743289999Sglebius	}
744182007Sroberto}
745182007Sroberto
746285612Sdelphij
747182007Sroberto/*
748285612Sdelphij * newpeer - initialize a new peer association
749182007Sroberto */
750285612Sdelphijstruct peer *
751285612Sdelphijnewpeer(
752285612Sdelphij	sockaddr_u *	srcadr,
753285612Sdelphij	const char *	hostname,
754285612Sdelphij	endpt *		dstadr,
755285612Sdelphij	u_char		hmode,
756285612Sdelphij	u_char		version,
757285612Sdelphij	u_char		minpoll,
758285612Sdelphij	u_char		maxpoll,
759285612Sdelphij	u_int		flags,
760285612Sdelphij	u_char		cast_flags,
761285612Sdelphij	u_int32		ttl,
762285612Sdelphij	keyid_t		key,
763285612Sdelphij	const char *	ident
764285612Sdelphij	)
765182007Sroberto{
766285612Sdelphij	struct peer *	peer;
767285612Sdelphij	u_int		hash;
768285612Sdelphij
769289999Sglebius	DEBUG_REQUIRE(srcadr);
770289999Sglebius
771285612Sdelphij#ifdef AUTOKEY
772182007Sroberto	/*
773285612Sdelphij	 * If Autokey is requested but not configured, complain loudly.
774182007Sroberto	 */
775285612Sdelphij	if (!crypto_flags) {
776285612Sdelphij		if (key > NTP_MAXKEY) {
777285612Sdelphij			return (NULL);
778182007Sroberto
779285612Sdelphij		} else if (flags & FLAG_SKEY) {
780285612Sdelphij			msyslog(LOG_ERR, "Autokey not configured");
781285612Sdelphij			return (NULL);
782285612Sdelphij		}
783285612Sdelphij	}
784285612Sdelphij#endif	/* AUTOKEY */
785285612Sdelphij
786182007Sroberto	/*
787285612Sdelphij	 * For now only pool associations have a hostname.
788182007Sroberto	 */
789289999Sglebius	INSIST(NULL == hostname || (MDF_POOL & cast_flags));
790182007Sroberto
791285612Sdelphij	/*
792285612Sdelphij	 * First search from the beginning for an association with given
793285612Sdelphij	 * remote address and mode. If an interface is given, search
794285612Sdelphij	 * from there to find the association which matches that
795285612Sdelphij	 * destination. If the given interface is "any", track down the
796285612Sdelphij	 * actual interface, because that's what gets put into the peer
797285612Sdelphij	 * structure.
798285612Sdelphij	 */
799285612Sdelphij	if (dstadr != NULL) {
800285612Sdelphij		peer = findexistingpeer(srcadr, hostname, NULL, hmode,
801285612Sdelphij					cast_flags);
802285612Sdelphij		while (peer != NULL) {
803285612Sdelphij			if (peer->dstadr == dstadr ||
804285612Sdelphij			    ((MDF_BCLNT & cast_flags) &&
805285612Sdelphij			     (MDF_BCLNT & peer->cast_flags)))
806285612Sdelphij				break;
807182007Sroberto
808285612Sdelphij			if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) &&
809285612Sdelphij			    peer->dstadr == findinterface(srcadr))
810285612Sdelphij				break;
811182007Sroberto
812285612Sdelphij			peer = findexistingpeer(srcadr, hostname, peer,
813285612Sdelphij						hmode, cast_flags);
814285612Sdelphij		}
815285612Sdelphij	} else {
816285612Sdelphij		/* no endpt address given */
817285612Sdelphij		peer = findexistingpeer(srcadr, hostname, NULL, hmode,
818285612Sdelphij					cast_flags);
819285612Sdelphij	}
82054359Sroberto
82154359Sroberto	/*
822285612Sdelphij	 * If a peer is found, this would be a duplicate and we don't
823285612Sdelphij	 * allow that. This avoids duplicate ephemeral (broadcast/
824285612Sdelphij	 * multicast) and preemptible (manycast and pool) client
825285612Sdelphij	 * associations.
826285612Sdelphij	 */
827285612Sdelphij	if (peer != NULL) {
828285612Sdelphij		DPRINTF(2, ("newpeer(%s) found existing association\n",
829285612Sdelphij			(hostname)
830285612Sdelphij			    ? hostname
831285612Sdelphij			    : stoa(srcadr)));
832285612Sdelphij		return NULL;
833285612Sdelphij	}
834285612Sdelphij
835285612Sdelphij	/*
83682498Sroberto	 * Allocate a new peer structure. Some dirt here, since some of
83782498Sroberto	 * the initialization requires knowlege of our system state.
83854359Sroberto	 */
83954359Sroberto	if (peer_free_count == 0)
84082498Sroberto		getmorepeermem();
841285612Sdelphij	UNLINK_HEAD_SLIST(peer, peer_free, p_link);
842289999Sglebius	INSIST(peer != NULL);
84354359Sroberto	peer_free_count--;
84454359Sroberto	peer_associations++;
845285612Sdelphij	if (FLAG_PREEMPT & flags)
846182007Sroberto		peer_preempt++;
84754359Sroberto
84854359Sroberto	/*
849132451Sroberto	 * Assign an association ID and increment the system variable.
850132451Sroberto	 */
851132451Sroberto	peer->associd = current_association_ID;
852132451Sroberto	if (++current_association_ID == 0)
853132451Sroberto		++current_association_ID;
854132451Sroberto
85582498Sroberto	peer->srcadr = *srcadr;
856285612Sdelphij	if (hostname != NULL)
857285612Sdelphij		peer->hostname = estrdup(hostname);
858285612Sdelphij	peer->hmode = hmode;
859285612Sdelphij	peer->version = version;
860132451Sroberto	peer->flags = flags;
861285612Sdelphij	peer->cast_flags = cast_flags;
862285612Sdelphij	set_peerdstadr(peer,
863285612Sdelphij		       select_peerinterface(peer, srcadr, dstadr));
864182007Sroberto
865182007Sroberto	/*
866285612Sdelphij	 * It is an error to set minpoll less than NTP_MINPOLL or to
867285612Sdelphij	 * set maxpoll greater than NTP_MAXPOLL. However, minpoll is
868285612Sdelphij	 * clamped not greater than NTP_MAXPOLL and maxpoll is clamped
869285612Sdelphij	 * not less than NTP_MINPOLL without complaint. Finally,
870285612Sdelphij	 * minpoll is clamped not greater than maxpoll.
871285612Sdelphij	 */
872285612Sdelphij	if (minpoll == 0)
873285612Sdelphij		peer->minpoll = NTP_MINDPOLL;
874285612Sdelphij	else
875285612Sdelphij		peer->minpoll = min(minpoll, NTP_MAXPOLL);
876285612Sdelphij	if (maxpoll == 0)
877285612Sdelphij		peer->maxpoll = NTP_MAXDPOLL;
878285612Sdelphij	else
879285612Sdelphij		peer->maxpoll = max(maxpoll, NTP_MINPOLL);
880285612Sdelphij	if (peer->minpoll > peer->maxpoll)
881285612Sdelphij		peer->minpoll = peer->maxpoll;
882285612Sdelphij
883285612Sdelphij	if (peer->dstadr != NULL)
884285612Sdelphij		DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n",
885285612Sdelphij			stoa(srcadr), peer->dstadr->fd,
886285612Sdelphij			stoa(&peer->dstadr->sin)));
887285612Sdelphij	else
888285612Sdelphij		DPRINTF(3, ("newpeer(%s): local interface currently not bound\n",
889285612Sdelphij			stoa(srcadr)));
890285612Sdelphij
891285612Sdelphij	/*
892182007Sroberto	 * Broadcast needs the socket enabled for broadcast
893182007Sroberto	 */
894285612Sdelphij	if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL)
895182007Sroberto		enable_broadcast(peer->dstadr, srcadr);
896285612Sdelphij
897182007Sroberto	/*
898182007Sroberto	 * Multicast needs the socket interface enabled for multicast
899182007Sroberto	 */
900285612Sdelphij	if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL)
901182007Sroberto		enable_multicast_if(peer->dstadr, srcadr);
902285612Sdelphij
903285612Sdelphij#ifdef AUTOKEY
904132451Sroberto	if (key > NTP_MAXKEY)
905132451Sroberto		peer->flags |= FLAG_SKEY;
906285612Sdelphij#endif	/* AUTOKEY */
907285612Sdelphij	peer->ttl = ttl;
90854359Sroberto	peer->keyid = key;
909285612Sdelphij	if (ident != NULL)
910285612Sdelphij		peer->ident = estrdup(ident);
91154359Sroberto	peer->precision = sys_precision;
912182007Sroberto	peer->hpoll = peer->minpoll;
913132451Sroberto	if (cast_flags & MDF_ACAST)
914132451Sroberto		peer_clear(peer, "ACST");
915285612Sdelphij	else if (cast_flags & MDF_POOL)
916285612Sdelphij		peer_clear(peer, "POOL");
917132451Sroberto	else if (cast_flags & MDF_MCAST)
918132451Sroberto		peer_clear(peer, "MCST");
919132451Sroberto	else if (cast_flags & MDF_BCAST)
920132451Sroberto		peer_clear(peer, "BCST");
921132451Sroberto	else
922132451Sroberto		peer_clear(peer, "INIT");
92382498Sroberto	if (mode_ntpdate)
92482498Sroberto		peer_ntpdate++;
92554359Sroberto
92654359Sroberto	/*
92754359Sroberto	 * Note time on statistics timers.
92854359Sroberto	 */
92954359Sroberto	peer->timereset = current_time;
93054359Sroberto	peer->timereachable = current_time;
93154359Sroberto	peer->timereceived = current_time;
932182007Sroberto
933285612Sdelphij	if (ISREFCLOCKADR(&peer->srcadr)) {
93454359Sroberto#ifdef REFCLOCK
93554359Sroberto		/*
93654359Sroberto		 * We let the reference clock support do clock
93754359Sroberto		 * dependent initialization.  This includes setting
93854359Sroberto		 * the peer timer, since the clock may have requirements
93954359Sroberto		 * for this.
94054359Sroberto		 */
941285612Sdelphij		if (maxpoll == 0)
942285612Sdelphij			peer->maxpoll = peer->minpoll;
94354359Sroberto		if (!refclock_newpeer(peer)) {
94454359Sroberto			/*
94554359Sroberto			 * Dump it, something screwed up
94654359Sroberto			 */
947182007Sroberto			set_peerdstadr(peer, NULL);
948285612Sdelphij			free_peer(peer, 0);
949285612Sdelphij			return NULL;
95054359Sroberto		}
951285612Sdelphij#else /* REFCLOCK */
952285612Sdelphij		msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.",
953285612Sdelphij			stoa(&peer->srcadr));
954285612Sdelphij		set_peerdstadr(peer, NULL);
955285612Sdelphij		free_peer(peer, 0);
956285612Sdelphij		return NULL;
957285612Sdelphij#endif /* REFCLOCK */
95854359Sroberto	}
95954359Sroberto
96054359Sroberto	/*
96182498Sroberto	 * Put the new peer in the hash tables.
96254359Sroberto	 */
963285612Sdelphij	hash = NTP_HASH_ADDR(&peer->srcadr);
964285612Sdelphij	LINK_SLIST(peer_hash[hash], peer, adr_link);
965285612Sdelphij	peer_hash_count[hash]++;
966285612Sdelphij	hash = peer->associd & NTP_HASH_MASK;
967285612Sdelphij	LINK_SLIST(assoc_hash[hash], peer, aid_link);
968285612Sdelphij	assoc_hash_count[hash]++;
969285612Sdelphij	LINK_SLIST(peer_list, peer, p_link);
970182007Sroberto
971285612Sdelphij	restrict_source(&peer->srcadr, 0, 0);
972285612Sdelphij	mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd);
973285612Sdelphij	DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n",
974285612Sdelphij	    latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode,
975285612Sdelphij	    peer->version, peer->minpoll, peer->maxpoll, peer->flags,
976285612Sdelphij	    peer->cast_flags, peer->ttl, peer->keyid));
977285612Sdelphij	return peer;
97854359Sroberto}
97954359Sroberto
98054359Sroberto
98154359Sroberto/*
982285612Sdelphij * peer_clr_stats - clear peer module statistics counters
98354359Sroberto */
98454359Srobertovoid
98554359Srobertopeer_clr_stats(void)
98654359Sroberto{
98754359Sroberto	findpeer_calls = 0;
98854359Sroberto	assocpeer_calls = 0;
98954359Sroberto	peer_allocations = 0;
99054359Sroberto	peer_demobilizations = 0;
99154359Sroberto	peer_timereset = current_time;
99254359Sroberto}
99354359Sroberto
994285612Sdelphij
99554359Sroberto/*
996285612Sdelphij * peer_reset - reset statistics counters
99754359Sroberto */
99854359Srobertovoid
99954359Srobertopeer_reset(
100054359Sroberto	struct peer *peer
100154359Sroberto	)
100254359Sroberto{
1003285612Sdelphij	if (peer == NULL)
1004285612Sdelphij		return;
1005285612Sdelphij
1006285612Sdelphij	peer->timereset = current_time;
100754359Sroberto	peer->sent = 0;
100854359Sroberto	peer->received = 0;
100954359Sroberto	peer->processed = 0;
101054359Sroberto	peer->badauth = 0;
101154359Sroberto	peer->bogusorg = 0;
101254359Sroberto	peer->oldpkt = 0;
101354359Sroberto	peer->seldisptoolarge = 0;
1014285612Sdelphij	peer->selbroken = 0;
101554359Sroberto}
101654359Sroberto
101754359Sroberto
101854359Sroberto/*
1019285612Sdelphij * peer_all_reset - reset all peer statistics counters
102054359Sroberto */
102154359Srobertovoid
102254359Srobertopeer_all_reset(void)
102354359Sroberto{
102454359Sroberto	struct peer *peer;
102554359Sroberto
1026285612Sdelphij	for (peer = peer_list; peer != NULL; peer = peer->p_link)
102754359Sroberto		peer_reset(peer);
102854359Sroberto}
102982498Sroberto
103082498Sroberto
103182498Sroberto/*
1032285612Sdelphij * findmanycastpeer - find and return a manycastclient or pool
1033285612Sdelphij *		      association matching a received response.
103482498Sroberto */
103582498Srobertostruct peer *
103682498Srobertofindmanycastpeer(
1037285612Sdelphij	struct recvbuf *rbufp	/* receive buffer pointer */
103882498Sroberto	)
103982498Sroberto{
1040285612Sdelphij	struct peer *peer;
104182498Sroberto	struct pkt *pkt;
104282498Sroberto	l_fp p_org;
104382498Sroberto
104482498Sroberto 	/*
1045285612Sdelphij 	 * This routine is called upon arrival of a server-mode response
1046285612Sdelphij	 * to a manycastclient multicast solicitation, or to a pool
1047285612Sdelphij	 * server unicast solicitation.  Search the peer list for a
1048285612Sdelphij	 * manycastclient association where the last transmit timestamp
1049285612Sdelphij	 * matches the response packet's originate timestamp.  There can
1050285612Sdelphij	 * be multiple manycastclient associations, or multiple pool
1051285612Sdelphij	 * solicitation assocations, so this assumes the transmit
1052285612Sdelphij	 * timestamps are unique for such.
105382498Sroberto	 */
105482498Sroberto	pkt = &rbufp->recv_pkt;
1055285612Sdelphij	for (peer = peer_list; peer != NULL; peer = peer->p_link)
1056285612Sdelphij		if (MDF_SOLICIT_MASK & peer->cast_flags) {
1057285612Sdelphij			NTOHL_FP(&pkt->org, &p_org);
1058285612Sdelphij			if (L_ISEQU(&p_org, &peer->aorg))
1059285612Sdelphij				break;
1060285612Sdelphij		}
106182498Sroberto
1062285612Sdelphij	return peer;
106382498Sroberto}
1064285612Sdelphij
1065285612Sdelphij/* peer_cleanup - clean peer list prior to shutdown */
1066285612Sdelphijvoid peer_cleanup(void)
1067285612Sdelphij{
1068285612Sdelphij        struct peer *peer;
1069285612Sdelphij        associd_t assoc;
1070285612Sdelphij
1071285612Sdelphij        for (assoc = initial_association_ID; assoc != current_association_ID; assoc++) {
1072285612Sdelphij            if (assoc != 0U) {
1073285612Sdelphij                peer = findpeerbyassoc(assoc);
1074285612Sdelphij                if (peer != NULL)
1075285612Sdelphij                    unpeer(peer);
1076285612Sdelphij            }
1077285612Sdelphij        }
1078285612Sdelphij        peer = findpeerbyassoc(current_association_ID);
1079285612Sdelphij        if (peer != NULL)
1080285612Sdelphij            unpeer(peer);
1081285612Sdelphij}
1082