1 /* $NetBSD: qop_cbq.c,v 1.9 2010/01/06 06:30:37 mbalmer Exp $ */ 2 /* $KAME: qop_cbq.c,v 1.7 2002/05/31 06:03:35 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 if (bandwidth <= ctl_bandwidth) 346 LOG(LOG_ERR, 0, 347 "bandwidth for default class too small!"); 348 bandwidth -= ctl_bandwidth; 349 } 350 } 351 352 if (error == 0) 353 error = qop_cbq_add_class(NULL, class_name, ifinfo, parent, 354 borrow, pri, bandwidth, 355 maxdelay, maxburst, minburst, 356 av_pkt_size, max_pkt_size, 357 admission_type, flags); 358 if (error != 0) 359 LOG(LOG_ERR, errno, 360 "cbq: %s: can't add class '%s' on interface '%s'", 361 qoperror(error), class_name, ifname); 362 363 if (ctl_bandwidth != 0) { 364 /* 365 * If were adding the default traffic class and 366 * no ctl_class is defined, also add the ctl traffic class. 367 * This is for RSVP and IGMP packets. 368 */ 369 if (qcmd_cbq_add_class(ifname, "ctl_class", parent_name, 370 borrow_name, 6, ctl_bandwidth, 371 maxdelay, maxburst, minburst, av_pkt_size, 372 max_pkt_size, admission_type, CBQCLF_CTLCLASS) != 0) { 373 LOG(LOG_ERR, errno, "can't create ctl_class!"); 374 return (QOPERR_CLASS); 375 } 376 } 377 378 /* 379 * if this is a ctl class, add the default filters for backward 380 * compatibility 381 */ 382 if (flags & CBQCLF_CTLCLASS) 383 qcmd_cbq_add_ctl_filters(ifname, class_name); 384 385 return (error); 386 } 387 388 int 389 qcmd_cbq_modify_class(const char *ifname, const char *class_name, 390 u_int pri, u_int bandwidth, 391 u_int maxdelay, u_int maxburst, u_int minburst, 392 u_int av_pkt_size, u_int max_pkt_size, int flags) 393 { 394 struct ifinfo *ifinfo; 395 struct classinfo *clinfo; 396 397 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) 398 return (QOPERR_BADIF); 399 400 if ((clinfo = clname2clinfo(ifinfo, class_name)) == NULL) 401 return (QOPERR_BADCLASS); 402 403 return qop_cbq_modify_class(clinfo, pri, bandwidth, 404 maxdelay, maxburst, minburst, 405 av_pkt_size, max_pkt_size, flags); 406 } 407 408 /* 409 * add the default filters for ctl_class (for backward compatibility). 410 */ 411 #ifndef IPPROTO_RSVP 412 #define IPPROTO_RSVP 46 413 #endif 414 415 static int 416 qcmd_cbq_add_ctl_filters(const char *ifname, const char *clname) 417 { 418 struct flow_filter sfilt; 419 u_int8_t ctl_protos[3] = {IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_RSVP}; 420 #ifdef INET6 421 struct flow_filter6 sfilt6; 422 u_int8_t ctl6_protos[3] = {IPPROTO_ICMPV6, IPPROTO_IGMP, IPPROTO_RSVP}; 423 #endif 424 int error; 425 size_t i; 426 427 for (i = 0; i < sizeof(ctl_protos); i++) { 428 memset(&sfilt, 0, sizeof(sfilt)); 429 sfilt.ff_flow.fi_family = AF_INET; 430 sfilt.ff_flow.fi_proto = ctl_protos[i]; 431 432 filter_dontwarn = 1; /* XXX */ 433 error = qcmd_add_filter(ifname, clname, NULL, &sfilt); 434 filter_dontwarn = 0; /* XXX */ 435 if (error) { 436 LOG(LOG_ERR, 0, 437 "can't add ctl class filter on interface '%s'", 438 ifname); 439 return (error); 440 } 441 } 442 443 #ifdef INET6 444 for (i = 0; i < sizeof(ctl6_protos); i++) { 445 memset(&sfilt6, 0, sizeof(sfilt6)); 446 sfilt6.ff_flow6.fi6_family = AF_INET6; 447 sfilt6.ff_flow6.fi6_proto = ctl6_protos[i]; 448 449 error = qcmd_add_filter(ifname, clname, NULL, 450 (struct flow_filter *)&sfilt6); 451 if (error) { 452 LOG(LOG_WARNING, 0, 453 "can't add ctl class IPv6 filter on interface '%s'", 454 ifname); 455 return (error); 456 } 457 } 458 #endif 459 return (0); 460 } 461 462 /* 463 * qop api 464 */ 465 int 466 qop_cbq_add_if(struct ifinfo **rp, const char *ifname, 467 u_int bandwidth, int is_wrr, int efficient) 468 { 469 struct ifinfo *ifinfo = NULL; 470 struct cbq_ifinfo *cbq_ifinfo = NULL; 471 int error; 472 473 if ((cbq_ifinfo = calloc(1, sizeof(*cbq_ifinfo))) == NULL) 474 return (QOPERR_NOMEM); 475 476 cbq_ifinfo->nsPerByte = 477 (1.0 / (double)bandwidth) * NS_PER_SEC * 8; 478 cbq_ifinfo->is_wrr = is_wrr; 479 cbq_ifinfo->is_efficient = efficient; 480 481 error = qop_add_if(&ifinfo, ifname, bandwidth, 482 &cbq_qdisc, cbq_ifinfo); 483 if (error != 0) 484 goto err_ret; 485 486 /* set enable hook */ 487 ifinfo->enable_hook = qop_cbq_enable_hook; 488 489 if (rp != NULL) 490 *rp = ifinfo; 491 return (0); 492 493 err_ret: 494 if (cbq_ifinfo != NULL) { 495 free(cbq_ifinfo); 496 if (ifinfo != NULL) 497 ifinfo->private = NULL; 498 } 499 return (error); 500 } 501 502 #define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) 503 504 int 505 qop_cbq_add_class(struct classinfo **rp, const char *class_name, 506 struct ifinfo *ifinfo, struct classinfo *parent, 507 struct classinfo *borrow, u_int pri, u_int bandwidth, 508 u_int maxdelay, u_int maxburst, u_int minburst, 509 u_int av_pkt_size, u_int max_pkt_size, 510 int admission_type, int flags) 511 { 512 struct classinfo *clinfo; 513 struct cbq_ifinfo *cbq_ifinfo; 514 struct cbq_classinfo *cbq_clinfo, *parent_clinfo; 515 u_int parent_handle, borrow_handle; 516 int error; 517 518 cbq_ifinfo = ifinfo->private; 519 520 if (parent == NULL) { 521 if (cbq_ifinfo->root_class != NULL) 522 return (QOPERR_CLASS_INVAL); 523 flags |= CBQCLF_ROOTCLASS; 524 } 525 if ((flags & CBQCLF_DEFCLASS) && cbq_ifinfo->default_class != NULL) 526 return (QOPERR_CLASS_INVAL); 527 if ((flags & CBQCLF_CTLCLASS) && cbq_ifinfo->ctl_class != NULL) 528 return (QOPERR_CLASS_INVAL); 529 530 /* admission control */ 531 if (parent != NULL) { 532 parent_clinfo = parent->private; 533 if (bandwidth > 534 parent_clinfo->bandwidth - parent_clinfo->allocated) { 535 #ifdef ALLOW_OVERCOMMIT 536 LOG(LOG_WARNING, 0, 537 "bandwidth overcommitted %uK requested but only %dK available (%uK already allocated)", 538 bandwidth / 1000, 539 ((int)parent_clinfo->bandwidth - 540 parent_clinfo->allocated) / 1000, 541 parent_clinfo->allocated / 1000); 542 #else /* !ALLOW_OVERCOMMIT */ 543 LOG(LOG_ERR, 0, 544 "cbq admission failed! %uK requested but only %uK available (%uK already allocated)", 545 bandwidth / 1000, 546 (parent_clinfo->bandwidth - 547 parent_clinfo->allocated) / 1000, 548 parent_clinfo->allocated / 1000); 549 return (QOPERR_ADMISSION_NOBW); 550 #endif /* !ALLOW_OVERCOMMIT */ 551 } 552 } 553 554 if ((cbq_clinfo = calloc(1, sizeof(*cbq_clinfo))) == NULL) 555 return (QOPERR_NOMEM); 556 557 cbq_clinfo->bandwidth = bandwidth; 558 cbq_clinfo->allocated = 0; 559 560 /* if average packet size isn't specified, set if mtu. */ 561 if (av_pkt_size == 0) { /* use default */ 562 av_pkt_size = ifinfo->ifmtu; 563 if (av_pkt_size > MCLBYTES) /* do what TCP does */ 564 av_pkt_size &= ~MCLBYTES; 565 } else if (av_pkt_size > ifinfo->ifmtu) 566 av_pkt_size = ifinfo->ifmtu; 567 568 if (max_pkt_size == 0) /* use default */ 569 max_pkt_size = ifinfo->ifmtu; 570 else if (max_pkt_size > ifinfo->ifmtu) 571 max_pkt_size = ifinfo->ifmtu; 572 573 cbq_clinfo->maxdelay = maxdelay; 574 cbq_clinfo->maxburst = maxburst; 575 cbq_clinfo->minburst = minburst; 576 cbq_clinfo->av_pkt_size = av_pkt_size; 577 cbq_clinfo->max_pkt_size = max_pkt_size; 578 579 parent_handle = parent != NULL ? parent->handle : NULL_CLASS_HANDLE; 580 borrow_handle = borrow != NULL ? borrow->handle : NULL_CLASS_HANDLE; 581 582 if (cbq_class_spec(ifinfo, parent_handle, borrow_handle, pri, flags, 583 bandwidth, maxdelay, maxburst, minburst, 584 av_pkt_size, max_pkt_size, 585 &cbq_clinfo->class_spec) < 0) { 586 error = QOPERR_INVAL; 587 goto err_ret; 588 } 589 590 clinfo = NULL; 591 error = qop_add_class(&clinfo, class_name, ifinfo, parent, cbq_clinfo); 592 if (error != 0) 593 goto err_ret; 594 595 /* set delete hook */ 596 clinfo->delete_hook = qop_cbq_delete_class_hook; 597 598 if (parent == NULL) 599 cbq_ifinfo->root_class = clinfo; 600 else { 601 parent_clinfo = parent->private; 602 parent_clinfo->allocated += bandwidth; 603 } 604 if (flags & CBQCLF_DEFCLASS) 605 cbq_ifinfo->default_class = clinfo; 606 if (flags & CBQCLF_CTLCLASS) 607 cbq_ifinfo->ctl_class = clinfo; 608 609 switch (admission_type) { 610 case CBQ_QOS_CNTR_LOAD: 611 case CBQ_QOS_GUARANTEED: 612 case CBQ_QOS_PREDICTIVE: 613 case CBQ_QOS_CNTR_DELAY: 614 if (ifinfo->resv_class != NULL) { 615 LOG(LOG_ERR, 0, 616 "%s: duplicate resv meta class", class_name); 617 return (QOPERR_CLASS); 618 } 619 ifinfo->resv_class = clinfo; 620 } 621 622 if (rp != NULL) 623 *rp = clinfo; 624 return (0); 625 626 err_ret: 627 if (cbq_clinfo != NULL) { 628 free(cbq_clinfo); 629 if (clinfo != NULL) 630 clinfo->private = NULL; 631 } 632 return (error); 633 } 634 635 /* 636 * this is called from qop_delete_class() before a class is destroyed 637 * for discipline specific cleanup. 638 */ 639 static int 640 qop_cbq_delete_class_hook(struct classinfo *clinfo) 641 { 642 struct cbq_classinfo *cbq_clinfo, *parent_clinfo; 643 644 /* cancel admission control */ 645 if (clinfo->parent != NULL) { 646 cbq_clinfo = clinfo->private; 647 parent_clinfo = clinfo->parent->private; 648 649 parent_clinfo->allocated -= cbq_clinfo->bandwidth; 650 } 651 return (0); 652 } 653 654 int 655 qop_cbq_modify_class(struct classinfo *clinfo, u_int pri, u_int bandwidth, 656 u_int maxdelay, u_int maxburst, u_int minburst, 657 u_int av_pkt_size, u_int max_pkt_size, int flags) 658 { 659 struct ifinfo *ifinfo; 660 struct cbq_classinfo *cbq_clinfo, *parent_clinfo; 661 u_int parent_handle, borrow_handle; 662 u_int old_bandwidth; 663 int error; 664 665 ifinfo = clinfo->ifinfo; 666 cbq_clinfo = clinfo->private; 667 668 /* admission control */ 669 old_bandwidth = cbq_clinfo->bandwidth; 670 if (clinfo->parent != NULL) { 671 parent_clinfo = clinfo->parent->private; 672 if (bandwidth > old_bandwidth) { 673 /* increase bandwidth */ 674 if (bandwidth - old_bandwidth > 675 parent_clinfo->bandwidth 676 - parent_clinfo->allocated) 677 return (QOPERR_ADMISSION_NOBW); 678 } else if (bandwidth < old_bandwidth) { 679 /* decrease bandwidth */ 680 if (bandwidth < cbq_clinfo->allocated) 681 return (QOPERR_ADMISSION); 682 } 683 } 684 685 /* if average packet size isn't specified, set if mtu. */ 686 if (av_pkt_size == 0) { /* use default */ 687 av_pkt_size = ifinfo->ifmtu; 688 if (av_pkt_size > MCLBYTES) /* do what TCP does */ 689 av_pkt_size &= ~MCLBYTES; 690 } else if (av_pkt_size > ifinfo->ifmtu) 691 av_pkt_size = ifinfo->ifmtu; 692 693 if (max_pkt_size == 0) /* use default */ 694 max_pkt_size = ifinfo->ifmtu; 695 else if (max_pkt_size > ifinfo->ifmtu) 696 max_pkt_size = ifinfo->ifmtu; 697 698 cbq_clinfo->maxdelay = maxdelay; 699 cbq_clinfo->maxburst = maxburst; 700 cbq_clinfo->minburst = minburst; 701 cbq_clinfo->av_pkt_size = av_pkt_size; 702 cbq_clinfo->max_pkt_size = max_pkt_size; 703 704 parent_handle = cbq_clinfo->class_spec.parent_class_handle; 705 borrow_handle = cbq_clinfo->class_spec.borrow_class_handle; 706 707 if (cbq_class_spec(ifinfo, parent_handle, borrow_handle, pri, flags, 708 bandwidth, maxdelay, maxburst, minburst, 709 av_pkt_size, max_pkt_size, 710 &cbq_clinfo->class_spec) < 0) { 711 return (QOPERR_INVAL); 712 } 713 714 error = qop_modify_class(clinfo, NULL); 715 716 if (error == 0) { 717 if (clinfo->parent != NULL) { 718 parent_clinfo = clinfo->parent->private; 719 parent_clinfo->allocated -= old_bandwidth; 720 parent_clinfo->allocated += bandwidth; 721 } 722 cbq_clinfo->bandwidth = bandwidth; 723 } 724 return (error); 725 } 726 727 /* 728 * sanity check at enabling cbq: 729 * there must one root class and one default class for an interface 730 */ 731 static int 732 qop_cbq_enable_hook(struct ifinfo *ifinfo) 733 { 734 struct cbq_ifinfo *cbq_ifinfo; 735 736 cbq_ifinfo = ifinfo->private; 737 if (cbq_ifinfo->root_class == NULL) { 738 LOG(LOG_ERR, 0, "cbq: no root class on interface %s!", 739 ifinfo->ifname); 740 return (QOPERR_CLASS); 741 } 742 if (cbq_ifinfo->default_class == NULL) { 743 LOG(LOG_ERR, 0, "cbq: no default class on interface %s!", 744 ifinfo->ifname); 745 return (QOPERR_CLASS); 746 } 747 return (0); 748 } 749 750 static int 751 cbq_class_spec(struct ifinfo *ifinfo, u_long parent_class, 752 u_long borrow_class, u_int pri, int flags, 753 u_int bandwidth, u_int maxdelay, u_int maxburst, 754 u_int minburst, u_int av_pkt_size, u_int max_pkt_size, 755 cbq_class_spec_t *cl_spec) 756 { 757 struct cbq_ifinfo *cbq_ifinfo = ifinfo->private; 758 double maxq, maxidle_s, maxidle, minidle, 759 lofftime, nsPerByte, ptime, cptime; 760 double z = (double)(1 << RM_FILTER_GAIN); 761 double g = (1.0 - 1.0 / z); 762 double f; 763 double gton; 764 double gtom; 765 double maxrate; 766 767 /* Compute other class parameters */ 768 if (bandwidth == 0) 769 f = 0.0001; /* small enough? */ 770 else 771 f = ((double) bandwidth / (double) ifinfo->bandwidth); 772 773 if (av_pkt_size == 0) { /* use default */ 774 av_pkt_size = ifinfo->ifmtu; 775 if (av_pkt_size > MCLBYTES) /* do what TCP does */ 776 av_pkt_size &= ~MCLBYTES; 777 } else if (av_pkt_size > ifinfo->ifmtu) 778 av_pkt_size = ifinfo->ifmtu; 779 if (max_pkt_size == 0) /* use default */ 780 max_pkt_size = ifinfo->ifmtu; 781 else if (max_pkt_size > ifinfo->ifmtu) 782 max_pkt_size = ifinfo->ifmtu; 783 784 nsPerByte = cbq_ifinfo->nsPerByte / f; 785 ptime = (double) av_pkt_size * (double)cbq_ifinfo->nsPerByte; 786 maxrate = f * ((double)ifinfo->bandwidth / 8.0); 787 cptime = ptime * (1.0 - f) / f; 788 #if 1 /* ALTQ */ 789 if (nsPerByte * (double)max_pkt_size > (double)INT_MAX) { 790 /* 791 * this causes integer overflow in kernel! 792 * (bandwidth < 6Kbps when max_pkt_size=1500) 793 */ 794 if (bandwidth != 0) 795 LOG(LOG_WARNING, 0, "warning: class is too slow!!"); 796 nsPerByte = (double)(INT_MAX / max_pkt_size); 797 } 798 #endif 799 if (maxburst == 0) { /* use default */ 800 if (cptime > 10.0 * NS_PER_MS) 801 maxburst = 4; 802 else 803 maxburst = 16; 804 } 805 if (minburst == 0) /* use default */ 806 minburst = 2; 807 if (minburst > maxburst) 808 minburst = maxburst; 809 810 if (IsDebug(DEBUG_ALTQ)) { 811 int packet_time; 812 LOG(LOG_DEBUG, 0, 813 "cbq_flowspec: maxburst=%d,minburst=%d,pkt_size=%d", 814 maxburst, minburst, av_pkt_size); 815 LOG(LOG_DEBUG, 0, 816 " nsPerByte=%.2f ns, link's nsPerByte=%.2f, f=%.3f", 817 nsPerByte, cbq_ifinfo->nsPerByte, f); 818 packet_time = av_pkt_size * (int)nsPerByte / 1000; 819 LOG(LOG_DEBUG, 0, 820 " packet time=%d [us]\n", packet_time); 821 if (maxburst * packet_time < 20000) { 822 LOG(LOG_WARNING, 0, 823 "warning: maxburst smaller than timer granularity!"); 824 LOG(LOG_WARNING, 0, 825 " maxburst=%d, packet_time=%d [us]", 826 maxburst, packet_time); 827 } 828 } 829 gton = pow(g, (double)maxburst); 830 gtom = pow(g, (double)(minburst-1)); 831 maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); 832 maxidle_s = (1.0 - g); 833 if (maxidle > maxidle_s) 834 maxidle = ptime * maxidle; 835 else 836 maxidle = ptime * maxidle_s; 837 if (IsDebug(DEBUG_ALTQ)) 838 LOG(LOG_DEBUG, 0, " maxidle=%.2f us", maxidle/1000.0); 839 if (minburst) 840 lofftime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); 841 else 842 lofftime = cptime; 843 minidle = -((double)max_pkt_size * (double)nsPerByte); 844 if (IsDebug(DEBUG_ALTQ)) 845 LOG(LOG_DEBUG, 0, " lofftime=%.2f us minidle=%.2f us", 846 lofftime/1000.0, minidle/1000.0); 847 848 maxidle = ((maxidle * 8.0) / nsPerByte) * pow(2, RM_FILTER_GAIN); 849 #if 1 /* ALTQ */ 850 /* also scale lofftime and minidle */ 851 lofftime = (lofftime * 8.0) / nsPerByte * pow(2, RM_FILTER_GAIN); 852 minidle = ((minidle * 8.0) / nsPerByte) * pow(2, RM_FILTER_GAIN); 853 #endif 854 maxidle = maxidle / 1000.0; 855 lofftime = lofftime / 1000.0; 856 minidle = minidle / 1000.0; 857 /* adjust queue size when maxdelay is specified. 858 queue size should be relative to its share */ 859 if (maxdelay == 0) { 860 if (flags & (CBQCLF_RED|CBQCLF_RIO)) 861 maxq = 60.0; 862 else 863 maxq = 30.0; 864 } else { 865 maxq = ((double) maxdelay * NS_PER_MS) / (nsPerByte * av_pkt_size); 866 if (maxq < 4) { 867 LOG(LOG_WARNING, 0, 868 "warning: maxq (%d) is too small. set to %d", 869 (int)maxq, 4); 870 maxq = 4; 871 } 872 } 873 if (bandwidth == 0 && borrow_class == NULL_CLASS_HANDLE) 874 /* filter out this class by setting queue size to zero */ 875 maxq = 0; 876 if (IsDebug(DEBUG_ALTQ)) { 877 if ((u_int)maxq < maxburst) 878 LOG(LOG_WARNING, 0, 879 "warning: maxq (%d) is smaller than maxburst(%d)", 880 (int)maxq, maxburst); 881 else if (maxq > 100.0) 882 LOG(LOG_WARNING, 0, 883 "warning: maxq %d too large\n", (int)maxq); 884 LOG(LOG_DEBUG, 0, " maxq=%d", (int)maxq); 885 } 886 887 if (parent_class == NULL_CLASS_HANDLE) { 888 if ((flags & CBQCLF_ROOTCLASS) == 0) 889 flags |= CBQCLF_ROOTCLASS; 890 if (cbq_ifinfo->is_wrr) 891 flags |= CBQCLF_WRR; 892 if (cbq_ifinfo->is_efficient) 893 flags |= CBQCLF_EFFICIENT; 894 } 895 896 memset((void *)cl_spec, 0, sizeof(cbq_class_spec_t)); 897 cl_spec->priority = pri; 898 cl_spec->nano_sec_per_byte = (u_int) nsPerByte; 899 cl_spec->maxq = (u_int) maxq; 900 cl_spec->maxidle = (u_int) fabs(maxidle); 901 cl_spec->minidle = (int)minidle; 902 cl_spec->offtime = (u_int) fabs(lofftime); 903 904 cl_spec->parent_class_handle = parent_class; 905 cl_spec->borrow_class_handle = borrow_class; 906 907 cl_spec->pktsize = av_pkt_size; 908 cl_spec->flags = flags; 909 910 return (0); 911 } 912 913 914 /* 915 * system call interfaces for qdisc_ops 916 */ 917 static int 918 cbq_attach(struct ifinfo *ifinfo) 919 { 920 struct cbq_interface iface; 921 922 if (cbq_fd < 0 && 923 (cbq_fd = open(CBQ_DEVICE, O_RDWR)) < 0 && 924 (cbq_fd = open_module(CBQ_DEVICE, O_RDWR)) < 0) { 925 LOG(LOG_ERR, errno, "CBQ open"); 926 return (QOPERR_SYSCALL); 927 } 928 929 cbq_refcount++; 930 memset(&iface, 0, sizeof(iface)); 931 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 932 933 if (ioctl(cbq_fd, CBQ_IF_ATTACH, &iface) < 0) 934 return (QOPERR_SYSCALL); 935 return (0); 936 } 937 938 static int 939 cbq_detach(struct ifinfo *ifinfo) 940 { 941 struct cbq_interface iface; 942 943 memset(&iface, 0, sizeof(iface)); 944 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 945 946 if (ioctl(cbq_fd, CBQ_IF_DETACH, &iface) < 0) 947 return (QOPERR_SYSCALL); 948 949 if (--cbq_refcount == 0) { 950 close(cbq_fd); 951 cbq_fd = -1; 952 } 953 return (0); 954 } 955 956 static int 957 cbq_clear(struct ifinfo *ifinfo) 958 { 959 struct cbq_interface iface; 960 961 memset(&iface, 0, sizeof(iface)); 962 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 963 964 if (ioctl(cbq_fd, CBQ_CLEAR_HIERARCHY, &iface) < 0) 965 return (QOPERR_SYSCALL); 966 return (0); 967 } 968 969 static int 970 cbq_enable(struct ifinfo *ifinfo) 971 { 972 struct cbq_interface iface; 973 974 memset(&iface, 0, sizeof(iface)); 975 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 976 977 if (ioctl(cbq_fd, CBQ_ENABLE, &iface) < 0) 978 return (QOPERR_SYSCALL); 979 return (0); 980 } 981 982 static int 983 cbq_disable(struct ifinfo *ifinfo) 984 { 985 struct cbq_interface iface; 986 987 memset(&iface, 0, sizeof(iface)); 988 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 989 990 if (ioctl(cbq_fd, CBQ_DISABLE, &iface) < 0) 991 return (QOPERR_SYSCALL); 992 return (0); 993 } 994 995 static int 996 cbq_add_class(struct classinfo *clinfo) 997 { 998 struct cbq_add_class class_add; 999 struct cbq_classinfo *cbq_clinfo; 1000 struct cbq_ifinfo *cbq_ifinfo; 1001 1002 cbq_ifinfo = clinfo->ifinfo->private; 1003 cbq_clinfo = clinfo->private; 1004 1005 memset(&class_add, 0, sizeof(class_add)); 1006 strncpy(class_add.cbq_iface.cbq_ifacename, 1007 clinfo->ifinfo->ifname, IFNAMSIZ); 1008 1009 class_add.cbq_class = cbq_clinfo->class_spec; 1010 1011 if (ioctl(cbq_fd, CBQ_ADD_CLASS, &class_add) < 0) 1012 return (QOPERR_SYSCALL); 1013 1014 clinfo->handle = class_add.cbq_class_handle; 1015 return (0); 1016 } 1017 1018 static int 1019 cbq_modify_class(struct classinfo *clinfo, void *arg) 1020 { 1021 struct cbq_modify_class class_mod; 1022 struct cbq_classinfo *cbq_clinfo; 1023 1024 cbq_clinfo = clinfo->private; 1025 1026 memset(&class_mod, 0, sizeof(class_mod)); 1027 strncpy(class_mod.cbq_iface.cbq_ifacename, 1028 clinfo->ifinfo->ifname, IFNAMSIZ); 1029 class_mod.cbq_class_handle = clinfo->handle; 1030 class_mod.cbq_class = cbq_clinfo->class_spec; 1031 1032 if (ioctl(cbq_fd, CBQ_MODIFY_CLASS, &class_mod) < 0) 1033 return (QOPERR_SYSCALL); 1034 return (0); 1035 } 1036 1037 static int 1038 cbq_delete_class(struct classinfo *clinfo) 1039 { 1040 struct cbq_delete_class class_delete; 1041 1042 memset(&class_delete, 0, sizeof(class_delete)); 1043 strncpy(class_delete.cbq_iface.cbq_ifacename, 1044 clinfo->ifinfo->ifname, IFNAMSIZ); 1045 class_delete.cbq_class_handle = clinfo->handle; 1046 1047 if (ioctl(cbq_fd, CBQ_DEL_CLASS, &class_delete) < 0) 1048 return (QOPERR_SYSCALL); 1049 return (0); 1050 } 1051 1052 static int 1053 cbq_add_filter(struct fltrinfo *fltrinfo) 1054 { 1055 struct cbq_add_filter fltr_add; 1056 1057 memset(&fltr_add, 0, sizeof(fltr_add)); 1058 strncpy(fltr_add.cbq_iface.cbq_ifacename, 1059 fltrinfo->clinfo->ifinfo->ifname, IFNAMSIZ); 1060 fltr_add.cbq_class_handle = fltrinfo->clinfo->handle; 1061 fltr_add.cbq_filter = fltrinfo->fltr; 1062 1063 if (ioctl(cbq_fd, CBQ_ADD_FILTER, &fltr_add) < 0) 1064 return (QOPERR_SYSCALL); 1065 fltrinfo->handle = fltr_add.cbq_filter_handle; 1066 return (0); 1067 } 1068 1069 static int 1070 cbq_delete_filter(struct fltrinfo *fltrinfo) 1071 { 1072 struct cbq_delete_filter fltr_del; 1073 1074 memset(&fltr_del, 0, sizeof(fltr_del)); 1075 strncpy(fltr_del.cbq_iface.cbq_ifacename, 1076 fltrinfo->clinfo->ifinfo->ifname, IFNAMSIZ); 1077 fltr_del.cbq_filter_handle = fltrinfo->handle; 1078 1079 if (ioctl(cbq_fd, CBQ_DEL_FILTER, &fltr_del) < 0) 1080 return (QOPERR_SYSCALL); 1081 return (0); 1082 } 1083 1084 1085