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