1 /* $NetBSD: qop_cbq.c,v 1.5 2002/03/05 04:11:53 itojun Exp $ */ 2 /* $KAME: qop_cbq.c,v 1.6 2001/12/03 08:20:55 kjc Exp $ */ 3 /* 4 * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 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 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the SMCC Technology 20 * Development Group at Sun Microsystems, Inc. 21 * 22 * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or 23 * promote products derived from this software without specific prior 24 * written permission. 25 * 26 * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE 27 * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is 28 * provided "as is" without express or implied warranty of any kind. 29 * 30 * These notices must be retained in any copies of any part of this software. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/socket.h> 35 #include <sys/sockio.h> 36 #include <sys/ioctl.h> 37 #include <sys/fcntl.h> 38 #include <net/if.h> 39 #include <netinet/in.h> 40 #include <arpa/inet.h> 41 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <unistd.h> 45 #include <stddef.h> 46 #include <string.h> 47 #include <ctype.h> 48 #include <errno.h> 49 #include <syslog.h> 50 #include <netdb.h> 51 #include <math.h> 52 53 #include <altq/altq.h> 54 #include <altq/altq_cbq.h> 55 #include "altq_qop.h" 56 #include "qop_cbq.h" 57 58 static int qcmd_cbq_add_ctl_filters(const char *, const char *); 59 60 static int qop_cbq_enable_hook(struct ifinfo *); 61 static int qop_cbq_delete_class_hook(struct classinfo *); 62 63 static int cbq_class_spec(struct ifinfo *, u_long, u_long, u_int, int, 64 u_int, u_int, u_int, u_int, u_int, 65 u_int, cbq_class_spec_t *); 66 67 static int cbq_attach(struct ifinfo *); 68 static int cbq_detach(struct ifinfo *); 69 static int cbq_clear(struct ifinfo *); 70 static int cbq_enable(struct ifinfo *); 71 static int cbq_disable(struct ifinfo *); 72 static int cbq_add_class(struct classinfo *); 73 static int cbq_modify_class(struct classinfo *, void *); 74 static int cbq_delete_class(struct classinfo *); 75 static int cbq_add_filter(struct fltrinfo *); 76 static int cbq_delete_filter(struct fltrinfo *); 77 78 #define CTL_PBANDWIDTH 2 79 #define NS_PER_MS (1000000.0) 80 #define NS_PER_SEC (NS_PER_MS*1000.0) 81 #define RM_FILTER_GAIN 5 82 83 #define CBQ_DEVICE "/dev/altq/cbq" 84 85 static int cbq_fd = -1; 86 static int cbq_refcount = 0; 87 88 static struct qdisc_ops cbq_qdisc = { 89 ALTQT_CBQ, 90 "cbq", 91 cbq_attach, 92 cbq_detach, 93 cbq_clear, 94 cbq_enable, 95 cbq_disable, 96 cbq_add_class, 97 cbq_modify_class, 98 cbq_delete_class, 99 cbq_add_filter, 100 cbq_delete_filter, 101 }; 102 103 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) 104 105 /* 106 * parser interface 107 */ 108 int 109 cbq_interface_parser(const char *ifname, int argc, char **argv) 110 { 111 u_int bandwidth = 100000000; /* 100Mbps */ 112 u_int tbrsize = 0; 113 u_int is_efficient = 0; 114 u_int is_wrr = 1; /* weighted round-robin is default */ 115 116 /* 117 * process options 118 */ 119 while (argc > 0) { 120 if (EQUAL(*argv, "bandwidth")) { 121 argc--; argv++; 122 if (argc > 0) 123 bandwidth = atobps(*argv); 124 } else if (EQUAL(*argv, "tbrsize")) { 125 argc--; argv++; 126 if (argc > 0) 127 tbrsize = atobytes(*argv); 128 } else if (EQUAL(*argv, "efficient")) { 129 is_efficient = 1; 130 } else if (EQUAL(*argv, "cbq")) { 131 /* just skip */ 132 } else if (EQUAL(*argv, "cbq-wrr")) { 133 is_wrr = 1; 134 } else if (EQUAL(*argv, "cbq-prr")) { 135 is_wrr = 0; 136 } else { 137 LOG(LOG_ERR, 0, "Unknown keyword '%s'", *argv); 138 return (0); 139 } 140 argc--; argv++; 141 } 142 143 if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0) 144 return (0); 145 146 if (qcmd_cbq_add_if(ifname, bandwidth, 147 is_wrr, is_efficient) != 0) 148 return (0); 149 return (1); 150 } 151 152 int 153 cbq_class_parser(const char *ifname, const char *class_name, 154 const char *parent_name, int argc, char **argv) 155 { 156 const char *borrow = NULL; 157 u_int pri = 1; 158 u_int pbandwidth = 0; 159 u_int bandwidth = 0; 160 u_int maxdelay = 0; /* 0 means default */ 161 u_int maxburst = 0; /* 0 means default */ 162 u_int minburst = 0; /* 0 means default */ 163 u_int av_pkt_size = 0; /* 0 means use if mtu as default */ 164 u_int max_pkt_size = 0; /* 0 means use if mtu as default */ 165 int flags = 0; 166 cbq_tos_t admission_type = CBQ_QOS_NONE; 167 int error; 168 169 if (parent_name == NULL) 170 flags |= CBQCLF_ROOTCLASS; 171 172 while (argc > 0) { 173 if (EQUAL(*argv, "priority")) { 174 argc--; argv++; 175 if (argc > 0) 176 pri = strtoul(*argv, NULL, 0); 177 } else if (EQUAL(*argv, "default")) { 178 flags |= CBQCLF_DEFCLASS; 179 } else if (EQUAL(*argv, "control")) { 180 flags |= CBQCLF_CTLCLASS; 181 } else if (EQUAL(*argv, "admission")) { 182 argc--; argv++; 183 if (argc > 0) { 184 if (EQUAL(*argv, "guaranteed")) 185 admission_type = CBQ_QOS_GUARANTEED; 186 else if (EQUAL(*argv, "predictive")) 187 admission_type = CBQ_QOS_PREDICTIVE; 188 else if (EQUAL(*argv, "cntlload")) 189 admission_type = CBQ_QOS_CNTR_LOAD; 190 else if (EQUAL(*argv, "cntldelay")) 191 admission_type = CBQ_QOS_CNTR_DELAY; 192 else if (EQUAL(*argv, "none")) 193 admission_type = CBQ_QOS_NONE; 194 else { 195 LOG(LOG_ERR, 0, 196 "unknown admission type - %s, line %d", 197 *argv, line_no); 198 return (0); 199 } 200 } 201 } else if (EQUAL(*argv, "maxdelay")) { 202 argc--; argv++; 203 if (argc > 0) 204 maxdelay = strtoul(*argv, NULL, 0); 205 } else if (EQUAL(*argv, "borrow")) { 206 borrow = parent_name; 207 #if 1 208 /* support old style "borrow [parent]" */ 209 if (argc > 1 && 210 EQUAL(*(argv + 1), parent_name)) { 211 /* old style, skip borrow_name */ 212 argc--; argv++; 213 } 214 #endif 215 } else if (EQUAL(*argv, "pbandwidth")) { 216 argc--; argv++; 217 if (argc > 0) 218 pbandwidth = strtoul(*argv, NULL, 0); 219 if (pbandwidth > 100) { 220 LOG(LOG_ERR, 0, 221 "bad pbandwidth %d for %s!", 222 pbandwidth, class_name); 223 return (0); 224 } 225 } else if (EQUAL(*argv, "exactbandwidth")) { 226 argc--; argv++; 227 if (argc > 0) 228 bandwidth = atobps(*argv); 229 } else if (EQUAL(*argv, "maxburst")) { 230 argc--; argv++; 231 if (argc > 0) 232 maxburst = strtoul(*argv, NULL, 0); 233 } else if (EQUAL(*argv, "minburst")) { 234 argc--; argv++; 235 if (argc > 0) 236 minburst = strtoul(*argv, NULL, 0); 237 } else if (EQUAL(*argv, "packetsize")) { 238 argc--; argv++; 239 if (argc > 0) 240 av_pkt_size = atobytes(*argv); 241 } else if (EQUAL(*argv, "maxpacketsize")) { 242 argc--; argv++; 243 if (argc > 0) 244 max_pkt_size = atobytes(*argv); 245 } else if (EQUAL(*argv, "red")) { 246 flags |= CBQCLF_RED; 247 } else if (EQUAL(*argv, "ecn")) { 248 flags |= CBQCLF_ECN; 249 } else if (EQUAL(*argv, "flowvalve")) { 250 flags |= CBQCLF_FLOWVALVE; 251 } else if (EQUAL(*argv, "rio")) { 252 flags |= CBQCLF_RIO; 253 } else if (EQUAL(*argv, "cleardscp")) { 254 flags |= CBQCLF_CLEARDSCP; 255 } else { 256 LOG(LOG_ERR, 0, 257 "Unknown keyword '%s' in %s, line %d", 258 *argv, altqconfigfile, line_no); 259 return (0); 260 } 261 262 argc--; argv++; 263 } 264 265 if ((flags & (CBQCLF_RED|CBQCLF_RIO)) == (CBQCLF_RED|CBQCLF_RIO)) { 266 LOG(LOG_ERR, 0, 267 "both red and rio defined on interface '%s'", 268 ifname); 269 return (0); 270 } 271 if ((flags & (CBQCLF_ECN|CBQCLF_FLOWVALVE)) 272 && (flags & (CBQCLF_RED|CBQCLF_RIO)) == 0) 273 flags |= CBQCLF_RED; 274 275 if (strcmp("ctl_class", class_name) == 0) 276 flags |= CBQCLF_CTLCLASS; 277 278 if (bandwidth == 0 && pbandwidth != 0) { 279 struct ifinfo *ifinfo; 280 281 if ((ifinfo = ifname2ifinfo(ifname)) != NULL) 282 bandwidth = ifinfo->bandwidth / 100 * pbandwidth; 283 } 284 285 error = qcmd_cbq_add_class(ifname, class_name, parent_name, borrow, 286 pri, bandwidth, 287 maxdelay, maxburst, minburst, 288 av_pkt_size, max_pkt_size, 289 admission_type, flags); 290 if (error) 291 return (0); 292 return (1); 293 } 294 295 /* 296 * qcmd api 297 */ 298 int 299 qcmd_cbq_add_if(const char *ifname, u_int bandwidth, int is_wrr, int efficient) 300 { 301 int error; 302 303 error = qop_cbq_add_if(NULL, ifname, bandwidth, is_wrr, efficient); 304 if (error != 0) 305 LOG(LOG_ERR, errno, "%s: can't add cbq on interface '%s'", 306 qoperror(error), ifname); 307 return (error); 308 } 309 310 int 311 qcmd_cbq_add_class(const char *ifname, const char *class_name, 312 const char *parent_name, const char *borrow_name, 313 u_int pri, u_int bandwidth, 314 u_int maxdelay, u_int maxburst, u_int minburst, 315 u_int av_pkt_size, u_int max_pkt_size, 316 int admission_type, int flags) 317 { 318 struct ifinfo *ifinfo; 319 struct cbq_ifinfo *cbq_ifinfo; 320 struct classinfo *parent = NULL, *borrow = NULL; 321 u_int ctl_bandwidth = 0; 322 int error = 0; 323 324 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 325 error = QOPERR_BADIF; 326 cbq_ifinfo = ifinfo->private; 327 328 if (error == 0 && parent_name != NULL && 329 (parent = clname2clinfo(ifinfo, parent_name)) == NULL) 330 error = QOPERR_BADCLASS; 331 332 if (error == 0 && borrow_name != NULL && 333 (borrow = clname2clinfo(ifinfo, borrow_name)) == NULL) 334 error = QOPERR_BADCLASS; 335 336 if (flags & CBQCLF_DEFCLASS) { 337 /* 338 * if this is a default class and no ctl_class is defined, 339 * we will create a ctl_class. 340 */ 341 if (cbq_ifinfo->ctl_class == NULL) { 342 /* reserve bandwidth for ctl_class */ 343 ctl_bandwidth = 344 ifinfo->bandwidth / 100 * CTL_PBANDWIDTH; 345 bandwidth -= ctl_bandwidth; 346 } 347 } 348 349 if (error == 0) 350 error = qop_cbq_add_class(NULL, class_name, ifinfo, parent, 351 borrow, pri, bandwidth, 352 maxdelay, maxburst, minburst, 353 av_pkt_size, max_pkt_size, 354 admission_type, flags); 355 if (error != 0) 356 LOG(LOG_ERR, errno, 357 "cbq: %s: can't add class '%s' on interface '%s'", 358 qoperror(error), class_name, ifname); 359 360 if (ctl_bandwidth != 0) { 361 /* 362 * If were adding the default traffic class and 363 * no ctl_class is defined, also add the ctl traffic class. 364 * This is for RSVP and IGMP packets. 365 */ 366 if (qcmd_cbq_add_class(ifname, "ctl_class", parent_name, 367 borrow_name, 6, ctl_bandwidth, 368 maxdelay, maxburst, minburst, av_pkt_size, 369 max_pkt_size, admission_type, CBQCLF_CTLCLASS) != 0) { 370 LOG(LOG_ERR, errno, "can't create ctl_class!"); 371 return (QOPERR_CLASS); 372 } 373 } 374 375 /* 376 * if this is a ctl class, add the default filters for backward 377 * compatibility 378 */ 379 if (flags & CBQCLF_CTLCLASS) 380 qcmd_cbq_add_ctl_filters(ifname, class_name); 381 382 return (error); 383 } 384 385 int 386 qcmd_cbq_modify_class(const char *ifname, const char *class_name, 387 u_int pri, u_int bandwidth, 388 u_int maxdelay, u_int maxburst, u_int minburst, 389 u_int av_pkt_size, u_int max_pkt_size, int flags) 390 { 391 struct ifinfo *ifinfo; 392 struct classinfo *clinfo; 393 394 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 395 return (QOPERR_BADIF); 396 397 if ((clinfo = clname2clinfo(ifinfo, class_name)) == NULL) 398 return (QOPERR_BADCLASS); 399 400 return qop_cbq_modify_class(clinfo, pri, bandwidth, 401 maxdelay, maxburst, minburst, 402 av_pkt_size, max_pkt_size, flags); 403 } 404 405 /* 406 * add the default filters for ctl_class (for backward compatibility). 407 */ 408 #ifndef IPPROTO_RSVP 409 #define IPPROTO_RSVP 46 410 #endif 411 412 static int 413 qcmd_cbq_add_ctl_filters(const char *ifname, const char *clname) 414 { 415 struct flow_filter sfilt; 416 u_int8_t ctl_protos[3] = {IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_RSVP}; 417 #ifdef INET6 418 struct flow_filter6 sfilt6; 419 u_int8_t ctl6_protos[3] = {IPPROTO_ICMPV6, IPPROTO_IGMP, IPPROTO_RSVP}; 420 #endif 421 int error, i; 422 423 for (i = 0; i < (int)sizeof(ctl_protos); i++) { 424 memset(&sfilt, 0, sizeof(sfilt)); 425 sfilt.ff_flow.fi_family = AF_INET; 426 sfilt.ff_flow.fi_proto = ctl_protos[i]; 427 428 filter_dontwarn = 1; /* XXX */ 429 error = qcmd_add_filter(ifname, clname, NULL, &sfilt); 430 filter_dontwarn = 0; /* XXX */ 431 if (error) { 432 LOG(LOG_ERR, 0, 433 "can't add ctl class filter on interface '%s'", 434 ifname); 435 return (error); 436 } 437 } 438 439 #ifdef INET6 440 for (i = 0; i < sizeof(ctl6_protos); i++) { 441 memset(&sfilt6, 0, sizeof(sfilt6)); 442 sfilt6.ff_flow6.fi6_family = AF_INET6; 443 sfilt6.ff_flow6.fi6_proto = ctl6_protos[i]; 444 445 error = qcmd_add_filter(ifname, clname, NULL, 446 (struct flow_filter *)&sfilt6); 447 if (error) { 448 LOG(LOG_WARNING, 0, 449 "can't add ctl class IPv6 filter on interface '%s'", 450 ifname); 451 return (error); 452 } 453 } 454 #endif 455 return (0); 456 } 457 458 /* 459 * qop api 460 */ 461 int 462 qop_cbq_add_if(struct ifinfo **rp, const char *ifname, 463 u_int bandwidth, int is_wrr, int efficient) 464 { 465 struct ifinfo *ifinfo = NULL; 466 struct cbq_ifinfo *cbq_ifinfo = NULL; 467 int error; 468 469 if ((cbq_ifinfo = calloc(1, sizeof(*cbq_ifinfo))) == NULL) 470 return (QOPERR_NOMEM); 471 472 cbq_ifinfo->nsPerByte = 473 (1.0 / (double)bandwidth) * NS_PER_SEC * 8; 474 cbq_ifinfo->is_wrr = is_wrr; 475 cbq_ifinfo->is_efficient = efficient; 476 477 error = qop_add_if(&ifinfo, ifname, bandwidth, 478 &cbq_qdisc, cbq_ifinfo); 479 if (error != 0) 480 goto err_ret; 481 482 /* set enable hook */ 483 ifinfo->enable_hook = qop_cbq_enable_hook; 484 485 if (rp != NULL) 486 *rp = ifinfo; 487 return (0); 488 489 err_ret: 490 if (cbq_ifinfo != NULL) { 491 free(cbq_ifinfo); 492 if (ifinfo != NULL) 493 ifinfo->private = NULL; 494 } 495 return (error); 496 } 497 498 #define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) 499 500 int 501 qop_cbq_add_class(struct classinfo **rp, const char *class_name, 502 struct ifinfo *ifinfo, struct classinfo *parent, 503 struct classinfo *borrow, u_int pri, u_int bandwidth, 504 u_int maxdelay, u_int maxburst, u_int minburst, 505 u_int av_pkt_size, u_int max_pkt_size, 506 int admission_type, int flags) 507 { 508 struct classinfo *clinfo; 509 struct cbq_ifinfo *cbq_ifinfo; 510 struct cbq_classinfo *cbq_clinfo, *parent_clinfo; 511 u_int parent_handle, borrow_handle; 512 int error; 513 514 cbq_ifinfo = ifinfo->private; 515 516 if (parent == NULL) { 517 if (cbq_ifinfo->root_class != NULL) 518 return (QOPERR_CLASS_INVAL); 519 flags |= CBQCLF_ROOTCLASS; 520 } 521 if ((flags & CBQCLF_DEFCLASS) && cbq_ifinfo->default_class != NULL) 522 return (QOPERR_CLASS_INVAL); 523 if ((flags & CBQCLF_CTLCLASS) && cbq_ifinfo->ctl_class != NULL) 524 return (QOPERR_CLASS_INVAL); 525 526 /* admission control */ 527 if (parent != NULL) { 528 parent_clinfo = parent->private; 529 if (bandwidth > 530 parent_clinfo->bandwidth - parent_clinfo->allocated) { 531 #ifdef ALLOW_OVERCOMMIT 532 LOG(LOG_WARNING, 0, 533 "bandwidth overcommitted %uK requested but only %dK available (%uK already allocated)", 534 bandwidth / 1000, 535 ((int)parent_clinfo->bandwidth - 536 parent_clinfo->allocated) / 1000, 537 parent_clinfo->allocated / 1000); 538 #else /* !ALLOW_OVERCOMMIT */ 539 LOG(LOG_ERR, 0, 540 "cbq admission failed! %uK requested but only %uK available (%uK already allocated)", 541 bandwidth / 1000, 542 (parent_clinfo->bandwidth - 543 parent_clinfo->allocated) / 1000, 544 parent_clinfo->allocated / 1000); 545 return (QOPERR_ADMISSION_NOBW); 546 #endif /* !ALLOW_OVERCOMMIT */ 547 } 548 } 549 550 if ((cbq_clinfo = calloc(1, sizeof(*cbq_clinfo))) == NULL) 551 return (QOPERR_NOMEM); 552 553 cbq_clinfo->bandwidth = bandwidth; 554 cbq_clinfo->allocated = 0; 555 556 /* if average paket size isn't specified, set if mtu. */ 557 if (av_pkt_size == 0) { /* use default */ 558 av_pkt_size = ifinfo->ifmtu; 559 if (av_pkt_size > MCLBYTES) /* do what TCP does */ 560 av_pkt_size &= ~MCLBYTES; 561 } else if (av_pkt_size > ifinfo->ifmtu) 562 av_pkt_size = ifinfo->ifmtu; 563 564 if (max_pkt_size == 0) /* use default */ 565 max_pkt_size = ifinfo->ifmtu; 566 else if (max_pkt_size > ifinfo->ifmtu) 567 max_pkt_size = ifinfo->ifmtu; 568 569 cbq_clinfo->maxdelay = maxdelay; 570 cbq_clinfo->maxburst = maxburst; 571 cbq_clinfo->minburst = minburst; 572 cbq_clinfo->av_pkt_size = av_pkt_size; 573 cbq_clinfo->max_pkt_size = max_pkt_size; 574 575 parent_handle = parent != NULL ? parent->handle : NULL_CLASS_HANDLE; 576 borrow_handle = borrow != NULL ? borrow->handle : NULL_CLASS_HANDLE; 577 578 if (cbq_class_spec(ifinfo, parent_handle, borrow_handle, pri, flags, 579 bandwidth, maxdelay, maxburst, minburst, 580 av_pkt_size, max_pkt_size, 581 &cbq_clinfo->class_spec) < 0) { 582 error = QOPERR_INVAL; 583 goto err_ret; 584 } 585 586 clinfo = NULL; 587 error = qop_add_class(&clinfo, class_name, ifinfo, parent, cbq_clinfo); 588 if (error != 0) 589 goto err_ret; 590 591 /* set delete hook */ 592 clinfo->delete_hook = qop_cbq_delete_class_hook; 593 594 if (parent == NULL) 595 cbq_ifinfo->root_class = clinfo; 596 else { 597 parent_clinfo = parent->private; 598 parent_clinfo->allocated += bandwidth; 599 } 600 if (flags & CBQCLF_DEFCLASS) 601 cbq_ifinfo->default_class = clinfo; 602 if (flags & CBQCLF_CTLCLASS) 603 cbq_ifinfo->ctl_class = clinfo; 604 605 switch (admission_type) { 606 case CBQ_QOS_CNTR_LOAD: 607 case CBQ_QOS_GUARANTEED: 608 case CBQ_QOS_PREDICTIVE: 609 case CBQ_QOS_CNTR_DELAY: 610 if (ifinfo->resv_class != NULL) { 611 LOG(LOG_ERR, 0, 612 "%s: duplicate resv meta class", class_name); 613 return (QOPERR_CLASS); 614 } 615 ifinfo->resv_class = clinfo; 616 } 617 618 if (rp != NULL) 619 *rp = clinfo; 620 return (0); 621 622 err_ret: 623 if (cbq_clinfo != NULL) { 624 free(cbq_clinfo); 625 if (clinfo != NULL) 626 clinfo->private = NULL; 627 } 628 return (error); 629 } 630 631 /* 632 * this is called from qop_delete_class() before a class is destroyed 633 * for discipline specific cleanup. 634 */ 635 static int 636 qop_cbq_delete_class_hook(struct classinfo *clinfo) 637 { 638 struct cbq_classinfo *cbq_clinfo, *parent_clinfo; 639 640 /* cancel admission control */ 641 if (clinfo->parent != NULL) { 642 cbq_clinfo = clinfo->private; 643 parent_clinfo = clinfo->parent->private; 644 645 parent_clinfo->allocated -= cbq_clinfo->bandwidth; 646 } 647 return (0); 648 } 649 650 int 651 qop_cbq_modify_class(struct classinfo *clinfo, u_int pri, u_int bandwidth, 652 u_int maxdelay, u_int maxburst, u_int minburst, 653 u_int av_pkt_size, u_int max_pkt_size, int flags) 654 { 655 struct ifinfo *ifinfo; 656 struct cbq_classinfo *cbq_clinfo, *parent_clinfo; 657 u_int parent_handle, borrow_handle; 658 u_int old_bandwidth; 659 int error; 660 661 ifinfo = clinfo->ifinfo; 662 cbq_clinfo = clinfo->private; 663 664 /* admission control */ 665 old_bandwidth = cbq_clinfo->bandwidth; 666 if (clinfo->parent != NULL) { 667 parent_clinfo = clinfo->parent->private; 668 if (bandwidth > old_bandwidth) { 669 /* increase bandwidth */ 670 if (bandwidth - old_bandwidth > 671 parent_clinfo->bandwidth 672 - parent_clinfo->allocated) 673 return (QOPERR_ADMISSION_NOBW); 674 } else if (bandwidth < old_bandwidth) { 675 /* decrease bandwidth */ 676 if (bandwidth < cbq_clinfo->allocated) 677 return (QOPERR_ADMISSION); 678 } 679 } 680 681 /* if average paket size isn't specified, set if mtu. */ 682 if (av_pkt_size == 0) { /* use default */ 683 av_pkt_size = ifinfo->ifmtu; 684 if (av_pkt_size > MCLBYTES) /* do what TCP does */ 685 av_pkt_size &= ~MCLBYTES; 686 } else if (av_pkt_size > ifinfo->ifmtu) 687 av_pkt_size = ifinfo->ifmtu; 688 689 if (max_pkt_size == 0) /* use default */ 690 max_pkt_size = ifinfo->ifmtu; 691 else if (max_pkt_size > ifinfo->ifmtu) 692 max_pkt_size = ifinfo->ifmtu; 693 694 cbq_clinfo->maxdelay = maxdelay; 695 cbq_clinfo->maxburst = maxburst; 696 cbq_clinfo->minburst = minburst; 697 cbq_clinfo->av_pkt_size = av_pkt_size; 698 cbq_clinfo->max_pkt_size = max_pkt_size; 699 700 parent_handle = cbq_clinfo->class_spec.parent_class_handle; 701 borrow_handle = cbq_clinfo->class_spec.borrow_class_handle; 702 703 if (cbq_class_spec(ifinfo, parent_handle, borrow_handle, pri, flags, 704 bandwidth, maxdelay, maxburst, minburst, 705 av_pkt_size, max_pkt_size, 706 &cbq_clinfo->class_spec) < 0) { 707 return (QOPERR_INVAL); 708 } 709 710 error = qop_modify_class(clinfo, NULL); 711 712 if (error == 0) { 713 if (clinfo->parent != NULL) { 714 parent_clinfo = clinfo->parent->private; 715 parent_clinfo->allocated -= old_bandwidth; 716 parent_clinfo->allocated += bandwidth; 717 } 718 cbq_clinfo->bandwidth = bandwidth; 719 } 720 return (error); 721 } 722 723 /* 724 * sanity check at enabling cbq: 725 * there must one root class and one default class for an interface 726 */ 727 static int 728 qop_cbq_enable_hook(struct ifinfo *ifinfo) 729 { 730 struct cbq_ifinfo *cbq_ifinfo; 731 732 cbq_ifinfo = ifinfo->private; 733 if (cbq_ifinfo->root_class == NULL) { 734 LOG(LOG_ERR, 0, "cbq: no root class on interface %s!", 735 ifinfo->ifname); 736 return (QOPERR_CLASS); 737 } 738 if (cbq_ifinfo->default_class == NULL) { 739 LOG(LOG_ERR, 0, "cbq: no default class on interface %s!", 740 ifinfo->ifname); 741 return (QOPERR_CLASS); 742 } 743 return (0); 744 } 745 746 static int 747 cbq_class_spec(struct ifinfo *ifinfo, u_long parent_class, 748 u_long borrow_class, u_int pri, int flags, 749 u_int bandwidth, u_int maxdelay, u_int maxburst, 750 u_int minburst, u_int av_pkt_size, u_int max_pkt_size, 751 cbq_class_spec_t *cl_spec) 752 { 753 struct cbq_ifinfo *cbq_ifinfo = ifinfo->private; 754 double maxq, maxidle_s, maxidle, minidle, 755 offtime, nsPerByte, ptime, cptime; 756 double z = (double)(1 << RM_FILTER_GAIN); 757 double g = (1.0 - 1.0 / z); 758 double f; 759 double gton; 760 double gtom; 761 double maxrate; 762 763 /* Compute other class parameters */ 764 if (bandwidth == 0) 765 f = 0.0001; /* small enough? */ 766 else 767 f = ((double) bandwidth / (double) ifinfo->bandwidth); 768 769 if (av_pkt_size == 0) { /* use default */ 770 av_pkt_size = ifinfo->ifmtu; 771 if (av_pkt_size > MCLBYTES) /* do what TCP does */ 772 av_pkt_size &= ~MCLBYTES; 773 } else if (av_pkt_size > ifinfo->ifmtu) 774 av_pkt_size = ifinfo->ifmtu; 775 if (max_pkt_size == 0) /* use default */ 776 max_pkt_size = ifinfo->ifmtu; 777 else if (max_pkt_size > ifinfo->ifmtu) 778 max_pkt_size = ifinfo->ifmtu; 779 780 nsPerByte = cbq_ifinfo->nsPerByte / f; 781 ptime = (double) av_pkt_size * (double)cbq_ifinfo->nsPerByte; 782 maxrate = f * ((double)ifinfo->bandwidth / 8.0); 783 cptime = ptime * (1.0 - f) / f; 784 #if 1 /* ALTQ */ 785 if (nsPerByte * (double)max_pkt_size > (double)INT_MAX) { 786 /* 787 * this causes integer overflow in kernel! 788 * (bandwidth < 6Kbps when max_pkt_size=1500) 789 */ 790 if (bandwidth != 0) 791 LOG(LOG_WARNING, 0, "warning: class is too slow!!"); 792 nsPerByte = (double)(INT_MAX / max_pkt_size); 793 } 794 #endif 795 if (maxburst == 0) { /* use default */ 796 if (cptime > 10.0 * NS_PER_MS) 797 maxburst = 4; 798 else 799 maxburst = 16; 800 } 801 if (minburst == 0) /* use default */ 802 minburst = 2; 803 if (minburst > maxburst) 804 minburst = maxburst; 805 806 if (IsDebug(DEBUG_ALTQ)) { 807 int packet_time; 808 LOG(LOG_DEBUG, 0, 809 "cbq_flowspec: maxburst=%d,minburst=%d,pkt_size=%d", 810 maxburst, minburst, av_pkt_size); 811 LOG(LOG_DEBUG, 0, 812 " nsPerByte=%.2f ns, link's nsPerByte=%.2f, f=%.3f", 813 nsPerByte, cbq_ifinfo->nsPerByte, f); 814 packet_time = av_pkt_size * (int)nsPerByte / 1000; 815 LOG(LOG_DEBUG, 0, 816 " packet time=%d [us]\n", packet_time); 817 if (maxburst * packet_time < 20000) { 818 LOG(LOG_WARNING, 0, 819 "warning: maxburst smaller than timer granularity!"); 820 LOG(LOG_WARNING, 0, 821 " maxburst=%d, packet_time=%d [us]", 822 maxburst, packet_time); 823 } 824 } 825 gton = pow(g, (double)maxburst); 826 gtom = pow(g, (double)(minburst-1)); 827 maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); 828 maxidle_s = (1.0 - g); 829 if (maxidle > maxidle_s) 830 maxidle = ptime * maxidle; 831 else 832 maxidle = ptime * maxidle_s; 833 if (IsDebug(DEBUG_ALTQ)) 834 LOG(LOG_DEBUG, 0, " maxidle=%.2f us", maxidle/1000.0); 835 if (minburst) 836 offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); 837 else 838 offtime = cptime; 839 minidle = -((double)max_pkt_size * (double)nsPerByte); 840 if (IsDebug(DEBUG_ALTQ)) 841 LOG(LOG_DEBUG, 0, " offtime=%.2f us minidle=%.2f us", 842 offtime/1000.0, minidle/1000.0); 843 844 maxidle = ((maxidle * 8.0) / nsPerByte) * pow(2, RM_FILTER_GAIN); 845 #if 1 /* ALTQ */ 846 /* also scale offtime and minidle */ 847 offtime = (offtime * 8.0) / nsPerByte * pow(2, RM_FILTER_GAIN); 848 minidle = ((minidle * 8.0) / nsPerByte) * pow(2, RM_FILTER_GAIN); 849 #endif 850 maxidle = maxidle / 1000.0; 851 offtime = offtime / 1000.0; 852 minidle = minidle / 1000.0; 853 /* adjust queue size when maxdelay is specified. 854 queue size should be relative to its share */ 855 if (maxdelay == 0) { 856 if (flags & (CBQCLF_RED|CBQCLF_RIO)) 857 maxq = 60.0; 858 else 859 maxq = 30.0; 860 } else { 861 maxq = ((double) maxdelay * NS_PER_MS) / (nsPerByte * av_pkt_size); 862 if (maxq < 4) { 863 LOG(LOG_WARNING, 0, 864 "warning: maxq (%d) is too small. set to %d", 865 (int)maxq, 4); 866 maxq = 4; 867 } 868 } 869 if (bandwidth == 0 && borrow_class == NULL_CLASS_HANDLE) 870 /* filter out this class by setting queue size to zero */ 871 maxq = 0; 872 if (IsDebug(DEBUG_ALTQ)) { 873 if ((u_int)maxq < maxburst) 874 LOG(LOG_WARNING, 0, 875 "warning: maxq (%d) is smaller than maxburst(%d)", 876 (int)maxq, maxburst); 877 else if (maxq > 100.0) 878 LOG(LOG_WARNING, 0, 879 "warning: maxq %d too large\n", (int)maxq); 880 LOG(LOG_DEBUG, 0, " maxq=%d", (int)maxq); 881 } 882 883 if (parent_class == NULL_CLASS_HANDLE) { 884 if ((flags & CBQCLF_ROOTCLASS) == 0) 885 flags |= CBQCLF_ROOTCLASS; 886 if (cbq_ifinfo->is_wrr) 887 flags |= CBQCLF_WRR; 888 if (cbq_ifinfo->is_efficient) 889 flags |= CBQCLF_EFFICIENT; 890 } 891 892 memset((void *)cl_spec, 0, sizeof(cbq_class_spec_t)); 893 cl_spec->priority = pri; 894 cl_spec->nano_sec_per_byte = (u_int) nsPerByte; 895 cl_spec->maxq = (u_int) maxq; 896 cl_spec->maxidle = (u_int) fabs(maxidle); 897 cl_spec->minidle = (int)minidle; 898 cl_spec->offtime = (u_int) fabs(offtime); 899 900 cl_spec->parent_class_handle = parent_class; 901 cl_spec->borrow_class_handle = borrow_class; 902 903 cl_spec->pktsize = av_pkt_size; 904 cl_spec->flags = flags; 905 906 return (0); 907 } 908 909 910 /* 911 * system call interfaces for qdisc_ops 912 */ 913 static int 914 cbq_attach(struct ifinfo *ifinfo) 915 { 916 struct cbq_interface iface; 917 918 if (cbq_fd < 0 && 919 (cbq_fd = open(CBQ_DEVICE, O_RDWR)) < 0 && 920 (cbq_fd = open_module(CBQ_DEVICE, O_RDWR)) < 0) { 921 LOG(LOG_ERR, errno, "CBQ open"); 922 return (QOPERR_SYSCALL); 923 } 924 925 cbq_refcount++; 926 memset(&iface, 0, sizeof(iface)); 927 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 928 929 if (ioctl(cbq_fd, CBQ_IF_ATTACH, &iface) < 0) 930 return (QOPERR_SYSCALL); 931 return (0); 932 } 933 934 static int 935 cbq_detach(struct ifinfo *ifinfo) 936 { 937 struct cbq_interface iface; 938 939 memset(&iface, 0, sizeof(iface)); 940 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 941 942 if (ioctl(cbq_fd, CBQ_IF_DETACH, &iface) < 0) 943 return (QOPERR_SYSCALL); 944 945 if (--cbq_refcount == 0) { 946 close(cbq_fd); 947 cbq_fd = -1; 948 } 949 return (0); 950 } 951 952 static int 953 cbq_clear(struct ifinfo *ifinfo) 954 { 955 struct cbq_interface iface; 956 957 memset(&iface, 0, sizeof(iface)); 958 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 959 960 if (ioctl(cbq_fd, CBQ_CLEAR_HIERARCHY, &iface) < 0) 961 return (QOPERR_SYSCALL); 962 return (0); 963 } 964 965 static int 966 cbq_enable(struct ifinfo *ifinfo) 967 { 968 struct cbq_interface iface; 969 970 memset(&iface, 0, sizeof(iface)); 971 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 972 973 if (ioctl(cbq_fd, CBQ_ENABLE, &iface) < 0) 974 return (QOPERR_SYSCALL); 975 return (0); 976 } 977 978 static int 979 cbq_disable(struct ifinfo *ifinfo) 980 { 981 struct cbq_interface iface; 982 983 memset(&iface, 0, sizeof(iface)); 984 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 985 986 if (ioctl(cbq_fd, CBQ_DISABLE, &iface) < 0) 987 return (QOPERR_SYSCALL); 988 return (0); 989 } 990 991 static int 992 cbq_add_class(struct classinfo *clinfo) 993 { 994 struct cbq_add_class class_add; 995 struct cbq_classinfo *cbq_clinfo; 996 struct cbq_ifinfo *cbq_ifinfo; 997 998 cbq_ifinfo = clinfo->ifinfo->private; 999 cbq_clinfo = clinfo->private; 1000 1001 memset(&class_add, 0, sizeof(class_add)); 1002 strncpy(class_add.cbq_iface.cbq_ifacename, 1003 clinfo->ifinfo->ifname, IFNAMSIZ); 1004 1005 class_add.cbq_class = cbq_clinfo->class_spec; 1006 1007 if (ioctl(cbq_fd, CBQ_ADD_CLASS, &class_add) < 0) 1008 return (QOPERR_SYSCALL); 1009 1010 clinfo->handle = class_add.cbq_class_handle; 1011 return (0); 1012 } 1013 1014 static int 1015 cbq_modify_class(struct classinfo *clinfo, void *arg) 1016 { 1017 struct cbq_modify_class class_mod; 1018 struct cbq_classinfo *cbq_clinfo; 1019 1020 cbq_clinfo = clinfo->private; 1021 1022 memset(&class_mod, 0, sizeof(class_mod)); 1023 strncpy(class_mod.cbq_iface.cbq_ifacename, 1024 clinfo->ifinfo->ifname, IFNAMSIZ); 1025 class_mod.cbq_class_handle = clinfo->handle; 1026 class_mod.cbq_class = cbq_clinfo->class_spec; 1027 1028 if (ioctl(cbq_fd, CBQ_MODIFY_CLASS, &class_mod) < 0) 1029 return (QOPERR_SYSCALL); 1030 return (0); 1031 } 1032 1033 static int 1034 cbq_delete_class(struct classinfo *clinfo) 1035 { 1036 struct cbq_delete_class class_delete; 1037 1038 memset(&class_delete, 0, sizeof(class_delete)); 1039 strncpy(class_delete.cbq_iface.cbq_ifacename, 1040 clinfo->ifinfo->ifname, IFNAMSIZ); 1041 class_delete.cbq_class_handle = clinfo->handle; 1042 1043 if (ioctl(cbq_fd, CBQ_DEL_CLASS, &class_delete) < 0) 1044 return (QOPERR_SYSCALL); 1045 return (0); 1046 } 1047 1048 static int 1049 cbq_add_filter(struct fltrinfo *fltrinfo) 1050 { 1051 struct cbq_add_filter fltr_add; 1052 1053 memset(&fltr_add, 0, sizeof(fltr_add)); 1054 strncpy(fltr_add.cbq_iface.cbq_ifacename, 1055 fltrinfo->clinfo->ifinfo->ifname, IFNAMSIZ); 1056 fltr_add.cbq_class_handle = fltrinfo->clinfo->handle; 1057 fltr_add.cbq_filter = fltrinfo->fltr; 1058 1059 if (ioctl(cbq_fd, CBQ_ADD_FILTER, &fltr_add) < 0) 1060 return (QOPERR_SYSCALL); 1061 fltrinfo->handle = fltr_add.cbq_filter_handle; 1062 return (0); 1063 } 1064 1065 static int 1066 cbq_delete_filter(struct fltrinfo *fltrinfo) 1067 { 1068 struct cbq_delete_filter fltr_del; 1069 1070 memset(&fltr_del, 0, sizeof(fltr_del)); 1071 strncpy(fltr_del.cbq_iface.cbq_ifacename, 1072 fltrinfo->clinfo->ifinfo->ifname, IFNAMSIZ); 1073 fltr_del.cbq_filter_handle = fltrinfo->handle; 1074 1075 if (ioctl(cbq_fd, CBQ_DEL_FILTER, &fltr_del) < 0) 1076 return (QOPERR_SYSCALL); 1077 return (0); 1078 } 1079 1080 1081