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