1c0b746e5SOllivier Robert /*
2c0b746e5SOllivier Robert * ntp_peer.c - management of data maintained for peer associations
3c0b746e5SOllivier Robert */
4c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
5c0b746e5SOllivier Robert #include <config.h>
6c0b746e5SOllivier Robert #endif
7c0b746e5SOllivier Robert
8c0b746e5SOllivier Robert #include <stdio.h>
9c0b746e5SOllivier Robert #include <sys/types.h>
10c0b746e5SOllivier Robert
11c0b746e5SOllivier Robert #include "ntpd.h"
12c0b746e5SOllivier Robert #include "ntp_lists.h"
139c2daa00SOllivier Robert #include "ntp_stdlib.h"
149c2daa00SOllivier Robert #include "ntp_control.h"
159c2daa00SOllivier Robert #include <ntp_random.h>
16c0b746e5SOllivier Robert
17c0b746e5SOllivier Robert /*
18c0b746e5SOllivier Robert * Table of valid association combinations
19c0b746e5SOllivier Robert * ---------------------------------------
20c0b746e5SOllivier Robert *
21c0b746e5SOllivier Robert * packet->mode
22c0b746e5SOllivier Robert * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST
23c0b746e5SOllivier Robert * ---------- | ---------------------------------------------
24c0b746e5SOllivier Robert * NO_PEER | e 1 0 1 1 1
25c0b746e5SOllivier Robert * ACTIVE | e 1 1 0 0 0
26c0b746e5SOllivier Robert * PASSIVE | e 1 e 0 0 0
27c0b746e5SOllivier Robert * CLIENT | e 0 0 0 1 0
28c0b746e5SOllivier Robert * SERVER | e 0 0 0 0 0
29c0b746e5SOllivier Robert * BCAST | e 0 0 0 0 0
30c0b746e5SOllivier Robert * BCLIENT | e 0 0 0 e 1
31c0b746e5SOllivier Robert *
32c0b746e5SOllivier Robert * One point to note here: a packet in BCAST mode can potentially match
33c0b746e5SOllivier Robert * a peer in CLIENT mode, but we that is a special case and we check for
34224ba2bdSOllivier Robert * that early in the decision process. This avoids having to keep track
35224ba2bdSOllivier Robert * of what kind of associations are possible etc... We actually
36224ba2bdSOllivier Robert * circumvent that problem by requiring that the first b(m)roadcast
37224ba2bdSOllivier Robert * received after the change back to BCLIENT mode sets the clock.
38c0b746e5SOllivier Robert */
39c0b746e5SOllivier Robert #define AM_MODES 7 /* number of rows and columns */
40c0b746e5SOllivier Robert #define NO_PEER 0 /* action when no peer is found */
41c0b746e5SOllivier Robert
42c0b746e5SOllivier Robert int AM[AM_MODES][AM_MODES] = {
43c0b746e5SOllivier Robert /* packet->mode */
44c0b746e5SOllivier Robert /* peer { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */
45c0b746e5SOllivier Robert /* mode */
46c0b746e5SOllivier Robert /*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL},
47c0b746e5SOllivier Robert
48c0b746e5SOllivier Robert /*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
49c0b746e5SOllivier Robert
50c0b746e5SOllivier Robert /*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
51c0b746e5SOllivier Robert
52c0b746e5SOllivier Robert /*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_NOMATCH},
53c0b746e5SOllivier Robert
54c0b746e5SOllivier Robert /*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
55c0b746e5SOllivier Robert
56c0b746e5SOllivier Robert /*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
57c0b746e5SOllivier Robert
58c0b746e5SOllivier Robert /*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT},
59c0b746e5SOllivier Robert };
60c0b746e5SOllivier Robert
61c0b746e5SOllivier Robert #define MATCH_ASSOC(x, y) AM[(x)][(y)]
62c0b746e5SOllivier Robert
63c0b746e5SOllivier Robert /*
64c0b746e5SOllivier Robert * These routines manage the allocation of memory to peer structures
65c0b746e5SOllivier Robert * and the maintenance of three data structures involving all peers:
66c0b746e5SOllivier Robert *
67c0b746e5SOllivier Robert * - peer_list is a single list with all peers, suitable for scanning
68c0b746e5SOllivier Robert * operations over all peers.
69224ba2bdSOllivier Robert * - peer_adr_hash is an array of lists indexed by hashed peer address.
70224ba2bdSOllivier Robert * - peer_aid_hash is an array of lists indexed by hashed associd.
71224ba2bdSOllivier Robert *
72c0b746e5SOllivier Robert * They also maintain a free list of peer structures, peer_free.
73c0b746e5SOllivier Robert *
74c0b746e5SOllivier Robert * The three main entry points are findpeer(), which looks for matching
75224ba2bdSOllivier Robert * peer structures in the peer list, newpeer(), which allocates a new
76c0b746e5SOllivier Robert * peer structure and adds it to the list, and unpeer(), which
77224ba2bdSOllivier Robert * demobilizes the association and deallocates the structure.
78224ba2bdSOllivier Robert */
79224ba2bdSOllivier Robert /*
80224ba2bdSOllivier Robert * Peer hash tables
81224ba2bdSOllivier Robert */
82224ba2bdSOllivier Robert struct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */
83c0b746e5SOllivier Robert int peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */
84c0b746e5SOllivier Robert struct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */
85224ba2bdSOllivier Robert int assoc_hash_count[NTP_HASH_SIZE];/* peers in each bucket */
86c0b746e5SOllivier Robert struct peer *peer_list; /* peer structures list */
87c0b746e5SOllivier Robert static struct peer *peer_free; /* peer structures free list */
88224ba2bdSOllivier Robert int peer_free_count; /* count of free structures */
89c0b746e5SOllivier Robert
90c0b746e5SOllivier Robert /*
91c0b746e5SOllivier Robert * Association ID. We initialize this value randomly, then assign a new
92c0b746e5SOllivier Robert * value every time an association is mobilized.
93224ba2bdSOllivier Robert */
94224ba2bdSOllivier Robert static associd_t current_association_ID; /* association ID */
95c0b746e5SOllivier Robert static associd_t initial_association_ID; /* association ID */
96c0b746e5SOllivier Robert
97c0b746e5SOllivier Robert /*
98c0b746e5SOllivier Robert * Memory allocation watermarks.
99224ba2bdSOllivier Robert */
100224ba2bdSOllivier Robert #define INIT_PEER_ALLOC 8 /* static preallocation */
101224ba2bdSOllivier Robert #define INC_PEER_ALLOC 4 /* add N more when empty */
102224ba2bdSOllivier Robert
103224ba2bdSOllivier Robert /*
104224ba2bdSOllivier Robert * Miscellaneous statistic counters which may be queried.
105224ba2bdSOllivier Robert */
106224ba2bdSOllivier Robert u_long peer_timereset; /* time stat counters zeroed */
107c0b746e5SOllivier Robert u_long findpeer_calls; /* calls to findpeer */
108c0b746e5SOllivier Robert u_long assocpeer_calls; /* calls to findpeerbyassoc */
109c0b746e5SOllivier Robert u_long peer_allocations; /* allocations from free list */
110c0b746e5SOllivier Robert u_long peer_demobilizations; /* structs freed to free list */
111c0b746e5SOllivier Robert int total_peer_structs; /* peer structs */
112c0b746e5SOllivier Robert int peer_associations; /* mobilized associations */
113c0b746e5SOllivier Robert int peer_preempt; /* preemptable associations */
114c0b746e5SOllivier Robert static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */
115c0b746e5SOllivier Robert
116c0b746e5SOllivier Robert static struct peer * findexistingpeer_name(const char *, u_short,
117c0b746e5SOllivier Robert struct peer *, int);
118c0b746e5SOllivier Robert static struct peer * findexistingpeer_addr(sockaddr_u *,
119c0b746e5SOllivier Robert struct peer *, int,
120c0b746e5SOllivier Robert u_char, int *);
121c0b746e5SOllivier Robert static void free_peer(struct peer *, int);
122c0b746e5SOllivier Robert static void getmorepeermem(void);
123c0b746e5SOllivier Robert static int score(struct peer *);
124c0b746e5SOllivier Robert
125c0b746e5SOllivier Robert
126c0b746e5SOllivier Robert /*
127c0b746e5SOllivier Robert * init_peer - initialize peer data structures and counters
128c0b746e5SOllivier Robert *
129c0b746e5SOllivier Robert * N.B. We use the random number routine in here. It had better be
130c0b746e5SOllivier Robert * initialized prior to getting here.
131c0b746e5SOllivier Robert */
132c0b746e5SOllivier Robert void
init_peer(void)133c0b746e5SOllivier Robert init_peer(void)
134c0b746e5SOllivier Robert {
135c0b746e5SOllivier Robert int i;
136c0b746e5SOllivier Robert
137c0b746e5SOllivier Robert /*
138c0b746e5SOllivier Robert * Initialize peer free list from static allocation.
139c0b746e5SOllivier Robert */
140c0b746e5SOllivier Robert for (i = COUNTOF(init_peer_alloc) - 1; i >= 0; i--)
141c0b746e5SOllivier Robert LINK_SLIST(peer_free, &init_peer_alloc[i], p_link);
142c0b746e5SOllivier Robert total_peer_structs = COUNTOF(init_peer_alloc);
143c0b746e5SOllivier Robert peer_free_count = COUNTOF(init_peer_alloc);
144c0b746e5SOllivier Robert
145c0b746e5SOllivier Robert /*
146c0b746e5SOllivier Robert * Initialize our first association ID
147c0b746e5SOllivier Robert */
148c0b746e5SOllivier Robert do
149c0b746e5SOllivier Robert current_association_ID = ntp_random() & ASSOCID_MAX;
150c0b746e5SOllivier Robert while (!current_association_ID);
151224ba2bdSOllivier Robert initial_association_ID = current_association_ID;
152c0b746e5SOllivier Robert }
153c0b746e5SOllivier Robert
154c0b746e5SOllivier Robert
155c0b746e5SOllivier Robert /*
156c0b746e5SOllivier Robert * getmorepeermem - add more peer structures to the free list
157c0b746e5SOllivier Robert */
158c0b746e5SOllivier Robert static void
getmorepeermem(void)159c0b746e5SOllivier Robert getmorepeermem(void)
160c0b746e5SOllivier Robert {
161c0b746e5SOllivier Robert int i;
162c0b746e5SOllivier Robert struct peer *peers;
163c0b746e5SOllivier Robert
164c0b746e5SOllivier Robert peers = eallocarray(INC_PEER_ALLOC, sizeof(*peers));
165c0b746e5SOllivier Robert
166224ba2bdSOllivier Robert for (i = INC_PEER_ALLOC - 1; i >= 0; i--)
167224ba2bdSOllivier Robert LINK_SLIST(peer_free, &peers[i], p_link);
168c0b746e5SOllivier Robert
169c0b746e5SOllivier Robert total_peer_structs += INC_PEER_ALLOC;
170c0b746e5SOllivier Robert peer_free_count += INC_PEER_ALLOC;
171c0b746e5SOllivier Robert }
172c0b746e5SOllivier Robert
173c0b746e5SOllivier Robert
174c0b746e5SOllivier Robert static struct peer *
findexistingpeer_name(const char * hostname,u_short hname_fam,struct peer * start_peer,int mode)175c0b746e5SOllivier Robert findexistingpeer_name(
176c0b746e5SOllivier Robert const char * hostname,
177c0b746e5SOllivier Robert u_short hname_fam,
178c0b746e5SOllivier Robert struct peer * start_peer,
179c0b746e5SOllivier Robert int mode
180c0b746e5SOllivier Robert )
181c0b746e5SOllivier Robert {
182c0b746e5SOllivier Robert struct peer *p;
183c0b746e5SOllivier Robert
1849c2daa00SOllivier Robert if (NULL == start_peer)
185c0b746e5SOllivier Robert p = peer_list;
186c0b746e5SOllivier Robert else
187c0b746e5SOllivier Robert p = start_peer->p_link;
188c0b746e5SOllivier Robert for (; p != NULL; p = p->p_link)
189c0b746e5SOllivier Robert if (p->hostname != NULL
190c0b746e5SOllivier Robert && (-1 == mode || p->hmode == mode)
191c0b746e5SOllivier Robert && (AF_UNSPEC == hname_fam
192c0b746e5SOllivier Robert || AF_UNSPEC == AF(&p->srcadr)
193c0b746e5SOllivier Robert || hname_fam == AF(&p->srcadr))
194c0b746e5SOllivier Robert && !strcasecmp(p->hostname, hostname))
195c0b746e5SOllivier Robert break;
196c0b746e5SOllivier Robert return p;
197c0b746e5SOllivier Robert }
198c0b746e5SOllivier Robert
199c0b746e5SOllivier Robert
200c0b746e5SOllivier Robert static
2019c2daa00SOllivier Robert struct peer *
findexistingpeer_addr(sockaddr_u * addr,struct peer * start_peer,int mode,u_char cast_flags,int * ip_count)202c0b746e5SOllivier Robert findexistingpeer_addr(
203c0b746e5SOllivier Robert sockaddr_u * addr,
204224ba2bdSOllivier Robert struct peer * start_peer,
205c0b746e5SOllivier Robert int mode,
206c0b746e5SOllivier Robert u_char cast_flags,
207c0b746e5SOllivier Robert int * ip_count
208c0b746e5SOllivier Robert )
209c0b746e5SOllivier Robert {
210224ba2bdSOllivier Robert struct peer *peer;
211c0b746e5SOllivier Robert
212c0b746e5SOllivier Robert DPRINTF(2, ("findexistingpeer_addr(%s, %s, %d, 0x%x, %p)\n",
213c0b746e5SOllivier Robert sptoa(addr),
214c0b746e5SOllivier Robert (start_peer)
215c0b746e5SOllivier Robert ? sptoa(&start_peer->srcadr)
216c0b746e5SOllivier Robert : "NULL",
217c0b746e5SOllivier Robert mode, (u_int)cast_flags, ip_count));
218c0b746e5SOllivier Robert
2199c2daa00SOllivier Robert /*
220c0b746e5SOllivier Robert * start_peer is included so we can locate instances of the
221c0b746e5SOllivier Robert * same peer through different interfaces in the hash table.
222c0b746e5SOllivier Robert * Without MDF_BCLNT, a match requires the same mode and remote
223c0b746e5SOllivier Robert * address. MDF_BCLNT associations start out as MODE_CLIENT
224c0b746e5SOllivier Robert * if broadcastdelay is not specified, and switch to
225c0b746e5SOllivier Robert * MODE_BCLIENT after estimating the one-way delay. Duplicate
226c0b746e5SOllivier Robert * associations are expanded in definition to match any other
227c0b746e5SOllivier Robert * MDF_BCLNT with the same srcadr (remote, unicast address).
228c0b746e5SOllivier Robert */
229c0b746e5SOllivier Robert if (NULL == start_peer)
230c0b746e5SOllivier Robert peer = peer_hash[NTP_HASH_ADDR(addr)];
2319c2daa00SOllivier Robert else
2329c2daa00SOllivier Robert peer = start_peer->adr_link;
233c0b746e5SOllivier Robert
234224ba2bdSOllivier Robert while (peer != NULL) {
235c0b746e5SOllivier Robert DPRINTF(3, ("%s %s %d %d 0x%x 0x%x ", sptoa(addr),
236224ba2bdSOllivier Robert sptoa(&peer->srcadr), mode, peer->hmode,
237224ba2bdSOllivier Robert (u_int)cast_flags, (u_int)peer->cast_flags));
238224ba2bdSOllivier Robert if (ip_count) {
239c0b746e5SOllivier Robert if (SOCK_EQ(addr, &peer->srcadr)) {
240c0b746e5SOllivier Robert (*ip_count)++;
241c0b746e5SOllivier Robert }
242c0b746e5SOllivier Robert }
243c0b746e5SOllivier Robert if ((-1 == mode || peer->hmode == mode ||
244224ba2bdSOllivier Robert ((MDF_BCLNT & peer->cast_flags) &&
245c0b746e5SOllivier Robert (MDF_BCLNT & cast_flags))) &&
246224ba2bdSOllivier Robert ADDR_PORT_EQ(addr, &peer->srcadr)) {
247224ba2bdSOllivier Robert DPRINTF(3, ("found.\n"));
248c0b746e5SOllivier Robert break;
249c0b746e5SOllivier Robert }
250224ba2bdSOllivier Robert DPRINTF(3, ("\n"));
251224ba2bdSOllivier Robert peer = peer->adr_link;
252224ba2bdSOllivier Robert }
253224ba2bdSOllivier Robert
254c0b746e5SOllivier Robert return peer;
255224ba2bdSOllivier Robert }
256c0b746e5SOllivier Robert
257224ba2bdSOllivier Robert
258224ba2bdSOllivier Robert /*
259224ba2bdSOllivier Robert * findexistingpeer - search by address and return a pointer to a peer.
260c0b746e5SOllivier Robert */
261c0b746e5SOllivier Robert struct peer *
findexistingpeer(sockaddr_u * addr,const char * hostname,struct peer * start_peer,int mode,u_char cast_flags,int * ip_count)262c0b746e5SOllivier Robert findexistingpeer(
263c0b746e5SOllivier Robert sockaddr_u * addr,
264c0b746e5SOllivier Robert const char * hostname,
265224ba2bdSOllivier Robert struct peer * start_peer,
266224ba2bdSOllivier Robert int mode,
267224ba2bdSOllivier Robert u_char cast_flags,
268c0b746e5SOllivier Robert int * ip_count
269c0b746e5SOllivier Robert )
270224ba2bdSOllivier Robert {
271c0b746e5SOllivier Robert if (hostname != NULL)
272c0b746e5SOllivier Robert return findexistingpeer_name(hostname, AF(addr),
273224ba2bdSOllivier Robert start_peer, mode);
274c0b746e5SOllivier Robert else
275c0b746e5SOllivier Robert return findexistingpeer_addr(addr, start_peer, mode,
276c0b746e5SOllivier Robert cast_flags, ip_count);
277c0b746e5SOllivier Robert }
278c0b746e5SOllivier Robert
279c0b746e5SOllivier Robert
280c0b746e5SOllivier Robert /*
281224ba2bdSOllivier Robert * findpeer - find and return a peer match for a received datagram in
282c0b746e5SOllivier Robert * the peer_hash table.
283c0b746e5SOllivier Robert *
284c0b746e5SOllivier Robert * [Bug 3072] To faciliate a faster reorganisation after routing changes
285c0b746e5SOllivier Robert * the original code re-assigned the peer address to be the destination
286c0b746e5SOllivier Robert * of the received packet and initiated another round on a mismatch.
287c0b746e5SOllivier Robert * Unfortunately this leaves us wide open for a DoS attack where the
288c0b746e5SOllivier Robert * attacker directs a packet with forged destination address to us --
289c0b746e5SOllivier Robert * this results in a wrong interface assignment, actually creating a DoS
290224ba2bdSOllivier Robert * situation.
291224ba2bdSOllivier Robert *
292224ba2bdSOllivier Robert * This condition would persist until the next update of the interface
293224ba2bdSOllivier Robert * list, but a continued attack would put us out of business again soon
294c0b746e5SOllivier Robert * enough. Authentication alone does not help here, since it does not
295224ba2bdSOllivier Robert * protect the UDP layer and leaves us open for a replay attack.
296224ba2bdSOllivier Robert *
297224ba2bdSOllivier Robert * So we do not update the adresses and wait until the next interface
298c0b746e5SOllivier Robert * list update does the right thing for us.
299c0b746e5SOllivier Robert */
300224ba2bdSOllivier Robert struct peer *
findpeer(struct recvbuf * rbufp,int pkt_mode,int * action)301c0b746e5SOllivier Robert findpeer(
302c0b746e5SOllivier Robert struct recvbuf *rbufp,
303224ba2bdSOllivier Robert int pkt_mode,
304c0b746e5SOllivier Robert int * action
305c0b746e5SOllivier Robert )
306c0b746e5SOllivier Robert {
307c0b746e5SOllivier Robert struct peer * p;
308224ba2bdSOllivier Robert sockaddr_u * srcadr;
309224ba2bdSOllivier Robert u_int hash;
310224ba2bdSOllivier Robert struct pkt * pkt;
311224ba2bdSOllivier Robert l_fp pkt_org;
312c0b746e5SOllivier Robert
313c0b746e5SOllivier Robert findpeer_calls++;
314c0b746e5SOllivier Robert srcadr = &rbufp->recv_srcadr;
3159c2daa00SOllivier Robert hash = NTP_HASH_ADDR(srcadr);
3169c2daa00SOllivier Robert for (p = peer_hash[hash]; p != NULL; p = p->adr_link) {
3179c2daa00SOllivier Robert
3189c2daa00SOllivier Robert /* [Bug 3072] ensure interface of peer matches */
3199c2daa00SOllivier Robert /* [Bug 3356] ... if NOT a broadcast peer! */
3209c2daa00SOllivier Robert if (p->hmode != MODE_BCLIENT && p->dstadr != rbufp->dstadr)
3219c2daa00SOllivier Robert continue;
322c0b746e5SOllivier Robert
323c0b746e5SOllivier Robert /* ensure peer source address matches */
324c0b746e5SOllivier Robert if ( ! ADDR_PORT_EQ(srcadr, &p->srcadr))
325c0b746e5SOllivier Robert continue;
326224ba2bdSOllivier Robert
327c0b746e5SOllivier Robert /* If the association matching rules determine that this
328c0b746e5SOllivier Robert * is not a valid combination, then look for the next
329224ba2bdSOllivier Robert * valid peer association.
330224ba2bdSOllivier Robert */
331c0b746e5SOllivier Robert *action = MATCH_ASSOC(p->hmode, pkt_mode);
332c0b746e5SOllivier Robert
333c0b746e5SOllivier Robert /* A response to our manycastclient solicitation might
334c0b746e5SOllivier Robert * be misassociated with an ephemeral peer already spun
335c0b746e5SOllivier Robert * for the server. If the packet's org timestamp
336c0b746e5SOllivier Robert * doesn't match the peer's, check if it matches the
337c0b746e5SOllivier Robert * ACST prototype peer's. If so it is a redundant
338c0b746e5SOllivier Robert * solicitation response, return AM_ERR to discard it.
339c0b746e5SOllivier Robert * [Bug 1762]
3409c2daa00SOllivier Robert */
3419c2daa00SOllivier Robert if (MODE_SERVER == pkt_mode && AM_PROCPKT == *action) {
342c0b746e5SOllivier Robert pkt = &rbufp->recv_pkt;
3439c2daa00SOllivier Robert NTOHL_FP(&pkt->org, &pkt_org);
3449c2daa00SOllivier Robert if (!L_ISEQU(&p->aorg, &pkt_org) &&
3459c2daa00SOllivier Robert findmanycastpeer(rbufp))
3469c2daa00SOllivier Robert *action = AM_ERR;
3479c2daa00SOllivier Robert }
3489c2daa00SOllivier Robert
3499c2daa00SOllivier Robert /* if an error was returned, exit back right here. */
3509c2daa00SOllivier Robert if (*action == AM_ERR)
3519c2daa00SOllivier Robert return NULL;
3529c2daa00SOllivier Robert
3539c2daa00SOllivier Robert /* if a match is found, we stop our search. */
354c0b746e5SOllivier Robert if (*action != AM_NOMATCH)
355224ba2bdSOllivier Robert break;
356224ba2bdSOllivier Robert }
357224ba2bdSOllivier Robert
358c0b746e5SOllivier Robert /* If no matching association is found... */
3599c2daa00SOllivier Robert if (NULL == p)
360c0b746e5SOllivier Robert *action = MATCH_ASSOC(NO_PEER, pkt_mode);
361c0b746e5SOllivier Robert
362c0b746e5SOllivier Robert return p;
363c0b746e5SOllivier Robert }
364c0b746e5SOllivier Robert
365c0b746e5SOllivier Robert /*
366c0b746e5SOllivier Robert * findpeerbyassoc - find and return a peer using his association ID
367c0b746e5SOllivier Robert */
368c0b746e5SOllivier Robert struct peer *
findpeerbyassoc(associd_t assoc)369c0b746e5SOllivier Robert findpeerbyassoc(
370c0b746e5SOllivier Robert associd_t assoc
371c0b746e5SOllivier Robert )
372c0b746e5SOllivier Robert {
373c0b746e5SOllivier Robert struct peer *p;
374c0b746e5SOllivier Robert u_int hash;
375c0b746e5SOllivier Robert
376c0b746e5SOllivier Robert assocpeer_calls++;
377c0b746e5SOllivier Robert hash = assoc & NTP_HASH_MASK;
378c0b746e5SOllivier Robert for (p = assoc_hash[hash]; p != NULL; p = p->aid_link)
379c0b746e5SOllivier Robert if (assoc == p->associd)
380c0b746e5SOllivier Robert break;
381c0b746e5SOllivier Robert return p;
382c0b746e5SOllivier Robert }
3839c2daa00SOllivier Robert
384c0b746e5SOllivier Robert
385c0b746e5SOllivier Robert /*
386c0b746e5SOllivier Robert * clear_all - flush all time values for all associations
387c0b746e5SOllivier Robert */
388c0b746e5SOllivier Robert void
clear_all(void)389c0b746e5SOllivier Robert clear_all(void)
390c0b746e5SOllivier Robert {
391c0b746e5SOllivier Robert struct peer *p;
392c0b746e5SOllivier Robert
393c0b746e5SOllivier Robert /*
394c0b746e5SOllivier Robert * This routine is called when the clock is stepped, and so all
395c0b746e5SOllivier Robert * previously saved time values are untrusted.
396c0b746e5SOllivier Robert */
397c0b746e5SOllivier Robert for (p = peer_list; p != NULL; p = p->p_link)
398c0b746e5SOllivier Robert if (!(MDF_TXONLY_MASK & p->cast_flags))
399c0b746e5SOllivier Robert peer_clear(p, "STEP");
400c0b746e5SOllivier Robert
401c0b746e5SOllivier Robert DPRINTF(1, ("clear_all: at %lu\n", current_time));
402c0b746e5SOllivier Robert }
403c0b746e5SOllivier Robert
404c0b746e5SOllivier Robert
405c0b746e5SOllivier Robert /*
406c0b746e5SOllivier Robert * score_all() - determine if an association can be demobilized
4079c2daa00SOllivier Robert */
408c0b746e5SOllivier Robert int
score_all(struct peer * peer)409c0b746e5SOllivier Robert score_all(
410c0b746e5SOllivier Robert struct peer *peer /* peer structure pointer */
411c0b746e5SOllivier Robert )
412c0b746e5SOllivier Robert {
413c0b746e5SOllivier Robert struct peer *speer;
414c0b746e5SOllivier Robert int temp, tamp;
4159c2daa00SOllivier Robert int x;
416c0b746e5SOllivier Robert
417c0b746e5SOllivier Robert /*
418c0b746e5SOllivier Robert * This routine finds the minimum score for all preemptible
419c0b746e5SOllivier Robert * associations and returns > 0 if the association can be
420224ba2bdSOllivier Robert * demobilized.
421c0b746e5SOllivier Robert */
422c0b746e5SOllivier Robert tamp = score(peer);
423c0b746e5SOllivier Robert temp = 100;
4249c2daa00SOllivier Robert for (speer = peer_list; speer != NULL; speer = speer->p_link)
425c0b746e5SOllivier Robert if (speer->flags & FLAG_PREEMPT) {
426c0b746e5SOllivier Robert x = score(speer);
427c0b746e5SOllivier Robert if (x < temp)
428c0b746e5SOllivier Robert temp = x;
429c0b746e5SOllivier Robert }
430224ba2bdSOllivier Robert DPRINTF(1, ("score_all: at %lu score %d min %d\n",
431c0b746e5SOllivier Robert current_time, tamp, temp));
432224ba2bdSOllivier Robert
433224ba2bdSOllivier Robert if (tamp != temp)
434c0b746e5SOllivier Robert temp = 0;
435c0b746e5SOllivier Robert
436c0b746e5SOllivier Robert return temp;
4379c2daa00SOllivier Robert }
438c0b746e5SOllivier Robert
439c0b746e5SOllivier Robert
440224ba2bdSOllivier Robert /*
441224ba2bdSOllivier Robert * score() - calculate preemption score
442224ba2bdSOllivier Robert */
443224ba2bdSOllivier Robert static int
score(struct peer * peer)444c0b746e5SOllivier Robert score(
445c0b746e5SOllivier Robert struct peer *peer /* peer structure pointer */
446c0b746e5SOllivier Robert )
447c0b746e5SOllivier Robert {
448c0b746e5SOllivier Robert int temp;
449c0b746e5SOllivier Robert
450c0b746e5SOllivier Robert /*
451c0b746e5SOllivier Robert * This routine calculates the premption score from the peer
452c0b746e5SOllivier Robert * error bits and status. Increasing values are more cherished.
453c0b746e5SOllivier Robert */
454c0b746e5SOllivier Robert temp = 0;
455224ba2bdSOllivier Robert if (!(peer->flash & TEST10))
456224ba2bdSOllivier Robert temp++; /* 1 good synch and stratum */
457224ba2bdSOllivier Robert if (!(peer->flash & TEST13))
458224ba2bdSOllivier Robert temp++; /* 2 reachable */
459224ba2bdSOllivier Robert if (!(peer->flash & TEST12))
460224ba2bdSOllivier Robert temp++; /* 3 no loop */
461224ba2bdSOllivier Robert if (!(peer->flash & TEST11))
4629c2daa00SOllivier Robert temp++; /* 4 good distance */
4639c2daa00SOllivier Robert if (peer->status >= CTL_PST_SEL_SELCAND)
464224ba2bdSOllivier Robert temp++; /* 5 in the hunt */
465224ba2bdSOllivier Robert if (peer->status != CTL_PST_SEL_EXCESS)
466224ba2bdSOllivier Robert temp++; /* 6 not spare tire */
467224ba2bdSOllivier Robert return (temp); /* selection status */
4689c2daa00SOllivier Robert }
4699c2daa00SOllivier Robert
4709c2daa00SOllivier Robert
4719c2daa00SOllivier Robert /*
4729c2daa00SOllivier Robert * free_peer - internal routine to free memory referred to by a struct
4739c2daa00SOllivier Robert * peer and return it to the peer free list. If unlink is
4749c2daa00SOllivier Robert * nonzero, unlink from the various lists.
4759c2daa00SOllivier Robert */
476224ba2bdSOllivier Robert static void
free_peer(struct peer * p,int unlink_peer)477224ba2bdSOllivier Robert free_peer(
4789c2daa00SOllivier Robert struct peer * p,
4799c2daa00SOllivier Robert int unlink_peer
480224ba2bdSOllivier Robert )
481224ba2bdSOllivier Robert {
482224ba2bdSOllivier Robert struct peer * unlinked;
483224ba2bdSOllivier Robert int hash;
4849c2daa00SOllivier Robert
4859c2daa00SOllivier Robert if (unlink_peer) {
4869c2daa00SOllivier Robert hash = NTP_HASH_ADDR(&p->srcadr);
4879c2daa00SOllivier Robert peer_hash_count[hash]--;
4889c2daa00SOllivier Robert
4899c2daa00SOllivier Robert UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link,
4909c2daa00SOllivier Robert struct peer);
4919c2daa00SOllivier Robert if (NULL == unlinked) {
492224ba2bdSOllivier Robert peer_hash_count[hash]++;
493224ba2bdSOllivier Robert msyslog(LOG_ERR, "peer %s not in address table!",
494224ba2bdSOllivier Robert stoa(&p->srcadr));
495224ba2bdSOllivier Robert }
496224ba2bdSOllivier Robert
497224ba2bdSOllivier Robert /*
498224ba2bdSOllivier Robert * Remove him from the association hash as well.
499224ba2bdSOllivier Robert */
500c0b746e5SOllivier Robert hash = p->associd & NTP_HASH_MASK;
501c0b746e5SOllivier Robert assoc_hash_count[hash]--;
502c0b746e5SOllivier Robert
503c0b746e5SOllivier Robert UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link,
504c0b746e5SOllivier Robert struct peer);
505c0b746e5SOllivier Robert if (NULL == unlinked) {
506c0b746e5SOllivier Robert assoc_hash_count[hash]++;
507c0b746e5SOllivier Robert msyslog(LOG_ERR,
508224ba2bdSOllivier Robert "peer %s not in association ID table!",
5099c2daa00SOllivier Robert stoa(&p->srcadr));
510c0b746e5SOllivier Robert }
5119c2daa00SOllivier Robert
5129c2daa00SOllivier Robert /* Remove him from the overall list. */
513224ba2bdSOllivier Robert UNLINK_SLIST(unlinked, peer_list, p, p_link,
514c0b746e5SOllivier Robert struct peer);
515c0b746e5SOllivier Robert if (NULL == unlinked)
516c0b746e5SOllivier Robert msyslog(LOG_ERR, "%s not in peer list!",
517224ba2bdSOllivier Robert stoa(&p->srcadr));
518224ba2bdSOllivier Robert }
5199c2daa00SOllivier Robert
520c0b746e5SOllivier Robert if (p->hostname != NULL)
5219c2daa00SOllivier Robert free(p->hostname);
5229c2daa00SOllivier Robert
523c0b746e5SOllivier Robert if (p->ident != NULL)
524224ba2bdSOllivier Robert free(p->ident);
525224ba2bdSOllivier Robert
526c0b746e5SOllivier Robert if (p->addrs != NULL)
527c0b746e5SOllivier Robert free(p->addrs); /* from copy_addrinfo_list() */
528c0b746e5SOllivier Robert
529c0b746e5SOllivier Robert /* Add his corporeal form to peer free list */
530c0b746e5SOllivier Robert ZERO(*p);
531c0b746e5SOllivier Robert LINK_SLIST(peer_free, p, p_link);
532c0b746e5SOllivier Robert peer_free_count++;
533c0b746e5SOllivier Robert }
5349c2daa00SOllivier Robert
535c0b746e5SOllivier Robert
536c0b746e5SOllivier Robert /*
537c0b746e5SOllivier Robert * unpeer - remove peer structure from hash table and free structure
538c0b746e5SOllivier Robert */
539c0b746e5SOllivier Robert void
unpeer(struct peer * peer)540224ba2bdSOllivier Robert unpeer(
5419c2daa00SOllivier Robert struct peer *peer
542c0b746e5SOllivier Robert )
543224ba2bdSOllivier Robert {
544c0b746e5SOllivier Robert mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd);
545c0b746e5SOllivier Robert restrict_source(&peer->srcadr, 1, 0);
546c0b746e5SOllivier Robert set_peerdstadr(peer, NULL);
547c0b746e5SOllivier Robert peer_demobilizations++;
5489c2daa00SOllivier Robert peer_associations--;
5499c2daa00SOllivier Robert if (FLAG_PREEMPT & peer->flags)
5509c2daa00SOllivier Robert peer_preempt--;
551c0b746e5SOllivier Robert #ifdef REFCLOCK
552c0b746e5SOllivier Robert /*
553224ba2bdSOllivier Robert * If this peer is actually a clock, shut it down first
554224ba2bdSOllivier Robert */
555c0b746e5SOllivier Robert if (FLAG_REFCLOCK & peer->flags)
556c0b746e5SOllivier Robert refclock_unpeer(peer);
557c0b746e5SOllivier Robert #endif
558c0b746e5SOllivier Robert
559c0b746e5SOllivier Robert free_peer(peer, TRUE);
560c0b746e5SOllivier Robert }
561c0b746e5SOllivier Robert
562c0b746e5SOllivier Robert
563c0b746e5SOllivier Robert /*
564224ba2bdSOllivier Robert * peer_config - configure a new association
5659c2daa00SOllivier Robert */
5669c2daa00SOllivier Robert struct peer *
peer_config(sockaddr_u * srcadr,const char * hostname,endpt * dstadr,int ippeerlimit,u_char hmode,u_char version,u_char minpoll,u_char maxpoll,u_int flags,u_int32 ttl,keyid_t key,const char * ident)5679c2daa00SOllivier Robert peer_config(
5689c2daa00SOllivier Robert sockaddr_u * srcadr,
5699c2daa00SOllivier Robert const char * hostname,
5709c2daa00SOllivier Robert endpt * dstadr,
5719c2daa00SOllivier Robert int ippeerlimit,
572224ba2bdSOllivier Robert u_char hmode,
573224ba2bdSOllivier Robert u_char version,
574224ba2bdSOllivier Robert u_char minpoll,
575224ba2bdSOllivier Robert u_char maxpoll,
576224ba2bdSOllivier Robert u_int flags,
577224ba2bdSOllivier Robert u_int32 ttl,
578224ba2bdSOllivier Robert keyid_t key,
579224ba2bdSOllivier Robert const char * ident /* autokey group */
580224ba2bdSOllivier Robert )
5819c2daa00SOllivier Robert {
582c0b746e5SOllivier Robert u_char cast_flags;
5839c2daa00SOllivier Robert
5849c2daa00SOllivier Robert /*
5859c2daa00SOllivier Robert * We do a dirty little jig to figure the cast flags. This is
5869c2daa00SOllivier Robert * probably not the best place to do this, at least until the
5879c2daa00SOllivier Robert * configure code is rebuilt. Note only one flag can be set.
5889c2daa00SOllivier Robert */
5899c2daa00SOllivier Robert switch (hmode) {
590224ba2bdSOllivier Robert case MODE_BROADCAST:
591c0b746e5SOllivier Robert if (IS_MCAST(srcadr))
592224ba2bdSOllivier Robert cast_flags = MDF_MCAST;
593224ba2bdSOllivier Robert else
594c0b746e5SOllivier Robert cast_flags = MDF_BCAST;
595c0b746e5SOllivier Robert break;
596224ba2bdSOllivier Robert
597224ba2bdSOllivier Robert case MODE_CLIENT:
5989c2daa00SOllivier Robert if (hostname != NULL && SOCK_UNSPEC(srcadr))
5999c2daa00SOllivier Robert cast_flags = MDF_POOL;
6009c2daa00SOllivier Robert else if (IS_MCAST(srcadr))
6019c2daa00SOllivier Robert cast_flags = MDF_ACAST;
6029c2daa00SOllivier Robert else
603224ba2bdSOllivier Robert cast_flags = MDF_UCAST;
6049c2daa00SOllivier Robert break;
605224ba2bdSOllivier Robert
606c0b746e5SOllivier Robert default:
6079c2daa00SOllivier Robert cast_flags = MDF_UCAST;
6089c2daa00SOllivier Robert }
6099c2daa00SOllivier Robert
6109c2daa00SOllivier Robert /*
6119c2daa00SOllivier Robert * Mobilize the association and initialize its variables. If
6129c2daa00SOllivier Robert * emulating ntpdate, force iburst. For pool and manycastclient
6139c2daa00SOllivier Robert * strip FLAG_PREEMPT as the prototype associations are not
6149c2daa00SOllivier Robert * themselves preemptible, though the resulting associations
615224ba2bdSOllivier Robert * are.
616224ba2bdSOllivier Robert */
617c0b746e5SOllivier Robert flags |= FLAG_CONFIG;
618c0b746e5SOllivier Robert if (mode_ntpdate)
619c0b746e5SOllivier Robert flags |= FLAG_IBURST;
620c0b746e5SOllivier Robert if ((MDF_ACAST | MDF_POOL) & cast_flags)
621c0b746e5SOllivier Robert flags &= ~FLAG_PREEMPT;
622c0b746e5SOllivier Robert return newpeer(srcadr, hostname, dstadr, ippeerlimit, hmode, version,
623c0b746e5SOllivier Robert minpoll, maxpoll, flags, cast_flags, ttl, key, ident);
624c0b746e5SOllivier Robert }
625c0b746e5SOllivier Robert
626c0b746e5SOllivier Robert /*
627c0b746e5SOllivier Robert * setup peer dstadr field keeping it in sync with the interface
628c0b746e5SOllivier Robert * structures
629c0b746e5SOllivier Robert */
630c0b746e5SOllivier Robert void
set_peerdstadr(struct peer * p,endpt * dstadr)631c0b746e5SOllivier Robert set_peerdstadr(
632c0b746e5SOllivier Robert struct peer * p,
633c0b746e5SOllivier Robert endpt * dstadr
634c0b746e5SOllivier Robert )
635c0b746e5SOllivier Robert {
636c0b746e5SOllivier Robert struct peer * unlinked;
637c0b746e5SOllivier Robert
638c0b746e5SOllivier Robert DEBUG_INSIST(p != NULL);
639224ba2bdSOllivier Robert
640c0b746e5SOllivier Robert if (p == NULL)
641c0b746e5SOllivier Robert return;
642c0b746e5SOllivier Robert
643c0b746e5SOllivier Robert /* check for impossible or identical assignment */
644c0b746e5SOllivier Robert if (p->dstadr == dstadr)
645224ba2bdSOllivier Robert return;
646c0b746e5SOllivier Robert
647c0b746e5SOllivier Robert /*
648c0b746e5SOllivier Robert * Don't accept updates to a separate multicast receive-only
649c0b746e5SOllivier Robert * endpt while a BCLNT peer is running its unicast protocol.
650c0b746e5SOllivier Robert */
651c0b746e5SOllivier Robert if (dstadr != NULL && (FLAG_BC_VOL & p->flags) &&
652c0b746e5SOllivier Robert (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) {
653c0b746e5SOllivier Robert return;
654c0b746e5SOllivier Robert }
6559c2daa00SOllivier Robert
6569c2daa00SOllivier Robert /* unlink from list if we have an address prior to assignment */
6579c2daa00SOllivier Robert if (p->dstadr != NULL) {
6589c2daa00SOllivier Robert p->dstadr->peercnt--;
6599c2daa00SOllivier Robert UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink,
6609c2daa00SOllivier Robert struct peer);
6619c2daa00SOllivier Robert msyslog(LOG_INFO, "%s local addr %s -> %s",
6629c2daa00SOllivier Robert stoa(&p->srcadr), latoa(p->dstadr),
6639c2daa00SOllivier Robert latoa(dstadr));
6649c2daa00SOllivier Robert }
665c0b746e5SOllivier Robert
666224ba2bdSOllivier Robert p->dstadr = dstadr;
667224ba2bdSOllivier Robert
6689c2daa00SOllivier Robert /* link to list if we have an address after assignment */
6699c2daa00SOllivier Robert if (p->dstadr != NULL) {
6709c2daa00SOllivier Robert LINK_SLIST(dstadr->peers, p, ilink);
671224ba2bdSOllivier Robert dstadr->peercnt++;
672224ba2bdSOllivier Robert }
6739c2daa00SOllivier Robert }
674c0b746e5SOllivier Robert
675224ba2bdSOllivier Robert /*
676c0b746e5SOllivier Robert * attempt to re-rebind interface if necessary
677c0b746e5SOllivier Robert */
678c0b746e5SOllivier Robert static void
peer_refresh_interface(struct peer * p)679c0b746e5SOllivier Robert peer_refresh_interface(
680c0b746e5SOllivier Robert struct peer *p
681c0b746e5SOllivier Robert )
682c0b746e5SOllivier Robert {
683c0b746e5SOllivier Robert endpt * niface;
6849c2daa00SOllivier Robert endpt * piface;
685c0b746e5SOllivier Robert
686c0b746e5SOllivier Robert niface = select_peerinterface(p, &p->srcadr, NULL);
687c0b746e5SOllivier Robert
688c0b746e5SOllivier Robert DPRINTF(4, (
689c0b746e5SOllivier Robert "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ",
690c0b746e5SOllivier Robert p->dstadr == NULL ? "<null>" :
691c0b746e5SOllivier Robert stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode,
692c0b746e5SOllivier Robert p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags,
693c0b746e5SOllivier Robert p->ttl, p->keyid));
694c0b746e5SOllivier Robert if (niface != NULL) {
695c0b746e5SOllivier Robert DPRINTF(4, (
696c0b746e5SOllivier Robert "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s",
697c0b746e5SOllivier Robert niface->fd, niface->bfd, niface->name,
698224ba2bdSOllivier Robert niface->flags, niface->ifindex,
699c0b746e5SOllivier Robert stoa(&niface->sin)));
700c0b746e5SOllivier Robert if (niface->flags & INT_BROADCAST)
701224ba2bdSOllivier Robert DPRINTF(4, (", bcast=%s",
702224ba2bdSOllivier Robert stoa(&niface->bcast)));
703224ba2bdSOllivier Robert DPRINTF(4, (", mask=%s\n", stoa(&niface->mask)));
704224ba2bdSOllivier Robert } else {
705224ba2bdSOllivier Robert DPRINTF(4, ("<NONE>\n"));
706224ba2bdSOllivier Robert }
707c0b746e5SOllivier Robert
708c0b746e5SOllivier Robert piface = p->dstadr;
709c0b746e5SOllivier Robert set_peerdstadr(p, niface);
710c0b746e5SOllivier Robert if (p->dstadr != NULL) {
711c0b746e5SOllivier Robert /*
712c0b746e5SOllivier Robert * clear crypto if we change the local address
713c0b746e5SOllivier Robert */
714c0b746e5SOllivier Robert if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags)
715c0b746e5SOllivier Robert && MODE_BROADCAST != p->pmode)
716c0b746e5SOllivier Robert peer_clear(p, "XFAC");
717c0b746e5SOllivier Robert
718c0b746e5SOllivier Robert /*
719224ba2bdSOllivier Robert * Broadcast needs the socket enabled for broadcast
720c0b746e5SOllivier Robert */
721c0b746e5SOllivier Robert if (MDF_BCAST & p->cast_flags)
722c0b746e5SOllivier Robert enable_broadcast(p->dstadr, &p->srcadr);
723c0b746e5SOllivier Robert
724c0b746e5SOllivier Robert /*
725c0b746e5SOllivier Robert * Multicast needs the socket interface enabled for
726c0b746e5SOllivier Robert * multicast
727c0b746e5SOllivier Robert */
728c0b746e5SOllivier Robert if (MDF_MCAST & p->cast_flags)
729c0b746e5SOllivier Robert enable_multicast_if(p->dstadr, &p->srcadr);
730c0b746e5SOllivier Robert }
731c0b746e5SOllivier Robert }
732c0b746e5SOllivier Robert
733c0b746e5SOllivier Robert
734c0b746e5SOllivier Robert /*
735c0b746e5SOllivier Robert * refresh_all_peerinterfaces - see that all interface bindings are up
736c0b746e5SOllivier Robert * to date
737c0b746e5SOllivier Robert */
738c0b746e5SOllivier Robert void
refresh_all_peerinterfaces(void)739c0b746e5SOllivier Robert refresh_all_peerinterfaces(void)
740c0b746e5SOllivier Robert {
741c0b746e5SOllivier Robert struct peer *p;
742c0b746e5SOllivier Robert
743c0b746e5SOllivier Robert /*
744c0b746e5SOllivier Robert * this is called when the interface list has changed
745c0b746e5SOllivier Robert * give all peers a chance to find a better interface
746c0b746e5SOllivier Robert * but only if either they don't have an address already
747c0b746e5SOllivier Robert * or if the one they have hasn't worked for a while.
748c0b746e5SOllivier Robert */
749c0b746e5SOllivier Robert for (p = peer_list; p != NULL; p = p->p_link) {
750c0b746e5SOllivier Robert if (!(p->dstadr && (p->reach & 0x3))) // Bug 2849 XOR 2043
751c0b746e5SOllivier Robert peer_refresh_interface(p);
752c0b746e5SOllivier Robert }
7539c2daa00SOllivier Robert }
754c0b746e5SOllivier Robert
755c0b746e5SOllivier Robert
756c0b746e5SOllivier Robert /*
757c0b746e5SOllivier Robert * newpeer - initialize a new peer association
758c0b746e5SOllivier Robert */
759c0b746e5SOllivier Robert struct peer *
newpeer(sockaddr_u * srcadr,const char * hostname,endpt * dstadr,int ippeerlimit,u_char hmode,u_char version,u_char minpoll,u_char maxpoll,u_int flags,u_char cast_flags,u_int32 ttl,keyid_t key,const char * ident)760c0b746e5SOllivier Robert newpeer(
761c0b746e5SOllivier Robert sockaddr_u * srcadr,
762c0b746e5SOllivier Robert const char * hostname,
763c0b746e5SOllivier Robert endpt * dstadr,
764c0b746e5SOllivier Robert int ippeerlimit,
765c0b746e5SOllivier Robert u_char hmode,
766c0b746e5SOllivier Robert u_char version,
767c0b746e5SOllivier Robert u_char minpoll,
768c0b746e5SOllivier Robert u_char maxpoll,
769c0b746e5SOllivier Robert u_int flags,
770c0b746e5SOllivier Robert u_char cast_flags,
771224ba2bdSOllivier Robert u_int32 ttl,
772224ba2bdSOllivier Robert keyid_t key,
7739c2daa00SOllivier Robert const char * ident
774224ba2bdSOllivier Robert )
775224ba2bdSOllivier Robert {
776224ba2bdSOllivier Robert struct peer * peer;
777224ba2bdSOllivier Robert u_int hash;
778224ba2bdSOllivier Robert int ip_count = 0;
779224ba2bdSOllivier Robert
780224ba2bdSOllivier Robert
781224ba2bdSOllivier Robert DEBUG_REQUIRE(srcadr);
782224ba2bdSOllivier Robert
783224ba2bdSOllivier Robert #ifdef AUTOKEY
784224ba2bdSOllivier Robert /*
785224ba2bdSOllivier Robert * If Autokey is requested but not configured, complain loudly.
786224ba2bdSOllivier Robert */
787224ba2bdSOllivier Robert if (!crypto_flags) {
788224ba2bdSOllivier Robert if (key > NTP_MAXKEY) {
789224ba2bdSOllivier Robert return (NULL);
790224ba2bdSOllivier Robert
7919c2daa00SOllivier Robert } else if (flags & FLAG_SKEY) {
7929c2daa00SOllivier Robert msyslog(LOG_ERR, "Autokey not configured");
793224ba2bdSOllivier Robert return (NULL);
794224ba2bdSOllivier Robert }
795224ba2bdSOllivier Robert }
7969c2daa00SOllivier Robert #endif /* AUTOKEY */
7979c2daa00SOllivier Robert
7989c2daa00SOllivier Robert /*
7999c2daa00SOllivier Robert * For now only pool associations have a hostname.
8009c2daa00SOllivier Robert */
8019c2daa00SOllivier Robert INSIST(NULL == hostname || (MDF_POOL & cast_flags));
802224ba2bdSOllivier Robert
8039c2daa00SOllivier Robert /*
8049c2daa00SOllivier Robert * First search from the beginning for an association with given
805224ba2bdSOllivier Robert * remote address and mode. If an interface is given, search
806224ba2bdSOllivier Robert * from there to find the association which matches that
807224ba2bdSOllivier Robert * destination. If the given interface is "any", track down the
808224ba2bdSOllivier Robert * actual interface, because that's what gets put into the peer
8099c2daa00SOllivier Robert * structure.
8109c2daa00SOllivier Robert */
8119c2daa00SOllivier Robert if (dstadr != NULL) {
812224ba2bdSOllivier Robert peer = findexistingpeer(srcadr, hostname, NULL, hmode,
8139c2daa00SOllivier Robert cast_flags, &ip_count);
814224ba2bdSOllivier Robert while (peer != NULL) {
815224ba2bdSOllivier Robert if ( peer->dstadr == dstadr
816224ba2bdSOllivier Robert || ( (MDF_BCLNT & cast_flags)
817224ba2bdSOllivier Robert && (MDF_BCLNT & peer->cast_flags)))
818224ba2bdSOllivier Robert break;
819224ba2bdSOllivier Robert
820224ba2bdSOllivier Robert if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) &&
821224ba2bdSOllivier Robert peer->dstadr == findinterface(srcadr))
822224ba2bdSOllivier Robert break;
823224ba2bdSOllivier Robert
824224ba2bdSOllivier Robert peer = findexistingpeer(srcadr, hostname, peer,
825224ba2bdSOllivier Robert hmode, cast_flags, &ip_count);
826224ba2bdSOllivier Robert }
827224ba2bdSOllivier Robert } else {
828224ba2bdSOllivier Robert /* no endpt address given */
829224ba2bdSOllivier Robert peer = findexistingpeer(srcadr, hostname, NULL, hmode,
830224ba2bdSOllivier Robert cast_flags, &ip_count);
831224ba2bdSOllivier Robert }
832224ba2bdSOllivier Robert
833224ba2bdSOllivier Robert /*
834224ba2bdSOllivier Robert * If a peer is found, this would be a duplicate and we don't
835224ba2bdSOllivier Robert * allow that. This avoids duplicate ephemeral (broadcast/
836224ba2bdSOllivier Robert * multicast) and preemptible (manycast and pool) client
837224ba2bdSOllivier Robert * associations.
838224ba2bdSOllivier Robert */
839224ba2bdSOllivier Robert if (peer != NULL) {
840224ba2bdSOllivier Robert DPRINTF(2, ("newpeer(%s) found existing association\n",
841224ba2bdSOllivier Robert (hostname)
842224ba2bdSOllivier Robert ? hostname
843224ba2bdSOllivier Robert : stoa(srcadr)));
844224ba2bdSOllivier Robert return NULL;
845224ba2bdSOllivier Robert }
846224ba2bdSOllivier Robert
847224ba2bdSOllivier Robert #if 0
848224ba2bdSOllivier Robert DPRINTF(1, ("newpeer(%s) found no existing and %d other associations\n",
849224ba2bdSOllivier Robert (hostname)
850224ba2bdSOllivier Robert ? hostname
851224ba2bdSOllivier Robert : stoa(srcadr),
852224ba2bdSOllivier Robert ip_count));
853224ba2bdSOllivier Robert #endif
854224ba2bdSOllivier Robert
855224ba2bdSOllivier Robert /* Check ippeerlimit wrt ip_count */
856224ba2bdSOllivier Robert if (ippeerlimit > -1) {
857224ba2bdSOllivier Robert if (ip_count + 1 > ippeerlimit) {
858224ba2bdSOllivier Robert DPRINTF(2, ("newpeer(%s) denied - ippeerlimit %d\n",
859224ba2bdSOllivier Robert (hostname)
860224ba2bdSOllivier Robert ? hostname
861224ba2bdSOllivier Robert : stoa(srcadr),
862224ba2bdSOllivier Robert ippeerlimit));
863224ba2bdSOllivier Robert return NULL;
864224ba2bdSOllivier Robert }
865224ba2bdSOllivier Robert } else {
866224ba2bdSOllivier Robert DPRINTF(1, ("newpeer(%s) - ippeerlimit %d ignored\n",
867224ba2bdSOllivier Robert (hostname)
868224ba2bdSOllivier Robert ? hostname
869224ba2bdSOllivier Robert : stoa(srcadr),
870224ba2bdSOllivier Robert ippeerlimit));
871224ba2bdSOllivier Robert }
872224ba2bdSOllivier Robert
873224ba2bdSOllivier Robert /*
874224ba2bdSOllivier Robert * Allocate a new peer structure. Some dirt here, since some of
875224ba2bdSOllivier Robert * the initialization requires knowlege of our system state.
8769c2daa00SOllivier Robert */
877224ba2bdSOllivier Robert if (peer_free_count == 0)
878224ba2bdSOllivier Robert getmorepeermem();
879224ba2bdSOllivier Robert UNLINK_HEAD_SLIST(peer, peer_free, p_link);
880224ba2bdSOllivier Robert INSIST(peer != NULL);
881 peer_free_count--;
882 peer_associations++;
883 if (FLAG_PREEMPT & flags)
884 peer_preempt++;
885
886 /*
887 * Assign an association ID and increment the system variable.
888 */
889 peer->associd = current_association_ID;
890 if (++current_association_ID == 0)
891 ++current_association_ID;
892
893 peer->srcadr = *srcadr;
894 if (hostname != NULL)
895 peer->hostname = estrdup(hostname);
896 peer->hmode = hmode;
897 peer->version = version;
898 peer->flags = flags;
899 peer->cast_flags = cast_flags;
900 set_peerdstadr(peer,
901 select_peerinterface(peer, srcadr, dstadr));
902
903 /*
904 * It is an error to set minpoll less than NTP_MINPOLL or to
905 * set maxpoll greater than NTP_MAXPOLL. However, minpoll is
906 * clamped not greater than NTP_MAXPOLL and maxpoll is clamped
907 * not less than NTP_MINPOLL without complaint. Finally,
908 * minpoll is clamped not greater than maxpoll.
909 */
910 if (minpoll == 0)
911 peer->minpoll = NTP_MINDPOLL;
912 else
913 peer->minpoll = min(minpoll, NTP_MAXPOLL);
914 if (maxpoll == 0)
915 peer->maxpoll = NTP_MAXDPOLL;
916 else
917 peer->maxpoll = max(maxpoll, NTP_MINPOLL);
918 if (peer->minpoll > peer->maxpoll)
919 peer->minpoll = peer->maxpoll;
920
921 if (peer->dstadr != NULL)
922 DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n",
923 stoa(srcadr), peer->dstadr->fd,
924 stoa(&peer->dstadr->sin)));
925 else
926 DPRINTF(3, ("newpeer(%s): local interface currently not bound\n",
927 stoa(srcadr)));
928
929 /*
930 * Broadcast needs the socket enabled for broadcast
931 */
932 if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL)
933 enable_broadcast(peer->dstadr, srcadr);
934
935 /*
936 * Multicast needs the socket interface enabled for multicast
937 */
938 if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL)
939 enable_multicast_if(peer->dstadr, srcadr);
940
941 #ifdef AUTOKEY
942 if (key > NTP_MAXKEY)
943 peer->flags |= FLAG_SKEY;
944 #endif /* AUTOKEY */
945 peer->ttl = ttl;
946 peer->keyid = key;
947 if (ident != NULL)
948 peer->ident = estrdup(ident);
949 peer->precision = sys_precision;
950 peer->hpoll = peer->minpoll;
951 if (cast_flags & MDF_ACAST)
952 peer_clear(peer, "ACST");
953 else if (cast_flags & MDF_POOL)
954 peer_clear(peer, "POOL");
955 else if (cast_flags & MDF_MCAST)
956 peer_clear(peer, "MCST");
957 else if (cast_flags & MDF_BCAST)
958 peer_clear(peer, "BCST");
959 else
960 peer_clear(peer, "INIT");
961 if (mode_ntpdate)
962 peer_ntpdate++;
963
964 /*
965 * Note time on statistics timers.
966 */
967 peer->timereset = current_time;
968 peer->timereachable = current_time;
969 peer->timereceived = current_time;
970
971 if (ISREFCLOCKADR(&peer->srcadr)) {
972 #ifdef REFCLOCK
973 /*
974 * We let the reference clock support do clock
975 * dependent initialization. This includes setting
976 * the peer timer, since the clock may have requirements
977 * for this.
978 */
979 if (maxpoll == 0)
980 peer->maxpoll = peer->minpoll;
981 if (!refclock_newpeer(peer)) {
982 /*
983 * Dump it, something screwed up
984 */
985 set_peerdstadr(peer, NULL);
986 free_peer(peer, 0);
987 return NULL;
988 }
989 #else /* REFCLOCK */
990 msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.",
991 stoa(&peer->srcadr));
992 set_peerdstadr(peer, NULL);
993 free_peer(peer, 0);
994 return NULL;
995 #endif /* REFCLOCK */
996 }
997
998 /*
999 * Put the new peer in the hash tables.
1000 */
1001 hash = NTP_HASH_ADDR(&peer->srcadr);
1002 LINK_SLIST(peer_hash[hash], peer, adr_link);
1003 peer_hash_count[hash]++;
1004 hash = peer->associd & NTP_HASH_MASK;
1005 LINK_SLIST(assoc_hash[hash], peer, aid_link);
1006 assoc_hash_count[hash]++;
1007 LINK_SLIST(peer_list, peer, p_link);
1008
1009 restrict_source(&peer->srcadr, 0, 0);
1010 mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd);
1011 DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n",
1012 latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode,
1013 peer->version, peer->minpoll, peer->maxpoll, peer->flags,
1014 peer->cast_flags, peer->ttl, peer->keyid));
1015 return peer;
1016 }
1017
1018
1019 /*
1020 * peer_clr_stats - clear peer module statistics counters
1021 */
1022 void
peer_clr_stats(void)1023 peer_clr_stats(void)
1024 {
1025 findpeer_calls = 0;
1026 assocpeer_calls = 0;
1027 peer_allocations = 0;
1028 peer_demobilizations = 0;
1029 peer_timereset = current_time;
1030 }
1031
1032
1033 /*
1034 * peer_reset - reset statistics counters
1035 */
1036 void
peer_reset(struct peer * peer)1037 peer_reset(
1038 struct peer *peer
1039 )
1040 {
1041 if (peer == NULL)
1042 return;
1043
1044 peer->timereset = current_time;
1045 peer->sent = 0;
1046 peer->received = 0;
1047 peer->processed = 0;
1048 peer->badauth = 0;
1049 peer->bogusorg = 0;
1050 peer->oldpkt = 0;
1051 peer->seldisptoolarge = 0;
1052 peer->selbroken = 0;
1053 }
1054
1055
1056 /*
1057 * peer_all_reset - reset all peer statistics counters
1058 */
1059 void
peer_all_reset(void)1060 peer_all_reset(void)
1061 {
1062 struct peer *peer;
1063
1064 for (peer = peer_list; peer != NULL; peer = peer->p_link)
1065 peer_reset(peer);
1066 }
1067
1068
1069 /*
1070 * findmanycastpeer - find and return a manycastclient or pool
1071 * association matching a received response.
1072 */
1073 struct peer *
findmanycastpeer(struct recvbuf * rbufp)1074 findmanycastpeer(
1075 struct recvbuf *rbufp /* receive buffer pointer */
1076 )
1077 {
1078 struct peer *peer;
1079 struct pkt *pkt;
1080 l_fp p_org;
1081
1082 /*
1083 * This routine is called upon arrival of a server-mode response
1084 * to a manycastclient multicast solicitation, or to a pool
1085 * server unicast solicitation. Search the peer list for a
1086 * manycastclient association where the last transmit timestamp
1087 * matches the response packet's originate timestamp. There can
1088 * be multiple manycastclient associations, or multiple pool
1089 * solicitation assocations, so this assumes the transmit
1090 * timestamps are unique for such.
1091 */
1092 pkt = &rbufp->recv_pkt;
1093 for (peer = peer_list; peer != NULL; peer = peer->p_link)
1094 if (MDF_SOLICIT_MASK & peer->cast_flags) {
1095 NTOHL_FP(&pkt->org, &p_org);
1096 if (L_ISEQU(&p_org, &peer->aorg))
1097 break;
1098 }
1099
1100 return peer;
1101 }
1102
1103 /* peer_cleanup - clean peer list prior to shutdown */
peer_cleanup(void)1104 void peer_cleanup(void)
1105 {
1106 struct peer *peer;
1107 associd_t assoc;
1108
1109 for (assoc = initial_association_ID; assoc != current_association_ID; assoc++) {
1110 if (assoc != 0U) {
1111 peer = findpeerbyassoc(assoc);
1112 if (peer != NULL)
1113 unpeer(peer);
1114 }
1115 }
1116 peer = findpeerbyassoc(current_association_ID);
1117 if (peer != NULL)
1118 unpeer(peer);
1119 }
1120