pfctl_altq.c revision 223637
1171802Sdelphij/* $OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $ */ 2170808Sdelphij 3182739Sdelphij/* 4171802Sdelphij * Copyright (c) 2002 5170808Sdelphij * Sony Computer Science Laboratories Inc. 6170808Sdelphij * Copyright (c) 2002, 2003 Henning Brauer <henning@openbsd.org> 7170808Sdelphij * 8170808Sdelphij * Permission to use, copy, modify, and distribute this software for any 9170808Sdelphij * purpose with or without fee is hereby granted, provided that the above 10170808Sdelphij * copyright notice and this permission notice appear in all copies. 11170808Sdelphij * 12170808Sdelphij * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13170808Sdelphij * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14170808Sdelphij * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15170808Sdelphij * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16170808Sdelphij * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17170808Sdelphij * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18170808Sdelphij * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19170808Sdelphij */ 20170808Sdelphij 21170808Sdelphij#include <sys/cdefs.h> 22170808Sdelphij__FBSDID("$FreeBSD: head/contrib/pf/pfctl/pfctl_altq.c 223637 2011-06-28 11:57:25Z bz $"); 23170808Sdelphij 24170808Sdelphij#include <sys/types.h> 25170808Sdelphij#include <sys/ioctl.h> 26170808Sdelphij#include <sys/socket.h> 27170808Sdelphij 28170808Sdelphij#include <net/if.h> 29170808Sdelphij#include <netinet/in.h> 30170808Sdelphij#include <net/pfvar.h> 31170808Sdelphij 32170808Sdelphij#include <err.h> 33170808Sdelphij#include <errno.h> 34170808Sdelphij#include <limits.h> 35170808Sdelphij#include <math.h> 36170808Sdelphij#include <stdio.h> 37170808Sdelphij#include <stdlib.h> 38170808Sdelphij#include <string.h> 39170808Sdelphij#include <unistd.h> 40170808Sdelphij 41170808Sdelphij#include <altq/altq.h> 42170808Sdelphij#include <altq/altq_cbq.h> 43170808Sdelphij#include <altq/altq_priq.h> 44170808Sdelphij#include <altq/altq_hfsc.h> 45170808Sdelphij 46170808Sdelphij#include "pfctl_parser.h" 47170808Sdelphij#include "pfctl.h" 48170808Sdelphij 49170808Sdelphij#define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0)) 50170808Sdelphij 51170808SdelphijTAILQ_HEAD(altqs, pf_altq) altqs = TAILQ_HEAD_INITIALIZER(altqs); 52170808SdelphijLIST_HEAD(gen_sc, segment) rtsc, lssc; 53170808Sdelphij 54170808Sdelphijstruct pf_altq *qname_to_pfaltq(const char *, const char *); 55188929Salcu_int32_t qname_to_qid(const char *); 56170808Sdelphij 57170808Sdelphijstatic int eval_pfqueue_cbq(struct pfctl *, struct pf_altq *); 58170808Sdelphijstatic int cbq_compute_idletime(struct pfctl *, struct pf_altq *); 59170808Sdelphijstatic int check_commit_cbq(int, int, struct pf_altq *); 60170808Sdelphijstatic int print_cbq_opts(const struct pf_altq *); 61170808Sdelphij 62170808Sdelphijstatic int eval_pfqueue_priq(struct pfctl *, struct pf_altq *); 63170808Sdelphijstatic int check_commit_priq(int, int, struct pf_altq *); 64171069Sdelphijstatic int print_priq_opts(const struct pf_altq *); 65170808Sdelphij 66170808Sdelphijstatic int eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *); 67170808Sdelphijstatic int check_commit_hfsc(int, int, struct pf_altq *); 68170808Sdelphijstatic int print_hfsc_opts(const struct pf_altq *, 69170808Sdelphij const struct node_queue_opt *); 70170808Sdelphij 71170808Sdelphijstatic void gsc_add_sc(struct gen_sc *, struct service_curve *); 72170808Sdelphijstatic int is_gsc_under_sc(struct gen_sc *, 73170808Sdelphij struct service_curve *); 74170808Sdelphijstatic void gsc_destroy(struct gen_sc *); 75170808Sdelphijstatic struct segment *gsc_getentry(struct gen_sc *, double); 76170808Sdelphijstatic int gsc_add_seg(struct gen_sc *, double, double, double, 77170808Sdelphij double); 78170808Sdelphijstatic double sc_x2y(struct service_curve *, double); 79191990Sattilio 80170808Sdelphij#ifdef __FreeBSD__ 81170808Sdelphiju_int32_t getifspeed(int, char *); 82170808Sdelphij#else 83170808Sdelphiju_int32_t getifspeed(char *); 84170808Sdelphij#endif 85170808Sdelphiju_long getifmtu(char *); 86170808Sdelphijint eval_queue_opts(struct pf_altq *, struct node_queue_opt *, 87170808Sdelphij u_int32_t); 88170808Sdelphiju_int32_t eval_bwspec(struct node_queue_bw *, u_int32_t); 89171799Sdelphijvoid print_hfsc_sc(const char *, u_int, u_int, u_int, 90171799Sdelphij const struct node_hfsc_sc *); 91176559Sattilio 92171802Sdelphijvoid 93175294Sattiliopfaltq_store(struct pf_altq *a) 94170808Sdelphij{ 95171799Sdelphij struct pf_altq *altq; 96191990Sattilio 97170808Sdelphij if ((altq = malloc(sizeof(*altq))) == NULL) 98175202Sattilio err(1, "malloc"); 99171802Sdelphij memcpy(altq, a, sizeof(struct pf_altq)); 100170808Sdelphij TAILQ_INSERT_TAIL(&altqs, altq, entries); 101170808Sdelphij} 102170808Sdelphij 103170808Sdelphijstruct pf_altq * 104170808Sdelphijpfaltq_lookup(const char *ifname) 105188318Skib{ 106170808Sdelphij struct pf_altq *altq; 107170808Sdelphij 108170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 109170808Sdelphij if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && 110170808Sdelphij altq->qname[0] == 0) 111170808Sdelphij return (altq); 112170808Sdelphij } 113170808Sdelphij return (NULL); 114170808Sdelphij} 115170808Sdelphij 116170808Sdelphijstruct pf_altq * 117170808Sdelphijqname_to_pfaltq(const char *qname, const char *ifname) 118170808Sdelphij{ 119170808Sdelphij struct pf_altq *altq; 120170808Sdelphij 121170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 122170808Sdelphij if (strncmp(ifname, altq->ifname, IFNAMSIZ) == 0 && 123170808Sdelphij strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) 124170808Sdelphij return (altq); 125170808Sdelphij } 126170808Sdelphij return (NULL); 127170808Sdelphij} 128170808Sdelphij 129170808Sdelphiju_int32_t 130170808Sdelphijqname_to_qid(const char *qname) 131170808Sdelphij{ 132170808Sdelphij struct pf_altq *altq; 133170808Sdelphij 134170808Sdelphij /* 135170808Sdelphij * We guarantee that same named queues on different interfaces 136170808Sdelphij * have the same qid, so we do NOT need to limit matching on 137170808Sdelphij * one interface! 138170808Sdelphij */ 139170808Sdelphij 140170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 141170808Sdelphij if (strncmp(qname, altq->qname, PF_QNAME_SIZE) == 0) 142170808Sdelphij return (altq->qid); 143170808Sdelphij } 144170808Sdelphij return (0); 145170808Sdelphij} 146170808Sdelphij 147170808Sdelphijvoid 148170808Sdelphijprint_altq(const struct pf_altq *a, unsigned int level, 149170808Sdelphij struct node_queue_bw *bw, struct node_queue_opt *qopts) 150170808Sdelphij{ 151170808Sdelphij if (a->qname[0] != 0) { 152170808Sdelphij print_queue(a, level, bw, 1, qopts); 153170808Sdelphij return; 154171070Sdelphij } 155170808Sdelphij 156171799Sdelphij#ifdef __FreeBSD__ 157191990Sattilio if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) 158170808Sdelphij printf("INACTIVE "); 159170808Sdelphij#endif 160170808Sdelphij 161170808Sdelphij printf("altq on %s ", a->ifname); 162170808Sdelphij 163170808Sdelphij switch (a->scheduler) { 164170808Sdelphij case ALTQT_CBQ: 165170808Sdelphij if (!print_cbq_opts(a)) 166170808Sdelphij printf("cbq "); 167170808Sdelphij break; 168171070Sdelphij case ALTQT_PRIQ: 169170808Sdelphij if (!print_priq_opts(a)) 170171799Sdelphij printf("priq "); 171171799Sdelphij break; 172191990Sattilio case ALTQT_HFSC: 173170808Sdelphij if (!print_hfsc_opts(a, qopts)) 174170808Sdelphij printf("hfsc "); 175170808Sdelphij break; 176170808Sdelphij } 177170808Sdelphij 178170808Sdelphij if (bw != NULL && bw->bw_percent > 0) { 179170808Sdelphij if (bw->bw_percent < 100) 180170808Sdelphij printf("bandwidth %u%% ", bw->bw_percent); 181170808Sdelphij } else 182170808Sdelphij printf("bandwidth %s ", rate2str((double)a->ifbandwidth)); 183170808Sdelphij 184170808Sdelphij if (a->qlimit != DEFAULT_QLIMIT) 185170808Sdelphij printf("qlimit %u ", a->qlimit); 186176559Sattilio printf("tbrsize %u ", a->tbrsize); 187170808Sdelphij} 188170808Sdelphij 189170808Sdelphijvoid 190170808Sdelphijprint_queue(const struct pf_altq *a, unsigned int level, 191170808Sdelphij struct node_queue_bw *bw, int print_interface, 192170808Sdelphij struct node_queue_opt *qopts) 193171069Sdelphij{ 194170808Sdelphij unsigned int i; 195170808Sdelphij 196170808Sdelphij#ifdef __FreeBSD__ 197170808Sdelphij if (a->local_flags & PFALTQ_FLAG_IF_REMOVED) 198170808Sdelphij printf("INACTIVE "); 199170808Sdelphij#endif 200170808Sdelphij printf("queue "); 201170808Sdelphij for (i = 0; i < level; ++i) 202170808Sdelphij printf(" "); 203170808Sdelphij printf("%s ", a->qname); 204170808Sdelphij if (print_interface) 205170808Sdelphij printf("on %s ", a->ifname); 206170808Sdelphij if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC) { 207171069Sdelphij if (bw != NULL && bw->bw_percent > 0) { 208170808Sdelphij if (bw->bw_percent < 100) 209170808Sdelphij printf("bandwidth %u%% ", bw->bw_percent); 210170808Sdelphij } else 211170808Sdelphij printf("bandwidth %s ", rate2str((double)a->bandwidth)); 212170808Sdelphij } 213170808Sdelphij if (a->priority != DEFAULT_PRIORITY) 214170808Sdelphij printf("priority %u ", a->priority); 215170808Sdelphij if (a->qlimit != DEFAULT_QLIMIT) 216170808Sdelphij printf("qlimit %u ", a->qlimit); 217170808Sdelphij switch (a->scheduler) { 218170808Sdelphij case ALTQT_CBQ: 219170808Sdelphij print_cbq_opts(a); 220170808Sdelphij break; 221170808Sdelphij case ALTQT_PRIQ: 222170808Sdelphij print_priq_opts(a); 223170808Sdelphij break; 224171069Sdelphij case ALTQT_HFSC: 225170808Sdelphij print_hfsc_opts(a, qopts); 226170808Sdelphij break; 227170808Sdelphij } 228170808Sdelphij} 229170808Sdelphij 230170808Sdelphij/* 231170808Sdelphij * eval_pfaltq computes the discipline parameters. 232170808Sdelphij */ 233176559Sattilioint 234170808Sdelphijeval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, 235170808Sdelphij struct node_queue_opt *opts) 236171070Sdelphij{ 237170808Sdelphij u_int rate, size, errors = 0; 238170808Sdelphij 239170808Sdelphij if (bw->bw_absolute > 0) 240170808Sdelphij pa->ifbandwidth = bw->bw_absolute; 241170808Sdelphij else 242170808Sdelphij#ifdef __FreeBSD__ 243170808Sdelphij if ((rate = getifspeed(pf->dev, pa->ifname)) == 0) { 244170808Sdelphij#else 245170808Sdelphij if ((rate = getifspeed(pa->ifname)) == 0) { 246170808Sdelphij#endif 247170808Sdelphij fprintf(stderr, "interface %s does not know its bandwidth, " 248171070Sdelphij "please specify an absolute bandwidth\n", 249170808Sdelphij pa->ifname); 250170808Sdelphij errors++; 251176559Sattilio } else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0) 252170808Sdelphij pa->ifbandwidth = rate; 253170808Sdelphij 254170808Sdelphij errors += eval_queue_opts(pa, opts, pa->ifbandwidth); 255170808Sdelphij 256170808Sdelphij /* if tbrsize is not specified, use heuristics */ 257171069Sdelphij if (pa->tbrsize == 0) { 258170808Sdelphij rate = pa->ifbandwidth; 259170808Sdelphij if (rate <= 1 * 1000 * 1000) 260170808Sdelphij size = 1; 261170808Sdelphij else if (rate <= 10 * 1000 * 1000) 262170808Sdelphij size = 4; 263170808Sdelphij else if (rate <= 200 * 1000 * 1000) 264176559Sattilio size = 8; 265170808Sdelphij else 266170808Sdelphij size = 24; 267170808Sdelphij size = size * getifmtu(pa->ifname); 268170808Sdelphij if (size > 0xffff) 269170808Sdelphij size = 0xffff; 270170808Sdelphij pa->tbrsize = size; 271170808Sdelphij } 272170808Sdelphij return (errors); 273170808Sdelphij} 274170808Sdelphij 275170808Sdelphij/* 276170808Sdelphij * check_commit_altq does consistency check for each interface 277170808Sdelphij */ 278170808Sdelphijint 279170808Sdelphijcheck_commit_altq(int dev, int opts) 280170808Sdelphij{ 281170808Sdelphij struct pf_altq *altq; 282170808Sdelphij int error = 0; 283184413Strasz 284170808Sdelphij /* call the discipline check for each interface. */ 285170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 286170808Sdelphij if (altq->qname[0] == 0) { 287170808Sdelphij switch (altq->scheduler) { 288170808Sdelphij case ALTQT_CBQ: 289176559Sattilio error = check_commit_cbq(dev, opts, altq); 290170808Sdelphij break; 291170808Sdelphij case ALTQT_PRIQ: 292170808Sdelphij error = check_commit_priq(dev, opts, altq); 293170808Sdelphij break; 294170808Sdelphij case ALTQT_HFSC: 295170808Sdelphij error = check_commit_hfsc(dev, opts, altq); 296170808Sdelphij break; 297170808Sdelphij default: 298170808Sdelphij break; 299184413Strasz } 300170808Sdelphij } 301170808Sdelphij } 302170808Sdelphij return (error); 303170808Sdelphij} 304170808Sdelphij 305170808Sdelphij/* 306170808Sdelphij * eval_pfqueue computes the queue parameters. 307170808Sdelphij */ 308170808Sdelphijint 309170808Sdelphijeval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw, 310170808Sdelphij struct node_queue_opt *opts) 311170808Sdelphij{ 312170808Sdelphij /* should be merged with expand_queue */ 313170808Sdelphij struct pf_altq *if_pa, *parent, *altq; 314170808Sdelphij u_int32_t bwsum; 315170808Sdelphij int error = 0; 316170808Sdelphij 317170808Sdelphij /* find the corresponding interface and copy fields used by queues */ 318170808Sdelphij if ((if_pa = pfaltq_lookup(pa->ifname)) == NULL) { 319184413Strasz fprintf(stderr, "altq not defined on %s\n", pa->ifname); 320170808Sdelphij return (1); 321170808Sdelphij } 322170808Sdelphij pa->scheduler = if_pa->scheduler; 323170808Sdelphij pa->ifbandwidth = if_pa->ifbandwidth; 324170808Sdelphij 325184413Strasz if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) { 326170808Sdelphij fprintf(stderr, "queue %s already exists on interface %s\n", 327170808Sdelphij pa->qname, pa->ifname); 328176559Sattilio return (1); 329170808Sdelphij } 330170808Sdelphij pa->qid = qname_to_qid(pa->qname); 331170808Sdelphij 332170808Sdelphij parent = NULL; 333170808Sdelphij if (pa->parent[0] != 0) { 334170808Sdelphij parent = qname_to_pfaltq(pa->parent, pa->ifname); 335170808Sdelphij if (parent == NULL) { 336170808Sdelphij fprintf(stderr, "parent %s not found for %s\n", 337170808Sdelphij pa->parent, pa->qname); 338170808Sdelphij return (1); 339170808Sdelphij } 340170808Sdelphij pa->parent_qid = parent->qid; 341170808Sdelphij } 342170808Sdelphij if (pa->qlimit == 0) 343170808Sdelphij pa->qlimit = DEFAULT_QLIMIT; 344170808Sdelphij 345170808Sdelphij if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC) { 346170808Sdelphij pa->bandwidth = eval_bwspec(bw, 347170808Sdelphij parent == NULL ? 0 : parent->bandwidth); 348170808Sdelphij 349170808Sdelphij if (pa->bandwidth > pa->ifbandwidth) { 350170808Sdelphij fprintf(stderr, "bandwidth for %s higher than " 351170808Sdelphij "interface\n", pa->qname); 352170808Sdelphij return (1); 353170808Sdelphij } 354170808Sdelphij /* check the sum of the child bandwidth is under parent's */ 355170808Sdelphij if (parent != NULL) { 356170808Sdelphij if (pa->bandwidth > parent->bandwidth) { 357170808Sdelphij warnx("bandwidth for %s higher than parent", 358170808Sdelphij pa->qname); 359170808Sdelphij return (1); 360170808Sdelphij } 361170808Sdelphij bwsum = 0; 362170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 363183214Skib if (strncmp(altq->ifname, pa->ifname, 364170808Sdelphij IFNAMSIZ) == 0 && 365183212Skib altq->qname[0] != 0 && 366170808Sdelphij strncmp(altq->parent, pa->parent, 367170808Sdelphij PF_QNAME_SIZE) == 0) 368170808Sdelphij bwsum += altq->bandwidth; 369170808Sdelphij } 370170808Sdelphij bwsum += pa->bandwidth; 371170808Sdelphij if (bwsum > parent->bandwidth) { 372170808Sdelphij warnx("the sum of the child bandwidth higher" 373170808Sdelphij " than parent \"%s\"", parent->qname); 374170808Sdelphij } 375170808Sdelphij } 376170808Sdelphij } 377170808Sdelphij 378170808Sdelphij if (eval_queue_opts(pa, opts, parent == NULL? 0 : parent->bandwidth)) 379170808Sdelphij return (1); 380182371Sattilio 381170808Sdelphij switch (pa->scheduler) { 382170808Sdelphij case ALTQT_CBQ: 383170808Sdelphij error = eval_pfqueue_cbq(pf, pa); 384176559Sattilio break; 385170808Sdelphij case ALTQT_PRIQ: 386170808Sdelphij error = eval_pfqueue_priq(pf, pa); 387170808Sdelphij break; 388170808Sdelphij case ALTQT_HFSC: 389170808Sdelphij error = eval_pfqueue_hfsc(pf, pa); 390170808Sdelphij break; 391170808Sdelphij default: 392170808Sdelphij break; 393170808Sdelphij } 394170808Sdelphij return (error); 395170808Sdelphij} 396170808Sdelphij 397170808Sdelphij/* 398170808Sdelphij * CBQ support functions 399170808Sdelphij */ 400182371Sattilio#define RM_FILTER_GAIN 5 /* log2 of gain, e.g., 5 => 31/32 */ 401170808Sdelphij#define RM_NS_PER_SEC (1000000000) 402170808Sdelphij 403182371Sattiliostatic int 404170808Sdelphijeval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa) 405170808Sdelphij{ 406182371Sattilio struct cbq_opts *opts; 407170808Sdelphij u_int ifmtu; 408170808Sdelphij 409182371Sattilio if (pa->priority >= CBQ_MAXPRI) { 410170808Sdelphij warnx("priority out of range: max %d", CBQ_MAXPRI - 1); 411170808Sdelphij return (-1); 412170808Sdelphij } 413170808Sdelphij 414170808Sdelphij ifmtu = getifmtu(pa->ifname); 415170808Sdelphij opts = &pa->pq_u.cbq_opts; 416170808Sdelphij 417171070Sdelphij if (opts->pktsize == 0) { /* use default */ 418182371Sattilio opts->pktsize = ifmtu; 419170808Sdelphij if (opts->pktsize > MCLBYTES) /* do what TCP does */ 420170808Sdelphij opts->pktsize &= ~MCLBYTES; 421170808Sdelphij } else if (opts->pktsize > ifmtu) 422170808Sdelphij opts->pktsize = ifmtu; 423170808Sdelphij if (opts->maxpktsize == 0) /* use default */ 424170808Sdelphij opts->maxpktsize = ifmtu; 425176559Sattilio else if (opts->maxpktsize > ifmtu) 426170808Sdelphij opts->pktsize = ifmtu; 427170808Sdelphij 428170808Sdelphij if (opts->pktsize > opts->maxpktsize) 429170808Sdelphij opts->pktsize = opts->maxpktsize; 430170808Sdelphij 431171489Sdelphij if (pa->parent[0] == 0) 432170808Sdelphij opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR); 433171489Sdelphij 434170808Sdelphij cbq_compute_idletime(pf, pa); 435171489Sdelphij return (0); 436171489Sdelphij} 437188929Salc 438188929Salc/* 439171489Sdelphij * compute ns_per_byte, maxidle, minidle, and offtime 440171489Sdelphij */ 441170808Sdelphijstatic int 442171489Sdelphijcbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa) 443171489Sdelphij{ 444171489Sdelphij struct cbq_opts *opts; 445171489Sdelphij double maxidle_s, maxidle, minidle; 446170808Sdelphij double offtime, nsPerByte, ifnsPerByte, ptime, cptime; 447171489Sdelphij double z, g, f, gton, gtom; 448171489Sdelphij u_int minburst, maxburst; 449170808Sdelphij 450171489Sdelphij opts = &pa->pq_u.cbq_opts; 451171489Sdelphij ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8; 452171489Sdelphij minburst = opts->minburst; 453171489Sdelphij maxburst = opts->maxburst; 454171489Sdelphij 455171489Sdelphij if (pa->bandwidth == 0) 456171489Sdelphij f = 0.0001; /* small enough? */ 457171489Sdelphij else 458188929Salc f = ((double) pa->bandwidth / (double) pa->ifbandwidth); 459171489Sdelphij 460171489Sdelphij nsPerByte = ifnsPerByte / f; 461171489Sdelphij ptime = (double)opts->pktsize * ifnsPerByte; 462171489Sdelphij cptime = ptime * (1.0 - f) / f; 463171799Sdelphij 464171489Sdelphij if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) { 465171489Sdelphij /* 466171489Sdelphij * this causes integer overflow in kernel! 467171489Sdelphij * (bandwidth < 6Kbps when max_pkt_size=1500) 468171489Sdelphij */ 469171489Sdelphij if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0) 470171489Sdelphij warnx("queue bandwidth must be larger than %s", 471171489Sdelphij rate2str(ifnsPerByte * (double)opts->maxpktsize / 472171489Sdelphij (double)INT_MAX * (double)pa->ifbandwidth)); 473171489Sdelphij fprintf(stderr, "cbq: queue %s is too slow!\n", 474171489Sdelphij pa->qname); 475171489Sdelphij nsPerByte = (double)(INT_MAX / opts->maxpktsize); 476171489Sdelphij } 477171489Sdelphij 478171489Sdelphij if (maxburst == 0) { /* use default */ 479170808Sdelphij if (cptime > 10.0 * 1000000) 480170808Sdelphij maxburst = 4; 481171489Sdelphij else 482188929Salc maxburst = 16; 483171489Sdelphij } 484171308Sdelphij if (minburst == 0) /* use default */ 485171489Sdelphij minburst = 2; 486188921Salc if (minburst > maxburst) 487171489Sdelphij minburst = maxburst; 488171489Sdelphij 489171489Sdelphij z = (double)(1 << RM_FILTER_GAIN); 490171489Sdelphij g = (1.0 - 1.0 / z); 491171489Sdelphij gton = pow(g, (double)maxburst); 492171489Sdelphij gtom = pow(g, (double)(minburst-1)); 493170808Sdelphij maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton)); 494170808Sdelphij maxidle_s = (1.0 - g); 495171069Sdelphij if (maxidle > maxidle_s) 496170808Sdelphij maxidle = ptime * maxidle; 497170808Sdelphij else 498170808Sdelphij maxidle = ptime * maxidle_s; 499170808Sdelphij offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom); 500170808Sdelphij minidle = -((double)opts->maxpktsize * (double)nsPerByte); 501170808Sdelphij 502170808Sdelphij /* scale parameters */ 503171489Sdelphij maxidle = ((maxidle * 8.0) / nsPerByte) * 504171489Sdelphij pow(2.0, (double)RM_FILTER_GAIN); 505170808Sdelphij offtime = (offtime * 8.0) / nsPerByte * 506174265Swkoszek pow(2.0, (double)RM_FILTER_GAIN); 507170808Sdelphij minidle = ((minidle * 8.0) / nsPerByte) * 508170808Sdelphij pow(2.0, (double)RM_FILTER_GAIN); 509170808Sdelphij 510170808Sdelphij maxidle = maxidle / 1000.0; 511170808Sdelphij offtime = offtime / 1000.0; 512170808Sdelphij minidle = minidle / 1000.0; 513170808Sdelphij 514170808Sdelphij opts->minburst = minburst; 515170808Sdelphij opts->maxburst = maxburst; 516170808Sdelphij opts->ns_per_byte = (u_int)nsPerByte; 517170808Sdelphij opts->maxidle = (u_int)fabs(maxidle); 518170808Sdelphij opts->minidle = (int)minidle; 519170808Sdelphij opts->offtime = (u_int)fabs(offtime); 520170808Sdelphij 521170808Sdelphij return (0); 522170808Sdelphij} 523171489Sdelphij 524171489Sdelphijstatic int 525171489Sdelphijcheck_commit_cbq(int dev, int opts, struct pf_altq *pa) 526171489Sdelphij{ 527171489Sdelphij struct pf_altq *altq; 528171489Sdelphij int root_class, default_class; 529171489Sdelphij int error = 0; 530171489Sdelphij 531171489Sdelphij /* 532171489Sdelphij * check if cbq has one root queue and one default queue 533171489Sdelphij * for this interface 534170808Sdelphij */ 535170808Sdelphij root_class = default_class = 0; 536170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 537170808Sdelphij if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 538170808Sdelphij continue; 539170808Sdelphij if (altq->qname[0] == 0) /* this is for interface */ 540170808Sdelphij continue; 541170808Sdelphij if (altq->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS) 542171069Sdelphij root_class++; 543171489Sdelphij if (altq->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS) 544171489Sdelphij default_class++; 545171489Sdelphij } 546171489Sdelphij if (root_class != 1) { 547188929Salc warnx("should have one root queue on %s", pa->ifname); 548188929Salc error++; 549171489Sdelphij } 550171489Sdelphij if (default_class != 1) { 551171489Sdelphij warnx("should have one default queue on %s", pa->ifname); 552174265Swkoszek error++; 553174265Swkoszek } 554171489Sdelphij return (error); 555171489Sdelphij} 556171489Sdelphij 557171489Sdelphijstatic int 558171489Sdelphijprint_cbq_opts(const struct pf_altq *a) 559171489Sdelphij{ 560171489Sdelphij const struct cbq_opts *opts; 561171489Sdelphij 562171489Sdelphij opts = &a->pq_u.cbq_opts; 563171489Sdelphij if (opts->flags) { 564171489Sdelphij printf("cbq("); 565171489Sdelphij if (opts->flags & CBQCLF_RED) 566171489Sdelphij printf(" red"); 567171489Sdelphij if (opts->flags & CBQCLF_ECN) 568171489Sdelphij printf(" ecn"); 569171489Sdelphij if (opts->flags & CBQCLF_RIO) 570171489Sdelphij printf(" rio"); 571171489Sdelphij if (opts->flags & CBQCLF_CLEARDSCP) 572171489Sdelphij printf(" cleardscp"); 573171489Sdelphij if (opts->flags & CBQCLF_FLOWVALVE) 574171489Sdelphij printf(" flowvalve"); 575188929Salc if (opts->flags & CBQCLF_BORROW) 576171489Sdelphij printf(" borrow"); 577171489Sdelphij if (opts->flags & CBQCLF_WRR) 578171489Sdelphij printf(" wrr"); 579171489Sdelphij if (opts->flags & CBQCLF_EFFICIENT) 580171489Sdelphij printf(" efficient"); 581171489Sdelphij if (opts->flags & CBQCLF_ROOTCLASS) 582171489Sdelphij printf(" root"); 583171489Sdelphij if (opts->flags & CBQCLF_DEFCLASS) 584171489Sdelphij printf(" default"); 585171489Sdelphij printf(" ) "); 586171489Sdelphij 587171489Sdelphij return (1); 588171489Sdelphij } else 589171489Sdelphij return (0); 590171489Sdelphij} 591171489Sdelphij 592171489Sdelphij/* 593171489Sdelphij * PRIQ support functions 594171489Sdelphij */ 595171489Sdelphijstatic int 596171489Sdelphijeval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa) 597188929Salc{ 598188929Salc struct pf_altq *altq; 599188929Salc 600171489Sdelphij if (pa->priority >= PRIQ_MAXPRI) { 601171489Sdelphij warnx("priority out of range: max %d", PRIQ_MAXPRI - 1); 602171489Sdelphij return (-1); 603171489Sdelphij } 604171489Sdelphij /* the priority should be unique for the interface */ 605171489Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 606171489Sdelphij if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) == 0 && 607171489Sdelphij altq->qname[0] != 0 && altq->priority == pa->priority) { 608171489Sdelphij warnx("%s and %s have the same priority", 609171489Sdelphij altq->qname, pa->qname); 610171489Sdelphij return (-1); 611171489Sdelphij } 612171489Sdelphij } 613188921Salc 614171489Sdelphij return (0); 615171489Sdelphij} 616171489Sdelphij 617171489Sdelphijstatic int 618171489Sdelphijcheck_commit_priq(int dev, int opts, struct pf_altq *pa) 619171489Sdelphij{ 620171489Sdelphij struct pf_altq *altq; 621171489Sdelphij int default_class; 622171489Sdelphij int error = 0; 623171489Sdelphij 624171489Sdelphij /* 625171489Sdelphij * check if priq has one default class for this interface 626171489Sdelphij */ 627170808Sdelphij default_class = 0; 628170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 629170808Sdelphij if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 630170808Sdelphij continue; 631170808Sdelphij if (altq->qname[0] == 0) /* this is for interface */ 632170808Sdelphij continue; 633170808Sdelphij if (altq->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS) 634170808Sdelphij default_class++; 635171489Sdelphij } 636170808Sdelphij if (default_class != 1) { 637170808Sdelphij warnx("should have one default queue on %s", pa->ifname); 638170808Sdelphij error++; 639171489Sdelphij } 640171489Sdelphij return (error); 641170808Sdelphij} 642170808Sdelphij 643170808Sdelphijstatic int 644170808Sdelphijprint_priq_opts(const struct pf_altq *a) 645170808Sdelphij{ 646170808Sdelphij const struct priq_opts *opts; 647170808Sdelphij 648170808Sdelphij opts = &a->pq_u.priq_opts; 649170808Sdelphij 650170808Sdelphij if (opts->flags) { 651170808Sdelphij printf("priq("); 652170808Sdelphij if (opts->flags & PRCF_RED) 653170808Sdelphij printf(" red"); 654170808Sdelphij if (opts->flags & PRCF_ECN) 655170808Sdelphij printf(" ecn"); 656170808Sdelphij if (opts->flags & PRCF_RIO) 657171070Sdelphij printf(" rio"); 658171070Sdelphij if (opts->flags & PRCF_CLEARDSCP) 659170808Sdelphij printf(" cleardscp"); 660170808Sdelphij if (opts->flags & PRCF_DEFAULTCLASS) 661170808Sdelphij printf(" default"); 662170808Sdelphij printf(" ) "); 663170808Sdelphij 664170808Sdelphij return (1); 665170808Sdelphij } else 666170808Sdelphij return (0); 667170808Sdelphij} 668170808Sdelphij 669170808Sdelphij/* 670170808Sdelphij * HFSC support functions 671170808Sdelphij */ 672170808Sdelphijstatic int 673170808Sdelphijeval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa) 674170808Sdelphij{ 675170808Sdelphij struct pf_altq *altq, *parent; 676170808Sdelphij struct hfsc_opts *opts; 677170808Sdelphij struct service_curve sc; 678170808Sdelphij 679170808Sdelphij opts = &pa->pq_u.hfsc_opts; 680170808Sdelphij 681171489Sdelphij if (pa->parent[0] == 0) { 682171489Sdelphij /* root queue */ 683171489Sdelphij opts->lssc_m1 = pa->ifbandwidth; 684171489Sdelphij opts->lssc_m2 = pa->ifbandwidth; 685171489Sdelphij opts->lssc_d = 0; 686171489Sdelphij return (0); 687171489Sdelphij } 688171489Sdelphij 689171489Sdelphij LIST_INIT(&rtsc); 690171489Sdelphij LIST_INIT(&lssc); 691170808Sdelphij 692170808Sdelphij /* if link_share is not specified, use bandwidth */ 693170808Sdelphij if (opts->lssc_m2 == 0) 694170808Sdelphij opts->lssc_m2 = pa->bandwidth; 695170808Sdelphij 696170808Sdelphij if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) || 697170808Sdelphij (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) || 698170808Sdelphij (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) { 699170808Sdelphij warnx("m2 is zero for %s", pa->qname); 700170808Sdelphij return (-1); 701170808Sdelphij } 702170808Sdelphij 703170808Sdelphij if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) || 704170808Sdelphij (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) || 705170808Sdelphij (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) { 706170808Sdelphij warnx("m1 must be zero for convex curve: %s", pa->qname); 707170808Sdelphij return (-1); 708170808Sdelphij } 709170808Sdelphij 710170808Sdelphij /* 711170808Sdelphij * admission control: 712171069Sdelphij * for the real-time service curve, the sum of the service curves 713170808Sdelphij * should not exceed 80% of the interface bandwidth. 20% is reserved 714170808Sdelphij * not to over-commit the actual interface bandwidth. 715170808Sdelphij * for the linkshare service curve, the sum of the child service 716170808Sdelphij * curve should not exceed the parent service curve. 717176559Sattilio * for the upper-limit service curve, the assigned bandwidth should 718170808Sdelphij * be smaller than the interface bandwidth, and the upper-limit should 719170808Sdelphij * be larger than the real-time service curve when both are defined. 720170808Sdelphij */ 721170808Sdelphij parent = qname_to_pfaltq(pa->parent, pa->ifname); 722170808Sdelphij if (parent == NULL) 723170808Sdelphij errx(1, "parent %s not found for %s", pa->parent, pa->qname); 724170808Sdelphij 725170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 726171069Sdelphij if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 727170808Sdelphij continue; 728170808Sdelphij if (altq->qname[0] == 0) /* this is for interface */ 729170808Sdelphij continue; 730170808Sdelphij 731170808Sdelphij /* if the class has a real-time service curve, add it. */ 732170808Sdelphij if (opts->rtsc_m2 != 0 && altq->pq_u.hfsc_opts.rtsc_m2 != 0) { 733170808Sdelphij sc.m1 = altq->pq_u.hfsc_opts.rtsc_m1; 734170808Sdelphij sc.d = altq->pq_u.hfsc_opts.rtsc_d; 735170808Sdelphij sc.m2 = altq->pq_u.hfsc_opts.rtsc_m2; 736170808Sdelphij gsc_add_sc(&rtsc, &sc); 737170808Sdelphij } 738176559Sattilio 739176559Sattilio if (strncmp(altq->parent, pa->parent, PF_QNAME_SIZE) != 0) 740170808Sdelphij continue; 741170808Sdelphij 742170808Sdelphij /* if the class has a linkshare service curve, add it. */ 743170808Sdelphij if (opts->lssc_m2 != 0 && altq->pq_u.hfsc_opts.lssc_m2 != 0) { 744170808Sdelphij sc.m1 = altq->pq_u.hfsc_opts.lssc_m1; 745170808Sdelphij sc.d = altq->pq_u.hfsc_opts.lssc_d; 746170808Sdelphij sc.m2 = altq->pq_u.hfsc_opts.lssc_m2; 747170808Sdelphij gsc_add_sc(&lssc, &sc); 748170808Sdelphij } 749188318Skib } 750170808Sdelphij 751170808Sdelphij /* check the real-time service curve. reserve 20% of interface bw */ 752170808Sdelphij if (opts->rtsc_m2 != 0) { 753170808Sdelphij /* add this queue to the sum */ 754170808Sdelphij sc.m1 = opts->rtsc_m1; 755170808Sdelphij sc.d = opts->rtsc_d; 756170808Sdelphij sc.m2 = opts->rtsc_m2; 757170808Sdelphij gsc_add_sc(&rtsc, &sc); 758170808Sdelphij /* compare the sum with 80% of the interface */ 759170808Sdelphij sc.m1 = 0; 760170808Sdelphij sc.d = 0; 761170808Sdelphij sc.m2 = pa->ifbandwidth / 100 * 80; 762170808Sdelphij if (!is_gsc_under_sc(&rtsc, &sc)) { 763170808Sdelphij warnx("real-time sc exceeds 80%% of the interface " 764170808Sdelphij "bandwidth (%s)", rate2str((double)sc.m2)); 765170808Sdelphij goto err_ret; 766170808Sdelphij } 767170808Sdelphij } 768170808Sdelphij 769170808Sdelphij /* check the linkshare service curve. */ 770170808Sdelphij if (opts->lssc_m2 != 0) { 771170808Sdelphij /* add this queue to the child sum */ 772170808Sdelphij sc.m1 = opts->lssc_m1; 773170808Sdelphij sc.d = opts->lssc_d; 774170808Sdelphij sc.m2 = opts->lssc_m2; 775170808Sdelphij gsc_add_sc(&lssc, &sc); 776170808Sdelphij /* compare the sum of the children with parent's sc */ 777170808Sdelphij sc.m1 = parent->pq_u.hfsc_opts.lssc_m1; 778170808Sdelphij sc.d = parent->pq_u.hfsc_opts.lssc_d; 779170808Sdelphij sc.m2 = parent->pq_u.hfsc_opts.lssc_m2; 780171069Sdelphij if (!is_gsc_under_sc(&lssc, &sc)) { 781170808Sdelphij warnx("linkshare sc exceeds parent's sc"); 782170808Sdelphij goto err_ret; 783170808Sdelphij } 784170808Sdelphij } 785170808Sdelphij 786170808Sdelphij /* check the upper-limit service curve. */ 787170808Sdelphij if (opts->ulsc_m2 != 0) { 788170808Sdelphij if (opts->ulsc_m1 > pa->ifbandwidth || 789170808Sdelphij opts->ulsc_m2 > pa->ifbandwidth) { 790170808Sdelphij warnx("upper-limit larger than interface bandwidth"); 791176559Sattilio goto err_ret; 792170808Sdelphij } 793170808Sdelphij if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) { 794170808Sdelphij warnx("upper-limit sc smaller than real-time sc"); 795170808Sdelphij goto err_ret; 796170808Sdelphij } 797170808Sdelphij } 798170808Sdelphij 799170808Sdelphij gsc_destroy(&rtsc); 800170808Sdelphij gsc_destroy(&lssc); 801170808Sdelphij 802170808Sdelphij return (0); 803170808Sdelphij 804170808Sdelphijerr_ret: 805170808Sdelphij gsc_destroy(&rtsc); 806170808Sdelphij gsc_destroy(&lssc); 807170808Sdelphij return (-1); 808170808Sdelphij} 809170808Sdelphij 810170808Sdelphijstatic int 811170808Sdelphijcheck_commit_hfsc(int dev, int opts, struct pf_altq *pa) 812170808Sdelphij{ 813170808Sdelphij struct pf_altq *altq, *def = NULL; 814170808Sdelphij int default_class; 815170808Sdelphij int error = 0; 816170808Sdelphij 817170808Sdelphij /* check if hfsc has one default queue for this interface */ 818170808Sdelphij default_class = 0; 819170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 820170808Sdelphij if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 821170808Sdelphij continue; 822170808Sdelphij if (altq->qname[0] == 0) /* this is for interface */ 823170808Sdelphij continue; 824170808Sdelphij if (altq->parent[0] == 0) /* dummy root */ 825170808Sdelphij continue; 826170808Sdelphij if (altq->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) { 827170808Sdelphij default_class++; 828170808Sdelphij def = altq; 829170808Sdelphij } 830170808Sdelphij } 831170808Sdelphij if (default_class != 1) { 832170808Sdelphij warnx("should have one default queue on %s", pa->ifname); 833170808Sdelphij return (1); 834170808Sdelphij } 835170808Sdelphij /* make sure the default queue is a leaf */ 836170808Sdelphij TAILQ_FOREACH(altq, &altqs, entries) { 837170808Sdelphij if (strncmp(altq->ifname, pa->ifname, IFNAMSIZ) != 0) 838170808Sdelphij continue; 839171070Sdelphij if (altq->qname[0] == 0) /* this is for interface */ 840170808Sdelphij continue; 841170808Sdelphij if (strncmp(altq->parent, def->qname, PF_QNAME_SIZE) == 0) { 842170808Sdelphij warnx("default queue is not a leaf"); 843170808Sdelphij error++; 844170808Sdelphij } 845170808Sdelphij } 846171069Sdelphij return (error); 847170808Sdelphij} 848170808Sdelphij 849170808Sdelphijstatic int 850170808Sdelphijprint_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts) 851170808Sdelphij{ 852170808Sdelphij const struct hfsc_opts *opts; 853170808Sdelphij const struct node_hfsc_sc *rtsc, *lssc, *ulsc; 854170808Sdelphij 855170808Sdelphij opts = &a->pq_u.hfsc_opts; 856170808Sdelphij if (qopts == NULL) 857170808Sdelphij rtsc = lssc = ulsc = NULL; 858170808Sdelphij else { 859170808Sdelphij rtsc = &qopts->data.hfsc_opts.realtime; 860170808Sdelphij lssc = &qopts->data.hfsc_opts.linkshare; 861171799Sdelphij ulsc = &qopts->data.hfsc_opts.upperlimit; 862170808Sdelphij } 863170808Sdelphij 864176559Sattilio if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 || 865176559Sattilio (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || 866170808Sdelphij opts->lssc_d != 0))) { 867170808Sdelphij printf("hfsc("); 868170808Sdelphij if (opts->flags & HFCF_RED) 869171799Sdelphij printf(" red"); 870170808Sdelphij if (opts->flags & HFCF_ECN) 871170808Sdelphij printf(" ecn"); 872170808Sdelphij if (opts->flags & HFCF_RIO) 873170808Sdelphij printf(" rio"); 874170808Sdelphij if (opts->flags & HFCF_CLEARDSCP) 875170808Sdelphij printf(" cleardscp"); 876170808Sdelphij if (opts->flags & HFCF_DEFAULTCLASS) 877170808Sdelphij printf(" default"); 878170808Sdelphij if (opts->rtsc_m2 != 0) 879170808Sdelphij print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d, 880170808Sdelphij opts->rtsc_m2, rtsc); 881170808Sdelphij if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth || 882170808Sdelphij opts->lssc_d != 0)) 883170808Sdelphij print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d, 884170808Sdelphij opts->lssc_m2, lssc); 885170808Sdelphij if (opts->ulsc_m2 != 0) 886170808Sdelphij print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d, 887173725Sdelphij opts->ulsc_m2, ulsc); 888173725Sdelphij printf(" ) "); 889173725Sdelphij 890175202Sattilio return (1); 891173725Sdelphij } else 892173725Sdelphij return (0); 893173725Sdelphij} 894173725Sdelphij 895173725Sdelphij/* 896188318Skib * admission control using generalized service curve 897173725Sdelphij */ 898170808Sdelphij 899170808Sdelphij/* add a new service curve to a generalized service curve */ 900170808Sdelphijstatic void 901170808Sdelphijgsc_add_sc(struct gen_sc *gsc, struct service_curve *sc) 902173725Sdelphij{ 903170808Sdelphij if (is_sc_null(sc)) 904170808Sdelphij return; 905170808Sdelphij if (sc->d != 0) 906171070Sdelphij gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1); 907170808Sdelphij gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2); 908171070Sdelphij} 909170808Sdelphij 910170808Sdelphij/* 911170808Sdelphij * check whether all points of a generalized service curve have 912171799Sdelphij * their y-coordinates no larger than a given two-piece linear 913171070Sdelphij * service curve. 914170808Sdelphij */ 915170808Sdelphijstatic int 916170808Sdelphijis_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc) 917173725Sdelphij{ 918170808Sdelphij struct segment *s, *last, *end; 919170808Sdelphij double y; 920171799Sdelphij 921171799Sdelphij if (is_sc_null(sc)) { 922171799Sdelphij if (LIST_EMPTY(gsc)) 923173725Sdelphij return (1); 924171799Sdelphij LIST_FOREACH(s, gsc, _next) { 925171799Sdelphij if (s->m != 0) 926171799Sdelphij return (0); 927173725Sdelphij } 928171799Sdelphij return (1); 929171799Sdelphij } 930173725Sdelphij /* 931171799Sdelphij * gsc has a dummy entry at the end with x = INFINITY. 932171799Sdelphij * loop through up to this dummy entry. 933171799Sdelphij */ 934170808Sdelphij end = gsc_getentry(gsc, INFINITY); 935170808Sdelphij if (end == NULL) 936170808Sdelphij return (1); 937170808Sdelphij last = NULL; 938170808Sdelphij for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) { 939170808Sdelphij if (s->y > sc_x2y(sc, s->x)) 940170808Sdelphij return (0); 941170808Sdelphij last = s; 942170808Sdelphij } 943170808Sdelphij /* last now holds the real last segment */ 944170808Sdelphij if (last == NULL) 945170808Sdelphij return (1); 946183299Sobrien if (last->m > sc->m2) 947171087Sdelphij return (0); 948170808Sdelphij if (last->x < sc->d && last->m > sc->m1) { 949170808Sdelphij y = last->y + (sc->d - last->x) * last->m; 950170808Sdelphij if (y > sc_x2y(sc, sc->d)) 951170808Sdelphij return (0); 952170808Sdelphij } 953170808Sdelphij return (1); 954170808Sdelphij} 955170808Sdelphij 956170808Sdelphijstatic void 957170808Sdelphijgsc_destroy(struct gen_sc *gsc) 958170808Sdelphij{ 959170808Sdelphij struct segment *s; 960170808Sdelphij 961170808Sdelphij while ((s = LIST_FIRST(gsc)) != NULL) { 962170808Sdelphij LIST_REMOVE(s, _next); 963170808Sdelphij free(s); 964170808Sdelphij } 965170808Sdelphij} 966170808Sdelphij 967171087Sdelphij/* 968170808Sdelphij * return a segment entry starting at x. 969170808Sdelphij * if gsc has no entry starting at x, a new entry is created at x. 970170808Sdelphij */ 971170808Sdelphijstatic struct segment * 972170808Sdelphijgsc_getentry(struct gen_sc *gsc, double x) 973170808Sdelphij{ 974170808Sdelphij struct segment *new, *prev, *s; 975170808Sdelphij 976170808Sdelphij prev = NULL; 977170808Sdelphij LIST_FOREACH(s, gsc, _next) { 978170808Sdelphij if (s->x == x) 979170808Sdelphij return (s); /* matching entry found */ 980170808Sdelphij else if (s->x < x) 981170808Sdelphij prev = s; 982170808Sdelphij else 983170808Sdelphij break; 984170808Sdelphij } 985170808Sdelphij 986170808Sdelphij /* we have to create a new entry */ 987170808Sdelphij if ((new = calloc(1, sizeof(struct segment))) == NULL) 988170808Sdelphij return (NULL); 989170808Sdelphij 990170808Sdelphij new->x = x; 991170808Sdelphij if (x == INFINITY || s == NULL) 992170808Sdelphij new->d = 0; 993170808Sdelphij else if (s->x == INFINITY) 994170808Sdelphij new->d = INFINITY; 995171087Sdelphij else 996170808Sdelphij new->d = s->x - x; 997170808Sdelphij if (prev == NULL) { 998170808Sdelphij /* insert the new entry at the head of the list */ 999170808Sdelphij new->y = 0; 1000170808Sdelphij new->m = 0; 1001170808Sdelphij LIST_INSERT_HEAD(gsc, new, _next); 1002170808Sdelphij } else { 1003170808Sdelphij /* 1004170808Sdelphij * the start point intersects with the segment pointed by 1005170808Sdelphij * prev. divide prev into 2 segments 1006170808Sdelphij */ 1007170808Sdelphij if (x == INFINITY) { 1008188318Skib prev->d = INFINITY; 1009170808Sdelphij if (prev->m == 0) 1010170808Sdelphij new->y = prev->y; 1011170808Sdelphij else 1012170808Sdelphij new->y = INFINITY; 1013170808Sdelphij } else { 1014170808Sdelphij prev->d = x - prev->x; 1015170808Sdelphij new->y = prev->d * prev->m + prev->y; 1016170808Sdelphij } 1017170808Sdelphij new->m = prev->m; 1018170808Sdelphij LIST_INSERT_AFTER(prev, new, _next); 1019170808Sdelphij } 1020170808Sdelphij return (new); 1021175294Sattilio} 1022170808Sdelphij 1023170808Sdelphij/* add a segment to a generalized service curve */ 1024170808Sdelphijstatic int 1025170808Sdelphijgsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m) 1026170808Sdelphij{ 1027170808Sdelphij struct segment *start, *end, *s; 1028170808Sdelphij double x2; 1029170808Sdelphij 1030170808Sdelphij if (d == INFINITY) 1031170808Sdelphij x2 = INFINITY; 1032170808Sdelphij else 1033170808Sdelphij x2 = x + d; 1034170808Sdelphij start = gsc_getentry(gsc, x); 1035170808Sdelphij end = gsc_getentry(gsc, x2); 1036170808Sdelphij if (start == NULL || end == NULL) 1037170808Sdelphij return (-1); 1038170808Sdelphij 1039170808Sdelphij for (s = start; s != end; s = LIST_NEXT(s, _next)) { 1040170808Sdelphij s->m += m; 1041170808Sdelphij s->y += y + (s->x - x) * m; 1042170808Sdelphij } 1043171069Sdelphij 1044170808Sdelphij end = gsc_getentry(gsc, INFINITY); 1045170808Sdelphij for (; s != end; s = LIST_NEXT(s, _next)) { 1046170808Sdelphij s->y += m * d; 1047170808Sdelphij } 1048170808Sdelphij 1049170808Sdelphij return (0); 1050170808Sdelphij} 1051170808Sdelphij 1052170808Sdelphij/* get y-projection of a service curve */ 1053170808Sdelphijstatic double 1054170808Sdelphijsc_x2y(struct service_curve *sc, double x) 1055170808Sdelphij{ 1056170808Sdelphij double y; 1057170808Sdelphij 1058171069Sdelphij if (x <= (double)sc->d) 1059170808Sdelphij /* y belongs to the 1st segment */ 1060170808Sdelphij y = x * (double)sc->m1; 1061170808Sdelphij else 1062170808Sdelphij /* y belongs to the 2nd segment */ 1063170808Sdelphij y = (double)sc->d * (double)sc->m1 1064170808Sdelphij + (x - (double)sc->d) * (double)sc->m2; 1065170808Sdelphij return (y); 1066170808Sdelphij} 1067170808Sdelphij 1068170808Sdelphij/* 1069170808Sdelphij * misc utilities 1070176559Sattilio */ 1071176559Sattilio#define R2S_BUFS 8 1072170808Sdelphij#define RATESTR_MAX 16 1073170808Sdelphij 1074170808Sdelphijchar * 1075170808Sdelphijrate2str(double rate) 1076170808Sdelphij{ 1077171070Sdelphij char *buf; 1078171070Sdelphij static char r2sbuf[R2S_BUFS][RATESTR_MAX]; /* ring bufer */ 1079171070Sdelphij static int idx = 0; 1080171070Sdelphij int i; 1081171070Sdelphij static const char unit[] = " KMG"; 1082171070Sdelphij 1083170808Sdelphij buf = r2sbuf[idx++]; 1084170808Sdelphij if (idx == R2S_BUFS) 1085170808Sdelphij idx = 0; 1086170808Sdelphij 1087170808Sdelphij for (i = 0; rate >= 1000 && i <= 3; i++) 1088170808Sdelphij rate /= 1000; 1089170808Sdelphij 1090171070Sdelphij if ((int)(rate * 100) % 100) 1091171070Sdelphij snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]); 1092170808Sdelphij else 1093170808Sdelphij snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]); 1094170808Sdelphij 1095170808Sdelphij return (buf); 1096188318Skib} 1097170808Sdelphij 1098170808Sdelphij#ifdef __FreeBSD__ 1099170808Sdelphij/* 1100170808Sdelphij * XXX 1101170808Sdelphij * FreeBSD does not have SIOCGIFDATA. 1102170808Sdelphij * To emulate this, DIOCGIFSPEED ioctl added to pf. 1103170808Sdelphij */ 1104170808Sdelphiju_int32_t 1105170808Sdelphijgetifspeed(int pfdev, char *ifname) 1106170808Sdelphij{ 1107170808Sdelphij struct pf_ifspeed io; 1108170808Sdelphij 1109170808Sdelphij bzero(&io, sizeof io); 1110170808Sdelphij if (strlcpy(io.ifname, ifname, IFNAMSIZ) >= 1111170808Sdelphij sizeof(io.ifname)) 1112170808Sdelphij errx(1, "getifspeed: strlcpy"); 1113170808Sdelphij if (ioctl(pfdev, DIOCGIFSPEED, &io) == -1) 1114170808Sdelphij err(1, "DIOCGIFSPEED"); 1115170808Sdelphij return ((u_int32_t)io.baudrate); 1116170808Sdelphij} 1117170808Sdelphij#else 1118171070Sdelphiju_int32_t 1119170808Sdelphijgetifspeed(char *ifname) 1120170808Sdelphij{ 1121170808Sdelphij int s; 1122170808Sdelphij struct ifreq ifr; 1123170808Sdelphij struct if_data ifrdat; 1124170808Sdelphij 1125170808Sdelphij if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1126170808Sdelphij err(1, "socket"); 1127170808Sdelphij bzero(&ifr, sizeof(ifr)); 1128170808Sdelphij if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 1129170808Sdelphij sizeof(ifr.ifr_name)) 1130170808Sdelphij errx(1, "getifspeed: strlcpy"); 1131170808Sdelphij ifr.ifr_data = (caddr_t)&ifrdat; 1132170808Sdelphij if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1) 1133170808Sdelphij err(1, "SIOCGIFDATA"); 1134170808Sdelphij if (close(s)) 1135170808Sdelphij err(1, "close"); 1136170808Sdelphij return ((u_int32_t)ifrdat.ifi_baudrate); 1137170808Sdelphij} 1138170808Sdelphij#endif 1139170808Sdelphij 1140171069Sdelphiju_long 1141170808Sdelphijgetifmtu(char *ifname) 1142170808Sdelphij{ 1143170808Sdelphij int s; 1144170808Sdelphij struct ifreq ifr; 1145170808Sdelphij 1146170808Sdelphij if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) 1147170808Sdelphij err(1, "socket"); 1148170808Sdelphij bzero(&ifr, sizeof(ifr)); 1149170808Sdelphij if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >= 1150170808Sdelphij sizeof(ifr.ifr_name)) 1151170808Sdelphij errx(1, "getifmtu: strlcpy"); 1152170808Sdelphij if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1) 1153170808Sdelphij#ifdef __FreeBSD__ 1154170808Sdelphij ifr.ifr_mtu = 1500; 1155170808Sdelphij#else 1156170808Sdelphij err(1, "SIOCGIFMTU"); 1157170808Sdelphij#endif 1158170808Sdelphij if (close(s)) 1159170808Sdelphij err(1, "close"); 1160171069Sdelphij if (ifr.ifr_mtu > 0) 1161170808Sdelphij return (ifr.ifr_mtu); 1162170808Sdelphij else { 1163170808Sdelphij warnx("could not get mtu for %s, assuming 1500", ifname); 1164170808Sdelphij return (1500); 1165170808Sdelphij } 1166170808Sdelphij} 1167170808Sdelphij 1168170808Sdelphijint 1169170808Sdelphijeval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts, 1170170808Sdelphij u_int32_t ref_bw) 1171171802Sdelphij{ 1172170808Sdelphij int errors = 0; 1173170808Sdelphij 1174170808Sdelphij switch (pa->scheduler) { 1175171802Sdelphij case ALTQT_CBQ: 1176171802Sdelphij pa->pq_u.cbq_opts = opts->data.cbq_opts; 1177170808Sdelphij break; 1178170808Sdelphij case ALTQT_PRIQ: 1179170808Sdelphij pa->pq_u.priq_opts = opts->data.priq_opts; 1180170808Sdelphij break; 1181170808Sdelphij case ALTQT_HFSC: 1182171862Sdelphij pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags; 1183170808Sdelphij if (opts->data.hfsc_opts.linkshare.used) { 1184171862Sdelphij pa->pq_u.hfsc_opts.lssc_m1 = 1185171862Sdelphij eval_bwspec(&opts->data.hfsc_opts.linkshare.m1, 1186171862Sdelphij ref_bw); 1187171862Sdelphij pa->pq_u.hfsc_opts.lssc_m2 = 1188171862Sdelphij eval_bwspec(&opts->data.hfsc_opts.linkshare.m2, 1189171862Sdelphij ref_bw); 1190170808Sdelphij pa->pq_u.hfsc_opts.lssc_d = 1191171862Sdelphij opts->data.hfsc_opts.linkshare.d; 1192171862Sdelphij } 1193171862Sdelphij if (opts->data.hfsc_opts.realtime.used) { 1194170808Sdelphij pa->pq_u.hfsc_opts.rtsc_m1 = 1195170808Sdelphij eval_bwspec(&opts->data.hfsc_opts.realtime.m1, 1196171862Sdelphij ref_bw); 1197171862Sdelphij pa->pq_u.hfsc_opts.rtsc_m2 = 1198171862Sdelphij eval_bwspec(&opts->data.hfsc_opts.realtime.m2, 1199171862Sdelphij ref_bw); 1200171862Sdelphij pa->pq_u.hfsc_opts.rtsc_d = 1201170808Sdelphij opts->data.hfsc_opts.realtime.d; 1202170808Sdelphij } 1203170808Sdelphij if (opts->data.hfsc_opts.upperlimit.used) { 1204170808Sdelphij pa->pq_u.hfsc_opts.ulsc_m1 = 1205170808Sdelphij eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1, 1206170808Sdelphij ref_bw); 1207170808Sdelphij pa->pq_u.hfsc_opts.ulsc_m2 = 1208170808Sdelphij eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2, 1209170808Sdelphij ref_bw); 1210170808Sdelphij pa->pq_u.hfsc_opts.ulsc_d = 1211170808Sdelphij opts->data.hfsc_opts.upperlimit.d; 1212170808Sdelphij } 1213170808Sdelphij break; 1214170808Sdelphij default: 1215170808Sdelphij warnx("eval_queue_opts: unknown scheduler type %u", 1216170808Sdelphij opts->qtype); 1217170808Sdelphij errors++; 1218170808Sdelphij break; 1219170808Sdelphij } 1220170808Sdelphij 1221170808Sdelphij return (errors); 1222170808Sdelphij} 1223170808Sdelphij 1224170808Sdelphiju_int32_t 1225170808Sdelphijeval_bwspec(struct node_queue_bw *bw, u_int32_t ref_bw) 1226170808Sdelphij{ 1227170808Sdelphij if (bw->bw_absolute > 0) 1228170808Sdelphij return (bw->bw_absolute); 1229170808Sdelphij 1230170808Sdelphij if (bw->bw_percent > 0) 1231170808Sdelphij return (ref_bw / 100 * bw->bw_percent); 1232171802Sdelphij 1233170808Sdelphij return (0); 1234171802Sdelphij} 1235171802Sdelphij 1236170808Sdelphijvoid 1237170808Sdelphijprint_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2, 1238170808Sdelphij const struct node_hfsc_sc *sc) 1239170808Sdelphij{ 1240170808Sdelphij printf(" %s", scname); 1241170808Sdelphij 1242170808Sdelphij if (d != 0) { 1243170808Sdelphij printf("("); 1244170808Sdelphij if (sc != NULL && sc->m1.bw_percent > 0) 1245170808Sdelphij printf("%u%%", sc->m1.bw_percent); 1246170808Sdelphij else 1247170808Sdelphij printf("%s", rate2str((double)m1)); 1248171069Sdelphij printf(" %u", d); 1249170808Sdelphij } 1250170808Sdelphij 1251170808Sdelphij if (sc != NULL && sc->m2.bw_percent > 0) 1252170808Sdelphij printf(" %u%%", sc->m2.bw_percent); 1253170808Sdelphij else 1254170808Sdelphij printf(" %s", rate2str((double)m2)); 1255170808Sdelphij 1256170808Sdelphij if (d != 0) 1257170808Sdelphij printf(")"); 1258170808Sdelphij} 1259170808Sdelphij