pfctl_altq.c revision 1.1.1.2
1/* $OpenBSD: pfctl_altq.c,v 1.85 2004/05/20 12:18:52 henning Exp $ */ 2 3/* 4 * Copyright (c) 2002 5 * Sony Computer Science Laboratories Inc. 6 * Copyright (c) 2002, 2003 Henning Brauer <henning@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21#include <sys/types.h> 22#include <sys/ioctl.h> 23#include <sys/socket.h> 24 25#include <net/if.h> 26#include <netinet/in.h> 27#include <net/pfvar.h> 28 29#include <err.h> 30#include <errno.h> 31#include <limits.h> 32#include <math.h> 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36#include <unistd.h> 37 38#include <altq/altq.h> 39#include <altq/altq_cbq.h> 40#include <altq/altq_priq.h> 41#include <altq/altq_hfsc.h> 42 43#include "pfctl_parser.h" 44#include "pfctl.h" 45 46#define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) 47 48TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs); 49LIST_HEAD(gen_sc, segment) rtsc, lssc; 50 51struct pf_altq *qname_to_pfaltq(const char *, const char *); 52u_int32_t qname_to_qid(const char *); 53 54static int eval_pfqueue_cbq(struct pfctl *, struct pf_altq *); 55static int cbq_compute_idletime(struct pfctl *, struct pf_altq *); 56static int check_commit_cbq(int, int, struct pf_altq *); 57static int print_cbq_opts(const struct pf_altq *); 58 59static int eval_pfqueue_priq(struct pfctl *, struct pf_altq *); 60static int check_commit_priq(int, int, struct pf_altq *); 61static int print_priq_opts(const struct pf_altq *); 62 63static int eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *); 64static int check_commit_hfsc(int, int, struct pf_altq *); 65static int print_hfsc_opts(const struct pf_altq *, 66 const struct node_queue_opt *); 67 68static void gsc_add_sc(struct gen_sc *, struct service_curve *); 69static int is_gsc_under_sc(struct gen_sc *, 70 struct service_curve *); 71static void gsc_destroy(struct gen_sc *); 72static struct segment *gsc_getentry(struct gen_sc *, double); 73static int gsc_add_seg(struct gen_sc *, double, double, double, 74 double); 75static double sc_x2y(struct service_curve *, double); 76 77u_int32_t getifspeed(char *); 78u_long getifmtu(char *); 79int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, 80 u_int32_t); 81u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); 82void print_hfsc_sc(const char *, u_int, u_int, u_int, 83 const struct node_hfsc_sc *); 84 85void 86pfaltq_store(struct pf_altq *a) 87{ 88 struct pf_altq *altq; 89 90 if ((altq = malloc(sizeof(*altq))) == NULL) 91 err(1, "malloc"); 92 memcpy(altq, a, sizeof(struct pf_altq)); 93 TAILQ_INSERT_TAIL(&altqs, altq, entries); 94} 95 96void 97pfaltq_free(struct pf_altq *a) 98{ 99 struct pf_altq *altq; 100 101 TAILQ_FOREACH(altq, &altqs, entries) { 102 if (strncmp(a->ifname, altq->ifname, IFNAMSIZ) == 0 && 103 strncmp(a->qname, altq->qname, PF_QNAME_SIZE) == 0) { 104 TAILQ_REMOVE(&altqs, altq, entries); 105 free(altq); 106 return; 107 } 108 } 109} 110 111struct pf_altq * 112pfaltq_lookup(const char *ifname) 113{ 114 struct pf_altq *altq; 115 116 TAILQ_FOREACH(altq, &altqs, entries) { 117 if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && 118 altq->qname[0] == 0) 119 return (altq); 120 } 121 return (NULL); 122} 123 124struct pf_altq * 125qname_to_pfaltq(const char *qname, const char *ifname) 126{ 127 struct pf_altq *altq; 128 129 TAILQ_FOREACH(altq, &altqs, entries) { 130 if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && 131 strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) 132 return (altq); 133 } 134 return (NULL); 135} 136 137u_int32_t 138qname_to_qid(const char *qname) 139{ 140 struct pf_altq *altq; 141 142 /* 143 * We guarantee that same named queues on different interfaces 144 * have the same qid, so we do NOT need to limit matching on 145 * one interface! 146 */ 147 148 TAILQ_FOREACH(altq, &altqs, entries) { 149 if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) 150 return (altq->qid); 151 } 152 return (0); 153} 154 155void 156print_altq(const struct pf_altq *a, unsigned level, struct node_queue_bw *bw, 157 struct node_queue_opt *qopts) 158{ 159 if (a->qname[0] != 0) { 160 print_queue(a, level, bw, 0, qopts); 161 return; 162 } 163 164 printf("altq on %s ", a->ifname); 165 166 switch (a->scheduler) { 167 case ALTQT_CBQ: 168 if (!print_cbq_opts(a)) 169 printf("cbq "); 170 break; 171 case ALTQT_PRIQ: 172 if (!print_priq_opts(a)) 173 printf("priq "); 174 break; 175 case ALTQT_HFSC: 176 if (!print_hfsc_opts(a, qopts)) 177 printf("hfsc "); 178 break; 179 } 180 181 if (bw != NULL && bw->bw_percent > 0) { 182 if (bw->bw_percent < 100) 183 printf("bandwidth %u%% ", bw->bw_percent); 184 } else 185 printf("bandwidth %s ", rate2str((double)a->ifbandwidth)); 186 187 if (a->qlimit != DEFAULT_QLIMIT) 188 printf("qlimit %u ", a->qlimit); 189 printf("tbrsize %u ", a->tbrsize); 190} 191 192void 193print_queue(const struct pf_altq *a, unsigned level, struct node_queue_bw *bw, 194 int print_interface, struct node_queue_opt *qopts) 195{ 196 unsigned i; 197 198 printf("queue "); 199 for (i = 0; i < level; ++i) 200 printf(" "); 201 printf("%s ", a->qname); 202 if (print_interface) 203 printf("on %s ", a->ifname); 204 if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) { 205 if (bw != NULL && bw->bw_percent > 0) { 206 if (bw->bw_percent < 100) 207 printf("bandwidth %u%% ", bw->bw_percent); 208 } else 209 printf("bandwidth %s ", rate2str((double)a->bandwidth)); 210 } 211 if (a->priority != DEFAULT_PRIORITY) 212 printf("priority %u ", a->priority); 213 if (a->qlimit != DEFAULT_QLIMIT) 214 printf("qlimit %u ", a->qlimit); 215 switch (a->scheduler) { 216 case ALTQT_CBQ: 217 print_cbq_opts(a); 218 break; 219 case ALTQT_PRIQ: 220 print_priq_opts(a); 221 break; 222 case ALTQT_HFSC: 223 print_hfsc_opts(a, qopts); 224 break; 225 } 226} 227 228/* 229 * eval_pfaltq computes the discipline parameters. 230 */ 231int 232eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, 233 struct node_queue_opt *opts) 234{ 235 u_int rate, size, errors = 0; 236 237 if (bw->bw_absolute > 0) 238 pa->ifbandwidth = bw->bw_absolute; 239 else 240 if ((rate = getifspeed(pa->ifname)) == 0) { 241 fprintf(stderr, "cannot determine interface bandwidth " 242 "for %s, specify an absolute bandwidth\n", 243 pa->ifname); 244 errors++; 245 } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) 246 pa->ifbandwidth = rate; 247 248 errors += eval_queue_opts(pa, opts, pa->ifbandwidth); 249 250 /* if tbrsize is not specified, use heuristics */ 251 if (pa->tbrsize == 0) { 252 rate = pa->ifbandwidth; 253 if (rate <= 1 * 1000 * 1000) 254 size = 1; 255 else if (rate <= 10 * 1000 * 1000) 256 size = 4; 257 else if (rate <= 200 * 1000 * 1000) 258 size = 8; 259 else 260 size = 24; 261 size = size * getifmtu(pa->ifname); 262 if (size > 0xffff) 263 size = 0xffff; 264 pa->tbrsize = size; 265 } 266 return (errors); 267} 268 269/* 270 * check_commit_altq does consistency check for each interface 271 */ 272int 273check_commit_altq(int dev, int opts) 274{ 275 struct pf_altq *altq; 276 int error = 0; 277 278 /* call the discipline check for each interface. */ 279 TAILQ_FOREACH(altq, &altqs, entries) { 280 if (altq->qname[0] == 0) { 281 switch (altq->scheduler) { 282 case ALTQT_CBQ: 283 error = check_commit_cbq(dev, opts, altq); 284 break; 285 case ALTQT_PRIQ: 286 error = check_commit_priq(dev, opts, altq); 287 break; 288 case ALTQT_HFSC: 289 error = check_commit_hfsc(dev, opts, altq); 290 break; 291 default: 292 break; 293 } 294 } 295 } 296 return (error); 297} 298 299/* 300 * eval_pfqueue computes the queue parameters. 301 */ 302int 303eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, 304 struct node_queue_opt *opts) 305{ 306 /* should be merged with expand_queue */ 307 struct pf_altq *if_pa, *parent, *altq; 308 u_int32_t bwsum; 309 int error = 0; 310 311 /* find the corresponding interface and copy fields used by queues */ 312 if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) { 313 fprintf(stderr, "altq not defined on %s\n", pa->ifname); 314 return (1); 315 } 316 pa->scheduler = if_pa->scheduler; 317 pa->ifbandwidth = if_pa->ifbandwidth; 318 319 if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) { 320 fprintf(stderr, "queue %s already exists on interface %s\n", 321 pa->qname, pa->ifname); 322 return (1); 323 } 324 pa->qid = qname_to_qid(pa->qname); 325 326 parent = NULL; 327 if (pa->parent[0] != 0) { 328 parent = qname_to_pfaltq(pa->parent, pa->ifname); 329 if (parent == NULL) { 330 fprintf(stderr, "parent %s not found for %s\n", 331 pa->parent, pa->qname); 332 return (1); 333 } 334 pa->parent_qid = parent->qid; 335 } 336 if (pa->qlimit == 0) 337 pa->qlimit = DEFAULT_QLIMIT; 338 339 if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) { 340 pa->bandwidth = eval_bwspec(bw, 341 parent == NULL ? 0 : parent->bandwidth); 342 343 if (pa->bandwidth > pa->ifbandwidth) { 344 fprintf(stderr, "bandwidth for %s higher than " 345 "interface\n", pa->qname); 346 return (1); 347 } 348 /* check the sum of the child bandwidth is under parent's */ 349 if (parent != NULL) { 350 if (pa->bandwidth > parent->bandwidth) { 351 warnx("bandwidth for %s higher than parent", 352 pa->qname); 353 return (1); 354 } 355 bwsum = 0; 356 TAILQ_FOREACH(altq, &altqs, entries) { 357 if (strncmp(altq->ifname, pa->ifname, 358 IFNAMSIZ) == 0 && 359 altq->qname[0] != 0 && 360 strncmp(altq->parent, pa->parent, 361 PF_QNAME_SIZE) == 0) 362 bwsum += altq->bandwidth; 363 } 364 bwsum += pa->bandwidth; 365 if (bwsum > parent->bandwidth) { 366 warnx("the sum of the child bandwidth higher" 367 " than parent \"%s\"", parent->qname); 368 } 369 } 370 } 371 372 if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth)) 373 return (1); 374 375 switch (pa->scheduler) { 376 case ALTQT_CBQ: 377 error = eval_pfqueue_cbq(pf, pa); 378 break; 379 case ALTQT_PRIQ: 380 error = eval_pfqueue_priq(pf, pa); 381 break; 382 case ALTQT_HFSC: 383 error = eval_pfqueue_hfsc(pf, pa); 384 break; 385 default: 386 break; 387 } 388 return (error); 389} 390 391/* 392 * CBQ support functions 393 */ 394#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */ 395#define RM_NS_PER_SEC (1000000000) 396 397static int 398eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa) 399{ 400 struct cbq_opts *opts; 401 u_int ifmtu; 402 403 if (pa->priority >= CBQ_MAXPRI) { 404 warnx("priority out of range: max %d", CBQ_MAXPRI - 1); 405 return (-1); 406 } 407 408 ifmtu = getifmtu(pa->ifname); 409 opts = &pa->pq_u.cbq_opts; 410 411 if (opts->pktsize == 0) { /* use default */ 412 opts->pktsize = ifmtu; 413 if (opts->pktsize > MCLBYTES) /* do what TCP does */ 414 opts->pktsize &= ~MCLBYTES; 415 } else if (opts->pktsize > ifmtu) 416 opts->pktsize = ifmtu; 417 if (opts->maxpktsize == 0) /* use default */ 418 opts->maxpktsize = ifmtu; 419 else if (opts->maxpktsize > ifmtu) 420 opts->pktsize = ifmtu; 421 422 if (opts->pktsize > opts->maxpktsize) 423 opts->pktsize = opts->maxpktsize; 424 425 if (pa->parent[0] == 0) 426 opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR); 427 428 cbq_compute_idletime(pf, pa); 429 return (0); 430} 431 432/* 433 * compute ns_per_byte, maxidle, minidle, and offtime 434 */ 435static int 436cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa) 437{ 438 struct cbq_opts *opts; 439 double maxidle_s, maxidle, minidle; 440 double offtime, nsPerByte, ifnsPerByte, ptime, cptime; 441 double z, g, f, gton, gtom; 442 u_int minburst, maxburst; 443 444 opts = &pa->pq_u.cbq_opts; 445 ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8; 446 minburst = opts->minburst; 447 maxburst = opts->maxburst; 448 449 if (pa->bandwidth == 0) 450 f = 0.0001; /* small enough? */ 451 else 452 f = ((double) pa->bandwidth / (double) pa->ifbandwidth); 453 454 nsPerByte = ifnsPerByte / f; 455 ptime = (double)opts->pktsize * ifnsPerByte; 456 cptime = ptime * (1.0 - f) / f; 457 458 if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) { 459 /* 460 * this causes integer overflow in kernel! 461 * (bandwidth < 6Kbps when max_pkt_size=1500) 462 */ 463 if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0) 464 warnx("queue bandwidth must be larger than %s", 465 rate2str(ifnsPerByte * (double)opts->maxpktsize / 466 (double)INT_MAX * (double)pa->ifbandwidth)); 467 fprintf(stderr, "cbq: queue %s is too slow!\n", 468 pa->qname); 469 nsPerByte = (double)(INT_MAX / opts->maxpktsize); 470 } 471 472 if (maxburst == 0) { /* use default */ 473 if (cptime > 10.0 * 1000000) 474 maxburst = 4; 475 else 476 maxburst = 16; 477 } 478 if (minburst == 0) /* use default */ 479 minburst = 2; 480 if (minburst > maxburst) 481 minburst = maxburst; 482 483 z = (double)(1 << RM_FILTER_GAIN); 484 g = (1.0 - 1.0 / z); 485 gton = pow(g, (double)maxburst); 486 gtom = pow(g, (double)(minburst-1)); 487 maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); 488 maxidle_s = (1.0 - g); 489 if (maxidle > maxidle_s) 490 maxidle = ptime * maxidle; 491 else 492 maxidle = ptime * maxidle_s; 493 if (minburst) 494 offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); 495 else 496 offtime = cptime; 497 minidle = -((double)opts->maxpktsize * (double)nsPerByte); 498 499 /* scale parameters */ 500 maxidle = ((maxidle * 8.0) / nsPerByte) * 501 pow(2.0, (double)RM_FILTER_GAIN); 502 offtime = (offtime * 8.0) / nsPerByte * 503 pow(2.0, (double)RM_FILTER_GAIN); 504 minidle = ((minidle * 8.0) / nsPerByte) * 505 pow(2.0, (double)RM_FILTER_GAIN); 506 507 maxidle = maxidle / 1000.0; 508 offtime = offtime / 1000.0; 509 minidle = minidle / 1000.0; 510 511 opts->minburst = minburst; 512 opts->maxburst = maxburst; 513 opts->ns_per_byte = (u_int)nsPerByte; 514 opts->maxidle = (u_int)fabs(maxidle); 515 opts->minidle = (int)minidle; 516 opts->offtime = (u_int)fabs(offtime); 517 518 return (0); 519} 520 521static int 522check_commit_cbq(int dev, int opts, struct pf_altq *pa) 523{ 524 struct pf_altq *altq; 525 int root_class, default_class; 526 int error = 0; 527 528 /* 529 * check if cbq has one root queue and one default queue 530 * for this interface 531 */ 532 root_class = default_class = 0; 533 TAILQ_FOREACH(altq, &altqs, entries) { 534 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 535 continue; 536 if (altq->qname[0] == 0) /* this is for interface */ 537 continue; 538 if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS) 539 root_class++; 540 if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS) 541 default_class++; 542 } 543 if (root_class != 1) { 544 warnx("should have one root queue on %s", pa->ifname); 545 error++; 546 } 547 if (default_class != 1) { 548 warnx("should have one default queue on %s", pa->ifname); 549 error++; 550 } 551 return (error); 552} 553 554static int 555print_cbq_opts(const struct pf_altq *a) 556{ 557 const struct cbq_opts *opts; 558 559 opts = &a->pq_u.cbq_opts; 560 if (opts->flags) { 561 printf("cbq("); 562 if (opts->flags & CBQCLF_RED) 563 printf(" red"); 564 if (opts->flags & CBQCLF_ECN) 565 printf(" ecn"); 566 if (opts->flags & CBQCLF_RIO) 567 printf(" rio"); 568 if (opts->flags & CBQCLF_CLEARDSCP) 569 printf(" cleardscp"); 570 if (opts->flags & CBQCLF_FLOWVALVE) 571 printf(" flowvalve"); 572 if (opts->flags & CBQCLF_BORROW) 573 printf(" borrow"); 574 if (opts->flags & CBQCLF_WRR) 575 printf(" wrr"); 576 if (opts->flags & CBQCLF_EFFICIENT) 577 printf(" efficient"); 578 if (opts->flags & CBQCLF_ROOTCLASS) 579 printf(" root"); 580 if (opts->flags & CBQCLF_DEFCLASS) 581 printf(" default"); 582 printf(" ) "); 583 584 return (1); 585 } else 586 return (0); 587} 588 589/* 590 * PRIQ support functions 591 */ 592static int 593eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa) 594{ 595 struct pf_altq *altq; 596 597 if (pa->priority >= PRIQ_MAXPRI) { 598 warnx("priority out of range: max %d", PRIQ_MAXPRI - 1); 599 return (-1); 600 } 601 /* the priority should be unique for the interface */ 602 TAILQ_FOREACH(altq, &altqs, entries) { 603 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 && 604 altq->qname[0] != 0 && altq->priority == pa->priority) { 605 warnx("%s and %s have the same priority", 606 altq->qname, pa->qname); 607 return (-1); 608 } 609 } 610 611 return (0); 612} 613 614static int 615check_commit_priq(int dev, int opts, struct pf_altq *pa) 616{ 617 struct pf_altq *altq; 618 int default_class; 619 int error = 0; 620 621 /* 622 * check if priq has one default class for this interface 623 */ 624 default_class = 0; 625 TAILQ_FOREACH(altq, &altqs, entries) { 626 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 627 continue; 628 if (altq->qname[0] == 0) /* this is for interface */ 629 continue; 630 if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS) 631 default_class++; 632 } 633 if (default_class != 1) { 634 warnx("should have one default queue on %s", pa->ifname); 635 error++; 636 } 637 return (error); 638} 639 640static int 641print_priq_opts(const struct pf_altq *a) 642{ 643 const struct priq_opts *opts; 644 645 opts = &a->pq_u.priq_opts; 646 647 if (opts->flags) { 648 printf("priq("); 649 if (opts->flags & PRCF_RED) 650 printf(" red"); 651 if (opts->flags & PRCF_ECN) 652 printf(" ecn"); 653 if (opts->flags & PRCF_RIO) 654 printf(" rio"); 655 if (opts->flags & PRCF_CLEARDSCP) 656 printf(" cleardscp"); 657 if (opts->flags & PRCF_DEFAULTCLASS) 658 printf(" default"); 659 printf(" ) "); 660 661 return (1); 662 } else 663 return (0); 664} 665 666/* 667 * HFSC support functions 668 */ 669static int 670eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa) 671{ 672 struct pf_altq *altq, *parent; 673 struct hfsc_opts *opts; 674 struct service_curve sc; 675 676 opts = &pa->pq_u.hfsc_opts; 677 678 if (pa->parent[0] == 0) { 679 /* root queue */ 680 opts->lssc_m1 = pa->ifbandwidth; 681 opts->lssc_m2 = pa->ifbandwidth; 682 opts->lssc_d = 0; 683 return (0); 684 } 685 686 LIST_INIT(&rtsc); 687 LIST_INIT(&lssc); 688 689 /* if link_share is not specified, use bandwidth */ 690 if (opts->lssc_m2 == 0) 691 opts->lssc_m2 = pa->bandwidth; 692 693 if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) || 694 (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) || 695 (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) { 696 warnx("m2 is zero for %s", pa->qname); 697 return (-1); 698 } 699 700 if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || 701 (opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || 702 (opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0)) { 703 warnx("m1 must be zero for convex curve: %s", pa->qname); 704 return (-1); 705 } 706 707 /* 708 * admission control: 709 * for the real-time service curve, the sum of the service curves 710 * should not exceed 80% of the interface bandwidth. 20% is reserved 711 * not to over-commit the actual interface bandwidth. 712 * for the link-sharing service curve, the sum of the child service 713 * curve should not exceed the parent service curve. 714 * for the upper-limit service curve, the assigned bandwidth should 715 * be smaller than the interface bandwidth, and the upper-limit should 716 * be larger than the real-time service curve when both are defined. 717 */ 718 parent = qname_to_pfaltq(pa->parent, pa->ifname); 719 if (parent == NULL) 720 errx(1, "parent %s not found for %s", pa->parent, pa->qname); 721 722 TAILQ_FOREACH(altq, &altqs, entries) { 723 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 724 continue; 725 if (altq->qname[0] == 0) /* this is for interface */ 726 continue; 727 728 /* if the class has a real-time service curve, add it. */ 729 if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) { 730 sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1; 731 sc.d = altq->pq_u.hfsc_opts.rtsc_d; 732 sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2; 733 gsc_add_sc(&rtsc, &sc); 734 } 735 736 if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) 737 continue; 738 739 /* if the class has a link-sharing service curve, add it. */ 740 if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) { 741 sc.m1 = altq->pq_u.hfsc_opts.lssc_m1; 742 sc.d = altq->pq_u.hfsc_opts.lssc_d; 743 sc.m2 = altq->pq_u.hfsc_opts.lssc_m2; 744 gsc_add_sc(&lssc, &sc); 745 } 746 } 747 748 /* check the real-time service curve. reserve 20% of interface bw */ 749 if (opts->rtsc_m2 != 0) { 750 /* add this queue to the sum */ 751 sc.m1 = opts->rtsc_m1; 752 sc.d = opts->rtsc_d; 753 sc.m2 = opts->rtsc_m2; 754 gsc_add_sc(&rtsc, &sc); 755 /* compare the sum with 80% of the interface */ 756 sc.m1 = 0; 757 sc.d = 0; 758 sc.m2 = pa->ifbandwidth / 100 * 80; 759 if (!is_gsc_under_sc(&rtsc, &sc)) { 760 warnx("real-time sc exceeds 80%% of the interface " 761 "bandwidth (%s)", rate2str((double)sc.m2)); 762 goto err_ret; 763 } 764 } 765 766 /* check the link-sharing service curve. */ 767 if (opts->lssc_m2 != 0) { 768 /* add this queue to the child sum */ 769 sc.m1 = opts->lssc_m1; 770 sc.d = opts->lssc_d; 771 sc.m2 = opts->lssc_m2; 772 gsc_add_sc(&lssc, &sc); 773 /* compare the sum of the children with parent's sc */ 774 sc.m1 = parent->pq_u.hfsc_opts.lssc_m1; 775 sc.d = parent->pq_u.hfsc_opts.lssc_d; 776 sc.m2 = parent->pq_u.hfsc_opts.lssc_m2; 777 if (!is_gsc_under_sc(&lssc, &sc)) { 778 warnx("link-sharing sc exceeds parent's sc"); 779 goto err_ret; 780 } 781 } 782 783 /* check the upper-limit service curve. */ 784 if (opts->ulsc_m2 != 0) { 785 if (opts->ulsc_m1 > pa->ifbandwidth || 786 opts->ulsc_m2 > pa->ifbandwidth) { 787 warnx("upper-limit larger than interface bandwidth"); 788 goto err_ret; 789 } 790 if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) { 791 warnx("upper-limit sc smaller than real-time sc"); 792 goto err_ret; 793 } 794 } 795 796 gsc_destroy(&rtsc); 797 gsc_destroy(&lssc); 798 799 return (0); 800 801err_ret: 802 gsc_destroy(&rtsc); 803 gsc_destroy(&lssc); 804 return (-1); 805} 806 807static int 808check_commit_hfsc(int dev, int opts, struct pf_altq *pa) 809{ 810 struct pf_altq *altq, *def = NULL; 811 int default_class; 812 int error = 0; 813 814 /* check if hfsc has one default queue for this interface */ 815 default_class = 0; 816 TAILQ_FOREACH(altq, &altqs, entries) { 817 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 818 continue; 819 if (altq->qname[0] == 0) /* this is for interface */ 820 continue; 821 if (altq->parent[0] == 0) /* dummy root */ 822 continue; 823 if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) { 824 default_class++; 825 def = altq; 826 } 827 } 828 if (default_class != 1) { 829 warnx("should have one default queue on %s", pa->ifname); 830 return (1); 831 } 832 /* make sure the default queue is a leaf */ 833 TAILQ_FOREACH(altq, &altqs, entries) { 834 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 835 continue; 836 if (altq->qname[0] == 0) /* this is for interface */ 837 continue; 838 if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { 839 warnx("default queue is not a leaf"); 840 error++; 841 } 842 } 843 return (error); 844} 845 846static int 847print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) 848{ 849 const struct hfsc_opts *opts; 850 const struct node_hfsc_sc *rtsc, *lssc, *ulsc; 851 852 opts = &a->pq_u.hfsc_opts; 853 if (qopts == NULL) 854 rtsc = lssc = ulsc = NULL; 855 else { 856 rtsc = &qopts->data.hfsc_opts.realtime; 857 lssc = &qopts->data.hfsc_opts.linkshare; 858 ulsc = &qopts->data.hfsc_opts.upperlimit; 859 } 860 861 if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 || 862 (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || 863 opts->lssc_d != 0))) { 864 printf("hfsc("); 865 if (opts->flags & HFCF_RED) 866 printf(" red"); 867 if (opts->flags & HFCF_ECN) 868 printf(" ecn"); 869 if (opts->flags & HFCF_RIO) 870 printf(" rio"); 871 if (opts->flags & HFCF_CLEARDSCP) 872 printf(" cleardscp"); 873 if (opts->flags & HFCF_DEFAULTCLASS) 874 printf(" default"); 875 if (opts->rtsc_m2 != 0) 876 print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d, 877 opts->rtsc_m2, rtsc); 878 if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || 879 opts->lssc_d != 0)) 880 print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d, 881 opts->lssc_m2, lssc); 882 if (opts->ulsc_m2 != 0) 883 print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d, 884 opts->ulsc_m2, ulsc); 885 printf(" ) "); 886 887 return (1); 888 } else 889 return (0); 890} 891 892/* 893 * admission control using generalized service curve 894 */ 895#define INFINITY HUGE_VAL /* positive infinity defined in <math.h> */ 896 897/* add a new service curve to a generalized service curve */ 898static void 899gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc) 900{ 901 if (is_sc_null(sc)) 902 return; 903 if (sc->d != 0) 904 gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1); 905 gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2); 906} 907 908/* 909 * check whether all points of a generalized service curve have 910 * their y-coordinates no larger than a given two-piece linear 911 * service curve. 912 */ 913static int 914is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc) 915{ 916 struct segment *s, *last, *end; 917 double y; 918 919 if (is_sc_null(sc)) { 920 if (LIST_EMPTY(gsc)) 921 return (1); 922 LIST_FOREACH(s, gsc, _next) { 923 if (s->m != 0) 924 return (0); 925 } 926 return (1); 927 } 928 /* 929 * gsc has a dummy entry at the end with x = INFINITY. 930 * loop through up to this dummy entry. 931 */ 932 end = gsc_getentry(gsc, INFINITY); 933 if (end == NULL) 934 return (1); 935 last = NULL; 936 for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) { 937 if (s->y > sc_x2y(sc, s->x)) 938 return (0); 939 last = s; 940 } 941 /* last now holds the real last segment */ 942 if (last == NULL) 943 return (1); 944 if (last->m > sc->m2) 945 return (0); 946 if (last->x < sc->d && last->m > sc->m1) { 947 y = last->y + (sc->d - last->x) * last->m; 948 if (y > sc_x2y(sc, sc->d)) 949 return (0); 950 } 951 return (1); 952} 953 954static void 955gsc_destroy(struct gen_sc *gsc) 956{ 957 struct segment *s; 958 959 while ((s = LIST_FIRST(gsc)) != NULL) { 960 LIST_REMOVE(s, _next); 961 free(s); 962 } 963} 964 965/* 966 * return a segment entry starting at x. 967 * if gsc has no entry starting at x, a new entry is created at x. 968 */ 969static struct segment * 970gsc_getentry(struct gen_sc *gsc, double x) 971{ 972 struct segment *new, *prev, *s; 973 974 prev = NULL; 975 LIST_FOREACH(s, gsc, _next) { 976 if (s->x == x) 977 return (s); /* matching entry found */ 978 else if (s->x < x) 979 prev = s; 980 else 981 break; 982 } 983 984 /* we have to create a new entry */ 985 if ((new = calloc(1, sizeof(struct segment))) == NULL) 986 return (NULL); 987 988 new->x = x; 989 if (x == INFINITY || s == NULL) 990 new->d = 0; 991 else if (s->x == INFINITY) 992 new->d = INFINITY; 993 else 994 new->d = s->x - x; 995 if (prev == NULL) { 996 /* insert the new entry at the head of the list */ 997 new->y = 0; 998 new->m = 0; 999 LIST_INSERT_HEAD(gsc, new, _next); 1000 } else { 1001 /* 1002 * the start point intersects with the segment pointed by 1003 * prev. divide prev into 2 segments 1004 */ 1005 if (x == INFINITY) { 1006 prev->d = INFINITY; 1007 if (prev->m == 0) 1008 new->y = prev->y; 1009 else 1010 new->y = INFINITY; 1011 } else { 1012 prev->d = x - prev->x; 1013 new->y = prev->d * prev->m + prev->y; 1014 } 1015 new->m = prev->m; 1016 LIST_INSERT_AFTER(prev, new, _next); 1017 } 1018 return (new); 1019} 1020 1021/* add a segment to a generalized service curve */ 1022static int 1023gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m) 1024{ 1025 struct segment *start, *end, *s; 1026 double x2; 1027 1028 if (d == INFINITY) 1029 x2 = INFINITY; 1030 else 1031 x2 = x + d; 1032 start = gsc_getentry(gsc, x); 1033 end = gsc_getentry(gsc, x2); 1034 if (start == NULL || end == NULL) 1035 return (-1); 1036 1037 for (s = start; s != end; s = LIST_NEXT(s, _next)) { 1038 s->m += m; 1039 s->y += y + (s->x - x) * m; 1040 } 1041 1042 end = gsc_getentry(gsc, INFINITY); 1043 for (; s != end; s = LIST_NEXT(s, _next)) { 1044 s->y += m * d; 1045 } 1046 1047 return (0); 1048} 1049 1050/* get y-projection of a service curve */ 1051static double 1052sc_x2y(struct service_curve *sc, double x) 1053{ 1054 double y; 1055 1056 if (x <= (double)sc->d) 1057 /* y belongs to the 1st segment */ 1058 y = x * (double)sc->m1; 1059 else 1060 /* y belongs to the 2nd segment */ 1061 y = (double)sc->d * (double)sc->m1 1062 + (x - (double)sc->d) * (double)sc->m2; 1063 return (y); 1064} 1065 1066/* 1067 * misc utilities 1068 */ 1069#define R2S_BUFS 8 1070#define RATESTR_MAX 16 1071 1072char * 1073rate2str(double rate) 1074{ 1075 char *buf; 1076 static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */ 1077 static int idx = 0; 1078 int i; 1079 static const char unit[] = " KMG"; 1080 1081 buf = r2sbuf[idx++]; 1082 if (idx == R2S_BUFS) 1083 idx = 0; 1084 1085 for (i = 0; rate >= 1000 && i <= 3; i++) 1086 rate /= 1000; 1087 1088 if ((int)(rate * 100) % 100) 1089 snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]); 1090 else 1091 snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]); 1092 1093 return (buf); 1094} 1095 1096u_int32_t 1097getifspeed(char *ifname) 1098{ 1099 int s; 1100 struct ifreq ifr; 1101 struct if_data ifrdat; 1102 1103 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1104 err(1, "socket"); 1105 bzero(&ifr, sizeof(ifr)); 1106 if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 1107 sizeof(ifr.ifr_name)) 1108 errx(1, "getifspeed: strlcpy"); 1109 ifr.ifr_data = (caddr_t)&ifrdat; 1110 if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) 1111 err(1, "SIOCGIFDATA"); 1112 if (shutdown(s, SHUT_RDWR) == -1) 1113 err(1, "shutdown"); 1114 if (close(s)) 1115 err(1, "close"); 1116 return ((u_int32_t)ifrdat.ifi_baudrate); 1117} 1118 1119u_long 1120getifmtu(char *ifname) 1121{ 1122 int s; 1123 struct ifreq ifr; 1124 1125 if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1126 err(1, "socket"); 1127 bzero(&ifr, sizeof(ifr)); 1128 if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 1129 sizeof(ifr.ifr_name)) 1130 errx(1, "getifmtu: strlcpy"); 1131 if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1) 1132 err(1, "SIOCGIFMTU"); 1133 if (shutdown(s, SHUT_RDWR) == -1) 1134 err(1, "shutdown"); 1135 if (close(s)) 1136 err(1, "close"); 1137 if (ifr.ifr_mtu > 0) 1138 return (ifr.ifr_mtu); 1139 else { 1140 warnx("could not get mtu for %s, assuming 1500", ifname); 1141 return (1500); 1142 } 1143} 1144 1145int 1146eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, 1147 u_int32_t ref_bw) 1148{ 1149 int errors = 0; 1150 1151 switch (pa->scheduler) { 1152 case ALTQT_CBQ: 1153 pa->pq_u.cbq_opts = opts->data.cbq_opts; 1154 break; 1155 case ALTQT_PRIQ: 1156 pa->pq_u.priq_opts = opts->data.priq_opts; 1157 break; 1158 case ALTQT_HFSC: 1159 pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags; 1160 if (opts->data.hfsc_opts.linkshare.used) { 1161 pa->pq_u.hfsc_opts.lssc_m1 = 1162 eval_bwspec(&opts->data.hfsc_opts.linkshare.m1, 1163 ref_bw); 1164 pa->pq_u.hfsc_opts.lssc_m2 = 1165 eval_bwspec(&opts->data.hfsc_opts.linkshare.m2, 1166 ref_bw); 1167 pa->pq_u.hfsc_opts.lssc_d = 1168 opts->data.hfsc_opts.linkshare.d; 1169 } 1170 if (opts->data.hfsc_opts.realtime.used) { 1171 pa->pq_u.hfsc_opts.rtsc_m1 = 1172 eval_bwspec(&opts->data.hfsc_opts.realtime.m1, 1173 ref_bw); 1174 pa->pq_u.hfsc_opts.rtsc_m2 = 1175 eval_bwspec(&opts->data.hfsc_opts.realtime.m2, 1176 ref_bw); 1177 pa->pq_u.hfsc_opts.rtsc_d = 1178 opts->data.hfsc_opts.realtime.d; 1179 } 1180 if (opts->data.hfsc_opts.upperlimit.used) { 1181 pa->pq_u.hfsc_opts.ulsc_m1 = 1182 eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1, 1183 ref_bw); 1184 pa->pq_u.hfsc_opts.ulsc_m2 = 1185 eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2, 1186 ref_bw); 1187 pa->pq_u.hfsc_opts.ulsc_d = 1188 opts->data.hfsc_opts.upperlimit.d; 1189 } 1190 break; 1191 default: 1192 warnx("eval_queue_opts: unknown scheduler type %u", 1193 opts->qtype); 1194 errors++; 1195 break; 1196 } 1197 1198 return (errors); 1199} 1200 1201u_int32_t 1202eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw) 1203{ 1204 if (bw->bw_absolute > 0) 1205 return (bw->bw_absolute); 1206 1207 if (bw->bw_percent > 0) 1208 return (ref_bw / 100 * bw->bw_percent); 1209 1210 return (0); 1211} 1212 1213void 1214print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, 1215 const struct node_hfsc_sc *sc) 1216{ 1217 printf(" %s", scname); 1218 1219 if (d != 0) { 1220 printf("("); 1221 if (sc != NULL && sc->m1.bw_percent > 0) 1222 printf("%u%%", sc->m1.bw_percent); 1223 else 1224 printf("%s", rate2str((double)m1)); 1225 printf(" %u", d); 1226 } 1227 1228 if (sc != NULL && sc->m2.bw_percent > 0) 1229 printf(" %u%%", sc->m2.bw_percent); 1230 else 1231 printf(" %s", rate2str((double)m2)); 1232 1233 if (d != 0) 1234 printf(")"); 1235} 1236