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