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