Annotation of sys/altq/altq_cdnr.c, Revision 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