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