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