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>