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