/src/ntp-dev/ntpd/ntp_peer.c
Line | Count | Source |
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 | | const s_char 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 | 0 | #define MATCH_ASSOC(x, y) (AM[CLAMP((x), 0, AM_MODES)][CLAMP((y), 0, AM_MODES)]) |
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; /* actually next poss. ID */ |
95 | | static associd_t initial_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 name+family or address. |
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 addresses 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 | 0 | { |
307 | 0 | struct peer * p; |
308 | 0 | sockaddr_u * srcadr; |
309 | 0 | u_int hash; |
310 | 0 | struct pkt * pkt; |
311 | 0 | l_fp pkt_org; |
312 | |
|
313 | 0 | findpeer_calls++; |
314 | 0 | srcadr = &rbufp->recv_srcadr; |
315 | 0 | hash = NTP_HASH_ADDR(srcadr); |
316 | 0 | 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 | 0 | } |
353 | | |
354 | | /* if a match is found, we stop our search. */ |
355 | 0 | if (*action != AM_NOMATCH) { |
356 | 0 | break; |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | | /* If no matching association is found... */ |
361 | 0 | if (NULL == p) { |
362 | 0 | *action = MATCH_ASSOC(NO_PEER, pkt_mode); |
363 | 0 | } |
364 | 0 | return p; |
365 | 0 | } |
366 | | |
367 | | /* |
368 | | * findpeerbyassoc - find and return a peer using his association ID |
369 | | */ |
370 | | struct peer * |
371 | | findpeerbyassoc( |
372 | | associd_t assoc |
373 | | ) |
374 | 0 | { |
375 | 0 | struct peer *p; |
376 | 0 | u_int hash; |
377 | |
|
378 | 0 | assocpeer_calls++; |
379 | 0 | hash = assoc & NTP_HASH_MASK; |
380 | 0 | for (p = assoc_hash[hash]; p != NULL; p = p->aid_link) |
381 | 0 | if (assoc == p->associd) |
382 | 0 | break; |
383 | 0 | return p; |
384 | 0 | } |
385 | | |
386 | | |
387 | | /* |
388 | | * clear_all - flush all time values for all associations |
389 | | */ |
390 | | void |
391 | | clear_all(void) |
392 | 0 | { |
393 | 0 | struct peer *p; |
394 | | |
395 | | /* |
396 | | * This routine is called when the clock is stepped, and so all |
397 | | * previously saved time values are untrusted. |
398 | | */ |
399 | 0 | for (p = peer_list; p != NULL; p = p->p_link) |
400 | 0 | if (!(MDF_TXONLY_MASK & p->cast_flags)) |
401 | 0 | peer_clear(p, "STEP"); |
402 | |
|
403 | 0 | DPRINTF(1, ("clear_all: at %lu\n", current_time)); |
404 | 0 | } |
405 | | |
406 | | |
407 | | /* |
408 | | * score_all() - determine if an association can be demobilized |
409 | | */ |
410 | | int |
411 | | score_all( |
412 | | struct peer *peer /* peer structure pointer */ |
413 | | ) |
414 | 0 | { |
415 | 0 | struct peer *speer; |
416 | 0 | int temp, tamp; |
417 | 0 | int x; |
418 | | |
419 | | /* |
420 | | * This routine finds the minimum score for all preemptible |
421 | | * associations and returns > 0 if the association can be |
422 | | * demobilized. |
423 | | */ |
424 | 0 | tamp = score(peer); |
425 | 0 | temp = 100; |
426 | 0 | for (speer = peer_list; speer != NULL; speer = speer->p_link) |
427 | 0 | if (speer->flags & FLAG_PREEMPT) { |
428 | 0 | x = score(speer); |
429 | 0 | if (x < temp) |
430 | 0 | temp = x; |
431 | 0 | } |
432 | 0 | DPRINTF(1, ("score_all: at %lu score %d min %d\n", |
433 | 0 | current_time, tamp, temp)); |
434 | |
|
435 | 0 | if (tamp != temp) |
436 | 0 | temp = 0; |
437 | |
|
438 | 0 | return temp; |
439 | 0 | } |
440 | | |
441 | | |
442 | | /* |
443 | | * score() - calculate preemption score |
444 | | */ |
445 | | static int |
446 | | score( |
447 | | struct peer *peer /* peer structure pointer */ |
448 | | ) |
449 | 0 | { |
450 | 0 | int temp; |
451 | | |
452 | | /* |
453 | | * This routine calculates the premption score from the peer |
454 | | * error bits and status. Increasing values are more cherished. |
455 | | */ |
456 | 0 | temp = 0; |
457 | 0 | if (!(peer->flash & TEST10)) |
458 | 0 | temp++; /* 1 good synch and stratum */ |
459 | 0 | if (!(peer->flash & TEST13)) |
460 | 0 | temp++; /* 2 reachable */ |
461 | 0 | if (!(peer->flash & TEST12)) |
462 | 0 | temp++; /* 3 no loop */ |
463 | 0 | if (!(peer->flash & TEST11)) |
464 | 0 | temp++; /* 4 good distance */ |
465 | 0 | if (peer->status >= CTL_PST_SEL_SELCAND) |
466 | 0 | temp++; /* 5 in the hunt */ |
467 | 0 | if (peer->status != CTL_PST_SEL_EXCESS) |
468 | 0 | temp++; /* 6 not spare tire */ |
469 | 0 | return (temp); /* selection status */ |
470 | 0 | } |
471 | | |
472 | | |
473 | | /* |
474 | | * free_peer - internal routine to free memory referred to by a struct |
475 | | * peer and return it to the peer free list. If unlink is |
476 | | * nonzero, unlink from the various lists. |
477 | | */ |
478 | | static void |
479 | | free_peer( |
480 | | struct peer * p, |
481 | | int unlink_peer |
482 | | ) |
483 | 0 | { |
484 | 0 | struct peer * unlinked; |
485 | 0 | int hash; |
486 | |
|
487 | 0 | if (unlink_peer) { |
488 | 0 | hash = NTP_HASH_ADDR(&p->srcadr); |
489 | 0 | peer_hash_count[hash]--; |
490 | |
|
491 | 0 | UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link, |
492 | 0 | struct peer); |
493 | 0 | if (NULL == unlinked) { |
494 | 0 | peer_hash_count[hash]++; |
495 | 0 | msyslog(LOG_ERR, "peer %s not in address table!", |
496 | 0 | stoa(&p->srcadr)); |
497 | 0 | } |
498 | | |
499 | | /* |
500 | | * Remove him from the association hash as well. |
501 | | */ |
502 | 0 | hash = p->associd & NTP_HASH_MASK; |
503 | 0 | assoc_hash_count[hash]--; |
504 | |
|
505 | 0 | UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link, |
506 | 0 | struct peer); |
507 | 0 | if (NULL == unlinked) { |
508 | 0 | assoc_hash_count[hash]++; |
509 | 0 | msyslog(LOG_ERR, |
510 | 0 | "peer %s not in association ID table!", |
511 | 0 | stoa(&p->srcadr)); |
512 | 0 | } |
513 | | |
514 | | /* Remove him from the overall list. */ |
515 | 0 | UNLINK_SLIST(unlinked, peer_list, p, p_link, |
516 | 0 | struct peer); |
517 | 0 | if (NULL == unlinked) |
518 | 0 | msyslog(LOG_ERR, "%s not in peer list!", |
519 | 0 | stoa(&p->srcadr)); |
520 | 0 | } |
521 | |
|
522 | 0 | if (p->hostname != NULL) |
523 | 0 | free(p->hostname); |
524 | |
|
525 | 0 | if (p->ident != NULL) |
526 | 0 | free(p->ident); |
527 | |
|
528 | 0 | if (p->addrs != NULL) |
529 | 0 | free(p->addrs); /* from copy_addrinfo_list() */ |
530 | | |
531 | | /* Add his corporeal form to peer free list */ |
532 | 0 | ZERO(*p); |
533 | 0 | LINK_SLIST(peer_free, p, p_link); |
534 | 0 | peer_free_count++; |
535 | 0 | } |
536 | | |
537 | | |
538 | | /* |
539 | | * unpeer - remove peer structure from hash table and free structure |
540 | | */ |
541 | | void |
542 | | unpeer( |
543 | | struct peer *peer |
544 | | ) |
545 | 0 | { |
546 | 0 | mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd); |
547 | 0 | restrict_source(&peer->srcadr, TRUE, 0); |
548 | 0 | peer->flags |= FLAG_DISABLED; |
549 | 0 | set_peerdstadr(peer, NULL); |
550 | 0 | peer_demobilizations++; |
551 | 0 | peer_associations--; |
552 | 0 | if (FLAG_PREEMPT & peer->flags) |
553 | 0 | peer_preempt--; |
554 | 0 | #ifdef REFCLOCK |
555 | | /* |
556 | | * If this peer is actually a clock, shut it down first |
557 | | */ |
558 | 0 | if (FLAG_REFCLOCK & peer->flags) |
559 | 0 | refclock_unpeer(peer); |
560 | 0 | #endif |
561 | |
|
562 | 0 | free_peer(peer, TRUE); |
563 | 0 | } |
564 | | |
565 | | |
566 | | /* |
567 | | * peer_config - configure a new association |
568 | | */ |
569 | | struct peer * |
570 | | peer_config( |
571 | | sockaddr_u * srcadr, |
572 | | const char * hostname, |
573 | | endpt * dstadr, |
574 | | int ippeerlimit, |
575 | | u_char hmode, |
576 | | u_char version, |
577 | | u_char minpoll, |
578 | | u_char maxpoll, |
579 | | u_int flags, |
580 | | u_int32 ttl, |
581 | | keyid_t key, |
582 | | const char * ident /* autokey group */ |
583 | | ) |
584 | 0 | { |
585 | 0 | u_char cast_flags; |
586 | | |
587 | | /* |
588 | | * We do a dirty little jig to figure the cast flags. This is |
589 | | * probably not the best place to do this, at least until the |
590 | | * configure code is rebuilt. Note only one flag can be set. |
591 | | */ |
592 | 0 | switch (hmode) { |
593 | 0 | case MODE_BROADCAST: |
594 | 0 | if (IS_MCAST(srcadr)) |
595 | 0 | cast_flags = MDF_MCAST; |
596 | 0 | else |
597 | 0 | cast_flags = MDF_BCAST; |
598 | 0 | break; |
599 | | |
600 | 0 | case MODE_CLIENT: |
601 | 0 | if (hostname != NULL && SOCK_UNSPEC(srcadr)) |
602 | 0 | cast_flags = MDF_POOL; |
603 | 0 | else if (IS_MCAST(srcadr)) |
604 | 0 | cast_flags = MDF_ACAST; |
605 | 0 | else |
606 | 0 | cast_flags = MDF_UCAST; |
607 | 0 | break; |
608 | | |
609 | 0 | default: |
610 | 0 | cast_flags = MDF_UCAST; |
611 | 0 | } |
612 | | |
613 | | /* |
614 | | * Mobilize the association and initialize its variables. If |
615 | | * emulating ntpdate, force iburst. For pool and manycastclient |
616 | | * strip FLAG_PREEMPT as the prototype associations are not |
617 | | * themselves preemptible, though the resulting associations |
618 | | * are. |
619 | | */ |
620 | 0 | flags |= FLAG_CONFIG; |
621 | 0 | if (mode_ntpdate) |
622 | 0 | flags |= FLAG_IBURST; |
623 | 0 | if ((MDF_ACAST | MDF_POOL) & cast_flags) |
624 | 0 | flags &= ~FLAG_PREEMPT; |
625 | 0 | return newpeer(srcadr, hostname, dstadr, ippeerlimit, hmode, version, |
626 | 0 | minpoll, maxpoll, flags, cast_flags, ttl, key, ident); |
627 | 0 | } |
628 | | |
629 | | /* |
630 | | * setup peer dstadr field keeping it in sync with the interface |
631 | | * structures |
632 | | */ |
633 | | void |
634 | | set_peerdstadr( |
635 | | struct peer * p, |
636 | | endpt * dstadr |
637 | | ) |
638 | 0 | { |
639 | 0 | struct peer * unlinked; |
640 | |
|
641 | 0 | DEBUG_INSIST(p != NULL); |
642 | | |
643 | 0 | if (p == NULL) |
644 | 0 | return; |
645 | | |
646 | | /* check for impossible or identical assignment */ |
647 | 0 | if (p->dstadr == dstadr) |
648 | 0 | return; |
649 | | |
650 | | /* |
651 | | * Do not change the local address of a link-local |
652 | | * peer address. |
653 | | */ |
654 | 0 | if ( p->dstadr != NULL && is_linklocal(&p->dstadr->sin) |
655 | 0 | && dstadr != NULL) { |
656 | 0 | return; |
657 | 0 | } |
658 | | |
659 | | /* |
660 | | * Do not set the local address for a link-local IPv6 peer |
661 | | * to one with a different scope ID. |
662 | | */ |
663 | 0 | if ( dstadr != NULL && IS_IPV6(&p->srcadr) |
664 | 0 | && SCOPE(&dstadr->sin) != SCOPE(&p->srcadr)) { |
665 | 0 | return; |
666 | 0 | } |
667 | | |
668 | | /* |
669 | | * Don't accept updates to a separate multicast receive-only |
670 | | * endpt while a BCLNT peer is running its unicast protocol. |
671 | | */ |
672 | 0 | if (dstadr != NULL && (FLAG_BC_VOL & p->flags) && |
673 | 0 | (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) { |
674 | 0 | return; |
675 | 0 | } |
676 | | |
677 | | /* unlink from list if we have an address prior to assignment */ |
678 | 0 | if (p->dstadr != NULL) { |
679 | 0 | p->dstadr->peercnt--; |
680 | 0 | UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink, |
681 | 0 | struct peer); |
682 | 0 | } |
683 | 0 | if ( !IS_MCAST(&p->srcadr) && !(FLAG_DISABLED & p->flags) |
684 | 0 | && !initializing) { |
685 | 0 | msyslog(LOG_INFO, "%s local addr %s -> %s", |
686 | 0 | stoa(&p->srcadr), eptoa(p->dstadr), |
687 | 0 | eptoa(dstadr)); |
688 | 0 | } |
689 | |
|
690 | 0 | p->dstadr = dstadr; |
691 | | |
692 | | /* link to list if we have an address after assignment */ |
693 | 0 | if (p->dstadr != NULL) { |
694 | 0 | LINK_SLIST(dstadr->peers, p, ilink); |
695 | 0 | dstadr->peercnt++; |
696 | 0 | } |
697 | 0 | } |
698 | | |
699 | | /* |
700 | | * attempt to re-rebind interface if necessary |
701 | | */ |
702 | | static void |
703 | | peer_refresh_interface( |
704 | | struct peer *p |
705 | | ) |
706 | 0 | { |
707 | 0 | endpt * niface; |
708 | 0 | endpt * piface; |
709 | |
|
710 | 0 | niface = select_peerinterface(p, &p->srcadr, NULL); |
711 | |
|
712 | 0 | DPRINTF(4, ( |
713 | 0 | "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ", |
714 | 0 | p->dstadr == NULL ? "<null>" : |
715 | 0 | stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode, |
716 | 0 | p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags, |
717 | 0 | p->ttl, p->keyid)); |
718 | 0 | if (niface != NULL) { |
719 | 0 | DPRINTF(4, ( |
720 | 0 | "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s", |
721 | 0 | niface->fd, niface->bfd, niface->name, |
722 | 0 | niface->flags, niface->ifindex, |
723 | 0 | stoa(&niface->sin))); |
724 | 0 | if (niface->flags & INT_BROADCAST) |
725 | 0 | DPRINTF(4, (", bcast=%s", |
726 | 0 | stoa(&niface->bcast))); |
727 | 0 | DPRINTF(4, (", mask=%s\n", stoa(&niface->mask))); |
728 | 0 | } else { |
729 | 0 | DPRINTF(4, ("<NONE>\n")); |
730 | 0 | } |
731 | |
|
732 | 0 | piface = p->dstadr; |
733 | 0 | set_peerdstadr(p, niface); |
734 | 0 | if (p->dstadr != NULL) { |
735 | | /* |
736 | | * clear crypto if we change the local address |
737 | | */ |
738 | 0 | if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags) |
739 | 0 | && MODE_BROADCAST != p->pmode) |
740 | 0 | peer_clear(p, "XFAC"); |
741 | | |
742 | | /* |
743 | | * Broadcast needs the socket enabled for broadcast |
744 | | */ |
745 | 0 | if (MDF_BCAST & p->cast_flags) |
746 | 0 | enable_broadcast(p->dstadr, &p->srcadr); |
747 | | |
748 | | /* |
749 | | * Multicast needs the socket interface enabled for |
750 | | * multicast |
751 | | */ |
752 | 0 | if (MDF_MCAST & p->cast_flags) |
753 | 0 | enable_multicast_if(p->dstadr, &p->srcadr); |
754 | 0 | } |
755 | 0 | } |
756 | | |
757 | | |
758 | | /* |
759 | | * refresh_all_peerinterfaces - see that all interface bindings are up |
760 | | * to date |
761 | | */ |
762 | | void |
763 | | refresh_all_peerinterfaces(void) |
764 | 1 | { |
765 | 1 | struct peer *p; |
766 | | |
767 | | /* |
768 | | * This is called when the interface list has changed. |
769 | | * Give peers a chance to find a better interface. |
770 | | */ |
771 | 1 | for (p = peer_list; p != NULL; p = p->p_link) { |
772 | | /* |
773 | | * Bug 2849 XOR 2043 |
774 | | * Change local address only if the peer doesn't |
775 | | * have a local address already or if the one |
776 | | * they have hasn't worked for a while. |
777 | | */ |
778 | 0 | if (p->dstadr != NULL && (p->reach & 0x3)) { |
779 | 0 | continue; |
780 | 0 | } |
781 | 0 | peer_refresh_interface(p); |
782 | 0 | } |
783 | 1 | } |
784 | | |
785 | | |
786 | | /* |
787 | | * newpeer - initialize a new peer association |
788 | | */ |
789 | | struct peer * |
790 | | newpeer( |
791 | | sockaddr_u * srcadr, |
792 | | const char * hostname, |
793 | | endpt * dstadr, |
794 | | int ippeerlimit, |
795 | | u_char hmode, |
796 | | u_char version, |
797 | | u_char minpoll, |
798 | | u_char maxpoll, |
799 | | u_int flags, |
800 | | u_char cast_flags, |
801 | | u_int32 ttl, |
802 | | keyid_t key, |
803 | | const char * ident |
804 | | ) |
805 | 0 | { |
806 | 0 | struct peer * peer; |
807 | 0 | u_int hash; |
808 | 0 | int ip_count = 0; |
809 | |
|
810 | 0 | DEBUG_REQUIRE(srcadr); |
811 | | |
812 | 0 | #ifdef AUTOKEY |
813 | | /* |
814 | | * If Autokey is requested but not configured, complain loudly. |
815 | | */ |
816 | 0 | if (!crypto_flags) { |
817 | 0 | if (key > NTP_MAXKEY) { |
818 | 0 | return (NULL); |
819 | |
|
820 | 0 | } else if (flags & FLAG_SKEY) { |
821 | 0 | msyslog(LOG_ERR, "Rejecting Autokey with %s," |
822 | 0 | " built without support.", |
823 | 0 | stoa(srcadr)); |
824 | 0 | return (NULL); |
825 | 0 | } |
826 | 0 | } |
827 | 0 | #endif /* AUTOKEY */ |
828 | | |
829 | | /* |
830 | | * For now only pool associations have a hostname. |
831 | | */ |
832 | 0 | INSIST(NULL == hostname || (MDF_POOL & cast_flags)); |
833 | | |
834 | | /* |
835 | | * First search from the beginning for an association with given |
836 | | * remote address and mode. If an interface is given, search |
837 | | * from there to find the association which matches that |
838 | | * destination. If the given interface is "any", track down the |
839 | | * actual interface, because that's what gets put into the peer |
840 | | * structure. |
841 | | */ |
842 | 0 | if (dstadr != NULL) { |
843 | 0 | peer = findexistingpeer(srcadr, hostname, NULL, hmode, |
844 | 0 | cast_flags, &ip_count); |
845 | 0 | while (peer != NULL) { |
846 | 0 | if ( peer->dstadr == dstadr |
847 | 0 | || ( (MDF_BCLNT & cast_flags) |
848 | 0 | && (MDF_BCLNT & peer->cast_flags))) |
849 | 0 | break; |
850 | | |
851 | 0 | if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) && |
852 | 0 | peer->dstadr == findinterface(srcadr)) |
853 | 0 | break; |
854 | | |
855 | 0 | peer = findexistingpeer(srcadr, hostname, peer, |
856 | 0 | hmode, cast_flags, &ip_count); |
857 | 0 | } |
858 | 0 | } else { |
859 | | /* no endpt address given */ |
860 | 0 | peer = findexistingpeer(srcadr, hostname, NULL, hmode, |
861 | 0 | cast_flags, &ip_count); |
862 | 0 | } |
863 | | |
864 | | /* |
865 | | * In any case, do not create an association with a duplicate |
866 | | * remote address (srcadr) except for undefined (zero) address. |
867 | | * Arguably this should be part of the logic above but |
868 | | * [Bug 3888] exposed a situation with manycastclient where |
869 | | * duplicate associations happened. |
870 | | */ |
871 | 0 | if (NULL == peer) { |
872 | 0 | for (peer = peer_list; |
873 | 0 | peer != NULL; |
874 | 0 | peer = peer->p_link) { |
875 | 0 | if ( SOCK_EQ(srcadr, &peer->srcadr) |
876 | 0 | && !SOCK_UNSPEC(srcadr) |
877 | 0 | && !SOCK_UNSPEC(&peer->srcadr)) { |
878 | | /* leave peer non-NULL */ |
879 | 0 | break; |
880 | 0 | } |
881 | 0 | } |
882 | 0 | } |
883 | | |
884 | | /* |
885 | | * If a peer is found, this would be a duplicate and we don't |
886 | | * allow that. This avoids duplicate ephemeral (broadcast/ |
887 | | * multicast) and preemptible (manycast and pool) client |
888 | | * associations. |
889 | | */ |
890 | 0 | if (peer != NULL) { |
891 | 0 | DPRINTF(2, ("%s(%s) found existing association\n", |
892 | 0 | __func__, |
893 | 0 | (hostname) |
894 | 0 | ? hostname |
895 | 0 | : stoa(srcadr))); |
896 | 0 | return NULL; |
897 | 0 | } |
898 | | |
899 | | #if 0 |
900 | | DPRINTF(1, ("newpeer(%s) found no existing and %d other associations\n", |
901 | | (hostname) |
902 | | ? hostname |
903 | | : stoa(srcadr), |
904 | | ip_count)); |
905 | | #endif |
906 | | |
907 | | /* Check ippeerlimit wrt ip_count */ |
908 | 0 | if (ippeerlimit > -1) { |
909 | 0 | if (ip_count + 1 > ippeerlimit) { |
910 | 0 | DPRINTF(2, ("newpeer(%s) denied - ippeerlimit %d\n", |
911 | 0 | (hostname) |
912 | 0 | ? hostname |
913 | 0 | : stoa(srcadr), |
914 | 0 | ippeerlimit)); |
915 | 0 | return NULL; |
916 | 0 | } |
917 | 0 | } else { |
918 | 0 | DPRINTF(1, ("newpeer(%s) - ippeerlimit %d ignored\n", |
919 | 0 | (hostname) |
920 | 0 | ? hostname |
921 | 0 | : stoa(srcadr), |
922 | 0 | ippeerlimit)); |
923 | 0 | } |
924 | | |
925 | | /* |
926 | | * Allocate a new peer structure. Some dirt here, since some of |
927 | | * the initialization requires knowlege of our system state. |
928 | | */ |
929 | 0 | if (peer_free_count == 0) |
930 | 0 | getmorepeermem(); |
931 | 0 | UNLINK_HEAD_SLIST(peer, peer_free, p_link); |
932 | 0 | INSIST(peer != NULL); |
933 | 0 | peer_free_count--; |
934 | 0 | peer_associations++; |
935 | 0 | if (FLAG_PREEMPT & flags) |
936 | 0 | peer_preempt++; |
937 | | |
938 | | /* |
939 | | * Assign an available association ID. Zero is reserved. |
940 | | */ |
941 | 0 | do { |
942 | 0 | while (0 == ++current_association_ID) { |
943 | | /* EMPTY */ |
944 | 0 | } |
945 | 0 | } while (NULL != findpeerbyassoc(current_association_ID)); |
946 | 0 | peer->associd = current_association_ID; |
947 | |
|
948 | 0 | peer->srcadr = *srcadr; |
949 | 0 | if (hostname != NULL) { |
950 | 0 | peer->hostname = estrdup(hostname); |
951 | 0 | } |
952 | 0 | peer->hmode = hmode; |
953 | 0 | peer->version = version; |
954 | 0 | peer->flags = flags; |
955 | 0 | peer->cast_flags = cast_flags; |
956 | 0 | set_peerdstadr(peer, |
957 | 0 | select_peerinterface(peer, srcadr, dstadr)); |
958 | | |
959 | | /* |
960 | | * Zero for minpoll or maxpoll means use defaults. |
961 | | */ |
962 | 0 | peer->maxpoll = (0 == maxpoll) |
963 | 0 | ? NTP_MAXDPOLL |
964 | 0 | : maxpoll; |
965 | 0 | peer->minpoll = (0 == minpoll) |
966 | 0 | ? NTP_MINDPOLL |
967 | 0 | : minpoll; |
968 | | |
969 | | /* |
970 | | * Clamp maxpoll and minpoll within NTP_MINPOLL and NTP_MAXPOLL, |
971 | | * and further clamp minpoll less than or equal maxpoll. |
972 | | */ |
973 | 0 | peer->maxpoll = CLAMP(peer->maxpoll, NTP_MINPOLL, NTP_MAXPOLL); |
974 | 0 | peer->minpoll = CLAMP(peer->minpoll, NTP_MINPOLL, peer->maxpoll); |
975 | |
|
976 | 0 | if (peer->dstadr != NULL) { |
977 | 0 | DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n", |
978 | 0 | stoa(srcadr), peer->dstadr->fd, |
979 | 0 | stoa(&peer->dstadr->sin))); |
980 | 0 | } else { |
981 | 0 | DPRINTF(3, ("newpeer(%s): local addr unavailable\n", |
982 | 0 | stoa(srcadr))); |
983 | 0 | } |
984 | | /* |
985 | | * Broadcast needs the socket enabled for broadcast |
986 | | */ |
987 | 0 | if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL) { |
988 | 0 | enable_broadcast(peer->dstadr, srcadr); |
989 | 0 | } |
990 | | /* |
991 | | * Multicast needs the socket interface enabled for multicast |
992 | | */ |
993 | 0 | if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL) { |
994 | 0 | enable_multicast_if(peer->dstadr, srcadr); |
995 | 0 | } |
996 | 0 | #ifdef AUTOKEY |
997 | 0 | if (key > NTP_MAXKEY) |
998 | 0 | peer->flags |= FLAG_SKEY; |
999 | 0 | #endif /* AUTOKEY */ |
1000 | 0 | peer->ttl = ttl; |
1001 | 0 | peer->keyid = key; |
1002 | 0 | if (ident != NULL) { |
1003 | 0 | peer->ident = estrdup(ident); |
1004 | 0 | } |
1005 | 0 | peer->precision = sys_precision; |
1006 | 0 | peer->hpoll = peer->minpoll; |
1007 | 0 | if (cast_flags & MDF_ACAST) { |
1008 | 0 | peer_clear(peer, "ACST"); |
1009 | 0 | } else if (cast_flags & MDF_POOL) { |
1010 | 0 | peer_clear(peer, "POOL"); |
1011 | 0 | } else if (cast_flags & MDF_MCAST) { |
1012 | 0 | peer_clear(peer, "MCST"); |
1013 | 0 | } else if (cast_flags & MDF_BCAST) { |
1014 | 0 | peer_clear(peer, "BCST"); |
1015 | 0 | } else { |
1016 | 0 | peer_clear(peer, "INIT"); |
1017 | 0 | } |
1018 | 0 | if (mode_ntpdate) { |
1019 | 0 | peer_ntpdate++; |
1020 | 0 | } |
1021 | | /* |
1022 | | * Note time on statistics timers. |
1023 | | */ |
1024 | 0 | peer->timereset = current_time; |
1025 | 0 | peer->timereachable = current_time; |
1026 | 0 | peer->timereceived = current_time; |
1027 | |
|
1028 | 0 | if (ISREFCLOCKADR(&peer->srcadr)) { |
1029 | 0 | #ifdef REFCLOCK |
1030 | | /* |
1031 | | * We let the reference clock support do clock |
1032 | | * dependent initialization. This includes setting |
1033 | | * the peer timer, since the clock may have requirements |
1034 | | * for this. |
1035 | | */ |
1036 | 0 | if (!refclock_newpeer(peer)) { |
1037 | | /* |
1038 | | * Dump it, something screwed up |
1039 | | */ |
1040 | 0 | set_peerdstadr(peer, NULL); |
1041 | 0 | free_peer(peer, 0); |
1042 | 0 | return NULL; |
1043 | 0 | } |
1044 | | #else /* REFCLOCK */ |
1045 | | msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.", |
1046 | | stoa(&peer->srcadr)); |
1047 | | set_peerdstadr(peer, NULL); |
1048 | | free_peer(peer, 0); |
1049 | | return NULL; |
1050 | | #endif /* REFCLOCK */ |
1051 | 0 | } |
1052 | | |
1053 | | /* |
1054 | | * Put the new peer in the hash tables. |
1055 | | */ |
1056 | 0 | hash = NTP_HASH_ADDR(&peer->srcadr); |
1057 | 0 | LINK_SLIST(peer_hash[hash], peer, adr_link); |
1058 | 0 | peer_hash_count[hash]++; |
1059 | 0 | hash = peer->associd & NTP_HASH_MASK; |
1060 | 0 | LINK_SLIST(assoc_hash[hash], peer, aid_link); |
1061 | 0 | assoc_hash_count[hash]++; |
1062 | 0 | LINK_SLIST(peer_list, peer, p_link); |
1063 | |
|
1064 | 0 | restrict_source(&peer->srcadr, FALSE, 0); |
1065 | 0 | mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd); |
1066 | 0 | DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n", |
1067 | 0 | latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode, |
1068 | 0 | peer->version, peer->minpoll, peer->maxpoll, peer->flags, |
1069 | 0 | peer->cast_flags, peer->ttl, peer->keyid)); |
1070 | 0 | return peer; |
1071 | 0 | } |
1072 | | |
1073 | | |
1074 | | /* |
1075 | | * peer_clr_stats - clear peer module statistics counters |
1076 | | */ |
1077 | | void |
1078 | | peer_clr_stats(void) |
1079 | 0 | { |
1080 | 0 | findpeer_calls = 0; |
1081 | 0 | assocpeer_calls = 0; |
1082 | 0 | peer_allocations = 0; |
1083 | 0 | peer_demobilizations = 0; |
1084 | 0 | peer_timereset = current_time; |
1085 | 0 | } |
1086 | | |
1087 | | |
1088 | | /* |
1089 | | * peer_reset - reset statistics counters |
1090 | | */ |
1091 | | void |
1092 | | peer_reset( |
1093 | | struct peer *peer |
1094 | | ) |
1095 | 0 | { |
1096 | 0 | if (peer == NULL) |
1097 | 0 | return; |
1098 | | |
1099 | 0 | peer->timereset = current_time; |
1100 | 0 | peer->sent = 0; |
1101 | 0 | peer->received = 0; |
1102 | 0 | peer->processed = 0; |
1103 | 0 | peer->badauth = 0; |
1104 | 0 | peer->bogusorg = 0; |
1105 | 0 | peer->oldpkt = 0; |
1106 | 0 | peer->seldisptoolarge = 0; |
1107 | 0 | peer->selbroken = 0; |
1108 | 0 | } |
1109 | | |
1110 | | |
1111 | | /* |
1112 | | * peer_all_reset - reset all peer statistics counters |
1113 | | */ |
1114 | | void |
1115 | | peer_all_reset(void) |
1116 | 0 | { |
1117 | 0 | struct peer *peer; |
1118 | |
|
1119 | 0 | for (peer = peer_list; peer != NULL; peer = peer->p_link) |
1120 | 0 | peer_reset(peer); |
1121 | 0 | } |
1122 | | |
1123 | | |
1124 | | /* |
1125 | | * findmanycastpeer - find and return a manycastclient or pool |
1126 | | * association matching a received response. |
1127 | | */ |
1128 | | struct peer * |
1129 | | findmanycastpeer( |
1130 | | struct recvbuf *rbufp /* receive buffer pointer */ |
1131 | | ) |
1132 | 0 | { |
1133 | 0 | struct peer *peer; |
1134 | 0 | struct pkt *pkt; |
1135 | 0 | l_fp p_org; |
1136 | | |
1137 | | /* |
1138 | | * This routine is called upon arrival of a server-mode response |
1139 | | * to a manycastclient multicast solicitation, or to a pool |
1140 | | * server unicast solicitation. Search the peer list for a |
1141 | | * manycastclient association where the last transmit timestamp |
1142 | | * matches the response packet's originate timestamp. There can |
1143 | | * be multiple manycastclient associations, or multiple pool |
1144 | | * solicitation assocations, so this assumes the transmit |
1145 | | * timestamps are unique for such. |
1146 | | */ |
1147 | 0 | pkt = &rbufp->recv_pkt; |
1148 | 0 | for (peer = peer_list; peer != NULL; peer = peer->p_link) |
1149 | 0 | if (MDF_SOLICIT_MASK & peer->cast_flags) { |
1150 | 0 | NTOHL_FP(&pkt->org, &p_org); |
1151 | 0 | if (L_ISEQU(&p_org, &peer->aorg)) { |
1152 | 0 | break; |
1153 | 0 | } |
1154 | 0 | } |
1155 | |
|
1156 | 0 | return peer; |
1157 | 0 | } |
1158 | | |
1159 | | /* peer_cleanup - clean peer list prior to shutdown */ |
1160 | | void peer_cleanup(void) |
1161 | 0 | { |
1162 | 0 | struct peer *peer; |
1163 | 0 | struct peer *nextpeer; |
1164 | |
|
1165 | 0 | for (peer = peer_list; peer != NULL; peer = nextpeer) { |
1166 | 0 | nextpeer = peer->p_link; |
1167 | 0 | unpeer(peer); |
1168 | 0 | } |
1169 | 0 | } |