#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <sys/types.h>
#include "ntpd.h"
#include "ntp_stdlib.h"
#ifdef __APPLE__
__private_extern__
#endif
int AM[AM_MODES][AM_MODES] = {
{ AM_ERR, AM_NEWPASS, AM_ERR, AM_FXMIT, AM_MANYCAST, AM_NEWBCL},
{ AM_ERR, AM_PROCPKT, AM_PROCPKT, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
{ AM_ERR, AM_PROCPKT, AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_PROCPKT, AM_POSSBCL},
{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH},
{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_ERR, AM_PROCPKT},
{ AM_ERR, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH, AM_NOMATCH}
};
#define MATCH_ASSOC(x,y) AM[(x)][(y)]
struct peer *peer_hash[HASH_SIZE];
int peer_hash_count[HASH_SIZE];
struct peer *assoc_hash[HASH_SIZE];
int assoc_hash_count[HASH_SIZE];
static struct peer *peer_free;
int peer_free_count;
static u_short current_association_ID;
#define INIT_PEER_ALLOC 15
#define INC_PEER_ALLOC 5
u_long peer_timereset;
u_long findpeer_calls;
static u_long assocpeer_calls;
u_long peer_allocations;
u_long peer_demobilizations;
int total_peer_structs;
int peer_associations;
static struct peer init_peer_alloc[INIT_PEER_ALLOC];
static u_long init_peer_starttime;
static void getmorepeermem P((void));
static void key_expire P((struct peer *));
void
init_peer(void)
{
register int i;
for (i = 0; i < HASH_SIZE; i++) {
peer_hash[i] = 0;
peer_hash_count[i] = 0;
assoc_hash[i] = 0;
assoc_hash_count[i] = 0;
}
findpeer_calls = peer_allocations = 0;
assocpeer_calls = peer_demobilizations = 0;
init_peer_starttime = 0;
peer_free = 0;
for (i = 0; i < INIT_PEER_ALLOC; i++) {
init_peer_alloc[i].next = peer_free;
peer_free = &init_peer_alloc[i];
}
total_peer_structs = INIT_PEER_ALLOC;
peer_free_count = INIT_PEER_ALLOC;
current_association_ID = (u_short)ranp2(16);
if (current_association_ID == 0)
current_association_ID = 1;
}
static void
getmorepeermem(void)
{
register int i;
register struct peer *peer;
peer = (struct peer *)emalloc(INC_PEER_ALLOC*sizeof(struct peer));
for (i = 0; i < INC_PEER_ALLOC; i++) {
peer->next = peer_free;
peer_free = peer;
peer++;
}
total_peer_structs += INC_PEER_ALLOC;
peer_free_count += INC_PEER_ALLOC;
}
struct peer *
findexistingpeer(
struct sockaddr_in *addr,
struct peer *start_peer,
int mode
)
{
register struct peer *peer;
if (start_peer == 0)
peer = peer_hash[HASH_ADDR(addr)];
else
peer = start_peer->next;
while (peer != 0) {
if (NSRCADR(addr) == NSRCADR(&peer->srcadr)
&& NSRCPORT(addr) == NSRCPORT(&peer->srcadr)) {
if (mode == -1)
return peer;
else if (peer->hmode == mode)
break;
}
peer = peer->next;
}
return peer;
}
struct peer *
findpeer(
struct sockaddr_in *srcadr,
struct interface *dstadr,
int fd,
int pkt_mode,
int *action
)
{
register struct peer *peer;
int hash;
findpeer_calls++;
hash = HASH_ADDR(srcadr);
for (peer = peer_hash[hash]; peer != 0; peer = peer->next) {
if (NSRCADR(srcadr) == NSRCADR(&peer->srcadr)
&& NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) {
*action = MATCH_ASSOC(peer->hmode, pkt_mode);
if ((*action == AM_POSSBCL) &&
!(peer->cast_flags & FLAG_MCAST1)) {
*action = AM_ERR;
}
if (*action == AM_ERR)
return (struct peer *)0;
if (*action != AM_NOMATCH)
break;
}
}
#ifdef DEBUG
if (debug)
printf("pkt_mode %d action %d\n", pkt_mode, *action);
#endif
if (peer == 0) {
*action = MATCH_ASSOC(NO_PEER, pkt_mode);
#ifdef DEBUG
if (debug)
printf("pkt_mode %d action %d\n", pkt_mode, *action);
#endif
return (struct peer *)0;
}
if ((peer->dstadr == any_interface))
peer->dstadr = dstadr;
return peer;
}
struct peer *
findpeerbyassoc(
int assoc
)
{
register struct peer *peer;
int hash;
assocpeer_calls++;
hash = assoc & HASH_MASK;
for (peer = assoc_hash[hash]; peer != 0; peer = peer->ass_next) {
if ((u_short)assoc == peer->associd)
return peer;
}
return (struct peer *)0;
}
struct peer *
findmanycastpeer(
l_fp *p_org
)
{
register struct peer *peer;
register struct peer *manycast_peer = 0;
int i = 0;
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->flags & FLAG_CONFIG) {
if (L_ISEQU(&peer->xmt, p_org))
return peer;
else
manycast_peer = peer;
}
}
}
return manycast_peer;
}
static void
key_expire(
struct peer *peer
)
{
int i;
if (peer->keylist != 0) {
for (i = 0; i <= peer->keynumber; i++)
authtrust(peer->keylist[i], 0);
free(peer->keylist);
peer->keylist = 0;
}
if (peer->keyid > NTP_MAXKEY) {
authtrust(peer->keyid, 0);
peer->keyid = 0;
}
}
void
key_expire_all(
)
{
struct peer *peer, *next_peer;
int n;
for (n = 0; n < HASH_SIZE; n++) {
for (peer = peer_hash[n]; peer != 0; peer = next_peer) {
next_peer = peer->next;
key_expire(peer);
}
}
sys_private = (u_long)RANDOM & 0xffffffff;
#ifdef DEBUG
if (debug)
printf("key_expire_all: at %lu private %08lx\n",
current_time, sys_private);
#endif
}
void
unpeer(
struct peer *peer_to_remove
)
{
int hash;
#ifdef DEBUG
if (debug > 1)
printf("demobilize %u\n", peer_to_remove->associd);
#endif
key_expire(peer_to_remove);
hash = HASH_ADDR(&peer_to_remove->srcadr);
peer_hash_count[hash]--;
peer_demobilizations++;
peer_associations--;
#ifdef REFCLOCK
if (peer_to_remove->flags & FLAG_REFCLOCK)
refclock_unpeer(peer_to_remove);
#endif
peer_to_remove->action = 0;
if (peer_hash[hash] == peer_to_remove)
peer_hash[hash] = peer_to_remove->next;
else {
register struct peer *peer;
peer = peer_hash[hash];
while (peer != 0 && peer->next != peer_to_remove)
peer = peer->next;
if (peer == 0) {
peer_hash_count[hash]++;
msyslog(LOG_ERR, "peer struct for %s not in table!",
ntoa(&peer->srcadr));
} else {
peer->next = peer_to_remove->next;
}
}
hash = peer_to_remove->associd & HASH_MASK;
assoc_hash_count[hash]--;
if (assoc_hash[hash] == peer_to_remove)
assoc_hash[hash] = peer_to_remove->ass_next;
else {
register struct peer *peer;
peer = assoc_hash[hash];
while (peer != 0 && peer->ass_next != peer_to_remove)
peer = peer->ass_next;
if (peer == 0) {
assoc_hash_count[hash]++;
msyslog(LOG_ERR,
"peer struct for %s not in association table!",
ntoa(&peer->srcadr));
} else {
peer->ass_next = peer_to_remove->ass_next;
}
}
peer_to_remove->next = peer_free;
peer_free = peer_to_remove;
peer_free_count++;
}
struct peer *
peer_config(
struct sockaddr_in *srcadr,
struct interface *dstadr,
int hmode,
int version,
int minpoll,
int maxpoll,
int flags,
int ttl,
u_long key
)
{
register struct peer *peer;
peer = findexistingpeer(srcadr, (struct peer *)0, hmode);
if (dstadr != 0) {
while (peer != 0) {
if (peer->dstadr == dstadr)
break;
peer = findexistingpeer(srcadr, peer, hmode);
}
}
if (peer != 0) {
peer->hmode = (u_char)hmode;
peer->version = (u_char)version;
peer->minpoll = (u_char)minpoll;
peer->maxpoll = (u_char)maxpoll;
peer->hpoll = peer->minpoll;
peer->ppoll = peer->minpoll;
peer->flags = flags | FLAG_CONFIG |
(peer->flags & FLAG_REFCLOCK);
peer->cast_flags = (hmode == MODE_BROADCAST) ?
IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)) ? MDF_MCAST : MDF_BCAST : MDF_UCAST;
peer->ttl = (u_char)ttl;
peer->keyid = key;
peer->keynumber = 0;
return peer;
}
peer = newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll,
ttl, key);
if (peer != 0) {
peer->flags |= flags | FLAG_CONFIG;
#ifdef DEBUG
if (debug)
printf("peer_config: %s mode %d vers %d min %d max %d flags 0x%04x ttl %d key %lu\n",
ntoa(&peer->srcadr), peer->hmode, peer->version,
peer->minpoll, peer->maxpoll, peer->flags,
peer->ttl, peer->keyid);
#endif
}
return peer;
}
struct peer *
newpeer(
struct sockaddr_in *srcadr,
struct interface *dstadr,
int hmode,
int version,
int minpoll,
int maxpoll,
int ttl,
u_long key
)
{
register struct peer *peer;
register int i;
if (peer_free_count == 0)
getmorepeermem();
peer = peer_free;
peer_free = peer->next;
peer_free_count--;
peer_associations++;
memset((char *)peer, 0, sizeof(struct peer));
peer->srcadr = *srcadr;
if (dstadr != 0)
peer->dstadr = dstadr;
else if (hmode == MODE_BROADCAST)
peer->dstadr = findbcastinter(srcadr);
else
peer->dstadr = any_interface;
peer->cast_flags = (hmode == MODE_BROADCAST) ?
(IN_CLASSD(ntohl(srcadr->sin_addr.s_addr))) ? MDF_MCAST :
MDF_BCAST : (hmode == MODE_BCLIENT || hmode == MODE_MCLIENT) ?
(peer->dstadr->flags & INT_MULTICAST) ? MDF_MCAST : MDF_BCAST :
MDF_UCAST;
if (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)) && hmode == MODE_CLIENT)
peer->cast_flags = MDF_ACAST;
peer->hmode = (u_char)hmode;
peer->keyid = key;
peer->version = (u_char)version;
peer->minpoll = (u_char)minpoll;
peer->maxpoll = (u_char)maxpoll;
peer->hpoll = peer->minpoll;
peer->ppoll = peer->minpoll;
peer->ttl = ttl;
peer->leap = LEAP_NOTINSYNC;
peer->precision = sys_precision;
peer->variance = MAXDISPERSE;
peer->epoch = current_time;
peer->stratum = STRATUM_UNSPEC;
peer_clear(peer);
peer->update = peer->outdate = current_time;
peer->nextdate = peer->outdate + RANDPOLL(NTP_MINPOLL);
if (peer->flags & FLAG_BURST)
peer->burst = NTP_SHIFT;
peer->associd = current_association_ID;
if (++current_association_ID == 0)
++current_association_ID;
peer->timereset = current_time;
peer->timereachable = current_time;
peer->timereceived = current_time;
#ifdef REFCLOCK
if (ISREFCLOCKADR(&peer->srcadr)) {
if (!refclock_newpeer(peer)) {
peer->next = peer_free;
peer_free = peer;
peer_free_count++;
return 0;
}
}
#endif
i = HASH_ADDR(&peer->srcadr);
peer->next = peer_hash[i];
peer_hash[i] = peer;
peer_hash_count[i]++;
i = peer->associd & HASH_MASK;
peer->ass_next = assoc_hash[i];
assoc_hash[i] = peer;
assoc_hash_count[i]++;
#ifdef DEBUG
if (debug > 1)
printf("mobilize %u next %lu\n", peer->associd,
peer->nextdate - peer->outdate);
#endif
return peer;
}
int
peer_unconfig(
struct sockaddr_in *srcadr,
struct interface *dstadr,
int mode
)
{
register struct peer *peer;
int num_found;
num_found = 0;
peer = findexistingpeer(srcadr, (struct peer *)0, mode);
while (peer != 0) {
if (peer->flags & FLAG_CONFIG
&& (dstadr == 0 || peer->dstadr == dstadr)) {
num_found++;
if (peer->hmode == MODE_ACTIVE
&& peer->pmode == MODE_ACTIVE) {
peer->hmode = MODE_PASSIVE;
peer->flags &= ~FLAG_CONFIG;
} else {
unpeer(peer);
peer = 0;
}
}
peer = findexistingpeer(srcadr, peer, mode);
}
return num_found;
}
void
peer_config_manycast(
struct peer *peer1,
struct peer *peer2
)
{
peer2->cast_flags = MDF_ACAST;
peer2->xmt = peer1->xmt;
}
void
peer_clr_stats(void)
{
findpeer_calls = 0;
assocpeer_calls = 0;
peer_allocations = 0;
peer_demobilizations = 0;
peer_timereset = current_time;
}
void
peer_reset(
struct peer *peer
)
{
if (peer == 0)
return;
peer->sent = 0;
peer->received = 0;
peer->processed = 0;
peer->badauth = 0;
peer->bogusorg = 0;
peer->oldpkt = 0;
peer->seldisptoolarge = 0;
peer->selbroken = 0;
peer->seltooold = 0;
peer->timereset = current_time;
}
void
peer_all_reset(void)
{
struct peer *peer;
int hash;
for (hash = 0; hash < HASH_SIZE; hash++)
for (peer = peer_hash[hash]; peer != 0; peer = peer->next)
peer_reset(peer);
}