xref: /freebsd/sbin/pfctl/pfctl_altq.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
13b3a8eb9SGleb Smirnoff /*	$OpenBSD: pfctl_altq.c,v 1.93 2007/10/15 02:16:35 deraadt Exp $	*/
23b3a8eb9SGleb Smirnoff 
33b3a8eb9SGleb Smirnoff /*
43b3a8eb9SGleb Smirnoff  * Copyright (c) 2002
53b3a8eb9SGleb Smirnoff  *	Sony Computer Science Laboratories Inc.
63b3a8eb9SGleb Smirnoff  * Copyright (c) 2002, 2003 Henning Brauer <henning@openbsd.org>
73b3a8eb9SGleb Smirnoff  *
83b3a8eb9SGleb Smirnoff  * Permission to use, copy, modify, and distribute this software for any
93b3a8eb9SGleb Smirnoff  * purpose with or without fee is hereby granted, provided that the above
103b3a8eb9SGleb Smirnoff  * copyright notice and this permission notice appear in all copies.
113b3a8eb9SGleb Smirnoff  *
123b3a8eb9SGleb Smirnoff  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
133b3a8eb9SGleb Smirnoff  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
143b3a8eb9SGleb Smirnoff  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
153b3a8eb9SGleb Smirnoff  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
163b3a8eb9SGleb Smirnoff  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
173b3a8eb9SGleb Smirnoff  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
183b3a8eb9SGleb Smirnoff  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
193b3a8eb9SGleb Smirnoff  */
203b3a8eb9SGleb Smirnoff 
213b3a8eb9SGleb Smirnoff #include <sys/cdefs.h>
22249cc75fSPatrick Kelsey #define PFIOC_USE_LATEST
235e04571cSStefan Eßer #define _WANT_FREEBSD_BITSET
24249cc75fSPatrick Kelsey 
253b3a8eb9SGleb Smirnoff #include <sys/types.h>
261d34c9daSPatrick Kelsey #include <sys/bitset.h>
273b3a8eb9SGleb Smirnoff #include <sys/ioctl.h>
283b3a8eb9SGleb Smirnoff #include <sys/socket.h>
293b3a8eb9SGleb Smirnoff 
303b3a8eb9SGleb Smirnoff #include <net/if.h>
313b3a8eb9SGleb Smirnoff #include <netinet/in.h>
323b3a8eb9SGleb Smirnoff #include <net/pfvar.h>
333b3a8eb9SGleb Smirnoff 
343b3a8eb9SGleb Smirnoff #include <err.h>
353b3a8eb9SGleb Smirnoff #include <errno.h>
36249cc75fSPatrick Kelsey #include <inttypes.h>
373b3a8eb9SGleb Smirnoff #include <limits.h>
383b3a8eb9SGleb Smirnoff #include <math.h>
391d34c9daSPatrick Kelsey #include <search.h>
403b3a8eb9SGleb Smirnoff #include <stdio.h>
413b3a8eb9SGleb Smirnoff #include <stdlib.h>
423b3a8eb9SGleb Smirnoff #include <string.h>
433b3a8eb9SGleb Smirnoff #include <unistd.h>
443b3a8eb9SGleb Smirnoff 
45772e66a6SGleb Smirnoff #include <net/altq/altq.h>
46772e66a6SGleb Smirnoff #include <net/altq/altq_cbq.h>
470a70aaf8SLuiz Otavio O Souza #include <net/altq/altq_codel.h>
48772e66a6SGleb Smirnoff #include <net/altq/altq_priq.h>
49772e66a6SGleb Smirnoff #include <net/altq/altq_hfsc.h>
50a5b789f6SErmal Luçi #include <net/altq/altq_fairq.h>
513b3a8eb9SGleb Smirnoff 
523b3a8eb9SGleb Smirnoff #include "pfctl_parser.h"
533b3a8eb9SGleb Smirnoff #include "pfctl.h"
543b3a8eb9SGleb Smirnoff 
553b3a8eb9SGleb Smirnoff #define is_sc_null(sc)	(((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
563b3a8eb9SGleb Smirnoff 
571d34c9daSPatrick Kelsey static STAILQ_HEAD(interfaces, pfctl_altq) interfaces = STAILQ_HEAD_INITIALIZER(interfaces);
581d34c9daSPatrick Kelsey static struct hsearch_data queue_map;
591d34c9daSPatrick Kelsey static struct hsearch_data if_map;
601d34c9daSPatrick Kelsey static struct hsearch_data qid_map;
613b3a8eb9SGleb Smirnoff 
621d34c9daSPatrick Kelsey static struct pfctl_altq *pfaltq_lookup(char *ifname);
631d34c9daSPatrick Kelsey static struct pfctl_altq *qname_to_pfaltq(const char *, const char *);
641d34c9daSPatrick Kelsey static u_int32_t	 qname_to_qid(char *);
653b3a8eb9SGleb Smirnoff 
661d34c9daSPatrick Kelsey static int	eval_pfqueue_cbq(struct pfctl *, struct pf_altq *,
671d34c9daSPatrick Kelsey 		    struct pfctl_altq *);
683b3a8eb9SGleb Smirnoff static int	cbq_compute_idletime(struct pfctl *, struct pf_altq *);
691d34c9daSPatrick Kelsey static int	check_commit_cbq(int, int, struct pfctl_altq *);
703b3a8eb9SGleb Smirnoff static int	print_cbq_opts(const struct pf_altq *);
713b3a8eb9SGleb Smirnoff 
720a70aaf8SLuiz Otavio O Souza static int	print_codel_opts(const struct pf_altq *,
730a70aaf8SLuiz Otavio O Souza 		    const struct node_queue_opt *);
740a70aaf8SLuiz Otavio O Souza 
751d34c9daSPatrick Kelsey static int	eval_pfqueue_priq(struct pfctl *, struct pf_altq *,
761d34c9daSPatrick Kelsey 		    struct pfctl_altq *);
771d34c9daSPatrick Kelsey static int	check_commit_priq(int, int, struct pfctl_altq *);
783b3a8eb9SGleb Smirnoff static int	print_priq_opts(const struct pf_altq *);
793b3a8eb9SGleb Smirnoff 
801d34c9daSPatrick Kelsey static int	eval_pfqueue_hfsc(struct pfctl *, struct pf_altq *,
811d34c9daSPatrick Kelsey 		    struct pfctl_altq *, struct pfctl_altq *);
821d34c9daSPatrick Kelsey static int	check_commit_hfsc(int, int, struct pfctl_altq *);
833b3a8eb9SGleb Smirnoff static int	print_hfsc_opts(const struct pf_altq *,
843b3a8eb9SGleb Smirnoff 		    const struct node_queue_opt *);
853b3a8eb9SGleb Smirnoff 
861d34c9daSPatrick Kelsey static int	eval_pfqueue_fairq(struct pfctl *, struct pf_altq *,
871d34c9daSPatrick Kelsey 		    struct pfctl_altq *, struct pfctl_altq *);
88a5b789f6SErmal Luçi static int	print_fairq_opts(const struct pf_altq *,
89a5b789f6SErmal Luçi 		    const struct node_queue_opt *);
901d34c9daSPatrick Kelsey static int	check_commit_fairq(int, int, struct pfctl_altq *);
91a5b789f6SErmal Luçi 
923b3a8eb9SGleb Smirnoff static void		 gsc_add_sc(struct gen_sc *, struct service_curve *);
933b3a8eb9SGleb Smirnoff static int		 is_gsc_under_sc(struct gen_sc *,
943b3a8eb9SGleb Smirnoff 			     struct service_curve *);
953b3a8eb9SGleb Smirnoff static struct segment	*gsc_getentry(struct gen_sc *, double);
963b3a8eb9SGleb Smirnoff static int		 gsc_add_seg(struct gen_sc *, double, double, double,
973b3a8eb9SGleb Smirnoff 			     double);
983b3a8eb9SGleb Smirnoff static double		 sc_x2y(struct service_curve *, double);
993b3a8eb9SGleb Smirnoff 
1003b3a8eb9SGleb Smirnoff u_int32_t	 getifspeed(char *);
1013b3a8eb9SGleb Smirnoff u_long		 getifmtu(char *);
1023b3a8eb9SGleb Smirnoff int		 eval_queue_opts(struct pf_altq *, struct node_queue_opt *,
103249cc75fSPatrick Kelsey 		     u_int64_t);
104249cc75fSPatrick Kelsey u_int64_t	 eval_bwspec(struct node_queue_bw *, u_int64_t);
1053b3a8eb9SGleb Smirnoff void		 print_hfsc_sc(const char *, u_int, u_int, u_int,
1063b3a8eb9SGleb Smirnoff 		     const struct node_hfsc_sc *);
107a5b789f6SErmal Luçi void		 print_fairq_sc(const char *, u_int, u_int, u_int,
108a5b789f6SErmal Luçi 		     const struct node_fairq_sc *);
1093b3a8eb9SGleb Smirnoff 
1101d34c9daSPatrick Kelsey static __attribute__((constructor)) void
pfctl_altq_init(void)1111d34c9daSPatrick Kelsey pfctl_altq_init(void)
1121d34c9daSPatrick Kelsey {
1131d34c9daSPatrick Kelsey 	/*
1141d34c9daSPatrick Kelsey 	 * As hdestroy() will never be called on these tables, it will be
1151d34c9daSPatrick Kelsey 	 * safe to use references into the stored data as keys.
1161d34c9daSPatrick Kelsey 	 */
1171d34c9daSPatrick Kelsey 	if (hcreate_r(0, &queue_map) == 0)
1181d34c9daSPatrick Kelsey 		err(1, "Failed to create altq queue map");
1191d34c9daSPatrick Kelsey 	if (hcreate_r(0, &if_map) == 0)
1201d34c9daSPatrick Kelsey 		err(1, "Failed to create altq interface map");
1211d34c9daSPatrick Kelsey 	if (hcreate_r(0, &qid_map) == 0)
1221d34c9daSPatrick Kelsey 		err(1, "Failed to create altq queue id map");
1231d34c9daSPatrick Kelsey }
1241d34c9daSPatrick Kelsey 
1253b3a8eb9SGleb Smirnoff void
pfaltq_store(struct pf_altq * a)1263b3a8eb9SGleb Smirnoff pfaltq_store(struct pf_altq *a)
1273b3a8eb9SGleb Smirnoff {
1281d34c9daSPatrick Kelsey 	struct pfctl_altq	*altq;
1291d34c9daSPatrick Kelsey 	ENTRY 			 item;
1301d34c9daSPatrick Kelsey 	ENTRY			*ret_item;
1311d34c9daSPatrick Kelsey 	size_t			 key_size;
1323b3a8eb9SGleb Smirnoff 
1333b3a8eb9SGleb Smirnoff 	if ((altq = malloc(sizeof(*altq))) == NULL)
1341d34c9daSPatrick Kelsey 		err(1, "queue malloc");
1351d34c9daSPatrick Kelsey 	memcpy(&altq->pa, a, sizeof(struct pf_altq));
1361d34c9daSPatrick Kelsey 	memset(&altq->meta, 0, sizeof(altq->meta));
1371d34c9daSPatrick Kelsey 
1381d34c9daSPatrick Kelsey 	if (a->qname[0] == 0) {
1391d34c9daSPatrick Kelsey 		item.key = altq->pa.ifname;
1401d34c9daSPatrick Kelsey 		item.data = altq;
1411d34c9daSPatrick Kelsey 		if (hsearch_r(item, ENTER, &ret_item, &if_map) == 0)
1421d34c9daSPatrick Kelsey 			err(1, "interface map insert");
1431d34c9daSPatrick Kelsey 		STAILQ_INSERT_TAIL(&interfaces, altq, meta.link);
1441d34c9daSPatrick Kelsey 	} else {
1451d34c9daSPatrick Kelsey 		key_size = sizeof(a->ifname) + sizeof(a->qname);
1461d34c9daSPatrick Kelsey 		if ((item.key = malloc(key_size)) == NULL)
1471d34c9daSPatrick Kelsey 			err(1, "queue map key malloc");
1481d34c9daSPatrick Kelsey 		snprintf(item.key, key_size, "%s:%s", a->ifname, a->qname);
1491d34c9daSPatrick Kelsey 		item.data = altq;
1501d34c9daSPatrick Kelsey 		if (hsearch_r(item, ENTER, &ret_item, &queue_map) == 0)
1511d34c9daSPatrick Kelsey 			err(1, "queue map insert");
1521d34c9daSPatrick Kelsey 
1531d34c9daSPatrick Kelsey 		item.key = altq->pa.qname;
1541d34c9daSPatrick Kelsey 		item.data = &altq->pa.qid;
1551d34c9daSPatrick Kelsey 		if (hsearch_r(item, ENTER, &ret_item, &qid_map) == 0)
1561d34c9daSPatrick Kelsey 			err(1, "qid map insert");
1571d34c9daSPatrick Kelsey 	}
1583b3a8eb9SGleb Smirnoff }
1593b3a8eb9SGleb Smirnoff 
1601d34c9daSPatrick Kelsey static struct pfctl_altq *
pfaltq_lookup(char * ifname)1611d34c9daSPatrick Kelsey pfaltq_lookup(char *ifname)
1623b3a8eb9SGleb Smirnoff {
1631d34c9daSPatrick Kelsey 	ENTRY	 item;
1641d34c9daSPatrick Kelsey 	ENTRY	*ret_item;
1653b3a8eb9SGleb Smirnoff 
1661d34c9daSPatrick Kelsey 	item.key = ifname;
1671d34c9daSPatrick Kelsey 	if (hsearch_r(item, FIND, &ret_item, &if_map) == 0)
1683b3a8eb9SGleb Smirnoff 		return (NULL);
1691d34c9daSPatrick Kelsey 
1701d34c9daSPatrick Kelsey 	return (ret_item->data);
1713b3a8eb9SGleb Smirnoff }
1723b3a8eb9SGleb Smirnoff 
1731d34c9daSPatrick Kelsey static struct pfctl_altq *
qname_to_pfaltq(const char * qname,const char * ifname)1743b3a8eb9SGleb Smirnoff qname_to_pfaltq(const char *qname, const char *ifname)
1753b3a8eb9SGleb Smirnoff {
1761d34c9daSPatrick Kelsey 	ENTRY	 item;
1771d34c9daSPatrick Kelsey 	ENTRY	*ret_item;
1781d34c9daSPatrick Kelsey 	char	 key[IFNAMSIZ + PF_QNAME_SIZE];
1793b3a8eb9SGleb Smirnoff 
1801d34c9daSPatrick Kelsey 	item.key = key;
1811d34c9daSPatrick Kelsey 	snprintf(item.key, sizeof(key), "%s:%s", ifname, qname);
1821d34c9daSPatrick Kelsey 	if (hsearch_r(item, FIND, &ret_item, &queue_map) == 0)
1833b3a8eb9SGleb Smirnoff 		return (NULL);
1841d34c9daSPatrick Kelsey 
1851d34c9daSPatrick Kelsey 	return (ret_item->data);
1863b3a8eb9SGleb Smirnoff }
1873b3a8eb9SGleb Smirnoff 
1881d34c9daSPatrick Kelsey static u_int32_t
qname_to_qid(char * qname)1891d34c9daSPatrick Kelsey qname_to_qid(char *qname)
1903b3a8eb9SGleb Smirnoff {
1911d34c9daSPatrick Kelsey 	ENTRY	 item;
1921d34c9daSPatrick Kelsey 	ENTRY	*ret_item;
1931d34c9daSPatrick Kelsey 	uint32_t qid;
1943b3a8eb9SGleb Smirnoff 
1953b3a8eb9SGleb Smirnoff 	/*
1963b3a8eb9SGleb Smirnoff 	 * We guarantee that same named queues on different interfaces
1971d34c9daSPatrick Kelsey 	 * have the same qid.
1983b3a8eb9SGleb Smirnoff 	 */
1991d34c9daSPatrick Kelsey 	item.key = qname;
2001d34c9daSPatrick Kelsey 	if (hsearch_r(item, FIND, &ret_item, &qid_map) == 0)
2013b3a8eb9SGleb Smirnoff 		return (0);
2021d34c9daSPatrick Kelsey 
2031d34c9daSPatrick Kelsey 	qid = *(uint32_t *)ret_item->data;
2041d34c9daSPatrick Kelsey 	return (qid);
2053b3a8eb9SGleb Smirnoff }
2063b3a8eb9SGleb Smirnoff 
2073b3a8eb9SGleb Smirnoff void
print_altq(const struct pf_altq * a,unsigned int level,struct node_queue_bw * bw,struct node_queue_opt * qopts)2083b3a8eb9SGleb Smirnoff print_altq(const struct pf_altq *a, unsigned int level,
2093b3a8eb9SGleb Smirnoff     struct node_queue_bw *bw, struct node_queue_opt *qopts)
2103b3a8eb9SGleb Smirnoff {
2113b3a8eb9SGleb Smirnoff 	if (a->qname[0] != 0) {
2123b3a8eb9SGleb Smirnoff 		print_queue(a, level, bw, 1, qopts);
2133b3a8eb9SGleb Smirnoff 		return;
2143b3a8eb9SGleb Smirnoff 	}
2153b3a8eb9SGleb Smirnoff 
2163b3a8eb9SGleb Smirnoff #ifdef __FreeBSD__
2173b3a8eb9SGleb Smirnoff 	if (a->local_flags & PFALTQ_FLAG_IF_REMOVED)
2183b3a8eb9SGleb Smirnoff 		printf("INACTIVE ");
2193b3a8eb9SGleb Smirnoff #endif
2203b3a8eb9SGleb Smirnoff 
2213b3a8eb9SGleb Smirnoff 	printf("altq on %s ", a->ifname);
2223b3a8eb9SGleb Smirnoff 
2233b3a8eb9SGleb Smirnoff 	switch (a->scheduler) {
2243b3a8eb9SGleb Smirnoff 	case ALTQT_CBQ:
2253b3a8eb9SGleb Smirnoff 		if (!print_cbq_opts(a))
2263b3a8eb9SGleb Smirnoff 			printf("cbq ");
2273b3a8eb9SGleb Smirnoff 		break;
2283b3a8eb9SGleb Smirnoff 	case ALTQT_PRIQ:
2293b3a8eb9SGleb Smirnoff 		if (!print_priq_opts(a))
2303b3a8eb9SGleb Smirnoff 			printf("priq ");
2313b3a8eb9SGleb Smirnoff 		break;
2323b3a8eb9SGleb Smirnoff 	case ALTQT_HFSC:
2333b3a8eb9SGleb Smirnoff 		if (!print_hfsc_opts(a, qopts))
2343b3a8eb9SGleb Smirnoff 			printf("hfsc ");
2353b3a8eb9SGleb Smirnoff 		break;
236a5b789f6SErmal Luçi 	case ALTQT_FAIRQ:
237a5b789f6SErmal Luçi 		if (!print_fairq_opts(a, qopts))
238a5b789f6SErmal Luçi 			printf("fairq ");
239a5b789f6SErmal Luçi 		break;
2400a70aaf8SLuiz Otavio O Souza 	case ALTQT_CODEL:
2410a70aaf8SLuiz Otavio O Souza 		if (!print_codel_opts(a, qopts))
2420a70aaf8SLuiz Otavio O Souza 			printf("codel ");
2430a70aaf8SLuiz Otavio O Souza 		break;
2443b3a8eb9SGleb Smirnoff 	}
2453b3a8eb9SGleb Smirnoff 
2463b3a8eb9SGleb Smirnoff 	if (bw != NULL && bw->bw_percent > 0) {
2473b3a8eb9SGleb Smirnoff 		if (bw->bw_percent < 100)
2483b3a8eb9SGleb Smirnoff 			printf("bandwidth %u%% ", bw->bw_percent);
2493b3a8eb9SGleb Smirnoff 	} else
2503b3a8eb9SGleb Smirnoff 		printf("bandwidth %s ", rate2str((double)a->ifbandwidth));
2513b3a8eb9SGleb Smirnoff 
2523b3a8eb9SGleb Smirnoff 	if (a->qlimit != DEFAULT_QLIMIT)
2533b3a8eb9SGleb Smirnoff 		printf("qlimit %u ", a->qlimit);
2543b3a8eb9SGleb Smirnoff 	printf("tbrsize %u ", a->tbrsize);
2553b3a8eb9SGleb Smirnoff }
2563b3a8eb9SGleb Smirnoff 
2573b3a8eb9SGleb Smirnoff void
print_queue(const struct pf_altq * a,unsigned int level,struct node_queue_bw * bw,int print_interface,struct node_queue_opt * qopts)2583b3a8eb9SGleb Smirnoff print_queue(const struct pf_altq *a, unsigned int level,
2593b3a8eb9SGleb Smirnoff     struct node_queue_bw *bw, int print_interface,
2603b3a8eb9SGleb Smirnoff     struct node_queue_opt *qopts)
2613b3a8eb9SGleb Smirnoff {
2623b3a8eb9SGleb Smirnoff 	unsigned int	i;
2633b3a8eb9SGleb Smirnoff 
2643b3a8eb9SGleb Smirnoff #ifdef __FreeBSD__
2653b3a8eb9SGleb Smirnoff 	if (a->local_flags & PFALTQ_FLAG_IF_REMOVED)
2663b3a8eb9SGleb Smirnoff 		printf("INACTIVE ");
2673b3a8eb9SGleb Smirnoff #endif
2683b3a8eb9SGleb Smirnoff 	printf("queue ");
2693b3a8eb9SGleb Smirnoff 	for (i = 0; i < level; ++i)
2703b3a8eb9SGleb Smirnoff 		printf(" ");
2713b3a8eb9SGleb Smirnoff 	printf("%s ", a->qname);
2723b3a8eb9SGleb Smirnoff 	if (print_interface)
2733b3a8eb9SGleb Smirnoff 		printf("on %s ", a->ifname);
274a5b789f6SErmal Luçi 	if (a->scheduler == ALTQT_CBQ || a->scheduler == ALTQT_HFSC ||
275a5b789f6SErmal Luçi 		a->scheduler == ALTQT_FAIRQ) {
2763b3a8eb9SGleb Smirnoff 		if (bw != NULL && bw->bw_percent > 0) {
2773b3a8eb9SGleb Smirnoff 			if (bw->bw_percent < 100)
2783b3a8eb9SGleb Smirnoff 				printf("bandwidth %u%% ", bw->bw_percent);
2793b3a8eb9SGleb Smirnoff 		} else
2803b3a8eb9SGleb Smirnoff 			printf("bandwidth %s ", rate2str((double)a->bandwidth));
2813b3a8eb9SGleb Smirnoff 	}
2823b3a8eb9SGleb Smirnoff 	if (a->priority != DEFAULT_PRIORITY)
2833b3a8eb9SGleb Smirnoff 		printf("priority %u ", a->priority);
2843b3a8eb9SGleb Smirnoff 	if (a->qlimit != DEFAULT_QLIMIT)
2853b3a8eb9SGleb Smirnoff 		printf("qlimit %u ", a->qlimit);
2863b3a8eb9SGleb Smirnoff 	switch (a->scheduler) {
2873b3a8eb9SGleb Smirnoff 	case ALTQT_CBQ:
2883b3a8eb9SGleb Smirnoff 		print_cbq_opts(a);
2893b3a8eb9SGleb Smirnoff 		break;
2903b3a8eb9SGleb Smirnoff 	case ALTQT_PRIQ:
2913b3a8eb9SGleb Smirnoff 		print_priq_opts(a);
2923b3a8eb9SGleb Smirnoff 		break;
2933b3a8eb9SGleb Smirnoff 	case ALTQT_HFSC:
2943b3a8eb9SGleb Smirnoff 		print_hfsc_opts(a, qopts);
2953b3a8eb9SGleb Smirnoff 		break;
296a5b789f6SErmal Luçi 	case ALTQT_FAIRQ:
297a5b789f6SErmal Luçi 		print_fairq_opts(a, qopts);
298a5b789f6SErmal Luçi 		break;
2993b3a8eb9SGleb Smirnoff 	}
3003b3a8eb9SGleb Smirnoff }
3013b3a8eb9SGleb Smirnoff 
3023b3a8eb9SGleb Smirnoff /*
3033b3a8eb9SGleb Smirnoff  * eval_pfaltq computes the discipline parameters.
3043b3a8eb9SGleb Smirnoff  */
3053b3a8eb9SGleb Smirnoff int
eval_pfaltq(struct pfctl * pf,struct pf_altq * pa,struct node_queue_bw * bw,struct node_queue_opt * opts)3063b3a8eb9SGleb Smirnoff eval_pfaltq(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
3073b3a8eb9SGleb Smirnoff     struct node_queue_opt *opts)
3083b3a8eb9SGleb Smirnoff {
309249cc75fSPatrick Kelsey 	u_int64_t	rate;
310249cc75fSPatrick Kelsey 	u_int		size, errors = 0;
3113b3a8eb9SGleb Smirnoff 
3123b3a8eb9SGleb Smirnoff 	if (bw->bw_absolute > 0)
3133b3a8eb9SGleb Smirnoff 		pa->ifbandwidth = bw->bw_absolute;
3143b3a8eb9SGleb Smirnoff 	else
3153b3a8eb9SGleb Smirnoff 		if ((rate = getifspeed(pa->ifname)) == 0) {
3163b3a8eb9SGleb Smirnoff 			fprintf(stderr, "interface %s does not know its bandwidth, "
3173b3a8eb9SGleb Smirnoff 			    "please specify an absolute bandwidth\n",
3183b3a8eb9SGleb Smirnoff 			    pa->ifname);
3193b3a8eb9SGleb Smirnoff 			errors++;
3203b3a8eb9SGleb Smirnoff 		} else if ((pa->ifbandwidth = eval_bwspec(bw, rate)) == 0)
3213b3a8eb9SGleb Smirnoff 			pa->ifbandwidth = rate;
3223b3a8eb9SGleb Smirnoff 
323249cc75fSPatrick Kelsey 	/*
324249cc75fSPatrick Kelsey 	 * Limit bandwidth to UINT_MAX for schedulers that aren't 64-bit ready.
325249cc75fSPatrick Kelsey 	 */
326249cc75fSPatrick Kelsey 	if ((pa->scheduler != ALTQT_HFSC) && (pa->ifbandwidth > UINT_MAX)) {
327249cc75fSPatrick Kelsey 		pa->ifbandwidth = UINT_MAX;
328249cc75fSPatrick Kelsey 		warnx("interface %s bandwidth limited to %" PRIu64 " bps "
329249cc75fSPatrick Kelsey 		    "because selected scheduler is 32-bit limited\n", pa->ifname,
330249cc75fSPatrick Kelsey 		    pa->ifbandwidth);
331249cc75fSPatrick Kelsey 	}
3323b3a8eb9SGleb Smirnoff 	errors += eval_queue_opts(pa, opts, pa->ifbandwidth);
3333b3a8eb9SGleb Smirnoff 
3343b3a8eb9SGleb Smirnoff 	/* if tbrsize is not specified, use heuristics */
3353b3a8eb9SGleb Smirnoff 	if (pa->tbrsize == 0) {
3363b3a8eb9SGleb Smirnoff 		rate = pa->ifbandwidth;
3373b3a8eb9SGleb Smirnoff 		if (rate <= 1 * 1000 * 1000)
3383b3a8eb9SGleb Smirnoff 			size = 1;
3393b3a8eb9SGleb Smirnoff 		else if (rate <= 10 * 1000 * 1000)
3403b3a8eb9SGleb Smirnoff 			size = 4;
3413b3a8eb9SGleb Smirnoff 		else if (rate <= 200 * 1000 * 1000)
3423b3a8eb9SGleb Smirnoff 			size = 8;
343ef9afde4SPatrick Kelsey 		else if (rate <= 2500 * 1000 * 1000ULL)
3443b3a8eb9SGleb Smirnoff 			size = 24;
345ff7b3f73SPatrick Kelsey 		else
346ff7b3f73SPatrick Kelsey 			size = 128;
3473b3a8eb9SGleb Smirnoff 		size = size * getifmtu(pa->ifname);
3483b3a8eb9SGleb Smirnoff 		pa->tbrsize = size;
3493b3a8eb9SGleb Smirnoff 	}
3503b3a8eb9SGleb Smirnoff 	return (errors);
3513b3a8eb9SGleb Smirnoff }
3523b3a8eb9SGleb Smirnoff 
3533b3a8eb9SGleb Smirnoff /*
3543b3a8eb9SGleb Smirnoff  * check_commit_altq does consistency check for each interface
3553b3a8eb9SGleb Smirnoff  */
3563b3a8eb9SGleb Smirnoff int
check_commit_altq(int dev,int opts)3573b3a8eb9SGleb Smirnoff check_commit_altq(int dev, int opts)
3583b3a8eb9SGleb Smirnoff {
3591d34c9daSPatrick Kelsey 	struct pfctl_altq	*if_ppa;
3603b3a8eb9SGleb Smirnoff 	int			 error = 0;
3613b3a8eb9SGleb Smirnoff 
3623b3a8eb9SGleb Smirnoff 	/* call the discipline check for each interface. */
3631d34c9daSPatrick Kelsey 	STAILQ_FOREACH(if_ppa, &interfaces, meta.link) {
3641d34c9daSPatrick Kelsey 		switch (if_ppa->pa.scheduler) {
3653b3a8eb9SGleb Smirnoff 		case ALTQT_CBQ:
3661d34c9daSPatrick Kelsey 			error = check_commit_cbq(dev, opts, if_ppa);
3673b3a8eb9SGleb Smirnoff 			break;
3683b3a8eb9SGleb Smirnoff 		case ALTQT_PRIQ:
3691d34c9daSPatrick Kelsey 			error = check_commit_priq(dev, opts, if_ppa);
3703b3a8eb9SGleb Smirnoff 			break;
3713b3a8eb9SGleb Smirnoff 		case ALTQT_HFSC:
3721d34c9daSPatrick Kelsey 			error = check_commit_hfsc(dev, opts, if_ppa);
3733b3a8eb9SGleb Smirnoff 			break;
374a5b789f6SErmal Luçi 		case ALTQT_FAIRQ:
3751d34c9daSPatrick Kelsey 			error = check_commit_fairq(dev, opts, if_ppa);
376a5b789f6SErmal Luçi 			break;
3773b3a8eb9SGleb Smirnoff 		default:
3783b3a8eb9SGleb Smirnoff 			break;
3793b3a8eb9SGleb Smirnoff 		}
3803b3a8eb9SGleb Smirnoff 	}
3813b3a8eb9SGleb Smirnoff 	return (error);
3823b3a8eb9SGleb Smirnoff }
3833b3a8eb9SGleb Smirnoff 
3843b3a8eb9SGleb Smirnoff /*
3853b3a8eb9SGleb Smirnoff  * eval_pfqueue computes the queue parameters.
3863b3a8eb9SGleb Smirnoff  */
3873b3a8eb9SGleb Smirnoff int
eval_pfqueue(struct pfctl * pf,struct pf_altq * pa,struct node_queue_bw * bw,struct node_queue_opt * opts)3883b3a8eb9SGleb Smirnoff eval_pfqueue(struct pfctl *pf, struct pf_altq *pa, struct node_queue_bw *bw,
3893b3a8eb9SGleb Smirnoff     struct node_queue_opt *opts)
3903b3a8eb9SGleb Smirnoff {
3913b3a8eb9SGleb Smirnoff 	/* should be merged with expand_queue */
3921d34c9daSPatrick Kelsey 	struct pfctl_altq	*if_ppa, *parent;
3933b3a8eb9SGleb Smirnoff 	int		 	 error = 0;
3943b3a8eb9SGleb Smirnoff 
3953b3a8eb9SGleb Smirnoff 	/* find the corresponding interface and copy fields used by queues */
3961d34c9daSPatrick Kelsey 	if ((if_ppa = pfaltq_lookup(pa->ifname)) == NULL) {
3973b3a8eb9SGleb Smirnoff 		fprintf(stderr, "altq not defined on %s\n", pa->ifname);
3983b3a8eb9SGleb Smirnoff 		return (1);
3993b3a8eb9SGleb Smirnoff 	}
4001d34c9daSPatrick Kelsey 	pa->scheduler = if_ppa->pa.scheduler;
4011d34c9daSPatrick Kelsey 	pa->ifbandwidth = if_ppa->pa.ifbandwidth;
4023b3a8eb9SGleb Smirnoff 
4033b3a8eb9SGleb Smirnoff 	if (qname_to_pfaltq(pa->qname, pa->ifname) != NULL) {
4043b3a8eb9SGleb Smirnoff 		fprintf(stderr, "queue %s already exists on interface %s\n",
4053b3a8eb9SGleb Smirnoff 		    pa->qname, pa->ifname);
4063b3a8eb9SGleb Smirnoff 		return (1);
4073b3a8eb9SGleb Smirnoff 	}
4083b3a8eb9SGleb Smirnoff 	pa->qid = qname_to_qid(pa->qname);
4093b3a8eb9SGleb Smirnoff 
4103b3a8eb9SGleb Smirnoff 	parent = NULL;
4113b3a8eb9SGleb Smirnoff 	if (pa->parent[0] != 0) {
4123b3a8eb9SGleb Smirnoff 		parent = qname_to_pfaltq(pa->parent, pa->ifname);
4133b3a8eb9SGleb Smirnoff 		if (parent == NULL) {
4143b3a8eb9SGleb Smirnoff 			fprintf(stderr, "parent %s not found for %s\n",
4153b3a8eb9SGleb Smirnoff 			    pa->parent, pa->qname);
4163b3a8eb9SGleb Smirnoff 			return (1);
4173b3a8eb9SGleb Smirnoff 		}
4181d34c9daSPatrick Kelsey 		pa->parent_qid = parent->pa.qid;
4193b3a8eb9SGleb Smirnoff 	}
4203b3a8eb9SGleb Smirnoff 	if (pa->qlimit == 0)
4213b3a8eb9SGleb Smirnoff 		pa->qlimit = DEFAULT_QLIMIT;
4223b3a8eb9SGleb Smirnoff 
423a5b789f6SErmal Luçi 	if (pa->scheduler == ALTQT_CBQ || pa->scheduler == ALTQT_HFSC ||
424a5b789f6SErmal Luçi 		pa->scheduler == ALTQT_FAIRQ) {
4253b3a8eb9SGleb Smirnoff 		pa->bandwidth = eval_bwspec(bw,
4261d34c9daSPatrick Kelsey 		    parent == NULL ? pa->ifbandwidth : parent->pa.bandwidth);
4271d34c9daSPatrick Kelsey 
4283b3a8eb9SGleb Smirnoff 		if (pa->bandwidth > pa->ifbandwidth) {
4293b3a8eb9SGleb Smirnoff 			fprintf(stderr, "bandwidth for %s higher than "
4303b3a8eb9SGleb Smirnoff 			    "interface\n", pa->qname);
4313b3a8eb9SGleb Smirnoff 			return (1);
4323b3a8eb9SGleb Smirnoff 		}
43399766730SPatrick Kelsey 		/*
43499766730SPatrick Kelsey 		 * If not HFSC, then check that the sum of the child
43599766730SPatrick Kelsey 		 * bandwidths is less than the parent's bandwidth.  For
43699766730SPatrick Kelsey 		 * HFSC, the equivalent concept is to check that the sum of
43799766730SPatrick Kelsey 		 * the child linkshare service curves are under the parent's
43899766730SPatrick Kelsey 		 * linkshare service curve, and that check is performed by
43999766730SPatrick Kelsey 		 * eval_pfqueue_hfsc().
44099766730SPatrick Kelsey 		 */
44199766730SPatrick Kelsey 		if ((parent != NULL) && (pa->scheduler != ALTQT_HFSC)) {
4421d34c9daSPatrick Kelsey 			if (pa->bandwidth > parent->pa.bandwidth) {
4433b3a8eb9SGleb Smirnoff 				warnx("bandwidth for %s higher than parent",
4443b3a8eb9SGleb Smirnoff 				    pa->qname);
4453b3a8eb9SGleb Smirnoff 				return (1);
4463b3a8eb9SGleb Smirnoff 			}
4471d34c9daSPatrick Kelsey 			parent->meta.bwsum += pa->bandwidth;
4481d34c9daSPatrick Kelsey 			if (parent->meta.bwsum > parent->pa.bandwidth) {
4491d34c9daSPatrick Kelsey 				warnx("the sum of the child bandwidth (%" PRIu64
4501d34c9daSPatrick Kelsey 				    ") higher than parent \"%s\" (%" PRIu64 ")",
4511d34c9daSPatrick Kelsey 				    parent->meta.bwsum, parent->pa.qname,
4521d34c9daSPatrick Kelsey 				    parent->pa.bandwidth);
4533b3a8eb9SGleb Smirnoff 			}
4543b3a8eb9SGleb Smirnoff 		}
4553b3a8eb9SGleb Smirnoff 	}
4563b3a8eb9SGleb Smirnoff 
45799766730SPatrick Kelsey 	if (eval_queue_opts(pa, opts,
45899766730SPatrick Kelsey 		parent == NULL ? pa->ifbandwidth : parent->pa.bandwidth))
45999766730SPatrick Kelsey 		return (1);
46099766730SPatrick Kelsey 
4611d34c9daSPatrick Kelsey 	if (parent != NULL)
4621d34c9daSPatrick Kelsey 		parent->meta.children++;
4633b3a8eb9SGleb Smirnoff 
4643b3a8eb9SGleb Smirnoff 	switch (pa->scheduler) {
4653b3a8eb9SGleb Smirnoff 	case ALTQT_CBQ:
4661d34c9daSPatrick Kelsey 		error = eval_pfqueue_cbq(pf, pa, if_ppa);
4673b3a8eb9SGleb Smirnoff 		break;
4683b3a8eb9SGleb Smirnoff 	case ALTQT_PRIQ:
4691d34c9daSPatrick Kelsey 		error = eval_pfqueue_priq(pf, pa, if_ppa);
4703b3a8eb9SGleb Smirnoff 		break;
4713b3a8eb9SGleb Smirnoff 	case ALTQT_HFSC:
4721d34c9daSPatrick Kelsey 		error = eval_pfqueue_hfsc(pf, pa, if_ppa, parent);
4733b3a8eb9SGleb Smirnoff 		break;
474a5b789f6SErmal Luçi 	case ALTQT_FAIRQ:
4751d34c9daSPatrick Kelsey 		error = eval_pfqueue_fairq(pf, pa, if_ppa, parent);
476a5b789f6SErmal Luçi 		break;
4773b3a8eb9SGleb Smirnoff 	default:
4783b3a8eb9SGleb Smirnoff 		break;
4793b3a8eb9SGleb Smirnoff 	}
4803b3a8eb9SGleb Smirnoff 	return (error);
4813b3a8eb9SGleb Smirnoff }
4823b3a8eb9SGleb Smirnoff 
4833b3a8eb9SGleb Smirnoff /*
4843b3a8eb9SGleb Smirnoff  * CBQ support functions
4853b3a8eb9SGleb Smirnoff  */
4863b3a8eb9SGleb Smirnoff #define	RM_FILTER_GAIN	5	/* log2 of gain, e.g., 5 => 31/32 */
4873b3a8eb9SGleb Smirnoff #define	RM_NS_PER_SEC	(1000000000)
4883b3a8eb9SGleb Smirnoff 
4893b3a8eb9SGleb Smirnoff static int
eval_pfqueue_cbq(struct pfctl * pf,struct pf_altq * pa,struct pfctl_altq * if_ppa)4901d34c9daSPatrick Kelsey eval_pfqueue_cbq(struct pfctl *pf, struct pf_altq *pa, struct pfctl_altq *if_ppa)
4913b3a8eb9SGleb Smirnoff {
4923b3a8eb9SGleb Smirnoff 	struct cbq_opts	*opts;
4933b3a8eb9SGleb Smirnoff 	u_int		 ifmtu;
4943b3a8eb9SGleb Smirnoff 
4953b3a8eb9SGleb Smirnoff 	if (pa->priority >= CBQ_MAXPRI) {
4963b3a8eb9SGleb Smirnoff 		warnx("priority out of range: max %d", CBQ_MAXPRI - 1);
4973b3a8eb9SGleb Smirnoff 		return (-1);
4983b3a8eb9SGleb Smirnoff 	}
4993b3a8eb9SGleb Smirnoff 
5003b3a8eb9SGleb Smirnoff 	ifmtu = getifmtu(pa->ifname);
5013b3a8eb9SGleb Smirnoff 	opts = &pa->pq_u.cbq_opts;
5023b3a8eb9SGleb Smirnoff 
5033b3a8eb9SGleb Smirnoff 	if (opts->pktsize == 0) {	/* use default */
5043b3a8eb9SGleb Smirnoff 		opts->pktsize = ifmtu;
5053b3a8eb9SGleb Smirnoff 		if (opts->pktsize > MCLBYTES)	/* do what TCP does */
5063b3a8eb9SGleb Smirnoff 			opts->pktsize &= ~MCLBYTES;
5073b3a8eb9SGleb Smirnoff 	} else if (opts->pktsize > ifmtu)
5083b3a8eb9SGleb Smirnoff 		opts->pktsize = ifmtu;
5093b3a8eb9SGleb Smirnoff 	if (opts->maxpktsize == 0)	/* use default */
5103b3a8eb9SGleb Smirnoff 		opts->maxpktsize = ifmtu;
5113b3a8eb9SGleb Smirnoff 	else if (opts->maxpktsize > ifmtu)
5123b3a8eb9SGleb Smirnoff 		opts->pktsize = ifmtu;
5133b3a8eb9SGleb Smirnoff 
5143b3a8eb9SGleb Smirnoff 	if (opts->pktsize > opts->maxpktsize)
5153b3a8eb9SGleb Smirnoff 		opts->pktsize = opts->maxpktsize;
5163b3a8eb9SGleb Smirnoff 
5173b3a8eb9SGleb Smirnoff 	if (pa->parent[0] == 0)
5183b3a8eb9SGleb Smirnoff 		opts->flags |= (CBQCLF_ROOTCLASS | CBQCLF_WRR);
5193b3a8eb9SGleb Smirnoff 
5201d34c9daSPatrick Kelsey 	if (pa->pq_u.cbq_opts.flags & CBQCLF_ROOTCLASS)
5211d34c9daSPatrick Kelsey 		if_ppa->meta.root_classes++;
5221d34c9daSPatrick Kelsey 	if (pa->pq_u.cbq_opts.flags & CBQCLF_DEFCLASS)
5231d34c9daSPatrick Kelsey 		if_ppa->meta.default_classes++;
5241d34c9daSPatrick Kelsey 
5253b3a8eb9SGleb Smirnoff 	cbq_compute_idletime(pf, pa);
5263b3a8eb9SGleb Smirnoff 	return (0);
5273b3a8eb9SGleb Smirnoff }
5283b3a8eb9SGleb Smirnoff 
5293b3a8eb9SGleb Smirnoff /*
5303b3a8eb9SGleb Smirnoff  * compute ns_per_byte, maxidle, minidle, and offtime
5313b3a8eb9SGleb Smirnoff  */
5323b3a8eb9SGleb Smirnoff static int
cbq_compute_idletime(struct pfctl * pf,struct pf_altq * pa)5333b3a8eb9SGleb Smirnoff cbq_compute_idletime(struct pfctl *pf, struct pf_altq *pa)
5343b3a8eb9SGleb Smirnoff {
5353b3a8eb9SGleb Smirnoff 	struct cbq_opts	*opts;
5363b3a8eb9SGleb Smirnoff 	double		 maxidle_s, maxidle, minidle;
5373b3a8eb9SGleb Smirnoff 	double		 offtime, nsPerByte, ifnsPerByte, ptime, cptime;
5383b3a8eb9SGleb Smirnoff 	double		 z, g, f, gton, gtom;
5393b3a8eb9SGleb Smirnoff 	u_int		 minburst, maxburst;
5403b3a8eb9SGleb Smirnoff 
5413b3a8eb9SGleb Smirnoff 	opts = &pa->pq_u.cbq_opts;
5423b3a8eb9SGleb Smirnoff 	ifnsPerByte = (1.0 / (double)pa->ifbandwidth) * RM_NS_PER_SEC * 8;
5433b3a8eb9SGleb Smirnoff 	minburst = opts->minburst;
5443b3a8eb9SGleb Smirnoff 	maxburst = opts->maxburst;
5453b3a8eb9SGleb Smirnoff 
5463b3a8eb9SGleb Smirnoff 	if (pa->bandwidth == 0)
5473b3a8eb9SGleb Smirnoff 		f = 0.0001;	/* small enough? */
5483b3a8eb9SGleb Smirnoff 	else
5493b3a8eb9SGleb Smirnoff 		f = ((double) pa->bandwidth / (double) pa->ifbandwidth);
5503b3a8eb9SGleb Smirnoff 
5513b3a8eb9SGleb Smirnoff 	nsPerByte = ifnsPerByte / f;
5523b3a8eb9SGleb Smirnoff 	ptime = (double)opts->pktsize * ifnsPerByte;
5533b3a8eb9SGleb Smirnoff 	cptime = ptime * (1.0 - f) / f;
5543b3a8eb9SGleb Smirnoff 
5553b3a8eb9SGleb Smirnoff 	if (nsPerByte * (double)opts->maxpktsize > (double)INT_MAX) {
5563b3a8eb9SGleb Smirnoff 		/*
5573b3a8eb9SGleb Smirnoff 		 * this causes integer overflow in kernel!
5583b3a8eb9SGleb Smirnoff 		 * (bandwidth < 6Kbps when max_pkt_size=1500)
5593b3a8eb9SGleb Smirnoff 		 */
5602d0730b2SSean Bruno 		if (pa->bandwidth != 0 && (pf->opts & PF_OPT_QUIET) == 0) {
5613b3a8eb9SGleb Smirnoff 			warnx("queue bandwidth must be larger than %s",
5623b3a8eb9SGleb Smirnoff 			    rate2str(ifnsPerByte * (double)opts->maxpktsize /
5633b3a8eb9SGleb Smirnoff 			    (double)INT_MAX * (double)pa->ifbandwidth));
5643b3a8eb9SGleb Smirnoff 			fprintf(stderr, "cbq: queue %s is too slow!\n",
5653b3a8eb9SGleb Smirnoff 			    pa->qname);
5662d0730b2SSean Bruno 		}
5673b3a8eb9SGleb Smirnoff 		nsPerByte = (double)(INT_MAX / opts->maxpktsize);
5683b3a8eb9SGleb Smirnoff 	}
5693b3a8eb9SGleb Smirnoff 
5703b3a8eb9SGleb Smirnoff 	if (maxburst == 0) {  /* use default */
5713b3a8eb9SGleb Smirnoff 		if (cptime > 10.0 * 1000000)
5723b3a8eb9SGleb Smirnoff 			maxburst = 4;
5733b3a8eb9SGleb Smirnoff 		else
5743b3a8eb9SGleb Smirnoff 			maxburst = 16;
5753b3a8eb9SGleb Smirnoff 	}
5763b3a8eb9SGleb Smirnoff 	if (minburst == 0)  /* use default */
5773b3a8eb9SGleb Smirnoff 		minburst = 2;
5783b3a8eb9SGleb Smirnoff 	if (minburst > maxburst)
5793b3a8eb9SGleb Smirnoff 		minburst = maxburst;
5803b3a8eb9SGleb Smirnoff 
5813b3a8eb9SGleb Smirnoff 	z = (double)(1 << RM_FILTER_GAIN);
5823b3a8eb9SGleb Smirnoff 	g = (1.0 - 1.0 / z);
5833b3a8eb9SGleb Smirnoff 	gton = pow(g, (double)maxburst);
5843b3a8eb9SGleb Smirnoff 	gtom = pow(g, (double)(minburst-1));
5853b3a8eb9SGleb Smirnoff 	maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton));
5863b3a8eb9SGleb Smirnoff 	maxidle_s = (1.0 - g);
5873b3a8eb9SGleb Smirnoff 	if (maxidle > maxidle_s)
5883b3a8eb9SGleb Smirnoff 		maxidle = ptime * maxidle;
5893b3a8eb9SGleb Smirnoff 	else
5903b3a8eb9SGleb Smirnoff 		maxidle = ptime * maxidle_s;
5913b3a8eb9SGleb Smirnoff 	offtime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom);
5923b3a8eb9SGleb Smirnoff 	minidle = -((double)opts->maxpktsize * (double)nsPerByte);
5933b3a8eb9SGleb Smirnoff 
5943b3a8eb9SGleb Smirnoff 	/* scale parameters */
5953b3a8eb9SGleb Smirnoff 	maxidle = ((maxidle * 8.0) / nsPerByte) *
5963b3a8eb9SGleb Smirnoff 	    pow(2.0, (double)RM_FILTER_GAIN);
5973b3a8eb9SGleb Smirnoff 	offtime = (offtime * 8.0) / nsPerByte *
5983b3a8eb9SGleb Smirnoff 	    pow(2.0, (double)RM_FILTER_GAIN);
5993b3a8eb9SGleb Smirnoff 	minidle = ((minidle * 8.0) / nsPerByte) *
6003b3a8eb9SGleb Smirnoff 	    pow(2.0, (double)RM_FILTER_GAIN);
6013b3a8eb9SGleb Smirnoff 
6023b3a8eb9SGleb Smirnoff 	maxidle = maxidle / 1000.0;
6033b3a8eb9SGleb Smirnoff 	offtime = offtime / 1000.0;
6043b3a8eb9SGleb Smirnoff 	minidle = minidle / 1000.0;
6053b3a8eb9SGleb Smirnoff 
6063b3a8eb9SGleb Smirnoff 	opts->minburst = minburst;
6073b3a8eb9SGleb Smirnoff 	opts->maxburst = maxburst;
6083b3a8eb9SGleb Smirnoff 	opts->ns_per_byte = (u_int)nsPerByte;
6093b3a8eb9SGleb Smirnoff 	opts->maxidle = (u_int)fabs(maxidle);
6103b3a8eb9SGleb Smirnoff 	opts->minidle = (int)minidle;
6113b3a8eb9SGleb Smirnoff 	opts->offtime = (u_int)fabs(offtime);
6123b3a8eb9SGleb Smirnoff 
6133b3a8eb9SGleb Smirnoff 	return (0);
6143b3a8eb9SGleb Smirnoff }
6153b3a8eb9SGleb Smirnoff 
6163b3a8eb9SGleb Smirnoff static int
check_commit_cbq(int dev,int opts,struct pfctl_altq * if_ppa)6171d34c9daSPatrick Kelsey check_commit_cbq(int dev, int opts, struct pfctl_altq *if_ppa)
6183b3a8eb9SGleb Smirnoff {
6193b3a8eb9SGleb Smirnoff 	int	error = 0;
6203b3a8eb9SGleb Smirnoff 
6213b3a8eb9SGleb Smirnoff 	/*
6223b3a8eb9SGleb Smirnoff 	 * check if cbq has one root queue and one default queue
6233b3a8eb9SGleb Smirnoff 	 * for this interface
6243b3a8eb9SGleb Smirnoff 	 */
6251d34c9daSPatrick Kelsey 	if (if_ppa->meta.root_classes != 1) {
6261d34c9daSPatrick Kelsey 		warnx("should have one root queue on %s", if_ppa->pa.ifname);
6273b3a8eb9SGleb Smirnoff 		error++;
6283b3a8eb9SGleb Smirnoff 	}
6291d34c9daSPatrick Kelsey 	if (if_ppa->meta.default_classes != 1) {
6301d34c9daSPatrick Kelsey 		warnx("should have one default queue on %s", if_ppa->pa.ifname);
6313b3a8eb9SGleb Smirnoff 		error++;
6323b3a8eb9SGleb Smirnoff 	}
6333b3a8eb9SGleb Smirnoff 	return (error);
6343b3a8eb9SGleb Smirnoff }
6353b3a8eb9SGleb Smirnoff 
6363b3a8eb9SGleb Smirnoff static int
print_cbq_opts(const struct pf_altq * a)6373b3a8eb9SGleb Smirnoff print_cbq_opts(const struct pf_altq *a)
6383b3a8eb9SGleb Smirnoff {
6393b3a8eb9SGleb Smirnoff 	const struct cbq_opts	*opts;
6403b3a8eb9SGleb Smirnoff 
6413b3a8eb9SGleb Smirnoff 	opts = &a->pq_u.cbq_opts;
6423b3a8eb9SGleb Smirnoff 	if (opts->flags) {
6433b3a8eb9SGleb Smirnoff 		printf("cbq(");
6443b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_RED)
6453b3a8eb9SGleb Smirnoff 			printf(" red");
6463b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_ECN)
6473b3a8eb9SGleb Smirnoff 			printf(" ecn");
6483b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_RIO)
6493b3a8eb9SGleb Smirnoff 			printf(" rio");
6500a70aaf8SLuiz Otavio O Souza 		if (opts->flags & CBQCLF_CODEL)
6510a70aaf8SLuiz Otavio O Souza 			printf(" codel");
6523b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_CLEARDSCP)
6533b3a8eb9SGleb Smirnoff 			printf(" cleardscp");
6543b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_FLOWVALVE)
6553b3a8eb9SGleb Smirnoff 			printf(" flowvalve");
6563b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_BORROW)
6573b3a8eb9SGleb Smirnoff 			printf(" borrow");
6583b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_WRR)
6593b3a8eb9SGleb Smirnoff 			printf(" wrr");
6603b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_EFFICIENT)
6613b3a8eb9SGleb Smirnoff 			printf(" efficient");
6623b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_ROOTCLASS)
6633b3a8eb9SGleb Smirnoff 			printf(" root");
6643b3a8eb9SGleb Smirnoff 		if (opts->flags & CBQCLF_DEFCLASS)
6653b3a8eb9SGleb Smirnoff 			printf(" default");
6663b3a8eb9SGleb Smirnoff 		printf(" ) ");
6673b3a8eb9SGleb Smirnoff 
6683b3a8eb9SGleb Smirnoff 		return (1);
6693b3a8eb9SGleb Smirnoff 	} else
6703b3a8eb9SGleb Smirnoff 		return (0);
6713b3a8eb9SGleb Smirnoff }
6723b3a8eb9SGleb Smirnoff 
6733b3a8eb9SGleb Smirnoff /*
6743b3a8eb9SGleb Smirnoff  * PRIQ support functions
6753b3a8eb9SGleb Smirnoff  */
6763b3a8eb9SGleb Smirnoff static int
eval_pfqueue_priq(struct pfctl * pf,struct pf_altq * pa,struct pfctl_altq * if_ppa)6771d34c9daSPatrick Kelsey eval_pfqueue_priq(struct pfctl *pf, struct pf_altq *pa, struct pfctl_altq *if_ppa)
6783b3a8eb9SGleb Smirnoff {
6793b3a8eb9SGleb Smirnoff 
6803b3a8eb9SGleb Smirnoff 	if (pa->priority >= PRIQ_MAXPRI) {
6813b3a8eb9SGleb Smirnoff 		warnx("priority out of range: max %d", PRIQ_MAXPRI - 1);
6823b3a8eb9SGleb Smirnoff 		return (-1);
6833b3a8eb9SGleb Smirnoff 	}
6841d34c9daSPatrick Kelsey 	if (BIT_ISSET(QPRI_BITSET_SIZE, pa->priority, &if_ppa->meta.qpris)) {
6851d34c9daSPatrick Kelsey 		warnx("%s does not have a unique priority on interface %s",
6861d34c9daSPatrick Kelsey 		    pa->qname, pa->ifname);
6873b3a8eb9SGleb Smirnoff 		return (-1);
6881d34c9daSPatrick Kelsey 	} else
6891d34c9daSPatrick Kelsey 		BIT_SET(QPRI_BITSET_SIZE, pa->priority, &if_ppa->meta.qpris);
6903b3a8eb9SGleb Smirnoff 
6911d34c9daSPatrick Kelsey 	if (pa->pq_u.priq_opts.flags & PRCF_DEFAULTCLASS)
6921d34c9daSPatrick Kelsey 		if_ppa->meta.default_classes++;
6933b3a8eb9SGleb Smirnoff 	return (0);
6943b3a8eb9SGleb Smirnoff }
6953b3a8eb9SGleb Smirnoff 
6963b3a8eb9SGleb Smirnoff static int
check_commit_priq(int dev,int opts,struct pfctl_altq * if_ppa)6971d34c9daSPatrick Kelsey check_commit_priq(int dev, int opts, struct pfctl_altq *if_ppa)
6983b3a8eb9SGleb Smirnoff {
6993b3a8eb9SGleb Smirnoff 
7003b3a8eb9SGleb Smirnoff 	/*
7013b3a8eb9SGleb Smirnoff 	 * check if priq has one default class for this interface
7023b3a8eb9SGleb Smirnoff 	 */
7031d34c9daSPatrick Kelsey 	if (if_ppa->meta.default_classes != 1) {
7041d34c9daSPatrick Kelsey 		warnx("should have one default queue on %s", if_ppa->pa.ifname);
7051d34c9daSPatrick Kelsey 		return (1);
7063b3a8eb9SGleb Smirnoff 	}
7071d34c9daSPatrick Kelsey 	return (0);
7083b3a8eb9SGleb Smirnoff }
7093b3a8eb9SGleb Smirnoff 
7103b3a8eb9SGleb Smirnoff static int
print_priq_opts(const struct pf_altq * a)7113b3a8eb9SGleb Smirnoff print_priq_opts(const struct pf_altq *a)
7123b3a8eb9SGleb Smirnoff {
7133b3a8eb9SGleb Smirnoff 	const struct priq_opts	*opts;
7143b3a8eb9SGleb Smirnoff 
7153b3a8eb9SGleb Smirnoff 	opts = &a->pq_u.priq_opts;
7163b3a8eb9SGleb Smirnoff 
7173b3a8eb9SGleb Smirnoff 	if (opts->flags) {
7183b3a8eb9SGleb Smirnoff 		printf("priq(");
7193b3a8eb9SGleb Smirnoff 		if (opts->flags & PRCF_RED)
7203b3a8eb9SGleb Smirnoff 			printf(" red");
7213b3a8eb9SGleb Smirnoff 		if (opts->flags & PRCF_ECN)
7223b3a8eb9SGleb Smirnoff 			printf(" ecn");
7233b3a8eb9SGleb Smirnoff 		if (opts->flags & PRCF_RIO)
7243b3a8eb9SGleb Smirnoff 			printf(" rio");
7250a70aaf8SLuiz Otavio O Souza 		if (opts->flags & PRCF_CODEL)
7260a70aaf8SLuiz Otavio O Souza 			printf(" codel");
7273b3a8eb9SGleb Smirnoff 		if (opts->flags & PRCF_CLEARDSCP)
7283b3a8eb9SGleb Smirnoff 			printf(" cleardscp");
7293b3a8eb9SGleb Smirnoff 		if (opts->flags & PRCF_DEFAULTCLASS)
7303b3a8eb9SGleb Smirnoff 			printf(" default");
7313b3a8eb9SGleb Smirnoff 		printf(" ) ");
7323b3a8eb9SGleb Smirnoff 
7333b3a8eb9SGleb Smirnoff 		return (1);
7343b3a8eb9SGleb Smirnoff 	} else
7353b3a8eb9SGleb Smirnoff 		return (0);
7363b3a8eb9SGleb Smirnoff }
7373b3a8eb9SGleb Smirnoff 
7383b3a8eb9SGleb Smirnoff /*
7393b3a8eb9SGleb Smirnoff  * HFSC support functions
7403b3a8eb9SGleb Smirnoff  */
7413b3a8eb9SGleb Smirnoff static int
eval_pfqueue_hfsc(struct pfctl * pf,struct pf_altq * pa,struct pfctl_altq * if_ppa,struct pfctl_altq * parent)7421d34c9daSPatrick Kelsey eval_pfqueue_hfsc(struct pfctl *pf, struct pf_altq *pa, struct pfctl_altq *if_ppa,
7431d34c9daSPatrick Kelsey     struct pfctl_altq *parent)
7443b3a8eb9SGleb Smirnoff {
745249cc75fSPatrick Kelsey 	struct hfsc_opts_v1	*opts;
7463b3a8eb9SGleb Smirnoff 	struct service_curve	 sc;
7473b3a8eb9SGleb Smirnoff 
7483b3a8eb9SGleb Smirnoff 	opts = &pa->pq_u.hfsc_opts;
7493b3a8eb9SGleb Smirnoff 
7501d34c9daSPatrick Kelsey 	if (parent == NULL) {
7513b3a8eb9SGleb Smirnoff 		/* root queue */
7523b3a8eb9SGleb Smirnoff 		opts->lssc_m1 = pa->ifbandwidth;
7533b3a8eb9SGleb Smirnoff 		opts->lssc_m2 = pa->ifbandwidth;
7543b3a8eb9SGleb Smirnoff 		opts->lssc_d = 0;
7553b3a8eb9SGleb Smirnoff 		return (0);
7563b3a8eb9SGleb Smirnoff 	}
7573b3a8eb9SGleb Smirnoff 
7581d34c9daSPatrick Kelsey 	/* First child initializes the parent's service curve accumulators. */
7591d34c9daSPatrick Kelsey 	if (parent->meta.children == 1) {
7601d34c9daSPatrick Kelsey 		LIST_INIT(&parent->meta.rtsc);
7611d34c9daSPatrick Kelsey 		LIST_INIT(&parent->meta.lssc);
7621d34c9daSPatrick Kelsey 	}
7631d34c9daSPatrick Kelsey 
7641d34c9daSPatrick Kelsey 	if (parent->pa.pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS) {
7651d34c9daSPatrick Kelsey 		warnx("adding %s would make default queue %s not a leaf",
7661d34c9daSPatrick Kelsey 		    pa->qname, pa->parent);
7671d34c9daSPatrick Kelsey 		return (-1);
7681d34c9daSPatrick Kelsey 	}
7691d34c9daSPatrick Kelsey 
7701d34c9daSPatrick Kelsey 	if (pa->pq_u.hfsc_opts.flags & HFCF_DEFAULTCLASS)
7711d34c9daSPatrick Kelsey 		if_ppa->meta.default_classes++;
7723b3a8eb9SGleb Smirnoff 
7733b3a8eb9SGleb Smirnoff 	/* if link_share is not specified, use bandwidth */
7743b3a8eb9SGleb Smirnoff 	if (opts->lssc_m2 == 0)
7753b3a8eb9SGleb Smirnoff 		opts->lssc_m2 = pa->bandwidth;
7763b3a8eb9SGleb Smirnoff 
7773b3a8eb9SGleb Smirnoff 	if ((opts->rtsc_m1 > 0 && opts->rtsc_m2 == 0) ||
7783b3a8eb9SGleb Smirnoff 	    (opts->lssc_m1 > 0 && opts->lssc_m2 == 0) ||
7793b3a8eb9SGleb Smirnoff 	    (opts->ulsc_m1 > 0 && opts->ulsc_m2 == 0)) {
7803b3a8eb9SGleb Smirnoff 		warnx("m2 is zero for %s", pa->qname);
7813b3a8eb9SGleb Smirnoff 		return (-1);
7823b3a8eb9SGleb Smirnoff 	}
7833b3a8eb9SGleb Smirnoff 
7843b3a8eb9SGleb Smirnoff 	if ((opts->rtsc_m1 < opts->rtsc_m2 && opts->rtsc_m1 != 0) ||
7853b3a8eb9SGleb Smirnoff 	    (opts->lssc_m1 < opts->lssc_m2 && opts->lssc_m1 != 0) ||
7863b3a8eb9SGleb Smirnoff 	    (opts->ulsc_m1 < opts->ulsc_m2 && opts->ulsc_m1 != 0)) {
7873b3a8eb9SGleb Smirnoff 		warnx("m1 must be zero for convex curve: %s", pa->qname);
7883b3a8eb9SGleb Smirnoff 		return (-1);
7893b3a8eb9SGleb Smirnoff 	}
7903b3a8eb9SGleb Smirnoff 
7913b3a8eb9SGleb Smirnoff 	/*
7923b3a8eb9SGleb Smirnoff 	 * admission control:
7933b3a8eb9SGleb Smirnoff 	 * for the real-time service curve, the sum of the service curves
7943b3a8eb9SGleb Smirnoff 	 * should not exceed 80% of the interface bandwidth.  20% is reserved
7953b3a8eb9SGleb Smirnoff 	 * not to over-commit the actual interface bandwidth.
7963b3a8eb9SGleb Smirnoff 	 * for the linkshare service curve, the sum of the child service
7973b3a8eb9SGleb Smirnoff 	 * curve should not exceed the parent service curve.
7983b3a8eb9SGleb Smirnoff 	 * for the upper-limit service curve, the assigned bandwidth should
7993b3a8eb9SGleb Smirnoff 	 * be smaller than the interface bandwidth, and the upper-limit should
8003b3a8eb9SGleb Smirnoff 	 * be larger than the real-time service curve when both are defined.
8013b3a8eb9SGleb Smirnoff 	 */
8023b3a8eb9SGleb Smirnoff 
8033b3a8eb9SGleb Smirnoff 	/* check the real-time service curve.  reserve 20% of interface bw */
8043b3a8eb9SGleb Smirnoff 	if (opts->rtsc_m2 != 0) {
8053b3a8eb9SGleb Smirnoff 		/* add this queue to the sum */
8063b3a8eb9SGleb Smirnoff 		sc.m1 = opts->rtsc_m1;
8073b3a8eb9SGleb Smirnoff 		sc.d = opts->rtsc_d;
8083b3a8eb9SGleb Smirnoff 		sc.m2 = opts->rtsc_m2;
8091d34c9daSPatrick Kelsey 		gsc_add_sc(&parent->meta.rtsc, &sc);
8103b3a8eb9SGleb Smirnoff 		/* compare the sum with 80% of the interface */
8113b3a8eb9SGleb Smirnoff 		sc.m1 = 0;
8123b3a8eb9SGleb Smirnoff 		sc.d = 0;
8133b3a8eb9SGleb Smirnoff 		sc.m2 = pa->ifbandwidth / 100 * 80;
8141d34c9daSPatrick Kelsey 		if (!is_gsc_under_sc(&parent->meta.rtsc, &sc)) {
8153b3a8eb9SGleb Smirnoff 			warnx("real-time sc exceeds 80%% of the interface "
8163b3a8eb9SGleb Smirnoff 			    "bandwidth (%s)", rate2str((double)sc.m2));
8171d34c9daSPatrick Kelsey 			return (-1);
8183b3a8eb9SGleb Smirnoff 		}
8193b3a8eb9SGleb Smirnoff 	}
8203b3a8eb9SGleb Smirnoff 
8213b3a8eb9SGleb Smirnoff 	/* check the linkshare service curve. */
8223b3a8eb9SGleb Smirnoff 	if (opts->lssc_m2 != 0) {
8233b3a8eb9SGleb Smirnoff 		/* add this queue to the child sum */
8243b3a8eb9SGleb Smirnoff 		sc.m1 = opts->lssc_m1;
8253b3a8eb9SGleb Smirnoff 		sc.d = opts->lssc_d;
8263b3a8eb9SGleb Smirnoff 		sc.m2 = opts->lssc_m2;
8271d34c9daSPatrick Kelsey 		gsc_add_sc(&parent->meta.lssc, &sc);
8283b3a8eb9SGleb Smirnoff 		/* compare the sum of the children with parent's sc */
8291d34c9daSPatrick Kelsey 		sc.m1 = parent->pa.pq_u.hfsc_opts.lssc_m1;
8301d34c9daSPatrick Kelsey 		sc.d = parent->pa.pq_u.hfsc_opts.lssc_d;
8311d34c9daSPatrick Kelsey 		sc.m2 = parent->pa.pq_u.hfsc_opts.lssc_m2;
8321d34c9daSPatrick Kelsey 		if (!is_gsc_under_sc(&parent->meta.lssc, &sc)) {
8333b3a8eb9SGleb Smirnoff 			warnx("linkshare sc exceeds parent's sc");
8341d34c9daSPatrick Kelsey 			return (-1);
8353b3a8eb9SGleb Smirnoff 		}
8363b3a8eb9SGleb Smirnoff 	}
8373b3a8eb9SGleb Smirnoff 
8383b3a8eb9SGleb Smirnoff 	/* check the upper-limit service curve. */
8393b3a8eb9SGleb Smirnoff 	if (opts->ulsc_m2 != 0) {
8403b3a8eb9SGleb Smirnoff 		if (opts->ulsc_m1 > pa->ifbandwidth ||
8413b3a8eb9SGleb Smirnoff 		    opts->ulsc_m2 > pa->ifbandwidth) {
8423b3a8eb9SGleb Smirnoff 			warnx("upper-limit larger than interface bandwidth");
8431d34c9daSPatrick Kelsey 			return (-1);
8443b3a8eb9SGleb Smirnoff 		}
8453b3a8eb9SGleb Smirnoff 		if (opts->rtsc_m2 != 0 && opts->rtsc_m2 > opts->ulsc_m2) {
8463b3a8eb9SGleb Smirnoff 			warnx("upper-limit sc smaller than real-time sc");
8471d34c9daSPatrick Kelsey 			return (-1);
8483b3a8eb9SGleb Smirnoff 		}
8493b3a8eb9SGleb Smirnoff 	}
8503b3a8eb9SGleb Smirnoff 
8513b3a8eb9SGleb Smirnoff 	return (0);
8523b3a8eb9SGleb Smirnoff }
8533b3a8eb9SGleb Smirnoff 
854a5b789f6SErmal Luçi /*
855a5b789f6SErmal Luçi  * FAIRQ support functions
856a5b789f6SErmal Luçi  */
857a5b789f6SErmal Luçi static int
eval_pfqueue_fairq(struct pfctl * pf __unused,struct pf_altq * pa,struct pfctl_altq * if_ppa,struct pfctl_altq * parent)8581d34c9daSPatrick Kelsey eval_pfqueue_fairq(struct pfctl *pf __unused, struct pf_altq *pa,
8591d34c9daSPatrick Kelsey     struct pfctl_altq *if_ppa, struct pfctl_altq *parent)
860a5b789f6SErmal Luçi {
861a5b789f6SErmal Luçi 	struct fairq_opts	*opts;
862a5b789f6SErmal Luçi 	struct service_curve	 sc;
863a5b789f6SErmal Luçi 
864a5b789f6SErmal Luçi 	opts = &pa->pq_u.fairq_opts;
865a5b789f6SErmal Luçi 
86626705a39SKristof Provost 	if (parent == NULL) {
867a5b789f6SErmal Luçi 		/* root queue */
868a5b789f6SErmal Luçi 		opts->lssc_m1 = pa->ifbandwidth;
869a5b789f6SErmal Luçi 		opts->lssc_m2 = pa->ifbandwidth;
870a5b789f6SErmal Luçi 		opts->lssc_d = 0;
871a5b789f6SErmal Luçi 		return (0);
872a5b789f6SErmal Luçi 	}
873a5b789f6SErmal Luçi 
8741d34c9daSPatrick Kelsey 	/* First child initializes the parent's service curve accumulator. */
8751d34c9daSPatrick Kelsey 	if (parent->meta.children == 1)
8761d34c9daSPatrick Kelsey 		LIST_INIT(&parent->meta.lssc);
8771d34c9daSPatrick Kelsey 
8781d34c9daSPatrick Kelsey 	if (parent->pa.pq_u.fairq_opts.flags & FARF_DEFAULTCLASS) {
8791d34c9daSPatrick Kelsey 		warnx("adding %s would make default queue %s not a leaf",
8801d34c9daSPatrick Kelsey 		    pa->qname, pa->parent);
8811d34c9daSPatrick Kelsey 		return (-1);
8821d34c9daSPatrick Kelsey 	}
8831d34c9daSPatrick Kelsey 
8841d34c9daSPatrick Kelsey 	if (pa->pq_u.fairq_opts.flags & FARF_DEFAULTCLASS)
8851d34c9daSPatrick Kelsey 		if_ppa->meta.default_classes++;
886a5b789f6SErmal Luçi 
887a5b789f6SErmal Luçi 	/* if link_share is not specified, use bandwidth */
888a5b789f6SErmal Luçi 	if (opts->lssc_m2 == 0)
889a5b789f6SErmal Luçi 		opts->lssc_m2 = pa->bandwidth;
890a5b789f6SErmal Luçi 
891a5b789f6SErmal Luçi 	/*
892a5b789f6SErmal Luçi 	 * admission control:
893a5b789f6SErmal Luçi 	 * for the real-time service curve, the sum of the service curves
894a5b789f6SErmal Luçi 	 * should not exceed 80% of the interface bandwidth.  20% is reserved
895a5b789f6SErmal Luçi 	 * not to over-commit the actual interface bandwidth.
896a5b789f6SErmal Luçi 	 * for the link-sharing service curve, the sum of the child service
897a5b789f6SErmal Luçi 	 * curve should not exceed the parent service curve.
898a5b789f6SErmal Luçi 	 * for the upper-limit service curve, the assigned bandwidth should
899a5b789f6SErmal Luçi 	 * be smaller than the interface bandwidth, and the upper-limit should
900a5b789f6SErmal Luçi 	 * be larger than the real-time service curve when both are defined.
901a5b789f6SErmal Luçi 	 */
902a5b789f6SErmal Luçi 
9031d34c9daSPatrick Kelsey 	/* check the linkshare service curve. */
904a5b789f6SErmal Luçi 	if (opts->lssc_m2 != 0) {
9051d34c9daSPatrick Kelsey 		/* add this queue to the child sum */
9061d34c9daSPatrick Kelsey 		sc.m1 = opts->lssc_m1;
9071d34c9daSPatrick Kelsey 		sc.d = opts->lssc_d;
9081d34c9daSPatrick Kelsey 		sc.m2 = opts->lssc_m2;
9091d34c9daSPatrick Kelsey 		gsc_add_sc(&parent->meta.lssc, &sc);
9101d34c9daSPatrick Kelsey 		/* compare the sum of the children with parent's sc */
9111d34c9daSPatrick Kelsey 		sc.m1 = parent->pa.pq_u.fairq_opts.lssc_m1;
9121d34c9daSPatrick Kelsey 		sc.d = parent->pa.pq_u.fairq_opts.lssc_d;
9131d34c9daSPatrick Kelsey 		sc.m2 = parent->pa.pq_u.fairq_opts.lssc_m2;
9141d34c9daSPatrick Kelsey 		if (!is_gsc_under_sc(&parent->meta.lssc, &sc)) {
915a5b789f6SErmal Luçi 			warnx("link-sharing sc exceeds parent's sc");
916a5b789f6SErmal Luçi 			return (-1);
917a5b789f6SErmal Luçi 		}
9181d34c9daSPatrick Kelsey 	}
9191d34c9daSPatrick Kelsey 
9201d34c9daSPatrick Kelsey 	return (0);
9211d34c9daSPatrick Kelsey }
922a5b789f6SErmal Luçi 
9233b3a8eb9SGleb Smirnoff static int
check_commit_hfsc(int dev,int opts,struct pfctl_altq * if_ppa)9241d34c9daSPatrick Kelsey check_commit_hfsc(int dev, int opts, struct pfctl_altq *if_ppa)
9253b3a8eb9SGleb Smirnoff {
9263b3a8eb9SGleb Smirnoff 
9273b3a8eb9SGleb Smirnoff 	/* check if hfsc has one default queue for this interface */
9281d34c9daSPatrick Kelsey 	if (if_ppa->meta.default_classes != 1) {
9291d34c9daSPatrick Kelsey 		warnx("should have one default queue on %s", if_ppa->pa.ifname);
9303b3a8eb9SGleb Smirnoff 		return (1);
9313b3a8eb9SGleb Smirnoff 	}
9321d34c9daSPatrick Kelsey 	return (0);
9333b3a8eb9SGleb Smirnoff }
9343b3a8eb9SGleb Smirnoff 
9353b3a8eb9SGleb Smirnoff static int
check_commit_fairq(int dev __unused,int opts __unused,struct pfctl_altq * if_ppa)9361d34c9daSPatrick Kelsey check_commit_fairq(int dev __unused, int opts __unused, struct pfctl_altq *if_ppa)
937a5b789f6SErmal Luçi {
938a5b789f6SErmal Luçi 
939a5b789f6SErmal Luçi 	/* check if fairq has one default queue for this interface */
9401d34c9daSPatrick Kelsey 	if (if_ppa->meta.default_classes != 1) {
9411d34c9daSPatrick Kelsey 		warnx("should have one default queue on %s", if_ppa->pa.ifname);
942a5b789f6SErmal Luçi 		return (1);
943a5b789f6SErmal Luçi 	}
9441d34c9daSPatrick Kelsey 	return (0);
945a5b789f6SErmal Luçi }
946a5b789f6SErmal Luçi 
947a5b789f6SErmal Luçi static int
print_hfsc_opts(const struct pf_altq * a,const struct node_queue_opt * qopts)9483b3a8eb9SGleb Smirnoff print_hfsc_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
9493b3a8eb9SGleb Smirnoff {
950249cc75fSPatrick Kelsey 	const struct hfsc_opts_v1	*opts;
9513b3a8eb9SGleb Smirnoff 	const struct node_hfsc_sc	*rtsc, *lssc, *ulsc;
9523b3a8eb9SGleb Smirnoff 
9533b3a8eb9SGleb Smirnoff 	opts = &a->pq_u.hfsc_opts;
9543b3a8eb9SGleb Smirnoff 	if (qopts == NULL)
9553b3a8eb9SGleb Smirnoff 		rtsc = lssc = ulsc = NULL;
9563b3a8eb9SGleb Smirnoff 	else {
9573b3a8eb9SGleb Smirnoff 		rtsc = &qopts->data.hfsc_opts.realtime;
9583b3a8eb9SGleb Smirnoff 		lssc = &qopts->data.hfsc_opts.linkshare;
9593b3a8eb9SGleb Smirnoff 		ulsc = &qopts->data.hfsc_opts.upperlimit;
9603b3a8eb9SGleb Smirnoff 	}
9613b3a8eb9SGleb Smirnoff 
9623b3a8eb9SGleb Smirnoff 	if (opts->flags || opts->rtsc_m2 != 0 || opts->ulsc_m2 != 0 ||
9633b3a8eb9SGleb Smirnoff 	    (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
9643b3a8eb9SGleb Smirnoff 	    opts->lssc_d != 0))) {
9653b3a8eb9SGleb Smirnoff 		printf("hfsc(");
9663b3a8eb9SGleb Smirnoff 		if (opts->flags & HFCF_RED)
9673b3a8eb9SGleb Smirnoff 			printf(" red");
9683b3a8eb9SGleb Smirnoff 		if (opts->flags & HFCF_ECN)
9693b3a8eb9SGleb Smirnoff 			printf(" ecn");
9703b3a8eb9SGleb Smirnoff 		if (opts->flags & HFCF_RIO)
9713b3a8eb9SGleb Smirnoff 			printf(" rio");
9720a70aaf8SLuiz Otavio O Souza 		if (opts->flags & HFCF_CODEL)
9730a70aaf8SLuiz Otavio O Souza 			printf(" codel");
9743b3a8eb9SGleb Smirnoff 		if (opts->flags & HFCF_CLEARDSCP)
9753b3a8eb9SGleb Smirnoff 			printf(" cleardscp");
9763b3a8eb9SGleb Smirnoff 		if (opts->flags & HFCF_DEFAULTCLASS)
9773b3a8eb9SGleb Smirnoff 			printf(" default");
9783b3a8eb9SGleb Smirnoff 		if (opts->rtsc_m2 != 0)
9793b3a8eb9SGleb Smirnoff 			print_hfsc_sc("realtime", opts->rtsc_m1, opts->rtsc_d,
9803b3a8eb9SGleb Smirnoff 			    opts->rtsc_m2, rtsc);
9813b3a8eb9SGleb Smirnoff 		if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
9823b3a8eb9SGleb Smirnoff 		    opts->lssc_d != 0))
9833b3a8eb9SGleb Smirnoff 			print_hfsc_sc("linkshare", opts->lssc_m1, opts->lssc_d,
9843b3a8eb9SGleb Smirnoff 			    opts->lssc_m2, lssc);
9853b3a8eb9SGleb Smirnoff 		if (opts->ulsc_m2 != 0)
9863b3a8eb9SGleb Smirnoff 			print_hfsc_sc("upperlimit", opts->ulsc_m1, opts->ulsc_d,
9873b3a8eb9SGleb Smirnoff 			    opts->ulsc_m2, ulsc);
9883b3a8eb9SGleb Smirnoff 		printf(" ) ");
9893b3a8eb9SGleb Smirnoff 
9903b3a8eb9SGleb Smirnoff 		return (1);
9913b3a8eb9SGleb Smirnoff 	} else
9923b3a8eb9SGleb Smirnoff 		return (0);
9933b3a8eb9SGleb Smirnoff }
9943b3a8eb9SGleb Smirnoff 
995a5b789f6SErmal Luçi static int
print_codel_opts(const struct pf_altq * a,const struct node_queue_opt * qopts)9960a70aaf8SLuiz Otavio O Souza print_codel_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
9970a70aaf8SLuiz Otavio O Souza {
9980a70aaf8SLuiz Otavio O Souza 	const struct codel_opts *opts;
9990a70aaf8SLuiz Otavio O Souza 
10000a70aaf8SLuiz Otavio O Souza 	opts = &a->pq_u.codel_opts;
10010a70aaf8SLuiz Otavio O Souza 	if (opts->target || opts->interval || opts->ecn) {
10020a70aaf8SLuiz Otavio O Souza 		printf("codel(");
10030a70aaf8SLuiz Otavio O Souza 		if (opts->target)
10040a70aaf8SLuiz Otavio O Souza 			printf(" target %d", opts->target);
10050a70aaf8SLuiz Otavio O Souza 		if (opts->interval)
10060a70aaf8SLuiz Otavio O Souza 			printf(" interval %d", opts->interval);
10070a70aaf8SLuiz Otavio O Souza 		if (opts->ecn)
10080a70aaf8SLuiz Otavio O Souza 			printf("ecn");
10090a70aaf8SLuiz Otavio O Souza 		printf(" ) ");
10100a70aaf8SLuiz Otavio O Souza 
10110a70aaf8SLuiz Otavio O Souza 		return (1);
10120a70aaf8SLuiz Otavio O Souza 	}
10130a70aaf8SLuiz Otavio O Souza 
10140a70aaf8SLuiz Otavio O Souza 	return (0);
10150a70aaf8SLuiz Otavio O Souza }
10160a70aaf8SLuiz Otavio O Souza 
10170a70aaf8SLuiz Otavio O Souza static int
print_fairq_opts(const struct pf_altq * a,const struct node_queue_opt * qopts)1018a5b789f6SErmal Luçi print_fairq_opts(const struct pf_altq *a, const struct node_queue_opt *qopts)
1019a5b789f6SErmal Luçi {
1020a5b789f6SErmal Luçi 	const struct fairq_opts		*opts;
1021a5b789f6SErmal Luçi 	const struct node_fairq_sc	*loc_lssc;
1022a5b789f6SErmal Luçi 
1023a5b789f6SErmal Luçi 	opts = &a->pq_u.fairq_opts;
1024a5b789f6SErmal Luçi 	if (qopts == NULL)
1025a5b789f6SErmal Luçi 		loc_lssc = NULL;
1026a5b789f6SErmal Luçi 	else
1027a5b789f6SErmal Luçi 		loc_lssc = &qopts->data.fairq_opts.linkshare;
1028a5b789f6SErmal Luçi 
1029a5b789f6SErmal Luçi 	if (opts->flags ||
1030a5b789f6SErmal Luçi 	    (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
1031a5b789f6SErmal Luçi 	    opts->lssc_d != 0))) {
1032a5b789f6SErmal Luçi 		printf("fairq(");
1033a5b789f6SErmal Luçi 		if (opts->flags & FARF_RED)
1034a5b789f6SErmal Luçi 			printf(" red");
1035a5b789f6SErmal Luçi 		if (opts->flags & FARF_ECN)
1036a5b789f6SErmal Luçi 			printf(" ecn");
1037a5b789f6SErmal Luçi 		if (opts->flags & FARF_RIO)
1038a5b789f6SErmal Luçi 			printf(" rio");
10390a70aaf8SLuiz Otavio O Souza 		if (opts->flags & FARF_CODEL)
10400a70aaf8SLuiz Otavio O Souza 			printf(" codel");
1041a5b789f6SErmal Luçi 		if (opts->flags & FARF_CLEARDSCP)
1042a5b789f6SErmal Luçi 			printf(" cleardscp");
1043a5b789f6SErmal Luçi 		if (opts->flags & FARF_DEFAULTCLASS)
1044a5b789f6SErmal Luçi 			printf(" default");
1045a5b789f6SErmal Luçi 		if (opts->lssc_m2 != 0 && (opts->lssc_m2 != a->bandwidth ||
1046a5b789f6SErmal Luçi 		    opts->lssc_d != 0))
1047a5b789f6SErmal Luçi 			print_fairq_sc("linkshare", opts->lssc_m1, opts->lssc_d,
1048a5b789f6SErmal Luçi 			    opts->lssc_m2, loc_lssc);
1049a5b789f6SErmal Luçi 		printf(" ) ");
1050a5b789f6SErmal Luçi 
1051a5b789f6SErmal Luçi 		return (1);
1052a5b789f6SErmal Luçi 	} else
1053a5b789f6SErmal Luçi 		return (0);
1054a5b789f6SErmal Luçi }
1055a5b789f6SErmal Luçi 
10563b3a8eb9SGleb Smirnoff /*
10573b3a8eb9SGleb Smirnoff  * admission control using generalized service curve
10583b3a8eb9SGleb Smirnoff  */
10593b3a8eb9SGleb Smirnoff 
10603b3a8eb9SGleb Smirnoff /* add a new service curve to a generalized service curve */
10613b3a8eb9SGleb Smirnoff static void
gsc_add_sc(struct gen_sc * gsc,struct service_curve * sc)10623b3a8eb9SGleb Smirnoff gsc_add_sc(struct gen_sc *gsc, struct service_curve *sc)
10633b3a8eb9SGleb Smirnoff {
10643b3a8eb9SGleb Smirnoff 	if (is_sc_null(sc))
10653b3a8eb9SGleb Smirnoff 		return;
10663b3a8eb9SGleb Smirnoff 	if (sc->d != 0)
10673b3a8eb9SGleb Smirnoff 		gsc_add_seg(gsc, 0.0, 0.0, (double)sc->d, (double)sc->m1);
10683b3a8eb9SGleb Smirnoff 	gsc_add_seg(gsc, (double)sc->d, 0.0, INFINITY, (double)sc->m2);
10693b3a8eb9SGleb Smirnoff }
10703b3a8eb9SGleb Smirnoff 
10713b3a8eb9SGleb Smirnoff /*
10723b3a8eb9SGleb Smirnoff  * check whether all points of a generalized service curve have
10733b3a8eb9SGleb Smirnoff  * their y-coordinates no larger than a given two-piece linear
10743b3a8eb9SGleb Smirnoff  * service curve.
10753b3a8eb9SGleb Smirnoff  */
10763b3a8eb9SGleb Smirnoff static int
is_gsc_under_sc(struct gen_sc * gsc,struct service_curve * sc)10773b3a8eb9SGleb Smirnoff is_gsc_under_sc(struct gen_sc *gsc, struct service_curve *sc)
10783b3a8eb9SGleb Smirnoff {
10793b3a8eb9SGleb Smirnoff 	struct segment	*s, *last, *end;
10803b3a8eb9SGleb Smirnoff 	double		 y;
10813b3a8eb9SGleb Smirnoff 
10823b3a8eb9SGleb Smirnoff 	if (is_sc_null(sc)) {
10833b3a8eb9SGleb Smirnoff 		if (LIST_EMPTY(gsc))
10843b3a8eb9SGleb Smirnoff 			return (1);
10853b3a8eb9SGleb Smirnoff 		LIST_FOREACH(s, gsc, _next) {
10863b3a8eb9SGleb Smirnoff 			if (s->m != 0)
10873b3a8eb9SGleb Smirnoff 				return (0);
10883b3a8eb9SGleb Smirnoff 		}
10893b3a8eb9SGleb Smirnoff 		return (1);
10903b3a8eb9SGleb Smirnoff 	}
10913b3a8eb9SGleb Smirnoff 	/*
10923b3a8eb9SGleb Smirnoff 	 * gsc has a dummy entry at the end with x = INFINITY.
10933b3a8eb9SGleb Smirnoff 	 * loop through up to this dummy entry.
10943b3a8eb9SGleb Smirnoff 	 */
10953b3a8eb9SGleb Smirnoff 	end = gsc_getentry(gsc, INFINITY);
10963b3a8eb9SGleb Smirnoff 	if (end == NULL)
10973b3a8eb9SGleb Smirnoff 		return (1);
10983b3a8eb9SGleb Smirnoff 	last = NULL;
10993b3a8eb9SGleb Smirnoff 	for (s = LIST_FIRST(gsc); s != end; s = LIST_NEXT(s, _next)) {
11003b3a8eb9SGleb Smirnoff 		if (s->y > sc_x2y(sc, s->x))
11013b3a8eb9SGleb Smirnoff 			return (0);
11023b3a8eb9SGleb Smirnoff 		last = s;
11033b3a8eb9SGleb Smirnoff 	}
11043b3a8eb9SGleb Smirnoff 	/* last now holds the real last segment */
11053b3a8eb9SGleb Smirnoff 	if (last == NULL)
11063b3a8eb9SGleb Smirnoff 		return (1);
11073b3a8eb9SGleb Smirnoff 	if (last->m > sc->m2)
11083b3a8eb9SGleb Smirnoff 		return (0);
11093b3a8eb9SGleb Smirnoff 	if (last->x < sc->d && last->m > sc->m1) {
11103b3a8eb9SGleb Smirnoff 		y = last->y + (sc->d - last->x) * last->m;
11113b3a8eb9SGleb Smirnoff 		if (y > sc_x2y(sc, sc->d))
11123b3a8eb9SGleb Smirnoff 			return (0);
11133b3a8eb9SGleb Smirnoff 	}
11143b3a8eb9SGleb Smirnoff 	return (1);
11153b3a8eb9SGleb Smirnoff }
11163b3a8eb9SGleb Smirnoff 
11173b3a8eb9SGleb Smirnoff /*
11183b3a8eb9SGleb Smirnoff  * return a segment entry starting at x.
11193b3a8eb9SGleb Smirnoff  * if gsc has no entry starting at x, a new entry is created at x.
11203b3a8eb9SGleb Smirnoff  */
11213b3a8eb9SGleb Smirnoff static struct segment *
gsc_getentry(struct gen_sc * gsc,double x)11223b3a8eb9SGleb Smirnoff gsc_getentry(struct gen_sc *gsc, double x)
11233b3a8eb9SGleb Smirnoff {
11243b3a8eb9SGleb Smirnoff 	struct segment	*new, *prev, *s;
11253b3a8eb9SGleb Smirnoff 
11263b3a8eb9SGleb Smirnoff 	prev = NULL;
11273b3a8eb9SGleb Smirnoff 	LIST_FOREACH(s, gsc, _next) {
11283b3a8eb9SGleb Smirnoff 		if (s->x == x)
11293b3a8eb9SGleb Smirnoff 			return (s);	/* matching entry found */
11303b3a8eb9SGleb Smirnoff 		else if (s->x < x)
11313b3a8eb9SGleb Smirnoff 			prev = s;
11323b3a8eb9SGleb Smirnoff 		else
11333b3a8eb9SGleb Smirnoff 			break;
11343b3a8eb9SGleb Smirnoff 	}
11353b3a8eb9SGleb Smirnoff 
11363b3a8eb9SGleb Smirnoff 	/* we have to create a new entry */
11373b3a8eb9SGleb Smirnoff 	if ((new = calloc(1, sizeof(struct segment))) == NULL)
11383b3a8eb9SGleb Smirnoff 		return (NULL);
11393b3a8eb9SGleb Smirnoff 
11403b3a8eb9SGleb Smirnoff 	new->x = x;
11413b3a8eb9SGleb Smirnoff 	if (x == INFINITY || s == NULL)
11423b3a8eb9SGleb Smirnoff 		new->d = 0;
11433b3a8eb9SGleb Smirnoff 	else if (s->x == INFINITY)
11443b3a8eb9SGleb Smirnoff 		new->d = INFINITY;
11453b3a8eb9SGleb Smirnoff 	else
11463b3a8eb9SGleb Smirnoff 		new->d = s->x - x;
11473b3a8eb9SGleb Smirnoff 	if (prev == NULL) {
11483b3a8eb9SGleb Smirnoff 		/* insert the new entry at the head of the list */
11493b3a8eb9SGleb Smirnoff 		new->y = 0;
11503b3a8eb9SGleb Smirnoff 		new->m = 0;
11513b3a8eb9SGleb Smirnoff 		LIST_INSERT_HEAD(gsc, new, _next);
11523b3a8eb9SGleb Smirnoff 	} else {
11533b3a8eb9SGleb Smirnoff 		/*
11543b3a8eb9SGleb Smirnoff 		 * the start point intersects with the segment pointed by
11553b3a8eb9SGleb Smirnoff 		 * prev.  divide prev into 2 segments
11563b3a8eb9SGleb Smirnoff 		 */
11573b3a8eb9SGleb Smirnoff 		if (x == INFINITY) {
11583b3a8eb9SGleb Smirnoff 			prev->d = INFINITY;
11593b3a8eb9SGleb Smirnoff 			if (prev->m == 0)
11603b3a8eb9SGleb Smirnoff 				new->y = prev->y;
11613b3a8eb9SGleb Smirnoff 			else
11623b3a8eb9SGleb Smirnoff 				new->y = INFINITY;
11633b3a8eb9SGleb Smirnoff 		} else {
11643b3a8eb9SGleb Smirnoff 			prev->d = x - prev->x;
11653b3a8eb9SGleb Smirnoff 			new->y = prev->d * prev->m + prev->y;
11663b3a8eb9SGleb Smirnoff 		}
11673b3a8eb9SGleb Smirnoff 		new->m = prev->m;
11683b3a8eb9SGleb Smirnoff 		LIST_INSERT_AFTER(prev, new, _next);
11693b3a8eb9SGleb Smirnoff 	}
11703b3a8eb9SGleb Smirnoff 	return (new);
11713b3a8eb9SGleb Smirnoff }
11723b3a8eb9SGleb Smirnoff 
11733b3a8eb9SGleb Smirnoff /* add a segment to a generalized service curve */
11743b3a8eb9SGleb Smirnoff static int
gsc_add_seg(struct gen_sc * gsc,double x,double y,double d,double m)11753b3a8eb9SGleb Smirnoff gsc_add_seg(struct gen_sc *gsc, double x, double y, double d, double m)
11763b3a8eb9SGleb Smirnoff {
11773b3a8eb9SGleb Smirnoff 	struct segment	*start, *end, *s;
11783b3a8eb9SGleb Smirnoff 	double		 x2;
11793b3a8eb9SGleb Smirnoff 
11803b3a8eb9SGleb Smirnoff 	if (d == INFINITY)
11813b3a8eb9SGleb Smirnoff 		x2 = INFINITY;
11823b3a8eb9SGleb Smirnoff 	else
11833b3a8eb9SGleb Smirnoff 		x2 = x + d;
11843b3a8eb9SGleb Smirnoff 	start = gsc_getentry(gsc, x);
11853b3a8eb9SGleb Smirnoff 	end = gsc_getentry(gsc, x2);
11863b3a8eb9SGleb Smirnoff 	if (start == NULL || end == NULL)
11873b3a8eb9SGleb Smirnoff 		return (-1);
11883b3a8eb9SGleb Smirnoff 
11893b3a8eb9SGleb Smirnoff 	for (s = start; s != end; s = LIST_NEXT(s, _next)) {
11903b3a8eb9SGleb Smirnoff 		s->m += m;
11913b3a8eb9SGleb Smirnoff 		s->y += y + (s->x - x) * m;
11923b3a8eb9SGleb Smirnoff 	}
11933b3a8eb9SGleb Smirnoff 
11943b3a8eb9SGleb Smirnoff 	end = gsc_getentry(gsc, INFINITY);
11953b3a8eb9SGleb Smirnoff 	for (; s != end; s = LIST_NEXT(s, _next)) {
11963b3a8eb9SGleb Smirnoff 		s->y += m * d;
11973b3a8eb9SGleb Smirnoff 	}
11983b3a8eb9SGleb Smirnoff 
11993b3a8eb9SGleb Smirnoff 	return (0);
12003b3a8eb9SGleb Smirnoff }
12013b3a8eb9SGleb Smirnoff 
12023b3a8eb9SGleb Smirnoff /* get y-projection of a service curve */
12033b3a8eb9SGleb Smirnoff static double
sc_x2y(struct service_curve * sc,double x)12043b3a8eb9SGleb Smirnoff sc_x2y(struct service_curve *sc, double x)
12053b3a8eb9SGleb Smirnoff {
12063b3a8eb9SGleb Smirnoff 	double	y;
12073b3a8eb9SGleb Smirnoff 
12083b3a8eb9SGleb Smirnoff 	if (x <= (double)sc->d)
12093b3a8eb9SGleb Smirnoff 		/* y belongs to the 1st segment */
12103b3a8eb9SGleb Smirnoff 		y = x * (double)sc->m1;
12113b3a8eb9SGleb Smirnoff 	else
12123b3a8eb9SGleb Smirnoff 		/* y belongs to the 2nd segment */
12133b3a8eb9SGleb Smirnoff 		y = (double)sc->d * (double)sc->m1
12143b3a8eb9SGleb Smirnoff 			+ (x - (double)sc->d) * (double)sc->m2;
12153b3a8eb9SGleb Smirnoff 	return (y);
12163b3a8eb9SGleb Smirnoff }
12173b3a8eb9SGleb Smirnoff 
12183b3a8eb9SGleb Smirnoff /*
12193b3a8eb9SGleb Smirnoff  * misc utilities
12203b3a8eb9SGleb Smirnoff  */
12213b3a8eb9SGleb Smirnoff #define	R2S_BUFS	8
12223b3a8eb9SGleb Smirnoff #define	RATESTR_MAX	16
12233b3a8eb9SGleb Smirnoff 
12243b3a8eb9SGleb Smirnoff char *
rate2str(double rate)12253b3a8eb9SGleb Smirnoff rate2str(double rate)
12263b3a8eb9SGleb Smirnoff {
12273b3a8eb9SGleb Smirnoff 	char		*buf;
1228*c9856c4fSGordon Bergling 	static char	 r2sbuf[R2S_BUFS][RATESTR_MAX];  /* ring buffer */
12293b3a8eb9SGleb Smirnoff 	static int	 idx = 0;
12303b3a8eb9SGleb Smirnoff 	int		 i;
12313b3a8eb9SGleb Smirnoff 	static const char unit[] = " KMG";
12323b3a8eb9SGleb Smirnoff 
12333b3a8eb9SGleb Smirnoff 	buf = r2sbuf[idx++];
12343b3a8eb9SGleb Smirnoff 	if (idx == R2S_BUFS)
12353b3a8eb9SGleb Smirnoff 		idx = 0;
12363b3a8eb9SGleb Smirnoff 
12373b3a8eb9SGleb Smirnoff 	for (i = 0; rate >= 1000 && i <= 3; i++)
12383b3a8eb9SGleb Smirnoff 		rate /= 1000;
12393b3a8eb9SGleb Smirnoff 
12403b3a8eb9SGleb Smirnoff 	if ((int)(rate * 100) % 100)
12413b3a8eb9SGleb Smirnoff 		snprintf(buf, RATESTR_MAX, "%.2f%cb", rate, unit[i]);
12423b3a8eb9SGleb Smirnoff 	else
12433b3a8eb9SGleb Smirnoff 		snprintf(buf, RATESTR_MAX, "%d%cb", (int)rate, unit[i]);
12443b3a8eb9SGleb Smirnoff 
12453b3a8eb9SGleb Smirnoff 	return (buf);
12463b3a8eb9SGleb Smirnoff }
12473b3a8eb9SGleb Smirnoff 
12483b3a8eb9SGleb Smirnoff u_int32_t
getifspeed(char * ifname)12493b3a8eb9SGleb Smirnoff getifspeed(char *ifname)
12503b3a8eb9SGleb Smirnoff {
12513b3a8eb9SGleb Smirnoff 	int		s;
12523b3a8eb9SGleb Smirnoff 	struct ifreq	ifr;
12533b3a8eb9SGleb Smirnoff 	struct if_data	ifrdat;
12543b3a8eb9SGleb Smirnoff 
12551d34c9daSPatrick Kelsey 	s = get_query_socket();
12563b3a8eb9SGleb Smirnoff 	bzero(&ifr, sizeof(ifr));
12573b3a8eb9SGleb Smirnoff 	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
12583b3a8eb9SGleb Smirnoff 	    sizeof(ifr.ifr_name))
12593b3a8eb9SGleb Smirnoff 		errx(1, "getifspeed: strlcpy");
12603b3a8eb9SGleb Smirnoff 	ifr.ifr_data = (caddr_t)&ifrdat;
12613b3a8eb9SGleb Smirnoff 	if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == -1)
12623b3a8eb9SGleb Smirnoff 		err(1, "SIOCGIFDATA");
12633b3a8eb9SGleb Smirnoff 	return ((u_int32_t)ifrdat.ifi_baudrate);
12643b3a8eb9SGleb Smirnoff }
12653b3a8eb9SGleb Smirnoff 
12663b3a8eb9SGleb Smirnoff u_long
getifmtu(char * ifname)12673b3a8eb9SGleb Smirnoff getifmtu(char *ifname)
12683b3a8eb9SGleb Smirnoff {
12693b3a8eb9SGleb Smirnoff 	int		s;
12703b3a8eb9SGleb Smirnoff 	struct ifreq	ifr;
12713b3a8eb9SGleb Smirnoff 
12721d34c9daSPatrick Kelsey 	s = get_query_socket();
12733b3a8eb9SGleb Smirnoff 	bzero(&ifr, sizeof(ifr));
12743b3a8eb9SGleb Smirnoff 	if (strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)) >=
12753b3a8eb9SGleb Smirnoff 	    sizeof(ifr.ifr_name))
12763b3a8eb9SGleb Smirnoff 		errx(1, "getifmtu: strlcpy");
12773b3a8eb9SGleb Smirnoff 	if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == -1)
12783b3a8eb9SGleb Smirnoff #ifdef __FreeBSD__
12793b3a8eb9SGleb Smirnoff 		ifr.ifr_mtu = 1500;
12803b3a8eb9SGleb Smirnoff #else
12813b3a8eb9SGleb Smirnoff 		err(1, "SIOCGIFMTU");
12823b3a8eb9SGleb Smirnoff #endif
12833b3a8eb9SGleb Smirnoff 	if (ifr.ifr_mtu > 0)
12843b3a8eb9SGleb Smirnoff 		return (ifr.ifr_mtu);
12853b3a8eb9SGleb Smirnoff 	else {
12863b3a8eb9SGleb Smirnoff 		warnx("could not get mtu for %s, assuming 1500", ifname);
12873b3a8eb9SGleb Smirnoff 		return (1500);
12883b3a8eb9SGleb Smirnoff 	}
12893b3a8eb9SGleb Smirnoff }
12903b3a8eb9SGleb Smirnoff 
12913b3a8eb9SGleb Smirnoff int
eval_queue_opts(struct pf_altq * pa,struct node_queue_opt * opts,u_int64_t ref_bw)12923b3a8eb9SGleb Smirnoff eval_queue_opts(struct pf_altq *pa, struct node_queue_opt *opts,
1293249cc75fSPatrick Kelsey     u_int64_t ref_bw)
12943b3a8eb9SGleb Smirnoff {
12953b3a8eb9SGleb Smirnoff 	int	errors = 0;
12963b3a8eb9SGleb Smirnoff 
12973b3a8eb9SGleb Smirnoff 	switch (pa->scheduler) {
12983b3a8eb9SGleb Smirnoff 	case ALTQT_CBQ:
12993b3a8eb9SGleb Smirnoff 		pa->pq_u.cbq_opts = opts->data.cbq_opts;
13003b3a8eb9SGleb Smirnoff 		break;
13013b3a8eb9SGleb Smirnoff 	case ALTQT_PRIQ:
13023b3a8eb9SGleb Smirnoff 		pa->pq_u.priq_opts = opts->data.priq_opts;
13033b3a8eb9SGleb Smirnoff 		break;
13043b3a8eb9SGleb Smirnoff 	case ALTQT_HFSC:
13053b3a8eb9SGleb Smirnoff 		pa->pq_u.hfsc_opts.flags = opts->data.hfsc_opts.flags;
13063b3a8eb9SGleb Smirnoff 		if (opts->data.hfsc_opts.linkshare.used) {
13073b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.lssc_m1 =
13083b3a8eb9SGleb Smirnoff 			    eval_bwspec(&opts->data.hfsc_opts.linkshare.m1,
13093b3a8eb9SGleb Smirnoff 			    ref_bw);
13103b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.lssc_m2 =
13113b3a8eb9SGleb Smirnoff 			    eval_bwspec(&opts->data.hfsc_opts.linkshare.m2,
13123b3a8eb9SGleb Smirnoff 			    ref_bw);
13133b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.lssc_d =
13143b3a8eb9SGleb Smirnoff 			    opts->data.hfsc_opts.linkshare.d;
13153b3a8eb9SGleb Smirnoff 		}
13163b3a8eb9SGleb Smirnoff 		if (opts->data.hfsc_opts.realtime.used) {
13173b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.rtsc_m1 =
13183b3a8eb9SGleb Smirnoff 			    eval_bwspec(&opts->data.hfsc_opts.realtime.m1,
13193b3a8eb9SGleb Smirnoff 			    ref_bw);
13203b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.rtsc_m2 =
13213b3a8eb9SGleb Smirnoff 			    eval_bwspec(&opts->data.hfsc_opts.realtime.m2,
13223b3a8eb9SGleb Smirnoff 			    ref_bw);
13233b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.rtsc_d =
13243b3a8eb9SGleb Smirnoff 			    opts->data.hfsc_opts.realtime.d;
13253b3a8eb9SGleb Smirnoff 		}
13263b3a8eb9SGleb Smirnoff 		if (opts->data.hfsc_opts.upperlimit.used) {
13273b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.ulsc_m1 =
13283b3a8eb9SGleb Smirnoff 			    eval_bwspec(&opts->data.hfsc_opts.upperlimit.m1,
13293b3a8eb9SGleb Smirnoff 			    ref_bw);
13303b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.ulsc_m2 =
13313b3a8eb9SGleb Smirnoff 			    eval_bwspec(&opts->data.hfsc_opts.upperlimit.m2,
13323b3a8eb9SGleb Smirnoff 			    ref_bw);
13333b3a8eb9SGleb Smirnoff 			pa->pq_u.hfsc_opts.ulsc_d =
13343b3a8eb9SGleb Smirnoff 			    opts->data.hfsc_opts.upperlimit.d;
13353b3a8eb9SGleb Smirnoff 		}
13363b3a8eb9SGleb Smirnoff 		break;
1337a5b789f6SErmal Luçi 	case ALTQT_FAIRQ:
1338a5b789f6SErmal Luçi 		pa->pq_u.fairq_opts.flags = opts->data.fairq_opts.flags;
1339a5b789f6SErmal Luçi 		pa->pq_u.fairq_opts.nbuckets = opts->data.fairq_opts.nbuckets;
1340a5b789f6SErmal Luçi 		pa->pq_u.fairq_opts.hogs_m1 =
1341a5b789f6SErmal Luçi 			eval_bwspec(&opts->data.fairq_opts.hogs_bw, ref_bw);
1342a5b789f6SErmal Luçi 
1343a5b789f6SErmal Luçi 		if (opts->data.fairq_opts.linkshare.used) {
1344a5b789f6SErmal Luçi 			pa->pq_u.fairq_opts.lssc_m1 =
1345a5b789f6SErmal Luçi 			    eval_bwspec(&opts->data.fairq_opts.linkshare.m1,
1346a5b789f6SErmal Luçi 			    ref_bw);
1347a5b789f6SErmal Luçi 			pa->pq_u.fairq_opts.lssc_m2 =
1348a5b789f6SErmal Luçi 			    eval_bwspec(&opts->data.fairq_opts.linkshare.m2,
1349a5b789f6SErmal Luçi 			    ref_bw);
1350a5b789f6SErmal Luçi 			pa->pq_u.fairq_opts.lssc_d =
1351a5b789f6SErmal Luçi 			    opts->data.fairq_opts.linkshare.d;
1352a5b789f6SErmal Luçi 		}
1353a5b789f6SErmal Luçi 		break;
13540a70aaf8SLuiz Otavio O Souza 	case ALTQT_CODEL:
13550a70aaf8SLuiz Otavio O Souza 		pa->pq_u.codel_opts.target = opts->data.codel_opts.target;
13560a70aaf8SLuiz Otavio O Souza 		pa->pq_u.codel_opts.interval = opts->data.codel_opts.interval;
13570a70aaf8SLuiz Otavio O Souza 		pa->pq_u.codel_opts.ecn = opts->data.codel_opts.ecn;
13580a70aaf8SLuiz Otavio O Souza 		break;
13593b3a8eb9SGleb Smirnoff 	default:
13603b3a8eb9SGleb Smirnoff 		warnx("eval_queue_opts: unknown scheduler type %u",
13613b3a8eb9SGleb Smirnoff 		    opts->qtype);
13623b3a8eb9SGleb Smirnoff 		errors++;
13633b3a8eb9SGleb Smirnoff 		break;
13643b3a8eb9SGleb Smirnoff 	}
13653b3a8eb9SGleb Smirnoff 
13663b3a8eb9SGleb Smirnoff 	return (errors);
13673b3a8eb9SGleb Smirnoff }
13683b3a8eb9SGleb Smirnoff 
1369249cc75fSPatrick Kelsey /*
1370249cc75fSPatrick Kelsey  * If absolute bandwidth if set, return the lesser of that value and the
1371249cc75fSPatrick Kelsey  * reference bandwidth.  Limiting to the reference bandwidth allows simple
1372249cc75fSPatrick Kelsey  * limiting of configured bandwidth parameters for schedulers that are
1373249cc75fSPatrick Kelsey  * 32-bit limited, as the root/interface bandwidth (top-level reference
1374249cc75fSPatrick Kelsey  * bandwidth) will be properly limited in that case.
1375249cc75fSPatrick Kelsey  *
1376249cc75fSPatrick Kelsey  * Otherwise, if the absolute bandwidth is not set, return given percentage
1377249cc75fSPatrick Kelsey  * of reference bandwidth.
1378249cc75fSPatrick Kelsey  */
1379249cc75fSPatrick Kelsey u_int64_t
eval_bwspec(struct node_queue_bw * bw,u_int64_t ref_bw)1380249cc75fSPatrick Kelsey eval_bwspec(struct node_queue_bw *bw, u_int64_t ref_bw)
13813b3a8eb9SGleb Smirnoff {
13823b3a8eb9SGleb Smirnoff 	if (bw->bw_absolute > 0)
1383249cc75fSPatrick Kelsey 		return (MIN(bw->bw_absolute, ref_bw));
13843b3a8eb9SGleb Smirnoff 
13853b3a8eb9SGleb Smirnoff 	if (bw->bw_percent > 0)
13863b3a8eb9SGleb Smirnoff 		return (ref_bw / 100 * bw->bw_percent);
13873b3a8eb9SGleb Smirnoff 
13883b3a8eb9SGleb Smirnoff 	return (0);
13893b3a8eb9SGleb Smirnoff }
13903b3a8eb9SGleb Smirnoff 
13913b3a8eb9SGleb Smirnoff void
print_hfsc_sc(const char * scname,u_int m1,u_int d,u_int m2,const struct node_hfsc_sc * sc)13923b3a8eb9SGleb Smirnoff print_hfsc_sc(const char *scname, u_int m1, u_int d, u_int m2,
13933b3a8eb9SGleb Smirnoff     const struct node_hfsc_sc *sc)
13943b3a8eb9SGleb Smirnoff {
13953b3a8eb9SGleb Smirnoff 	printf(" %s", scname);
13963b3a8eb9SGleb Smirnoff 
13973b3a8eb9SGleb Smirnoff 	if (d != 0) {
13983b3a8eb9SGleb Smirnoff 		printf("(");
13993b3a8eb9SGleb Smirnoff 		if (sc != NULL && sc->m1.bw_percent > 0)
14003b3a8eb9SGleb Smirnoff 			printf("%u%%", sc->m1.bw_percent);
14013b3a8eb9SGleb Smirnoff 		else
14023b3a8eb9SGleb Smirnoff 			printf("%s", rate2str((double)m1));
14033b3a8eb9SGleb Smirnoff 		printf(" %u", d);
14043b3a8eb9SGleb Smirnoff 	}
14053b3a8eb9SGleb Smirnoff 
14063b3a8eb9SGleb Smirnoff 	if (sc != NULL && sc->m2.bw_percent > 0)
14073b3a8eb9SGleb Smirnoff 		printf(" %u%%", sc->m2.bw_percent);
14083b3a8eb9SGleb Smirnoff 	else
14093b3a8eb9SGleb Smirnoff 		printf(" %s", rate2str((double)m2));
14103b3a8eb9SGleb Smirnoff 
14113b3a8eb9SGleb Smirnoff 	if (d != 0)
14123b3a8eb9SGleb Smirnoff 		printf(")");
14133b3a8eb9SGleb Smirnoff }
1414a5b789f6SErmal Luçi 
1415a5b789f6SErmal Luçi void
print_fairq_sc(const char * scname,u_int m1,u_int d,u_int m2,const struct node_fairq_sc * sc)1416a5b789f6SErmal Luçi print_fairq_sc(const char *scname, u_int m1, u_int d, u_int m2,
1417a5b789f6SErmal Luçi     const struct node_fairq_sc *sc)
1418a5b789f6SErmal Luçi {
1419a5b789f6SErmal Luçi 	printf(" %s", scname);
1420a5b789f6SErmal Luçi 
1421a5b789f6SErmal Luçi 	if (d != 0) {
1422a5b789f6SErmal Luçi 		printf("(");
1423a5b789f6SErmal Luçi 		if (sc != NULL && sc->m1.bw_percent > 0)
1424a5b789f6SErmal Luçi 			printf("%u%%", sc->m1.bw_percent);
1425a5b789f6SErmal Luçi 		else
1426a5b789f6SErmal Luçi 			printf("%s", rate2str((double)m1));
1427a5b789f6SErmal Luçi 		printf(" %u", d);
1428a5b789f6SErmal Luçi 	}
1429a5b789f6SErmal Luçi 
1430a5b789f6SErmal Luçi 	if (sc != NULL && sc->m2.bw_percent > 0)
1431a5b789f6SErmal Luçi 		printf(" %u%%", sc->m2.bw_percent);
1432a5b789f6SErmal Luçi 	else
1433a5b789f6SErmal Luçi 		printf(" %s", rate2str((double)m2));
1434a5b789f6SErmal Luçi 
1435a5b789f6SErmal Luçi 	if (d != 0)
1436a5b789f6SErmal Luçi 		printf(")");
1437a5b789f6SErmal Luçi }
1438