Annotation of embedaddon/ntp/ntpd/ntp_monitor.c, revision 1.1
1.1 ! misho 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: * Record statistics based on source address, mode and version. The
! 22: * receive procedure calls us with the incoming rbufp before it does
! 23: * anything else. While at it, implement rate controls for inbound
! 24: * traffic.
! 25: *
! 26: * Each entry is doubly linked into two lists, a hash table and a most-
! 27: * recently-used (MRU) list. When a packet arrives it is looked up in
! 28: * the hash table. If found, the statistics are updated and the entry
! 29: * relinked at the head of the MRU list. If not found, a new entry is
! 30: * allocated, initialized and linked into both the hash table and at the
! 31: * head of the MRU list.
! 32: *
! 33: * Memory is usually allocated by grabbing a big chunk of new memory and
! 34: * cutting it up into littler pieces. The exception to this when we hit
! 35: * the memory limit. Then we free memory by grabbing entries off the
! 36: * tail for the MRU list, unlinking from the hash table, and
! 37: * reinitializing.
! 38: */
! 39: /*
! 40: * Limits on the number of structures allocated. This limit is picked
! 41: * with the illicit knowlege that we can only return somewhat less than
! 42: * 8K bytes in a mode 7 response packet, and that each structure will
! 43: * require about 20 bytes of space in the response.
! 44: *
! 45: * ... I don't believe the above is true anymore ... jdg
! 46: */
! 47: #ifndef MAXMONMEM
! 48: #define MAXMONMEM 600 /* we allocate up to 600 structures */
! 49: #endif
! 50: #ifndef MONMEMINC
! 51: #define MONMEMINC 40 /* allocate them 40 at a time */
! 52: #endif
! 53:
! 54: /*
! 55: * Hashing stuff
! 56: */
! 57: #define MON_HASH_SIZE NTP_HASH_SIZE
! 58: #define MON_HASH_MASK NTP_HASH_MASK
! 59: #define MON_HASH(addr) NTP_HASH_ADDR(addr)
! 60:
! 61: /*
! 62: * Pointers to the hash table, the MRU list and the count table. Memory
! 63: * for the hash and count tables is only allocated if monitoring is
! 64: * turned on.
! 65: */
! 66: static struct mon_data *mon_hash[MON_HASH_SIZE]; /* list ptrs */
! 67: struct mon_data mon_mru_list;
! 68:
! 69: /*
! 70: * List of free structures structures, and counters of free and total
! 71: * structures. The free structures are linked with the hash_next field.
! 72: */
! 73: static struct mon_data *mon_free; /* free list or null if none */
! 74: static int mon_total_mem; /* total structures allocated */
! 75: static int mon_mem_increments; /* times called malloc() */
! 76:
! 77: /*
! 78: * Parameters of the RES_LIMITED restriction option. We define headway
! 79: * as the idle time between packets. A packet is discarded if the
! 80: * headway is less than the minimum, as well as if the average headway
! 81: * is less than eight times the increment.
! 82: */
! 83: int ntp_minpkt = NTP_MINPKT; /* minimum (log 2 s) */
! 84: int ntp_minpoll = NTP_MINPOLL; /* increment (log 2 s) */
! 85:
! 86: /*
! 87: * Initialization state. We may be monitoring, we may not. If
! 88: * we aren't, we may not even have allocated any memory yet.
! 89: */
! 90: int mon_enabled; /* enable switch */
! 91: int mon_age = 3000; /* preemption limit */
! 92: static int mon_have_memory;
! 93: static void mon_getmoremem (void);
! 94: static void remove_from_hash (struct mon_data *);
! 95:
! 96: /*
! 97: * init_mon - initialize monitoring global data
! 98: */
! 99: void
! 100: init_mon(void)
! 101: {
! 102: /*
! 103: * Don't do much of anything here. We don't allocate memory
! 104: * until someone explicitly starts us.
! 105: */
! 106: mon_enabled = MON_OFF;
! 107: mon_have_memory = 0;
! 108: mon_total_mem = 0;
! 109: mon_mem_increments = 0;
! 110: mon_free = NULL;
! 111: memset(&mon_hash[0], 0, sizeof mon_hash);
! 112: memset(&mon_mru_list, 0, sizeof mon_mru_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;
! 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: mon_enabled = mode;
! 143: }
! 144:
! 145:
! 146: /*
! 147: * mon_stop - stop the monitoring software
! 148: */
! 149: void
! 150: mon_stop(
! 151: int mode
! 152: )
! 153: {
! 154: register struct mon_data *md, *md_next;
! 155: register int i;
! 156:
! 157: if (mon_enabled == MON_OFF)
! 158: return;
! 159: if ((mon_enabled & mode) == 0 || mode == MON_OFF)
! 160: return;
! 161:
! 162: mon_enabled &= ~mode;
! 163: if (mon_enabled != MON_OFF)
! 164: return;
! 165:
! 166: /*
! 167: * Put everything back on the free list
! 168: */
! 169: for (i = 0; i < MON_HASH_SIZE; i++) {
! 170: md = mon_hash[i]; /* get next list */
! 171: mon_hash[i] = NULL; /* zero the list head */
! 172: while (md != NULL) {
! 173: md_next = md->hash_next;
! 174: md->hash_next = mon_free;
! 175: mon_free = md;
! 176: md = md_next;
! 177: }
! 178: }
! 179: mon_mru_list.mru_next = &mon_mru_list;
! 180: mon_mru_list.mru_prev = &mon_mru_list;
! 181: }
! 182:
! 183: void
! 184: ntp_monclearinterface(struct interface *interface)
! 185: {
! 186: struct mon_data *md;
! 187:
! 188: for (md = mon_mru_list.mru_next; md != &mon_mru_list;
! 189: md = md->mru_next) {
! 190: if (md->interface == interface) {
! 191: /* dequeue from mru list and put to free list */
! 192: md->mru_prev->mru_next = md->mru_next;
! 193: md->mru_next->mru_prev = md->mru_prev;
! 194: remove_from_hash(md);
! 195: md->hash_next = mon_free;
! 196: mon_free = md;
! 197: }
! 198: }
! 199: }
! 200:
! 201:
! 202: /*
! 203: * ntp_monitor - record stats about this packet
! 204: *
! 205: * Returns flags
! 206: */
! 207: int
! 208: ntp_monitor(
! 209: struct recvbuf *rbufp,
! 210: int flags
! 211: )
! 212: {
! 213: register struct pkt *pkt;
! 214: register struct mon_data *md;
! 215: sockaddr_u addr;
! 216: register u_int hash;
! 217: register int mode;
! 218: int interval;
! 219:
! 220: if (mon_enabled == MON_OFF)
! 221: return (flags);
! 222:
! 223: pkt = &rbufp->recv_pkt;
! 224: memset(&addr, 0, sizeof(addr));
! 225: memcpy(&addr, &(rbufp->recv_srcadr), sizeof(addr));
! 226: hash = MON_HASH(&addr);
! 227: mode = PKT_MODE(pkt->li_vn_mode);
! 228: md = mon_hash[hash];
! 229: while (md != NULL) {
! 230: int head; /* headway increment */
! 231: int leak; /* new headway */
! 232: int limit; /* average threshold */
! 233:
! 234: /*
! 235: * Match address only to conserve MRU size.
! 236: */
! 237: if (SOCK_EQ(&md->rmtadr, &addr)) {
! 238: interval = current_time - md->lasttime;
! 239: md->lasttime = current_time;
! 240: md->count++;
! 241: md->flags = flags;
! 242: md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
! 243: md->mode = (u_char) mode;
! 244: md->version = PKT_VERSION(pkt->li_vn_mode);
! 245:
! 246: /*
! 247: * Shuffle to the head of the MRU list.
! 248: */
! 249: md->mru_next->mru_prev = md->mru_prev;
! 250: md->mru_prev->mru_next = md->mru_next;
! 251: md->mru_next = mon_mru_list.mru_next;
! 252: md->mru_prev = &mon_mru_list;
! 253: mon_mru_list.mru_next->mru_prev = md;
! 254: mon_mru_list.mru_next = md;
! 255:
! 256: /*
! 257: * At this point the most recent arrival is
! 258: * first in the MRU list. Decrease the counter
! 259: * by the headway, but not less than zero.
! 260: */
! 261: md->leak -= interval;
! 262: if (md->leak < 0)
! 263: md->leak = 0;
! 264: head = 1 << ntp_minpoll;
! 265: leak = md->leak + head;
! 266: limit = NTP_SHIFT * head;
! 267: #ifdef DEBUG
! 268: if (debug > 1)
! 269: printf("restrict: interval %d headway %d limit %d\n",
! 270: interval, leak, limit);
! 271: #endif
! 272:
! 273: /*
! 274: * If the minimum and average thresholds are not
! 275: * exceeded, douse the RES_LIMITED and RES_KOD
! 276: * bits and increase the counter by the headway
! 277: * increment. Note that we give a 1-s grace for
! 278: * the minimum threshold and a 2-s grace for the
! 279: * headway increment. If one or both thresholds
! 280: * are exceeded and the old counter is less than
! 281: * the average threshold, set the counter to the
! 282: * average threshold plus the inrcrment and
! 283: * leave the RES_KOD bit lit. Othewise, leave
! 284: * the counter alone and douse the RES_KOD bit.
! 285: * This rate-limits the KoDs to no less than the
! 286: * average headway.
! 287: */
! 288: if (interval + 1 >= (1 << ntp_minpkt) &&
! 289: leak < limit) {
! 290: md->leak = leak - 2;
! 291: md->flags &= ~(RES_LIMITED | RES_KOD);
! 292: } else if (md->leak < limit) {
! 293: md->leak = limit + head;
! 294: } else {
! 295: md->flags &= ~RES_KOD;
! 296: }
! 297: return (md->flags);
! 298: }
! 299: md = md->hash_next;
! 300: }
! 301:
! 302: /*
! 303: * If we got here, this is the first we've heard of this
! 304: * guy. Get him some memory, either from the free list
! 305: * or from the tail of the MRU list.
! 306: */
! 307: if (mon_free == NULL && mon_total_mem >= MAXMONMEM) {
! 308:
! 309: /*
! 310: * Preempt from the MRU list if old enough.
! 311: */
! 312: md = mon_mru_list.mru_prev;
! 313: if (ntp_random() / (2. * FRAC) > (double)(current_time
! 314: - md->lasttime) / mon_age)
! 315: return (flags & ~(RES_LIMITED | RES_KOD));
! 316:
! 317: md->mru_prev->mru_next = &mon_mru_list;
! 318: mon_mru_list.mru_prev = md->mru_prev;
! 319: remove_from_hash(md);
! 320: } else {
! 321: if (mon_free == NULL)
! 322: mon_getmoremem();
! 323: md = mon_free;
! 324: mon_free = md->hash_next;
! 325: }
! 326:
! 327: /*
! 328: * Got one, initialize it
! 329: */
! 330: md->lasttime = md->firsttime = current_time;
! 331: md->count = 1;
! 332: md->flags = flags & ~(RES_LIMITED | RES_KOD);
! 333: md->leak = 0;
! 334: memset(&md->rmtadr, 0, sizeof(md->rmtadr));
! 335: memcpy(&md->rmtadr, &addr, sizeof(addr));
! 336: md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
! 337: md->mode = (u_char) mode;
! 338: md->version = PKT_VERSION(pkt->li_vn_mode);
! 339: md->interface = rbufp->dstadr;
! 340: md->cast_flags = (u_char)(((rbufp->dstadr->flags &
! 341: INT_MCASTOPEN) && rbufp->fd == md->interface->fd) ?
! 342: MDF_MCAST: rbufp->fd == md->interface->bfd ? MDF_BCAST :
! 343: MDF_UCAST);
! 344:
! 345: /*
! 346: * Drop him into front of the hash table. Also put him on top of
! 347: * the MRU list.
! 348: */
! 349: md->hash_next = mon_hash[hash];
! 350: mon_hash[hash] = md;
! 351: md->mru_next = mon_mru_list.mru_next;
! 352: md->mru_prev = &mon_mru_list;
! 353: mon_mru_list.mru_next->mru_prev = md;
! 354: mon_mru_list.mru_next = md;
! 355: return (md->flags);
! 356: }
! 357:
! 358:
! 359: /*
! 360: * mon_getmoremem - get more memory and put it on the free list
! 361: */
! 362: static void
! 363: mon_getmoremem(void)
! 364: {
! 365: register struct mon_data *md;
! 366: register int i;
! 367: struct mon_data *freedata; /* 'old' free list (null) */
! 368:
! 369: md = (struct mon_data *)emalloc(MONMEMINC *
! 370: sizeof(struct mon_data));
! 371: freedata = mon_free;
! 372: mon_free = md;
! 373: for (i = 0; i < (MONMEMINC-1); i++) {
! 374: md->hash_next = (md + 1);
! 375: md++;
! 376: }
! 377:
! 378: /*
! 379: * md now points at the last. Link in the rest of the chain.
! 380: */
! 381: md->hash_next = freedata;
! 382: mon_total_mem += MONMEMINC;
! 383: mon_mem_increments++;
! 384: }
! 385:
! 386: static void
! 387: remove_from_hash(
! 388: struct mon_data *md
! 389: )
! 390: {
! 391: register u_int hash;
! 392: register struct mon_data *md_prev;
! 393:
! 394: hash = MON_HASH(&md->rmtadr);
! 395: if (mon_hash[hash] == md) {
! 396: mon_hash[hash] = md->hash_next;
! 397: } else {
! 398: md_prev = mon_hash[hash];
! 399: while (md_prev->hash_next != md) {
! 400: md_prev = md_prev->hash_next;
! 401: if (md_prev == NULL) {
! 402: /* logic error */
! 403: return;
! 404: }
! 405: }
! 406: md_prev->hash_next = md->hash_next;
! 407: }
! 408: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>