1 /* $NetBSD: qop_cbq.c,v 1.10 2013/10/19 17:16:37 christos 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 766 /* Compute other class parameters */ 767 if (bandwidth == 0) 768 f = 0.0001; /* small enough? */ 769 else 770 f = ((double) bandwidth / (double) ifinfo->bandwidth); 771 772 if (av_pkt_size == 0) { /* use default */ 773 av_pkt_size = ifinfo->ifmtu; 774 if (av_pkt_size > MCLBYTES) /* do what TCP does */ 775 av_pkt_size &= ~MCLBYTES; 776 } else if (av_pkt_size > ifinfo->ifmtu) 777 av_pkt_size = ifinfo->ifmtu; 778 if (max_pkt_size == 0) /* use default */ 779 max_pkt_size = ifinfo->ifmtu; 780 else if (max_pkt_size > ifinfo->ifmtu) 781 max_pkt_size = ifinfo->ifmtu; 782 783 nsPerByte = cbq_ifinfo->nsPerByte / f; 784 ptime = (double) av_pkt_size * (double)cbq_ifinfo->nsPerByte; 785 cptime = ptime * (1.0 - f) / f; 786 #if 1 /* ALTQ */ 787 if (nsPerByte * (double)max_pkt_size > (double)INT_MAX) { 788 /* 789 * this causes integer overflow in kernel! 790 * (bandwidth < 6Kbps when max_pkt_size=1500) 791 */ 792 if (bandwidth != 0) 793 LOG(LOG_WARNING, 0, "warning: class is too slow!!"); 794 nsPerByte = (double)(INT_MAX / max_pkt_size); 795 } 796 #endif 797 if (maxburst == 0) { /* use default */ 798 if (cptime > 10.0 * NS_PER_MS) 799 maxburst = 4; 800 else 801 maxburst = 16; 802 } 803 if (minburst == 0) /* use default */ 804 minburst = 2; 805 if (minburst > maxburst) 806 minburst = maxburst; 807 808 if (IsDebug(DEBUG_ALTQ)) { 809 int packet_time; 810 LOG(LOG_DEBUG, 0, 811 "cbq_flowspec: maxburst=%d,minburst=%d,pkt_size=%d", 812 maxburst, minburst, av_pkt_size); 813 LOG(LOG_DEBUG, 0, 814 " nsPerByte=%.2f ns, link's nsPerByte=%.2f, f=%.3f", 815 nsPerByte, cbq_ifinfo->nsPerByte, f); 816 packet_time = av_pkt_size * (int)nsPerByte / 1000; 817 LOG(LOG_DEBUG, 0, 818 " packet time=%d [us]\n", packet_time); 819 if (maxburst * packet_time < 20000) { 820 LOG(LOG_WARNING, 0, 821 "warning: maxburst smaller than timer granularity!"); 822 LOG(LOG_WARNING, 0, 823 " maxburst=%d, packet_time=%d [us]", 824 maxburst, packet_time); 825 } 826 } 827 gton = pow(g, (double)maxburst); 828 gtom = pow(g, (double)(minburst-1)); 829 maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); 830 maxidle_s = (1.0 - g); 831 if (maxidle > maxidle_s) 832 maxidle = ptime * maxidle; 833 else 834 maxidle = ptime * maxidle_s; 835 if (IsDebug(DEBUG_ALTQ)) 836 LOG(LOG_DEBUG, 0, " maxidle=%.2f us", maxidle/1000.0); 837 if (minburst) 838 lofftime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); 839 else 840 lofftime = cptime; 841 minidle = -((double)max_pkt_size * (double)nsPerByte); 842 if (IsDebug(DEBUG_ALTQ)) 843 LOG(LOG_DEBUG, 0, " lofftime=%.2f us minidle=%.2f us", 844 lofftime/1000.0, minidle/1000.0); 845 846 maxidle = ((maxidle * 8.0) / nsPerByte) * pow(2, RM_FILTER_GAIN); 847 #if 1 /* ALTQ */ 848 /* also scale lofftime and minidle */ 849 lofftime = (lofftime * 8.0) / nsPerByte * pow(2, RM_FILTER_GAIN); 850 minidle = ((minidle * 8.0) / nsPerByte) * pow(2, RM_FILTER_GAIN); 851 #endif 852 maxidle = maxidle / 1000.0; 853 lofftime = lofftime / 1000.0; 854 minidle = minidle / 1000.0; 855 /* adjust queue size when maxdelay is specified. 856 queue size should be relative to its share */ 857 if (maxdelay == 0) { 858 if (flags & (CBQCLF_RED|CBQCLF_RIO)) 859 maxq = 60.0; 860 else 861 maxq = 30.0; 862 } else { 863 maxq = ((double) maxdelay * NS_PER_MS) / (nsPerByte * av_pkt_size); 864 if (maxq < 4) { 865 LOG(LOG_WARNING, 0, 866 "warning: maxq (%d) is too small. set to %d", 867 (int)maxq, 4); 868 maxq = 4; 869 } 870 } 871 if (bandwidth == 0 && borrow_class == NULL_CLASS_HANDLE) 872 /* filter out this class by setting queue size to zero */ 873 maxq = 0; 874 if (IsDebug(DEBUG_ALTQ)) { 875 if ((u_int)maxq < maxburst) 876 LOG(LOG_WARNING, 0, 877 "warning: maxq (%d) is smaller than maxburst(%d)", 878 (int)maxq, maxburst); 879 else if (maxq > 100.0) 880 LOG(LOG_WARNING, 0, 881 "warning: maxq %d too large\n", (int)maxq); 882 LOG(LOG_DEBUG, 0, " maxq=%d", (int)maxq); 883 } 884 885 if (parent_class == NULL_CLASS_HANDLE) { 886 if ((flags & CBQCLF_ROOTCLASS) == 0) 887 flags |= CBQCLF_ROOTCLASS; 888 if (cbq_ifinfo->is_wrr) 889 flags |= CBQCLF_WRR; 890 if (cbq_ifinfo->is_efficient) 891 flags |= CBQCLF_EFFICIENT; 892 } 893 894 memset((void *)cl_spec, 0, sizeof(cbq_class_spec_t)); 895 cl_spec->priority = pri; 896 cl_spec->nano_sec_per_byte = (u_int) nsPerByte; 897 cl_spec->maxq = (u_int) maxq; 898 cl_spec->maxidle = (u_int) fabs(maxidle); 899 cl_spec->minidle = (int)minidle; 900 cl_spec->offtime = (u_int) fabs(lofftime); 901 902 cl_spec->parent_class_handle = parent_class; 903 cl_spec->borrow_class_handle = borrow_class; 904 905 cl_spec->pktsize = av_pkt_size; 906 cl_spec->flags = flags; 907 908 return (0); 909 } 910 911 912 /* 913 * system call interfaces for qdisc_ops 914 */ 915 static int 916 cbq_attach(struct ifinfo *ifinfo) 917 { 918 struct cbq_interface iface; 919 920 if (cbq_fd < 0 && 921 (cbq_fd = open(CBQ_DEVICE, O_RDWR)) < 0 && 922 (cbq_fd = open_module(CBQ_DEVICE, O_RDWR)) < 0) { 923 LOG(LOG_ERR, errno, "CBQ open"); 924 return (QOPERR_SYSCALL); 925 } 926 927 cbq_refcount++; 928 memset(&iface, 0, sizeof(iface)); 929 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 930 931 if (ioctl(cbq_fd, CBQ_IF_ATTACH, &iface) < 0) 932 return (QOPERR_SYSCALL); 933 return (0); 934 } 935 936 static int 937 cbq_detach(struct ifinfo *ifinfo) 938 { 939 struct cbq_interface iface; 940 941 memset(&iface, 0, sizeof(iface)); 942 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 943 944 if (ioctl(cbq_fd, CBQ_IF_DETACH, &iface) < 0) 945 return (QOPERR_SYSCALL); 946 947 if (--cbq_refcount == 0) { 948 close(cbq_fd); 949 cbq_fd = -1; 950 } 951 return (0); 952 } 953 954 static int 955 cbq_clear(struct ifinfo *ifinfo) 956 { 957 struct cbq_interface iface; 958 959 memset(&iface, 0, sizeof(iface)); 960 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 961 962 if (ioctl(cbq_fd, CBQ_CLEAR_HIERARCHY, &iface) < 0) 963 return (QOPERR_SYSCALL); 964 return (0); 965 } 966 967 static int 968 cbq_enable(struct ifinfo *ifinfo) 969 { 970 struct cbq_interface iface; 971 972 memset(&iface, 0, sizeof(iface)); 973 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 974 975 if (ioctl(cbq_fd, CBQ_ENABLE, &iface) < 0) 976 return (QOPERR_SYSCALL); 977 return (0); 978 } 979 980 static int 981 cbq_disable(struct ifinfo *ifinfo) 982 { 983 struct cbq_interface iface; 984 985 memset(&iface, 0, sizeof(iface)); 986 strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ); 987 988 if (ioctl(cbq_fd, CBQ_DISABLE, &iface) < 0) 989 return (QOPERR_SYSCALL); 990 return (0); 991 } 992 993 static int 994 cbq_add_class(struct classinfo *clinfo) 995 { 996 struct cbq_add_class class_add; 997 struct cbq_classinfo *cbq_clinfo; 998 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