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