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