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