Annotation of embedaddon/ntp/ntpd/ntp_monitor.c, revision 1.1.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>