154359Sroberto/* 2132451Sroberto * ntp_monitor - monitor ntpd statistics 354359Sroberto */ 454359Sroberto#ifdef HAVE_CONFIG_H 582498Sroberto# include <config.h> 654359Sroberto#endif 754359Sroberto 854359Sroberto#include "ntpd.h" 954359Sroberto#include "ntp_io.h" 1054359Sroberto#include "ntp_if.h" 1154359Sroberto#include "ntp_stdlib.h" 12182007Sroberto#include <ntp_random.h> 1354359Sroberto 1482498Sroberto#include <stdio.h> 1582498Sroberto#include <signal.h> 1682498Sroberto#ifdef HAVE_SYS_IOCTL_H 1782498Sroberto# include <sys/ioctl.h> 1882498Sroberto#endif 1982498Sroberto 2054359Sroberto/* 21132451Sroberto * I'm still not sure I like what I've done here. It certainly consumes 2254359Sroberto * memory like it is going out of style, and also may not be as low 2354359Sroberto * overhead as I'd imagined. 2454359Sroberto * 25132451Sroberto * Anyway, we record statistics based on source address, mode and 26132451Sroberto * version (for now, anyway. Check the code). The receive procedure 27132451Sroberto * calls us with the incoming rbufp before it does anything else. 2854359Sroberto * 2954359Sroberto * Each entry is doubly linked into two lists, a hash table and a 30132451Sroberto * most-recently-used list. When a packet arrives it is looked up in 31132451Sroberto * the hash table. If found, the statistics are updated and the entry 32132451Sroberto * relinked at the head of the MRU list. If not found, a new entry is 33132451Sroberto * allocated, initialized and linked into both the hash table and at the 34132451Sroberto * head of the MRU list. 3554359Sroberto * 36132451Sroberto * Memory is usually allocated by grabbing a big chunk of new memory and 37132451Sroberto * cutting it up into littler pieces. The exception to this when we hit 38132451Sroberto * the memory limit. Then we free memory by grabbing entries off the 39132451Sroberto * tail for the MRU list, unlinking from the hash table, and 4054359Sroberto * reinitializing. 4154359Sroberto * 4254359Sroberto * trimmed back memory consumption ... jdg 8/94 4354359Sroberto */ 4454359Sroberto/* 4554359Sroberto * Limits on the number of structures allocated. This limit is picked 4654359Sroberto * with the illicit knowlege that we can only return somewhat less 4754359Sroberto * than 8K bytes in a mode 7 response packet, and that each structure 4854359Sroberto * will require about 20 bytes of space in the response. 4954359Sroberto * 5054359Sroberto * ... I don't believe the above is true anymore ... jdg 5154359Sroberto */ 5254359Sroberto#ifndef MAXMONMEM 5354359Sroberto#define MAXMONMEM 600 /* we allocate up to 600 structures */ 5454359Sroberto#endif 5554359Sroberto#ifndef MONMEMINC 5654359Sroberto#define MONMEMINC 40 /* allocate them 40 at a time */ 5754359Sroberto#endif 5854359Sroberto 5954359Sroberto/* 6054359Sroberto * Hashing stuff 6154359Sroberto */ 6254359Sroberto#define MON_HASH_SIZE 128 6354359Sroberto#define MON_HASH_MASK (MON_HASH_SIZE-1) 64132451Sroberto#define MON_HASH(addr) sock_hash(addr) 6554359Sroberto 6654359Sroberto/* 6754359Sroberto * Pointers to the hash table, the MRU list and the count table. Memory 68132451Sroberto * for the hash and count tables is only allocated if monitoring is 69132451Sroberto * turned on. 7054359Sroberto */ 71132451Srobertostatic struct mon_data *mon_hash[MON_HASH_SIZE]; /* list ptrs */ 72132451Srobertostruct mon_data mon_mru_list; 73132451Sroberto 7454359Sroberto/* 7554359Sroberto * List of free structures structures, and counters of free and total 7654359Sroberto * structures. The free structures are linked with the hash_next field. 7754359Sroberto */ 78132451Srobertostatic struct mon_data *mon_free; /* free list or null if none */ 79132451Srobertostatic int mon_total_mem; /* total structures allocated */ 80132451Srobertostatic int mon_mem_increments; /* times called malloc() */ 8154359Sroberto 8254359Sroberto/* 8354359Sroberto * Initialization state. We may be monitoring, we may not. If 8454359Sroberto * we aren't, we may not even have allocated any memory yet. 8554359Sroberto */ 86132451Srobertoint mon_enabled; /* enable switch */ 87132451Srobertou_long mon_age = 3000; /* preemption limit */ 8854359Srobertostatic int mon_have_memory; 8954359Srobertostatic void mon_getmoremem P((void)); 9054359Srobertostatic void remove_from_hash P((struct mon_data *)); 9154359Sroberto 9254359Sroberto/* 9354359Sroberto * init_mon - initialize monitoring global data 9454359Sroberto */ 9554359Srobertovoid 9654359Srobertoinit_mon(void) 9754359Sroberto{ 9854359Sroberto /* 9954359Sroberto * Don't do much of anything here. We don't allocate memory 10054359Sroberto * until someone explicitly starts us. 10154359Sroberto */ 10254359Sroberto mon_enabled = MON_OFF; 10354359Sroberto mon_have_memory = 0; 10454359Sroberto 10554359Sroberto mon_total_mem = 0; 10654359Sroberto mon_mem_increments = 0; 10754359Sroberto mon_free = NULL; 108132451Sroberto memset(&mon_hash[0], 0, sizeof mon_hash); 109132451Sroberto memset(&mon_mru_list, 0, sizeof mon_mru_list); 11054359Sroberto} 11154359Sroberto 11254359Sroberto 11354359Sroberto/* 11454359Sroberto * mon_start - start up the monitoring software 11554359Sroberto */ 11654359Srobertovoid 11754359Srobertomon_start( 11854359Sroberto int mode 11954359Sroberto ) 12054359Sroberto{ 12154359Sroberto 12254359Sroberto if (mon_enabled != MON_OFF) { 12354359Sroberto mon_enabled |= mode; 12454359Sroberto return; 12554359Sroberto } 12654359Sroberto if (mode == MON_OFF) 127132451Sroberto return; 12854359Sroberto 12954359Sroberto if (!mon_have_memory) { 13054359Sroberto mon_total_mem = 0; 13154359Sroberto mon_mem_increments = 0; 13254359Sroberto mon_free = NULL; 13354359Sroberto mon_getmoremem(); 13454359Sroberto mon_have_memory = 1; 13554359Sroberto } 13654359Sroberto 13754359Sroberto mon_mru_list.mru_next = &mon_mru_list; 13854359Sroberto mon_mru_list.mru_prev = &mon_mru_list; 13954359Sroberto mon_enabled = mode; 14054359Sroberto} 14154359Sroberto 14254359Sroberto 14354359Sroberto/* 14454359Sroberto * mon_stop - stop the monitoring software 14554359Sroberto */ 14654359Srobertovoid 14754359Srobertomon_stop( 14854359Sroberto int mode 14954359Sroberto ) 15054359Sroberto{ 15154359Sroberto register struct mon_data *md, *md_next; 15254359Sroberto register int i; 15354359Sroberto 15454359Sroberto if (mon_enabled == MON_OFF) 15554359Sroberto return; 15654359Sroberto if ((mon_enabled & mode) == 0 || mode == MON_OFF) 15754359Sroberto return; 15854359Sroberto 15954359Sroberto mon_enabled &= ~mode; 16054359Sroberto if (mon_enabled != MON_OFF) 16154359Sroberto return; 16254359Sroberto 16354359Sroberto /* 16454359Sroberto * Put everything back on the free list 16554359Sroberto */ 16654359Sroberto for (i = 0; i < MON_HASH_SIZE; i++) { 16754359Sroberto md = mon_hash[i]; /* get next list */ 16854359Sroberto mon_hash[i] = NULL; /* zero the list head */ 16954359Sroberto while (md != NULL) { 17054359Sroberto md_next = md->hash_next; 17154359Sroberto md->hash_next = mon_free; 17254359Sroberto mon_free = md; 17354359Sroberto md = md_next; 17454359Sroberto } 17554359Sroberto } 17654359Sroberto 17754359Sroberto mon_mru_list.mru_next = &mon_mru_list; 17854359Sroberto mon_mru_list.mru_prev = &mon_mru_list; 17954359Sroberto} 18054359Sroberto 181182007Srobertovoid 182182007Srobertontp_monclearinterface(struct interface *interface) 183182007Sroberto{ 184182007Sroberto struct mon_data *md; 18554359Sroberto 186182007Sroberto for (md = mon_mru_list.mru_next; md != &mon_mru_list; 187182007Sroberto md = md->mru_next) { 188182007Sroberto if (md->interface == interface) 189182007Sroberto { 190182007Sroberto /* dequeue from mru list and put to free list */ 191182007Sroberto md->mru_prev->mru_next = md->mru_next; 192182007Sroberto md->mru_next->mru_prev = md->mru_prev; 193182007Sroberto remove_from_hash(md); 194182007Sroberto md->hash_next = mon_free; 195182007Sroberto mon_free = md; 196182007Sroberto } 197182007Sroberto } 198182007Sroberto} 199182007Sroberto 20054359Sroberto/* 20154359Sroberto * ntp_monitor - record stats about this packet 202182007Sroberto * 203182007Sroberto * Returns 1 if the packet is at the head of the list, 0 otherwise. 20454359Sroberto */ 205182007Srobertoint 20654359Srobertontp_monitor( 20754359Sroberto struct recvbuf *rbufp 20854359Sroberto ) 20954359Sroberto{ 21054359Sroberto register struct pkt *pkt; 21154359Sroberto register struct mon_data *md; 212132451Sroberto struct sockaddr_storage addr; 21354359Sroberto register int hash; 21454359Sroberto register int mode; 21554359Sroberto 21654359Sroberto if (mon_enabled == MON_OFF) 217182007Sroberto return 0; 21854359Sroberto 21954359Sroberto pkt = &rbufp->recv_pkt; 220132451Sroberto memset(&addr, 0, sizeof(addr)); 221132451Sroberto memcpy(&addr, &(rbufp->recv_srcadr), sizeof(addr)); 222132451Sroberto hash = MON_HASH(&addr); 22354359Sroberto mode = PKT_MODE(pkt->li_vn_mode); 22454359Sroberto md = mon_hash[hash]; 22554359Sroberto while (md != NULL) { 226132451Sroberto 227132451Sroberto /* 228132451Sroberto * Match address only to conserve MRU size. 229132451Sroberto */ 230132451Sroberto if (SOCKCMP(&md->rmtadr, &addr)) { 231132451Sroberto md->drop_count = current_time - md->lasttime; 23254359Sroberto md->lasttime = current_time; 23354359Sroberto md->count++; 234132451Sroberto md->rmtport = NSRCPORT(&rbufp->recv_srcadr); 235132451Sroberto md->mode = (u_char) mode; 23654359Sroberto md->version = PKT_VERSION(pkt->li_vn_mode); 23754359Sroberto 23854359Sroberto /* 239132451Sroberto * Shuffle to the head of the MRU list. 24054359Sroberto */ 24154359Sroberto md->mru_next->mru_prev = md->mru_prev; 24254359Sroberto md->mru_prev->mru_next = md->mru_next; 24354359Sroberto md->mru_next = mon_mru_list.mru_next; 24454359Sroberto md->mru_prev = &mon_mru_list; 24554359Sroberto mon_mru_list.mru_next->mru_prev = md; 24654359Sroberto mon_mru_list.mru_next = md; 247182007Sroberto return 1; 24854359Sroberto } 24954359Sroberto md = md->hash_next; 25054359Sroberto } 25154359Sroberto 25254359Sroberto /* 25354359Sroberto * If we got here, this is the first we've heard of this 25454359Sroberto * guy. Get him some memory, either from the free list 25554359Sroberto * or from the tail of the MRU list. 25654359Sroberto */ 25754359Sroberto if (mon_free == NULL && mon_total_mem >= MAXMONMEM) { 258132451Sroberto 25954359Sroberto /* 260132451Sroberto * Preempt from the MRU list if old enough. 26154359Sroberto */ 26254359Sroberto md = mon_mru_list.mru_prev; 263182007Sroberto /* We get 31 bits from ntp_random() */ 264182007Sroberto if (((u_long)ntp_random()) / FRAC > 265132451Sroberto (double)(current_time - md->lasttime) / mon_age) 266182007Sroberto return 0; 267132451Sroberto 26854359Sroberto md->mru_prev->mru_next = &mon_mru_list; 26954359Sroberto mon_mru_list.mru_prev = md->mru_prev; 27054359Sroberto remove_from_hash(md); 27154359Sroberto } else { 272132451Sroberto if (mon_free == NULL) 273132451Sroberto mon_getmoremem(); 27454359Sroberto md = mon_free; 27554359Sroberto mon_free = md->hash_next; 27654359Sroberto } 27754359Sroberto 27854359Sroberto /* 27954359Sroberto * Got one, initialize it 28054359Sroberto */ 281132451Sroberto md->avg_interval = 0; 282132451Sroberto md->lasttime = current_time; 28354359Sroberto md->count = 1; 284132451Sroberto md->drop_count = 0; 285132451Sroberto memset(&md->rmtadr, 0, sizeof(md->rmtadr)); 286132451Sroberto memcpy(&md->rmtadr, &addr, sizeof(addr)); 28754359Sroberto md->rmtport = NSRCPORT(&rbufp->recv_srcadr); 28854359Sroberto md->mode = (u_char) mode; 28954359Sroberto md->version = PKT_VERSION(pkt->li_vn_mode); 29054359Sroberto md->interface = rbufp->dstadr; 291182007Sroberto md->cast_flags = (u_char)(((rbufp->dstadr->flags & INT_MCASTOPEN) && 292132451Sroberto rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd == 293132451Sroberto md->interface->bfd ? MDF_BCAST : MDF_UCAST); 29454359Sroberto 29554359Sroberto /* 296132451Sroberto * Drop him into front of the hash table. Also put him on top of 297132451Sroberto * the MRU list. 29854359Sroberto */ 29954359Sroberto md->hash_next = mon_hash[hash]; 30054359Sroberto mon_hash[hash] = md; 30154359Sroberto md->mru_next = mon_mru_list.mru_next; 30254359Sroberto md->mru_prev = &mon_mru_list; 30354359Sroberto mon_mru_list.mru_next->mru_prev = md; 30454359Sroberto mon_mru_list.mru_next = md; 305182007Sroberto return 1; 30654359Sroberto} 30754359Sroberto 30854359Sroberto 30954359Sroberto/* 31054359Sroberto * mon_getmoremem - get more memory and put it on the free list 31154359Sroberto */ 31254359Srobertostatic void 31354359Srobertomon_getmoremem(void) 31454359Sroberto{ 31554359Sroberto register struct mon_data *md; 31654359Sroberto register int i; 31754359Sroberto struct mon_data *freedata; /* 'old' free list (null) */ 31854359Sroberto 319132451Sroberto md = (struct mon_data *)emalloc(MONMEMINC * 320132451Sroberto sizeof(struct mon_data)); 32154359Sroberto freedata = mon_free; 32254359Sroberto mon_free = md; 32354359Sroberto for (i = 0; i < (MONMEMINC-1); i++) { 32454359Sroberto md->hash_next = (md + 1); 32554359Sroberto md++; 32654359Sroberto } 32754359Sroberto 32854359Sroberto /* 32954359Sroberto * md now points at the last. Link in the rest of the chain. 33054359Sroberto */ 33154359Sroberto md->hash_next = freedata; 33254359Sroberto mon_total_mem += MONMEMINC; 33354359Sroberto mon_mem_increments++; 33454359Sroberto} 33554359Sroberto 33654359Srobertostatic void 33754359Srobertoremove_from_hash( 33854359Sroberto struct mon_data *md 33954359Sroberto ) 34054359Sroberto{ 34154359Sroberto register int hash; 34254359Sroberto register struct mon_data *md_prev; 34354359Sroberto 344132451Sroberto hash = MON_HASH(&md->rmtadr); 34554359Sroberto if (mon_hash[hash] == md) { 34654359Sroberto mon_hash[hash] = md->hash_next; 34754359Sroberto } else { 34854359Sroberto md_prev = mon_hash[hash]; 34954359Sroberto while (md_prev->hash_next != md) { 35054359Sroberto md_prev = md_prev->hash_next; 35154359Sroberto if (md_prev == NULL) { 35254359Sroberto /* logic error */ 35354359Sroberto return; 35454359Sroberto } 35554359Sroberto } 35654359Sroberto md_prev->hash_next = md->hash_next; 35754359Sroberto } 35854359Sroberto} 359