1 /* $OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt 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/cdefs.h> 22 __FBSDID("$FreeBSD$"); 23 24 #include <sys/types.h> 25 #include <sys/ioctl.h> 26 #include <sys/socket.h> 27 28 #include <net/if.h> 29 #include <netinet/in.h> 30 #include <net/pfvar.h> 31 32 #include <err.h> 33 #include <errno.h> 34 #include <limits.h> 35 #include <math.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <unistd.h> 40 41 #include <net/altq/altq.h> 42 #include <net/altq/altq_cbq.h> 43 #include <net/altq/altq_codel.h> 44 #include <net/altq/altq_priq.h> 45 #include <net/altq/altq_hfsc.h> 46 #include <net/altq/altq_fairq.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 53 TAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs); 54 LIST_HEAD(gen_sc, segment) rtsc, lssc; 55 56 struct pf_altq *qname_to_pfaltq(const char *, const char *); 57 u_int32_t qname_to_qid(const char *); 58 59 static int eval_pfqueue_cbq(struct pfctl *, struct pf_altq *); 60 static int cbq_compute_idletime(struct pfctl *, struct pf_altq *); 61 static int check_commit_cbq(int, int, struct pf_altq *); 62 static int print_cbq_opts(const struct pf_altq *); 63 64 static int print_codel_opts(const struct pf_altq *, 65 const struct node_queue_opt *); 66 67 static int eval_pfqueue_priq(struct pfctl *, struct pf_altq *); 68 static int check_commit_priq(int, int, struct pf_altq *); 69 static int print_priq_opts(const struct pf_altq *); 70 71 static int eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *); 72 static int check_commit_hfsc(int, int, struct pf_altq *); 73 static int print_hfsc_opts(const struct pf_altq *, 74 const struct node_queue_opt *); 75 76 static int eval_pfqueue_fairq(struct pfctl *, struct pf_altq *); 77 static int print_fairq_opts(const struct pf_altq *, 78 const struct node_queue_opt *); 79 static int check_commit_fairq(int, int, struct pf_altq *); 80 81 static void gsc_add_sc(struct gen_sc *, struct service_curve *); 82 static int is_gsc_under_sc(struct gen_sc *, 83 struct service_curve *); 84 static void gsc_destroy(struct gen_sc *); 85 static struct segment *gsc_getentry(struct gen_sc *, double); 86 static int gsc_add_seg(struct gen_sc *, double, double, double, 87 double); 88 static double sc_x2y(struct service_curve *, double); 89 90 #ifdef __FreeBSD__ 91 u_int32_t getifspeed(int, char *); 92 #else 93 u_int32_t getifspeed(char *); 94 #endif 95 u_long getifmtu(char *); 96 int eval_queue_opts(struct pf_altq *, struct node_queue_opt *, 97 u_int32_t); 98 u_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); 99 void print_hfsc_sc(const char *, u_int, u_int, u_int, 100 const struct node_hfsc_sc *); 101 void print_fairq_sc(const char *, u_int, u_int, u_int, 102 const struct node_fairq_sc *); 103 104 void 105 pfaltq_store(struct pf_altq *a) 106 { 107 struct pf_altq *altq; 108 109 if ((altq = malloc(sizeof(*altq))) == NULL) 110 err(1, "malloc"); 111 memcpy(altq, a, sizeof(struct pf_altq)); 112 TAILQ_INSERT_TAIL(&altqs, altq, entries); 113 } 114 115 struct pf_altq * 116 pfaltq_lookup(const char *ifname) 117 { 118 struct pf_altq *altq; 119 120 TAILQ_FOREACH(altq, &altqs, entries) { 121 if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && 122 altq->qname[0] == 0) 123 return (altq); 124 } 125 return (NULL); 126 } 127 128 struct pf_altq * 129 qname_to_pfaltq(const char *qname, const char *ifname) 130 { 131 struct pf_altq *altq; 132 133 TAILQ_FOREACH(altq, &altqs, entries) { 134 if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && 135 strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) 136 return (altq); 137 } 138 return (NULL); 139 } 140 141 u_int32_t 142 qname_to_qid(const char *qname) 143 { 144 struct pf_altq *altq; 145 146 /* 147 * We guarantee that same named queues on different interfaces 148 * have the same qid, so we do NOT need to limit matching on 149 * one interface! 150 */ 151 152 TAILQ_FOREACH(altq, &altqs, entries) { 153 if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) 154 return (altq->qid); 155 } 156 return (0); 157 } 158 159 void 160 print_altq(const struct pf_altq *a, unsigned int level, 161 struct node_queue_bw *bw, struct node_queue_opt *qopts) 162 { 163 if (a->qname[0] != 0) { 164 print_queue(a, level, bw, 1, qopts); 165 return; 166 } 167 168 #ifdef __FreeBSD__ 169 if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) 170 printf("INACTIVE "); 171 #endif 172 173 printf("altq on %s ", a->ifname); 174 175 switch (a->scheduler) { 176 case ALTQT_CBQ: 177 if (!print_cbq_opts(a)) 178 printf("cbq "); 179 break; 180 case ALTQT_PRIQ: 181 if (!print_priq_opts(a)) 182 printf("priq "); 183 break; 184 case ALTQT_HFSC: 185 if (!print_hfsc_opts(a, qopts)) 186 printf("hfsc "); 187 break; 188 case ALTQT_FAIRQ: 189 if (!print_fairq_opts(a, qopts)) 190 printf("fairq "); 191 break; 192 case ALTQT_CODEL: 193 if (!print_codel_opts(a, qopts)) 194 printf("codel "); 195 break; 196 } 197 198 if (bw != NULL && bw->bw_percent > 0) { 199 if (bw->bw_percent < 100) 200 printf("bandwidth %u%% ", bw->bw_percent); 201 } else 202 printf("bandwidth %s ", rate2str((double)a->ifbandwidth)); 203 204 if (a->qlimit != DEFAULT_QLIMIT) 205 printf("qlimit %u ", a->qlimit); 206 printf("tbrsize %u ", a->tbrsize); 207 } 208 209 void 210 print_queue(const struct pf_altq *a, unsigned int level, 211 struct node_queue_bw *bw, int print_interface, 212 struct node_queue_opt *qopts) 213 { 214 unsigned int i; 215 216 #ifdef __FreeBSD__ 217 if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) 218 printf("INACTIVE "); 219 #endif 220 printf("queue "); 221 for (i = 0; i < level; ++i) 222 printf(" "); 223 printf("%s ", a->qname); 224 if (print_interface) 225 printf("on %s ", a->ifname); 226 if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC || 227 a->scheduler == ALTQT_FAIRQ) { 228 if (bw != NULL && bw->bw_percent > 0) { 229 if (bw->bw_percent < 100) 230 printf("bandwidth %u%% ", bw->bw_percent); 231 } else 232 printf("bandwidth %s ", rate2str((double)a->bandwidth)); 233 } 234 if (a->priority != DEFAULT_PRIORITY) 235 printf("priority %u ", a->priority); 236 if (a->qlimit != DEFAULT_QLIMIT) 237 printf("qlimit %u ", a->qlimit); 238 switch (a->scheduler) { 239 case ALTQT_CBQ: 240 print_cbq_opts(a); 241 break; 242 case ALTQT_PRIQ: 243 print_priq_opts(a); 244 break; 245 case ALTQT_HFSC: 246 print_hfsc_opts(a, qopts); 247 break; 248 case ALTQT_FAIRQ: 249 print_fairq_opts(a, qopts); 250 break; 251 } 252 } 253 254 /* 255 * eval_pfaltq computes the discipline parameters. 256 */ 257 int 258 eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, 259 struct node_queue_opt *opts) 260 { 261 u_int rate, size, errors = 0; 262 263 if (bw->bw_absolute > 0) 264 pa->ifbandwidth = bw->bw_absolute; 265 else 266 #ifdef __FreeBSD__ 267 if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) { 268 #else 269 if ((rate = getifspeed(pa->ifname)) == 0) { 270 #endif 271 fprintf(stderr, "interface %s does not know its bandwidth, " 272 "please specify an absolute bandwidth\n", 273 pa->ifname); 274 errors++; 275 } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) 276 pa->ifbandwidth = rate; 277 278 errors += eval_queue_opts(pa, opts, pa->ifbandwidth); 279 280 /* if tbrsize is not specified, use heuristics */ 281 if (pa->tbrsize == 0) { 282 rate = pa->ifbandwidth; 283 if (rate <= 1 * 1000 * 1000) 284 size = 1; 285 else if (rate <= 10 * 1000 * 1000) 286 size = 4; 287 else if (rate <= 200 * 1000 * 1000) 288 size = 8; 289 else 290 size = 24; 291 size = size * getifmtu(pa->ifname); 292 if (size > 0xffff) 293 size = 0xffff; 294 pa->tbrsize = size; 295 } 296 return (errors); 297 } 298 299 /* 300 * check_commit_altq does consistency check for each interface 301 */ 302 int 303 check_commit_altq(int dev, int opts) 304 { 305 struct pf_altq *altq; 306 int error = 0; 307 308 /* call the discipline check for each interface. */ 309 TAILQ_FOREACH(altq, &altqs, entries) { 310 if (altq->qname[0] == 0) { 311 switch (altq->scheduler) { 312 case ALTQT_CBQ: 313 error = check_commit_cbq(dev, opts, altq); 314 break; 315 case ALTQT_PRIQ: 316 error = check_commit_priq(dev, opts, altq); 317 break; 318 case ALTQT_HFSC: 319 error = check_commit_hfsc(dev, opts, altq); 320 break; 321 case ALTQT_FAIRQ: 322 error = check_commit_fairq(dev, opts, altq); 323 break; 324 default: 325 break; 326 } 327 } 328 } 329 return (error); 330 } 331 332 /* 333 * eval_pfqueue computes the queue parameters. 334 */ 335 int 336 eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, 337 struct node_queue_opt *opts) 338 { 339 /* should be merged with expand_queue */ 340 struct pf_altq *if_pa, *parent, *altq; 341 u_int32_t bwsum; 342 int error = 0; 343 344 /* find the corresponding interface and copy fields used by queues */ 345 if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) { 346 fprintf(stderr, "altq not defined on %s\n", pa->ifname); 347 return (1); 348 } 349 pa->scheduler = if_pa->scheduler; 350 pa->ifbandwidth = if_pa->ifbandwidth; 351 352 if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) { 353 fprintf(stderr, "queue %s already exists on interface %s\n", 354 pa->qname, pa->ifname); 355 return (1); 356 } 357 pa->qid = qname_to_qid(pa->qname); 358 359 parent = NULL; 360 if (pa->parent[0] != 0) { 361 parent = qname_to_pfaltq(pa->parent, pa->ifname); 362 if (parent == NULL) { 363 fprintf(stderr, "parent %s not found for %s\n", 364 pa->parent, pa->qname); 365 return (1); 366 } 367 pa->parent_qid = parent->qid; 368 } 369 if (pa->qlimit == 0) 370 pa->qlimit = DEFAULT_QLIMIT; 371 372 if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC || 373 pa->scheduler == ALTQT_FAIRQ) { 374 pa->bandwidth = eval_bwspec(bw, 375 parent == NULL ? 0 : parent->bandwidth); 376 377 if (pa->bandwidth > pa->ifbandwidth) { 378 fprintf(stderr, "bandwidth for %s higher than " 379 "interface\n", pa->qname); 380 return (1); 381 } 382 /* check the sum of the child bandwidth is under parent's */ 383 if (parent != NULL) { 384 if (pa->bandwidth > parent->bandwidth) { 385 warnx("bandwidth for %s higher than parent", 386 pa->qname); 387 return (1); 388 } 389 bwsum = 0; 390 TAILQ_FOREACH(altq, &altqs, entries) { 391 if (strncmp(altq->ifname, pa->ifname, 392 IFNAMSIZ) == 0 && 393 altq->qname[0] != 0 && 394 strncmp(altq->parent, pa->parent, 395 PF_QNAME_SIZE) == 0) 396 bwsum += altq->bandwidth; 397 } 398 bwsum += pa->bandwidth; 399 if (bwsum > parent->bandwidth) { 400 warnx("the sum of the child bandwidth higher" 401 " than parent \"%s\"", parent->qname); 402 } 403 } 404 } 405 406 if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth)) 407 return (1); 408 409 switch (pa->scheduler) { 410 case ALTQT_CBQ: 411 error = eval_pfqueue_cbq(pf, pa); 412 break; 413 case ALTQT_PRIQ: 414 error = eval_pfqueue_priq(pf, pa); 415 break; 416 case ALTQT_HFSC: 417 error = eval_pfqueue_hfsc(pf, pa); 418 break; 419 case ALTQT_FAIRQ: 420 error = eval_pfqueue_fairq(pf, pa); 421 break; 422 default: 423 break; 424 } 425 return (error); 426 } 427 428 /* 429 * CBQ support functions 430 */ 431 #define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */ 432 #define RM_NS_PER_SEC (1000000000) 433 434 static int 435 eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa) 436 { 437 struct cbq_opts *opts; 438 u_int ifmtu; 439 440 if (pa->priority >= CBQ_MAXPRI) { 441 warnx("priority out of range: max %d", CBQ_MAXPRI - 1); 442 return (-1); 443 } 444 445 ifmtu = getifmtu(pa->ifname); 446 opts = &pa->pq_u.cbq_opts; 447 448 if (opts->pktsize == 0) { /* use default */ 449 opts->pktsize = ifmtu; 450 if (opts->pktsize > MCLBYTES) /* do what TCP does */ 451 opts->pktsize &= ~MCLBYTES; 452 } else if (opts->pktsize > ifmtu) 453 opts->pktsize = ifmtu; 454 if (opts->maxpktsize == 0) /* use default */ 455 opts->maxpktsize = ifmtu; 456 else if (opts->maxpktsize > ifmtu) 457 opts->pktsize = ifmtu; 458 459 if (opts->pktsize > opts->maxpktsize) 460 opts->pktsize = opts->maxpktsize; 461 462 if (pa->parent[0] == 0) 463 opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR); 464 465 cbq_compute_idletime(pf, pa); 466 return (0); 467 } 468 469 /* 470 * compute ns_per_byte, maxidle, minidle, and offtime 471 */ 472 static int 473 cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa) 474 { 475 struct cbq_opts *opts; 476 double maxidle_s, maxidle, minidle; 477 double offtime, nsPerByte, ifnsPerByte, ptime, cptime; 478 double z, g, f, gton, gtom; 479 u_int minburst, maxburst; 480 481 opts = &pa->pq_u.cbq_opts; 482 ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8; 483 minburst = opts->minburst; 484 maxburst = opts->maxburst; 485 486 if (pa->bandwidth == 0) 487 f = 0.0001; /* small enough? */ 488 else 489 f = ((double) pa->bandwidth / (double) pa->ifbandwidth); 490 491 nsPerByte = ifnsPerByte / f; 492 ptime = (double)opts->pktsize * ifnsPerByte; 493 cptime = ptime * (1.0 - f) / f; 494 495 if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) { 496 /* 497 * this causes integer overflow in kernel! 498 * (bandwidth < 6Kbps when max_pkt_size=1500) 499 */ 500 if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0) 501 warnx("queue bandwidth must be larger than %s", 502 rate2str(ifnsPerByte * (double)opts->maxpktsize / 503 (double)INT_MAX * (double)pa->ifbandwidth)); 504 fprintf(stderr, "cbq: queue %s is too slow!\n", 505 pa->qname); 506 nsPerByte = (double)(INT_MAX / opts->maxpktsize); 507 } 508 509 if (maxburst == 0) { /* use default */ 510 if (cptime > 10.0 * 1000000) 511 maxburst = 4; 512 else 513 maxburst = 16; 514 } 515 if (minburst == 0) /* use default */ 516 minburst = 2; 517 if (minburst > maxburst) 518 minburst = maxburst; 519 520 z = (double)(1 << RM_FILTER_GAIN); 521 g = (1.0 - 1.0 / z); 522 gton = pow(g, (double)maxburst); 523 gtom = pow(g, (double)(minburst-1)); 524 maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); 525 maxidle_s = (1.0 - g); 526 if (maxidle > maxidle_s) 527 maxidle = ptime * maxidle; 528 else 529 maxidle = ptime * maxidle_s; 530 offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); 531 minidle = -((double)opts->maxpktsize * (double)nsPerByte); 532 533 /* scale parameters */ 534 maxidle = ((maxidle * 8.0) / nsPerByte) * 535 pow(2.0, (double)RM_FILTER_GAIN); 536 offtime = (offtime * 8.0) / nsPerByte * 537 pow(2.0, (double)RM_FILTER_GAIN); 538 minidle = ((minidle * 8.0) / nsPerByte) * 539 pow(2.0, (double)RM_FILTER_GAIN); 540 541 maxidle = maxidle / 1000.0; 542 offtime = offtime / 1000.0; 543 minidle = minidle / 1000.0; 544 545 opts->minburst = minburst; 546 opts->maxburst = maxburst; 547 opts->ns_per_byte = (u_int)nsPerByte; 548 opts->maxidle = (u_int)fabs(maxidle); 549 opts->minidle = (int)minidle; 550 opts->offtime = (u_int)fabs(offtime); 551 552 return (0); 553 } 554 555 static int 556 check_commit_cbq(int dev, int opts, struct pf_altq *pa) 557 { 558 struct pf_altq *altq; 559 int root_class, default_class; 560 int error = 0; 561 562 /* 563 * check if cbq has one root queue and one default queue 564 * for this interface 565 */ 566 root_class = default_class = 0; 567 TAILQ_FOREACH(altq, &altqs, entries) { 568 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 569 continue; 570 if (altq->qname[0] == 0) /* this is for interface */ 571 continue; 572 if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS) 573 root_class++; 574 if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS) 575 default_class++; 576 } 577 if (root_class != 1) { 578 warnx("should have one root queue on %s", pa->ifname); 579 error++; 580 } 581 if (default_class != 1) { 582 warnx("should have one default queue on %s", pa->ifname); 583 error++; 584 } 585 return (error); 586 } 587 588 static int 589 print_cbq_opts(const struct pf_altq *a) 590 { 591 const struct cbq_opts *opts; 592 593 opts = &a->pq_u.cbq_opts; 594 if (opts->flags) { 595 printf("cbq("); 596 if (opts->flags & CBQCLF_RED) 597 printf(" red"); 598 if (opts->flags & CBQCLF_ECN) 599 printf(" ecn"); 600 if (opts->flags & CBQCLF_RIO) 601 printf(" rio"); 602 if (opts->flags & CBQCLF_CODEL) 603 printf(" codel"); 604 if (opts->flags & CBQCLF_CLEARDSCP) 605 printf(" cleardscp"); 606 if (opts->flags & CBQCLF_FLOWVALVE) 607 printf(" flowvalve"); 608 if (opts->flags & CBQCLF_BORROW) 609 printf(" borrow"); 610 if (opts->flags & CBQCLF_WRR) 611 printf(" wrr"); 612 if (opts->flags & CBQCLF_EFFICIENT) 613 printf(" efficient"); 614 if (opts->flags & CBQCLF_ROOTCLASS) 615 printf(" root"); 616 if (opts->flags & CBQCLF_DEFCLASS) 617 printf(" default"); 618 printf(" ) "); 619 620 return (1); 621 } else 622 return (0); 623 } 624 625 /* 626 * PRIQ support functions 627 */ 628 static int 629 eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa) 630 { 631 struct pf_altq *altq; 632 633 if (pa->priority >= PRIQ_MAXPRI) { 634 warnx("priority out of range: max %d", PRIQ_MAXPRI - 1); 635 return (-1); 636 } 637 /* the priority should be unique for the interface */ 638 TAILQ_FOREACH(altq, &altqs, entries) { 639 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 && 640 altq->qname[0] != 0 && altq->priority == pa->priority) { 641 warnx("%s and %s have the same priority", 642 altq->qname, pa->qname); 643 return (-1); 644 } 645 } 646 647 return (0); 648 } 649 650 static int 651 check_commit_priq(int dev, int opts, struct pf_altq *pa) 652 { 653 struct pf_altq *altq; 654 int default_class; 655 int error = 0; 656 657 /* 658 * check if priq has one default class for this interface 659 */ 660 default_class = 0; 661 TAILQ_FOREACH(altq, &altqs, entries) { 662 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 663 continue; 664 if (altq->qname[0] == 0) /* this is for interface */ 665 continue; 666 if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS) 667 default_class++; 668 } 669 if (default_class != 1) { 670 warnx("should have one default queue on %s", pa->ifname); 671 error++; 672 } 673 return (error); 674 } 675 676 static int 677 print_priq_opts(const struct pf_altq *a) 678 { 679 const struct priq_opts *opts; 680 681 opts = &a->pq_u.priq_opts; 682 683 if (opts->flags) { 684 printf("priq("); 685 if (opts->flags & PRCF_RED) 686 printf(" red"); 687 if (opts->flags & PRCF_ECN) 688 printf(" ecn"); 689 if (opts->flags & PRCF_RIO) 690 printf(" rio"); 691 if (opts->flags & PRCF_CODEL) 692 printf(" codel"); 693 if (opts->flags & PRCF_CLEARDSCP) 694 printf(" cleardscp"); 695 if (opts->flags & PRCF_DEFAULTCLASS) 696 printf(" default"); 697 printf(" ) "); 698 699 return (1); 700 } else 701 return (0); 702 } 703 704 /* 705 * HFSC support functions 706 */ 707 static int 708 eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa) 709 { 710 struct pf_altq *altq, *parent; 711 struct hfsc_opts *opts; 712 struct service_curve sc; 713 714 opts = &pa->pq_u.hfsc_opts; 715 716 if (pa->parent[0] == 0) { 717 /* root queue */ 718 opts->lssc_m1 = pa->ifbandwidth; 719 opts->lssc_m2 = pa->ifbandwidth; 720 opts->lssc_d = 0; 721 return (0); 722 } 723 724 LIST_INIT(&rtsc); 725 LIST_INIT(&lssc); 726 727 /* if link_share is not specified, use bandwidth */ 728 if (opts->lssc_m2 == 0) 729 opts->lssc_m2 = pa->bandwidth; 730 731 if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) || 732 (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) || 733 (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) { 734 warnx("m2 is zero for %s", pa->qname); 735 return (-1); 736 } 737 738 if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || 739 (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) || 740 (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) { 741 warnx("m1 must be zero for convex curve: %s", pa->qname); 742 return (-1); 743 } 744 745 /* 746 * admission control: 747 * for the real-time service curve, the sum of the service curves 748 * should not exceed 80% of the interface bandwidth. 20% is reserved 749 * not to over-commit the actual interface bandwidth. 750 * for the linkshare service curve, the sum of the child service 751 * curve should not exceed the parent service curve. 752 * for the upper-limit service curve, the assigned bandwidth should 753 * be smaller than the interface bandwidth, and the upper-limit should 754 * be larger than the real-time service curve when both are defined. 755 */ 756 parent = qname_to_pfaltq(pa->parent, pa->ifname); 757 if (parent == NULL) 758 errx(1, "parent %s not found for %s", pa->parent, pa->qname); 759 760 TAILQ_FOREACH(altq, &altqs, entries) { 761 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 762 continue; 763 if (altq->qname[0] == 0) /* this is for interface */ 764 continue; 765 766 /* if the class has a real-time service curve, add it. */ 767 if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) { 768 sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1; 769 sc.d = altq->pq_u.hfsc_opts.rtsc_d; 770 sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2; 771 gsc_add_sc(&rtsc, &sc); 772 } 773 774 if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) 775 continue; 776 777 /* if the class has a linkshare service curve, add it. */ 778 if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) { 779 sc.m1 = altq->pq_u.hfsc_opts.lssc_m1; 780 sc.d = altq->pq_u.hfsc_opts.lssc_d; 781 sc.m2 = altq->pq_u.hfsc_opts.lssc_m2; 782 gsc_add_sc(&lssc, &sc); 783 } 784 } 785 786 /* check the real-time service curve. reserve 20% of interface bw */ 787 if (opts->rtsc_m2 != 0) { 788 /* add this queue to the sum */ 789 sc.m1 = opts->rtsc_m1; 790 sc.d = opts->rtsc_d; 791 sc.m2 = opts->rtsc_m2; 792 gsc_add_sc(&rtsc, &sc); 793 /* compare the sum with 80% of the interface */ 794 sc.m1 = 0; 795 sc.d = 0; 796 sc.m2 = pa->ifbandwidth / 100 * 80; 797 if (!is_gsc_under_sc(&rtsc, &sc)) { 798 warnx("real-time sc exceeds 80%% of the interface " 799 "bandwidth (%s)", rate2str((double)sc.m2)); 800 goto err_ret; 801 } 802 } 803 804 /* check the linkshare service curve. */ 805 if (opts->lssc_m2 != 0) { 806 /* add this queue to the child sum */ 807 sc.m1 = opts->lssc_m1; 808 sc.d = opts->lssc_d; 809 sc.m2 = opts->lssc_m2; 810 gsc_add_sc(&lssc, &sc); 811 /* compare the sum of the children with parent's sc */ 812 sc.m1 = parent->pq_u.hfsc_opts.lssc_m1; 813 sc.d = parent->pq_u.hfsc_opts.lssc_d; 814 sc.m2 = parent->pq_u.hfsc_opts.lssc_m2; 815 if (!is_gsc_under_sc(&lssc, &sc)) { 816 warnx("linkshare sc exceeds parent's sc"); 817 goto err_ret; 818 } 819 } 820 821 /* check the upper-limit service curve. */ 822 if (opts->ulsc_m2 != 0) { 823 if (opts->ulsc_m1 > pa->ifbandwidth || 824 opts->ulsc_m2 > pa->ifbandwidth) { 825 warnx("upper-limit larger than interface bandwidth"); 826 goto err_ret; 827 } 828 if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) { 829 warnx("upper-limit sc smaller than real-time sc"); 830 goto err_ret; 831 } 832 } 833 834 gsc_destroy(&rtsc); 835 gsc_destroy(&lssc); 836 837 return (0); 838 839 err_ret: 840 gsc_destroy(&rtsc); 841 gsc_destroy(&lssc); 842 return (-1); 843 } 844 845 /* 846 * FAIRQ support functions 847 */ 848 static int 849 eval_pfqueue_fairq(struct pfctl *pf __unused, struct pf_altq *pa) 850 { 851 struct pf_altq *altq, *parent; 852 struct fairq_opts *opts; 853 struct service_curve sc; 854 855 opts = &pa->pq_u.fairq_opts; 856 857 if (pa->parent[0] == 0) { 858 /* root queue */ 859 opts->lssc_m1 = pa->ifbandwidth; 860 opts->lssc_m2 = pa->ifbandwidth; 861 opts->lssc_d = 0; 862 return (0); 863 } 864 865 LIST_INIT(&lssc); 866 867 /* if link_share is not specified, use bandwidth */ 868 if (opts->lssc_m2 == 0) 869 opts->lssc_m2 = pa->bandwidth; 870 871 /* 872 * admission control: 873 * for the real-time service curve, the sum of the service curves 874 * should not exceed 80% of the interface bandwidth. 20% is reserved 875 * not to over-commit the actual interface bandwidth. 876 * for the link-sharing service curve, the sum of the child service 877 * curve should not exceed the parent service curve. 878 * for the upper-limit service curve, the assigned bandwidth should 879 * be smaller than the interface bandwidth, and the upper-limit should 880 * be larger than the real-time service curve when both are defined. 881 */ 882 parent = qname_to_pfaltq(pa->parent, pa->ifname); 883 if (parent == NULL) 884 errx(1, "parent %s not found for %s", pa->parent, pa->qname); 885 886 TAILQ_FOREACH(altq, &altqs, entries) { 887 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 888 continue; 889 if (altq->qname[0] == 0) /* this is for interface */ 890 continue; 891 892 if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) 893 continue; 894 895 /* if the class has a link-sharing service curve, add it. */ 896 if (opts->lssc_m2 != 0 && altq->pq_u.fairq_opts.lssc_m2 != 0) { 897 sc.m1 = altq->pq_u.fairq_opts.lssc_m1; 898 sc.d = altq->pq_u.fairq_opts.lssc_d; 899 sc.m2 = altq->pq_u.fairq_opts.lssc_m2; 900 gsc_add_sc(&lssc, &sc); 901 } 902 } 903 904 /* check the link-sharing service curve. */ 905 if (opts->lssc_m2 != 0) { 906 sc.m1 = parent->pq_u.fairq_opts.lssc_m1; 907 sc.d = parent->pq_u.fairq_opts.lssc_d; 908 sc.m2 = parent->pq_u.fairq_opts.lssc_m2; 909 if (!is_gsc_under_sc(&lssc, &sc)) { 910 warnx("link-sharing sc exceeds parent's sc"); 911 goto err_ret; 912 } 913 } 914 915 gsc_destroy(&lssc); 916 917 return (0); 918 919 err_ret: 920 gsc_destroy(&lssc); 921 return (-1); 922 } 923 924 static int 925 check_commit_hfsc(int dev, int opts, struct pf_altq *pa) 926 { 927 struct pf_altq *altq, *def = NULL; 928 int default_class; 929 int error = 0; 930 931 /* check if hfsc has one default queue for this interface */ 932 default_class = 0; 933 TAILQ_FOREACH(altq, &altqs, entries) { 934 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 935 continue; 936 if (altq->qname[0] == 0) /* this is for interface */ 937 continue; 938 if (altq->parent[0] == 0) /* dummy root */ 939 continue; 940 if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) { 941 default_class++; 942 def = altq; 943 } 944 } 945 if (default_class != 1) { 946 warnx("should have one default queue on %s", pa->ifname); 947 return (1); 948 } 949 /* make sure the default queue is a leaf */ 950 TAILQ_FOREACH(altq, &altqs, entries) { 951 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 952 continue; 953 if (altq->qname[0] == 0) /* this is for interface */ 954 continue; 955 if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { 956 warnx("default queue is not a leaf"); 957 error++; 958 } 959 } 960 return (error); 961 } 962 963 static int 964 check_commit_fairq(int dev __unused, int opts __unused, struct pf_altq *pa) 965 { 966 struct pf_altq *altq, *def = NULL; 967 int default_class; 968 int error = 0; 969 970 /* check if fairq has one default queue for this interface */ 971 default_class = 0; 972 TAILQ_FOREACH(altq, &altqs, entries) { 973 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 974 continue; 975 if (altq->qname[0] == 0) /* this is for interface */ 976 continue; 977 if (altq->pq_u.fairq_opts.flags & FARF_DEFAULTCLASS) { 978 default_class++; 979 def = altq; 980 } 981 } 982 if (default_class != 1) { 983 warnx("should have one default queue on %s", pa->ifname); 984 return (1); 985 } 986 /* make sure the default queue is a leaf */ 987 TAILQ_FOREACH(altq, &altqs, entries) { 988 if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 989 continue; 990 if (altq->qname[0] == 0) /* this is for interface */ 991 continue; 992 if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { 993 warnx("default queue is not a leaf"); 994 error++; 995 } 996 } 997 return (error); 998 } 999 1000 static int 1001 print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) 1002 { 1003 const struct hfsc_opts *opts; 1004 const struct node_hfsc_sc *rtsc, *lssc, *ulsc; 1005 1006 opts = &a->pq_u.hfsc_opts; 1007 if (qopts == NULL) 1008 rtsc = lssc = ulsc = NULL; 1009 else { 1010 rtsc = &qopts->data.hfsc_opts.realtime; 1011 lssc = &qopts->data.hfsc_opts.linkshare; 1012 ulsc = &qopts->data.hfsc_opts.upperlimit; 1013 } 1014 1015 if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 || 1016 (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || 1017 opts->lssc_d != 0))) { 1018 printf("hfsc("); 1019 if (opts->flags & HFCF_RED) 1020 printf(" red"); 1021 if (opts->flags & HFCF_ECN) 1022 printf(" ecn"); 1023 if (opts->flags & HFCF_RIO) 1024 printf(" rio"); 1025 if (opts->flags & HFCF_CODEL) 1026 printf(" codel"); 1027 if (opts->flags & HFCF_CLEARDSCP) 1028 printf(" cleardscp"); 1029 if (opts->flags & HFCF_DEFAULTCLASS) 1030 printf(" default"); 1031 if (opts->rtsc_m2 != 0) 1032 print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d, 1033 opts->rtsc_m2, rtsc); 1034 if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || 1035 opts->lssc_d != 0)) 1036 print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d, 1037 opts->lssc_m2, lssc); 1038 if (opts->ulsc_m2 != 0) 1039 print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d, 1040 opts->ulsc_m2, ulsc); 1041 printf(" ) "); 1042 1043 return (1); 1044 } else 1045 return (0); 1046 } 1047 1048 static int 1049 print_codel_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) 1050 { 1051 const struct codel_opts *opts; 1052 1053 opts = &a->pq_u.codel_opts; 1054 if (opts->target || opts->interval || opts->ecn) { 1055 printf("codel("); 1056 if (opts->target) 1057 printf(" target %d", opts->target); 1058 if (opts->interval) 1059 printf(" interval %d", opts->interval); 1060 if (opts->ecn) 1061 printf("ecn"); 1062 printf(" ) "); 1063 1064 return (1); 1065 } 1066 1067 return (0); 1068 } 1069 1070 static int 1071 print_fairq_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) 1072 { 1073 const struct fairq_opts *opts; 1074 const struct node_fairq_sc *loc_lssc; 1075 1076 opts = &a->pq_u.fairq_opts; 1077 if (qopts == NULL) 1078 loc_lssc = NULL; 1079 else 1080 loc_lssc = &qopts->data.fairq_opts.linkshare; 1081 1082 if (opts->flags || 1083 (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || 1084 opts->lssc_d != 0))) { 1085 printf("fairq("); 1086 if (opts->flags & FARF_RED) 1087 printf(" red"); 1088 if (opts->flags & FARF_ECN) 1089 printf(" ecn"); 1090 if (opts->flags & FARF_RIO) 1091 printf(" rio"); 1092 if (opts->flags & FARF_CODEL) 1093 printf(" codel"); 1094 if (opts->flags & FARF_CLEARDSCP) 1095 printf(" cleardscp"); 1096 if (opts->flags & FARF_DEFAULTCLASS) 1097 printf(" default"); 1098 if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || 1099 opts->lssc_d != 0)) 1100 print_fairq_sc("linkshare", opts->lssc_m1, opts->lssc_d, 1101 opts->lssc_m2, loc_lssc); 1102 printf(" ) "); 1103 1104 return (1); 1105 } else 1106 return (0); 1107 } 1108 1109 /* 1110 * admission control using generalized service curve 1111 */ 1112 1113 /* add a new service curve to a generalized service curve */ 1114 static void 1115 gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc) 1116 { 1117 if (is_sc_null(sc)) 1118 return; 1119 if (sc->d != 0) 1120 gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1); 1121 gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2); 1122 } 1123 1124 /* 1125 * check whether all points of a generalized service curve have 1126 * their y-coordinates no larger than a given two-piece linear 1127 * service curve. 1128 */ 1129 static int 1130 is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc) 1131 { 1132 struct segment *s, *last, *end; 1133 double y; 1134 1135 if (is_sc_null(sc)) { 1136 if (LIST_EMPTY(gsc)) 1137 return (1); 1138 LIST_FOREACH(s, gsc, _next) { 1139 if (s->m != 0) 1140 return (0); 1141 } 1142 return (1); 1143 } 1144 /* 1145 * gsc has a dummy entry at the end with x = INFINITY. 1146 * loop through up to this dummy entry. 1147 */ 1148 end = gsc_getentry(gsc, INFINITY); 1149 if (end == NULL) 1150 return (1); 1151 last = NULL; 1152 for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) { 1153 if (s->y > sc_x2y(sc, s->x)) 1154 return (0); 1155 last = s; 1156 } 1157 /* last now holds the real last segment */ 1158 if (last == NULL) 1159 return (1); 1160 if (last->m > sc->m2) 1161 return (0); 1162 if (last->x < sc->d && last->m > sc->m1) { 1163 y = last->y + (sc->d - last->x) * last->m; 1164 if (y > sc_x2y(sc, sc->d)) 1165 return (0); 1166 } 1167 return (1); 1168 } 1169 1170 static void 1171 gsc_destroy(struct gen_sc *gsc) 1172 { 1173 struct segment *s; 1174 1175 while ((s = LIST_FIRST(gsc)) != NULL) { 1176 LIST_REMOVE(s, _next); 1177 free(s); 1178 } 1179 } 1180 1181 /* 1182 * return a segment entry starting at x. 1183 * if gsc has no entry starting at x, a new entry is created at x. 1184 */ 1185 static struct segment * 1186 gsc_getentry(struct gen_sc *gsc, double x) 1187 { 1188 struct segment *new, *prev, *s; 1189 1190 prev = NULL; 1191 LIST_FOREACH(s, gsc, _next) { 1192 if (s->x == x) 1193 return (s); /* matching entry found */ 1194 else if (s->x < x) 1195 prev = s; 1196 else 1197 break; 1198 } 1199 1200 /* we have to create a new entry */ 1201 if ((new = calloc(1, sizeof(struct segment))) == NULL) 1202 return (NULL); 1203 1204 new->x = x; 1205 if (x == INFINITY || s == NULL) 1206 new->d = 0; 1207 else if (s->x == INFINITY) 1208 new->d = INFINITY; 1209 else 1210 new->d = s->x - x; 1211 if (prev == NULL) { 1212 /* insert the new entry at the head of the list */ 1213 new->y = 0; 1214 new->m = 0; 1215 LIST_INSERT_HEAD(gsc, new, _next); 1216 } else { 1217 /* 1218 * the start point intersects with the segment pointed by 1219 * prev. divide prev into 2 segments 1220 */ 1221 if (x == INFINITY) { 1222 prev->d = INFINITY; 1223 if (prev->m == 0) 1224 new->y = prev->y; 1225 else 1226 new->y = INFINITY; 1227 } else { 1228 prev->d = x - prev->x; 1229 new->y = prev->d * prev->m + prev->y; 1230 } 1231 new->m = prev->m; 1232 LIST_INSERT_AFTER(prev, new, _next); 1233 } 1234 return (new); 1235 } 1236 1237 /* add a segment to a generalized service curve */ 1238 static int 1239 gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m) 1240 { 1241 struct segment *start, *end, *s; 1242 double x2; 1243 1244 if (d == INFINITY) 1245 x2 = INFINITY; 1246 else 1247 x2 = x + d; 1248 start = gsc_getentry(gsc, x); 1249 end = gsc_getentry(gsc, x2); 1250 if (start == NULL || end == NULL) 1251 return (-1); 1252 1253 for (s = start; s != end; s = LIST_NEXT(s, _next)) { 1254 s->m += m; 1255 s->y += y + (s->x - x) * m; 1256 } 1257 1258 end = gsc_getentry(gsc, INFINITY); 1259 for (; s != end; s = LIST_NEXT(s, _next)) { 1260 s->y += m * d; 1261 } 1262 1263 return (0); 1264 } 1265 1266 /* get y-projection of a service curve */ 1267 static double 1268 sc_x2y(struct service_curve *sc, double x) 1269 { 1270 double y; 1271 1272 if (x <= (double)sc->d) 1273 /* y belongs to the 1st segment */ 1274 y = x * (double)sc->m1; 1275 else 1276 /* y belongs to the 2nd segment */ 1277 y = (double)sc->d * (double)sc->m1 1278 + (x - (double)sc->d) * (double)sc->m2; 1279 return (y); 1280 } 1281 1282 /* 1283 * misc utilities 1284 */ 1285 #define R2S_BUFS 8 1286 #define RATESTR_MAX 16 1287 1288 char * 1289 rate2str(double rate) 1290 { 1291 char *buf; 1292 static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */ 1293 static int idx = 0; 1294 int i; 1295 static const char unit[] = " KMG"; 1296 1297 buf = r2sbuf[idx++]; 1298 if (idx == R2S_BUFS) 1299 idx = 0; 1300 1301 for (i = 0; rate >= 1000 && i <= 3; i++) 1302 rate /= 1000; 1303 1304 if ((int)(rate * 100) % 100) 1305 snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]); 1306 else 1307 snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]); 1308 1309 return (buf); 1310 } 1311 1312 #ifdef __FreeBSD__ 1313 /* 1314 * XXX 1315 * FreeBSD does not have SIOCGIFDATA. 1316 * To emulate this, DIOCGIFSPEED ioctl added to pf. 1317 */ 1318 u_int32_t 1319 getifspeed(int pfdev, char *ifname) 1320 { 1321 struct pf_ifspeed io; 1322 1323 bzero(&io, sizeof io); 1324 if (strlcpy(io.ifname, ifname, IFNAMSIZ) >= 1325 sizeof(io.ifname)) 1326 errx(1, "getifspeed: strlcpy"); 1327 if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1) 1328 err(1, "DIOCGIFSPEED"); 1329 return ((u_int32_t)io.baudrate); 1330 } 1331 #else 1332 u_int32_t 1333 getifspeed(char *ifname) 1334 { 1335 int s; 1336 struct ifreq ifr; 1337 struct if_data ifrdat; 1338 1339 if ((s = socket(get_socket_domain(), SOCK_DGRAM, 0)) < 0) 1340 err(1, "socket"); 1341 bzero(&ifr, sizeof(ifr)); 1342 if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 1343 sizeof(ifr.ifr_name)) 1344 errx(1, "getifspeed: strlcpy"); 1345 ifr.ifr_data = (caddr_t)&ifrdat; 1346 if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) 1347 err(1, "SIOCGIFDATA"); 1348 if (close(s)) 1349 err(1, "close"); 1350 return ((u_int32_t)ifrdat.ifi_baudrate); 1351 } 1352 #endif 1353 1354 u_long 1355 getifmtu(char *ifname) 1356 { 1357 int s; 1358 struct ifreq ifr; 1359 1360 if ((s = socket(get_socket_domain(), SOCK_DGRAM, 0)) < 0) 1361 err(1, "socket"); 1362 bzero(&ifr, sizeof(ifr)); 1363 if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 1364 sizeof(ifr.ifr_name)) 1365 errx(1, "getifmtu: strlcpy"); 1366 if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1) 1367 #ifdef __FreeBSD__ 1368 ifr.ifr_mtu = 1500; 1369 #else 1370 err(1, "SIOCGIFMTU"); 1371 #endif 1372 if (close(s)) 1373 err(1, "close"); 1374 if (ifr.ifr_mtu > 0) 1375 return (ifr.ifr_mtu); 1376 else { 1377 warnx("could not get mtu for %s, assuming 1500", ifname); 1378 return (1500); 1379 } 1380 } 1381 1382 int 1383 eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, 1384 u_int32_t ref_bw) 1385 { 1386 int errors = 0; 1387 1388 switch (pa->scheduler) { 1389 case ALTQT_CBQ: 1390 pa->pq_u.cbq_opts = opts->data.cbq_opts; 1391 break; 1392 case ALTQT_PRIQ: 1393 pa->pq_u.priq_opts = opts->data.priq_opts; 1394 break; 1395 case ALTQT_HFSC: 1396 pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags; 1397 if (opts->data.hfsc_opts.linkshare.used) { 1398 pa->pq_u.hfsc_opts.lssc_m1 = 1399 eval_bwspec(&opts->data.hfsc_opts.linkshare.m1, 1400 ref_bw); 1401 pa->pq_u.hfsc_opts.lssc_m2 = 1402 eval_bwspec(&opts->data.hfsc_opts.linkshare.m2, 1403 ref_bw); 1404 pa->pq_u.hfsc_opts.lssc_d = 1405 opts->data.hfsc_opts.linkshare.d; 1406 } 1407 if (opts->data.hfsc_opts.realtime.used) { 1408 pa->pq_u.hfsc_opts.rtsc_m1 = 1409 eval_bwspec(&opts->data.hfsc_opts.realtime.m1, 1410 ref_bw); 1411 pa->pq_u.hfsc_opts.rtsc_m2 = 1412 eval_bwspec(&opts->data.hfsc_opts.realtime.m2, 1413 ref_bw); 1414 pa->pq_u.hfsc_opts.rtsc_d = 1415 opts->data.hfsc_opts.realtime.d; 1416 } 1417 if (opts->data.hfsc_opts.upperlimit.used) { 1418 pa->pq_u.hfsc_opts.ulsc_m1 = 1419 eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1, 1420 ref_bw); 1421 pa->pq_u.hfsc_opts.ulsc_m2 = 1422 eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2, 1423 ref_bw); 1424 pa->pq_u.hfsc_opts.ulsc_d = 1425 opts->data.hfsc_opts.upperlimit.d; 1426 } 1427 break; 1428 case ALTQT_FAIRQ: 1429 pa->pq_u.fairq_opts.flags = opts->data.fairq_opts.flags; 1430 pa->pq_u.fairq_opts.nbuckets = opts->data.fairq_opts.nbuckets; 1431 pa->pq_u.fairq_opts.hogs_m1 = 1432 eval_bwspec(&opts->data.fairq_opts.hogs_bw, ref_bw); 1433 1434 if (opts->data.fairq_opts.linkshare.used) { 1435 pa->pq_u.fairq_opts.lssc_m1 = 1436 eval_bwspec(&opts->data.fairq_opts.linkshare.m1, 1437 ref_bw); 1438 pa->pq_u.fairq_opts.lssc_m2 = 1439 eval_bwspec(&opts->data.fairq_opts.linkshare.m2, 1440 ref_bw); 1441 pa->pq_u.fairq_opts.lssc_d = 1442 opts->data.fairq_opts.linkshare.d; 1443 } 1444 break; 1445 case ALTQT_CODEL: 1446 pa->pq_u.codel_opts.target = opts->data.codel_opts.target; 1447 pa->pq_u.codel_opts.interval = opts->data.codel_opts.interval; 1448 pa->pq_u.codel_opts.ecn = opts->data.codel_opts.ecn; 1449 break; 1450 default: 1451 warnx("eval_queue_opts: unknown scheduler type %u", 1452 opts->qtype); 1453 errors++; 1454 break; 1455 } 1456 1457 return (errors); 1458 } 1459 1460 u_int32_t 1461 eval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw) 1462 { 1463 if (bw->bw_absolute > 0) 1464 return (bw->bw_absolute); 1465 1466 if (bw->bw_percent > 0) 1467 return (ref_bw / 100 * bw->bw_percent); 1468 1469 return (0); 1470 } 1471 1472 void 1473 print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, 1474 const struct node_hfsc_sc *sc) 1475 { 1476 printf(" %s", scname); 1477 1478 if (d != 0) { 1479 printf("("); 1480 if (sc != NULL && sc->m1.bw_percent > 0) 1481 printf("%u%%", sc->m1.bw_percent); 1482 else 1483 printf("%s", rate2str((double)m1)); 1484 printf(" %u", d); 1485 } 1486 1487 if (sc != NULL && sc->m2.bw_percent > 0) 1488 printf(" %u%%", sc->m2.bw_percent); 1489 else 1490 printf(" %s", rate2str((double)m2)); 1491 1492 if (d != 0) 1493 printf(")"); 1494 } 1495 1496 void 1497 print_fairq_sc(const char *scname, u_int m1, u_int d, u_int m2, 1498 const struct node_fairq_sc *sc) 1499 { 1500 printf(" %s", scname); 1501 1502 if (d != 0) { 1503 printf("("); 1504 if (sc != NULL && sc->m1.bw_percent > 0) 1505 printf("%u%%", sc->m1.bw_percent); 1506 else 1507 printf("%s", rate2str((double)m1)); 1508 printf(" %u", d); 1509 } 1510 1511 if (sc != NULL && sc->m2.bw_percent > 0) 1512 printf(" %u%%", sc->m2.bw_percent); 1513 else 1514 printf(" %s", rate2str((double)m2)); 1515 1516 if (d != 0) 1517 printf(")"); 1518 } 1519