/src/ntp-dev/ntpd/ntp_peer.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * ntp_peer.c - management of data maintained for peer associations |
3 | | */ |
4 | | #ifdef HAVE_CONFIG_H |
5 | | #include <config.h> |
6 | | #endif |
7 | | |
8 | | #include <stdio.h> |
9 | | #include <sys/types.h> |
10 | | |
11 | | #include "ntpd.h" |
12 | | #include "ntp_lists.h" |
13 | | #include "ntp_stdlib.h" |
14 | | #include "ntp_control.h" |
15 | | #include <ntp_random.h> |
16 | | |
17 | | /* |
18 | | * Table of valid association combinations |
19 | | * --------------------------------------- |
20 | | * |
21 | | * packet->mode |
22 | | * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST |
23 | | * ---------- | --------------------------------------------- |
24 | | * NO_PEER | e 1 0 1 1 1 |
25 | | * ACTIVE | e 1 1 0 0 0 |
26 | | * PASSIVE | e 1 e 0 0 0 |
27 | | * CLIENT | e 0 0 0 1 0 |
28 | | * SERVER | e 0 0 0 0 0 |
29 | | * BCAST | e 0 0 0 0 0 |
30 | | * BCLIENT | e 0 0 0 e 1 |
31 | | * |
32 | | * One point to note here: a packet in BCAST mode can potentially match |
33 | | * a peer in CLIENT mode, but we that is a special case and we check for |
34 | | * that early in the decision process. This avoids having to keep track |
35 | | * of what kind of associations are possible etc... We actually |
36 | | * circumvent that problem by requiring that the first b(m)roadcast |
37 | | * received after the change back to BCLIENT mode sets the clock. |
38 | | */ |
39 | | #define AM_MODES 7 /* number of rows and columns */ |
40 | | #define NO_PEER 0 /* action when no peer is found */ |
41 | | |
42 | | int AM[AM_MODES][AM_MODES] = { |
43 | | /* packet->mode */ |
44 | | /* peer { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */ |
45 | | /* mode */ |
46 | | /*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL}, |
47 | | |
48 | | /*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, |
49 | | |
50 | | /*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, |
51 | | |
52 | | /*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_NOMATCH}, |
53 | | |
54 | | /*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, |
55 | | |
56 | | /*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, |
57 | | |
58 | | /*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT}, |
59 | | }; |
60 | | |
61 | 249 | #define MATCH_ASSOC(x, y) AM[(x)][(y)] |
62 | | |
63 | | /* |
64 | | * These routines manage the allocation of memory to peer structures |
65 | | * and the maintenance of three data structures involving all peers: |
66 | | * |
67 | | * - peer_list is a single list with all peers, suitable for scanning |
68 | | * operations over all peers. |
69 | | * - peer_adr_hash is an array of lists indexed by hashed peer address. |
70 | | * - peer_aid_hash is an array of lists indexed by hashed associd. |
71 | | * |
72 | | * They also maintain a free list of peer structures, peer_free. |
73 | | * |
74 | | * The three main entry points are findpeer(), which looks for matching |
75 | | * peer structures in the peer list, newpeer(), which allocates a new |
76 | | * peer structure and adds it to the list, and unpeer(), which |
77 | | * demobilizes the association and deallocates the structure. |
78 | | */ |
79 | | /* |
80 | | * Peer hash tables |
81 | | */ |
82 | | struct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */ |
83 | | int peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */ |
84 | | struct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */ |
85 | | int assoc_hash_count[NTP_HASH_SIZE];/* peers in each bucket */ |
86 | | struct peer *peer_list; /* peer structures list */ |
87 | | static struct peer *peer_free; /* peer structures free list */ |
88 | | int peer_free_count; /* count of free structures */ |
89 | | |
90 | | /* |
91 | | * Association ID. We initialize this value randomly, then assign a new |
92 | | * value every time an association is mobilized. |
93 | | */ |
94 | | static associd_t current_association_ID; /* association ID */ |
95 | | static associd_t initial_association_ID; /* association ID */ |
96 | | |
97 | | /* |
98 | | * Memory allocation watermarks. |
99 | | */ |
100 | | #define INIT_PEER_ALLOC 8 /* static preallocation */ |
101 | 0 | #define INC_PEER_ALLOC 4 /* add N more when empty */ |
102 | | |
103 | | /* |
104 | | * Miscellaneous statistic counters which may be queried. |
105 | | */ |
106 | | u_long peer_timereset; /* time stat counters zeroed */ |
107 | | u_long findpeer_calls; /* calls to findpeer */ |
108 | | u_long assocpeer_calls; /* calls to findpeerbyassoc */ |
109 | | u_long peer_allocations; /* allocations from free list */ |
110 | | u_long peer_demobilizations; /* structs freed to free list */ |
111 | | int total_peer_structs; /* peer structs */ |
112 | | int peer_associations; /* mobilized associations */ |
113 | | int peer_preempt; /* preemptable associations */ |
114 | | static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */ |
115 | | |
116 | | static struct peer * findexistingpeer_name(const char *, u_short, |
117 | | struct peer *, int); |
118 | | static struct peer * findexistingpeer_addr(sockaddr_u *, |
119 | | struct peer *, int, |
120 | | u_char, int *); |
121 | | static void free_peer(struct peer *, int); |
122 | | static void getmorepeermem(void); |
123 | | static int score(struct peer *); |
124 | | |
125 | | |
126 | | /* |
127 | | * init_peer - initialize peer data structures and counters |
128 | | * |
129 | | * N.B. We use the random number routine in here. It had better be |
130 | | * initialized prior to getting here. |
131 | | */ |
132 | | void |
133 | | init_peer(void) |
134 | 1 | { |
135 | 1 | int i; |
136 | | |
137 | | /* |
138 | | * Initialize peer free list from static allocation. |
139 | | */ |
140 | 9 | for (i = COUNTOF(init_peer_alloc) - 1; i >= 0; i--) |
141 | 8 | LINK_SLIST(peer_free, &init_peer_alloc[i], p_link); |
142 | 1 | total_peer_structs = COUNTOF(init_peer_alloc); |
143 | 1 | peer_free_count = COUNTOF(init_peer_alloc); |
144 | | |
145 | | /* |
146 | | * Initialize our first association ID |
147 | | */ |
148 | 1 | do |
149 | 1 | current_association_ID = ntp_random() & ASSOCID_MAX; |
150 | 1 | while (!current_association_ID); |
151 | 1 | initial_association_ID = current_association_ID; |
152 | 1 | } |
153 | | |
154 | | |
155 | | /* |
156 | | * getmorepeermem - add more peer structures to the free list |
157 | | */ |
158 | | static void |
159 | | getmorepeermem(void) |
160 | 0 | { |
161 | 0 | int i; |
162 | 0 | struct peer *peers; |
163 | |
|
164 | 0 | peers = eallocarray(INC_PEER_ALLOC, sizeof(*peers)); |
165 | |
|
166 | 0 | for (i = INC_PEER_ALLOC - 1; i >= 0; i--) |
167 | 0 | LINK_SLIST(peer_free, &peers[i], p_link); |
168 | |
|
169 | 0 | total_peer_structs += INC_PEER_ALLOC; |
170 | 0 | peer_free_count += INC_PEER_ALLOC; |
171 | 0 | } |
172 | | |
173 | | |
174 | | static struct peer * |
175 | | findexistingpeer_name( |
176 | | const char * hostname, |
177 | | u_short hname_fam, |
178 | | struct peer * start_peer, |
179 | | int mode |
180 | | ) |
181 | 0 | { |
182 | 0 | struct peer *p; |
183 | |
|
184 | 0 | if (NULL == start_peer) |
185 | 0 | p = peer_list; |
186 | 0 | else |
187 | 0 | p = start_peer->p_link; |
188 | 0 | for (; p != NULL; p = p->p_link) |
189 | 0 | if (p->hostname != NULL |
190 | 0 | && (-1 == mode || p->hmode == mode) |
191 | 0 | && (AF_UNSPEC == hname_fam |
192 | 0 | || AF_UNSPEC == AF(&p->srcadr) |
193 | 0 | || hname_fam == AF(&p->srcadr)) |
194 | 0 | && !strcasecmp(p->hostname, hostname)) |
195 | 0 | break; |
196 | 0 | return p; |
197 | 0 | } |
198 | | |
199 | | |
200 | | static |
201 | | struct peer * |
202 | | findexistingpeer_addr( |
203 | | sockaddr_u * addr, |
204 | | struct peer * start_peer, |
205 | | int mode, |
206 | | u_char cast_flags, |
207 | | int * ip_count |
208 | | ) |
209 | 0 | { |
210 | 0 | struct peer *peer; |
211 | |
|
212 | 0 | DPRINTF(2, ("findexistingpeer_addr(%s, %s, %d, 0x%x, %p)\n", |
213 | 0 | sptoa(addr), |
214 | 0 | (start_peer) |
215 | 0 | ? sptoa(&start_peer->srcadr) |
216 | 0 | : "NULL", |
217 | 0 | mode, (u_int)cast_flags, ip_count)); |
218 | | |
219 | | /* |
220 | | * start_peer is included so we can locate instances of the |
221 | | * same peer through different interfaces in the hash table. |
222 | | * Without MDF_BCLNT, a match requires the same mode and remote |
223 | | * address. MDF_BCLNT associations start out as MODE_CLIENT |
224 | | * if broadcastdelay is not specified, and switch to |
225 | | * MODE_BCLIENT after estimating the one-way delay. Duplicate |
226 | | * associations are expanded in definition to match any other |
227 | | * MDF_BCLNT with the same srcadr (remote, unicast address). |
228 | | */ |
229 | 0 | if (NULL == start_peer) |
230 | 0 | peer = peer_hash[NTP_HASH_ADDR(addr)]; |
231 | 0 | else |
232 | 0 | peer = start_peer->adr_link; |
233 | | |
234 | 0 | while (peer != NULL) { |
235 | 0 | DPRINTF(3, ("%s %s %d %d 0x%x 0x%x ", sptoa(addr), |
236 | 0 | sptoa(&peer->srcadr), mode, peer->hmode, |
237 | 0 | (u_int)cast_flags, (u_int)peer->cast_flags)); |
238 | 0 | if (ip_count) { |
239 | 0 | if (SOCK_EQ(addr, &peer->srcadr)) { |
240 | 0 | (*ip_count)++; |
241 | 0 | } |
242 | 0 | } |
243 | 0 | if ((-1 == mode || peer->hmode == mode || |
244 | 0 | ((MDF_BCLNT & peer->cast_flags) && |
245 | 0 | (MDF_BCLNT & cast_flags))) && |
246 | 0 | ADDR_PORT_EQ(addr, &peer->srcadr)) { |
247 | 0 | DPRINTF(3, ("found.\n")); |
248 | 0 | break; |
249 | 0 | } |
250 | 0 | DPRINTF(3, ("\n")); |
251 | 0 | peer = peer->adr_link; |
252 | 0 | } |
253 | |
|
254 | 0 | return peer; |
255 | 0 | } |
256 | | |
257 | | |
258 | | /* |
259 | | * findexistingpeer - search by address and return a pointer to a peer. |
260 | | */ |
261 | | struct peer * |
262 | | findexistingpeer( |
263 | | sockaddr_u * addr, |
264 | | const char * hostname, |
265 | | struct peer * start_peer, |
266 | | int mode, |
267 | | u_char cast_flags, |
268 | | int * ip_count |
269 | | ) |
270 | 0 | { |
271 | 0 | if (hostname != NULL) |
272 | 0 | return findexistingpeer_name(hostname, AF(addr), |
273 | 0 | start_peer, mode); |
274 | 0 | else |
275 | 0 | return findexistingpeer_addr(addr, start_peer, mode, |
276 | 0 | cast_flags, ip_count); |
277 | 0 | } |
278 | | |
279 | | |
280 | | /* |
281 | | * findpeer - find and return a peer match for a received datagram in |
282 | | * the peer_hash table. |
283 | | * |
284 | | * [Bug 3072] To faciliate a faster reorganisation after routing changes |
285 | | * the original code re-assigned the peer address to be the destination |
286 | | * of the received packet and initiated another round on a mismatch. |
287 | | * Unfortunately this leaves us wide open for a DoS attack where the |
288 | | * attacker directs a packet with forged destination address to us -- |
289 | | * this results in a wrong interface assignment, actually creating a DoS |
290 | | * situation. |
291 | | * |
292 | | * This condition would persist until the next update of the interface |
293 | | * list, but a continued attack would put us out of business again soon |
294 | | * enough. Authentication alone does not help here, since it does not |
295 | | * protect the UDP layer and leaves us open for a replay attack. |
296 | | * |
297 | | * So we do not update the adresses and wait until the next interface |
298 | | * list update does the right thing for us. |
299 | | */ |
300 | | struct peer * |
301 | | findpeer( |
302 | | struct recvbuf *rbufp, |
303 | | int pkt_mode, |
304 | | int * action |
305 | | ) |
306 | 249 | { |
307 | 249 | struct peer * p; |
308 | 249 | sockaddr_u * srcadr; |
309 | 249 | u_int hash; |
310 | 249 | struct pkt * pkt; |
311 | 249 | l_fp pkt_org; |
312 | | |
313 | 249 | findpeer_calls++; |
314 | 249 | srcadr = &rbufp->recv_srcadr; |
315 | 249 | hash = NTP_HASH_ADDR(srcadr); |
316 | 249 | for (p = peer_hash[hash]; p != NULL; p = p->adr_link) { |
317 | | |
318 | | /* [Bug 3072] ensure interface of peer matches */ |
319 | | /* [Bug 3356] ... if NOT a broadcast peer! */ |
320 | 0 | if (p->hmode != MODE_BCLIENT && p->dstadr != rbufp->dstadr) |
321 | 0 | continue; |
322 | | |
323 | | /* ensure peer source address matches */ |
324 | 0 | if ( ! ADDR_PORT_EQ(srcadr, &p->srcadr)) |
325 | 0 | continue; |
326 | | |
327 | | /* If the association matching rules determine that this |
328 | | * is not a valid combination, then look for the next |
329 | | * valid peer association. |
330 | | */ |
331 | 0 | *action = MATCH_ASSOC(p->hmode, pkt_mode); |
332 | | |
333 | | /* A response to our manycastclient solicitation might |
334 | | * be misassociated with an ephemeral peer already spun |
335 | | * for the server. If the packet's org timestamp |
336 | | * doesn't match the peer's, check if it matches the |
337 | | * ACST prototype peer's. If so it is a redundant |
338 | | * solicitation response, return AM_ERR to discard it. |
339 | | * [Bug 1762] |
340 | | */ |
341 | 0 | if (MODE_SERVER == pkt_mode && AM_PROCPKT == *action) { |
342 | 0 | pkt = &rbufp->recv_pkt; |
343 | 0 | NTOHL_FP(&pkt->org, &pkt_org); |
344 | 0 | if (!L_ISEQU(&p->aorg, &pkt_org) && |
345 | 0 | findmanycastpeer(rbufp)) |
346 | 0 | *action = AM_ERR; |
347 | 0 | } |
348 | | |
349 | | /* if an error was returned, exit back right here. */ |
350 | 0 | if (*action == AM_ERR) |
351 | 0 | return NULL; |
352 | | |
353 | | /* if a match is found, we stop our search. */ |
354 | 0 | if (*action != AM_NOMATCH) |
355 | 0 | break; |
356 | 0 | } |
357 | | |
358 | | /* If no matching association is found... */ |
359 | 249 | if (NULL == p) |
360 | 249 | *action = MATCH_ASSOC(NO_PEER, pkt_mode); |
361 | | |
362 | 249 | return p; |
363 | 249 | } |
364 | | |
365 | | /* |
366 | | * findpeerbyassoc - find and return a peer using his association ID |
367 | | */ |
368 | | struct peer * |
369 | | findpeerbyassoc( |
370 | | associd_t assoc |
371 | | ) |
372 | 154 | { |
373 | 154 | struct peer *p; |
374 | 154 | u_int hash; |
375 | | |
376 | 154 | assocpeer_calls++; |
377 | 154 | hash = assoc & NTP_HASH_MASK; |
378 | 154 | for (p = assoc_hash[hash]; p != NULL; p = p->aid_link) |
379 | 0 | if (assoc == p->associd) |
380 | 0 | break; |
381 | 154 | return p; |
382 | 154 | } |
383 | | |
384 | | |
385 | | /* |
386 | | * clear_all - flush all time values for all associations |
387 | | */ |
388 | | void |
389 | | clear_all(void) |
390 | 0 | { |
391 | 0 | struct peer *p; |
392 | | |
393 | | /* |
394 | | * This routine is called when the clock is stepped, and so all |
395 | | * previously saved time values are untrusted. |
396 | | */ |
397 | 0 | for (p = peer_list; p != NULL; p = p->p_link) |
398 | 0 | if (!(MDF_TXONLY_MASK & p->cast_flags)) |
399 | 0 | peer_clear(p, "STEP"); |
400 | |
|
401 | 0 | DPRINTF(1, ("clear_all: at %lu\n", current_time)); |
402 | 0 | } |
403 | | |
404 | | |
405 | | /* |
406 | | * score_all() - determine if an association can be demobilized |
407 | | */ |
408 | | int |
409 | | score_all( |
410 | | struct peer *peer /* peer structure pointer */ |
411 | | ) |
412 | 0 | { |
413 | 0 | struct peer *speer; |
414 | 0 | int temp, tamp; |
415 | 0 | int x; |
416 | | |
417 | | /* |
418 | | * This routine finds the minimum score for all preemptible |
419 | | * associations and returns > 0 if the association can be |
420 | | * demobilized. |
421 | | */ |
422 | 0 | tamp = score(peer); |
423 | 0 | temp = 100; |
424 | 0 | for (speer = peer_list; speer != NULL; speer = speer->p_link) |
425 | 0 | if (speer->flags & FLAG_PREEMPT) { |
426 | 0 | x = score(speer); |
427 | 0 | if (x < temp) |
428 | 0 | temp = x; |
429 | 0 | } |
430 | 0 | DPRINTF(1, ("score_all: at %lu score %d min %d\n", |
431 | 0 | current_time, tamp, temp)); |
432 | |
|
433 | 0 | if (tamp != temp) |
434 | 0 | temp = 0; |
435 | |
|
436 | 0 | return temp; |
437 | 0 | } |
438 | | |
439 | | |
440 | | /* |
441 | | * score() - calculate preemption score |
442 | | */ |
443 | | static int |
444 | | score( |
445 | | struct peer *peer /* peer structure pointer */ |
446 | | ) |
447 | 0 | { |
448 | 0 | int temp; |
449 | | |
450 | | /* |
451 | | * This routine calculates the premption score from the peer |
452 | | * error bits and status. Increasing values are more cherished. |
453 | | */ |
454 | 0 | temp = 0; |
455 | 0 | if (!(peer->flash & TEST10)) |
456 | 0 | temp++; /* 1 good synch and stratum */ |
457 | 0 | if (!(peer->flash & TEST13)) |
458 | 0 | temp++; /* 2 reachable */ |
459 | 0 | if (!(peer->flash & TEST12)) |
460 | 0 | temp++; /* 3 no loop */ |
461 | 0 | if (!(peer->flash & TEST11)) |
462 | 0 | temp++; /* 4 good distance */ |
463 | 0 | if (peer->status >= CTL_PST_SEL_SELCAND) |
464 | 0 | temp++; /* 5 in the hunt */ |
465 | 0 | if (peer->status != CTL_PST_SEL_EXCESS) |
466 | 0 | temp++; /* 6 not spare tire */ |
467 | 0 | return (temp); /* selection status */ |
468 | 0 | } |
469 | | |
470 | | |
471 | | /* |
472 | | * free_peer - internal routine to free memory referred to by a struct |
473 | | * peer and return it to the peer free list. If unlink is |
474 | | * nonzero, unlink from the various lists. |
475 | | */ |
476 | | static void |
477 | | free_peer( |
478 | | struct peer * p, |
479 | | int unlink_peer |
480 | | ) |
481 | 0 | { |
482 | 0 | struct peer * unlinked; |
483 | 0 | int hash; |
484 | |
|
485 | 0 | if (unlink_peer) { |
486 | 0 | hash = NTP_HASH_ADDR(&p->srcadr); |
487 | 0 | peer_hash_count[hash]--; |
488 | |
|
489 | 0 | UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link, |
490 | 0 | struct peer); |
491 | 0 | if (NULL == unlinked) { |
492 | 0 | peer_hash_count[hash]++; |
493 | 0 | msyslog(LOG_ERR, "peer %s not in address table!", |
494 | 0 | stoa(&p->srcadr)); |
495 | 0 | } |
496 | | |
497 | | /* |
498 | | * Remove him from the association hash as well. |
499 | | */ |
500 | 0 | hash = p->associd & NTP_HASH_MASK; |
501 | 0 | assoc_hash_count[hash]--; |
502 | |
|
503 | 0 | UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link, |
504 | 0 | struct peer); |
505 | 0 | if (NULL == unlinked) { |
506 | 0 | assoc_hash_count[hash]++; |
507 | 0 | msyslog(LOG_ERR, |
508 | 0 | "peer %s not in association ID table!", |
509 | 0 | stoa(&p->srcadr)); |
510 | 0 | } |
511 | | |
512 | | /* Remove him from the overall list. */ |
513 | 0 | UNLINK_SLIST(unlinked, peer_list, p, p_link, |
514 | 0 | struct peer); |
515 | 0 | if (NULL == unlinked) |
516 | 0 | msyslog(LOG_ERR, "%s not in peer list!", |
517 | 0 | stoa(&p->srcadr)); |
518 | 0 | } |
519 | |
|
520 | 0 | if (p->hostname != NULL) |
521 | 0 | free(p->hostname); |
522 | |
|
523 | 0 | if (p->ident != NULL) |
524 | 0 | free(p->ident); |
525 | |
|
526 | 0 | if (p->addrs != NULL) |
527 | 0 | free(p->addrs); /* from copy_addrinfo_list() */ |
528 | | |
529 | | /* Add his corporeal form to peer free list */ |
530 | 0 | ZERO(*p); |
531 | 0 | LINK_SLIST(peer_free, p, p_link); |
532 | 0 | peer_free_count++; |
533 | 0 | } |
534 | | |
535 | | |
536 | | /* |
537 | | * unpeer - remove peer structure from hash table and free structure |
538 | | */ |
539 | | void |
540 | | unpeer( |
541 | | struct peer *peer |
542 | | ) |
543 | 0 | { |
544 | 0 | mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd); |
545 | 0 | restrict_source(&peer->srcadr, 1, 0); |
546 | 0 | set_peerdstadr(peer, NULL); |
547 | 0 | peer_demobilizations++; |
548 | 0 | peer_associations--; |
549 | 0 | if (FLAG_PREEMPT & peer->flags) |
550 | 0 | peer_preempt--; |
551 | 0 | #ifdef REFCLOCK |
552 | | /* |
553 | | * If this peer is actually a clock, shut it down first |
554 | | */ |
555 | 0 | if (FLAG_REFCLOCK & peer->flags) |
556 | 0 | refclock_unpeer(peer); |
557 | 0 | #endif |
558 | |
|
559 | 0 | free_peer(peer, TRUE); |
560 | 0 | } |
561 | | |
562 | | |
563 | | /* |
564 | | * peer_config - configure a new association |
565 | | */ |
566 | | struct peer * |
567 | | peer_config( |
568 | | sockaddr_u * srcadr, |
569 | | const char * hostname, |
570 | | endpt * dstadr, |
571 | | int ippeerlimit, |
572 | | u_char hmode, |
573 | | u_char version, |
574 | | u_char minpoll, |
575 | | u_char maxpoll, |
576 | | u_int flags, |
577 | | u_int32 ttl, |
578 | | keyid_t key, |
579 | | const char * ident /* autokey group */ |
580 | | ) |
581 | 0 | { |
582 | 0 | u_char cast_flags; |
583 | | |
584 | | /* |
585 | | * We do a dirty little jig to figure the cast flags. This is |
586 | | * probably not the best place to do this, at least until the |
587 | | * configure code is rebuilt. Note only one flag can be set. |
588 | | */ |
589 | 0 | switch (hmode) { |
590 | 0 | case MODE_BROADCAST: |
591 | 0 | if (IS_MCAST(srcadr)) |
592 | 0 | cast_flags = MDF_MCAST; |
593 | 0 | else |
594 | 0 | cast_flags = MDF_BCAST; |
595 | 0 | break; |
596 | | |
597 | 0 | case MODE_CLIENT: |
598 | 0 | if (hostname != NULL && SOCK_UNSPEC(srcadr)) |
599 | 0 | cast_flags = MDF_POOL; |
600 | 0 | else if (IS_MCAST(srcadr)) |
601 | 0 | cast_flags = MDF_ACAST; |
602 | 0 | else |
603 | 0 | cast_flags = MDF_UCAST; |
604 | 0 | break; |
605 | | |
606 | 0 | default: |
607 | 0 | cast_flags = MDF_UCAST; |
608 | 0 | } |
609 | | |
610 | | /* |
611 | | * Mobilize the association and initialize its variables. If |
612 | | * emulating ntpdate, force iburst. For pool and manycastclient |
613 | | * strip FLAG_PREEMPT as the prototype associations are not |
614 | | * themselves preemptible, though the resulting associations |
615 | | * are. |
616 | | */ |
617 | 0 | flags |= FLAG_CONFIG; |
618 | 0 | if (mode_ntpdate) |
619 | 0 | flags |= FLAG_IBURST; |
620 | 0 | if ((MDF_ACAST | MDF_POOL) & cast_flags) |
621 | 0 | flags &= ~FLAG_PREEMPT; |
622 | 0 | return newpeer(srcadr, hostname, dstadr, ippeerlimit, hmode, version, |
623 | 0 | minpoll, maxpoll, flags, cast_flags, ttl, key, ident); |
624 | 0 | } |
625 | | |
626 | | /* |
627 | | * setup peer dstadr field keeping it in sync with the interface |
628 | | * structures |
629 | | */ |
630 | | void |
631 | | set_peerdstadr( |
632 | | struct peer * p, |
633 | | endpt * dstadr |
634 | | ) |
635 | 0 | { |
636 | 0 | struct peer * unlinked; |
637 | |
|
638 | 0 | DEBUG_INSIST(p != NULL); |
639 | | |
640 | 0 | if (p == NULL) |
641 | 0 | return; |
642 | | |
643 | | /* check for impossible or identical assignment */ |
644 | 0 | if (p->dstadr == dstadr) |
645 | 0 | return; |
646 | | |
647 | | /* |
648 | | * Don't accept updates to a separate multicast receive-only |
649 | | * endpt while a BCLNT peer is running its unicast protocol. |
650 | | */ |
651 | 0 | if (dstadr != NULL && (FLAG_BC_VOL & p->flags) && |
652 | 0 | (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) { |
653 | 0 | return; |
654 | 0 | } |
655 | | |
656 | | /* unlink from list if we have an address prior to assignment */ |
657 | 0 | if (p->dstadr != NULL) { |
658 | 0 | p->dstadr->peercnt--; |
659 | 0 | UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink, |
660 | 0 | struct peer); |
661 | 0 | msyslog(LOG_INFO, "%s local addr %s -> %s", |
662 | 0 | stoa(&p->srcadr), latoa(p->dstadr), |
663 | 0 | latoa(dstadr)); |
664 | 0 | } |
665 | | |
666 | 0 | p->dstadr = dstadr; |
667 | | |
668 | | /* link to list if we have an address after assignment */ |
669 | 0 | if (p->dstadr != NULL) { |
670 | 0 | LINK_SLIST(dstadr->peers, p, ilink); |
671 | 0 | dstadr->peercnt++; |
672 | 0 | } |
673 | 0 | } |
674 | | |
675 | | /* |
676 | | * attempt to re-rebind interface if necessary |
677 | | */ |
678 | | static void |
679 | | peer_refresh_interface( |
680 | | struct peer *p |
681 | | ) |
682 | 0 | { |
683 | 0 | endpt * niface; |
684 | 0 | endpt * piface; |
685 | |
|
686 | 0 | niface = select_peerinterface(p, &p->srcadr, NULL); |
687 | |
|
688 | 0 | DPRINTF(4, ( |
689 | 0 | "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ", |
690 | 0 | p->dstadr == NULL ? "<null>" : |
691 | 0 | stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode, |
692 | 0 | p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags, |
693 | 0 | p->ttl, p->keyid)); |
694 | 0 | if (niface != NULL) { |
695 | 0 | DPRINTF(4, ( |
696 | 0 | "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s", |
697 | 0 | niface->fd, niface->bfd, niface->name, |
698 | 0 | niface->flags, niface->ifindex, |
699 | 0 | stoa(&niface->sin))); |
700 | 0 | if (niface->flags & INT_BROADCAST) |
701 | 0 | DPRINTF(4, (", bcast=%s", |
702 | 0 | stoa(&niface->bcast))); |
703 | 0 | DPRINTF(4, (", mask=%s\n", stoa(&niface->mask))); |
704 | 0 | } else { |
705 | 0 | DPRINTF(4, ("<NONE>\n")); |
706 | 0 | } |
707 | |
|
708 | 0 | piface = p->dstadr; |
709 | 0 | set_peerdstadr(p, niface); |
710 | 0 | if (p->dstadr != NULL) { |
711 | | /* |
712 | | * clear crypto if we change the local address |
713 | | */ |
714 | 0 | if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags) |
715 | 0 | && MODE_BROADCAST != p->pmode) |
716 | 0 | peer_clear(p, "XFAC"); |
717 | | |
718 | | /* |
719 | | * Broadcast needs the socket enabled for broadcast |
720 | | */ |
721 | 0 | if (MDF_BCAST & p->cast_flags) |
722 | 0 | enable_broadcast(p->dstadr, &p->srcadr); |
723 | | |
724 | | /* |
725 | | * Multicast needs the socket interface enabled for |
726 | | * multicast |
727 | | */ |
728 | 0 | if (MDF_MCAST & p->cast_flags) |
729 | 0 | enable_multicast_if(p->dstadr, &p->srcadr); |
730 | 0 | } |
731 | 0 | } |
732 | | |
733 | | |
734 | | /* |
735 | | * refresh_all_peerinterfaces - see that all interface bindings are up |
736 | | * to date |
737 | | */ |
738 | | void |
739 | | refresh_all_peerinterfaces(void) |
740 | 1 | { |
741 | 1 | struct peer *p; |
742 | | |
743 | | /* |
744 | | * this is called when the interface list has changed |
745 | | * give all peers a chance to find a better interface |
746 | | * but only if either they don't have an address already |
747 | | * or if the one they have hasn't worked for a while. |
748 | | */ |
749 | 1 | for (p = peer_list; p != NULL; p = p->p_link) { |
750 | 0 | if (!(p->dstadr && (p->reach & 0x3))) // Bug 2849 XOR 2043 |
751 | 0 | peer_refresh_interface(p); |
752 | 0 | } |
753 | 1 | } |
754 | | |
755 | | |
756 | | /* |
757 | | * newpeer - initialize a new peer association |
758 | | */ |
759 | | struct peer * |
760 | | newpeer( |
761 | | sockaddr_u * srcadr, |
762 | | const char * hostname, |
763 | | endpt * dstadr, |
764 | | int ippeerlimit, |
765 | | u_char hmode, |
766 | | u_char version, |
767 | | u_char minpoll, |
768 | | u_char maxpoll, |
769 | | u_int flags, |
770 | | u_char cast_flags, |
771 | | u_int32 ttl, |
772 | | keyid_t key, |
773 | | const char * ident |
774 | | ) |
775 | 0 | { |
776 | 0 | struct peer * peer; |
777 | 0 | u_int hash; |
778 | 0 | int ip_count = 0; |
779 | | |
780 | |
|
781 | 0 | DEBUG_REQUIRE(srcadr); |
782 | | |
783 | | #ifdef AUTOKEY |
784 | | /* |
785 | | * If Autokey is requested but not configured, complain loudly. |
786 | | */ |
787 | | if (!crypto_flags) { |
788 | | if (key > NTP_MAXKEY) { |
789 | | return (NULL); |
790 | | |
791 | | } else if (flags & FLAG_SKEY) { |
792 | | msyslog(LOG_ERR, "Autokey not configured"); |
793 | | return (NULL); |
794 | | } |
795 | | } |
796 | | #endif /* AUTOKEY */ |
797 | | |
798 | | /* |
799 | | * For now only pool associations have a hostname. |
800 | | */ |
801 | 0 | INSIST(NULL == hostname || (MDF_POOL & cast_flags)); |
802 | | |
803 | | /* |
804 | | * First search from the beginning for an association with given |
805 | | * remote address and mode. If an interface is given, search |
806 | | * from there to find the association which matches that |
807 | | * destination. If the given interface is "any", track down the |
808 | | * actual interface, because that's what gets put into the peer |
809 | | * structure. |
810 | | */ |
811 | 0 | if (dstadr != NULL) { |
812 | 0 | peer = findexistingpeer(srcadr, hostname, NULL, hmode, |
813 | 0 | cast_flags, &ip_count); |
814 | 0 | while (peer != NULL) { |
815 | 0 | if ( peer->dstadr == dstadr |
816 | 0 | || ( (MDF_BCLNT & cast_flags) |
817 | 0 | && (MDF_BCLNT & peer->cast_flags))) |
818 | 0 | break; |
819 | | |
820 | 0 | if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) && |
821 | 0 | peer->dstadr == findinterface(srcadr)) |
822 | 0 | break; |
823 | | |
824 | 0 | peer = findexistingpeer(srcadr, hostname, peer, |
825 | 0 | hmode, cast_flags, &ip_count); |
826 | 0 | } |
827 | 0 | } else { |
828 | | /* no endpt address given */ |
829 | 0 | peer = findexistingpeer(srcadr, hostname, NULL, hmode, |
830 | 0 | cast_flags, &ip_count); |
831 | 0 | } |
832 | | |
833 | | /* |
834 | | * If a peer is found, this would be a duplicate and we don't |
835 | | * allow that. This avoids duplicate ephemeral (broadcast/ |
836 | | * multicast) and preemptible (manycast and pool) client |
837 | | * associations. |
838 | | */ |
839 | 0 | if (peer != NULL) { |
840 | 0 | DPRINTF(2, ("newpeer(%s) found existing association\n", |
841 | 0 | (hostname) |
842 | 0 | ? hostname |
843 | 0 | : stoa(srcadr))); |
844 | 0 | return NULL; |
845 | 0 | } |
846 | | |
847 | 0 | DPRINTF(1, ("newpeer(%s) found no existing and %d other associations\n", |
848 | 0 | (hostname) |
849 | 0 | ? hostname |
850 | 0 | : stoa(srcadr), |
851 | 0 | ip_count)); |
852 | | |
853 | | /* Check ippeerlimit wrt ip_count */ |
854 | 0 | if (ippeerlimit > -1) { |
855 | 0 | if (ip_count + 1 > ippeerlimit) { |
856 | 0 | DPRINTF(2, ("newpeer(%s) denied - ippeerlimit %d\n", |
857 | 0 | (hostname) |
858 | 0 | ? hostname |
859 | 0 | : stoa(srcadr), |
860 | 0 | ippeerlimit)); |
861 | 0 | return NULL; |
862 | 0 | } |
863 | 0 | } else { |
864 | 0 | DPRINTF(1, ("newpeer(%s) - ippeerlimit %d ignored\n", |
865 | 0 | (hostname) |
866 | 0 | ? hostname |
867 | 0 | : stoa(srcadr), |
868 | 0 | ippeerlimit)); |
869 | 0 | } |
870 | | |
871 | | /* |
872 | | * Allocate a new peer structure. Some dirt here, since some of |
873 | | * the initialization requires knowlege of our system state. |
874 | | */ |
875 | 0 | if (peer_free_count == 0) |
876 | 0 | getmorepeermem(); |
877 | 0 | UNLINK_HEAD_SLIST(peer, peer_free, p_link); |
878 | 0 | INSIST(peer != NULL); |
879 | 0 | peer_free_count--; |
880 | 0 | peer_associations++; |
881 | 0 | if (FLAG_PREEMPT & flags) |
882 | 0 | peer_preempt++; |
883 | | |
884 | | /* |
885 | | * Assign an association ID and increment the system variable. |
886 | | */ |
887 | 0 | peer->associd = current_association_ID; |
888 | 0 | if (++current_association_ID == 0) |
889 | 0 | ++current_association_ID; |
890 | |
|
891 | 0 | peer->srcadr = *srcadr; |
892 | 0 | if (hostname != NULL) |
893 | 0 | peer->hostname = estrdup(hostname); |
894 | 0 | peer->hmode = hmode; |
895 | 0 | peer->version = version; |
896 | 0 | peer->flags = flags; |
897 | 0 | peer->cast_flags = cast_flags; |
898 | 0 | set_peerdstadr(peer, |
899 | 0 | select_peerinterface(peer, srcadr, dstadr)); |
900 | | |
901 | | /* |
902 | | * It is an error to set minpoll less than NTP_MINPOLL or to |
903 | | * set maxpoll greater than NTP_MAXPOLL. However, minpoll is |
904 | | * clamped not greater than NTP_MAXPOLL and maxpoll is clamped |
905 | | * not less than NTP_MINPOLL without complaint. Finally, |
906 | | * minpoll is clamped not greater than maxpoll. |
907 | | */ |
908 | 0 | if (minpoll == 0) |
909 | 0 | peer->minpoll = NTP_MINDPOLL; |
910 | 0 | else |
911 | 0 | peer->minpoll = min(minpoll, NTP_MAXPOLL); |
912 | 0 | if (maxpoll == 0) |
913 | 0 | peer->maxpoll = NTP_MAXDPOLL; |
914 | 0 | else |
915 | 0 | peer->maxpoll = max(maxpoll, NTP_MINPOLL); |
916 | 0 | if (peer->minpoll > peer->maxpoll) |
917 | 0 | peer->minpoll = peer->maxpoll; |
918 | |
|
919 | 0 | if (peer->dstadr != NULL) |
920 | 0 | DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n", |
921 | 0 | stoa(srcadr), peer->dstadr->fd, |
922 | 0 | stoa(&peer->dstadr->sin))); |
923 | 0 | else |
924 | 0 | DPRINTF(3, ("newpeer(%s): local interface currently not bound\n", |
925 | 0 | stoa(srcadr))); |
926 | | |
927 | | /* |
928 | | * Broadcast needs the socket enabled for broadcast |
929 | | */ |
930 | 0 | if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL) |
931 | 0 | enable_broadcast(peer->dstadr, srcadr); |
932 | | |
933 | | /* |
934 | | * Multicast needs the socket interface enabled for multicast |
935 | | */ |
936 | 0 | if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL) |
937 | 0 | enable_multicast_if(peer->dstadr, srcadr); |
938 | |
|
939 | | #ifdef AUTOKEY |
940 | | if (key > NTP_MAXKEY) |
941 | | peer->flags |= FLAG_SKEY; |
942 | | #endif /* AUTOKEY */ |
943 | 0 | peer->ttl = ttl; |
944 | 0 | peer->keyid = key; |
945 | 0 | if (ident != NULL) |
946 | 0 | peer->ident = estrdup(ident); |
947 | 0 | peer->precision = sys_precision; |
948 | 0 | peer->hpoll = peer->minpoll; |
949 | 0 | if (cast_flags & MDF_ACAST) |
950 | 0 | peer_clear(peer, "ACST"); |
951 | 0 | else if (cast_flags & MDF_POOL) |
952 | 0 | peer_clear(peer, "POOL"); |
953 | 0 | else if (cast_flags & MDF_MCAST) |
954 | 0 | peer_clear(peer, "MCST"); |
955 | 0 | else if (cast_flags & MDF_BCAST) |
956 | 0 | peer_clear(peer, "BCST"); |
957 | 0 | else |
958 | 0 | peer_clear(peer, "INIT"); |
959 | 0 | if (mode_ntpdate) |
960 | 0 | peer_ntpdate++; |
961 | | |
962 | | /* |
963 | | * Note time on statistics timers. |
964 | | */ |
965 | 0 | peer->timereset = current_time; |
966 | 0 | peer->timereachable = current_time; |
967 | 0 | peer->timereceived = current_time; |
968 | |
|
969 | 0 | if (ISREFCLOCKADR(&peer->srcadr)) { |
970 | 0 | #ifdef REFCLOCK |
971 | | /* |
972 | | * We let the reference clock support do clock |
973 | | * dependent initialization. This includes setting |
974 | | * the peer timer, since the clock may have requirements |
975 | | * for this. |
976 | | */ |
977 | 0 | if (maxpoll == 0) |
978 | 0 | peer->maxpoll = peer->minpoll; |
979 | 0 | if (!refclock_newpeer(peer)) { |
980 | | /* |
981 | | * Dump it, something screwed up |
982 | | */ |
983 | 0 | set_peerdstadr(peer, NULL); |
984 | 0 | free_peer(peer, 0); |
985 | 0 | return NULL; |
986 | 0 | } |
987 | | #else /* REFCLOCK */ |
988 | | msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.", |
989 | | stoa(&peer->srcadr)); |
990 | | set_peerdstadr(peer, NULL); |
991 | | free_peer(peer, 0); |
992 | | return NULL; |
993 | | #endif /* REFCLOCK */ |
994 | 0 | } |
995 | | |
996 | | /* |
997 | | * Put the new peer in the hash tables. |
998 | | */ |
999 | 0 | hash = NTP_HASH_ADDR(&peer->srcadr); |
1000 | 0 | LINK_SLIST(peer_hash[hash], peer, adr_link); |
1001 | 0 | peer_hash_count[hash]++; |
1002 | 0 | hash = peer->associd & NTP_HASH_MASK; |
1003 | 0 | LINK_SLIST(assoc_hash[hash], peer, aid_link); |
1004 | 0 | assoc_hash_count[hash]++; |
1005 | 0 | LINK_SLIST(peer_list, peer, p_link); |
1006 | |
|
1007 | 0 | restrict_source(&peer->srcadr, 0, 0); |
1008 | 0 | mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd); |
1009 | 0 | DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n", |
1010 | 0 | latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode, |
1011 | 0 | peer->version, peer->minpoll, peer->maxpoll, peer->flags, |
1012 | 0 | peer->cast_flags, peer->ttl, peer->keyid)); |
1013 | 0 | return peer; |
1014 | 0 | } |
1015 | | |
1016 | | |
1017 | | /* |
1018 | | * peer_clr_stats - clear peer module statistics counters |
1019 | | */ |
1020 | | void |
1021 | | peer_clr_stats(void) |
1022 | 0 | { |
1023 | 0 | findpeer_calls = 0; |
1024 | 0 | assocpeer_calls = 0; |
1025 | 0 | peer_allocations = 0; |
1026 | 0 | peer_demobilizations = 0; |
1027 | 0 | peer_timereset = current_time; |
1028 | 0 | } |
1029 | | |
1030 | | |
1031 | | /* |
1032 | | * peer_reset - reset statistics counters |
1033 | | */ |
1034 | | void |
1035 | | peer_reset( |
1036 | | struct peer *peer |
1037 | | ) |
1038 | 0 | { |
1039 | 0 | if (peer == NULL) |
1040 | 0 | return; |
1041 | | |
1042 | 0 | peer->timereset = current_time; |
1043 | 0 | peer->sent = 0; |
1044 | 0 | peer->received = 0; |
1045 | 0 | peer->processed = 0; |
1046 | 0 | peer->badauth = 0; |
1047 | 0 | peer->bogusorg = 0; |
1048 | 0 | peer->oldpkt = 0; |
1049 | 0 | peer->seldisptoolarge = 0; |
1050 | 0 | peer->selbroken = 0; |
1051 | 0 | } |
1052 | | |
1053 | | |
1054 | | /* |
1055 | | * peer_all_reset - reset all peer statistics counters |
1056 | | */ |
1057 | | void |
1058 | | peer_all_reset(void) |
1059 | 0 | { |
1060 | 0 | struct peer *peer; |
1061 | |
|
1062 | 0 | for (peer = peer_list; peer != NULL; peer = peer->p_link) |
1063 | 0 | peer_reset(peer); |
1064 | 0 | } |
1065 | | |
1066 | | |
1067 | | /* |
1068 | | * findmanycastpeer - find and return a manycastclient or pool |
1069 | | * association matching a received response. |
1070 | | */ |
1071 | | struct peer * |
1072 | | findmanycastpeer( |
1073 | | struct recvbuf *rbufp /* receive buffer pointer */ |
1074 | | ) |
1075 | 3 | { |
1076 | 3 | struct peer *peer; |
1077 | 3 | struct pkt *pkt; |
1078 | 3 | l_fp p_org; |
1079 | | |
1080 | | /* |
1081 | | * This routine is called upon arrival of a server-mode response |
1082 | | * to a manycastclient multicast solicitation, or to a pool |
1083 | | * server unicast solicitation. Search the peer list for a |
1084 | | * manycastclient association where the last transmit timestamp |
1085 | | * matches the response packet's originate timestamp. There can |
1086 | | * be multiple manycastclient associations, or multiple pool |
1087 | | * solicitation assocations, so this assumes the transmit |
1088 | | * timestamps are unique for such. |
1089 | | */ |
1090 | 3 | pkt = &rbufp->recv_pkt; |
1091 | 3 | for (peer = peer_list; peer != NULL; peer = peer->p_link) |
1092 | 0 | if (MDF_SOLICIT_MASK & peer->cast_flags) { |
1093 | 0 | NTOHL_FP(&pkt->org, &p_org); |
1094 | 0 | if (L_ISEQU(&p_org, &peer->aorg)) |
1095 | 0 | break; |
1096 | 0 | } |
1097 | | |
1098 | 3 | return peer; |
1099 | 3 | } |
1100 | | |
1101 | | /* peer_cleanup - clean peer list prior to shutdown */ |
1102 | | void peer_cleanup(void) |
1103 | 0 | { |
1104 | 0 | struct peer *peer; |
1105 | 0 | associd_t assoc; |
1106 | |
|
1107 | 0 | for (assoc = initial_association_ID; assoc != current_association_ID; assoc++) { |
1108 | 0 | if (assoc != 0U) { |
1109 | 0 | peer = findpeerbyassoc(assoc); |
1110 | 0 | if (peer != NULL) |
1111 | 0 | unpeer(peer); |
1112 | 0 | } |
1113 | 0 | } |
1114 | 0 | peer = findpeerbyassoc(current_association_ID); |
1115 | 0 | if (peer != NULL) |
1116 | 0 | unpeer(peer); |
1117 | 0 | } |