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