[BACK]Return to altq_cdnr.c CVS log [TXT][DIR] Up to [local] / sys / altq

Annotation of sys/altq/altq_cdnr.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: altq_cdnr.c,v 1.7 2002/12/16 17:27:20 henning Exp $   */
                      2: /*     $KAME: altq_cdnr.c,v 1.8 2000/12/14 08:12:45 thorpej Exp $      */
                      3:
                      4: /*
                      5:  * Copyright (C) 1999-2000
                      6:  *     Sony Computer Science Laboratories Inc.  All rights reserved.
                      7:  *
                      8:  * Redistribution and use in source and binary forms, with or without
                      9:  * modification, are permitted provided that the following conditions
                     10:  * are met:
                     11:  * 1. Redistributions of source code must retain the above copyright
                     12:  *    notice, this list of conditions and the following disclaimer.
                     13:  * 2. Redistributions in binary form must reproduce the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer in the
                     15:  *    documentation and/or other materials provided with the distribution.
                     16:  *
                     17:  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
                     18:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     19:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     20:  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
                     21:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     22:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     23:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     24:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     25:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     26:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     27:  * SUCH DAMAGE.
                     28:  */
                     29:
                     30: #include <sys/param.h>
                     31: #include <sys/malloc.h>
                     32: #include <sys/mbuf.h>
                     33: #include <sys/socket.h>
                     34: #include <sys/systm.h>
                     35: #include <sys/proc.h>
                     36: #include <sys/errno.h>
                     37: #include <sys/kernel.h>
                     38: #include <sys/queue.h>
                     39:
                     40: #include <net/if.h>
                     41: #include <net/if_types.h>
                     42: #include <netinet/in.h>
                     43: #include <netinet/in_systm.h>
                     44: #include <netinet/ip.h>
                     45: #ifdef INET6
                     46: #include <netinet/ip6.h>
                     47: #endif
                     48:
                     49: #include <altq/altq.h>
                     50: #include <altq/altq_cdnr.h>
                     51:
                     52: /*
                     53:  * diffserv traffic conditioning module
                     54:  */
                     55:
                     56: int altq_cdnr_enabled = 0;
                     57:
                     58: /* cdnr_list keeps all cdnr's allocated. */
                     59: static LIST_HEAD(, top_cdnr) tcb_list;
                     60:
                     61: static int altq_cdnr_input(struct mbuf *, int);
                     62: static struct top_cdnr *tcb_lookup(char *ifname);
                     63: static struct cdnr_block *cdnr_handle2cb(u_long);
                     64: static u_long cdnr_cb2handle(struct cdnr_block *);
                     65: static void *cdnr_cballoc(struct top_cdnr *, int,
                     66:        struct tc_action *(*)(struct cdnr_block *, struct cdnr_pktinfo *));
                     67: static void cdnr_cbdestroy(void *);
                     68: static int tca_verify_action(struct tc_action *);
                     69: static void tca_import_action(struct tc_action *, struct tc_action *);
                     70: static void tca_invalidate_action(struct tc_action *);
                     71:
                     72: static int generic_element_destroy(struct cdnr_block *);
                     73: static struct top_cdnr *top_create(struct ifaltq *);
                     74: static int top_destroy(struct top_cdnr *);
                     75: static struct cdnr_block *element_create(struct top_cdnr *,
                     76:                                              struct tc_action *);
                     77: static int element_destroy(struct cdnr_block *);
                     78: static void tb_import_profile(struct tbe *, struct tb_profile *);
                     79: static struct tbmeter *tbm_create(struct top_cdnr *, struct tb_profile *,
                     80:                           struct tc_action *, struct tc_action *);
                     81: static int tbm_destroy(struct tbmeter *);
                     82: static struct tc_action *tbm_input(struct cdnr_block *,
                     83:                                        struct cdnr_pktinfo *);
                     84: static struct trtcm *trtcm_create(struct top_cdnr *,
                     85:                  struct tb_profile *, struct tb_profile *,
                     86:                  struct tc_action *, struct tc_action *, struct tc_action *,
                     87:                  int);
                     88: static int trtcm_destroy(struct trtcm *);
                     89: static struct tc_action *trtcm_input(struct cdnr_block *,
                     90:                                          struct cdnr_pktinfo *);
                     91: static struct tswtcm *tswtcm_create(struct top_cdnr *,
                     92:                  u_int32_t, u_int32_t, u_int32_t,
                     93:                  struct tc_action *, struct tc_action *, struct tc_action *);
                     94: static int tswtcm_destroy(struct tswtcm *);
                     95: static struct tc_action *tswtcm_input(struct cdnr_block *,
                     96:                                           struct cdnr_pktinfo *);
                     97:
                     98: static int cdnrcmd_if_attach(char *);
                     99: static int cdnrcmd_if_detach(char *);
                    100: static int cdnrcmd_add_element(struct cdnr_add_element *);
                    101: static int cdnrcmd_delete_element(struct cdnr_delete_element *);
                    102: static int cdnrcmd_add_tbm(struct cdnr_add_tbmeter *);
                    103: static int cdnrcmd_modify_tbm(struct cdnr_modify_tbmeter *);
                    104: static int cdnrcmd_tbm_stats(struct cdnr_tbmeter_stats *);
                    105: static int cdnrcmd_add_trtcm(struct cdnr_add_trtcm *);
                    106: static int cdnrcmd_modify_trtcm(struct cdnr_modify_trtcm *);
                    107: static int cdnrcmd_tcm_stats(struct cdnr_tcm_stats *);
                    108: static int cdnrcmd_add_tswtcm(struct cdnr_add_tswtcm *);
                    109: static int cdnrcmd_modify_tswtcm(struct cdnr_modify_tswtcm *);
                    110: static int cdnrcmd_get_stats(struct cdnr_get_stats *);
                    111:
                    112: #if 1
                    113: /* dummy */
                    114: int cdnr_dummy(void);
                    115:
                    116: int cdnr_dummy(void)
                    117: {
                    118:        altq_cdnr_input(NULL, 0);
                    119:
                    120:        cdnrcmd_if_attach(NULL);
                    121:        cdnrcmd_if_detach(NULL);
                    122:        cdnrcmd_add_element(NULL);
                    123:        cdnrcmd_delete_element(NULL);
                    124:        cdnrcmd_add_tbm(NULL);
                    125:        cdnrcmd_modify_tbm(NULL);
                    126:        cdnrcmd_tbm_stats(NULL);
                    127:        cdnrcmd_add_trtcm(NULL);
                    128:        cdnrcmd_modify_trtcm(NULL);
                    129:        cdnrcmd_tcm_stats(NULL);
                    130:        cdnrcmd_add_tswtcm(NULL);
                    131:        cdnrcmd_modify_tswtcm(NULL);
                    132:        cdnrcmd_get_stats(NULL);
                    133:        return (0);
                    134: }
                    135:
                    136: #endif
                    137:
                    138: /*
                    139:  * top level input function called from ip_input.
                    140:  * should be called before converting header fields to host-byte-order.
                    141:  */
                    142: int
                    143: altq_cdnr_input(m, af)
                    144:        struct mbuf     *m;
                    145:        int             af;     /* address family */
                    146: {
                    147:        struct ifnet            *ifp;
                    148:        struct ip               *ip;
                    149:        struct top_cdnr         *top;
                    150:        struct tc_action        *tca;
                    151:        struct cdnr_block       *cb;
                    152:        struct cdnr_pktinfo     pktinfo;
                    153:
                    154:        ifp = m->m_pkthdr.rcvif;
                    155:        if (!ALTQ_IS_CNDTNING(&ifp->if_snd))
                    156:                /* traffic conditioner is not enabled on this interface */
                    157:                return (1);
                    158:
                    159:        top = ifp->if_snd.altq_cdnr;
                    160:
                    161:        ip = mtod(m, struct ip *);
                    162: #ifdef INET6
                    163:        if (af == AF_INET6) {
                    164:                u_int32_t flowlabel;
                    165:
                    166:                flowlabel = ((struct ip6_hdr *)ip)->ip6_flow;
                    167:                pktinfo.pkt_dscp = (ntohl(flowlabel) >> 20) & DSCP_MASK;
                    168:        } else
                    169: #endif
                    170:                pktinfo.pkt_dscp = ip->ip_tos & DSCP_MASK;
                    171:        pktinfo.pkt_len = m_pktlen(m);
                    172:
                    173:        tca = NULL;
                    174:
                    175: #if 0
                    176:        cb = acc_classify(&top->tc_classifier, m, af);
                    177: #endif
                    178:        if (cb != NULL)
                    179:                tca = &cb->cb_action;
                    180:
                    181:        if (tca == NULL)
                    182:                tca = &top->tc_block.cb_action;
                    183:
                    184:        while (1) {
                    185:                PKTCNTR_ADD(&top->tc_cnts[tca->tca_code], pktinfo.pkt_len);
                    186:
                    187:                switch (tca->tca_code) {
                    188:                case TCACODE_PASS:
                    189:                        return (1);
                    190:                case TCACODE_DROP:
                    191:                        m_freem(m);
                    192:                        return (0);
                    193:                case TCACODE_RETURN:
                    194:                        return (0);
                    195:                case TCACODE_MARK:
                    196: #ifdef INET6
                    197:                        if (af == AF_INET6) {
                    198:                                struct ip6_hdr *ip6 = (struct ip6_hdr *)ip;
                    199:                                u_int32_t flowlabel;
                    200:
                    201:                                flowlabel = ntohl(ip6->ip6_flow);
                    202:                                flowlabel = (tca->tca_dscp << 20) |
                    203:                                        (flowlabel & ~(DSCP_MASK << 20));
                    204:                                ip6->ip6_flow = htonl(flowlabel);
                    205:                        } else
                    206: #endif
                    207:                                ip->ip_tos = tca->tca_dscp |
                    208:                                        (ip->ip_tos & DSCP_CUMASK);
                    209:                        return (1);
                    210:                case TCACODE_NEXT:
                    211:                        cb = tca->tca_next;
                    212:                        tca = (*cb->cb_input)(cb, &pktinfo);
                    213:                        break;
                    214:                case TCACODE_NONE:
                    215:                default:
                    216:                        return (1);
                    217:                }
                    218:        }
                    219: }
                    220:
                    221: static struct top_cdnr *
                    222: tcb_lookup(ifname)
                    223:        char *ifname;
                    224: {
                    225:        struct top_cdnr *top;
                    226:        struct ifnet *ifp;
                    227:
                    228:        if ((ifp = ifunit(ifname)) != NULL)
                    229:                LIST_FOREACH(top, &tcb_list, tc_next)
                    230:                        if (top->tc_ifq->altq_ifp == ifp)
                    231:                                return (top);
                    232:        return (NULL);
                    233: }
                    234:
                    235: static struct cdnr_block *
                    236: cdnr_handle2cb(handle)
                    237:        u_long handle;
                    238: {
                    239:        struct cdnr_block *cb;
                    240:
                    241:        cb = (struct cdnr_block *)handle;
                    242:        if (handle != ALIGN(cb))
                    243:                return (NULL);
                    244:
                    245:        if (cb == NULL || cb->cb_handle != handle)
                    246:                return (NULL);
                    247:        return (cb);
                    248: }
                    249:
                    250: static u_long
                    251: cdnr_cb2handle(cb)
                    252:        struct cdnr_block *cb;
                    253: {
                    254:        return (cb->cb_handle);
                    255: }
                    256:
                    257: static void *
                    258: cdnr_cballoc(top, type, input_func)
                    259:        struct top_cdnr *top;
                    260:        int type;
                    261:        struct tc_action *(*input_func)(struct cdnr_block *,
                    262:                                        struct cdnr_pktinfo *);
                    263: {
                    264:        struct cdnr_block *cb;
                    265:        int size;
                    266:
                    267:        switch (type) {
                    268:        case TCETYPE_TOP:
                    269:                size = sizeof(struct top_cdnr);
                    270:                break;
                    271:        case TCETYPE_ELEMENT:
                    272:                size = sizeof(struct cdnr_block);
                    273:                break;
                    274:        case TCETYPE_TBMETER:
                    275:                size = sizeof(struct tbmeter);
                    276:                break;
                    277:        case TCETYPE_TRTCM:
                    278:                size = sizeof(struct trtcm);
                    279:                break;
                    280:        case TCETYPE_TSWTCM:
                    281:                size = sizeof(struct tswtcm);
                    282:                break;
                    283:        default:
                    284:                return (NULL);
                    285:        }
                    286:
                    287:        MALLOC(cb, struct cdnr_block *, size, M_DEVBUF, M_WAITOK);
                    288:        if (cb == NULL)
                    289:                return (NULL);
                    290:        bzero(cb, size);
                    291:
                    292:        cb->cb_len = size;
                    293:        cb->cb_type = type;
                    294:        cb->cb_ref = 0;
                    295:        cb->cb_handle = (u_long)cb;
                    296:        if (top == NULL)
                    297:                cb->cb_top = (struct top_cdnr *)cb;
                    298:        else
                    299:                cb->cb_top = top;
                    300:
                    301:        if (input_func != NULL) {
                    302:                /*
                    303:                 * if this cdnr has an action function,
                    304:                 * make tc_action to call itself.
                    305:                 */
                    306:                cb->cb_action.tca_code = TCACODE_NEXT;
                    307:                cb->cb_action.tca_next = cb;
                    308:                cb->cb_input = input_func;
                    309:        } else
                    310:                cb->cb_action.tca_code = TCACODE_NONE;
                    311:
                    312:        /* if this isn't top, register the element to the top level cdnr */
                    313:        if (top != NULL)
                    314:                LIST_INSERT_HEAD(&top->tc_elements, cb, cb_next);
                    315:
                    316:        return ((void *)cb);
                    317: }
                    318:
                    319: static void
                    320: cdnr_cbdestroy(cblock)
                    321:        void *cblock;
                    322: {
                    323:        struct cdnr_block *cb = cblock;
                    324:
                    325:        /* remove from the top level cdnr */
                    326:        if (cb->cb_top != cblock)
                    327:                LIST_REMOVE(cb, cb_next);
                    328:
                    329:        FREE(cb, M_DEVBUF);
                    330: }
                    331:
                    332: /*
                    333:  * conditioner common destroy routine
                    334:  */
                    335: static int
                    336: generic_element_destroy(cb)
                    337:        struct cdnr_block *cb;
                    338: {
                    339:        int error = 0;
                    340:
                    341:        switch (cb->cb_type) {
                    342:        case TCETYPE_TOP:
                    343:                error = top_destroy((struct top_cdnr *)cb);
                    344:                break;
                    345:        case TCETYPE_ELEMENT:
                    346:                error = element_destroy(cb);
                    347:                break;
                    348:        case TCETYPE_TBMETER:
                    349:                error = tbm_destroy((struct tbmeter *)cb);
                    350:                break;
                    351:        case TCETYPE_TRTCM:
                    352:                error = trtcm_destroy((struct trtcm *)cb);
                    353:                break;
                    354:        case TCETYPE_TSWTCM:
                    355:                error = tswtcm_destroy((struct tswtcm *)cb);
                    356:                break;
                    357:        default:
                    358:                error = EINVAL;
                    359:        }
                    360:        return (error);
                    361: }
                    362:
                    363: static int
                    364: tca_verify_action(utca)
                    365:        struct tc_action *utca;
                    366: {
                    367:        switch (utca->tca_code) {
                    368:        case TCACODE_PASS:
                    369:        case TCACODE_DROP:
                    370:        case TCACODE_MARK:
                    371:                /* these are ok */
                    372:                break;
                    373:
                    374:        case TCACODE_HANDLE:
                    375:                /* verify handle value */
                    376:                if (cdnr_handle2cb(utca->tca_handle) == NULL)
                    377:                        return (-1);
                    378:                break;
                    379:
                    380:        case TCACODE_NONE:
                    381:        case TCACODE_RETURN:
                    382:        case TCACODE_NEXT:
                    383:        default:
                    384:                /* should not be passed from a user */
                    385:                return (-1);
                    386:        }
                    387:        return (0);
                    388: }
                    389:
                    390: static void
                    391: tca_import_action(ktca, utca)
                    392:        struct tc_action *ktca, *utca;
                    393: {
                    394:        struct cdnr_block *cb;
                    395:
                    396:        *ktca = *utca;
                    397:        if (ktca->tca_code == TCACODE_HANDLE) {
                    398:                cb = cdnr_handle2cb(ktca->tca_handle);
                    399:                if (cb == NULL) {
                    400:                        ktca->tca_code = TCACODE_NONE;
                    401:                        return;
                    402:                }
                    403:                ktca->tca_code = TCACODE_NEXT;
                    404:                ktca->tca_next = cb;
                    405:                cb->cb_ref++;
                    406:        } else if (ktca->tca_code == TCACODE_MARK) {
                    407:                ktca->tca_dscp &= DSCP_MASK;
                    408:        }
                    409:        return;
                    410: }
                    411:
                    412: static void
                    413: tca_invalidate_action(tca)
                    414:        struct tc_action *tca;
                    415: {
                    416:        struct cdnr_block *cb;
                    417:
                    418:        if (tca->tca_code == TCACODE_NEXT) {
                    419:                cb = tca->tca_next;
                    420:                if (cb == NULL)
                    421:                        return;
                    422:                cb->cb_ref--;
                    423:        }
                    424:        tca->tca_code = TCACODE_NONE;
                    425: }
                    426:
                    427: /*
                    428:  * top level traffic conditioner
                    429:  */
                    430: static struct top_cdnr *
                    431: top_create(ifq)
                    432:        struct ifaltq *ifq;
                    433: {
                    434:        struct top_cdnr *top;
                    435:
                    436:        if ((top = cdnr_cballoc(NULL, TCETYPE_TOP, NULL)) == NULL)
                    437:                return (NULL);
                    438:
                    439:        top->tc_ifq = ifq;
                    440:        /* set default action for the top level conditioner */
                    441:        top->tc_block.cb_action.tca_code = TCACODE_PASS;
                    442:
                    443:        LIST_INSERT_HEAD(&tcb_list, top, tc_next);
                    444:
                    445:        ifq->altq_cdnr = top;
                    446:
                    447:        return (top);
                    448: }
                    449:
                    450: static int
                    451: top_destroy(top)
                    452:        struct top_cdnr *top;
                    453: {
                    454:        struct cdnr_block *cb;
                    455:
                    456:        if (ALTQ_IS_CNDTNING(top->tc_ifq))
                    457:                ALTQ_CLEAR_CNDTNING(top->tc_ifq);
                    458:        top->tc_ifq->altq_cdnr = NULL;
                    459:
                    460:        /*
                    461:         * destroy all the conditioner elements belonging to this interface
                    462:         */
                    463:        while ((cb = LIST_FIRST(&top->tc_elements)) != NULL) {
                    464:                while (cb != NULL && cb->cb_ref > 0)
                    465:                        cb = LIST_NEXT(cb, cb_next);
                    466:                if (cb != NULL)
                    467:                        generic_element_destroy(cb);
                    468:        }
                    469:
                    470:        LIST_REMOVE(top, tc_next);
                    471:
                    472:        cdnr_cbdestroy(top);
                    473:
                    474:        /* if there is no active conditioner, remove the input hook */
                    475:        if (altq_input != NULL) {
                    476:                LIST_FOREACH(top, &tcb_list, tc_next)
                    477:                        if (ALTQ_IS_CNDTNING(top->tc_ifq))
                    478:                                break;
                    479:                if (top == NULL)
                    480:                        altq_input = NULL;
                    481:        }
                    482:
                    483:        return (0);
                    484: }
                    485:
                    486: /*
                    487:  * simple tc elements without input function (e.g., dropper and makers).
                    488:  */
                    489: static struct cdnr_block *
                    490: element_create(top, action)
                    491:        struct top_cdnr *top;
                    492:        struct tc_action *action;
                    493: {
                    494:        struct cdnr_block *cb;
                    495:
                    496:        if (tca_verify_action(action) < 0)
                    497:                return (NULL);
                    498:
                    499:        if ((cb = cdnr_cballoc(top, TCETYPE_ELEMENT, NULL)) == NULL)
                    500:                return (NULL);
                    501:
                    502:        tca_import_action(&cb->cb_action, action);
                    503:
                    504:        return (cb);
                    505: }
                    506:
                    507: static int
                    508: element_destroy(cb)
                    509:        struct cdnr_block *cb;
                    510: {
                    511:        if (cb->cb_ref > 0)
                    512:                return (EBUSY);
                    513:
                    514:        tca_invalidate_action(&cb->cb_action);
                    515:
                    516:        cdnr_cbdestroy(cb);
                    517:        return (0);
                    518: }
                    519:
                    520: /*
                    521:  * internal representation of token bucket parameters
                    522:  *     rate:   byte_per_unittime << 32
                    523:  *             (((bits_per_sec) / 8) << 32) / machclk_freq
                    524:  *     depth:  byte << 32
                    525:  *
                    526:  */
                    527: #define        TB_SHIFT        32
                    528: #define        TB_SCALE(x)     ((u_int64_t)(x) << TB_SHIFT)
                    529: #define        TB_UNSCALE(x)   ((x) >> TB_SHIFT)
                    530:
                    531: static void
                    532: tb_import_profile(tb, profile)
                    533:        struct tbe *tb;
                    534:        struct tb_profile *profile;
                    535: {
                    536:        tb->rate = TB_SCALE(profile->rate / 8) / machclk_freq;
                    537:        tb->depth = TB_SCALE(profile->depth);
                    538:        if (tb->rate > 0)
                    539:                tb->filluptime = tb->depth / tb->rate;
                    540:        else
                    541:                tb->filluptime = 0xffffffffffffffffLL;
                    542:        tb->token = tb->depth;
                    543:        tb->last = read_machclk();
                    544: }
                    545:
                    546: /*
                    547:  * simple token bucket meter
                    548:  */
                    549: static struct tbmeter *
                    550: tbm_create(top, profile, in_action, out_action)
                    551:        struct top_cdnr *top;
                    552:        struct tb_profile *profile;
                    553:        struct tc_action *in_action, *out_action;
                    554: {
                    555:        struct tbmeter *tbm = NULL;
                    556:
                    557:        if (tca_verify_action(in_action) < 0
                    558:            || tca_verify_action(out_action) < 0)
                    559:                return (NULL);
                    560:
                    561:        if ((tbm = cdnr_cballoc(top, TCETYPE_TBMETER,
                    562:                                tbm_input)) == NULL)
                    563:                return (NULL);
                    564:
                    565:        tb_import_profile(&tbm->tb, profile);
                    566:
                    567:        tca_import_action(&tbm->in_action, in_action);
                    568:        tca_import_action(&tbm->out_action, out_action);
                    569:
                    570:        return (tbm);
                    571: }
                    572:
                    573: static int
                    574: tbm_destroy(tbm)
                    575:        struct tbmeter *tbm;
                    576: {
                    577:        if (tbm->cdnrblk.cb_ref > 0)
                    578:                return (EBUSY);
                    579:
                    580:        tca_invalidate_action(&tbm->in_action);
                    581:        tca_invalidate_action(&tbm->out_action);
                    582:
                    583:        cdnr_cbdestroy(tbm);
                    584:        return (0);
                    585: }
                    586:
                    587: static struct tc_action *
                    588: tbm_input(cb, pktinfo)
                    589:        struct cdnr_block *cb;
                    590:        struct cdnr_pktinfo *pktinfo;
                    591: {
                    592:        struct tbmeter *tbm = (struct tbmeter *)cb;
                    593:        u_int64_t       len;
                    594:        u_int64_t       interval, now;
                    595:
                    596:        len = TB_SCALE(pktinfo->pkt_len);
                    597:
                    598:        if (tbm->tb.token < len) {
                    599:                now = read_machclk();
                    600:                interval = now - tbm->tb.last;
                    601:                if (interval >= tbm->tb.filluptime)
                    602:                        tbm->tb.token = tbm->tb.depth;
                    603:                else {
                    604:                        tbm->tb.token += interval * tbm->tb.rate;
                    605:                        if (tbm->tb.token > tbm->tb.depth)
                    606:                                tbm->tb.token = tbm->tb.depth;
                    607:                }
                    608:                tbm->tb.last = now;
                    609:        }
                    610:
                    611:        if (tbm->tb.token < len) {
                    612:                PKTCNTR_ADD(&tbm->out_cnt, pktinfo->pkt_len);
                    613:                return (&tbm->out_action);
                    614:        }
                    615:
                    616:        tbm->tb.token -= len;
                    617:        PKTCNTR_ADD(&tbm->in_cnt, pktinfo->pkt_len);
                    618:        return (&tbm->in_action);
                    619: }
                    620:
                    621: /*
                    622:  * two rate three color marker
                    623:  * as described in draft-heinanen-diffserv-trtcm-01.txt
                    624:  */
                    625: static struct trtcm *
                    626: trtcm_create(top, cmtd_profile, peak_profile,
                    627:             green_action, yellow_action, red_action, coloraware)
                    628:        struct top_cdnr *top;
                    629:        struct tb_profile *cmtd_profile, *peak_profile;
                    630:        struct tc_action *green_action, *yellow_action, *red_action;
                    631:        int     coloraware;
                    632: {
                    633:        struct trtcm *tcm = NULL;
                    634:
                    635:        if (tca_verify_action(green_action) < 0
                    636:            || tca_verify_action(yellow_action) < 0
                    637:            || tca_verify_action(red_action) < 0)
                    638:                return (NULL);
                    639:
                    640:        if ((tcm = cdnr_cballoc(top, TCETYPE_TRTCM,
                    641:                                trtcm_input)) == NULL)
                    642:                return (NULL);
                    643:
                    644:        tb_import_profile(&tcm->cmtd_tb, cmtd_profile);
                    645:        tb_import_profile(&tcm->peak_tb, peak_profile);
                    646:
                    647:        tca_import_action(&tcm->green_action, green_action);
                    648:        tca_import_action(&tcm->yellow_action, yellow_action);
                    649:        tca_import_action(&tcm->red_action, red_action);
                    650:
                    651:        /* set dscps to use */
                    652:        if (tcm->green_action.tca_code == TCACODE_MARK)
                    653:                tcm->green_dscp = tcm->green_action.tca_dscp & DSCP_MASK;
                    654:        else
                    655:                tcm->green_dscp = DSCP_AF11;
                    656:        if (tcm->yellow_action.tca_code == TCACODE_MARK)
                    657:                tcm->yellow_dscp = tcm->yellow_action.tca_dscp & DSCP_MASK;
                    658:        else
                    659:                tcm->yellow_dscp = DSCP_AF12;
                    660:        if (tcm->red_action.tca_code == TCACODE_MARK)
                    661:                tcm->red_dscp = tcm->red_action.tca_dscp & DSCP_MASK;
                    662:        else
                    663:                tcm->red_dscp = DSCP_AF13;
                    664:
                    665:        tcm->coloraware = coloraware;
                    666:
                    667:        return (tcm);
                    668: }
                    669:
                    670: static int
                    671: trtcm_destroy(tcm)
                    672:        struct trtcm *tcm;
                    673: {
                    674:        if (tcm->cdnrblk.cb_ref > 0)
                    675:                return (EBUSY);
                    676:
                    677:        tca_invalidate_action(&tcm->green_action);
                    678:        tca_invalidate_action(&tcm->yellow_action);
                    679:        tca_invalidate_action(&tcm->red_action);
                    680:
                    681:        cdnr_cbdestroy(tcm);
                    682:        return (0);
                    683: }
                    684:
                    685: static struct tc_action *
                    686: trtcm_input(cb, pktinfo)
                    687:        struct cdnr_block *cb;
                    688:        struct cdnr_pktinfo *pktinfo;
                    689: {
                    690:        struct trtcm *tcm = (struct trtcm *)cb;
                    691:        u_int64_t       len;
                    692:        u_int64_t       interval, now;
                    693:        u_int8_t        color;
                    694:
                    695:        len = TB_SCALE(pktinfo->pkt_len);
                    696:        if (tcm->coloraware) {
                    697:                color = pktinfo->pkt_dscp;
                    698:                if (color != tcm->yellow_dscp && color != tcm->red_dscp)
                    699:                        color = tcm->green_dscp;
                    700:        } else {
                    701:                /* if color-blind, precolor it as green */
                    702:                color = tcm->green_dscp;
                    703:        }
                    704:
                    705:        now = read_machclk();
                    706:        if (tcm->cmtd_tb.token < len) {
                    707:                interval = now - tcm->cmtd_tb.last;
                    708:                if (interval >= tcm->cmtd_tb.filluptime)
                    709:                        tcm->cmtd_tb.token = tcm->cmtd_tb.depth;
                    710:                else {
                    711:                        tcm->cmtd_tb.token += interval * tcm->cmtd_tb.rate;
                    712:                        if (tcm->cmtd_tb.token > tcm->cmtd_tb.depth)
                    713:                                tcm->cmtd_tb.token = tcm->cmtd_tb.depth;
                    714:                }
                    715:                tcm->cmtd_tb.last = now;
                    716:        }
                    717:        if (tcm->peak_tb.token < len) {
                    718:                interval = now - tcm->peak_tb.last;
                    719:                if (interval >= tcm->peak_tb.filluptime)
                    720:                        tcm->peak_tb.token = tcm->peak_tb.depth;
                    721:                else {
                    722:                        tcm->peak_tb.token += interval * tcm->peak_tb.rate;
                    723:                        if (tcm->peak_tb.token > tcm->peak_tb.depth)
                    724:                                tcm->peak_tb.token = tcm->peak_tb.depth;
                    725:                }
                    726:                tcm->peak_tb.last = now;
                    727:        }
                    728:
                    729:        if (color == tcm->red_dscp || tcm->peak_tb.token < len) {
                    730:                pktinfo->pkt_dscp = tcm->red_dscp;
                    731:                PKTCNTR_ADD(&tcm->red_cnt, pktinfo->pkt_len);
                    732:                return (&tcm->red_action);
                    733:        }
                    734:
                    735:        if (color == tcm->yellow_dscp || tcm->cmtd_tb.token < len) {
                    736:                pktinfo->pkt_dscp = tcm->yellow_dscp;
                    737:                tcm->peak_tb.token -= len;
                    738:                PKTCNTR_ADD(&tcm->yellow_cnt, pktinfo->pkt_len);
                    739:                return (&tcm->yellow_action);
                    740:        }
                    741:
                    742:        pktinfo->pkt_dscp = tcm->green_dscp;
                    743:        tcm->cmtd_tb.token -= len;
                    744:        tcm->peak_tb.token -= len;
                    745:        PKTCNTR_ADD(&tcm->green_cnt, pktinfo->pkt_len);
                    746:        return (&tcm->green_action);
                    747: }
                    748:
                    749: /*
                    750:  * time sliding window three color marker
                    751:  * as described in draft-fang-diffserv-tc-tswtcm-00.txt
                    752:  */
                    753: static struct tswtcm *
                    754: tswtcm_create(top, cmtd_rate, peak_rate, avg_interval,
                    755:              green_action, yellow_action, red_action)
                    756:        struct top_cdnr *top;
                    757:        u_int32_t       cmtd_rate, peak_rate, avg_interval;
                    758:        struct tc_action *green_action, *yellow_action, *red_action;
                    759: {
                    760:        struct tswtcm *tsw;
                    761:
                    762:        if (tca_verify_action(green_action) < 0
                    763:            || tca_verify_action(yellow_action) < 0
                    764:            || tca_verify_action(red_action) < 0)
                    765:                return (NULL);
                    766:
                    767:        if ((tsw = cdnr_cballoc(top, TCETYPE_TSWTCM,
                    768:                                tswtcm_input)) == NULL)
                    769:                return (NULL);
                    770:
                    771:        tca_import_action(&tsw->green_action, green_action);
                    772:        tca_import_action(&tsw->yellow_action, yellow_action);
                    773:        tca_import_action(&tsw->red_action, red_action);
                    774:
                    775:        /* set dscps to use */
                    776:        if (tsw->green_action.tca_code == TCACODE_MARK)
                    777:                tsw->green_dscp = tsw->green_action.tca_dscp & DSCP_MASK;
                    778:        else
                    779:                tsw->green_dscp = DSCP_AF11;
                    780:        if (tsw->yellow_action.tca_code == TCACODE_MARK)
                    781:                tsw->yellow_dscp = tsw->yellow_action.tca_dscp & DSCP_MASK;
                    782:        else
                    783:                tsw->yellow_dscp = DSCP_AF12;
                    784:        if (tsw->red_action.tca_code == TCACODE_MARK)
                    785:                tsw->red_dscp = tsw->red_action.tca_dscp & DSCP_MASK;
                    786:        else
                    787:                tsw->red_dscp = DSCP_AF13;
                    788:
                    789:        /* convert rates from bits/sec to bytes/sec */
                    790:        tsw->cmtd_rate = cmtd_rate / 8;
                    791:        tsw->peak_rate = peak_rate / 8;
                    792:        tsw->avg_rate = 0;
                    793:
                    794:        /* timewin is converted from msec to machine clock unit */
                    795:        tsw->timewin = (u_int64_t)machclk_freq * avg_interval / 1000;
                    796:
                    797:        return (tsw);
                    798: }
                    799:
                    800: static int
                    801: tswtcm_destroy(tsw)
                    802:        struct tswtcm *tsw;
                    803: {
                    804:        if (tsw->cdnrblk.cb_ref > 0)
                    805:                return (EBUSY);
                    806:
                    807:        tca_invalidate_action(&tsw->green_action);
                    808:        tca_invalidate_action(&tsw->yellow_action);
                    809:        tca_invalidate_action(&tsw->red_action);
                    810:
                    811:        cdnr_cbdestroy(tsw);
                    812:        return (0);
                    813: }
                    814:
                    815: static struct tc_action *
                    816: tswtcm_input(cb, pktinfo)
                    817:        struct cdnr_block *cb;
                    818:        struct cdnr_pktinfo *pktinfo;
                    819: {
                    820:        struct tswtcm   *tsw = (struct tswtcm *)cb;
                    821:        int             len;
                    822:        u_int32_t       avg_rate;
                    823:        u_int64_t       interval, now, tmp;
                    824:
                    825:        /*
                    826:         * rate estimator
                    827:         */
                    828:        len = pktinfo->pkt_len;
                    829:        now = read_machclk();
                    830:
                    831:        interval = now - tsw->t_front;
                    832:        /*
                    833:         * calculate average rate:
                    834:         *      avg = (avg * timewin + pkt_len)/(timewin + interval)
                    835:         * pkt_len needs to be multiplied by machclk_freq in order to
                    836:         * get (bytes/sec).
                    837:         * note: when avg_rate (bytes/sec) and timewin (machclk unit) are
                    838:         * less than 32 bits, the following 64-bit operation has enough
                    839:         * precision.
                    840:         */
                    841:        tmp = ((u_int64_t)tsw->avg_rate * tsw->timewin
                    842:               + (u_int64_t)len * machclk_freq) / (tsw->timewin + interval);
                    843:        tsw->avg_rate = avg_rate = (u_int32_t)tmp;
                    844:        tsw->t_front = now;
                    845:
                    846:        /*
                    847:         * marker
                    848:         */
                    849:        if (avg_rate > tsw->cmtd_rate) {
                    850:                u_int32_t randval = random() % avg_rate;
                    851:
                    852:                if (avg_rate > tsw->peak_rate) {
                    853:                        if (randval < avg_rate - tsw->peak_rate) {
                    854:                                /* mark red */
                    855:                                pktinfo->pkt_dscp = tsw->red_dscp;
                    856:                                PKTCNTR_ADD(&tsw->red_cnt, len);
                    857:                                return (&tsw->red_action);
                    858:                        } else if (randval < avg_rate - tsw->cmtd_rate)
                    859:                                goto mark_yellow;
                    860:                } else {
                    861:                        /* peak_rate >= avg_rate > cmtd_rate */
                    862:                        if (randval < avg_rate - tsw->cmtd_rate) {
                    863:                        mark_yellow:
                    864:                                pktinfo->pkt_dscp = tsw->yellow_dscp;
                    865:                                PKTCNTR_ADD(&tsw->yellow_cnt, len);
                    866:                                return (&tsw->yellow_action);
                    867:                        }
                    868:                }
                    869:        }
                    870:
                    871:        /* mark green */
                    872:        pktinfo->pkt_dscp = tsw->green_dscp;
                    873:        PKTCNTR_ADD(&tsw->green_cnt, len);
                    874:        return (&tsw->green_action);
                    875: }
                    876:
                    877: /*
                    878:  * ioctl requests
                    879:  */
                    880: static int
                    881: cdnrcmd_if_attach(ifname)
                    882:        char *ifname;
                    883: {
                    884:        struct ifnet *ifp;
                    885:        struct top_cdnr *top;
                    886:
                    887:        if ((ifp = ifunit(ifname)) == NULL)
                    888:                return (EBADF);
                    889:
                    890:        if (ifp->if_snd.altq_cdnr != NULL)
                    891:                return (EBUSY);
                    892:
                    893:        if ((top = top_create(&ifp->if_snd)) == NULL)
                    894:                return (ENOMEM);
                    895:        return (0);
                    896: }
                    897:
                    898: static int
                    899: cdnrcmd_if_detach(ifname)
                    900:        char *ifname;
                    901: {
                    902:        struct top_cdnr *top;
                    903:
                    904:        if ((top = tcb_lookup(ifname)) == NULL)
                    905:                return (EBADF);
                    906:
                    907:        return top_destroy(top);
                    908: }
                    909:
                    910: static int
                    911: cdnrcmd_add_element(ap)
                    912:        struct cdnr_add_element *ap;
                    913: {
                    914:        struct top_cdnr *top;
                    915:        struct cdnr_block *cb;
                    916:
                    917:        if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL)
                    918:                return (EBADF);
                    919:
                    920:        cb = element_create(top, &ap->action);
                    921:        if (cb == NULL)
                    922:                return (EINVAL);
                    923:        /* return a class handle to the user */
                    924:        ap->cdnr_handle = cdnr_cb2handle(cb);
                    925:        return (0);
                    926: }
                    927:
                    928: static int
                    929: cdnrcmd_delete_element(ap)
                    930:        struct cdnr_delete_element *ap;
                    931: {
                    932:        struct top_cdnr *top;
                    933:        struct cdnr_block *cb;
                    934:
                    935:        if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL)
                    936:                return (EBADF);
                    937:
                    938:        if ((cb = cdnr_handle2cb(ap->cdnr_handle)) == NULL)
                    939:                return (EINVAL);
                    940:
                    941:        if (cb->cb_type != TCETYPE_ELEMENT)
                    942:                return generic_element_destroy(cb);
                    943:
                    944:        return element_destroy(cb);
                    945: }
                    946:
                    947: static int
                    948: cdnrcmd_add_tbm(ap)
                    949:        struct cdnr_add_tbmeter *ap;
                    950: {
                    951:        struct top_cdnr *top;
                    952:        struct tbmeter *tbm;
                    953:
                    954:        if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL)
                    955:                return (EBADF);
                    956:
                    957:        tbm = tbm_create(top, &ap->profile, &ap->in_action, &ap->out_action);
                    958:        if (tbm == NULL)
                    959:                return (EINVAL);
                    960:        /* return a class handle to the user */
                    961:        ap->cdnr_handle = cdnr_cb2handle(&tbm->cdnrblk);
                    962:        return (0);
                    963: }
                    964:
                    965: static int
                    966: cdnrcmd_modify_tbm(ap)
                    967:        struct cdnr_modify_tbmeter *ap;
                    968: {
                    969:        struct tbmeter *tbm;
                    970:
                    971:        if ((tbm = (struct tbmeter *)cdnr_handle2cb(ap->cdnr_handle)) == NULL)
                    972:                return (EINVAL);
                    973:
                    974:        tb_import_profile(&tbm->tb, &ap->profile);
                    975:
                    976:        return (0);
                    977: }
                    978:
                    979: static int
                    980: cdnrcmd_tbm_stats(ap)
                    981:        struct cdnr_tbmeter_stats *ap;
                    982: {
                    983:        struct tbmeter *tbm;
                    984:
                    985:        if ((tbm = (struct tbmeter *)cdnr_handle2cb(ap->cdnr_handle)) == NULL)
                    986:                return (EINVAL);
                    987:
                    988:        ap->in_cnt = tbm->in_cnt;
                    989:        ap->out_cnt = tbm->out_cnt;
                    990:
                    991:        return (0);
                    992: }
                    993:
                    994: static int
                    995: cdnrcmd_add_trtcm(ap)
                    996:        struct cdnr_add_trtcm *ap;
                    997: {
                    998:        struct top_cdnr *top;
                    999:        struct trtcm *tcm;
                   1000:
                   1001:        if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL)
                   1002:                return (EBADF);
                   1003:
                   1004:        tcm = trtcm_create(top, &ap->cmtd_profile, &ap->peak_profile,
                   1005:                           &ap->green_action, &ap->yellow_action,
                   1006:                           &ap->red_action, ap->coloraware);
                   1007:        if (tcm == NULL)
                   1008:                return (EINVAL);
                   1009:
                   1010:        /* return a class handle to the user */
                   1011:        ap->cdnr_handle = cdnr_cb2handle(&tcm->cdnrblk);
                   1012:        return (0);
                   1013: }
                   1014:
                   1015: static int
                   1016: cdnrcmd_modify_trtcm(ap)
                   1017:        struct cdnr_modify_trtcm *ap;
                   1018: {
                   1019:        struct trtcm *tcm;
                   1020:
                   1021:        if ((tcm = (struct trtcm *)cdnr_handle2cb(ap->cdnr_handle)) == NULL)
                   1022:                return (EINVAL);
                   1023:
                   1024:        tb_import_profile(&tcm->cmtd_tb, &ap->cmtd_profile);
                   1025:        tb_import_profile(&tcm->peak_tb, &ap->peak_profile);
                   1026:
                   1027:        return (0);
                   1028: }
                   1029:
                   1030: static int
                   1031: cdnrcmd_tcm_stats(ap)
                   1032:        struct cdnr_tcm_stats *ap;
                   1033: {
                   1034:        struct cdnr_block *cb;
                   1035:
                   1036:        if ((cb = cdnr_handle2cb(ap->cdnr_handle)) == NULL)
                   1037:                return (EINVAL);
                   1038:
                   1039:        if (cb->cb_type == TCETYPE_TRTCM) {
                   1040:            struct trtcm *tcm = (struct trtcm *)cb;
                   1041:
                   1042:            ap->green_cnt = tcm->green_cnt;
                   1043:            ap->yellow_cnt = tcm->yellow_cnt;
                   1044:            ap->red_cnt = tcm->red_cnt;
                   1045:        } else if (cb->cb_type == TCETYPE_TSWTCM) {
                   1046:            struct tswtcm *tsw = (struct tswtcm *)cb;
                   1047:
                   1048:            ap->green_cnt = tsw->green_cnt;
                   1049:            ap->yellow_cnt = tsw->yellow_cnt;
                   1050:            ap->red_cnt = tsw->red_cnt;
                   1051:        } else
                   1052:            return (EINVAL);
                   1053:
                   1054:        return (0);
                   1055: }
                   1056:
                   1057: static int
                   1058: cdnrcmd_add_tswtcm(ap)
                   1059:        struct cdnr_add_tswtcm *ap;
                   1060: {
                   1061:        struct top_cdnr *top;
                   1062:        struct tswtcm *tsw;
                   1063:
                   1064:        if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL)
                   1065:                return (EBADF);
                   1066:
                   1067:        if (ap->cmtd_rate > ap->peak_rate)
                   1068:                return (EINVAL);
                   1069:
                   1070:        tsw = tswtcm_create(top, ap->cmtd_rate, ap->peak_rate,
                   1071:                            ap->avg_interval, &ap->green_action,
                   1072:                            &ap->yellow_action, &ap->red_action);
                   1073:        if (tsw == NULL)
                   1074:            return (EINVAL);
                   1075:
                   1076:        /* return a class handle to the user */
                   1077:        ap->cdnr_handle = cdnr_cb2handle(&tsw->cdnrblk);
                   1078:        return (0);
                   1079: }
                   1080:
                   1081: static int
                   1082: cdnrcmd_modify_tswtcm(ap)
                   1083:        struct cdnr_modify_tswtcm *ap;
                   1084: {
                   1085:        struct tswtcm *tsw;
                   1086:
                   1087:        if ((tsw = (struct tswtcm *)cdnr_handle2cb(ap->cdnr_handle)) == NULL)
                   1088:                return (EINVAL);
                   1089:
                   1090:        if (ap->cmtd_rate > ap->peak_rate)
                   1091:                return (EINVAL);
                   1092:
                   1093:        /* convert rates from bits/sec to bytes/sec */
                   1094:        tsw->cmtd_rate = ap->cmtd_rate / 8;
                   1095:        tsw->peak_rate = ap->peak_rate / 8;
                   1096:        tsw->avg_rate = 0;
                   1097:
                   1098:        /* timewin is converted from msec to machine clock unit */
                   1099:        tsw->timewin = (u_int64_t)machclk_freq * ap->avg_interval / 1000;
                   1100:
                   1101:        return (0);
                   1102: }
                   1103:
                   1104: static int
                   1105: cdnrcmd_get_stats(ap)
                   1106:        struct cdnr_get_stats *ap;
                   1107: {
                   1108:        struct top_cdnr *top;
                   1109:        struct cdnr_block *cb;
                   1110:        struct tbmeter *tbm;
                   1111:        struct trtcm *tcm;
                   1112:        struct tswtcm *tsw;
                   1113:        struct tce_stats tce, *usp;
                   1114:        int error, n, nskip, nelements;
                   1115:
                   1116:        if ((top = tcb_lookup(ap->iface.cdnr_ifname)) == NULL)
                   1117:                return (EBADF);
                   1118:
                   1119:        /* copy action stats */
                   1120:        bcopy(top->tc_cnts, ap->cnts, sizeof(ap->cnts));
                   1121:
                   1122:        /* stats for each element */
                   1123:        nelements = ap->nelements;
                   1124:        usp = ap->tce_stats;
                   1125:        if (nelements <= 0 || usp == NULL)
                   1126:                return (0);
                   1127:
                   1128:        nskip = ap->nskip;
                   1129:        n = 0;
                   1130:        LIST_FOREACH(cb, &top->tc_elements, cb_next) {
                   1131:                if (nskip > 0) {
                   1132:                        nskip--;
                   1133:                        continue;
                   1134:                }
                   1135:
                   1136:                bzero(&tce, sizeof(tce));
                   1137:                tce.tce_handle = cb->cb_handle;
                   1138:                tce.tce_type = cb->cb_type;
                   1139:                switch (cb->cb_type) {
                   1140:                case TCETYPE_TBMETER:
                   1141:                        tbm = (struct tbmeter *)cb;
                   1142:                        tce.tce_cnts[0] = tbm->in_cnt;
                   1143:                        tce.tce_cnts[1] = tbm->out_cnt;
                   1144:                        break;
                   1145:                case TCETYPE_TRTCM:
                   1146:                        tcm = (struct trtcm *)cb;
                   1147:                        tce.tce_cnts[0] = tcm->green_cnt;
                   1148:                        tce.tce_cnts[1] = tcm->yellow_cnt;
                   1149:                        tce.tce_cnts[2] = tcm->red_cnt;
                   1150:                        break;
                   1151:                case TCETYPE_TSWTCM:
                   1152:                        tsw = (struct tswtcm *)cb;
                   1153:                        tce.tce_cnts[0] = tsw->green_cnt;
                   1154:                        tce.tce_cnts[1] = tsw->yellow_cnt;
                   1155:                        tce.tce_cnts[2] = tsw->red_cnt;
                   1156:                        break;
                   1157:                default:
                   1158:                        continue;
                   1159:                }
                   1160:
                   1161:                if ((error = copyout((caddr_t)&tce, (caddr_t)usp++,
                   1162:                                     sizeof(tce))) != 0)
                   1163:                        return (error);
                   1164:
                   1165:                if (++n == nelements)
                   1166:                        break;
                   1167:        }
                   1168:        ap->nelements = n;
                   1169:
                   1170:        return (0);
                   1171: }

CVSweb