154359Sroberto/* 254359Sroberto * ntp_peer.c - management of data maintained for peer associations 354359Sroberto */ 454359Sroberto#ifdef HAVE_CONFIG_H 554359Sroberto#include <config.h> 654359Sroberto#endif 754359Sroberto 854359Sroberto#include <stdio.h> 954359Sroberto#include <sys/types.h> 1054359Sroberto 1154359Sroberto#include "ntpd.h" 12290001Sglebius#include "ntp_lists.h" 1354359Sroberto#include "ntp_stdlib.h" 14290001Sglebius#include "ntp_control.h" 15182007Sroberto#include <ntp_random.h> 1654359Sroberto 1754359Sroberto/* 18290001Sglebius * Table of valid association combinations 19290001Sglebius * --------------------------------------- 2054359Sroberto * 2154359Sroberto * packet->mode 2254359Sroberto * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST 2354359Sroberto * ---------- | --------------------------------------------- 24182007Sroberto * NO_PEER | e 1 0 1 1 1 2554359Sroberto * ACTIVE | e 1 1 0 0 0 2654359Sroberto * PASSIVE | e 1 e 0 0 0 27290001Sglebius * CLIENT | e 0 0 0 1 0 2854359Sroberto * SERVER | e 0 0 0 0 0 29182007Sroberto * BCAST | e 0 0 0 0 0 3054359Sroberto * BCLIENT | e 0 0 0 e 1 3154359Sroberto * 3282498Sroberto * One point to note here: a packet in BCAST mode can potentially match 3382498Sroberto * a peer in CLIENT mode, but we that is a special case and we check for 3482498Sroberto * that early in the decision process. This avoids having to keep track 3582498Sroberto * of what kind of associations are possible etc... We actually 3682498Sroberto * circumvent that problem by requiring that the first b(m)roadcast 3782498Sroberto * received after the change back to BCLIENT mode sets the clock. 3854359Sroberto */ 39182007Sroberto#define AM_MODES 7 /* number of rows and columns */ 40182007Sroberto#define NO_PEER 0 /* action when no peer is found */ 4154359Sroberto 4254359Srobertoint AM[AM_MODES][AM_MODES] = { 43290001Sglebius/* packet->mode */ 44290001Sglebius/* peer { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */ 45290001Sglebius/* mode */ 46182007Sroberto/*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL}, 4754359Sroberto 4854359Sroberto/*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, 4954359Sroberto 5054359Sroberto/*P*/ { AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, 5154359Sroberto 52290001Sglebius/*C*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_NOMATCH}, 5354359Sroberto 5454359Sroberto/*S*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, 5554359Sroberto 5654359Sroberto/*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, 5754359Sroberto 58182007Sroberto/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT}, 5954359Sroberto}; 6054359Sroberto 61290001Sglebius#define MATCH_ASSOC(x, y) AM[(x)][(y)] 6254359Sroberto 6354359Sroberto/* 6454359Sroberto * These routines manage the allocation of memory to peer structures 65290001Sglebius * and the maintenance of three data structures involving all peers: 66290001Sglebius * 67290001Sglebius * - peer_list is a single list with all peers, suitable for scanning 68290001Sglebius * operations over all peers. 69290001Sglebius * - peer_adr_hash is an array of lists indexed by hashed peer address. 70290001Sglebius * - peer_aid_hash is an array of lists indexed by hashed associd. 71290001Sglebius * 72290001Sglebius * They also maintain a free list of peer structures, peer_free. 73290001Sglebius * 74290001Sglebius * The three main entry points are findpeer(), which looks for matching 75290001Sglebius * peer structures in the peer list, newpeer(), which allocates a new 76290001Sglebius * peer structure and adds it to the list, and unpeer(), which 77290001Sglebius * demobilizes the association and deallocates the structure. 7854359Sroberto */ 7954359Sroberto/* 8082498Sroberto * Peer hash tables 8154359Sroberto */ 82182007Srobertostruct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */ 83290001Sglebiusint peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */ 84182007Srobertostruct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */ 85290001Sglebiusint assoc_hash_count[NTP_HASH_SIZE];/* peers in each bucket */ 86290001Sglebiusstruct peer *peer_list; /* peer structures list */ 8782498Srobertostatic struct peer *peer_free; /* peer structures free list */ 88290001Sglebiusint peer_free_count; /* count of free structures */ 8954359Sroberto 9054359Sroberto/* 9182498Sroberto * Association ID. We initialize this value randomly, then assign a new 92290001Sglebius * value every time an association is mobilized. 9354359Sroberto */ 9482498Srobertostatic associd_t current_association_ID; /* association ID */ 95290001Sglebiusstatic associd_t initial_association_ID; /* association ID */ 9654359Sroberto 9754359Sroberto/* 9854359Sroberto * Memory allocation watermarks. 9954359Sroberto */ 100290001Sglebius#define INIT_PEER_ALLOC 8 /* static preallocation */ 101290001Sglebius#define INC_PEER_ALLOC 4 /* add N more when empty */ 10254359Sroberto 10354359Sroberto/* 10454359Sroberto * Miscellaneous statistic counters which may be queried. 10554359Sroberto */ 106290001Sglebiusu_long peer_timereset; /* time stat counters zeroed */ 107290001Sglebiusu_long findpeer_calls; /* calls to findpeer */ 108290001Sglebiusu_long assocpeer_calls; /* calls to findpeerbyassoc */ 109290001Sglebiusu_long peer_allocations; /* allocations from free list */ 110290001Sglebiusu_long peer_demobilizations; /* structs freed to free list */ 111290001Sglebiusint total_peer_structs; /* peer structs */ 112290001Sglebiusint peer_associations; /* mobilized associations */ 113290001Sglebiusint peer_preempt; /* preemptable associations */ 11482498Srobertostatic struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */ 11554359Sroberto 116290001Sglebiusstatic struct peer * findexistingpeer_name(const char *, u_short, 117290001Sglebius struct peer *, int); 118290001Sglebiusstatic struct peer * findexistingpeer_addr(sockaddr_u *, 119290001Sglebius struct peer *, int, 120290001Sglebius u_char); 121290001Sglebiusstatic void free_peer(struct peer *, int); 122290001Sglebiusstatic void getmorepeermem(void); 123290001Sglebiusstatic int score(struct peer *); 12454359Sroberto 125290001Sglebius 12654359Sroberto/* 12754359Sroberto * init_peer - initialize peer data structures and counters 12854359Sroberto * 12982498Sroberto * N.B. We use the random number routine in here. It had better be 13082498Sroberto * initialized prior to getting here. 13154359Sroberto */ 13254359Srobertovoid 13354359Srobertoinit_peer(void) 13454359Sroberto{ 135290001Sglebius int i; 13654359Sroberto 13754359Sroberto /* 138290001Sglebius * Initialize peer free list from static allocation. 13954359Sroberto */ 140290001Sglebius for (i = COUNTOF(init_peer_alloc) - 1; i >= 0; i--) 141290001Sglebius LINK_SLIST(peer_free, &init_peer_alloc[i], p_link); 142290001Sglebius total_peer_structs = COUNTOF(init_peer_alloc); 143290001Sglebius peer_free_count = COUNTOF(init_peer_alloc); 14454359Sroberto 14554359Sroberto /* 14654359Sroberto * Initialize our first association ID 14754359Sroberto */ 148290001Sglebius do 149290001Sglebius current_association_ID = ntp_random() & ASSOCID_MAX; 150290001Sglebius while (!current_association_ID); 151290001Sglebius initial_association_ID = current_association_ID; 15254359Sroberto} 15354359Sroberto 15454359Sroberto 15554359Sroberto/* 15654359Sroberto * getmorepeermem - add more peer structures to the free list 15754359Sroberto */ 15854359Srobertostatic void 15954359Srobertogetmorepeermem(void) 16054359Sroberto{ 161290001Sglebius int i; 162290001Sglebius struct peer *peers; 16354359Sroberto 164290001Sglebius peers = emalloc_zero(INC_PEER_ALLOC * sizeof(*peers)); 16554359Sroberto 166290001Sglebius for (i = INC_PEER_ALLOC - 1; i >= 0; i--) 167290001Sglebius LINK_SLIST(peer_free, &peers[i], p_link); 168290001Sglebius 16954359Sroberto total_peer_structs += INC_PEER_ALLOC; 17054359Sroberto peer_free_count += INC_PEER_ALLOC; 17154359Sroberto} 17254359Sroberto 17354359Sroberto 174290001Sglebiusstatic struct peer * 175290001Sglebiusfindexistingpeer_name( 176290001Sglebius const char * hostname, 177290001Sglebius u_short hname_fam, 178290001Sglebius struct peer * start_peer, 179290001Sglebius int mode 18054359Sroberto ) 18154359Sroberto{ 182290001Sglebius struct peer *p; 18354359Sroberto 184290001Sglebius if (NULL == start_peer) 185290001Sglebius p = peer_list; 186290001Sglebius else 187290001Sglebius p = start_peer->p_link; 188290001Sglebius for (; p != NULL; p = p->p_link) 189290001Sglebius if (p->hostname != NULL 190290001Sglebius && (-1 == mode || p->hmode == mode) 191290001Sglebius && (AF_UNSPEC == hname_fam 192290001Sglebius || AF_UNSPEC == AF(&p->srcadr) 193290001Sglebius || hname_fam == AF(&p->srcadr)) 194290001Sglebius && !strcasecmp(p->hostname, hostname)) 195290001Sglebius break; 196290001Sglebius return p; 197290001Sglebius} 198290001Sglebius 199290001Sglebius 200290001Sglebiusstatic 201290001Sglebiusstruct peer * 202290001Sglebiusfindexistingpeer_addr( 203290001Sglebius sockaddr_u * addr, 204290001Sglebius struct peer * start_peer, 205290001Sglebius int mode, 206290001Sglebius u_char cast_flags 207290001Sglebius ) 208290001Sglebius{ 209290001Sglebius struct peer *peer; 210290001Sglebius 211290001Sglebius DPRINTF(2, ("findexistingpeer_addr(%s, %s, %d, 0x%x)\n", 212290001Sglebius sptoa(addr), 213290001Sglebius (start_peer) 214290001Sglebius ? sptoa(&start_peer->srcadr) 215290001Sglebius : "NULL", 216290001Sglebius mode, (u_int)cast_flags)); 217290001Sglebius 21854359Sroberto /* 21954359Sroberto * start_peer is included so we can locate instances of the 22054359Sroberto * same peer through different interfaces in the hash table. 221290001Sglebius * Without MDF_BCLNT, a match requires the same mode and remote 222290001Sglebius * address. MDF_BCLNT associations start out as MODE_CLIENT 223290001Sglebius * if broadcastdelay is not specified, and switch to 224290001Sglebius * MODE_BCLIENT after estimating the one-way delay. Duplicate 225290001Sglebius * associations are expanded in definition to match any other 226290001Sglebius * MDF_BCLNT with the same srcadr (remote, unicast address). 22754359Sroberto */ 228290001Sglebius if (NULL == start_peer) 229182007Sroberto peer = peer_hash[NTP_HASH_ADDR(addr)]; 23054359Sroberto else 231290001Sglebius peer = start_peer->adr_link; 23254359Sroberto 233290001Sglebius while (peer != NULL) { 234290001Sglebius DPRINTF(3, ("%s %s %d %d 0x%x 0x%x ", sptoa(addr), 235290001Sglebius sptoa(&peer->srcadr), mode, peer->hmode, 236290001Sglebius (u_int)cast_flags, (u_int)peer->cast_flags)); 237290001Sglebius if ((-1 == mode || peer->hmode == mode || 238290001Sglebius ((MDF_BCLNT & peer->cast_flags) && 239290001Sglebius (MDF_BCLNT & cast_flags))) && 240290001Sglebius ADDR_PORT_EQ(addr, &peer->srcadr)) { 241290001Sglebius DPRINTF(3, ("found.\n")); 242290001Sglebius break; 24354359Sroberto } 244290001Sglebius DPRINTF(3, ("\n")); 245290001Sglebius peer = peer->adr_link; 24654359Sroberto } 247290001Sglebius 248290001Sglebius return peer; 24954359Sroberto} 25054359Sroberto 25154359Sroberto 25254359Sroberto/* 253290001Sglebius * findexistingpeer - search by address and return a pointer to a peer. 25454359Sroberto */ 25554359Srobertostruct peer * 256290001Sglebiusfindexistingpeer( 257290001Sglebius sockaddr_u * addr, 258290001Sglebius const char * hostname, 259290001Sglebius struct peer * start_peer, 260290001Sglebius int mode, 261290001Sglebius u_char cast_flags 262290001Sglebius ) 263290001Sglebius{ 264290001Sglebius if (hostname != NULL) 265290001Sglebius return findexistingpeer_name(hostname, AF(addr), 266290001Sglebius start_peer, mode); 267290001Sglebius else 268290001Sglebius return findexistingpeer_addr(addr, start_peer, mode, 269290001Sglebius cast_flags); 270290001Sglebius} 271290001Sglebius 272290001Sglebius 273290001Sglebius/* 274290001Sglebius * findpeer - find and return a peer match for a received datagram in 275290001Sglebius * the peer_hash table. 276310419Sdelphij * 277310419Sdelphij * [Bug 3072] To faciliate a faster reorganisation after routing changes 278310419Sdelphij * the original code re-assigned the peer address to be the destination 279310419Sdelphij * of the received packet and initiated another round on a mismatch. 280310419Sdelphij * Unfortunately this leaves us wide open for a DoS attack where the 281310419Sdelphij * attacker directs a packet with forged destination address to us -- 282310419Sdelphij * this results in a wrong interface assignment, actually creating a DoS 283310419Sdelphij * situation. 284310419Sdelphij * 285310419Sdelphij * This condition would persist until the next update of the interface 286310419Sdelphij * list, but a continued attack would put us out of business again soon 287310419Sdelphij * enough. Authentication alone does not help here, since it does not 288310419Sdelphij * protect the UDP layer and leaves us open for a replay attack. 289310419Sdelphij * 290310419Sdelphij * So we do not update the adresses and wait until the next interface 291310419Sdelphij * list update does the right thing for us. 292290001Sglebius */ 293290001Sglebiusstruct peer * 29454359Srobertofindpeer( 295290001Sglebius struct recvbuf *rbufp, 296290001Sglebius int pkt_mode, 297290001Sglebius int * action 29854359Sroberto ) 29954359Sroberto{ 300290001Sglebius struct peer * p; 301290001Sglebius sockaddr_u * srcadr; 302290001Sglebius u_int hash; 303290001Sglebius struct pkt * pkt; 304290001Sglebius l_fp pkt_org; 30554359Sroberto 30654359Sroberto findpeer_calls++; 307290001Sglebius srcadr = &rbufp->recv_srcadr; 308182007Sroberto hash = NTP_HASH_ADDR(srcadr); 309290001Sglebius for (p = peer_hash[hash]; p != NULL; p = p->adr_link) { 31082498Sroberto 311310419Sdelphij /* [Bug 3072] ensure interface of peer matches */ 312310419Sdelphij if (p->dstadr != rbufp->dstadr) 313310419Sdelphij continue; 31454359Sroberto 315310419Sdelphij /* ensure peer source address matches */ 316310419Sdelphij if ( ! ADDR_PORT_EQ(srcadr, &p->srcadr)) 317310419Sdelphij continue; 318310419Sdelphij 319310419Sdelphij /* If the association matching rules determine that this 320310419Sdelphij * is not a valid combination, then look for the next 321310419Sdelphij * valid peer association. 322310419Sdelphij */ 323310419Sdelphij *action = MATCH_ASSOC(p->hmode, pkt_mode); 324290001Sglebius 325310419Sdelphij /* A response to our manycastclient solicitation might 326310419Sdelphij * be misassociated with an ephemeral peer already spun 327310419Sdelphij * for the server. If the packet's org timestamp 328310419Sdelphij * doesn't match the peer's, check if it matches the 329310419Sdelphij * ACST prototype peer's. If so it is a redundant 330310419Sdelphij * solicitation response, return AM_ERR to discard it. 331310419Sdelphij * [Bug 1762] 332310419Sdelphij */ 333310419Sdelphij if (MODE_SERVER == pkt_mode && AM_PROCPKT == *action) { 334310419Sdelphij pkt = &rbufp->recv_pkt; 335310419Sdelphij NTOHL_FP(&pkt->org, &pkt_org); 336310419Sdelphij if (!L_ISEQU(&p->aorg, &pkt_org) && 337310419Sdelphij findmanycastpeer(rbufp)) 338310419Sdelphij *action = AM_ERR; 339310419Sdelphij } 34054359Sroberto 341310419Sdelphij /* if an error was returned, exit back right here. */ 342310419Sdelphij if (*action == AM_ERR) 343310419Sdelphij return NULL; 344310419Sdelphij 345310419Sdelphij /* if a match is found, we stop our search. */ 346310419Sdelphij if (*action != AM_NOMATCH) 347310419Sdelphij break; 34854359Sroberto } 34954359Sroberto 350310419Sdelphij /* If no matching association is found... */ 351310419Sdelphij if (NULL == p) 35254359Sroberto *action = MATCH_ASSOC(NO_PEER, pkt_mode); 353310419Sdelphij 354290001Sglebius return p; 35554359Sroberto} 35654359Sroberto 35754359Sroberto/* 358290001Sglebius * findpeerbyassoc - find and return a peer using his association ID 35954359Sroberto */ 36054359Srobertostruct peer * 36154359Srobertofindpeerbyassoc( 362290001Sglebius associd_t assoc 36354359Sroberto ) 36454359Sroberto{ 365290001Sglebius struct peer *p; 366290001Sglebius u_int hash; 36754359Sroberto 36854359Sroberto assocpeer_calls++; 369182007Sroberto hash = assoc & NTP_HASH_MASK; 370290001Sglebius for (p = assoc_hash[hash]; p != NULL; p = p->aid_link) 371290001Sglebius if (assoc == p->associd) 372290001Sglebius break; 373290001Sglebius return p; 37454359Sroberto} 37554359Sroberto 37654359Sroberto 37754359Sroberto/* 37882498Sroberto * clear_all - flush all time values for all associations 37954359Sroberto */ 38054359Srobertovoid 38182498Srobertoclear_all(void) 38254359Sroberto{ 383290001Sglebius struct peer *p; 38454359Sroberto 38582498Sroberto /* 38682498Sroberto * This routine is called when the clock is stepped, and so all 38782498Sroberto * previously saved time values are untrusted. 38882498Sroberto */ 389290001Sglebius for (p = peer_list; p != NULL; p = p->p_link) 390290001Sglebius if (!(MDF_TXONLY_MASK & p->cast_flags)) 391290001Sglebius peer_clear(p, "STEP"); 392290001Sglebius 393290001Sglebius DPRINTF(1, ("clear_all: at %lu\n", current_time)); 39454359Sroberto} 39582498Sroberto 39682498Sroberto 39754359Sroberto/* 398290001Sglebius * score_all() - determine if an association can be demobilized 39954359Sroberto */ 400290001Sglebiusint 401290001Sglebiusscore_all( 402290001Sglebius struct peer *peer /* peer structure pointer */ 40354359Sroberto ) 40454359Sroberto{ 405290001Sglebius struct peer *speer; 406290001Sglebius int temp, tamp; 407290001Sglebius int x; 40854359Sroberto 409290001Sglebius /* 410290001Sglebius * This routine finds the minimum score for all preemptible 411290001Sglebius * associations and returns > 0 if the association can be 412290001Sglebius * demobilized. 413290001Sglebius */ 414290001Sglebius tamp = score(peer); 415290001Sglebius temp = 100; 416290001Sglebius for (speer = peer_list; speer != NULL; speer = speer->p_link) 417290001Sglebius if (speer->flags & FLAG_PREEMPT) { 418290001Sglebius x = score(speer); 419290001Sglebius if (x < temp) 420290001Sglebius temp = x; 421290001Sglebius } 422290001Sglebius DPRINTF(1, ("score_all: at %lu score %d min %d\n", 423290001Sglebius current_time, tamp, temp)); 424182007Sroberto 425290001Sglebius if (tamp != temp) 426290001Sglebius temp = 0; 427182007Sroberto 428290001Sglebius return temp; 429290001Sglebius} 430290001Sglebius 431290001Sglebius 432290001Sglebius/* 433290001Sglebius * score() - calculate preemption score 434290001Sglebius */ 435290001Sglebiusstatic int 436290001Sglebiusscore( 437290001Sglebius struct peer *peer /* peer structure pointer */ 438290001Sglebius ) 439290001Sglebius{ 440290001Sglebius int temp; 441290001Sglebius 44254359Sroberto /* 443290001Sglebius * This routine calculates the premption score from the peer 444290001Sglebius * error bits and status. Increasing values are more cherished. 44554359Sroberto */ 446290001Sglebius temp = 0; 447290001Sglebius if (!(peer->flash & TEST10)) 448290001Sglebius temp++; /* 1 good synch and stratum */ 449290001Sglebius if (!(peer->flash & TEST13)) 450290001Sglebius temp++; /* 2 reachable */ 451290001Sglebius if (!(peer->flash & TEST12)) 452290001Sglebius temp++; /* 3 no loop */ 453290001Sglebius if (!(peer->flash & TEST11)) 454290001Sglebius temp++; /* 4 good distance */ 455290001Sglebius if (peer->status >= CTL_PST_SEL_SELCAND) 456290001Sglebius temp++; /* 5 in the hunt */ 457290001Sglebius if (peer->status != CTL_PST_SEL_EXCESS) 458290001Sglebius temp++; /* 6 not spare tire */ 459290001Sglebius return (temp); /* selection status */ 460290001Sglebius} 46154359Sroberto 462290001Sglebius 463290001Sglebius/* 464290001Sglebius * free_peer - internal routine to free memory referred to by a struct 465290001Sglebius * peer and return it to the peer free list. If unlink is 466290001Sglebius * nonzero, unlink from the various lists. 467290001Sglebius */ 468290001Sglebiusstatic void 469290001Sglebiusfree_peer( 470290001Sglebius struct peer * p, 471290001Sglebius int unlink_peer 472290001Sglebius ) 473290001Sglebius{ 474290001Sglebius struct peer * unlinked; 475290001Sglebius int hash; 476290001Sglebius 477290001Sglebius if (unlink_peer) { 478290001Sglebius hash = NTP_HASH_ADDR(&p->srcadr); 479290001Sglebius peer_hash_count[hash]--; 480290001Sglebius 481290001Sglebius UNLINK_SLIST(unlinked, peer_hash[hash], p, adr_link, 482290001Sglebius struct peer); 483290001Sglebius if (NULL == unlinked) { 48454359Sroberto peer_hash_count[hash]++; 485290001Sglebius msyslog(LOG_ERR, "peer %s not in address table!", 486290001Sglebius stoa(&p->srcadr)); 48754359Sroberto } 48854359Sroberto 489290001Sglebius /* 490290001Sglebius * Remove him from the association hash as well. 491290001Sglebius */ 492290001Sglebius hash = p->associd & NTP_HASH_MASK; 493290001Sglebius assoc_hash_count[hash]--; 49454359Sroberto 495290001Sglebius UNLINK_SLIST(unlinked, assoc_hash[hash], p, aid_link, 496290001Sglebius struct peer); 497290001Sglebius if (NULL == unlinked) { 49854359Sroberto assoc_hash_count[hash]++; 49954359Sroberto msyslog(LOG_ERR, 500290001Sglebius "peer %s not in association ID table!", 501290001Sglebius stoa(&p->srcadr)); 50254359Sroberto } 503290001Sglebius 504290001Sglebius /* Remove him from the overall list. */ 505290001Sglebius UNLINK_SLIST(unlinked, peer_list, p, p_link, 506290001Sglebius struct peer); 507290001Sglebius if (NULL == unlinked) 508290001Sglebius msyslog(LOG_ERR, "%s not in peer list!", 509290001Sglebius stoa(&p->srcadr)); 51054359Sroberto } 511290001Sglebius 512290001Sglebius if (p->hostname != NULL) 513290001Sglebius free(p->hostname); 514290001Sglebius 515290001Sglebius if (p->ident != NULL) 516290001Sglebius free(p->ident); 517290001Sglebius 518290001Sglebius if (p->addrs != NULL) 519290001Sglebius free(p->addrs); /* from copy_addrinfo_list() */ 520290001Sglebius 521290001Sglebius /* Add his corporeal form to peer free list */ 522290001Sglebius ZERO(*p); 523290001Sglebius LINK_SLIST(peer_free, p, p_link); 52454359Sroberto peer_free_count++; 52554359Sroberto} 52654359Sroberto 52754359Sroberto 52854359Sroberto/* 529290001Sglebius * unpeer - remove peer structure from hash table and free structure 530290001Sglebius */ 531290001Sglebiusvoid 532290001Sglebiusunpeer( 533290001Sglebius struct peer *peer 534290001Sglebius ) 535290001Sglebius{ 536290001Sglebius mprintf_event(PEVNT_DEMOBIL, peer, "assoc %u", peer->associd); 537290001Sglebius restrict_source(&peer->srcadr, 1, 0); 538290001Sglebius set_peerdstadr(peer, NULL); 539290001Sglebius peer_demobilizations++; 540290001Sglebius peer_associations--; 541290001Sglebius if (FLAG_PREEMPT & peer->flags) 542290001Sglebius peer_preempt--; 543290001Sglebius#ifdef REFCLOCK 544290001Sglebius /* 545290001Sglebius * If this peer is actually a clock, shut it down first 546290001Sglebius */ 547290001Sglebius if (FLAG_REFCLOCK & peer->flags) 548290001Sglebius refclock_unpeer(peer); 549290001Sglebius#endif 550290001Sglebius 551290001Sglebius free_peer(peer, TRUE); 552290001Sglebius} 553290001Sglebius 554290001Sglebius 555290001Sglebius/* 55682498Sroberto * peer_config - configure a new association 55754359Sroberto */ 55854359Srobertostruct peer * 55954359Srobertopeer_config( 560290001Sglebius sockaddr_u * srcadr, 561290001Sglebius const char * hostname, 562290001Sglebius endpt * dstadr, 563290001Sglebius u_char hmode, 564290001Sglebius u_char version, 565290001Sglebius u_char minpoll, 566290001Sglebius u_char maxpoll, 567290001Sglebius u_int flags, 568290001Sglebius u_int32 ttl, 569290001Sglebius keyid_t key, 570290001Sglebius const char * ident /* autokey group */ 57154359Sroberto ) 57254359Sroberto{ 573132451Sroberto u_char cast_flags; 57454359Sroberto 57554359Sroberto /* 57682498Sroberto * We do a dirty little jig to figure the cast flags. This is 57782498Sroberto * probably not the best place to do this, at least until the 57882498Sroberto * configure code is rebuilt. Note only one flag can be set. 57954359Sroberto */ 58082498Sroberto switch (hmode) { 58182498Sroberto case MODE_BROADCAST: 582290001Sglebius if (IS_MCAST(srcadr)) 583290001Sglebius cast_flags = MDF_MCAST; 584290001Sglebius else 585290001Sglebius cast_flags = MDF_BCAST; 586290001Sglebius break; 58782498Sroberto 58882498Sroberto case MODE_CLIENT: 589290001Sglebius if (hostname != NULL && SOCK_UNSPEC(srcadr)) 590290001Sglebius cast_flags = MDF_POOL; 591290001Sglebius else if (IS_MCAST(srcadr)) 592290001Sglebius cast_flags = MDF_ACAST; 593290001Sglebius else 594290001Sglebius cast_flags = MDF_UCAST; 595290001Sglebius break; 59682498Sroberto 59782498Sroberto default: 59882498Sroberto cast_flags = MDF_UCAST; 59982498Sroberto } 60082498Sroberto 60182498Sroberto /* 602290001Sglebius * Mobilize the association and initialize its variables. If 603290001Sglebius * emulating ntpdate, force iburst. For pool and manycastclient 604290001Sglebius * strip FLAG_PREEMPT as the prototype associations are not 605290001Sglebius * themselves preemptible, though the resulting associations 606290001Sglebius * are. 60782498Sroberto */ 608290001Sglebius flags |= FLAG_CONFIG; 609132451Sroberto if (mode_ntpdate) 610132451Sroberto flags |= FLAG_IBURST; 611290001Sglebius if ((MDF_ACAST | MDF_POOL) & cast_flags) 612290001Sglebius flags &= ~FLAG_PREEMPT; 613290001Sglebius return newpeer(srcadr, hostname, dstadr, hmode, version, 614290001Sglebius minpoll, maxpoll, flags, cast_flags, ttl, key, ident); 61554359Sroberto} 61654359Sroberto 617182007Sroberto/* 618290001Sglebius * setup peer dstadr field keeping it in sync with the interface 619290001Sglebius * structures 620182007Sroberto */ 621182007Srobertovoid 622290001Sglebiusset_peerdstadr( 623290001Sglebius struct peer * p, 624290001Sglebius endpt * dstadr 625290001Sglebius ) 626182007Sroberto{ 627290001Sglebius struct peer * unlinked; 62854359Sroberto 629310419Sdelphij DEBUG_INSIST(p != NULL); 630310419Sdelphij 631310419Sdelphij if (p == NULL) 632310419Sdelphij return; 633310419Sdelphij 634310419Sdelphij /* check for impossible or identical assignment */ 635290001Sglebius if (p->dstadr == dstadr) 636290001Sglebius return; 637182007Sroberto 638290001Sglebius /* 639290001Sglebius * Don't accept updates to a separate multicast receive-only 640290001Sglebius * endpt while a BCLNT peer is running its unicast protocol. 641290001Sglebius */ 642290001Sglebius if (dstadr != NULL && (FLAG_BC_VOL & p->flags) && 643290001Sglebius (INT_MCASTIF & dstadr->flags) && MODE_CLIENT == p->hmode) { 644290001Sglebius return; 645182007Sroberto } 646310419Sdelphij 647310419Sdelphij /* unlink from list if we have an address prior to assignment */ 648290001Sglebius if (p->dstadr != NULL) { 649290001Sglebius p->dstadr->peercnt--; 650290001Sglebius UNLINK_SLIST(unlinked, p->dstadr->peers, p, ilink, 651290001Sglebius struct peer); 652290001Sglebius msyslog(LOG_INFO, "%s local addr %s -> %s", 653290001Sglebius stoa(&p->srcadr), latoa(p->dstadr), 654290001Sglebius latoa(dstadr)); 655290001Sglebius } 656310419Sdelphij 657290001Sglebius p->dstadr = dstadr; 658310419Sdelphij 659310419Sdelphij /* link to list if we have an address after assignment */ 660310419Sdelphij if (p->dstadr != NULL) { 661290001Sglebius LINK_SLIST(dstadr->peers, p, ilink); 662290001Sglebius dstadr->peercnt++; 663290001Sglebius } 664182007Sroberto} 665182007Sroberto 66654359Sroberto/* 667182007Sroberto * attempt to re-rebind interface if necessary 668182007Sroberto */ 669182007Srobertostatic void 670290001Sglebiuspeer_refresh_interface( 671290001Sglebius struct peer *p 672290001Sglebius ) 673182007Sroberto{ 674290001Sglebius endpt * niface; 675290001Sglebius endpt * piface; 676182007Sroberto 677290001Sglebius niface = select_peerinterface(p, &p->srcadr, NULL); 678182007Sroberto 679290001Sglebius DPRINTF(4, ( 680290001Sglebius "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %u key %08x: new interface: ", 681290001Sglebius p->dstadr == NULL ? "<null>" : 682290001Sglebius stoa(&p->dstadr->sin), stoa(&p->srcadr), p->hmode, 683290001Sglebius p->version, p->minpoll, p->maxpoll, p->flags, p->cast_flags, 684290001Sglebius p->ttl, p->keyid)); 685290001Sglebius if (niface != NULL) { 686290001Sglebius DPRINTF(4, ( 687290001Sglebius "fd=%d, bfd=%d, name=%.16s, flags=0x%x, ifindex=%u, sin=%s", 688290001Sglebius niface->fd, niface->bfd, niface->name, 689290001Sglebius niface->flags, niface->ifindex, 690290001Sglebius stoa(&niface->sin))); 691290001Sglebius if (niface->flags & INT_BROADCAST) 692290001Sglebius DPRINTF(4, (", bcast=%s", 693290001Sglebius stoa(&niface->bcast))); 694290001Sglebius DPRINTF(4, (", mask=%s\n", stoa(&niface->mask))); 695290001Sglebius } else { 696290001Sglebius DPRINTF(4, ("<NONE>\n")); 697182007Sroberto } 698182007Sroberto 699290001Sglebius piface = p->dstadr; 700290001Sglebius set_peerdstadr(p, niface); 701290001Sglebius if (p->dstadr != NULL) { 702290001Sglebius /* 703290001Sglebius * clear crypto if we change the local address 704290001Sglebius */ 705290001Sglebius if (p->dstadr != piface && !(MDF_ACAST & p->cast_flags) 706290001Sglebius && MODE_BROADCAST != p->pmode) 707290001Sglebius peer_clear(p, "XFAC"); 708182007Sroberto 709182007Sroberto /* 710182007Sroberto * Broadcast needs the socket enabled for broadcast 711182007Sroberto */ 712290001Sglebius if (MDF_BCAST & p->cast_flags) 713290001Sglebius enable_broadcast(p->dstadr, &p->srcadr); 714182007Sroberto 715182007Sroberto /* 716290001Sglebius * Multicast needs the socket interface enabled for 717290001Sglebius * multicast 718182007Sroberto */ 719290001Sglebius if (MDF_MCAST & p->cast_flags) 720290001Sglebius enable_multicast_if(p->dstadr, &p->srcadr); 721182007Sroberto } 722182007Sroberto} 723182007Sroberto 724290001Sglebius 725182007Sroberto/* 726290001Sglebius * refresh_all_peerinterfaces - see that all interface bindings are up 727290001Sglebius * to date 728182007Sroberto */ 729182007Srobertovoid 730182007Srobertorefresh_all_peerinterfaces(void) 731182007Sroberto{ 732290001Sglebius struct peer *p; 733182007Sroberto 734182007Sroberto /* 735182007Sroberto * this is called when the interface list has changed 736182007Sroberto * give all peers a chance to find a better interface 737290001Sglebius * but only if either they don't have an address already 738290001Sglebius * or if the one they have hasn't worked for a while. 739182007Sroberto */ 740290001Sglebius for (p = peer_list; p != NULL; p = p->p_link) { 741290001Sglebius if (!(p->dstadr && (p->reach & 0x3))) // Bug 2849 XOR 2043 742290001Sglebius peer_refresh_interface(p); 743182007Sroberto } 744182007Sroberto} 745182007Sroberto 746290001Sglebius 747182007Sroberto/* 748290001Sglebius * newpeer - initialize a new peer association 749182007Sroberto */ 750290001Sglebiusstruct peer * 751290001Sglebiusnewpeer( 752290001Sglebius sockaddr_u * srcadr, 753290001Sglebius const char * hostname, 754290001Sglebius endpt * dstadr, 755290001Sglebius u_char hmode, 756290001Sglebius u_char version, 757290001Sglebius u_char minpoll, 758290001Sglebius u_char maxpoll, 759290001Sglebius u_int flags, 760290001Sglebius u_char cast_flags, 761290001Sglebius u_int32 ttl, 762290001Sglebius keyid_t key, 763290001Sglebius const char * ident 764290001Sglebius ) 765182007Sroberto{ 766290001Sglebius struct peer * peer; 767290001Sglebius u_int hash; 768290001Sglebius 769290001Sglebius DEBUG_REQUIRE(srcadr); 770290001Sglebius 771290001Sglebius#ifdef AUTOKEY 772182007Sroberto /* 773290001Sglebius * If Autokey is requested but not configured, complain loudly. 774182007Sroberto */ 775290001Sglebius if (!crypto_flags) { 776290001Sglebius if (key > NTP_MAXKEY) { 777290001Sglebius return (NULL); 778182007Sroberto 779290001Sglebius } else if (flags & FLAG_SKEY) { 780290001Sglebius msyslog(LOG_ERR, "Autokey not configured"); 781290001Sglebius return (NULL); 782290001Sglebius } 783290001Sglebius } 784290001Sglebius#endif /* AUTOKEY */ 785290001Sglebius 786182007Sroberto /* 787290001Sglebius * For now only pool associations have a hostname. 788182007Sroberto */ 789290001Sglebius INSIST(NULL == hostname || (MDF_POOL & cast_flags)); 790182007Sroberto 791290001Sglebius /* 792290001Sglebius * First search from the beginning for an association with given 793290001Sglebius * remote address and mode. If an interface is given, search 794290001Sglebius * from there to find the association which matches that 795290001Sglebius * destination. If the given interface is "any", track down the 796290001Sglebius * actual interface, because that's what gets put into the peer 797290001Sglebius * structure. 798290001Sglebius */ 799290001Sglebius if (dstadr != NULL) { 800290001Sglebius peer = findexistingpeer(srcadr, hostname, NULL, hmode, 801290001Sglebius cast_flags); 802290001Sglebius while (peer != NULL) { 803290001Sglebius if (peer->dstadr == dstadr || 804290001Sglebius ((MDF_BCLNT & cast_flags) && 805290001Sglebius (MDF_BCLNT & peer->cast_flags))) 806290001Sglebius break; 807182007Sroberto 808290001Sglebius if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) && 809290001Sglebius peer->dstadr == findinterface(srcadr)) 810290001Sglebius break; 811182007Sroberto 812290001Sglebius peer = findexistingpeer(srcadr, hostname, peer, 813290001Sglebius hmode, cast_flags); 814290001Sglebius } 815290001Sglebius } else { 816290001Sglebius /* no endpt address given */ 817290001Sglebius peer = findexistingpeer(srcadr, hostname, NULL, hmode, 818290001Sglebius cast_flags); 819290001Sglebius } 82054359Sroberto 82154359Sroberto /* 822290001Sglebius * If a peer is found, this would be a duplicate and we don't 823290001Sglebius * allow that. This avoids duplicate ephemeral (broadcast/ 824290001Sglebius * multicast) and preemptible (manycast and pool) client 825290001Sglebius * associations. 826290001Sglebius */ 827290001Sglebius if (peer != NULL) { 828290001Sglebius DPRINTF(2, ("newpeer(%s) found existing association\n", 829290001Sglebius (hostname) 830290001Sglebius ? hostname 831290001Sglebius : stoa(srcadr))); 832290001Sglebius return NULL; 833290001Sglebius } 834290001Sglebius 835290001Sglebius /* 83682498Sroberto * Allocate a new peer structure. Some dirt here, since some of 83782498Sroberto * the initialization requires knowlege of our system state. 83854359Sroberto */ 83954359Sroberto if (peer_free_count == 0) 84082498Sroberto getmorepeermem(); 841290001Sglebius UNLINK_HEAD_SLIST(peer, peer_free, p_link); 842290001Sglebius INSIST(peer != NULL); 84354359Sroberto peer_free_count--; 84454359Sroberto peer_associations++; 845290001Sglebius if (FLAG_PREEMPT & flags) 846182007Sroberto peer_preempt++; 84754359Sroberto 84854359Sroberto /* 849132451Sroberto * Assign an association ID and increment the system variable. 850132451Sroberto */ 851132451Sroberto peer->associd = current_association_ID; 852132451Sroberto if (++current_association_ID == 0) 853132451Sroberto ++current_association_ID; 854132451Sroberto 85582498Sroberto peer->srcadr = *srcadr; 856290001Sglebius if (hostname != NULL) 857290001Sglebius peer->hostname = estrdup(hostname); 858290001Sglebius peer->hmode = hmode; 859290001Sglebius peer->version = version; 860132451Sroberto peer->flags = flags; 861290001Sglebius peer->cast_flags = cast_flags; 862290001Sglebius set_peerdstadr(peer, 863290001Sglebius select_peerinterface(peer, srcadr, dstadr)); 864182007Sroberto 865182007Sroberto /* 866290001Sglebius * It is an error to set minpoll less than NTP_MINPOLL or to 867290001Sglebius * set maxpoll greater than NTP_MAXPOLL. However, minpoll is 868290001Sglebius * clamped not greater than NTP_MAXPOLL and maxpoll is clamped 869290001Sglebius * not less than NTP_MINPOLL without complaint. Finally, 870290001Sglebius * minpoll is clamped not greater than maxpoll. 871290001Sglebius */ 872290001Sglebius if (minpoll == 0) 873290001Sglebius peer->minpoll = NTP_MINDPOLL; 874290001Sglebius else 875290001Sglebius peer->minpoll = min(minpoll, NTP_MAXPOLL); 876290001Sglebius if (maxpoll == 0) 877290001Sglebius peer->maxpoll = NTP_MAXDPOLL; 878290001Sglebius else 879290001Sglebius peer->maxpoll = max(maxpoll, NTP_MINPOLL); 880290001Sglebius if (peer->minpoll > peer->maxpoll) 881290001Sglebius peer->minpoll = peer->maxpoll; 882290001Sglebius 883290001Sglebius if (peer->dstadr != NULL) 884290001Sglebius DPRINTF(3, ("newpeer(%s): using fd %d and our addr %s\n", 885290001Sglebius stoa(srcadr), peer->dstadr->fd, 886290001Sglebius stoa(&peer->dstadr->sin))); 887290001Sglebius else 888290001Sglebius DPRINTF(3, ("newpeer(%s): local interface currently not bound\n", 889290001Sglebius stoa(srcadr))); 890290001Sglebius 891290001Sglebius /* 892182007Sroberto * Broadcast needs the socket enabled for broadcast 893182007Sroberto */ 894290001Sglebius if ((MDF_BCAST & cast_flags) && peer->dstadr != NULL) 895182007Sroberto enable_broadcast(peer->dstadr, srcadr); 896290001Sglebius 897182007Sroberto /* 898182007Sroberto * Multicast needs the socket interface enabled for multicast 899182007Sroberto */ 900290001Sglebius if ((MDF_MCAST & cast_flags) && peer->dstadr != NULL) 901182007Sroberto enable_multicast_if(peer->dstadr, srcadr); 902290001Sglebius 903290001Sglebius#ifdef AUTOKEY 904132451Sroberto if (key > NTP_MAXKEY) 905132451Sroberto peer->flags |= FLAG_SKEY; 906290001Sglebius#endif /* AUTOKEY */ 907290001Sglebius peer->ttl = ttl; 90854359Sroberto peer->keyid = key; 909290001Sglebius if (ident != NULL) 910290001Sglebius peer->ident = estrdup(ident); 91154359Sroberto peer->precision = sys_precision; 912182007Sroberto peer->hpoll = peer->minpoll; 913132451Sroberto if (cast_flags & MDF_ACAST) 914132451Sroberto peer_clear(peer, "ACST"); 915290001Sglebius else if (cast_flags & MDF_POOL) 916290001Sglebius peer_clear(peer, "POOL"); 917132451Sroberto else if (cast_flags & MDF_MCAST) 918132451Sroberto peer_clear(peer, "MCST"); 919132451Sroberto else if (cast_flags & MDF_BCAST) 920132451Sroberto peer_clear(peer, "BCST"); 921132451Sroberto else 922132451Sroberto peer_clear(peer, "INIT"); 92382498Sroberto if (mode_ntpdate) 92482498Sroberto peer_ntpdate++; 92554359Sroberto 92654359Sroberto /* 92754359Sroberto * Note time on statistics timers. 92854359Sroberto */ 92954359Sroberto peer->timereset = current_time; 93054359Sroberto peer->timereachable = current_time; 93154359Sroberto peer->timereceived = current_time; 932182007Sroberto 933290001Sglebius if (ISREFCLOCKADR(&peer->srcadr)) { 93454359Sroberto#ifdef REFCLOCK 93554359Sroberto /* 93654359Sroberto * We let the reference clock support do clock 93754359Sroberto * dependent initialization. This includes setting 93854359Sroberto * the peer timer, since the clock may have requirements 93954359Sroberto * for this. 94054359Sroberto */ 941290001Sglebius if (maxpoll == 0) 942290001Sglebius peer->maxpoll = peer->minpoll; 94354359Sroberto if (!refclock_newpeer(peer)) { 94454359Sroberto /* 94554359Sroberto * Dump it, something screwed up 94654359Sroberto */ 947182007Sroberto set_peerdstadr(peer, NULL); 948290001Sglebius free_peer(peer, 0); 949290001Sglebius return NULL; 95054359Sroberto } 951290001Sglebius#else /* REFCLOCK */ 952290001Sglebius msyslog(LOG_ERR, "refclock %s isn't supported. ntpd was compiled without refclock support.", 953290001Sglebius stoa(&peer->srcadr)); 954290001Sglebius set_peerdstadr(peer, NULL); 955290001Sglebius free_peer(peer, 0); 956290001Sglebius return NULL; 957290001Sglebius#endif /* REFCLOCK */ 95854359Sroberto } 95954359Sroberto 96054359Sroberto /* 96182498Sroberto * Put the new peer in the hash tables. 96254359Sroberto */ 963290001Sglebius hash = NTP_HASH_ADDR(&peer->srcadr); 964290001Sglebius LINK_SLIST(peer_hash[hash], peer, adr_link); 965290001Sglebius peer_hash_count[hash]++; 966290001Sglebius hash = peer->associd & NTP_HASH_MASK; 967290001Sglebius LINK_SLIST(assoc_hash[hash], peer, aid_link); 968290001Sglebius assoc_hash_count[hash]++; 969290001Sglebius LINK_SLIST(peer_list, peer, p_link); 970182007Sroberto 971290001Sglebius restrict_source(&peer->srcadr, 0, 0); 972290001Sglebius mprintf_event(PEVNT_MOBIL, peer, "assoc %d", peer->associd); 973290001Sglebius DPRINTF(1, ("newpeer: %s->%s mode %u vers %u poll %u %u flags 0x%x 0x%x ttl %u key %08x\n", 974290001Sglebius latoa(peer->dstadr), stoa(&peer->srcadr), peer->hmode, 975290001Sglebius peer->version, peer->minpoll, peer->maxpoll, peer->flags, 976290001Sglebius peer->cast_flags, peer->ttl, peer->keyid)); 977290001Sglebius return peer; 97854359Sroberto} 97954359Sroberto 98054359Sroberto 98154359Sroberto/* 982290001Sglebius * peer_clr_stats - clear peer module statistics counters 98354359Sroberto */ 98454359Srobertovoid 98554359Srobertopeer_clr_stats(void) 98654359Sroberto{ 98754359Sroberto findpeer_calls = 0; 98854359Sroberto assocpeer_calls = 0; 98954359Sroberto peer_allocations = 0; 99054359Sroberto peer_demobilizations = 0; 99154359Sroberto peer_timereset = current_time; 99254359Sroberto} 99354359Sroberto 994290001Sglebius 99554359Sroberto/* 996290001Sglebius * peer_reset - reset statistics counters 99754359Sroberto */ 99854359Srobertovoid 99954359Srobertopeer_reset( 100054359Sroberto struct peer *peer 100154359Sroberto ) 100254359Sroberto{ 1003290001Sglebius if (peer == NULL) 1004290001Sglebius return; 1005290001Sglebius 1006290001Sglebius peer->timereset = current_time; 100754359Sroberto peer->sent = 0; 100854359Sroberto peer->received = 0; 100954359Sroberto peer->processed = 0; 101054359Sroberto peer->badauth = 0; 101154359Sroberto peer->bogusorg = 0; 101254359Sroberto peer->oldpkt = 0; 101354359Sroberto peer->seldisptoolarge = 0; 1014290001Sglebius peer->selbroken = 0; 101554359Sroberto} 101654359Sroberto 101754359Sroberto 101854359Sroberto/* 1019290001Sglebius * peer_all_reset - reset all peer statistics counters 102054359Sroberto */ 102154359Srobertovoid 102254359Srobertopeer_all_reset(void) 102354359Sroberto{ 102454359Sroberto struct peer *peer; 102554359Sroberto 1026290001Sglebius for (peer = peer_list; peer != NULL; peer = peer->p_link) 102754359Sroberto peer_reset(peer); 102854359Sroberto} 102982498Sroberto 103082498Sroberto 103182498Sroberto/* 1032290001Sglebius * findmanycastpeer - find and return a manycastclient or pool 1033290001Sglebius * association matching a received response. 103482498Sroberto */ 103582498Srobertostruct peer * 103682498Srobertofindmanycastpeer( 1037290001Sglebius struct recvbuf *rbufp /* receive buffer pointer */ 103882498Sroberto ) 103982498Sroberto{ 1040290001Sglebius struct peer *peer; 104182498Sroberto struct pkt *pkt; 104282498Sroberto l_fp p_org; 104382498Sroberto 104482498Sroberto /* 1045290001Sglebius * This routine is called upon arrival of a server-mode response 1046290001Sglebius * to a manycastclient multicast solicitation, or to a pool 1047290001Sglebius * server unicast solicitation. Search the peer list for a 1048290001Sglebius * manycastclient association where the last transmit timestamp 1049290001Sglebius * matches the response packet's originate timestamp. There can 1050290001Sglebius * be multiple manycastclient associations, or multiple pool 1051290001Sglebius * solicitation assocations, so this assumes the transmit 1052290001Sglebius * timestamps are unique for such. 105382498Sroberto */ 105482498Sroberto pkt = &rbufp->recv_pkt; 1055290001Sglebius for (peer = peer_list; peer != NULL; peer = peer->p_link) 1056290001Sglebius if (MDF_SOLICIT_MASK & peer->cast_flags) { 1057290001Sglebius NTOHL_FP(&pkt->org, &p_org); 1058290001Sglebius if (L_ISEQU(&p_org, &peer->aorg)) 1059290001Sglebius break; 1060290001Sglebius } 106182498Sroberto 1062290001Sglebius return peer; 106382498Sroberto} 1064290001Sglebius 1065290001Sglebius/* peer_cleanup - clean peer list prior to shutdown */ 1066290001Sglebiusvoid peer_cleanup(void) 1067290001Sglebius{ 1068290001Sglebius struct peer *peer; 1069290001Sglebius associd_t assoc; 1070290001Sglebius 1071290001Sglebius for (assoc = initial_association_ID; assoc != current_association_ID; assoc++) { 1072290001Sglebius if (assoc != 0U) { 1073290001Sglebius peer = findpeerbyassoc(assoc); 1074290001Sglebius if (peer != NULL) 1075290001Sglebius unpeer(peer); 1076290001Sglebius } 1077290001Sglebius } 1078290001Sglebius peer = findpeerbyassoc(current_association_ID); 1079290001Sglebius if (peer != NULL) 1080290001Sglebius unpeer(peer); 1081290001Sglebius} 1082