ntp_monitor.c revision 132451
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" 1254359Sroberto 1382498Sroberto#include <stdio.h> 1482498Sroberto#include <signal.h> 1582498Sroberto#ifdef HAVE_SYS_IOCTL_H 1682498Sroberto# include <sys/ioctl.h> 1782498Sroberto#endif 1882498Sroberto 1954359Sroberto/* 20132451Sroberto * I'm still not sure I like what I've done here. It certainly consumes 2154359Sroberto * memory like it is going out of style, and also may not be as low 2254359Sroberto * overhead as I'd imagined. 2354359Sroberto * 24132451Sroberto * Anyway, we record statistics based on source address, mode and 25132451Sroberto * version (for now, anyway. Check the code). The receive procedure 26132451Sroberto * calls us with the incoming rbufp before it does anything else. 2754359Sroberto * 2854359Sroberto * Each entry is doubly linked into two lists, a hash table and a 29132451Sroberto * most-recently-used list. When a packet arrives it is looked up in 30132451Sroberto * the hash table. If found, the statistics are updated and the entry 31132451Sroberto * relinked at the head of the MRU list. If not found, a new entry is 32132451Sroberto * allocated, initialized and linked into both the hash table and at the 33132451Sroberto * head of the MRU list. 3454359Sroberto * 35132451Sroberto * Memory is usually allocated by grabbing a big chunk of new memory and 36132451Sroberto * cutting it up into littler pieces. The exception to this when we hit 37132451Sroberto * the memory limit. Then we free memory by grabbing entries off the 38132451Sroberto * tail for the MRU list, unlinking from the hash table, and 3954359Sroberto * reinitializing. 4054359Sroberto * 4154359Sroberto * trimmed back memory consumption ... jdg 8/94 4254359Sroberto */ 4354359Sroberto/* 4454359Sroberto * Limits on the number of structures allocated. This limit is picked 4554359Sroberto * with the illicit knowlege that we can only return somewhat less 4654359Sroberto * than 8K bytes in a mode 7 response packet, and that each structure 4754359Sroberto * will require about 20 bytes of space in the response. 4854359Sroberto * 4954359Sroberto * ... I don't believe the above is true anymore ... jdg 5054359Sroberto */ 5154359Sroberto#ifndef MAXMONMEM 5254359Sroberto#define MAXMONMEM 600 /* we allocate up to 600 structures */ 5354359Sroberto#endif 5454359Sroberto#ifndef MONMEMINC 5554359Sroberto#define MONMEMINC 40 /* allocate them 40 at a time */ 5654359Sroberto#endif 5754359Sroberto 5854359Sroberto/* 5954359Sroberto * Hashing stuff 6054359Sroberto */ 6154359Sroberto#define MON_HASH_SIZE 128 6254359Sroberto#define MON_HASH_MASK (MON_HASH_SIZE-1) 63132451Sroberto#define MON_HASH(addr) sock_hash(addr) 6454359Sroberto 6554359Sroberto/* 6654359Sroberto * Pointers to the hash table, the MRU list and the count table. Memory 67132451Sroberto * for the hash and count tables is only allocated if monitoring is 68132451Sroberto * turned on. 6954359Sroberto */ 70132451Srobertostatic struct mon_data *mon_hash[MON_HASH_SIZE]; /* list ptrs */ 71132451Srobertostruct mon_data mon_mru_list; 72132451Sroberto 7354359Sroberto/* 7454359Sroberto * List of free structures structures, and counters of free and total 7554359Sroberto * structures. The free structures are linked with the hash_next field. 7654359Sroberto */ 77132451Srobertostatic struct mon_data *mon_free; /* free list or null if none */ 78132451Srobertostatic int mon_total_mem; /* total structures allocated */ 79132451Srobertostatic int mon_mem_increments; /* times called malloc() */ 8054359Sroberto 8154359Sroberto/* 8254359Sroberto * Initialization state. We may be monitoring, we may not. If 8354359Sroberto * we aren't, we may not even have allocated any memory yet. 8454359Sroberto */ 85132451Srobertoint mon_enabled; /* enable switch */ 86132451Srobertou_long mon_age = 3000; /* preemption limit */ 8754359Srobertostatic int mon_have_memory; 8854359Srobertostatic void mon_getmoremem P((void)); 8954359Srobertostatic void remove_from_hash P((struct mon_data *)); 9054359Sroberto 9154359Sroberto/* 9254359Sroberto * init_mon - initialize monitoring global data 9354359Sroberto */ 9454359Srobertovoid 9554359Srobertoinit_mon(void) 9654359Sroberto{ 9754359Sroberto /* 9854359Sroberto * Don't do much of anything here. We don't allocate memory 9954359Sroberto * until someone explicitly starts us. 10054359Sroberto */ 10154359Sroberto mon_enabled = MON_OFF; 10254359Sroberto mon_have_memory = 0; 10354359Sroberto 10454359Sroberto mon_total_mem = 0; 10554359Sroberto mon_mem_increments = 0; 10654359Sroberto mon_free = NULL; 107132451Sroberto memset(&mon_hash[0], 0, sizeof mon_hash); 108132451Sroberto memset(&mon_mru_list, 0, sizeof mon_mru_list); 10954359Sroberto} 11054359Sroberto 11154359Sroberto 11254359Sroberto/* 11354359Sroberto * mon_start - start up the monitoring software 11454359Sroberto */ 11554359Srobertovoid 11654359Srobertomon_start( 11754359Sroberto int mode 11854359Sroberto ) 11954359Sroberto{ 12054359Sroberto 12154359Sroberto if (mon_enabled != MON_OFF) { 12254359Sroberto mon_enabled |= mode; 12354359Sroberto return; 12454359Sroberto } 12554359Sroberto if (mode == MON_OFF) 126132451Sroberto return; 12754359Sroberto 12854359Sroberto if (!mon_have_memory) { 12954359Sroberto mon_total_mem = 0; 13054359Sroberto mon_mem_increments = 0; 13154359Sroberto mon_free = NULL; 13254359Sroberto mon_getmoremem(); 13354359Sroberto mon_have_memory = 1; 13454359Sroberto } 13554359Sroberto 13654359Sroberto mon_mru_list.mru_next = &mon_mru_list; 13754359Sroberto mon_mru_list.mru_prev = &mon_mru_list; 13854359Sroberto mon_enabled = mode; 13954359Sroberto} 14054359Sroberto 14154359Sroberto 14254359Sroberto/* 14354359Sroberto * mon_stop - stop the monitoring software 14454359Sroberto */ 14554359Srobertovoid 14654359Srobertomon_stop( 14754359Sroberto int mode 14854359Sroberto ) 14954359Sroberto{ 15054359Sroberto register struct mon_data *md, *md_next; 15154359Sroberto register int i; 15254359Sroberto 15354359Sroberto if (mon_enabled == MON_OFF) 15454359Sroberto return; 15554359Sroberto if ((mon_enabled & mode) == 0 || mode == MON_OFF) 15654359Sroberto return; 15754359Sroberto 15854359Sroberto mon_enabled &= ~mode; 15954359Sroberto if (mon_enabled != MON_OFF) 16054359Sroberto return; 16154359Sroberto 16254359Sroberto /* 16354359Sroberto * Put everything back on the free list 16454359Sroberto */ 16554359Sroberto for (i = 0; i < MON_HASH_SIZE; i++) { 16654359Sroberto md = mon_hash[i]; /* get next list */ 16754359Sroberto mon_hash[i] = NULL; /* zero the list head */ 16854359Sroberto while (md != NULL) { 16954359Sroberto md_next = md->hash_next; 17054359Sroberto md->hash_next = mon_free; 17154359Sroberto mon_free = md; 17254359Sroberto md = md_next; 17354359Sroberto } 17454359Sroberto } 17554359Sroberto 17654359Sroberto mon_mru_list.mru_next = &mon_mru_list; 17754359Sroberto mon_mru_list.mru_prev = &mon_mru_list; 17854359Sroberto} 17954359Sroberto 18054359Sroberto 18154359Sroberto/* 18254359Sroberto * ntp_monitor - record stats about this packet 18354359Sroberto */ 18454359Srobertovoid 18554359Srobertontp_monitor( 18654359Sroberto struct recvbuf *rbufp 18754359Sroberto ) 18854359Sroberto{ 18954359Sroberto register struct pkt *pkt; 19054359Sroberto register struct mon_data *md; 191132451Sroberto struct sockaddr_storage addr; 19254359Sroberto register int hash; 19354359Sroberto register int mode; 19454359Sroberto 19554359Sroberto if (mon_enabled == MON_OFF) 196132451Sroberto return; 19754359Sroberto 19854359Sroberto pkt = &rbufp->recv_pkt; 199132451Sroberto memset(&addr, 0, sizeof(addr)); 200132451Sroberto memcpy(&addr, &(rbufp->recv_srcadr), sizeof(addr)); 201132451Sroberto hash = MON_HASH(&addr); 20254359Sroberto mode = PKT_MODE(pkt->li_vn_mode); 20354359Sroberto md = mon_hash[hash]; 20454359Sroberto while (md != NULL) { 205132451Sroberto 206132451Sroberto /* 207132451Sroberto * Match address only to conserve MRU size. 208132451Sroberto */ 209132451Sroberto if (SOCKCMP(&md->rmtadr, &addr)) { 210132451Sroberto md->drop_count = current_time - md->lasttime; 21154359Sroberto md->lasttime = current_time; 21254359Sroberto md->count++; 213132451Sroberto md->rmtport = NSRCPORT(&rbufp->recv_srcadr); 214132451Sroberto md->mode = (u_char) mode; 21554359Sroberto md->version = PKT_VERSION(pkt->li_vn_mode); 21654359Sroberto 21754359Sroberto /* 218132451Sroberto * Shuffle to the head of the MRU list. 21954359Sroberto */ 22054359Sroberto md->mru_next->mru_prev = md->mru_prev; 22154359Sroberto md->mru_prev->mru_next = md->mru_next; 22254359Sroberto md->mru_next = mon_mru_list.mru_next; 22354359Sroberto md->mru_prev = &mon_mru_list; 22454359Sroberto mon_mru_list.mru_next->mru_prev = md; 22554359Sroberto mon_mru_list.mru_next = md; 22654359Sroberto return; 22754359Sroberto } 22854359Sroberto md = md->hash_next; 22954359Sroberto } 23054359Sroberto 23154359Sroberto /* 23254359Sroberto * If we got here, this is the first we've heard of this 23354359Sroberto * guy. Get him some memory, either from the free list 23454359Sroberto * or from the tail of the MRU list. 23554359Sroberto */ 23654359Sroberto if (mon_free == NULL && mon_total_mem >= MAXMONMEM) { 237132451Sroberto 23854359Sroberto /* 239132451Sroberto * Preempt from the MRU list if old enough. 24054359Sroberto */ 24154359Sroberto md = mon_mru_list.mru_prev; 242132451Sroberto if (((u_long)RANDOM & 0xffffffff) / FRAC > 243132451Sroberto (double)(current_time - md->lasttime) / mon_age) 244132451Sroberto return; 245132451Sroberto 24654359Sroberto md->mru_prev->mru_next = &mon_mru_list; 24754359Sroberto mon_mru_list.mru_prev = md->mru_prev; 24854359Sroberto remove_from_hash(md); 24954359Sroberto } else { 250132451Sroberto if (mon_free == NULL) 251132451Sroberto mon_getmoremem(); 25254359Sroberto md = mon_free; 25354359Sroberto mon_free = md->hash_next; 25454359Sroberto } 25554359Sroberto 25654359Sroberto /* 25754359Sroberto * Got one, initialize it 25854359Sroberto */ 259132451Sroberto md->avg_interval = 0; 260132451Sroberto md->lasttime = current_time; 26154359Sroberto md->count = 1; 262132451Sroberto md->drop_count = 0; 263132451Sroberto memset(&md->rmtadr, 0, sizeof(md->rmtadr)); 264132451Sroberto memcpy(&md->rmtadr, &addr, sizeof(addr)); 26554359Sroberto md->rmtport = NSRCPORT(&rbufp->recv_srcadr); 26654359Sroberto md->mode = (u_char) mode; 26754359Sroberto md->version = PKT_VERSION(pkt->li_vn_mode); 26854359Sroberto md->interface = rbufp->dstadr; 269132451Sroberto md->cast_flags = (u_char)(((rbufp->dstadr->flags & INT_MULTICAST) && 270132451Sroberto rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd == 271132451Sroberto md->interface->bfd ? MDF_BCAST : MDF_UCAST); 27254359Sroberto 27354359Sroberto /* 274132451Sroberto * Drop him into front of the hash table. Also put him on top of 275132451Sroberto * the MRU list. 27654359Sroberto */ 27754359Sroberto md->hash_next = mon_hash[hash]; 27854359Sroberto mon_hash[hash] = md; 27954359Sroberto md->mru_next = mon_mru_list.mru_next; 28054359Sroberto md->mru_prev = &mon_mru_list; 28154359Sroberto mon_mru_list.mru_next->mru_prev = md; 28254359Sroberto mon_mru_list.mru_next = md; 28354359Sroberto} 28454359Sroberto 28554359Sroberto 28654359Sroberto/* 28754359Sroberto * mon_getmoremem - get more memory and put it on the free list 28854359Sroberto */ 28954359Srobertostatic void 29054359Srobertomon_getmoremem(void) 29154359Sroberto{ 29254359Sroberto register struct mon_data *md; 29354359Sroberto register int i; 29454359Sroberto struct mon_data *freedata; /* 'old' free list (null) */ 29554359Sroberto 296132451Sroberto md = (struct mon_data *)emalloc(MONMEMINC * 297132451Sroberto sizeof(struct mon_data)); 29854359Sroberto freedata = mon_free; 29954359Sroberto mon_free = md; 30054359Sroberto for (i = 0; i < (MONMEMINC-1); i++) { 30154359Sroberto md->hash_next = (md + 1); 30254359Sroberto md++; 30354359Sroberto } 30454359Sroberto 30554359Sroberto /* 30654359Sroberto * md now points at the last. Link in the rest of the chain. 30754359Sroberto */ 30854359Sroberto md->hash_next = freedata; 30954359Sroberto mon_total_mem += MONMEMINC; 31054359Sroberto mon_mem_increments++; 31154359Sroberto} 31254359Sroberto 31354359Srobertostatic void 31454359Srobertoremove_from_hash( 31554359Sroberto struct mon_data *md 31654359Sroberto ) 31754359Sroberto{ 31854359Sroberto register int hash; 31954359Sroberto register struct mon_data *md_prev; 32054359Sroberto 321132451Sroberto hash = MON_HASH(&md->rmtadr); 32254359Sroberto if (mon_hash[hash] == md) { 32354359Sroberto mon_hash[hash] = md->hash_next; 32454359Sroberto } else { 32554359Sroberto md_prev = mon_hash[hash]; 32654359Sroberto while (md_prev->hash_next != md) { 32754359Sroberto md_prev = md_prev->hash_next; 32854359Sroberto if (md_prev == NULL) { 32954359Sroberto /* logic error */ 33054359Sroberto return; 33154359Sroberto } 33254359Sroberto } 33354359Sroberto md_prev->hash_next = md->hash_next; 33454359Sroberto } 33554359Sroberto} 336