diff options
author | roberto <roberto@FreeBSD.org> | 2008-08-22 15:58:00 +0000 |
---|---|---|
committer | roberto <roberto@FreeBSD.org> | 2008-08-22 15:58:00 +0000 |
commit | b85c7169a740b2edf0106ad59fdaa1b0160f823c (patch) | |
tree | 2b9fb7f64eacb322e95695e412c923e97ba33e88 /contrib/ntp/ntpd/ntp_peer.c | |
parent | 1d197cfe9feac6bc29537d8e53c30b6435937b95 (diff) | |
parent | 7a6072eb585696f8856cd498c3fd194cf49f14c6 (diff) | |
download | FreeBSD-src-b85c7169a740b2edf0106ad59fdaa1b0160f823c.zip FreeBSD-src-b85c7169a740b2edf0106ad59fdaa1b0160f823c.tar.gz |
Merge ntpd & friends 4.2.4p5 from vendor/ntp/dist into head. Next commit
will update usr.sbin/ntp to match this.
MFC after: 2 weeks
Diffstat (limited to 'contrib/ntp/ntpd/ntp_peer.c')
-rw-r--r-- | contrib/ntp/ntpd/ntp_peer.c | 421 |
1 files changed, 296 insertions, 125 deletions
diff --git a/contrib/ntp/ntpd/ntp_peer.c b/contrib/ntp/ntpd/ntp_peer.c index cf8a600..deeec5b 100644 --- a/contrib/ntp/ntpd/ntp_peer.c +++ b/contrib/ntp/ntpd/ntp_peer.c @@ -10,10 +10,15 @@ #include "ntpd.h" #include "ntp_stdlib.h" +#include <ntp_random.h> #ifdef OPENSSL #include "openssl/rand.h" #endif /* OPENSSL */ +#ifdef SYS_WINNT +extern int accept_wildcard_if_for_winnt; +#endif + /* * Table of valid association combinations * --------------------------------------- @@ -21,14 +26,12 @@ * packet->mode * peer->mode | UNSPEC ACTIVE PASSIVE CLIENT SERVER BCAST * ---------- | --------------------------------------------- - * NO_PEER | e 1 e 1 1 1 + * NO_PEER | e 1 0 1 1 1 * ACTIVE | e 1 1 0 0 0 * PASSIVE | e 1 e 0 0 0 * CLIENT | e 0 0 0 1 1 * SERVER | e 0 0 0 0 0 - * BCAST | e 0 0 0 0 0 - * CONTROL | e 0 0 0 0 0 - * PRIVATE | e 0 0 0 0 0 + * BCAST | e 0 0 0 0 0 * BCLIENT | e 0 0 0 e 1 * * One point to note here: a packet in BCAST mode can potentially match @@ -38,11 +41,13 @@ * circumvent that problem by requiring that the first b(m)roadcast * received after the change back to BCLIENT mode sets the clock. */ +#define AM_MODES 7 /* number of rows and columns */ +#define NO_PEER 0 /* action when no peer is found */ int AM[AM_MODES][AM_MODES] = { /* { UNSPEC, ACTIVE, PASSIVE, CLIENT, SERVER, BCAST } */ -/*NONE*/{ AM_ERR, AM_NEWPASS, AM_ERR, AM_FXMIT, AM_MANYCAST, AM_NEWBCL}, +/*NONE*/{ AM_ERR, AM_NEWPASS, AM_NOMATCH, AM_FXMIT, AM_MANYCAST, AM_NEWBCL}, /*A*/ { AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, @@ -54,11 +59,7 @@ int AM[AM_MODES][AM_MODES] = { /*BCST*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, -/*CNTL*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, - -/*PRIV*/{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}, - -/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_ERR, AM_PROCPKT}, +/*BCL*/ { AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT}, }; #define MATCH_ASSOC(x,y) AM[(x)][(y)] @@ -74,10 +75,10 @@ int AM[AM_MODES][AM_MODES] = { /* * Peer hash tables */ -struct peer *peer_hash[HASH_SIZE]; /* peer hash table */ -int peer_hash_count[HASH_SIZE]; /* peers in each bucket */ -struct peer *assoc_hash[HASH_SIZE]; /* association ID hash table */ -int assoc_hash_count[HASH_SIZE]; /* peers in each bucket */ +struct peer *peer_hash[NTP_HASH_SIZE]; /* peer hash table */ +int peer_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */ +struct peer *assoc_hash[NTP_HASH_SIZE]; /* association ID hash table */ +int assoc_hash_count[NTP_HASH_SIZE]; /* peers in each bucket */ static struct peer *peer_free; /* peer structures free list */ int peer_free_count; /* count of free structures */ @@ -102,10 +103,12 @@ u_long assocpeer_calls; /* calls to findpeerbyassoc */ u_long peer_allocations; /* allocations from free list */ u_long peer_demobilizations; /* structs freed to free list */ int total_peer_structs; /* peer structs */ -int peer_associations; /* active associations */ +int peer_associations; /* mobilized associations */ +int peer_preempt; /* preemptable associations */ static struct peer init_peer_alloc[INIT_PEER_ALLOC]; /* init alloc */ -static void getmorepeermem P((void)); +static void getmorepeermem P((void)); +static struct interface *select_peerinterface P((struct peer *, struct sockaddr_storage *, struct interface *, u_char)); /* * init_peer - initialize peer data structures and counters @@ -121,7 +124,7 @@ init_peer(void) /* * Clear hash table and counters. */ - for (i = 0; i < HASH_SIZE; i++) { + for (i = 0; i < NTP_HASH_SIZE; i++) { peer_hash[i] = 0; peer_hash_count[i] = 0; assoc_hash[i] = 0; @@ -148,9 +151,7 @@ init_peer(void) /* * Initialize our first association ID */ - current_association_ID = (associd_t)ranp2(16); - if (current_association_ID == 0) - current_association_ID = 1; + while ((current_association_ID = ntp_random() & 0xffff) == 0); } @@ -193,7 +194,7 @@ findexistingpeer( * same peer through different interfaces in the hash table. */ if (start_peer == 0) - peer = peer_hash[HASH_ADDR(addr)]; + peer = peer_hash[NTP_HASH_ADDR(addr)]; else peer = start_peer->next; @@ -218,19 +219,18 @@ struct peer * findpeer( struct sockaddr_storage *srcadr, struct interface *dstadr, - int fd, - int pkt_mode, - int *action + int pkt_mode, + int *action ) { register struct peer *peer; int hash; findpeer_calls++; - hash = HASH_ADDR(srcadr); + hash = NTP_HASH_ADDR(srcadr); for (peer = peer_hash[hash]; peer != NULL; peer = peer->next) { - if (SOCKCMP(srcadr, &peer->srcadr) - && NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) { + if (SOCKCMP(srcadr, &peer->srcadr) && + NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) { /* * if the association matching rules determine @@ -240,14 +240,6 @@ findpeer( *action = MATCH_ASSOC(peer->hmode, pkt_mode); /* - * Sigh! Check if BCLIENT peer in client - * server mode, else return error. - */ - if ((*action == AM_POSSBCL) && !(peer->flags & - FLAG_MCAST)) - *action = AM_ERR; - - /* * if an error was returned, exit back right * here. */ @@ -269,7 +261,9 @@ findpeer( *action = MATCH_ASSOC(NO_PEER, pkt_mode); return ((struct peer *)0); } - peer->dstadr = dstadr; + + set_peerdstadr(peer, dstadr); + return (peer); } @@ -286,7 +280,7 @@ findpeerbyassoc( assocpeer_calls++; - hash = assoc & HASH_MASK; + hash = assoc & NTP_HASH_MASK; for (peer = assoc_hash[hash]; peer != 0; peer = peer->ass_next) { if (assoc == peer->associd) @@ -309,15 +303,13 @@ clear_all(void) * This routine is called when the clock is stepped, and so all * previously saved time values are untrusted. */ - for (n = 0; n < HASH_SIZE; n++) { + for (n = 0; n < NTP_HASH_SIZE; n++) { for (peer = peer_hash[n]; peer != 0; peer = next_peer) { next_peer = peer->next; - if (peer->flags & FLAG_CONFIG) { - if (!(peer->cast_flags & (MDF_ACAST | - MDF_MCAST | MDF_BCAST))) - peer_clear(peer, "STEP"); - } else { - unpeer(peer); + if (!(peer->cast_flags & (MDF_ACAST | MDF_MCAST | + MDF_BCAST))) { + peer->hpoll = peer->minpoll; + peer_clear(peer, "STEP"); } } } @@ -353,13 +345,19 @@ unpeer( #endif /* OPENSSL */ #ifdef DEBUG if (debug) - printf("demobilize %u %d\n", peer_to_remove->associd, - peer_associations); + printf("demobilize %u %d %d\n", peer_to_remove->associd, + peer_associations, peer_preempt); #endif - peer_clear(peer_to_remove, "NULL"); - hash = HASH_ADDR(&peer_to_remove->srcadr); + set_peerdstadr(peer_to_remove, NULL); + + /* XXXMEMLEAK? peer_clear->crypto allocation */ + + hash = NTP_HASH_ADDR(&peer_to_remove->srcadr); peer_hash_count[hash]--; peer_demobilizations++; + peer_associations--; + if (peer_to_remove->flags & FLAG_PREEMPT) + peer_preempt--; #ifdef REFCLOCK /* * If this peer is actually a clock, shut it down first @@ -389,7 +387,7 @@ unpeer( /* * Remove him from the association hash as well. */ - hash = peer_to_remove->associd & HASH_MASK; + hash = peer_to_remove->associd & NTP_HASH_MASK; assoc_hash_count[hash]--; if (assoc_hash[hash] == peer_to_remove) assoc_hash[hash] = peer_to_remove->ass_next; @@ -412,7 +410,6 @@ unpeer( peer_to_remove->next = peer_free; peer_free = peer_to_remove; peer_free_count++; - peer_associations--; } @@ -440,13 +437,18 @@ peer_config( * First search from the beginning for an association with given * remote address and mode. If an interface is given, search * from there to find the association which matches that - * destination. + * destination. If the given interface is "any", track down + * the actual interface, because that's what gets put into the + * peer structure. */ peer = findexistingpeer(srcadr, (struct peer *)0, hmode); if (dstadr != 0) { while (peer != 0) { if (peer->dstadr == dstadr) break; + if (dstadr == ANY_INTERFACE_CHOOSE(srcadr) && + peer->dstadr == findinterface(srcadr)) + break; peer = findexistingpeer(srcadr, peer, hmode); } } @@ -525,6 +527,197 @@ peer_config( return (peer); } +/* + * setup peer dstadr field keeping it in sync with the interface structures + */ +void +set_peerdstadr(struct peer *peer, struct interface *interface) +{ + if (peer->dstadr != interface) { + if (interface != NULL && + (peer->cast_flags & MDF_BCLNT) && + (interface->flags & INT_MCASTIF) && + peer->burst) { + /* + * don't accept updates to a true multicast reception + * interface while a BCLNT peer is running it's + * unicast protocol + */ + return; + } + + if (peer->dstadr != NULL) + { + peer->dstadr->peercnt--; + ISC_LIST_UNLINK_TYPE(peer->dstadr->peers, peer, ilink, struct peer); + } + + DPRINTF(4, ("set_peerdstadr(%s): change interface from %s to %s\n", + stoa(&peer->srcadr), + (peer->dstadr != NULL) ? stoa(&peer->dstadr->sin) : "<null>", + (interface != NULL) ? stoa(&interface->sin) : "<null>")); + + peer->dstadr = interface; + + if (peer->dstadr != NULL) + { + ISC_LIST_APPEND(peer->dstadr->peers, peer, ilink); + peer->dstadr->peercnt++; + } + } +} + +/* + * attempt to re-rebind interface if necessary + */ +static void +peer_refresh_interface(struct peer *peer) +{ + struct interface *niface, *piface; + + niface = select_peerinterface(peer, &peer->srcadr, NULL, peer->cast_flags); + +#ifdef DEBUG + if (debug > 3) + { + printf( + "peer_refresh_interface: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %d key %08x: new interface: ", + peer->dstadr == NULL ? "<null>" : stoa(&peer->dstadr->sin), + stoa(&peer->srcadr), + peer->hmode, peer->version, peer->minpoll, + peer->maxpoll, peer->flags, peer->cast_flags, + peer->ttl, peer->keyid); + if (niface != NULL) + { + printf("fd=%d, bfd=%d, name=%.16s, flags=0x%x, scope=%d, ", + niface->fd, + niface->bfd, + niface->name, + niface->flags, + niface->scopeid); + /* Leave these as three printf calls. */ + printf(", sin=%s", + stoa((&niface->sin))); + if (niface->flags & INT_BROADCAST) + printf(", bcast=%s,", + stoa((&niface->bcast))); + printf(", mask=%s\n", + stoa((&niface->mask))); + } + else + { + printf("<NONE>\n"); + } + } +#endif + + piface = peer->dstadr; + + set_peerdstadr(peer, niface); + + if (peer->dstadr) { + /* + * clear crypto if we change the local address + */ + if (peer->dstadr != piface && !(peer->cast_flags & MDF_BCLNT)) { + peer_crypto_clear(peer); + } + + /* + * Broadcast needs the socket enabled for broadcast + */ + if (peer->cast_flags & MDF_BCAST) { + enable_broadcast(peer->dstadr, &peer->srcadr); + } + + /* + * Multicast needs the socket interface enabled for multicast + */ + if (peer->cast_flags & MDF_MCAST) { + enable_multicast_if(peer->dstadr, &peer->srcadr); + } + } +} + +/* + * refresh_all_peerinterfaces - see that all interface bindings are up to date + */ +void +refresh_all_peerinterfaces(void) +{ + struct peer *peer, *next_peer; + int n; + + /* + * this is called when the interface list has changed + * give all peers a chance to find a better interface + */ + for (n = 0; n < NTP_HASH_SIZE; n++) { + for (peer = peer_hash[n]; peer != 0; peer = next_peer) { + next_peer = peer->next; + peer_refresh_interface(peer); + } + } +} + + +/* + * find an interface suitable for the src address + */ +static struct interface * +select_peerinterface(struct peer *peer, struct sockaddr_storage *srcadr, struct interface *dstadr, u_char cast_flags) +{ + struct interface *interface; + + /* + * Initialize the peer structure and dance the interface jig. + * Reference clocks step the loopback waltz, the others + * squaredance around the interface list looking for a buddy. If + * the dance peters out, there is always the wildcard interface. + * This might happen in some systems and would preclude proper + * operation with public key cryptography. + */ + if (ISREFCLOCKADR(srcadr)) + interface = loopback_interface; + else + if (cast_flags & (MDF_BCLNT | MDF_ACAST | MDF_MCAST | MDF_BCAST)) { + interface = findbcastinter(srcadr); +#ifdef DEBUG + if (debug > 3) { + if (interface != NULL) + printf("Found *-cast interface address %s, for address %s\n", + stoa(&(interface)->sin), stoa(srcadr)); + else + printf("No *-cast local address found for address %s\n", + stoa(srcadr)); + } +#endif + /* + * If it was a multicast packet, findbcastinter() may not + * find it, so try a little harder. + */ + if (interface == ANY_INTERFACE_CHOOSE(srcadr)) + interface = findinterface(srcadr); + } + else if (dstadr != NULL && dstadr != ANY_INTERFACE_CHOOSE(srcadr)) + interface = dstadr; + else + interface = findinterface(srcadr); + + /* + * we do not bind to the wildcard interfaces for output + * as our (network) source address would be undefined and + * crypto will not work without knowing the own transmit address + */ + if (interface != NULL && interface->flags & INT_WILDCARD) +#ifdef SYS_WINNT + if ( !accept_wildcard_if_for_winnt ) +#endif + interface = NULL; + + + return interface; +} /* * newpeer - initialize a new peer association @@ -559,6 +752,8 @@ newpeer( peer_free = peer->next; peer_free_count--; peer_associations++; + if (flags & FLAG_PREEMPT) + peer_preempt++; memset((char *)peer, 0, sizeof(struct peer)); /* @@ -568,34 +763,41 @@ newpeer( if (++current_association_ID == 0) ++current_association_ID; - /* - * Initialize the peer structure and dance the interface jig. - * Reference clocks step the loopback waltz, the others - * squaredance around the interface list looking for a buddy. If - * the dance peters out, there is always the wildcard interface. - * This might happen in some systems and would preclude proper - * operation with public key cryptography. - */ - if (ISREFCLOCKADR(srcadr)) - peer->dstadr = loopback_interface; - else if (cast_flags & (MDF_BCLNT | MDF_ACAST | MDF_MCAST | MDF_BCAST)) { - peer->dstadr = findbcastinter(srcadr); - /* - * If it was a multicast packet, findbcastinter() may not - * find it, so try a little harder. - */ - if (peer->dstadr == ANY_INTERFACE_CHOOSE(srcadr)) - peer->dstadr = findinterface(srcadr); - } else if (dstadr != NULL && dstadr != ANY_INTERFACE_CHOOSE(srcadr)) - peer->dstadr = dstadr; - else - peer->dstadr = findinterface(srcadr); + DPRINTF(3, ("newpeer: cast flags: 0x%x for address: %s\n", + cast_flags, stoa(srcadr))); + + ISC_LINK_INIT(peer, ilink); /* set up interface link chain */ peer->srcadr = *srcadr; + set_peerdstadr(peer, select_peerinterface(peer, srcadr, dstadr, + cast_flags)); peer->hmode = (u_char)hmode; peer->version = (u_char)version; peer->minpoll = (u_char)max(NTP_MINPOLL, minpoll); peer->maxpoll = (u_char)min(NTP_MAXPOLL, maxpoll); peer->flags = flags; +#ifdef DEBUG + if (debug > 2) { + if (peer->dstadr) + printf("newpeer: using fd %d and our addr %s\n", + peer->dstadr->fd, + stoa(&peer->dstadr->sin)); + else + printf("newpeer: local interface currently not bound\n"); + } +#endif + + /* + * Broadcast needs the socket enabled for broadcast + */ + if (cast_flags & MDF_BCAST && peer->dstadr) { + enable_broadcast(peer->dstadr, srcadr); + } + /* + * Multicast needs the socket interface enabled for multicast + */ + if (cast_flags & MDF_MCAST && peer->dstadr) { + enable_multicast_if(peer->dstadr, srcadr); + } if (key != 0) peer->flags |= FLAG_AUTHENABLE; if (key > NTP_MAXKEY) @@ -604,6 +806,7 @@ newpeer( peer->ttl = (u_char)ttl; peer->keyid = key; peer->precision = sys_precision; + peer->hpoll = peer->minpoll; if (cast_flags & MDF_ACAST) peer_clear(peer, "ACST"); else if (cast_flags & MDF_MCAST) @@ -621,8 +824,10 @@ newpeer( peer->timereset = current_time; peer->timereachable = current_time; peer->timereceived = current_time; + #ifdef REFCLOCK if (ISREFCLOCKADR(&peer->srcadr)) { + /* * We let the reference clock support do clock * dependent initialization. This includes setting @@ -633,6 +838,8 @@ newpeer( /* * Dump it, something screwed up */ + set_peerdstadr(peer, NULL); + peer->next = peer_free; peer_free = peer; peer_free_count++; @@ -644,34 +851,30 @@ newpeer( /* * Put the new peer in the hash tables. */ - i = HASH_ADDR(&peer->srcadr); + i = NTP_HASH_ADDR(&peer->srcadr); peer->next = peer_hash[i]; peer_hash[i] = peer; peer_hash_count[i]++; - i = peer->associd & HASH_MASK; + i = peer->associd & NTP_HASH_MASK; peer->ass_next = assoc_hash[i]; assoc_hash[i] = peer; assoc_hash_count[i]++; + #ifdef OPENSSL if (peer->flags & FLAG_SKEY) { sprintf(statstr, "newpeer %d", peer->associd); record_crypto_stats(&peer->srcadr, statstr); -#ifdef DEBUG - if (debug) - printf("peer: %s\n", statstr); -#endif + DPRINTF(1, ("peer: %s\n", statstr)); } #endif /* OPENSSL */ -#ifdef DEBUG - if (debug) - printf( - "newpeer: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %d key %08x\n", - peer->dstadr == NULL ? "null" : stoa(&peer->dstadr->sin), + + DPRINTF(1, ("newpeer: %s->%s mode %d vers %d poll %d %d flags 0x%x 0x%x ttl %d key %08x\n", + peer->dstadr == NULL ? "<null>" : stoa(&peer->dstadr->sin), stoa(&peer->srcadr), peer->hmode, peer->version, peer->minpoll, peer->maxpoll, peer->flags, peer->cast_flags, - peer->ttl, peer->keyid); -#endif + peer->ttl, peer->keyid)); + return (peer); } @@ -749,8 +952,6 @@ peer_reset( peer->bogusorg = 0; peer->oldpkt = 0; peer->seldisptoolarge = 0; - peer->selbroken = 0; - peer->rank = 0; peer->timereset = current_time; } @@ -764,7 +965,7 @@ peer_all_reset(void) struct peer *peer; int hash; - for (hash = 0; hash < HASH_SIZE; hash++) + for (hash = 0; hash < NTP_HASH_SIZE; hash++) for (peer = peer_hash[hash]; peer != 0; peer = peer->next) peer_reset(peer); } @@ -790,13 +991,13 @@ expire_all(void) */ if (!crypto_flags) return; - for (n = 0; n < HASH_SIZE; n++) { + + for (n = 0; n < NTP_HASH_SIZE; n++) { for (peer = peer_hash[n]; peer != 0; peer = next_peer) { next_peer = peer->next; if (!(peer->flags & FLAG_SKEY)) { continue; - } else if (peer->cast_flags & MDF_ACAST) { - peer_clear(peer, "ACST"); + } else if (peer->hmode == MODE_ACTIVE || peer->hmode == MODE_PASSIVE) { key_expire(peer); @@ -808,7 +1009,6 @@ expire_all(void) } RAND_bytes((u_char *)&sys_private, 4); crypto_update(); - resetmanycast(); } #endif /* OPENSSL */ @@ -827,14 +1027,14 @@ findmanycastpeer( int i; /* - * This routine is called upon arrival of a client-mode message - * from a manycast server. Search the peer list for a manycast + * This routine is called upon arrival of a server-mode message + * from a manycast client. Search the peer list for a manycast * client association where the last transmit timestamp matches * the originate timestamp. This assumes the transmit timestamps * for possibly more than one manycast association are unique. */ pkt = &rbufp->recv_pkt; - for (i = 0; i < HASH_SIZE; i++) { + for (i = 0; i < NTP_HASH_SIZE; i++) { if (peer_hash_count[i] == 0) continue; @@ -849,32 +1049,3 @@ findmanycastpeer( } return (NULL); } - - -/* - * resetmanycast - reset all manycast clients - */ -void -resetmanycast(void) -{ - register struct peer *peer; - int i; - - /* - * This routine is called when the number of client associations - * falls below the minimum. Search the peer list for manycast - * client associations and reset the ttl and poll interval. - */ - for (i = 0; i < HASH_SIZE; i++) { - if (peer_hash_count[i] == 0) - continue; - - for (peer = peer_hash[i]; peer != 0; peer = - peer->next) { - if (peer->cast_flags & MDF_ACAST) { - peer->ttl = 0; - poll_update(peer, 0); - } - } - } -} |