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"
12290001Sglebius#include "ntp_lists.h"
1354359Sroberto#include "ntp_stdlib.h"
14290001Sglebius#include "ntp_control.h"
15182007Sroberto#include <ntp_random.h>
1654359Sroberto
1754359Sroberto/*
18290001Sglebius *		    Table of valid association combinations
19290001Sglebius *		    ---------------------------------------
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
27290001Sglebius * 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] = {
43290001Sglebius/*			packet->mode					    */
44290001Sglebius/* peer { UNSPEC,   ACTIVE,     PASSIVE,    CLIENT,     SERVER,     BCAST } */
45290001Sglebius/* 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
52290001Sglebius/*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
61290001Sglebius#define MATCH_ASSOC(x, y)	AM[(x)][(y)]
6254359Sroberto
6354359Sroberto/*
6454359Sroberto * These routines manage the allocation of memory to peer structures
65290001Sglebius * and the maintenance of three data structures involving all peers:
66290001Sglebius *
67290001Sglebius * - peer_list is a single list with all peers, suitable for scanning
68290001Sglebius *   operations over all peers.
69290001Sglebius * - peer_adr_hash is an array of lists indexed by hashed peer address.
70290001Sglebius * - peer_aid_hash is an array of lists indexed by hashed associd.
71290001Sglebius *
72290001Sglebius * They also maintain a free list of peer structures, peer_free.
73290001Sglebius *
74290001Sglebius * The three main entry points are findpeer(), which looks for matching
75290001Sglebius * peer structures in the peer list, newpeer(), which allocates a new
76290001Sglebius * peer structure and adds it to the list, and unpeer(), which
77290001Sglebius * demobilizes the association and deallocates the structure.
7854359Sroberto */
7954359Sroberto/*
8082498Sroberto * Peer hash tables
8154359Sroberto */
82182007Srobertostruct peer *peer_hash[NTP_HASH_SIZE];	/* peer hash table */
83290001Sglebiusint	peer_hash_count[NTP_HASH_SIZE];	/* peers in each bucket */
84182007Srobertostruct peer *assoc_hash[NTP_HASH_SIZE];	/* association ID hash table */
85290001Sglebiusint	assoc_hash_count[NTP_HASH_SIZE];/* peers in each bucket */
86290001Sglebiusstruct peer *peer_list;			/* peer structures list */
8782498Srobertostatic struct peer *peer_free;		/* peer structures free list */
88290001Sglebiusint	peer_free_count;		/* count of free structures */
8954359Sroberto
9054359Sroberto/*
9182498Sroberto * Association ID.  We initialize this value randomly, then assign a new
92290001Sglebius * value every time an association is mobilized.
9354359Sroberto */
9482498Srobertostatic associd_t current_association_ID; /* association ID */
95290001Sglebiusstatic associd_t initial_association_ID; /* association ID */
9654359Sroberto
9754359Sroberto/*
9854359Sroberto * Memory allocation watermarks.
9954359Sroberto */
100290001Sglebius#define	INIT_PEER_ALLOC		8	/* static preallocation */
101290001Sglebius#define	INC_PEER_ALLOC		4	/* add N more when empty */
10254359Sroberto
10354359Sroberto/*
10454359Sroberto * Miscellaneous statistic counters which may be queried.
10554359Sroberto */
106290001Sglebiusu_long	peer_timereset;			/* time stat counters zeroed */
107290001Sglebiusu_long	findpeer_calls;			/* calls to findpeer */
108290001Sglebiusu_long	assocpeer_calls;		/* calls to findpeerbyassoc */
109290001Sglebiusu_long	peer_allocations;		/* allocations from free list */
110290001Sglebiusu_long	peer_demobilizations;		/* structs freed to free list */
111290001Sglebiusint	total_peer_structs;		/* peer structs */
112290001Sglebiusint	peer_associations;		/* mobilized associations */
113290001Sglebiusint	peer_preempt;			/* preemptable associations */
11482498Srobertostatic struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */
11554359Sroberto
116290001Sglebiusstatic struct peer *	findexistingpeer_name(const char *, u_short,
117290001Sglebius					      struct peer *, int);
118290001Sglebiusstatic struct peer *	findexistingpeer_addr(sockaddr_u *,
119290001Sglebius					      struct peer *, int,
120290001Sglebius					      u_char);
121290001Sglebiusstatic void		free_peer(struct peer *, int);
122290001Sglebiusstatic void		getmorepeermem(void);
123290001Sglebiusstatic int		score(struct peer *);
12454359Sroberto
125290001Sglebius
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{
135290001Sglebius	int i;
13654359Sroberto
13754359Sroberto	/*
138290001Sglebius	 * Initialize peer free list from static allocation.
13954359Sroberto	 */
140290001Sglebius	for (i = COUNTOF(init_peer_alloc) - 1; i >= 0; i--)
141290001Sglebius		LINK_SLIST(peer_free, &init_peer_alloc[i], p_link);
142290001Sglebius	total_peer_structs = COUNTOF(init_peer_alloc);
143290001Sglebius	peer_free_count = COUNTOF(init_peer_alloc);
14454359Sroberto
14554359Sroberto	/*
14654359Sroberto	 * Initialize our first association ID
14754359Sroberto	 */
148290001Sglebius	do
149290001Sglebius		current_association_ID = ntp_random() & ASSOCID_MAX;
150290001Sglebius	while (!current_association_ID);
151290001Sglebius	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{
161290001Sglebius	int i;
162290001Sglebius	struct peer *peers;
16354359Sroberto
164290001Sglebius	peers = emalloc_zero(INC_PEER_ALLOC * sizeof(*peers));
16554359Sroberto
166290001Sglebius	for (i = INC_PEER_ALLOC - 1; i >= 0; i--)
167290001Sglebius		LINK_SLIST(peer_free, &peers[i], p_link);
168290001Sglebius
16954359Sroberto	total_peer_structs += INC_PEER_ALLOC;
17054359Sroberto	peer_free_count += INC_PEER_ALLOC;
17154359Sroberto}
17254359Sroberto
17354359Sroberto
174290001Sglebiusstatic struct peer *
175290001Sglebiusfindexistingpeer_name(
176290001Sglebius	const char *	hostname,
177290001Sglebius	u_short		hname_fam,
178290001Sglebius	struct peer *	start_peer,
179290001Sglebius	int		mode
18054359Sroberto	)
18154359Sroberto{
182290001Sglebius	struct peer *p;
18354359Sroberto
184290001Sglebius	if (NULL == start_peer)
185290001Sglebius		p = peer_list;
186290001Sglebius	else
187290001Sglebius		p = start_peer->p_link;
188290001Sglebius	for (; p != NULL; p = p->p_link)
189290001Sglebius		if (p->hostname != NULL
190290001Sglebius		    && (-1 == mode || p->hmode == mode)
191290001Sglebius		    && (AF_UNSPEC == hname_fam
192290001Sglebius			|| AF_UNSPEC == AF(&p->srcadr)
193290001Sglebius			|| hname_fam == AF(&p->srcadr))
194290001Sglebius		    && !strcasecmp(p->hostname, hostname))
195290001Sglebius			break;
196290001Sglebius	return p;
197290001Sglebius}
198290001Sglebius
199290001Sglebius
200290001Sglebiusstatic
201290001Sglebiusstruct peer *
202290001Sglebiusfindexistingpeer_addr(
203290001Sglebius	sockaddr_u *	addr,
204290001Sglebius	struct peer *	start_peer,
205290001Sglebius	int		mode,
206290001Sglebius	u_char		cast_flags
207290001Sglebius	)
208290001Sglebius{
209290001Sglebius	struct peer *peer;
210290001Sglebius
211290001Sglebius	DPRINTF(2, ("findexistingpeer_addr(%s, %s, %d, 0x%x)\n",
212290001Sglebius		sptoa(addr),
213290001Sglebius		(start_peer)
214290001Sglebius		    ? sptoa(&start_peer->srcadr)
215290001Sglebius		    : "NULL",
216290001Sglebius		mode, (u_int)cast_flags));
217290001Sglebius
21854359Sroberto	/*
21954359Sroberto	 * start_peer is included so we can locate instances of the
22054359Sroberto	 * same peer through different interfaces in the hash table.
221290001Sglebius	 * Without MDF_BCLNT, a match requires the same mode and remote
222290001Sglebius	 * address.  MDF_BCLNT associations start out as MODE_CLIENT
223290001Sglebius	 * if broadcastdelay is not specified, and switch to
224290001Sglebius	 * MODE_BCLIENT after estimating the one-way delay.  Duplicate
225290001Sglebius	 * associations are expanded in definition to match any other
226290001Sglebius	 * MDF_BCLNT with the same srcadr (remote, unicast address).
22754359Sroberto	 */
228290001Sglebius	if (NULL == start_peer)
229182007Sroberto		peer = peer_hash[NTP_HASH_ADDR(addr)];
23054359Sroberto	else
231290001Sglebius		peer = start_peer->adr_link;
23254359Sroberto
233290001Sglebius	while (peer != NULL) {
234290001Sglebius		DPRINTF(3, ("%s %s %d %d 0x%x 0x%x ", sptoa(addr),
235290001Sglebius			sptoa(&peer->srcadr), mode, peer->hmode,
236290001Sglebius			(u_int)cast_flags, (u_int)peer->cast_flags));
237290001Sglebius 		if ((-1 == mode || peer->hmode == mode ||
238290001Sglebius		     ((MDF_BCLNT & peer->cast_flags) &&
239290001Sglebius		      (MDF_BCLNT & cast_flags))) &&
240290001Sglebius		    ADDR_PORT_EQ(addr, &peer->srcadr)) {
241290001Sglebius			DPRINTF(3, ("found.\n"));
242290001Sglebius			break;
24354359Sroberto		}
244290001Sglebius		DPRINTF(3, ("\n"));
245290001Sglebius		peer = peer->adr_link;
24654359Sroberto	}
247290001Sglebius
248290001Sglebius	return peer;
24954359Sroberto}
25054359Sroberto
25154359Sroberto
25254359Sroberto/*
253290001Sglebius * findexistingpeer - search by address and return a pointer to a peer.
25454359Sroberto */
25554359Srobertostruct peer *
256290001Sglebiusfindexistingpeer(
257290001Sglebius	sockaddr_u *	addr,
258290001Sglebius	const char *	hostname,
259290001Sglebius	struct peer *	start_peer,
260290001Sglebius	int		mode,
261290001Sglebius	u_char		cast_flags
262290001Sglebius	)
263290001Sglebius{
264290001Sglebius	if (hostname != NULL)
265290001Sglebius		return findexistingpeer_name(hostname, AF(addr),
266290001Sglebius					     start_peer, mode);
267290001Sglebius	else
268290001Sglebius		return findexistingpeer_addr(addr, start_peer, mode,
269290001Sglebius					     cast_flags);
270290001Sglebius}
271290001Sglebius
272290001Sglebius
273290001Sglebius/*
274290001Sglebius * findpeer - find and return a peer match for a received datagram in
275290001Sglebius *	      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.
292290001Sglebius */
293290001Sglebiusstruct peer *
29454359Srobertofindpeer(
295290001Sglebius	struct recvbuf *rbufp,
296290001Sglebius	int		pkt_mode,
297290001Sglebius	int *		action
29854359Sroberto	)
29954359Sroberto{
300290001Sglebius	struct peer *	p;
301290001Sglebius	sockaddr_u *	srcadr;
302290001Sglebius	u_int		hash;
303290001Sglebius	struct pkt *	pkt;
304290001Sglebius	l_fp		pkt_org;
30554359Sroberto
30654359Sroberto	findpeer_calls++;
307290001Sglebius	srcadr = &rbufp->recv_srcadr;
308182007Sroberto	hash = NTP_HASH_ADDR(srcadr);
309290001Sglebius	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);
324290001Sglebius
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
354290001Sglebius	return p;
35554359Sroberto}
35654359Sroberto
35754359Sroberto/*
358290001Sglebius * findpeerbyassoc - find and return a peer using his association ID
35954359Sroberto */
36054359Srobertostruct peer *
36154359Srobertofindpeerbyassoc(
362290001Sglebius	associd_t assoc
36354359Sroberto	)
36454359Sroberto{
365290001Sglebius	struct peer *p;
366290001Sglebius	u_int hash;
36754359Sroberto
36854359Sroberto	assocpeer_calls++;
369182007Sroberto	hash = assoc & NTP_HASH_MASK;
370290001Sglebius	for (p = assoc_hash[hash]; p != NULL; p = p->aid_link)
371290001Sglebius		if (assoc == p->associd)
372290001Sglebius			break;
373290001Sglebius	return p;
37454359Sroberto}
37554359Sroberto
37654359Sroberto
37754359Sroberto/*
37882498Sroberto * clear_all - flush all time values for all associations
37954359Sroberto */
38054359Srobertovoid
38182498Srobertoclear_all(void)
38254359Sroberto{
383290001Sglebius	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	 */
389290001Sglebius	for (p = peer_list; p != NULL; p = p->p_link)
390290001Sglebius		if (!(MDF_TXONLY_MASK & p->cast_flags))
391290001Sglebius			peer_clear(p, "STEP");
392290001Sglebius
393290001Sglebius	DPRINTF(1, ("clear_all: at %lu\n", current_time));
39454359Sroberto}
39582498Sroberto
39682498Sroberto
39754359Sroberto/*
398290001Sglebius * score_all() - determine if an association can be demobilized
39954359Sroberto */
400290001Sglebiusint
401290001Sglebiusscore_all(
402290001Sglebius	struct peer *peer	/* peer structure pointer */
40354359Sroberto	)
40454359Sroberto{
405290001Sglebius	struct peer *speer;
406290001Sglebius	int	temp, tamp;
407290001Sglebius	int	x;
40854359Sroberto
409290001Sglebius	/*
410290001Sglebius	 * This routine finds the minimum score for all preemptible
411290001Sglebius	 * associations and returns > 0 if the association can be
412290001Sglebius	 * demobilized.
413290001Sglebius	 */
414290001Sglebius	tamp = score(peer);
415290001Sglebius	temp = 100;
416290001Sglebius	for (speer = peer_list; speer != NULL; speer = speer->p_link)
417290001Sglebius		if (speer->flags & FLAG_PREEMPT) {
418290001Sglebius			x = score(speer);
419290001Sglebius			if (x < temp)
420290001Sglebius				temp = x;
421290001Sglebius		}
422290001Sglebius	DPRINTF(1, ("score_all: at %lu score %d min %d\n",
423290001Sglebius		    current_time, tamp, temp));
424182007Sroberto
425290001Sglebius	if (tamp != temp)
426290001Sglebius		temp = 0;
427182007Sroberto
428290001Sglebius	return temp;
429290001Sglebius}
430290001Sglebius
431290001Sglebius
432290001Sglebius/*
433290001Sglebius * score() - calculate preemption score
434290001Sglebius */
435290001Sglebiusstatic int
436290001Sglebiusscore(
437290001Sglebius	struct peer *peer	/* peer structure pointer */
438290001Sglebius	)
439290001Sglebius{
440290001Sglebius	int	temp;
441290001Sglebius
44254359Sroberto	/*
443290001Sglebius	 * This routine calculates the premption score from the peer
444290001Sglebius	 * error bits and status. Increasing values are more cherished.
44554359Sroberto	 */
446290001Sglebius	temp = 0;
447290001Sglebius	if (!(peer->flash & TEST10))
448290001Sglebius		temp++;			/* 1 good synch and stratum */
449290001Sglebius	if (!(peer->flash & TEST13))
450290001Sglebius		temp++;			/* 2 reachable */
451290001Sglebius	if (!(peer->flash & TEST12))
452290001Sglebius		temp++;			/* 3 no loop */
453290001Sglebius	if (!(peer->flash & TEST11))
454290001Sglebius		temp++;			/* 4 good distance */
455290001Sglebius	if (peer->status >= CTL_PST_SEL_SELCAND)
456290001Sglebius		temp++;			/* 5 in the hunt */
457290001Sglebius	if (peer->status != CTL_PST_SEL_EXCESS)
458290001Sglebius		temp++;			/* 6 not spare tire */
459290001Sglebius	return (temp);			/* selection status */
460290001Sglebius}
46154359Sroberto
462290001Sglebius
463290001Sglebius/*
464290001Sglebius * free_peer - internal routine to free memory referred to by a struct
465290001Sglebius *	       peer and return it to the peer free list.  If unlink is
466290001Sglebius *	       nonzero, unlink from the various lists.
467290001Sglebius */
468290001Sglebiusstatic void
469290001Sglebiusfree_peer(
470290001Sglebius	struct peer *	p,
471290001Sglebius	int		unlink_peer
472290001Sglebius	)
473290001Sglebius{
474290001Sglebius	struct peer *	unlinked;
475290001Sglebius	int		hash;
476290001Sglebius
477290001Sglebius	if (unlink_peer) {
478290001Sglebius		hash = NTP_HASH_ADDR(&p->srcadr);
479290001Sglebius		peer_hash_count[hash]--;
480290001Sglebius
481290001Sglebius		UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link,
482290001Sglebius			     struct peer);
483290001Sglebius		if (NULL == unlinked) {
48454359Sroberto			peer_hash_count[hash]++;
485290001Sglebius			msyslog(LOG_ERR, "peer %s not in address table!",
486290001Sglebius				stoa(&p->srcadr));
48754359Sroberto		}
48854359Sroberto
489290001Sglebius		/*
490290001Sglebius		 * Remove him from the association hash as well.
491290001Sglebius		 */
492290001Sglebius		hash = p->associd & NTP_HASH_MASK;
493290001Sglebius		assoc_hash_count[hash]--;
49454359Sroberto
495290001Sglebius		UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link,
496290001Sglebius			     struct peer);
497290001Sglebius		if (NULL == unlinked) {
49854359Sroberto			assoc_hash_count[hash]++;
49954359Sroberto			msyslog(LOG_ERR,
500290001Sglebius				"peer %s not in association ID table!",
501290001Sglebius				stoa(&p->srcadr));
50254359Sroberto		}
503290001Sglebius
504290001Sglebius		/* Remove him from the overall list. */
505290001Sglebius		UNLINK_SLIST(unlinked, peer_list, p, p_link,
506290001Sglebius			     struct peer);
507290001Sglebius		if (NULL == unlinked)
508290001Sglebius			msyslog(LOG_ERR, "%s not in peer list!",
509290001Sglebius				stoa(&p->srcadr));
51054359Sroberto	}
511290001Sglebius
512290001Sglebius	if (p->hostname != NULL)
513290001Sglebius		free(p->hostname);
514290001Sglebius
515290001Sglebius	if (p->ident != NULL)
516290001Sglebius		free(p->ident);
517290001Sglebius
518290001Sglebius	if (p->addrs != NULL)
519290001Sglebius		free(p->addrs);		/* from copy_addrinfo_list() */
520290001Sglebius
521290001Sglebius	/* Add his corporeal form to peer free list */
522290001Sglebius	ZERO(*p);
523290001Sglebius	LINK_SLIST(peer_free, p, p_link);
52454359Sroberto	peer_free_count++;
52554359Sroberto}
52654359Sroberto
52754359Sroberto
52854359Sroberto/*
529290001Sglebius * unpeer - remove peer structure from hash table and free structure
530290001Sglebius */
531290001Sglebiusvoid
532290001Sglebiusunpeer(
533290001Sglebius	struct peer *peer
534290001Sglebius	)
535290001Sglebius{
536290001Sglebius	mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd);
537290001Sglebius	restrict_source(&peer->srcadr, 1, 0);
538290001Sglebius	set_peerdstadr(peer, NULL);
539290001Sglebius	peer_demobilizations++;
540290001Sglebius	peer_associations--;
541290001Sglebius	if (FLAG_PREEMPT & peer->flags)
542290001Sglebius		peer_preempt--;
543290001Sglebius#ifdef REFCLOCK
544290001Sglebius	/*
545290001Sglebius	 * If this peer is actually a clock, shut it down first
546290001Sglebius	 */
547290001Sglebius	if (FLAG_REFCLOCK & peer->flags)
548290001Sglebius		refclock_unpeer(peer);
549290001Sglebius#endif
550290001Sglebius
551290001Sglebius	free_peer(peer, TRUE);
552290001Sglebius}
553290001Sglebius
554290001Sglebius
555290001Sglebius/*
55682498Sroberto * peer_config - configure a new association
55754359Sroberto */
55854359Srobertostruct peer *
55954359Srobertopeer_config(
560290001Sglebius	sockaddr_u *	srcadr,
561290001Sglebius	const char *	hostname,
562290001Sglebius	endpt *		dstadr,
563290001Sglebius	u_char		hmode,
564290001Sglebius	u_char		version,
565290001Sglebius	u_char		minpoll,
566290001Sglebius	u_char		maxpoll,
567290001Sglebius	u_int		flags,
568290001Sglebius	u_int32		ttl,
569290001Sglebius	keyid_t		key,
570290001Sglebius	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:
582290001Sglebius		if (IS_MCAST(srcadr))
583290001Sglebius			cast_flags = MDF_MCAST;
584290001Sglebius		else
585290001Sglebius			cast_flags = MDF_BCAST;
586290001Sglebius		break;
58782498Sroberto
58882498Sroberto	case MODE_CLIENT:
589290001Sglebius		if (hostname != NULL && SOCK_UNSPEC(srcadr))
590290001Sglebius			cast_flags = MDF_POOL;
591290001Sglebius		else if (IS_MCAST(srcadr))
592290001Sglebius			cast_flags = MDF_ACAST;
593290001Sglebius		else
594290001Sglebius			cast_flags = MDF_UCAST;
595290001Sglebius		break;
59682498Sroberto
59782498Sroberto	default:
59882498Sroberto		cast_flags = MDF_UCAST;
59982498Sroberto	}
60082498Sroberto
60182498Sroberto	/*
602290001Sglebius	 * Mobilize the association and initialize its variables. If
603290001Sglebius	 * emulating ntpdate, force iburst.  For pool and manycastclient
604290001Sglebius	 * strip FLAG_PREEMPT as the prototype associations are not
605290001Sglebius	 * themselves preemptible, though the resulting associations
606290001Sglebius	 * are.
60782498Sroberto	 */
608290001Sglebius	flags |= FLAG_CONFIG;
609132451Sroberto	if (mode_ntpdate)
610132451Sroberto		flags |= FLAG_IBURST;
611290001Sglebius	if ((MDF_ACAST | MDF_POOL) & cast_flags)
612290001Sglebius		flags &= ~FLAG_PREEMPT;
613290001Sglebius	return newpeer(srcadr, hostname, dstadr, hmode, version,
614290001Sglebius	    minpoll, maxpoll, flags, cast_flags, ttl, key, ident);
61554359Sroberto}
61654359Sroberto
617182007Sroberto/*
618290001Sglebius * setup peer dstadr field keeping it in sync with the interface
619290001Sglebius * structures
620182007Sroberto */
621182007Srobertovoid
622290001Sglebiusset_peerdstadr(
623290001Sglebius	struct peer *	p,
624290001Sglebius	endpt *		dstadr
625290001Sglebius	)
626182007Sroberto{
627290001Sglebius	struct peer *	unlinked;
62854359Sroberto
629310419Sdelphij	DEBUG_INSIST(p != NULL);
630310419Sdelphij
631310419Sdelphij	if (p == NULL)
632310419Sdelphij		return;
633310419Sdelphij
634310419Sdelphij	/* check for impossible or identical assignment */
635290001Sglebius	if (p->dstadr == dstadr)
636290001Sglebius		return;
637182007Sroberto
638290001Sglebius	/*
639290001Sglebius	 * Don't accept updates to a separate multicast receive-only
640290001Sglebius	 * endpt while a BCLNT peer is running its unicast protocol.
641290001Sglebius	 */
642290001Sglebius	if (dstadr != NULL && (FLAG_BC_VOL & p->flags) &&
643290001Sglebius	    (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) {
644290001Sglebius		return;
645182007Sroberto	}
646310419Sdelphij
647310419Sdelphij	/* unlink from list if we have an address prior to assignment */
648290001Sglebius	if (p->dstadr != NULL) {
649290001Sglebius		p->dstadr->peercnt--;
650290001Sglebius		UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink,
651290001Sglebius			     struct peer);
652290001Sglebius		msyslog(LOG_INFO, "%s local addr %s -> %s",
653290001Sglebius			stoa(&p->srcadr), latoa(p->dstadr),
654290001Sglebius			latoa(dstadr));
655290001Sglebius	}
656310419Sdelphij
657290001Sglebius	p->dstadr = dstadr;
658310419Sdelphij
659310419Sdelphij	/* link to list if we have an address after assignment */
660310419Sdelphij	if (p->dstadr != NULL) {
661290001Sglebius		LINK_SLIST(dstadr->peers, p, ilink);
662290001Sglebius		dstadr->peercnt++;
663290001Sglebius	}
664182007Sroberto}
665182007Sroberto
66654359Sroberto/*
667182007Sroberto * attempt to re-rebind interface if necessary
668182007Sroberto */
669182007Srobertostatic void
670290001Sglebiuspeer_refresh_interface(
671290001Sglebius	struct peer *p
672290001Sglebius	)
673182007Sroberto{
674290001Sglebius	endpt *	niface;
675290001Sglebius	endpt *	piface;
676182007Sroberto
677290001Sglebius	niface = select_peerinterface(p, &p->srcadr, NULL);
678182007Sroberto
679290001Sglebius	DPRINTF(4, (
680290001Sglebius	    "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ",
681290001Sglebius	    p->dstadr == NULL ? "<null>" :
682290001Sglebius	    stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode,
683290001Sglebius	    p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags,
684290001Sglebius	    p->ttl, p->keyid));
685290001Sglebius	if (niface != NULL) {
686290001Sglebius		DPRINTF(4, (
687290001Sglebius		    "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s",
688290001Sglebius		    niface->fd,  niface->bfd, niface->name,
689290001Sglebius		    niface->flags, niface->ifindex,
690290001Sglebius		    stoa(&niface->sin)));
691290001Sglebius		if (niface->flags & INT_BROADCAST)
692290001Sglebius			DPRINTF(4, (", bcast=%s",
693290001Sglebius				stoa(&niface->bcast)));
694290001Sglebius		DPRINTF(4, (", mask=%s\n", stoa(&niface->mask)));
695290001Sglebius	} else {
696290001Sglebius		DPRINTF(4, ("<NONE>\n"));
697182007Sroberto	}
698182007Sroberto
699290001Sglebius	piface = p->dstadr;
700290001Sglebius	set_peerdstadr(p, niface);
701290001Sglebius	if (p->dstadr != NULL) {
702290001Sglebius		/*
703290001Sglebius		 * clear crypto if we change the local address
704290001Sglebius		 */
705290001Sglebius		if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags)
706290001Sglebius		    && MODE_BROADCAST != p->pmode)
707290001Sglebius			peer_clear(p, "XFAC");
708182007Sroberto
709182007Sroberto		/*
710182007Sroberto	 	 * Broadcast needs the socket enabled for broadcast
711182007Sroberto	 	 */
712290001Sglebius		if (MDF_BCAST & p->cast_flags)
713290001Sglebius			enable_broadcast(p->dstadr, &p->srcadr);
714182007Sroberto
715182007Sroberto		/*
716290001Sglebius	 	 * Multicast needs the socket interface enabled for
717290001Sglebius		 * multicast
718182007Sroberto	 	 */
719290001Sglebius		if (MDF_MCAST & p->cast_flags)
720290001Sglebius			enable_multicast_if(p->dstadr, &p->srcadr);
721182007Sroberto	}
722182007Sroberto}
723182007Sroberto
724290001Sglebius
725182007Sroberto/*
726290001Sglebius * refresh_all_peerinterfaces - see that all interface bindings are up
727290001Sglebius * to date
728182007Sroberto */
729182007Srobertovoid
730182007Srobertorefresh_all_peerinterfaces(void)
731182007Sroberto{
732290001Sglebius	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
737290001Sglebius	 * but only if either they don't have an address already
738290001Sglebius	 * or if the one they have hasn't worked for a while.
739182007Sroberto	 */
740290001Sglebius	for (p = peer_list; p != NULL; p = p->p_link) {
741290001Sglebius		if (!(p->dstadr && (p->reach & 0x3)))	// Bug 2849 XOR 2043
742290001Sglebius			peer_refresh_interface(p);
743182007Sroberto	}
744182007Sroberto}
745182007Sroberto
746290001Sglebius
747182007Sroberto/*
748290001Sglebius * newpeer - initialize a new peer association
749182007Sroberto */
750290001Sglebiusstruct peer *
751290001Sglebiusnewpeer(
752290001Sglebius	sockaddr_u *	srcadr,
753290001Sglebius	const char *	hostname,
754290001Sglebius	endpt *		dstadr,
755290001Sglebius	u_char		hmode,
756290001Sglebius	u_char		version,
757290001Sglebius	u_char		minpoll,
758290001Sglebius	u_char		maxpoll,
759290001Sglebius	u_int		flags,
760290001Sglebius	u_char		cast_flags,
761290001Sglebius	u_int32		ttl,
762290001Sglebius	keyid_t		key,
763290001Sglebius	const char *	ident
764290001Sglebius	)
765182007Sroberto{
766290001Sglebius	struct peer *	peer;
767290001Sglebius	u_int		hash;
768290001Sglebius
769290001Sglebius	DEBUG_REQUIRE(srcadr);
770290001Sglebius
771290001Sglebius#ifdef AUTOKEY
772182007Sroberto	/*
773290001Sglebius	 * If Autokey is requested but not configured, complain loudly.
774182007Sroberto	 */
775290001Sglebius	if (!crypto_flags) {
776290001Sglebius		if (key > NTP_MAXKEY) {
777290001Sglebius			return (NULL);
778182007Sroberto
779290001Sglebius		} else if (flags & FLAG_SKEY) {
780290001Sglebius			msyslog(LOG_ERR, "Autokey not configured");
781290001Sglebius			return (NULL);
782290001Sglebius		}
783290001Sglebius	}
784290001Sglebius#endif	/* AUTOKEY */
785290001Sglebius
786182007Sroberto	/*
787290001Sglebius	 * For now only pool associations have a hostname.
788182007Sroberto	 */
789290001Sglebius	INSIST(NULL == hostname || (MDF_POOL & cast_flags));
790182007Sroberto
791290001Sglebius	/*
792290001Sglebius	 * First search from the beginning for an association with given
793290001Sglebius	 * remote address and mode. If an interface is given, search
794290001Sglebius	 * from there to find the association which matches that
795290001Sglebius	 * destination. If the given interface is "any", track down the
796290001Sglebius	 * actual interface, because that's what gets put into the peer
797290001Sglebius	 * structure.
798290001Sglebius	 */
799290001Sglebius	if (dstadr != NULL) {
800290001Sglebius		peer = findexistingpeer(srcadr, hostname, NULL, hmode,
801290001Sglebius					cast_flags);
802290001Sglebius		while (peer != NULL) {
803290001Sglebius			if (peer->dstadr == dstadr ||
804290001Sglebius			    ((MDF_BCLNT & cast_flags) &&
805290001Sglebius			     (MDF_BCLNT & peer->cast_flags)))
806290001Sglebius				break;
807182007Sroberto
808290001Sglebius			if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) &&
809290001Sglebius			    peer->dstadr == findinterface(srcadr))
810290001Sglebius				break;
811182007Sroberto
812290001Sglebius			peer = findexistingpeer(srcadr, hostname, peer,
813290001Sglebius						hmode, cast_flags);
814290001Sglebius		}
815290001Sglebius	} else {
816290001Sglebius		/* no endpt address given */
817290001Sglebius		peer = findexistingpeer(srcadr, hostname, NULL, hmode,
818290001Sglebius					cast_flags);
819290001Sglebius	}
82054359Sroberto
82154359Sroberto	/*
822290001Sglebius	 * If a peer is found, this would be a duplicate and we don't
823290001Sglebius	 * allow that. This avoids duplicate ephemeral (broadcast/
824290001Sglebius	 * multicast) and preemptible (manycast and pool) client
825290001Sglebius	 * associations.
826290001Sglebius	 */
827290001Sglebius	if (peer != NULL) {
828290001Sglebius		DPRINTF(2, ("newpeer(%s) found existing association\n",
829290001Sglebius			(hostname)
830290001Sglebius			    ? hostname
831290001Sglebius			    : stoa(srcadr)));
832290001Sglebius		return NULL;
833290001Sglebius	}
834290001Sglebius
835290001Sglebius	/*
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();
841290001Sglebius	UNLINK_HEAD_SLIST(peer, peer_free, p_link);
842290001Sglebius	INSIST(peer != NULL);
84354359Sroberto	peer_free_count--;
84454359Sroberto	peer_associations++;
845290001Sglebius	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;
856290001Sglebius	if (hostname != NULL)
857290001Sglebius		peer->hostname = estrdup(hostname);
858290001Sglebius	peer->hmode = hmode;
859290001Sglebius	peer->version = version;
860132451Sroberto	peer->flags = flags;
861290001Sglebius	peer->cast_flags = cast_flags;
862290001Sglebius	set_peerdstadr(peer,
863290001Sglebius		       select_peerinterface(peer, srcadr, dstadr));
864182007Sroberto
865182007Sroberto	/*
866290001Sglebius	 * It is an error to set minpoll less than NTP_MINPOLL or to
867290001Sglebius	 * set maxpoll greater than NTP_MAXPOLL. However, minpoll is
868290001Sglebius	 * clamped not greater than NTP_MAXPOLL and maxpoll is clamped
869290001Sglebius	 * not less than NTP_MINPOLL without complaint. Finally,
870290001Sglebius	 * minpoll is clamped not greater than maxpoll.
871290001Sglebius	 */
872290001Sglebius	if (minpoll == 0)
873290001Sglebius		peer->minpoll = NTP_MINDPOLL;
874290001Sglebius	else
875290001Sglebius		peer->minpoll = min(minpoll, NTP_MAXPOLL);
876290001Sglebius	if (maxpoll == 0)
877290001Sglebius		peer->maxpoll = NTP_MAXDPOLL;
878290001Sglebius	else
879290001Sglebius		peer->maxpoll = max(maxpoll, NTP_MINPOLL);
880290001Sglebius	if (peer->minpoll > peer->maxpoll)
881290001Sglebius		peer->minpoll = peer->maxpoll;
882290001Sglebius
883290001Sglebius	if (peer->dstadr != NULL)
884290001Sglebius		DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n",
885290001Sglebius			stoa(srcadr), peer->dstadr->fd,
886290001Sglebius			stoa(&peer->dstadr->sin)));
887290001Sglebius	else
888290001Sglebius		DPRINTF(3, ("newpeer(%s): local interface currently not bound\n",
889290001Sglebius			stoa(srcadr)));
890290001Sglebius
891290001Sglebius	/*
892182007Sroberto	 * Broadcast needs the socket enabled for broadcast
893182007Sroberto	 */
894290001Sglebius	if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL)
895182007Sroberto		enable_broadcast(peer->dstadr, srcadr);
896290001Sglebius
897182007Sroberto	/*
898182007Sroberto	 * Multicast needs the socket interface enabled for multicast
899182007Sroberto	 */
900290001Sglebius	if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL)
901182007Sroberto		enable_multicast_if(peer->dstadr, srcadr);
902290001Sglebius
903290001Sglebius#ifdef AUTOKEY
904132451Sroberto	if (key > NTP_MAXKEY)
905132451Sroberto		peer->flags |= FLAG_SKEY;
906290001Sglebius#endif	/* AUTOKEY */
907290001Sglebius	peer->ttl = ttl;
90854359Sroberto	peer->keyid = key;
909290001Sglebius	if (ident != NULL)
910290001Sglebius		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");
915290001Sglebius	else if (cast_flags & MDF_POOL)
916290001Sglebius		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
933290001Sglebius	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		 */
941290001Sglebius		if (maxpoll == 0)
942290001Sglebius			peer->maxpoll = peer->minpoll;
94354359Sroberto		if (!refclock_newpeer(peer)) {
94454359Sroberto			/*
94554359Sroberto			 * Dump it, something screwed up
94654359Sroberto			 */
947182007Sroberto			set_peerdstadr(peer, NULL);
948290001Sglebius			free_peer(peer, 0);
949290001Sglebius			return NULL;
95054359Sroberto		}
951290001Sglebius#else /* REFCLOCK */
952290001Sglebius		msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.",
953290001Sglebius			stoa(&peer->srcadr));
954290001Sglebius		set_peerdstadr(peer, NULL);
955290001Sglebius		free_peer(peer, 0);
956290001Sglebius		return NULL;
957290001Sglebius#endif /* REFCLOCK */
95854359Sroberto	}
95954359Sroberto
96054359Sroberto	/*
96182498Sroberto	 * Put the new peer in the hash tables.
96254359Sroberto	 */
963290001Sglebius	hash = NTP_HASH_ADDR(&peer->srcadr);
964290001Sglebius	LINK_SLIST(peer_hash[hash], peer, adr_link);
965290001Sglebius	peer_hash_count[hash]++;
966290001Sglebius	hash = peer->associd & NTP_HASH_MASK;
967290001Sglebius	LINK_SLIST(assoc_hash[hash], peer, aid_link);
968290001Sglebius	assoc_hash_count[hash]++;
969290001Sglebius	LINK_SLIST(peer_list, peer, p_link);
970182007Sroberto
971290001Sglebius	restrict_source(&peer->srcadr, 0, 0);
972290001Sglebius	mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd);
973290001Sglebius	DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n",
974290001Sglebius	    latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode,
975290001Sglebius	    peer->version, peer->minpoll, peer->maxpoll, peer->flags,
976290001Sglebius	    peer->cast_flags, peer->ttl, peer->keyid));
977290001Sglebius	return peer;
97854359Sroberto}
97954359Sroberto
98054359Sroberto
98154359Sroberto/*
982290001Sglebius * 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
994290001Sglebius
99554359Sroberto/*
996290001Sglebius * peer_reset - reset statistics counters
99754359Sroberto */
99854359Srobertovoid
99954359Srobertopeer_reset(
100054359Sroberto	struct peer *peer
100154359Sroberto	)
100254359Sroberto{
1003290001Sglebius	if (peer == NULL)
1004290001Sglebius		return;
1005290001Sglebius
1006290001Sglebius	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;
1014290001Sglebius	peer->selbroken = 0;
101554359Sroberto}
101654359Sroberto
101754359Sroberto
101854359Sroberto/*
1019290001Sglebius * peer_all_reset - reset all peer statistics counters
102054359Sroberto */
102154359Srobertovoid
102254359Srobertopeer_all_reset(void)
102354359Sroberto{
102454359Sroberto	struct peer *peer;
102554359Sroberto
1026290001Sglebius	for (peer = peer_list; peer != NULL; peer = peer->p_link)
102754359Sroberto		peer_reset(peer);
102854359Sroberto}
102982498Sroberto
103082498Sroberto
103182498Sroberto/*
1032290001Sglebius * findmanycastpeer - find and return a manycastclient or pool
1033290001Sglebius *		      association matching a received response.
103482498Sroberto */
103582498Srobertostruct peer *
103682498Srobertofindmanycastpeer(
1037290001Sglebius	struct recvbuf *rbufp	/* receive buffer pointer */
103882498Sroberto	)
103982498Sroberto{
1040290001Sglebius	struct peer *peer;
104182498Sroberto	struct pkt *pkt;
104282498Sroberto	l_fp p_org;
104382498Sroberto
104482498Sroberto 	/*
1045290001Sglebius 	 * This routine is called upon arrival of a server-mode response
1046290001Sglebius	 * to a manycastclient multicast solicitation, or to a pool
1047290001Sglebius	 * server unicast solicitation.  Search the peer list for a
1048290001Sglebius	 * manycastclient association where the last transmit timestamp
1049290001Sglebius	 * matches the response packet's originate timestamp.  There can
1050290001Sglebius	 * be multiple manycastclient associations, or multiple pool
1051290001Sglebius	 * solicitation assocations, so this assumes the transmit
1052290001Sglebius	 * timestamps are unique for such.
105382498Sroberto	 */
105482498Sroberto	pkt = &rbufp->recv_pkt;
1055290001Sglebius	for (peer = peer_list; peer != NULL; peer = peer->p_link)
1056290001Sglebius		if (MDF_SOLICIT_MASK & peer->cast_flags) {
1057290001Sglebius			NTOHL_FP(&pkt->org, &p_org);
1058290001Sglebius			if (L_ISEQU(&p_org, &peer->aorg))
1059290001Sglebius				break;
1060290001Sglebius		}
106182498Sroberto
1062290001Sglebius	return peer;
106382498Sroberto}
1064290001Sglebius
1065290001Sglebius/* peer_cleanup - clean peer list prior to shutdown */
1066290001Sglebiusvoid peer_cleanup(void)
1067290001Sglebius{
1068290001Sglebius        struct peer *peer;
1069290001Sglebius        associd_t assoc;
1070290001Sglebius
1071290001Sglebius        for (assoc = initial_association_ID; assoc != current_association_ID; assoc++) {
1072290001Sglebius            if (assoc != 0U) {
1073290001Sglebius                peer = findpeerbyassoc(assoc);
1074290001Sglebius                if (peer != NULL)
1075290001Sglebius                    unpeer(peer);
1076290001Sglebius            }
1077290001Sglebius        }
1078290001Sglebius        peer = findpeerbyassoc(current_association_ID);
1079290001Sglebius        if (peer != NULL)
1080290001Sglebius            unpeer(peer);
1081290001Sglebius}
1082