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