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