1c0b746e5SOllivier Robert /* 29c2daa00SOllivier Robert * ntp_monitor - monitor ntpd statistics 3c0b746e5SOllivier Robert */ 4c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H 5c0b746e5SOllivier Robert # include <config.h> 6c0b746e5SOllivier Robert #endif 7c0b746e5SOllivier Robert 8c0b746e5SOllivier Robert #include "ntpd.h" 9c0b746e5SOllivier Robert #include "ntp_io.h" 10c0b746e5SOllivier Robert #include "ntp_if.h" 11c0b746e5SOllivier Robert #include "ntp_stdlib.h" 12c0b746e5SOllivier Robert 13224ba2bdSOllivier Robert #include <stdio.h> 14224ba2bdSOllivier Robert #include <signal.h> 15224ba2bdSOllivier Robert #ifdef HAVE_SYS_IOCTL_H 16224ba2bdSOllivier Robert # include <sys/ioctl.h> 17224ba2bdSOllivier Robert #endif 18224ba2bdSOllivier Robert 19c0b746e5SOllivier Robert /* 20c0b746e5SOllivier Robert * I'm still not sure I like what I've done here. It certainly consumes 21c0b746e5SOllivier Robert * memory like it is going out of style, and also may not be as low 22c0b746e5SOllivier Robert * overhead as I'd imagined. 23c0b746e5SOllivier Robert * 249c2daa00SOllivier Robert * Anyway, we record statistics based on source address, mode and 259c2daa00SOllivier Robert * version (for now, anyway. Check the code). The receive procedure 269c2daa00SOllivier Robert * calls us with the incoming rbufp before it does anything else. 27c0b746e5SOllivier Robert * 28c0b746e5SOllivier Robert * Each entry is doubly linked into two lists, a hash table and a 299c2daa00SOllivier Robert * most-recently-used list. When a packet arrives it is looked up in 309c2daa00SOllivier Robert * the hash table. If found, the statistics are updated and the entry 319c2daa00SOllivier Robert * relinked at the head of the MRU list. If not found, a new entry is 329c2daa00SOllivier Robert * allocated, initialized and linked into both the hash table and at the 339c2daa00SOllivier Robert * head of the MRU list. 34c0b746e5SOllivier Robert * 359c2daa00SOllivier Robert * Memory is usually allocated by grabbing a big chunk of new memory and 369c2daa00SOllivier Robert * cutting it up into littler pieces. The exception to this when we hit 379c2daa00SOllivier Robert * the memory limit. Then we free memory by grabbing entries off the 389c2daa00SOllivier Robert * tail for the MRU list, unlinking from the hash table, and 39c0b746e5SOllivier Robert * reinitializing. 40c0b746e5SOllivier Robert * 41c0b746e5SOllivier Robert * trimmed back memory consumption ... jdg 8/94 42c0b746e5SOllivier Robert */ 43c0b746e5SOllivier Robert /* 44c0b746e5SOllivier Robert * Limits on the number of structures allocated. This limit is picked 45c0b746e5SOllivier Robert * with the illicit knowlege that we can only return somewhat less 46c0b746e5SOllivier Robert * than 8K bytes in a mode 7 response packet, and that each structure 47c0b746e5SOllivier Robert * will require about 20 bytes of space in the response. 48c0b746e5SOllivier Robert * 49c0b746e5SOllivier Robert * ... I don't believe the above is true anymore ... jdg 50c0b746e5SOllivier Robert */ 51c0b746e5SOllivier Robert #ifndef MAXMONMEM 52c0b746e5SOllivier Robert #define MAXMONMEM 600 /* we allocate up to 600 structures */ 53c0b746e5SOllivier Robert #endif 54c0b746e5SOllivier Robert #ifndef MONMEMINC 55c0b746e5SOllivier Robert #define MONMEMINC 40 /* allocate them 40 at a time */ 56c0b746e5SOllivier Robert #endif 57c0b746e5SOllivier Robert 58c0b746e5SOllivier Robert /* 59c0b746e5SOllivier Robert * Hashing stuff 60c0b746e5SOllivier Robert */ 61c0b746e5SOllivier Robert #define MON_HASH_SIZE 128 62c0b746e5SOllivier Robert #define MON_HASH_MASK (MON_HASH_SIZE-1) 639c2daa00SOllivier Robert #define MON_HASH(addr) sock_hash(addr) 64c0b746e5SOllivier Robert 65c0b746e5SOllivier Robert /* 66c0b746e5SOllivier Robert * Pointers to the hash table, the MRU list and the count table. Memory 679c2daa00SOllivier Robert * for the hash and count tables is only allocated if monitoring is 689c2daa00SOllivier Robert * turned on. 69c0b746e5SOllivier Robert */ 709c2daa00SOllivier Robert static struct mon_data *mon_hash[MON_HASH_SIZE]; /* list ptrs */ 71c0b746e5SOllivier Robert struct mon_data mon_mru_list; 729c2daa00SOllivier Robert 73c0b746e5SOllivier Robert /* 74c0b746e5SOllivier Robert * List of free structures structures, and counters of free and total 75c0b746e5SOllivier Robert * structures. The free structures are linked with the hash_next field. 76c0b746e5SOllivier Robert */ 779c2daa00SOllivier Robert static struct mon_data *mon_free; /* free list or null if none */ 789c2daa00SOllivier Robert static int mon_total_mem; /* total structures allocated */ 799c2daa00SOllivier Robert static int mon_mem_increments; /* times called malloc() */ 80c0b746e5SOllivier Robert 81c0b746e5SOllivier Robert /* 82c0b746e5SOllivier Robert * Initialization state. We may be monitoring, we may not. If 83c0b746e5SOllivier Robert * we aren't, we may not even have allocated any memory yet. 84c0b746e5SOllivier Robert */ 859c2daa00SOllivier Robert int mon_enabled; /* enable switch */ 869c2daa00SOllivier Robert u_long mon_age = 3000; /* preemption limit */ 87c0b746e5SOllivier Robert static int mon_have_memory; 88c0b746e5SOllivier Robert static void mon_getmoremem P((void)); 89c0b746e5SOllivier Robert static void remove_from_hash P((struct mon_data *)); 90c0b746e5SOllivier Robert 91c0b746e5SOllivier Robert /* 92c0b746e5SOllivier Robert * init_mon - initialize monitoring global data 93c0b746e5SOllivier Robert */ 94c0b746e5SOllivier Robert void 95c0b746e5SOllivier Robert init_mon(void) 96c0b746e5SOllivier Robert { 97c0b746e5SOllivier Robert /* 98c0b746e5SOllivier Robert * Don't do much of anything here. We don't allocate memory 99c0b746e5SOllivier Robert * until someone explicitly starts us. 100c0b746e5SOllivier Robert */ 101c0b746e5SOllivier Robert mon_enabled = MON_OFF; 102c0b746e5SOllivier Robert mon_have_memory = 0; 103c0b746e5SOllivier Robert 104c0b746e5SOllivier Robert mon_total_mem = 0; 105c0b746e5SOllivier Robert mon_mem_increments = 0; 106c0b746e5SOllivier Robert mon_free = NULL; 1079c2daa00SOllivier Robert memset(&mon_hash[0], 0, sizeof mon_hash); 1089c2daa00SOllivier Robert memset(&mon_mru_list, 0, sizeof mon_mru_list); 109c0b746e5SOllivier Robert } 110c0b746e5SOllivier Robert 111c0b746e5SOllivier Robert 112c0b746e5SOllivier Robert /* 113c0b746e5SOllivier Robert * mon_start - start up the monitoring software 114c0b746e5SOllivier Robert */ 115c0b746e5SOllivier Robert void 116c0b746e5SOllivier Robert mon_start( 117c0b746e5SOllivier Robert int mode 118c0b746e5SOllivier Robert ) 119c0b746e5SOllivier Robert { 120c0b746e5SOllivier Robert 121c0b746e5SOllivier Robert if (mon_enabled != MON_OFF) { 122c0b746e5SOllivier Robert mon_enabled |= mode; 123c0b746e5SOllivier Robert return; 124c0b746e5SOllivier Robert } 125c0b746e5SOllivier Robert if (mode == MON_OFF) 1269c2daa00SOllivier Robert return; 127c0b746e5SOllivier Robert 128c0b746e5SOllivier Robert if (!mon_have_memory) { 129c0b746e5SOllivier Robert mon_total_mem = 0; 130c0b746e5SOllivier Robert mon_mem_increments = 0; 131c0b746e5SOllivier Robert mon_free = NULL; 132c0b746e5SOllivier Robert mon_getmoremem(); 133c0b746e5SOllivier Robert mon_have_memory = 1; 134c0b746e5SOllivier Robert } 135c0b746e5SOllivier Robert 136c0b746e5SOllivier Robert mon_mru_list.mru_next = &mon_mru_list; 137c0b746e5SOllivier Robert mon_mru_list.mru_prev = &mon_mru_list; 138c0b746e5SOllivier Robert mon_enabled = mode; 139c0b746e5SOllivier Robert } 140c0b746e5SOllivier Robert 141c0b746e5SOllivier Robert 142c0b746e5SOllivier Robert /* 143c0b746e5SOllivier Robert * mon_stop - stop the monitoring software 144c0b746e5SOllivier Robert */ 145c0b746e5SOllivier Robert void 146c0b746e5SOllivier Robert mon_stop( 147c0b746e5SOllivier Robert int mode 148c0b746e5SOllivier Robert ) 149c0b746e5SOllivier Robert { 150c0b746e5SOllivier Robert register struct mon_data *md, *md_next; 151c0b746e5SOllivier Robert register int i; 152c0b746e5SOllivier Robert 153c0b746e5SOllivier Robert if (mon_enabled == MON_OFF) 154c0b746e5SOllivier Robert return; 155c0b746e5SOllivier Robert if ((mon_enabled & mode) == 0 || mode == MON_OFF) 156c0b746e5SOllivier Robert return; 157c0b746e5SOllivier Robert 158c0b746e5SOllivier Robert mon_enabled &= ~mode; 159c0b746e5SOllivier Robert if (mon_enabled != MON_OFF) 160c0b746e5SOllivier Robert return; 161c0b746e5SOllivier Robert 162c0b746e5SOllivier Robert /* 163c0b746e5SOllivier Robert * Put everything back on the free list 164c0b746e5SOllivier Robert */ 165c0b746e5SOllivier Robert for (i = 0; i < MON_HASH_SIZE; i++) { 166c0b746e5SOllivier Robert md = mon_hash[i]; /* get next list */ 167c0b746e5SOllivier Robert mon_hash[i] = NULL; /* zero the list head */ 168c0b746e5SOllivier Robert while (md != NULL) { 169c0b746e5SOllivier Robert md_next = md->hash_next; 170c0b746e5SOllivier Robert md->hash_next = mon_free; 171c0b746e5SOllivier Robert mon_free = md; 172c0b746e5SOllivier Robert md = md_next; 173c0b746e5SOllivier Robert } 174c0b746e5SOllivier Robert } 175c0b746e5SOllivier Robert 176c0b746e5SOllivier Robert mon_mru_list.mru_next = &mon_mru_list; 177c0b746e5SOllivier Robert mon_mru_list.mru_prev = &mon_mru_list; 178c0b746e5SOllivier Robert } 179c0b746e5SOllivier Robert 180c0b746e5SOllivier Robert 181c0b746e5SOllivier Robert /* 182c0b746e5SOllivier Robert * ntp_monitor - record stats about this packet 183c0b746e5SOllivier Robert */ 184c0b746e5SOllivier Robert void 185c0b746e5SOllivier Robert ntp_monitor( 186c0b746e5SOllivier Robert struct recvbuf *rbufp 187c0b746e5SOllivier Robert ) 188c0b746e5SOllivier Robert { 189c0b746e5SOllivier Robert register struct pkt *pkt; 190c0b746e5SOllivier Robert register struct mon_data *md; 1919c2daa00SOllivier Robert struct sockaddr_storage addr; 192c0b746e5SOllivier Robert register int hash; 193c0b746e5SOllivier Robert register int mode; 194c0b746e5SOllivier Robert 195c0b746e5SOllivier Robert if (mon_enabled == MON_OFF) 196c0b746e5SOllivier Robert return; 197c0b746e5SOllivier Robert 198c0b746e5SOllivier Robert pkt = &rbufp->recv_pkt; 1999c2daa00SOllivier Robert memset(&addr, 0, sizeof(addr)); 2009c2daa00SOllivier Robert memcpy(&addr, &(rbufp->recv_srcadr), sizeof(addr)); 2019c2daa00SOllivier Robert hash = MON_HASH(&addr); 202c0b746e5SOllivier Robert mode = PKT_MODE(pkt->li_vn_mode); 203c0b746e5SOllivier Robert md = mon_hash[hash]; 204c0b746e5SOllivier Robert while (md != NULL) { 205c0b746e5SOllivier Robert 206c0b746e5SOllivier Robert /* 2079c2daa00SOllivier Robert * Match address only to conserve MRU size. 2089c2daa00SOllivier Robert */ 2099c2daa00SOllivier Robert if (SOCKCMP(&md->rmtadr, &addr)) { 2109c2daa00SOllivier Robert md->drop_count = current_time - md->lasttime; 2119c2daa00SOllivier Robert md->lasttime = current_time; 2129c2daa00SOllivier Robert md->count++; 2139c2daa00SOllivier Robert md->rmtport = NSRCPORT(&rbufp->recv_srcadr); 2149c2daa00SOllivier Robert md->mode = (u_char) mode; 2159c2daa00SOllivier Robert md->version = PKT_VERSION(pkt->li_vn_mode); 2169c2daa00SOllivier Robert 2179c2daa00SOllivier Robert /* 2189c2daa00SOllivier Robert * Shuffle to the head of the MRU list. 219c0b746e5SOllivier Robert */ 220c0b746e5SOllivier Robert md->mru_next->mru_prev = md->mru_prev; 221c0b746e5SOllivier Robert md->mru_prev->mru_next = md->mru_next; 222c0b746e5SOllivier Robert md->mru_next = mon_mru_list.mru_next; 223c0b746e5SOllivier Robert md->mru_prev = &mon_mru_list; 224c0b746e5SOllivier Robert mon_mru_list.mru_next->mru_prev = md; 225c0b746e5SOllivier Robert mon_mru_list.mru_next = md; 226c0b746e5SOllivier Robert return; 227c0b746e5SOllivier Robert } 228c0b746e5SOllivier Robert md = md->hash_next; 229c0b746e5SOllivier Robert } 230c0b746e5SOllivier Robert 231c0b746e5SOllivier Robert /* 232c0b746e5SOllivier Robert * If we got here, this is the first we've heard of this 233c0b746e5SOllivier Robert * guy. Get him some memory, either from the free list 234c0b746e5SOllivier Robert * or from the tail of the MRU list. 235c0b746e5SOllivier Robert */ 236c0b746e5SOllivier Robert if (mon_free == NULL && mon_total_mem >= MAXMONMEM) { 2379c2daa00SOllivier Robert 238c0b746e5SOllivier Robert /* 2399c2daa00SOllivier Robert * Preempt from the MRU list if old enough. 240c0b746e5SOllivier Robert */ 241c0b746e5SOllivier Robert md = mon_mru_list.mru_prev; 2429c2daa00SOllivier Robert if (((u_long)RANDOM & 0xffffffff) / FRAC > 2439c2daa00SOllivier Robert (double)(current_time - md->lasttime) / mon_age) 2449c2daa00SOllivier Robert return; 2459c2daa00SOllivier Robert 246c0b746e5SOllivier Robert md->mru_prev->mru_next = &mon_mru_list; 247c0b746e5SOllivier Robert mon_mru_list.mru_prev = md->mru_prev; 248c0b746e5SOllivier Robert remove_from_hash(md); 249c0b746e5SOllivier Robert } else { 2509c2daa00SOllivier Robert if (mon_free == NULL) 2519c2daa00SOllivier Robert mon_getmoremem(); 252c0b746e5SOllivier Robert md = mon_free; 253c0b746e5SOllivier Robert mon_free = md->hash_next; 254c0b746e5SOllivier Robert } 255c0b746e5SOllivier Robert 256c0b746e5SOllivier Robert /* 257c0b746e5SOllivier Robert * Got one, initialize it 258c0b746e5SOllivier Robert */ 2599c2daa00SOllivier Robert md->avg_interval = 0; 2609c2daa00SOllivier Robert md->lasttime = current_time; 261c0b746e5SOllivier Robert md->count = 1; 2629c2daa00SOllivier Robert md->drop_count = 0; 2639c2daa00SOllivier Robert memset(&md->rmtadr, 0, sizeof(md->rmtadr)); 2649c2daa00SOllivier Robert memcpy(&md->rmtadr, &addr, sizeof(addr)); 265c0b746e5SOllivier Robert md->rmtport = NSRCPORT(&rbufp->recv_srcadr); 266c0b746e5SOllivier Robert md->mode = (u_char) mode; 267c0b746e5SOllivier Robert md->version = PKT_VERSION(pkt->li_vn_mode); 268c0b746e5SOllivier Robert md->interface = rbufp->dstadr; 2699c2daa00SOllivier Robert md->cast_flags = (u_char)(((rbufp->dstadr->flags & INT_MULTICAST) && 270c0b746e5SOllivier Robert rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd == 2719c2daa00SOllivier Robert md->interface->bfd ? MDF_BCAST : MDF_UCAST); 272c0b746e5SOllivier Robert 273c0b746e5SOllivier Robert /* 2749c2daa00SOllivier Robert * Drop him into front of the hash table. Also put him on top of 2759c2daa00SOllivier Robert * the MRU list. 276c0b746e5SOllivier Robert */ 277c0b746e5SOllivier Robert md->hash_next = mon_hash[hash]; 278c0b746e5SOllivier Robert mon_hash[hash] = md; 279c0b746e5SOllivier Robert md->mru_next = mon_mru_list.mru_next; 280c0b746e5SOllivier Robert md->mru_prev = &mon_mru_list; 281c0b746e5SOllivier Robert mon_mru_list.mru_next->mru_prev = md; 282c0b746e5SOllivier Robert mon_mru_list.mru_next = md; 283c0b746e5SOllivier Robert } 284c0b746e5SOllivier Robert 285c0b746e5SOllivier Robert 286c0b746e5SOllivier Robert /* 287c0b746e5SOllivier Robert * mon_getmoremem - get more memory and put it on the free list 288c0b746e5SOllivier Robert */ 289c0b746e5SOllivier Robert static void 290c0b746e5SOllivier Robert mon_getmoremem(void) 291c0b746e5SOllivier Robert { 292c0b746e5SOllivier Robert register struct mon_data *md; 293c0b746e5SOllivier Robert register int i; 294c0b746e5SOllivier Robert struct mon_data *freedata; /* 'old' free list (null) */ 295c0b746e5SOllivier Robert 2969c2daa00SOllivier Robert md = (struct mon_data *)emalloc(MONMEMINC * 2979c2daa00SOllivier Robert sizeof(struct mon_data)); 298c0b746e5SOllivier Robert freedata = mon_free; 299c0b746e5SOllivier Robert mon_free = md; 300c0b746e5SOllivier Robert for (i = 0; i < (MONMEMINC-1); i++) { 301c0b746e5SOllivier Robert md->hash_next = (md + 1); 302c0b746e5SOllivier Robert md++; 303c0b746e5SOllivier Robert } 304c0b746e5SOllivier Robert 305c0b746e5SOllivier Robert /* 306c0b746e5SOllivier Robert * md now points at the last. Link in the rest of the chain. 307c0b746e5SOllivier Robert */ 308c0b746e5SOllivier Robert md->hash_next = freedata; 309c0b746e5SOllivier Robert mon_total_mem += MONMEMINC; 310c0b746e5SOllivier Robert mon_mem_increments++; 311c0b746e5SOllivier Robert } 312c0b746e5SOllivier Robert 313c0b746e5SOllivier Robert static void 314c0b746e5SOllivier Robert remove_from_hash( 315c0b746e5SOllivier Robert struct mon_data *md 316c0b746e5SOllivier Robert ) 317c0b746e5SOllivier Robert { 318c0b746e5SOllivier Robert register int hash; 319c0b746e5SOllivier Robert register struct mon_data *md_prev; 320c0b746e5SOllivier Robert 3219c2daa00SOllivier Robert hash = MON_HASH(&md->rmtadr); 322c0b746e5SOllivier Robert if (mon_hash[hash] == md) { 323c0b746e5SOllivier Robert mon_hash[hash] = md->hash_next; 324c0b746e5SOllivier Robert } else { 325c0b746e5SOllivier Robert md_prev = mon_hash[hash]; 326c0b746e5SOllivier Robert while (md_prev->hash_next != md) { 327c0b746e5SOllivier Robert md_prev = md_prev->hash_next; 328c0b746e5SOllivier Robert if (md_prev == NULL) { 329c0b746e5SOllivier Robert /* logic error */ 330c0b746e5SOllivier Robert return; 331c0b746e5SOllivier Robert } 332c0b746e5SOllivier Robert } 333c0b746e5SOllivier Robert md_prev->hash_next = md->hash_next; 334c0b746e5SOllivier Robert } 335c0b746e5SOllivier Robert } 336