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,
120330567Sgordon					      u_char, int *);
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
164316722Sdelphij	peers = eallocarray(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,
206330567Sgordon	u_char		cast_flags,
207330567Sgordon	int *		ip_count
208285612Sdelphij	)
209285612Sdelphij{
210285612Sdelphij	struct peer *peer;
211285612Sdelphij
212330567Sgordon	DPRINTF(2, ("findexistingpeer_addr(%s, %s, %d, 0x%x, %p)\n",
213285612Sdelphij		sptoa(addr),
214285612Sdelphij		(start_peer)
215285612Sdelphij		    ? sptoa(&start_peer->srcadr)
216285612Sdelphij		    : "NULL",
217330567Sgordon		mode, (u_int)cast_flags, ip_count));
218285612Sdelphij
21954359Sroberto	/*
22054359Sroberto	 * start_peer is included so we can locate instances of the
22154359Sroberto	 * same peer through different interfaces in the hash table.
222285612Sdelphij	 * Without MDF_BCLNT, a match requires the same mode and remote
223285612Sdelphij	 * address.  MDF_BCLNT associations start out as MODE_CLIENT
224285612Sdelphij	 * if broadcastdelay is not specified, and switch to
225285612Sdelphij	 * MODE_BCLIENT after estimating the one-way delay.  Duplicate
226285612Sdelphij	 * associations are expanded in definition to match any other
227285612Sdelphij	 * MDF_BCLNT with the same srcadr (remote, unicast address).
22854359Sroberto	 */
229285612Sdelphij	if (NULL == start_peer)
230182007Sroberto		peer = peer_hash[NTP_HASH_ADDR(addr)];
23154359Sroberto	else
232285612Sdelphij		peer = start_peer->adr_link;
23354359Sroberto
234285612Sdelphij	while (peer != NULL) {
235285612Sdelphij		DPRINTF(3, ("%s %s %d %d 0x%x 0x%x ", sptoa(addr),
236285612Sdelphij			sptoa(&peer->srcadr), mode, peer->hmode,
237285612Sdelphij			(u_int)cast_flags, (u_int)peer->cast_flags));
238330567Sgordon		if (ip_count) {
239330567Sgordon			if (SOCK_EQ(addr, &peer->srcadr)) {
240330567Sgordon				(*ip_count)++;
241330567Sgordon			}
242330567Sgordon		}
243285612Sdelphij 		if ((-1 == mode || peer->hmode == mode ||
244285612Sdelphij		     ((MDF_BCLNT & peer->cast_flags) &&
245285612Sdelphij		      (MDF_BCLNT & cast_flags))) &&
246285612Sdelphij		    ADDR_PORT_EQ(addr, &peer->srcadr)) {
247285612Sdelphij			DPRINTF(3, ("found.\n"));
248285612Sdelphij			break;
24954359Sroberto		}
250285612Sdelphij		DPRINTF(3, ("\n"));
251285612Sdelphij		peer = peer->adr_link;
25254359Sroberto	}
253285612Sdelphij
254285612Sdelphij	return peer;
25554359Sroberto}
25654359Sroberto
25754359Sroberto
25854359Sroberto/*
259285612Sdelphij * findexistingpeer - search by address and return a pointer to a peer.
26054359Sroberto */
26154359Srobertostruct peer *
262285612Sdelphijfindexistingpeer(
263285612Sdelphij	sockaddr_u *	addr,
264285612Sdelphij	const char *	hostname,
265285612Sdelphij	struct peer *	start_peer,
266285612Sdelphij	int		mode,
267330567Sgordon	u_char		cast_flags,
268330567Sgordon	int *		ip_count
269285612Sdelphij	)
270285612Sdelphij{
271285612Sdelphij	if (hostname != NULL)
272285612Sdelphij		return findexistingpeer_name(hostname, AF(addr),
273285612Sdelphij					     start_peer, mode);
274285612Sdelphij	else
275285612Sdelphij		return findexistingpeer_addr(addr, start_peer, mode,
276330567Sgordon					     cast_flags, ip_count);
277285612Sdelphij}
278285612Sdelphij
279285612Sdelphij
280285612Sdelphij/*
281285612Sdelphij * findpeer - find and return a peer match for a received datagram in
282285612Sdelphij *	      the peer_hash table.
283310419Sdelphij *
284310419Sdelphij * [Bug 3072] To faciliate a faster reorganisation after routing changes
285310419Sdelphij * the original code re-assigned the peer address to be the destination
286310419Sdelphij * of the received packet and initiated another round on a mismatch.
287310419Sdelphij * Unfortunately this leaves us wide open for a DoS attack where the
288310419Sdelphij * attacker directs a packet with forged destination address to us --
289310419Sdelphij * this results in a wrong interface assignment, actually creating a DoS
290310419Sdelphij * situation.
291310419Sdelphij *
292310419Sdelphij * This condition would persist until the next update of the interface
293310419Sdelphij * list, but a continued attack would put us out of business again soon
294310419Sdelphij * enough. Authentication alone does not help here, since it does not
295310419Sdelphij * protect the UDP layer and leaves us open for a replay attack.
296310419Sdelphij *
297310419Sdelphij * So we do not update the adresses and wait until the next interface
298310419Sdelphij * list update does the right thing for us.
299285612Sdelphij */
300285612Sdelphijstruct peer *
30154359Srobertofindpeer(
302285612Sdelphij	struct recvbuf *rbufp,
303285612Sdelphij	int		pkt_mode,
304285612Sdelphij	int *		action
30554359Sroberto	)
30654359Sroberto{
307285612Sdelphij	struct peer *	p;
308285612Sdelphij	sockaddr_u *	srcadr;
309285612Sdelphij	u_int		hash;
310285612Sdelphij	struct pkt *	pkt;
311285612Sdelphij	l_fp		pkt_org;
31254359Sroberto
31354359Sroberto	findpeer_calls++;
314285612Sdelphij	srcadr = &rbufp->recv_srcadr;
315182007Sroberto	hash = NTP_HASH_ADDR(srcadr);
316285612Sdelphij	for (p = peer_hash[hash]; p != NULL; p = p->adr_link) {
31782498Sroberto
318310419Sdelphij		/* [Bug 3072] ensure interface of peer matches */
319316722Sdelphij		/* [Bug 3356] ... if NOT a broadcast peer!     */
320316722Sdelphij		if (p->hmode != MODE_BCLIENT && p->dstadr != rbufp->dstadr)
321310419Sdelphij			continue;
32254359Sroberto
323310419Sdelphij		/* ensure peer source address matches */
324310419Sdelphij		if ( ! ADDR_PORT_EQ(srcadr, &p->srcadr))
325310419Sdelphij			continue;
326310419Sdelphij
327310419Sdelphij		/* If the association matching rules determine that this
328310419Sdelphij		 * is not a valid combination, then look for the next
329310419Sdelphij		 * valid peer association.
330310419Sdelphij		 */
331310419Sdelphij		*action = MATCH_ASSOC(p->hmode, pkt_mode);
332285612Sdelphij
333310419Sdelphij		/* A response to our manycastclient solicitation might
334310419Sdelphij		 * be misassociated with an ephemeral peer already spun
335310419Sdelphij		 * for the server.  If the packet's org timestamp
336310419Sdelphij		 * doesn't match the peer's, check if it matches the
337310419Sdelphij		 * ACST prototype peer's.  If so it is a redundant
338310419Sdelphij		 * solicitation response, return AM_ERR to discard it.
339310419Sdelphij		 * [Bug 1762]
340310419Sdelphij		 */
341310419Sdelphij		if (MODE_SERVER == pkt_mode && AM_PROCPKT == *action) {
342310419Sdelphij			pkt = &rbufp->recv_pkt;
343310419Sdelphij			NTOHL_FP(&pkt->org, &pkt_org);
344310419Sdelphij			if (!L_ISEQU(&p->aorg, &pkt_org) &&
345310419Sdelphij			    findmanycastpeer(rbufp))
346310419Sdelphij				*action = AM_ERR;
347310419Sdelphij		}
34854359Sroberto
349310419Sdelphij		/* if an error was returned, exit back right here. */
350310419Sdelphij		if (*action == AM_ERR)
351310419Sdelphij			return NULL;
352310419Sdelphij
353310419Sdelphij		/* if a match is found, we stop our search. */
354310419Sdelphij		if (*action != AM_NOMATCH)
355310419Sdelphij			break;
35654359Sroberto	}
35754359Sroberto
358310419Sdelphij	/* If no matching association is found... */
359310419Sdelphij	if (NULL == p)
36054359Sroberto		*action = MATCH_ASSOC(NO_PEER, pkt_mode);
361310419Sdelphij
362285612Sdelphij	return p;
36354359Sroberto}
36454359Sroberto
36554359Sroberto/*
366285612Sdelphij * findpeerbyassoc - find and return a peer using his association ID
36754359Sroberto */
36854359Srobertostruct peer *
36954359Srobertofindpeerbyassoc(
370285612Sdelphij	associd_t assoc
37154359Sroberto	)
37254359Sroberto{
373285612Sdelphij	struct peer *p;
374285612Sdelphij	u_int hash;
37554359Sroberto
37654359Sroberto	assocpeer_calls++;
377182007Sroberto	hash = assoc & NTP_HASH_MASK;
378285612Sdelphij	for (p = assoc_hash[hash]; p != NULL; p = p->aid_link)
379285612Sdelphij		if (assoc == p->associd)
380285612Sdelphij			break;
381285612Sdelphij	return p;
38254359Sroberto}
38354359Sroberto
38454359Sroberto
38554359Sroberto/*
38682498Sroberto * clear_all - flush all time values for all associations
38754359Sroberto */
38854359Srobertovoid
38982498Srobertoclear_all(void)
39054359Sroberto{
391285612Sdelphij	struct peer *p;
39254359Sroberto
39382498Sroberto	/*
39482498Sroberto	 * This routine is called when the clock is stepped, and so all
39582498Sroberto	 * previously saved time values are untrusted.
39682498Sroberto	 */
397285612Sdelphij	for (p = peer_list; p != NULL; p = p->p_link)
398285612Sdelphij		if (!(MDF_TXONLY_MASK & p->cast_flags))
399285612Sdelphij			peer_clear(p, "STEP");
400285612Sdelphij
401285612Sdelphij	DPRINTF(1, ("clear_all: at %lu\n", current_time));
40254359Sroberto}
40382498Sroberto
40482498Sroberto
40554359Sroberto/*
406285612Sdelphij * score_all() - determine if an association can be demobilized
40754359Sroberto */
408285612Sdelphijint
409285612Sdelphijscore_all(
410285612Sdelphij	struct peer *peer	/* peer structure pointer */
41154359Sroberto	)
41254359Sroberto{
413285612Sdelphij	struct peer *speer;
414285612Sdelphij	int	temp, tamp;
415285612Sdelphij	int	x;
41654359Sroberto
417285612Sdelphij	/*
418285612Sdelphij	 * This routine finds the minimum score for all preemptible
419285612Sdelphij	 * associations and returns > 0 if the association can be
420285612Sdelphij	 * demobilized.
421285612Sdelphij	 */
422285612Sdelphij	tamp = score(peer);
423285612Sdelphij	temp = 100;
424285612Sdelphij	for (speer = peer_list; speer != NULL; speer = speer->p_link)
425285612Sdelphij		if (speer->flags & FLAG_PREEMPT) {
426285612Sdelphij			x = score(speer);
427285612Sdelphij			if (x < temp)
428285612Sdelphij				temp = x;
429285612Sdelphij		}
430285612Sdelphij	DPRINTF(1, ("score_all: at %lu score %d min %d\n",
431285612Sdelphij		    current_time, tamp, temp));
432182007Sroberto
433285612Sdelphij	if (tamp != temp)
434285612Sdelphij		temp = 0;
435182007Sroberto
436285612Sdelphij	return temp;
437285612Sdelphij}
438285612Sdelphij
439285612Sdelphij
440285612Sdelphij/*
441285612Sdelphij * score() - calculate preemption score
442285612Sdelphij */
443285612Sdelphijstatic int
444285612Sdelphijscore(
445285612Sdelphij	struct peer *peer	/* peer structure pointer */
446285612Sdelphij	)
447285612Sdelphij{
448285612Sdelphij	int	temp;
449285612Sdelphij
45054359Sroberto	/*
451285612Sdelphij	 * This routine calculates the premption score from the peer
452285612Sdelphij	 * error bits and status. Increasing values are more cherished.
45354359Sroberto	 */
454285612Sdelphij	temp = 0;
455285612Sdelphij	if (!(peer->flash & TEST10))
456285612Sdelphij		temp++;			/* 1 good synch and stratum */
457285612Sdelphij	if (!(peer->flash & TEST13))
458285612Sdelphij		temp++;			/* 2 reachable */
459285612Sdelphij	if (!(peer->flash & TEST12))
460285612Sdelphij		temp++;			/* 3 no loop */
461285612Sdelphij	if (!(peer->flash & TEST11))
462285612Sdelphij		temp++;			/* 4 good distance */
463285612Sdelphij	if (peer->status >= CTL_PST_SEL_SELCAND)
464285612Sdelphij		temp++;			/* 5 in the hunt */
465285612Sdelphij	if (peer->status != CTL_PST_SEL_EXCESS)
466285612Sdelphij		temp++;			/* 6 not spare tire */
467285612Sdelphij	return (temp);			/* selection status */
468285612Sdelphij}
46954359Sroberto
470285612Sdelphij
471285612Sdelphij/*
472285612Sdelphij * free_peer - internal routine to free memory referred to by a struct
473285612Sdelphij *	       peer and return it to the peer free list.  If unlink is
474285612Sdelphij *	       nonzero, unlink from the various lists.
475285612Sdelphij */
476285612Sdelphijstatic void
477285612Sdelphijfree_peer(
478285612Sdelphij	struct peer *	p,
479285612Sdelphij	int		unlink_peer
480285612Sdelphij	)
481285612Sdelphij{
482285612Sdelphij	struct peer *	unlinked;
483285612Sdelphij	int		hash;
484285612Sdelphij
485285612Sdelphij	if (unlink_peer) {
486285612Sdelphij		hash = NTP_HASH_ADDR(&p->srcadr);
487285612Sdelphij		peer_hash_count[hash]--;
488285612Sdelphij
489285612Sdelphij		UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link,
490285612Sdelphij			     struct peer);
491285612Sdelphij		if (NULL == unlinked) {
49254359Sroberto			peer_hash_count[hash]++;
493285612Sdelphij			msyslog(LOG_ERR, "peer %s not in address table!",
494285612Sdelphij				stoa(&p->srcadr));
49554359Sroberto		}
49654359Sroberto
497285612Sdelphij		/*
498285612Sdelphij		 * Remove him from the association hash as well.
499285612Sdelphij		 */
500285612Sdelphij		hash = p->associd & NTP_HASH_MASK;
501285612Sdelphij		assoc_hash_count[hash]--;
50254359Sroberto
503285612Sdelphij		UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link,
504285612Sdelphij			     struct peer);
505285612Sdelphij		if (NULL == unlinked) {
50654359Sroberto			assoc_hash_count[hash]++;
50754359Sroberto			msyslog(LOG_ERR,
508285612Sdelphij				"peer %s not in association ID table!",
509285612Sdelphij				stoa(&p->srcadr));
51054359Sroberto		}
511285612Sdelphij
512285612Sdelphij		/* Remove him from the overall list. */
513285612Sdelphij		UNLINK_SLIST(unlinked, peer_list, p, p_link,
514285612Sdelphij			     struct peer);
515285612Sdelphij		if (NULL == unlinked)
516285612Sdelphij			msyslog(LOG_ERR, "%s not in peer list!",
517285612Sdelphij				stoa(&p->srcadr));
51854359Sroberto	}
519285612Sdelphij
520285612Sdelphij	if (p->hostname != NULL)
521285612Sdelphij		free(p->hostname);
522285612Sdelphij
523285612Sdelphij	if (p->ident != NULL)
524285612Sdelphij		free(p->ident);
525285612Sdelphij
526285612Sdelphij	if (p->addrs != NULL)
527285612Sdelphij		free(p->addrs);		/* from copy_addrinfo_list() */
528285612Sdelphij
529285612Sdelphij	/* Add his corporeal form to peer free list */
530285612Sdelphij	ZERO(*p);
531285612Sdelphij	LINK_SLIST(peer_free, p, p_link);
53254359Sroberto	peer_free_count++;
53354359Sroberto}
53454359Sroberto
53554359Sroberto
53654359Sroberto/*
537285612Sdelphij * unpeer - remove peer structure from hash table and free structure
538285612Sdelphij */
539285612Sdelphijvoid
540285612Sdelphijunpeer(
541285612Sdelphij	struct peer *peer
542285612Sdelphij	)
543285612Sdelphij{
544285612Sdelphij	mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd);
545285612Sdelphij	restrict_source(&peer->srcadr, 1, 0);
546285612Sdelphij	set_peerdstadr(peer, NULL);
547285612Sdelphij	peer_demobilizations++;
548285612Sdelphij	peer_associations--;
549285612Sdelphij	if (FLAG_PREEMPT & peer->flags)
550285612Sdelphij		peer_preempt--;
551285612Sdelphij#ifdef REFCLOCK
552285612Sdelphij	/*
553285612Sdelphij	 * If this peer is actually a clock, shut it down first
554285612Sdelphij	 */
555285612Sdelphij	if (FLAG_REFCLOCK & peer->flags)
556285612Sdelphij		refclock_unpeer(peer);
557285612Sdelphij#endif
558285612Sdelphij
559285612Sdelphij	free_peer(peer, TRUE);
560285612Sdelphij}
561285612Sdelphij
562285612Sdelphij
563285612Sdelphij/*
56482498Sroberto * peer_config - configure a new association
56554359Sroberto */
56654359Srobertostruct peer *
56754359Srobertopeer_config(
568285612Sdelphij	sockaddr_u *	srcadr,
569285612Sdelphij	const char *	hostname,
570285612Sdelphij	endpt *		dstadr,
571330567Sgordon	int		ippeerlimit,
572285612Sdelphij	u_char		hmode,
573285612Sdelphij	u_char		version,
574285612Sdelphij	u_char		minpoll,
575285612Sdelphij	u_char		maxpoll,
576285612Sdelphij	u_int		flags,
577285612Sdelphij	u_int32		ttl,
578285612Sdelphij	keyid_t		key,
579285612Sdelphij	const char *	ident		/* autokey group */
58054359Sroberto	)
58154359Sroberto{
582132451Sroberto	u_char cast_flags;
58354359Sroberto
58454359Sroberto	/*
58582498Sroberto	 * We do a dirty little jig to figure the cast flags. This is
58682498Sroberto	 * probably not the best place to do this, at least until the
58782498Sroberto	 * configure code is rebuilt. Note only one flag can be set.
58854359Sroberto	 */
58982498Sroberto	switch (hmode) {
59082498Sroberto	case MODE_BROADCAST:
591285612Sdelphij		if (IS_MCAST(srcadr))
592285612Sdelphij			cast_flags = MDF_MCAST;
593285612Sdelphij		else
594285612Sdelphij			cast_flags = MDF_BCAST;
595285612Sdelphij		break;
59682498Sroberto
59782498Sroberto	case MODE_CLIENT:
598285612Sdelphij		if (hostname != NULL && SOCK_UNSPEC(srcadr))
599285612Sdelphij			cast_flags = MDF_POOL;
600285612Sdelphij		else if (IS_MCAST(srcadr))
601285612Sdelphij			cast_flags = MDF_ACAST;
602285612Sdelphij		else
603285612Sdelphij			cast_flags = MDF_UCAST;
604285612Sdelphij		break;
60582498Sroberto
60682498Sroberto	default:
60782498Sroberto		cast_flags = MDF_UCAST;
60882498Sroberto	}
60982498Sroberto
61082498Sroberto	/*
611285612Sdelphij	 * Mobilize the association and initialize its variables. If
612285612Sdelphij	 * emulating ntpdate, force iburst.  For pool and manycastclient
613285612Sdelphij	 * strip FLAG_PREEMPT as the prototype associations are not
614285612Sdelphij	 * themselves preemptible, though the resulting associations
615285612Sdelphij	 * are.
61682498Sroberto	 */
617285612Sdelphij	flags |= FLAG_CONFIG;
618132451Sroberto	if (mode_ntpdate)
619132451Sroberto		flags |= FLAG_IBURST;
620285612Sdelphij	if ((MDF_ACAST | MDF_POOL) & cast_flags)
621285612Sdelphij		flags &= ~FLAG_PREEMPT;
622330567Sgordon	return newpeer(srcadr, hostname, dstadr, ippeerlimit, hmode, version,
623285612Sdelphij	    minpoll, maxpoll, flags, cast_flags, ttl, key, ident);
62454359Sroberto}
62554359Sroberto
626182007Sroberto/*
627285612Sdelphij * setup peer dstadr field keeping it in sync with the interface
628285612Sdelphij * structures
629182007Sroberto */
630182007Srobertovoid
631285612Sdelphijset_peerdstadr(
632285612Sdelphij	struct peer *	p,
633285612Sdelphij	endpt *		dstadr
634285612Sdelphij	)
635182007Sroberto{
636285612Sdelphij	struct peer *	unlinked;
63754359Sroberto
638310419Sdelphij	DEBUG_INSIST(p != NULL);
639310419Sdelphij
640310419Sdelphij	if (p == NULL)
641310419Sdelphij		return;
642310419Sdelphij
643310419Sdelphij	/* check for impossible or identical assignment */
644285612Sdelphij	if (p->dstadr == dstadr)
645285612Sdelphij		return;
646182007Sroberto
647285612Sdelphij	/*
648285612Sdelphij	 * Don't accept updates to a separate multicast receive-only
649285612Sdelphij	 * endpt while a BCLNT peer is running its unicast protocol.
650285612Sdelphij	 */
651285612Sdelphij	if (dstadr != NULL && (FLAG_BC_VOL & p->flags) &&
652285612Sdelphij	    (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) {
653285612Sdelphij		return;
654182007Sroberto	}
655310419Sdelphij
656310419Sdelphij	/* unlink from list if we have an address prior to assignment */
657285612Sdelphij	if (p->dstadr != NULL) {
658285612Sdelphij		p->dstadr->peercnt--;
659285612Sdelphij		UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink,
660285612Sdelphij			     struct peer);
661285612Sdelphij		msyslog(LOG_INFO, "%s local addr %s -> %s",
662285612Sdelphij			stoa(&p->srcadr), latoa(p->dstadr),
663285612Sdelphij			latoa(dstadr));
664285612Sdelphij	}
665310419Sdelphij
666285612Sdelphij	p->dstadr = dstadr;
667310419Sdelphij
668310419Sdelphij	/* link to list if we have an address after assignment */
669310419Sdelphij	if (p->dstadr != NULL) {
670285612Sdelphij		LINK_SLIST(dstadr->peers, p, ilink);
671285612Sdelphij		dstadr->peercnt++;
672285612Sdelphij	}
673182007Sroberto}
674182007Sroberto
67554359Sroberto/*
676182007Sroberto * attempt to re-rebind interface if necessary
677182007Sroberto */
678182007Srobertostatic void
679285612Sdelphijpeer_refresh_interface(
680285612Sdelphij	struct peer *p
681285612Sdelphij	)
682182007Sroberto{
683285612Sdelphij	endpt *	niface;
684285612Sdelphij	endpt *	piface;
685182007Sroberto
686285612Sdelphij	niface = select_peerinterface(p, &p->srcadr, NULL);
687182007Sroberto
688285612Sdelphij	DPRINTF(4, (
689285612Sdelphij	    "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ",
690285612Sdelphij	    p->dstadr == NULL ? "<null>" :
691285612Sdelphij	    stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode,
692285612Sdelphij	    p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags,
693285612Sdelphij	    p->ttl, p->keyid));
694285612Sdelphij	if (niface != NULL) {
695285612Sdelphij		DPRINTF(4, (
696285612Sdelphij		    "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s",
697285612Sdelphij		    niface->fd,  niface->bfd, niface->name,
698285612Sdelphij		    niface->flags, niface->ifindex,
699285612Sdelphij		    stoa(&niface->sin)));
700285612Sdelphij		if (niface->flags & INT_BROADCAST)
701285612Sdelphij			DPRINTF(4, (", bcast=%s",
702285612Sdelphij				stoa(&niface->bcast)));
703285612Sdelphij		DPRINTF(4, (", mask=%s\n", stoa(&niface->mask)));
704285612Sdelphij	} else {
705285612Sdelphij		DPRINTF(4, ("<NONE>\n"));
706182007Sroberto	}
707182007Sroberto
708285612Sdelphij	piface = p->dstadr;
709285612Sdelphij	set_peerdstadr(p, niface);
710285612Sdelphij	if (p->dstadr != NULL) {
711285612Sdelphij		/*
712285612Sdelphij		 * clear crypto if we change the local address
713285612Sdelphij		 */
714285612Sdelphij		if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags)
715285612Sdelphij		    && MODE_BROADCAST != p->pmode)
716285612Sdelphij			peer_clear(p, "XFAC");
717182007Sroberto
718182007Sroberto		/*
719182007Sroberto	 	 * Broadcast needs the socket enabled for broadcast
720182007Sroberto	 	 */
721285612Sdelphij		if (MDF_BCAST & p->cast_flags)
722285612Sdelphij			enable_broadcast(p->dstadr, &p->srcadr);
723182007Sroberto
724182007Sroberto		/*
725285612Sdelphij	 	 * Multicast needs the socket interface enabled for
726285612Sdelphij		 * multicast
727182007Sroberto	 	 */
728285612Sdelphij		if (MDF_MCAST & p->cast_flags)
729285612Sdelphij			enable_multicast_if(p->dstadr, &p->srcadr);
730182007Sroberto	}
731182007Sroberto}
732182007Sroberto
733285612Sdelphij
734182007Sroberto/*
735285612Sdelphij * refresh_all_peerinterfaces - see that all interface bindings are up
736285612Sdelphij * to date
737182007Sroberto */
738182007Srobertovoid
739182007Srobertorefresh_all_peerinterfaces(void)
740182007Sroberto{
741285612Sdelphij	struct peer *p;
742182007Sroberto
743182007Sroberto	/*
744182007Sroberto	 * this is called when the interface list has changed
745182007Sroberto	 * give all peers a chance to find a better interface
746289997Sglebius	 * but only if either they don't have an address already
747289997Sglebius	 * or if the one they have hasn't worked for a while.
748182007Sroberto	 */
749289997Sglebius	for (p = peer_list; p != NULL; p = p->p_link) {
750289997Sglebius		if (!(p->dstadr && (p->reach & 0x3)))	// Bug 2849 XOR 2043
751289997Sglebius			peer_refresh_interface(p);
752289997Sglebius	}
753182007Sroberto}
754182007Sroberto
755285612Sdelphij
756182007Sroberto/*
757285612Sdelphij * newpeer - initialize a new peer association
758182007Sroberto */
759285612Sdelphijstruct peer *
760285612Sdelphijnewpeer(
761285612Sdelphij	sockaddr_u *	srcadr,
762285612Sdelphij	const char *	hostname,
763285612Sdelphij	endpt *		dstadr,
764330567Sgordon	int		ippeerlimit,
765285612Sdelphij	u_char		hmode,
766285612Sdelphij	u_char		version,
767285612Sdelphij	u_char		minpoll,
768285612Sdelphij	u_char		maxpoll,
769285612Sdelphij	u_int		flags,
770285612Sdelphij	u_char		cast_flags,
771285612Sdelphij	u_int32		ttl,
772285612Sdelphij	keyid_t		key,
773285612Sdelphij	const char *	ident
774285612Sdelphij	)
775182007Sroberto{
776285612Sdelphij	struct peer *	peer;
777285612Sdelphij	u_int		hash;
778330567Sgordon	int		ip_count = 0;
779285612Sdelphij
780330567Sgordon
781289997Sglebius	DEBUG_REQUIRE(srcadr);
782289997Sglebius
783285612Sdelphij#ifdef AUTOKEY
784182007Sroberto	/*
785285612Sdelphij	 * If Autokey is requested but not configured, complain loudly.
786182007Sroberto	 */
787285612Sdelphij	if (!crypto_flags) {
788285612Sdelphij		if (key > NTP_MAXKEY) {
789285612Sdelphij			return (NULL);
790182007Sroberto
791285612Sdelphij		} else if (flags & FLAG_SKEY) {
792285612Sdelphij			msyslog(LOG_ERR, "Autokey not configured");
793285612Sdelphij			return (NULL);
794285612Sdelphij		}
795285612Sdelphij	}
796285612Sdelphij#endif	/* AUTOKEY */
797285612Sdelphij
798182007Sroberto	/*
799285612Sdelphij	 * For now only pool associations have a hostname.
800182007Sroberto	 */
801289997Sglebius	INSIST(NULL == hostname || (MDF_POOL & cast_flags));
802182007Sroberto
803285612Sdelphij	/*
804285612Sdelphij	 * First search from the beginning for an association with given
805285612Sdelphij	 * remote address and mode. If an interface is given, search
806285612Sdelphij	 * from there to find the association which matches that
807285612Sdelphij	 * destination. If the given interface is "any", track down the
808285612Sdelphij	 * actual interface, because that's what gets put into the peer
809285612Sdelphij	 * structure.
810285612Sdelphij	 */
811285612Sdelphij	if (dstadr != NULL) {
812285612Sdelphij		peer = findexistingpeer(srcadr, hostname, NULL, hmode,
813330567Sgordon					cast_flags, &ip_count);
814285612Sdelphij		while (peer != NULL) {
815330567Sgordon			if (   peer->dstadr == dstadr
816330567Sgordon			    || (   (MDF_BCLNT & cast_flags)
817330567Sgordon				&& (MDF_BCLNT & peer->cast_flags)))
818285612Sdelphij				break;
819182007Sroberto
820285612Sdelphij			if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) &&
821285612Sdelphij			    peer->dstadr == findinterface(srcadr))
822285612Sdelphij				break;
823182007Sroberto
824285612Sdelphij			peer = findexistingpeer(srcadr, hostname, peer,
825330567Sgordon						hmode, cast_flags, &ip_count);
826285612Sdelphij		}
827285612Sdelphij	} else {
828285612Sdelphij		/* no endpt address given */
829285612Sdelphij		peer = findexistingpeer(srcadr, hostname, NULL, hmode,
830330567Sgordon					cast_flags, &ip_count);
831285612Sdelphij	}
83254359Sroberto
83354359Sroberto	/*
834285612Sdelphij	 * If a peer is found, this would be a duplicate and we don't
835285612Sdelphij	 * allow that. This avoids duplicate ephemeral (broadcast/
836285612Sdelphij	 * multicast) and preemptible (manycast and pool) client
837285612Sdelphij	 * associations.
838285612Sdelphij	 */
839285612Sdelphij	if (peer != NULL) {
840285612Sdelphij		DPRINTF(2, ("newpeer(%s) found existing association\n",
841285612Sdelphij			(hostname)
842285612Sdelphij			    ? hostname
843285612Sdelphij			    : stoa(srcadr)));
844285612Sdelphij		return NULL;
845285612Sdelphij	}
846285612Sdelphij
847330567SgordonDPRINTF(1, ("newpeer(%s) found no existing and %d other associations\n",
848330567Sgordon		(hostname)
849330567Sgordon		    ? hostname
850330567Sgordon		    : stoa(srcadr),
851330567Sgordon		ip_count));
852330567Sgordon
853330567Sgordon	/* Check ippeerlimit wrt ip_count */
854330567Sgordon	if (ippeerlimit > -1) {
855330567Sgordon		if (ip_count + 1 > ippeerlimit) {
856330567Sgordon			DPRINTF(2, ("newpeer(%s) denied - ippeerlimit %d\n",
857330567Sgordon				(hostname)
858330567Sgordon				    ? hostname
859330567Sgordon				    : stoa(srcadr),
860330567Sgordon				ippeerlimit));
861330567Sgordon			return NULL;
862330567Sgordon		}
863330567Sgordon	} else {
864330567Sgordon		DPRINTF(1, ("newpeer(%s) - ippeerlimit %d ignored\n",
865330567Sgordon			(hostname)
866330567Sgordon			    ? hostname
867330567Sgordon			    : stoa(srcadr),
868330567Sgordon			ippeerlimit));
869330567Sgordon	}
870330567Sgordon
871285612Sdelphij	/*
87282498Sroberto	 * Allocate a new peer structure. Some dirt here, since some of
87382498Sroberto	 * the initialization requires knowlege of our system state.
87454359Sroberto	 */
87554359Sroberto	if (peer_free_count == 0)
87682498Sroberto		getmorepeermem();
877285612Sdelphij	UNLINK_HEAD_SLIST(peer, peer_free, p_link);
878289997Sglebius	INSIST(peer != NULL);
87954359Sroberto	peer_free_count--;
88054359Sroberto	peer_associations++;
881285612Sdelphij	if (FLAG_PREEMPT & flags)
882182007Sroberto		peer_preempt++;
88354359Sroberto
88454359Sroberto	/*
885132451Sroberto	 * Assign an association ID and increment the system variable.
886132451Sroberto	 */
887132451Sroberto	peer->associd = current_association_ID;
888132451Sroberto	if (++current_association_ID == 0)
889132451Sroberto		++current_association_ID;
890132451Sroberto
89182498Sroberto	peer->srcadr = *srcadr;
892285612Sdelphij	if (hostname != NULL)
893285612Sdelphij		peer->hostname = estrdup(hostname);
894285612Sdelphij	peer->hmode = hmode;
895285612Sdelphij	peer->version = version;
896132451Sroberto	peer->flags = flags;
897285612Sdelphij	peer->cast_flags = cast_flags;
898285612Sdelphij	set_peerdstadr(peer,
899285612Sdelphij		       select_peerinterface(peer, srcadr, dstadr));
900182007Sroberto
901182007Sroberto	/*
902285612Sdelphij	 * It is an error to set minpoll less than NTP_MINPOLL or to
903285612Sdelphij	 * set maxpoll greater than NTP_MAXPOLL. However, minpoll is
904285612Sdelphij	 * clamped not greater than NTP_MAXPOLL and maxpoll is clamped
905285612Sdelphij	 * not less than NTP_MINPOLL without complaint. Finally,
906285612Sdelphij	 * minpoll is clamped not greater than maxpoll.
907285612Sdelphij	 */
908285612Sdelphij	if (minpoll == 0)
909285612Sdelphij		peer->minpoll = NTP_MINDPOLL;
910285612Sdelphij	else
911285612Sdelphij		peer->minpoll = min(minpoll, NTP_MAXPOLL);
912285612Sdelphij	if (maxpoll == 0)
913285612Sdelphij		peer->maxpoll = NTP_MAXDPOLL;
914285612Sdelphij	else
915285612Sdelphij		peer->maxpoll = max(maxpoll, NTP_MINPOLL);
916285612Sdelphij	if (peer->minpoll > peer->maxpoll)
917285612Sdelphij		peer->minpoll = peer->maxpoll;
918285612Sdelphij
919285612Sdelphij	if (peer->dstadr != NULL)
920285612Sdelphij		DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n",
921285612Sdelphij			stoa(srcadr), peer->dstadr->fd,
922285612Sdelphij			stoa(&peer->dstadr->sin)));
923285612Sdelphij	else
924285612Sdelphij		DPRINTF(3, ("newpeer(%s): local interface currently not bound\n",
925285612Sdelphij			stoa(srcadr)));
926285612Sdelphij
927285612Sdelphij	/*
928182007Sroberto	 * Broadcast needs the socket enabled for broadcast
929182007Sroberto	 */
930285612Sdelphij	if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL)
931182007Sroberto		enable_broadcast(peer->dstadr, srcadr);
932285612Sdelphij
933182007Sroberto	/*
934182007Sroberto	 * Multicast needs the socket interface enabled for multicast
935182007Sroberto	 */
936285612Sdelphij	if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL)
937182007Sroberto		enable_multicast_if(peer->dstadr, srcadr);
938285612Sdelphij
939285612Sdelphij#ifdef AUTOKEY
940132451Sroberto	if (key > NTP_MAXKEY)
941132451Sroberto		peer->flags |= FLAG_SKEY;
942285612Sdelphij#endif	/* AUTOKEY */
943285612Sdelphij	peer->ttl = ttl;
94454359Sroberto	peer->keyid = key;
945285612Sdelphij	if (ident != NULL)
946285612Sdelphij		peer->ident = estrdup(ident);
94754359Sroberto	peer->precision = sys_precision;
948182007Sroberto	peer->hpoll = peer->minpoll;
949132451Sroberto	if (cast_flags & MDF_ACAST)
950132451Sroberto		peer_clear(peer, "ACST");
951285612Sdelphij	else if (cast_flags & MDF_POOL)
952285612Sdelphij		peer_clear(peer, "POOL");
953132451Sroberto	else if (cast_flags & MDF_MCAST)
954132451Sroberto		peer_clear(peer, "MCST");
955132451Sroberto	else if (cast_flags & MDF_BCAST)
956132451Sroberto		peer_clear(peer, "BCST");
957132451Sroberto	else
958132451Sroberto		peer_clear(peer, "INIT");
95982498Sroberto	if (mode_ntpdate)
96082498Sroberto		peer_ntpdate++;
96154359Sroberto
96254359Sroberto	/*
96354359Sroberto	 * Note time on statistics timers.
96454359Sroberto	 */
96554359Sroberto	peer->timereset = current_time;
96654359Sroberto	peer->timereachable = current_time;
96754359Sroberto	peer->timereceived = current_time;
968182007Sroberto
969285612Sdelphij	if (ISREFCLOCKADR(&peer->srcadr)) {
97054359Sroberto#ifdef REFCLOCK
97154359Sroberto		/*
97254359Sroberto		 * We let the reference clock support do clock
97354359Sroberto		 * dependent initialization.  This includes setting
97454359Sroberto		 * the peer timer, since the clock may have requirements
97554359Sroberto		 * for this.
97654359Sroberto		 */
977285612Sdelphij		if (maxpoll == 0)
978285612Sdelphij			peer->maxpoll = peer->minpoll;
97954359Sroberto		if (!refclock_newpeer(peer)) {
98054359Sroberto			/*
98154359Sroberto			 * Dump it, something screwed up
98254359Sroberto			 */
983182007Sroberto			set_peerdstadr(peer, NULL);
984285612Sdelphij			free_peer(peer, 0);
985285612Sdelphij			return NULL;
98654359Sroberto		}
987285612Sdelphij#else /* REFCLOCK */
988285612Sdelphij		msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.",
989285612Sdelphij			stoa(&peer->srcadr));
990285612Sdelphij		set_peerdstadr(peer, NULL);
991285612Sdelphij		free_peer(peer, 0);
992285612Sdelphij		return NULL;
993285612Sdelphij#endif /* REFCLOCK */
99454359Sroberto	}
99554359Sroberto
99654359Sroberto	/*
99782498Sroberto	 * Put the new peer in the hash tables.
99854359Sroberto	 */
999285612Sdelphij	hash = NTP_HASH_ADDR(&peer->srcadr);
1000285612Sdelphij	LINK_SLIST(peer_hash[hash], peer, adr_link);
1001285612Sdelphij	peer_hash_count[hash]++;
1002285612Sdelphij	hash = peer->associd & NTP_HASH_MASK;
1003285612Sdelphij	LINK_SLIST(assoc_hash[hash], peer, aid_link);
1004285612Sdelphij	assoc_hash_count[hash]++;
1005285612Sdelphij	LINK_SLIST(peer_list, peer, p_link);
1006182007Sroberto
1007285612Sdelphij	restrict_source(&peer->srcadr, 0, 0);
1008285612Sdelphij	mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd);
1009285612Sdelphij	DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n",
1010285612Sdelphij	    latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode,
1011285612Sdelphij	    peer->version, peer->minpoll, peer->maxpoll, peer->flags,
1012285612Sdelphij	    peer->cast_flags, peer->ttl, peer->keyid));
1013285612Sdelphij	return peer;
101454359Sroberto}
101554359Sroberto
101654359Sroberto
101754359Sroberto/*
1018285612Sdelphij * peer_clr_stats - clear peer module statistics counters
101954359Sroberto */
102054359Srobertovoid
102154359Srobertopeer_clr_stats(void)
102254359Sroberto{
102354359Sroberto	findpeer_calls = 0;
102454359Sroberto	assocpeer_calls = 0;
102554359Sroberto	peer_allocations = 0;
102654359Sroberto	peer_demobilizations = 0;
102754359Sroberto	peer_timereset = current_time;
102854359Sroberto}
102954359Sroberto
1030285612Sdelphij
103154359Sroberto/*
1032285612Sdelphij * peer_reset - reset statistics counters
103354359Sroberto */
103454359Srobertovoid
103554359Srobertopeer_reset(
103654359Sroberto	struct peer *peer
103754359Sroberto	)
103854359Sroberto{
1039285612Sdelphij	if (peer == NULL)
1040285612Sdelphij		return;
1041285612Sdelphij
1042285612Sdelphij	peer->timereset = current_time;
104354359Sroberto	peer->sent = 0;
104454359Sroberto	peer->received = 0;
104554359Sroberto	peer->processed = 0;
104654359Sroberto	peer->badauth = 0;
104754359Sroberto	peer->bogusorg = 0;
104854359Sroberto	peer->oldpkt = 0;
104954359Sroberto	peer->seldisptoolarge = 0;
1050285612Sdelphij	peer->selbroken = 0;
105154359Sroberto}
105254359Sroberto
105354359Sroberto
105454359Sroberto/*
1055285612Sdelphij * peer_all_reset - reset all peer statistics counters
105654359Sroberto */
105754359Srobertovoid
105854359Srobertopeer_all_reset(void)
105954359Sroberto{
106054359Sroberto	struct peer *peer;
106154359Sroberto
1062285612Sdelphij	for (peer = peer_list; peer != NULL; peer = peer->p_link)
106354359Sroberto		peer_reset(peer);
106454359Sroberto}
106582498Sroberto
106682498Sroberto
106782498Sroberto/*
1068285612Sdelphij * findmanycastpeer - find and return a manycastclient or pool
1069285612Sdelphij *		      association matching a received response.
107082498Sroberto */
107182498Srobertostruct peer *
107282498Srobertofindmanycastpeer(
1073285612Sdelphij	struct recvbuf *rbufp	/* receive buffer pointer */
107482498Sroberto	)
107582498Sroberto{
1076285612Sdelphij	struct peer *peer;
107782498Sroberto	struct pkt *pkt;
107882498Sroberto	l_fp p_org;
107982498Sroberto
108082498Sroberto 	/*
1081285612Sdelphij 	 * This routine is called upon arrival of a server-mode response
1082285612Sdelphij	 * to a manycastclient multicast solicitation, or to a pool
1083285612Sdelphij	 * server unicast solicitation.  Search the peer list for a
1084285612Sdelphij	 * manycastclient association where the last transmit timestamp
1085285612Sdelphij	 * matches the response packet's originate timestamp.  There can
1086285612Sdelphij	 * be multiple manycastclient associations, or multiple pool
1087285612Sdelphij	 * solicitation assocations, so this assumes the transmit
1088285612Sdelphij	 * timestamps are unique for such.
108982498Sroberto	 */
109082498Sroberto	pkt = &rbufp->recv_pkt;
1091285612Sdelphij	for (peer = peer_list; peer != NULL; peer = peer->p_link)
1092285612Sdelphij		if (MDF_SOLICIT_MASK & peer->cast_flags) {
1093285612Sdelphij			NTOHL_FP(&pkt->org, &p_org);
1094285612Sdelphij			if (L_ISEQU(&p_org, &peer->aorg))
1095285612Sdelphij				break;
1096285612Sdelphij		}
109782498Sroberto
1098285612Sdelphij	return peer;
109982498Sroberto}
1100285612Sdelphij
1101285612Sdelphij/* peer_cleanup - clean peer list prior to shutdown */
1102285612Sdelphijvoid peer_cleanup(void)
1103285612Sdelphij{
1104285612Sdelphij        struct peer *peer;
1105285612Sdelphij        associd_t assoc;
1106285612Sdelphij
1107285612Sdelphij        for (assoc = initial_association_ID; assoc != current_association_ID; assoc++) {
1108285612Sdelphij            if (assoc != 0U) {
1109285612Sdelphij                peer = findpeerbyassoc(assoc);
1110285612Sdelphij                if (peer != NULL)
1111285612Sdelphij                    unpeer(peer);
1112285612Sdelphij            }
1113285612Sdelphij        }
1114285612Sdelphij        peer = findpeerbyassoc(current_association_ID);
1115285612Sdelphij        if (peer != NULL)
1116285612Sdelphij            unpeer(peer);
1117285612Sdelphij}
1118