xref: /netbsd-src/usr.sbin/altq/libaltq/qop.c (revision 89ff1cb68ca175c833d375b99777073afd0d05eb)
1*89ff1cb6Sozaki-r /*	$NetBSD: qop.c,v 1.13 2024/12/24 08:35:28 ozaki-r Exp $	*/
269881cf6Sitojun /*	$KAME: qop.c,v 1.11 2001/10/26 04:57:59 kjc Exp $	*/
395a65560Sthorpej /*
495a65560Sthorpej  * Copyright (C) 1999-2000
595a65560Sthorpej  *	Sony Computer Science Laboratories, Inc.  All rights reserved.
695a65560Sthorpej  *
795a65560Sthorpej  * Redistribution and use in source and binary forms, with or without
895a65560Sthorpej  * modification, are permitted provided that the following conditions
995a65560Sthorpej  * are met:
1095a65560Sthorpej  * 1. Redistributions of source code must retain the above copyright
1195a65560Sthorpej  *    notice, this list of conditions and the following disclaimer.
1295a65560Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
1395a65560Sthorpej  *    notice, this list of conditions and the following disclaimer in the
1495a65560Sthorpej  *    documentation and/or other materials provided with the distribution.
1595a65560Sthorpej  *
1695a65560Sthorpej  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
1795a65560Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1895a65560Sthorpej  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1995a65560Sthorpej  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
2095a65560Sthorpej  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2195a65560Sthorpej  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2295a65560Sthorpej  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2395a65560Sthorpej  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2495a65560Sthorpej  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2595a65560Sthorpej  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2695a65560Sthorpej  * SUCH DAMAGE.
2795a65560Sthorpej  */
2895a65560Sthorpej 
2995a65560Sthorpej #include <sys/param.h>
3095a65560Sthorpej #include <sys/socket.h>
3195a65560Sthorpej #include <sys/sockio.h>
3295a65560Sthorpej #include <sys/ioctl.h>
3395a65560Sthorpej #include <sys/fcntl.h>
3495a65560Sthorpej #include <sys/stat.h>
3595a65560Sthorpej #if defined(__FreeBSD__) && (__FreeBSD_version > 300000)
3695a65560Sthorpej #include <sys/linker.h>
3795a65560Sthorpej #endif
3895a65560Sthorpej 
3995a65560Sthorpej #include <net/if.h>
4095a65560Sthorpej #include <netinet/in.h>
4195a65560Sthorpej #include <arpa/inet.h>
4295a65560Sthorpej #include <stdio.h>
4395a65560Sthorpej #include <stdlib.h>
4495a65560Sthorpej #include <unistd.h>
4595a65560Sthorpej #include <stddef.h>
4695a65560Sthorpej #include <string.h>
4795a65560Sthorpej #include <ctype.h>
4895a65560Sthorpej #include <errno.h>
4995a65560Sthorpej #include <err.h>
5095a65560Sthorpej #include <syslog.h>
5195a65560Sthorpej 
5295a65560Sthorpej #include <altq/altq.h>
5395a65560Sthorpej #include <altq/altq_red.h>
5495a65560Sthorpej #include <altq/altq_rio.h>
5595a65560Sthorpej #include <altq/altq_cdnr.h>
5695a65560Sthorpej #include "altq_qop.h"
5795a65560Sthorpej #include "qop_cdnr.h"
5895a65560Sthorpej 
5995a65560Sthorpej #define	ALTQ_DEVICE	"/dev/altq/altq"
6095a65560Sthorpej #define RED_DEVICE	"/dev/altq/red"
6195a65560Sthorpej #define RIO_DEVICE	"/dev/altq/rio"
6295a65560Sthorpej #define CDNR_DEVICE	"/dev/altq/cdnr"
6395a65560Sthorpej 
6495a65560Sthorpej #ifndef LIST_HEAD_INITIALIZER
6595a65560Sthorpej #define LIST_HEAD_INITIALIZER(head)	{ NULL }
6695a65560Sthorpej #endif
6795a65560Sthorpej 
6895a65560Sthorpej /*
6995a65560Sthorpej  * token bucket regulator information
7095a65560Sthorpej  */
7195a65560Sthorpej struct tbrinfo {
7295a65560Sthorpej 	LIST_ENTRY(tbrinfo) link;
7395a65560Sthorpej 	char	ifname[IFNAMSIZ];	/* if name, e.g. "en0" */
7495a65560Sthorpej 	struct tb_profile tb_prof, otb_prof;
7595a65560Sthorpej 	int installed;
7695a65560Sthorpej };
7795a65560Sthorpej 
7895a65560Sthorpej /*
7995a65560Sthorpej  * Static globals
8095a65560Sthorpej  */
8195a65560Sthorpej /* a list of configured interfaces */
8295a65560Sthorpej LIST_HEAD(qop_iflist, ifinfo)	qop_iflist = LIST_HEAD_INITIALIZER(&iflist);
8395a65560Sthorpej /* a list of configured token bucket regulators */
8495a65560Sthorpej LIST_HEAD(tbr_list, tbrinfo)	tbr_list = LIST_HEAD_INITIALIZER(&tbr_list);
8595a65560Sthorpej 
8695a65560Sthorpej /*
8795a65560Sthorpej  * internal functions
8895a65560Sthorpej  */
898b8734d2Sitojun static int get_ifmtu(const char *);
908b8734d2Sitojun static void tbr_install(const char *);
918b8734d2Sitojun static void tbr_deinstall(const char *);
928b8734d2Sitojun static int add_filter_rule(struct ifinfo *, struct fltrinfo *,
938b8734d2Sitojun 			   struct fltrinfo **);
948b8734d2Sitojun static int remove_filter_rule(struct ifinfo *,
958b8734d2Sitojun 			      struct fltrinfo *);
968b8734d2Sitojun static int filt_check_relation(struct flow_filter *, struct flow_filter *);
978b8734d2Sitojun static int filt_disjoint(struct flow_filter *, struct flow_filter *);
988b8734d2Sitojun static int filt_subset(struct flow_filter *, struct flow_filter *);
9995a65560Sthorpej 
10095a65560Sthorpej /*
10195a65560Sthorpej  * QCMD (Queue Command) API
10295a65560Sthorpej  */
10395a65560Sthorpej int
10495a65560Sthorpej qcmd_init(void)
10595a65560Sthorpej {
10695a65560Sthorpej 	int error;
10795a65560Sthorpej 
10895a65560Sthorpej 	/* read config file and execute commands */
10995a65560Sthorpej 	error = qcmd_config();
11095a65560Sthorpej 	if (error != 0)
111d5e1f166Sitojun 		return (error);
112d5e1f166Sitojun 
113d5e1f166Sitojun 	error = qcmd_enableall();
114d5e1f166Sitojun 	if (error != 0)
115d5e1f166Sitojun 		LOG(LOG_ERR, errno, "%s: qcmd_init failed", qoperror(error));
11695a65560Sthorpej 	return (error);
11795a65560Sthorpej }
11895a65560Sthorpej 
11995a65560Sthorpej int
12095a65560Sthorpej qcmd_enable(const char *ifname)
12195a65560Sthorpej {
12295a65560Sthorpej 	struct ifinfo	*ifinfo;
12395a65560Sthorpej 	int error = 0;
12495a65560Sthorpej 
12595a65560Sthorpej 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
12695a65560Sthorpej 		error = QOPERR_BADIF;
12795a65560Sthorpej 
12895a65560Sthorpej 	if (error == 0)
12995a65560Sthorpej 		error = qop_enable(ifinfo);
13095a65560Sthorpej 
13195a65560Sthorpej 	if (error == 0) {
132d5e1f166Sitojun 		LOG(LOG_INFO, 0, "%s enabled on interface %s (mtu:%d)",
13395a65560Sthorpej 		    ifinfo->qdisc->qname, ifname, ifinfo->ifmtu);
13495a65560Sthorpej 	} else
135d5e1f166Sitojun 		LOG(LOG_ERR, errno, "%s: enable failed!", qoperror(error));
13695a65560Sthorpej 	return (error);
13795a65560Sthorpej }
13895a65560Sthorpej 
13995a65560Sthorpej int
14095a65560Sthorpej qcmd_disable(const char *ifname)
14195a65560Sthorpej {
14295a65560Sthorpej 	struct ifinfo	*ifinfo;
14395a65560Sthorpej 	int error = 0;
14495a65560Sthorpej 
14595a65560Sthorpej 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
14695a65560Sthorpej 		error = QOPERR_BADIF;
14795a65560Sthorpej 
14895a65560Sthorpej 	if (error == 0)
14995a65560Sthorpej 		error = qop_disable(ifinfo);
15095a65560Sthorpej 
15195a65560Sthorpej 	if (error != 0)
152d5e1f166Sitojun 		LOG(LOG_ERR, errno, "%s: disable failed!", qoperror(error));
15395a65560Sthorpej 	return (error);
15495a65560Sthorpej }
15595a65560Sthorpej 
15695a65560Sthorpej int
15795a65560Sthorpej qcmd_enableall()
15895a65560Sthorpej {
15995a65560Sthorpej 	struct ifinfo	*ifinfo;
16095a65560Sthorpej 	int error;
16195a65560Sthorpej 
16295a65560Sthorpej 	LIST_FOREACH(ifinfo, &qop_iflist, next) {
16395a65560Sthorpej 		if ((error = qop_enable(ifinfo)) != 0)
16495a65560Sthorpej 			return (error);
165d5e1f166Sitojun 		LOG(LOG_INFO, 0, "%s enabled on interface %s (mtu:%d)",
16695a65560Sthorpej 		    ifinfo->qdisc->qname, ifinfo->ifname, ifinfo->ifmtu);
16795a65560Sthorpej 	}
16895a65560Sthorpej 	return (0);
16995a65560Sthorpej }
17095a65560Sthorpej 
17195a65560Sthorpej int
17295a65560Sthorpej qcmd_disableall()
17395a65560Sthorpej {
17495a65560Sthorpej 	struct ifinfo	*ifinfo;
1757097d2c1Sxtraeme 	int	lerr, error = 0;
17695a65560Sthorpej 
17795a65560Sthorpej 	LIST_FOREACH(ifinfo, &qop_iflist, next)
1787097d2c1Sxtraeme 		if ((lerr = qop_disable(ifinfo)) != 0)
17995a65560Sthorpej 			if (error == 0)
1807097d2c1Sxtraeme 				error = lerr;
18195a65560Sthorpej 	return (error);
18295a65560Sthorpej }
18395a65560Sthorpej 
18495a65560Sthorpej int
18595a65560Sthorpej qcmd_clear(const char *ifname)
18695a65560Sthorpej {
18795a65560Sthorpej 	struct ifinfo	*ifinfo;
18895a65560Sthorpej 	int error = 0;
18995a65560Sthorpej 
19095a65560Sthorpej 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
19195a65560Sthorpej 		error = QOPERR_BADIF;
19295a65560Sthorpej 
19395a65560Sthorpej 	if (error == 0)
19495a65560Sthorpej 		error = qop_clear(ifinfo);
19595a65560Sthorpej 	if (error != 0)
196d5e1f166Sitojun 		LOG(LOG_ERR, errno, "%s: clear failed!", qoperror(error));
19795a65560Sthorpej 	return (error);
19895a65560Sthorpej }
19995a65560Sthorpej 
20095a65560Sthorpej int
20195a65560Sthorpej qcmd_destroyall(void)
20295a65560Sthorpej {
20395a65560Sthorpej 	while (!LIST_EMPTY(&qop_iflist))
20495a65560Sthorpej 		(void)qop_delete_if(LIST_FIRST(&qop_iflist));
20595a65560Sthorpej 	return (0);
20695a65560Sthorpej }
20795a65560Sthorpej 
20895a65560Sthorpej int
20995a65560Sthorpej qcmd_restart(void)
21095a65560Sthorpej {
21195a65560Sthorpej 	qcmd_destroyall();
21295a65560Sthorpej 	return qcmd_init();
21395a65560Sthorpej }
21495a65560Sthorpej 
21595a65560Sthorpej int
21695a65560Sthorpej qcmd_delete_class(const char *ifname, const char *clname)
21795a65560Sthorpej {
21895a65560Sthorpej 	struct ifinfo		*ifinfo;
2197cc15c30She 	struct classinfo	*clinfo = NULL;
22095a65560Sthorpej 	int error = 0;
22195a65560Sthorpej 
22295a65560Sthorpej 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
22395a65560Sthorpej 		error = QOPERR_BADIF;
22495a65560Sthorpej 
22595a65560Sthorpej 	if (error == 0 &&
22695a65560Sthorpej 	    (clinfo = clname2clinfo(ifinfo, clname)) == NULL)
22795a65560Sthorpej 		error = QOPERR_BADCLASS;
22895a65560Sthorpej 
22995a65560Sthorpej 	if (error == 0)
23095a65560Sthorpej 		error = qop_delete_class(clinfo);
23195a65560Sthorpej 	if (error != 0)
232d5e1f166Sitojun 		LOG(LOG_ERR, errno, "%s: delete_class failed",
23395a65560Sthorpej 		    qoperror(error));
23495a65560Sthorpej 	return (error);
23595a65560Sthorpej }
23695a65560Sthorpej 
23795a65560Sthorpej int
23895a65560Sthorpej qcmd_add_filter(const char *ifname, const char *clname, const char *flname,
23995a65560Sthorpej 		 const struct flow_filter *fltr)
24095a65560Sthorpej {
24195a65560Sthorpej 	struct ifinfo		*ifinfo;
2427cc15c30She 	struct classinfo	*clinfo = NULL;
24395a65560Sthorpej 	int error = 0;
24495a65560Sthorpej 
24595a65560Sthorpej 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
24695a65560Sthorpej 		error = QOPERR_BADIF;
24795a65560Sthorpej 
24895a65560Sthorpej 	if (error == 0 &&
24995a65560Sthorpej 	    (clinfo = clname2clinfo(ifinfo, clname)) == NULL) {
25095a65560Sthorpej 		/*
25195a65560Sthorpej 		 * there is no matching class.
25295a65560Sthorpej 		 * check if it is for a traffic conditioner
25395a65560Sthorpej 		 */
25495a65560Sthorpej 		if ((ifinfo = input_ifname2ifinfo(ifname)) == NULL ||
25595a65560Sthorpej 		    (clinfo = clname2clinfo(ifinfo, clname)) == NULL)
25695a65560Sthorpej 			error = QOPERR_BADCLASS;
25795a65560Sthorpej 	}
25895a65560Sthorpej 
25995a65560Sthorpej 	if (error == 0)
26095a65560Sthorpej 		error = qop_add_filter(NULL, clinfo, flname, fltr, NULL);
26195a65560Sthorpej 
26295a65560Sthorpej 	if (error != 0)
263d5e1f166Sitojun 		LOG(LOG_ERR, errno, "%s: add filter failed!",
26495a65560Sthorpej 		    qoperror(error));
26595a65560Sthorpej 	else if (IsDebug(DEBUG_ALTQ)) {
266d5e1f166Sitojun 		LOG(LOG_DEBUG, 0, "%s: add a filter %s to class %s",
26795a65560Sthorpej 		    ifname, flname ? flname : "(null)",
26895a65560Sthorpej 		    clname ? clname : "(null)");
26995a65560Sthorpej 		print_filter(fltr);
27095a65560Sthorpej 	}
27195a65560Sthorpej 	return (error);
27295a65560Sthorpej }
27395a65560Sthorpej 
27495a65560Sthorpej int
27595a65560Sthorpej qcmd_delete_filter(const char *ifname, const char *clname, const char *flname)
27695a65560Sthorpej {
27795a65560Sthorpej 	struct ifinfo		*ifinfo;
2787cc15c30She 	struct classinfo	*clinfo = NULL;
2797cc15c30She 	struct fltrinfo		*fltrinfo = NULL;
28095a65560Sthorpej 	int error = 0;
28195a65560Sthorpej 
28295a65560Sthorpej 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
28395a65560Sthorpej 		error = QOPERR_BADIF;
28495a65560Sthorpej 
28595a65560Sthorpej 	if (error == 0 &&
28695a65560Sthorpej 	    (clinfo = clname2clinfo(ifinfo, clname)) == NULL) {
28795a65560Sthorpej 		/*
28895a65560Sthorpej 		 * there is no matching class.
28995a65560Sthorpej 		 * check if it is for a traffic conditioner
29095a65560Sthorpej 		 */
29195a65560Sthorpej 		if ((ifinfo = input_ifname2ifinfo(ifname)) == NULL ||
29295a65560Sthorpej 		    (clinfo = clname2clinfo(ifinfo, clname)) == NULL)
29395a65560Sthorpej 		error = QOPERR_BADCLASS;
29495a65560Sthorpej 	}
29595a65560Sthorpej 
29695a65560Sthorpej 	if (error == 0 &&
29795a65560Sthorpej 	    (fltrinfo = flname2flinfo(clinfo, flname)) == NULL)
29895a65560Sthorpej 		error = QOPERR_BADFILTER;
29995a65560Sthorpej 
30095a65560Sthorpej 	if (error == 0)
30195a65560Sthorpej 		error = qop_delete_filter(fltrinfo);
30295a65560Sthorpej 	if (error != 0)
303d5e1f166Sitojun 		LOG(LOG_ERR, errno, "%s: delete filter failed!",
30495a65560Sthorpej 		    qoperror(error));
30595a65560Sthorpej 	return (error);
30695a65560Sthorpej }
30795a65560Sthorpej 
30895a65560Sthorpej int
309*89ff1cb6Sozaki-r qcmd_tbr_register(const char *ifname, uint64_t rate, u_int size)
31095a65560Sthorpej {
31195a65560Sthorpej 	struct tbrinfo *info;
31295a65560Sthorpej 
31395a65560Sthorpej 	if ((info = calloc(1, sizeof(struct tbrinfo))) == NULL)
31495a65560Sthorpej 		return (QOPERR_NOMEM);
31595a65560Sthorpej 
3168b8734d2Sitojun 	strlcpy(info->ifname, ifname, sizeof(info->ifname));
31795a65560Sthorpej 	info->tb_prof.rate = rate;
31895a65560Sthorpej 	info->tb_prof.depth = size;
31995a65560Sthorpej 	info->installed = 0;
32095a65560Sthorpej 	LIST_INSERT_HEAD(&tbr_list, info, link);
32195a65560Sthorpej 	return (0);
32295a65560Sthorpej }
32395a65560Sthorpej 
32495a65560Sthorpej /*
32595a65560Sthorpej  * QOP (Queue Operation) API
32695a65560Sthorpej  */
32795a65560Sthorpej 
32895a65560Sthorpej int
329*89ff1cb6Sozaki-r qop_add_if(struct ifinfo **rp, const char *ifname, uint64_t bandwidth,
33095a65560Sthorpej 	   struct qdisc_ops *qdisc_ops, void *if_private)
33195a65560Sthorpej {
33295a65560Sthorpej 	struct ifinfo	*ifinfo;
33395a65560Sthorpej 	int error;
33495a65560Sthorpej 
33595a65560Sthorpej 	if (ifname2ifinfo(ifname) != NULL) {
336d5e1f166Sitojun 		LOG(LOG_ERR, 0, "qop_add_if: %s already exists!", ifname);
33795a65560Sthorpej 		return (QOPERR_BADIF);
33895a65560Sthorpej 	}
33995a65560Sthorpej 
34095a65560Sthorpej 	if ((ifinfo = calloc(1, sizeof(struct ifinfo))) == NULL)
34195a65560Sthorpej 		return (QOPERR_NOMEM);
34295a65560Sthorpej 	ifinfo->ifname = strdup(ifname);
34395a65560Sthorpej 	ifinfo->bandwidth = bandwidth;
34495a65560Sthorpej 	ifinfo->enabled = 0;
34595a65560Sthorpej 	if (ifname[0] == '_')
34695a65560Sthorpej 		/* input interface */
34795a65560Sthorpej 		ifname += 1;
34895a65560Sthorpej 	ifinfo->ifindex = get_ifindex(ifname);
34995a65560Sthorpej 	ifinfo->ifmtu = get_ifmtu(ifname);
35091c703aeSpeter 	if (qdisc_ops == NULL)
35195a65560Sthorpej 		ifinfo->qdisc = &nop_qdisc; /* replace syscalls by nops */
35295a65560Sthorpej 	else
35395a65560Sthorpej 		ifinfo->qdisc = qdisc_ops;
35495a65560Sthorpej 	ifinfo->private = if_private;
35595a65560Sthorpej 	LIST_INIT(&ifinfo->cllist);
35695a65560Sthorpej 	LIST_INIT(&ifinfo->fltr_rules);
35795a65560Sthorpej 
35895a65560Sthorpej 	/* Link the interface info structure */
35995a65560Sthorpej 	LIST_INSERT_HEAD(&qop_iflist, ifinfo, next);
36095a65560Sthorpej 
36195a65560Sthorpej 	/* install token bucket regulator, if necessary */
36295a65560Sthorpej 	tbr_install(ifname);
36395a65560Sthorpej 
36495a65560Sthorpej 	/* attach the discipline to the interface */
36595a65560Sthorpej 	if ((error = (*ifinfo->qdisc->attach)(ifinfo)) != 0)
36695a65560Sthorpej 		goto err_ret;
36795a65560Sthorpej 
36895a65560Sthorpej 	/* disable and clear the interface */
36995a65560Sthorpej 	if (ifinfo->qdisc->disable != NULL)
37095a65560Sthorpej 		if ((error = (*ifinfo->qdisc->disable)(ifinfo)) != 0)
37195a65560Sthorpej 			goto err_ret;
37295a65560Sthorpej 	if (ifinfo->qdisc->clear != NULL)
37395a65560Sthorpej 		if ((error = (*ifinfo->qdisc->clear)(ifinfo)) != 0)
37495a65560Sthorpej 			goto err_ret;
37595a65560Sthorpej 
37695a65560Sthorpej 	if (rp != NULL)
37795a65560Sthorpej 		*rp = ifinfo;
37895a65560Sthorpej 	return (0);
37995a65560Sthorpej 
38095a65560Sthorpej err_ret:
38195a65560Sthorpej 	if (ifinfo != NULL) {
38295a65560Sthorpej 		LIST_REMOVE(ifinfo, next);
38395a65560Sthorpej 		if (ifinfo->ifname != NULL)
38495a65560Sthorpej 			free(ifinfo->ifname);
38595a65560Sthorpej 		free(ifinfo);
38695a65560Sthorpej 	}
38795a65560Sthorpej 	return (error);
38895a65560Sthorpej }
38995a65560Sthorpej 
39095a65560Sthorpej int
39195a65560Sthorpej qop_delete_if(struct ifinfo *ifinfo)
39295a65560Sthorpej {
39395a65560Sthorpej 	(void)qop_disable(ifinfo);
39495a65560Sthorpej 	(void)qop_clear(ifinfo);
39595a65560Sthorpej 
39695a65560Sthorpej 	if (ifinfo->delete_hook != NULL)
39795a65560Sthorpej 		(*ifinfo->delete_hook)(ifinfo);
39895a65560Sthorpej 
39995a65560Sthorpej 	/* remove this entry from qop_iflist */
40095a65560Sthorpej 	LIST_REMOVE(ifinfo, next);
40195a65560Sthorpej 
40295a65560Sthorpej 	(void)(*ifinfo->qdisc->detach)(ifinfo);
40395a65560Sthorpej 
40495a65560Sthorpej 	/* deinstall token bucket regulator, if necessary */
40595a65560Sthorpej 	tbr_deinstall(ifinfo->ifname);
40695a65560Sthorpej 
40795a65560Sthorpej 	if (ifinfo->private != NULL)
40895a65560Sthorpej 		free(ifinfo->private);
40995a65560Sthorpej 	if (ifinfo->ifname != NULL)
41095a65560Sthorpej 		free(ifinfo->ifname);
41195a65560Sthorpej 	free(ifinfo);
41295a65560Sthorpej 	return (0);
41395a65560Sthorpej }
41495a65560Sthorpej 
41595a65560Sthorpej int
41695a65560Sthorpej qop_enable(struct ifinfo *ifinfo)
41795a65560Sthorpej {
41895a65560Sthorpej 	int error;
41995a65560Sthorpej 
42095a65560Sthorpej 	if (ifinfo->enable_hook != NULL)
42195a65560Sthorpej 		if ((error = (*ifinfo->enable_hook)(ifinfo)) != 0)
42295a65560Sthorpej 			return (error);
42395a65560Sthorpej 
42495a65560Sthorpej 	if (ifinfo->qdisc->enable != NULL)
42595a65560Sthorpej 		if ((error = (*ifinfo->qdisc->enable)(ifinfo)) != 0)
42695a65560Sthorpej 			return (error);
42795a65560Sthorpej 	ifinfo->enabled = 1;
42895a65560Sthorpej 	return (0);
42995a65560Sthorpej }
43095a65560Sthorpej 
43195a65560Sthorpej int
43295a65560Sthorpej qop_disable(struct ifinfo *ifinfo)
43395a65560Sthorpej {
43495a65560Sthorpej 	int error;
43595a65560Sthorpej 
43695a65560Sthorpej 	if (ifinfo->qdisc->disable != NULL)
43795a65560Sthorpej 		if ((error = (*ifinfo->qdisc->disable)(ifinfo)) != 0)
43895a65560Sthorpej 			return (error);
43995a65560Sthorpej 	ifinfo->enabled = 0;
44095a65560Sthorpej 	return (0);
44195a65560Sthorpej }
44295a65560Sthorpej 
44395a65560Sthorpej int
44495a65560Sthorpej qop_clear(struct ifinfo *ifinfo)
44595a65560Sthorpej {
44695a65560Sthorpej 	struct classinfo	*clinfo;
44795a65560Sthorpej 
44895a65560Sthorpej 	/* free all classes and filters */
44995a65560Sthorpej 	if (ifinfo->ifname[0] != '_') {
45095a65560Sthorpej 		/* output interface.  delete from leaf classes */
45195a65560Sthorpej 		while (!LIST_EMPTY(&ifinfo->cllist)) {
45295a65560Sthorpej 			LIST_FOREACH(clinfo, &ifinfo->cllist, next) {
45395a65560Sthorpej 				if (clinfo->child != NULL)
45495a65560Sthorpej 					continue;
45595a65560Sthorpej 				qop_delete_class(clinfo);
45695a65560Sthorpej 				/*
45795a65560Sthorpej 				 * the list has been changed,
45895a65560Sthorpej 				 *  restart from the head
45995a65560Sthorpej 				 */
46095a65560Sthorpej 				break;
46195a65560Sthorpej 			}
46295a65560Sthorpej 		}
46395a65560Sthorpej 	} else {
46495a65560Sthorpej 		/* input interface. delete from parents */
46595a65560Sthorpej 		struct classinfo *root = get_rootclass(ifinfo);
46695a65560Sthorpej 
46795a65560Sthorpej 		while (!LIST_EMPTY(&ifinfo->cllist)) {
46895a65560Sthorpej 			LIST_FOREACH(clinfo, &ifinfo->cllist, next)
46995a65560Sthorpej 				if (clinfo->parent == root) {
47095a65560Sthorpej 					qop_delete_cdnr(clinfo);
47195a65560Sthorpej 					break;
47295a65560Sthorpej 				}
473e02941daSchristos 			if (root->child != NULL)
47495a65560Sthorpej 				qop_delete_class(root);
47595a65560Sthorpej 		}
47695a65560Sthorpej 	}
47795a65560Sthorpej 
47895a65560Sthorpej 	/* clear the interface */
47995a65560Sthorpej 	if (ifinfo->qdisc->clear != NULL)
48095a65560Sthorpej 		return (*ifinfo->qdisc->clear)(ifinfo);
48195a65560Sthorpej 	return (0);
48295a65560Sthorpej }
48395a65560Sthorpej 
48495a65560Sthorpej int
48595a65560Sthorpej qop_add_class(struct classinfo **rp, const char *clname,
48695a65560Sthorpej 	      struct ifinfo *ifinfo, struct classinfo *parent,
48795a65560Sthorpej 	      void *class_private)
48895a65560Sthorpej {
48995a65560Sthorpej 	struct classinfo	*clinfo;
49095a65560Sthorpej 	int error;
49195a65560Sthorpej 
49295a65560Sthorpej 	if ((clinfo = calloc(1, sizeof(*clinfo))) == NULL)
49395a65560Sthorpej 		return (QOPERR_NOMEM);
49495a65560Sthorpej 
49595a65560Sthorpej 	if (clname != NULL)
49695a65560Sthorpej 		clinfo->clname = strdup(clname);
49795a65560Sthorpej 	else
49895a65560Sthorpej 		clinfo->clname = strdup("(null)");  /* dummy name */
49995a65560Sthorpej 	clinfo->ifinfo = ifinfo;
50095a65560Sthorpej 	clinfo->private = class_private;
50195a65560Sthorpej 	clinfo->parent = parent;
50295a65560Sthorpej 	clinfo->child = NULL;
50395a65560Sthorpej 	LIST_INIT(&clinfo->fltrlist);
50495a65560Sthorpej 
50595a65560Sthorpej 	if ((error = (*ifinfo->qdisc->add_class)(clinfo)) != 0)
50695a65560Sthorpej 		goto err_ret;
50795a65560Sthorpej 
50895a65560Sthorpej 	/* link classinfo in lists */
50995a65560Sthorpej 	LIST_INSERT_HEAD(&ifinfo->cllist, clinfo, next);
51095a65560Sthorpej 
51195a65560Sthorpej 	if (parent != NULL) {
51295a65560Sthorpej 		clinfo->sibling = parent->child;
51395a65560Sthorpej 		clinfo->parent->child = clinfo;
51495a65560Sthorpej 	}
51595a65560Sthorpej 
51695a65560Sthorpej 	if (rp != NULL)
51795a65560Sthorpej 		*rp = clinfo;
51895a65560Sthorpej 	return (0);
51995a65560Sthorpej 
52095a65560Sthorpej err_ret:
52195a65560Sthorpej 	if (clinfo != NULL) {
52295a65560Sthorpej 		if (clinfo->clname != NULL)
52395a65560Sthorpej 			free(clinfo->clname);
52495a65560Sthorpej 		free(clinfo);
52595a65560Sthorpej 	}
52695a65560Sthorpej 	return (error);
52795a65560Sthorpej }
52895a65560Sthorpej 
52995a65560Sthorpej int
53095a65560Sthorpej qop_modify_class(struct classinfo *clinfo, void *arg)
53195a65560Sthorpej {
53295a65560Sthorpej 	return (*clinfo->ifinfo->qdisc->modify_class)(clinfo, arg);
53395a65560Sthorpej }
53495a65560Sthorpej 
53595a65560Sthorpej int
53695a65560Sthorpej qop_delete_class(struct classinfo *clinfo)
53795a65560Sthorpej {
53895a65560Sthorpej 	struct ifinfo		*ifinfo = clinfo->ifinfo;
53995a65560Sthorpej 	struct classinfo	*prev;
54095a65560Sthorpej 	int error;
54195a65560Sthorpej 
54295a65560Sthorpej 	/* a class to be removed should not have a child */
54395a65560Sthorpej 	if (clinfo->child != NULL)
54495a65560Sthorpej 		return (QOPERR_CLASS_PERM);
54595a65560Sthorpej 
54695a65560Sthorpej 	/* remove filters associated to this class */
54795a65560Sthorpej 	while (!LIST_EMPTY(&clinfo->fltrlist))
54895a65560Sthorpej 		(void)qop_delete_filter(LIST_FIRST(&clinfo->fltrlist));
54995a65560Sthorpej 
55095a65560Sthorpej 	if (clinfo->delete_hook != NULL)
55195a65560Sthorpej 		(*clinfo->delete_hook)(clinfo);
55295a65560Sthorpej 
55395a65560Sthorpej 	/* remove class info from the interface */
55495a65560Sthorpej 	LIST_REMOVE(clinfo, next);
55595a65560Sthorpej 
55695a65560Sthorpej 	/* remove this class from the child list */
55795a65560Sthorpej 	if (clinfo->parent != NULL) {
55895a65560Sthorpej 		if (clinfo->parent->child == clinfo)
55995a65560Sthorpej 			clinfo->parent->child = clinfo->sibling;
56095a65560Sthorpej 		else for (prev = clinfo->parent->child; prev->sibling != NULL;
56195a65560Sthorpej 			  prev = prev->sibling)
56295a65560Sthorpej 			if (prev->sibling == clinfo) {
56395a65560Sthorpej 				prev->sibling = clinfo->sibling;
56495a65560Sthorpej 				break;
56595a65560Sthorpej 			}
56695a65560Sthorpej 	}
56795a65560Sthorpej 
56895a65560Sthorpej 	/* delete class from kernel */
56995a65560Sthorpej 	if ((error = (*ifinfo->qdisc->delete_class)(clinfo)) != 0)
57095a65560Sthorpej 		return (error);
57195a65560Sthorpej 
57295a65560Sthorpej 	if (clinfo->private != NULL)
57395a65560Sthorpej 		free(clinfo->private);
57495a65560Sthorpej 	if (clinfo->clname != NULL)
57595a65560Sthorpej 		free(clinfo->clname);
57695a65560Sthorpej 	free(clinfo);
57795a65560Sthorpej 	return (0);
57895a65560Sthorpej }
57995a65560Sthorpej 
58095a65560Sthorpej int
58195a65560Sthorpej qop_add_filter(struct fltrinfo **rp, struct classinfo *clinfo,
58295a65560Sthorpej 		   const char *flname, const struct flow_filter *fltr,
58395a65560Sthorpej 		   struct fltrinfo **conflict)
58495a65560Sthorpej {
58595a65560Sthorpej 	struct ifinfo	*ifinfo;
58695a65560Sthorpej 	struct fltrinfo *fltrinfo;
58795a65560Sthorpej 	int error;
58895a65560Sthorpej 
58995a65560Sthorpej 	if ((fltrinfo = calloc(1, sizeof(*fltrinfo))) == NULL)
59095a65560Sthorpej 		return (QOPERR_NOMEM);
59195a65560Sthorpej 
59295a65560Sthorpej 	fltrinfo->clinfo = clinfo;
59395a65560Sthorpej 	fltrinfo->fltr = *fltr;
59495a65560Sthorpej #if 1
59595a65560Sthorpej 	/* fix this */
59695a65560Sthorpej 	fltrinfo->line_no = line_no;		/* XXX */
59795a65560Sthorpej 	fltrinfo->dontwarn = filter_dontwarn;	/* XXX */
59895a65560Sthorpej #endif
59995a65560Sthorpej 	if (flname != NULL)
60095a65560Sthorpej 		fltrinfo->flname = strdup(flname);
60195a65560Sthorpej 	else
60295a65560Sthorpej 		fltrinfo->flname = strdup("(null)");  /* dummy name */
60395a65560Sthorpej 
60495a65560Sthorpej 	/* check and save the filter */
60595a65560Sthorpej 	ifinfo = clinfo->ifinfo;
60695a65560Sthorpej 	if ((error = add_filter_rule(ifinfo, fltrinfo, conflict)) != 0)
60795a65560Sthorpej 		goto err_ret;
60895a65560Sthorpej 
60995a65560Sthorpej 	/* install the filter to the kernel */
61095a65560Sthorpej 	if ((error = (*ifinfo->qdisc->add_filter)(fltrinfo)) != 0) {
61195a65560Sthorpej 		remove_filter_rule(ifinfo, fltrinfo);
61295a65560Sthorpej 		goto err_ret;
61395a65560Sthorpej 	}
61495a65560Sthorpej 
61595a65560Sthorpej 	/* link fltrinfo onto fltrlist of the class */
61695a65560Sthorpej 	LIST_INSERT_HEAD(&clinfo->fltrlist, fltrinfo, next);
61795a65560Sthorpej 
61895a65560Sthorpej 	if (rp != NULL)
61995a65560Sthorpej 		*rp = fltrinfo;
62095a65560Sthorpej 	return (0);
62195a65560Sthorpej 
62295a65560Sthorpej err_ret:
62395a65560Sthorpej 	if (fltrinfo != NULL) {
62495a65560Sthorpej 		if (fltrinfo->flname != NULL)
62595a65560Sthorpej 			free(fltrinfo->flname);
62695a65560Sthorpej 		free(fltrinfo);
62795a65560Sthorpej 	}
62895a65560Sthorpej 	return (error);
62995a65560Sthorpej }
63095a65560Sthorpej 
63195a65560Sthorpej int
63295a65560Sthorpej qop_delete_filter(struct fltrinfo *fltrinfo)
63395a65560Sthorpej {
63495a65560Sthorpej 	struct ifinfo		*ifinfo;
63595a65560Sthorpej 	struct classinfo	*clinfo;
63695a65560Sthorpej 	int error;
63795a65560Sthorpej 
63895a65560Sthorpej 	/* remove filter info from the class */
63995a65560Sthorpej 	clinfo = fltrinfo->clinfo;
64095a65560Sthorpej 	ifinfo = clinfo->ifinfo;
64195a65560Sthorpej 
64295a65560Sthorpej 
64395a65560Sthorpej 	/* remove the entry from fltrlist of the class */
64495a65560Sthorpej 	LIST_REMOVE(fltrinfo, next);
64595a65560Sthorpej 
64695a65560Sthorpej 	remove_filter_rule(ifinfo, fltrinfo);
64795a65560Sthorpej 
64895a65560Sthorpej 	/* delete filter from kernel */
64995a65560Sthorpej 	if ((error = (*ifinfo->qdisc->delete_filter)(fltrinfo)) != 0)
65095a65560Sthorpej 		return (error);
65195a65560Sthorpej 
65295a65560Sthorpej 	if (fltrinfo->flname)
65395a65560Sthorpej 		free(fltrinfo->flname);
65495a65560Sthorpej 	free(fltrinfo);
65595a65560Sthorpej 	return (0);
65695a65560Sthorpej }
65795a65560Sthorpej 
65895a65560Sthorpej const char *
65995a65560Sthorpej qoperror(int qoperrno)
66095a65560Sthorpej {
66195a65560Sthorpej 	static char buf[64];
66295a65560Sthorpej 
66395a65560Sthorpej 	if (qoperrno <= QOPERR_MAX)
66495a65560Sthorpej 		return (qop_errlist[qoperrno]);
6658b8734d2Sitojun 	snprintf(buf, sizeof(buf), "unknown error %d", qoperrno);
66695a65560Sthorpej 	return (buf);
66795a65560Sthorpej }
66895a65560Sthorpej 
66995a65560Sthorpej /*
67095a65560Sthorpej  * misc functions
67195a65560Sthorpej  */
67295a65560Sthorpej struct ifinfo *
67395a65560Sthorpej ifname2ifinfo(const char *ifname)
67495a65560Sthorpej {
67595a65560Sthorpej 	struct ifinfo	*ifinfo;
67695a65560Sthorpej 
67795a65560Sthorpej 	LIST_FOREACH(ifinfo, &qop_iflist, next)
67895a65560Sthorpej 		if (ifinfo->ifname != NULL &&
67995a65560Sthorpej 		    strcmp(ifinfo->ifname, ifname) == 0)
68095a65560Sthorpej 			return (ifinfo);
68195a65560Sthorpej 	return (NULL);
68295a65560Sthorpej }
68395a65560Sthorpej 
68495a65560Sthorpej struct ifinfo *
68595a65560Sthorpej input_ifname2ifinfo(const char *ifname)
68695a65560Sthorpej {
68795a65560Sthorpej 	struct ifinfo	*ifinfo;
68895a65560Sthorpej 
68995a65560Sthorpej 	LIST_FOREACH(ifinfo, &qop_iflist, next)
69095a65560Sthorpej 		if (ifinfo->ifname[0] == '_' &&
69195a65560Sthorpej 		    strcmp(ifinfo->ifname+1, ifname) == 0)
69295a65560Sthorpej 			return (ifinfo);
69395a65560Sthorpej 	return (NULL);
69495a65560Sthorpej }
69595a65560Sthorpej 
69695a65560Sthorpej struct classinfo *
69795a65560Sthorpej clname2clinfo(const struct ifinfo *ifinfo, const char *clname)
69895a65560Sthorpej {
69995a65560Sthorpej 	struct classinfo	*clinfo;
70095a65560Sthorpej 
70195a65560Sthorpej 	LIST_FOREACH(clinfo, &ifinfo->cllist, next)
70295a65560Sthorpej 		if (clinfo->clname != NULL &&
70395a65560Sthorpej 		    strcmp(clinfo->clname, clname) == 0)
70495a65560Sthorpej 			return (clinfo);
70595a65560Sthorpej 	return (NULL);
70695a65560Sthorpej }
70795a65560Sthorpej 
70895a65560Sthorpej struct classinfo *
70995a65560Sthorpej clhandle2clinfo(struct ifinfo *ifinfo, u_long handle)
71095a65560Sthorpej {
71195a65560Sthorpej 	struct classinfo *clinfo;
71295a65560Sthorpej 
71395a65560Sthorpej 	LIST_FOREACH(clinfo, &ifinfo->cllist, next)
71495a65560Sthorpej 		if (clinfo->handle == handle)
71595a65560Sthorpej 			return (clinfo);
71695a65560Sthorpej 	return (NULL);
71795a65560Sthorpej }
71895a65560Sthorpej 
71995a65560Sthorpej struct fltrinfo *
72095a65560Sthorpej flname2flinfo(const struct classinfo *clinfo, const char *flname)
72195a65560Sthorpej {
72295a65560Sthorpej 	struct fltrinfo	*fltrinfo;
72395a65560Sthorpej 
72495a65560Sthorpej 	LIST_FOREACH(fltrinfo, &clinfo->fltrlist, next)
72595a65560Sthorpej 		if (fltrinfo->flname != NULL &&
72695a65560Sthorpej 		    strcmp(fltrinfo->flname, flname) == 0)
72795a65560Sthorpej 			return (fltrinfo);
72895a65560Sthorpej 	return (NULL);
72995a65560Sthorpej }
73095a65560Sthorpej 
73195a65560Sthorpej struct fltrinfo *
73295a65560Sthorpej flhandle2fltrinfo(struct ifinfo *ifinfo, u_long handle)
73395a65560Sthorpej {
73495a65560Sthorpej 	struct fltrinfo *fltrinfo;
73595a65560Sthorpej 
73695a65560Sthorpej 	LIST_FOREACH(fltrinfo, &ifinfo->fltr_rules, nextrule)
73795a65560Sthorpej 		if (fltrinfo->handle == handle)
73895a65560Sthorpej 			return (fltrinfo);
73995a65560Sthorpej 	return (NULL);
74095a65560Sthorpej }
74195a65560Sthorpej 
74295a65560Sthorpej int
74395a65560Sthorpej is_q_enabled(const char *ifname)
74495a65560Sthorpej {
74595a65560Sthorpej 	struct ifinfo	*ifinfo;
74695a65560Sthorpej 
74795a65560Sthorpej 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
74895a65560Sthorpej 		return (0);
74995a65560Sthorpej 	return (ifinfo->enabled);
75095a65560Sthorpej }
75195a65560Sthorpej 
75295a65560Sthorpej /*
75395a65560Sthorpej  * functions to walk through a class tree:
75495a65560Sthorpej  *
75595a65560Sthorpej  *   for (clinfo = get_rootclass(ifinfo);
75695a65560Sthorpej  *	clinfo != NULL; clinfo = get_nextclass(clinfo)) {
75795a65560Sthorpej  *	  do_something;
75895a65560Sthorpej  *   }
75995a65560Sthorpej  */
76095a65560Sthorpej struct classinfo *get_rootclass(struct ifinfo *ifinfo)
76195a65560Sthorpej {
76295a65560Sthorpej 	struct classinfo *clinfo;
76395a65560Sthorpej 
76495a65560Sthorpej 	/* find a class without parent */
76595a65560Sthorpej 	LIST_FOREACH(clinfo, &ifinfo->cllist, next)
76695a65560Sthorpej 		if (clinfo->parent == NULL)
76795a65560Sthorpej 			return (clinfo);
76895a65560Sthorpej 	return (NULL);
76995a65560Sthorpej }
77095a65560Sthorpej 
77195a65560Sthorpej /* return next class in the tree */
77295a65560Sthorpej struct classinfo *get_nextclass(struct classinfo *clinfo)
77395a65560Sthorpej {
77495a65560Sthorpej 	struct classinfo *next;
77595a65560Sthorpej 
77695a65560Sthorpej 	if (clinfo->child != NULL)
77795a65560Sthorpej 		next = clinfo->child;
77895a65560Sthorpej 	else if (clinfo->sibling != NULL)
77995a65560Sthorpej 		next = clinfo->sibling;
78095a65560Sthorpej 	else {
78195a65560Sthorpej 		next = clinfo;
78295a65560Sthorpej 		while ((next = next->parent) != NULL)
78395a65560Sthorpej 			if (next->sibling) {
78495a65560Sthorpej 				next = next->sibling;
78595a65560Sthorpej 				break;
78695a65560Sthorpej 			}
78795a65560Sthorpej 	}
78895a65560Sthorpej 	return (next);
78995a65560Sthorpej }
79095a65560Sthorpej 
791*89ff1cb6Sozaki-r uint64_t
79295a65560Sthorpej atobps(const char *s)
79395a65560Sthorpej {
79469881cf6Sitojun 	double bandwidth;
79595a65560Sthorpej 	char *cp;
79695a65560Sthorpej 
79769881cf6Sitojun 	bandwidth = strtod(s, &cp);
79895a65560Sthorpej 	if (cp != NULL) {
79995a65560Sthorpej 		if (*cp == 'K' || *cp == 'k')
80095a65560Sthorpej 			bandwidth *= 1000;
80195a65560Sthorpej 		else if (*cp == 'M' || *cp == 'm')
80295a65560Sthorpej 			bandwidth *= 1000000;
80395a65560Sthorpej 		else if (*cp == 'G' || *cp == 'g')
80495a65560Sthorpej 			bandwidth *= 1000000000;
80595a65560Sthorpej 	}
80669881cf6Sitojun 	if (bandwidth < 0)
80769881cf6Sitojun 		bandwidth = 0;
808*89ff1cb6Sozaki-r 	return ((uint64_t)bandwidth);
80995a65560Sthorpej }
81095a65560Sthorpej 
81195a65560Sthorpej u_long
81295a65560Sthorpej atobytes(const char *s)
81395a65560Sthorpej {
81469881cf6Sitojun 	double bytes;
81595a65560Sthorpej 	char *cp;
81695a65560Sthorpej 
81769881cf6Sitojun 	bytes = strtod(s, &cp);
81895a65560Sthorpej 	if (cp != NULL) {
81995a65560Sthorpej 		if (*cp == 'K' || *cp == 'k')
82095a65560Sthorpej 			bytes *= 1024;
82195a65560Sthorpej 		else if (*cp == 'M' || *cp == 'm')
82295a65560Sthorpej 			bytes *= 1024 * 1024;
82395a65560Sthorpej 		else if (*cp == 'G' || *cp == 'g')
82495a65560Sthorpej 			bytes *= 1024 * 1024 * 1024;
82595a65560Sthorpej 	}
82669881cf6Sitojun 	if (bytes < 0)
82769881cf6Sitojun 		bytes = 0;
82869881cf6Sitojun 	return ((u_long)bytes);
82995a65560Sthorpej }
83095a65560Sthorpej 
83195a65560Sthorpej static int
83295a65560Sthorpej get_ifmtu(const char *ifname)
83395a65560Sthorpej {
83495a65560Sthorpej 	int s, mtu;
83595a65560Sthorpej 	struct ifreq ifr;
83695a65560Sthorpej #ifdef __OpenBSD__
83795a65560Sthorpej 	struct if_data ifdata;
83895a65560Sthorpej #endif
83995a65560Sthorpej 
84095a65560Sthorpej 	mtu = 512; /* default MTU */
84195a65560Sthorpej 
84295a65560Sthorpej 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
84395a65560Sthorpej 		return (mtu);
84495a65560Sthorpej 	strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
84595a65560Sthorpej #ifdef __OpenBSD__
84695a65560Sthorpej 	ifr.ifr_data = (caddr_t)&ifdata;
84795a65560Sthorpej 	if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == 0)
84895a65560Sthorpej 		mtu = ifdata.ifi_mtu;
84995a65560Sthorpej #else
85095a65560Sthorpej 	if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0)
85195a65560Sthorpej 		mtu = ifr.ifr_mtu;
85295a65560Sthorpej #endif
85395a65560Sthorpej 	close(s);
85495a65560Sthorpej 	return (mtu);
85595a65560Sthorpej }
85695a65560Sthorpej 
85795a65560Sthorpej static void
85895a65560Sthorpej tbr_install(const char *ifname)
85995a65560Sthorpej {
86095a65560Sthorpej 	struct tbrinfo *info;
86195a65560Sthorpej 	struct tbrreq req;
86295a65560Sthorpej 	int fd;
86395a65560Sthorpej 
86495a65560Sthorpej 	LIST_FOREACH(info, &tbr_list, link)
86595a65560Sthorpej 	    if (strcmp(info->ifname, ifname) == 0)
86695a65560Sthorpej 		    break;
86795a65560Sthorpej 	if (info == NULL)
86895a65560Sthorpej 		return;
86995a65560Sthorpej 	if (info->tb_prof.rate == 0 || info->installed)
87095a65560Sthorpej 		return;
87195a65560Sthorpej 
87295a65560Sthorpej 	/* get the current token bucket regulator */
87395a65560Sthorpej 	if ((fd = open(ALTQ_DEVICE, O_RDWR)) < 0)
87495a65560Sthorpej 		err(1, "can't open altq device");
87595a65560Sthorpej 	strncpy(req.ifname, ifname, IFNAMSIZ-1);
87695a65560Sthorpej 	if (ioctl(fd, ALTQTBRGET, &req) < 0)
87795a65560Sthorpej 		err(1, "ALTQTBRGET for interface %s", req.ifname);
87895a65560Sthorpej 
87995a65560Sthorpej 	/* save the current values */
88095a65560Sthorpej 	info->otb_prof.rate = req.tb_prof.rate;
88195a65560Sthorpej 	info->otb_prof.depth = req.tb_prof.depth;
88295a65560Sthorpej 
88395a65560Sthorpej 	/*
88495a65560Sthorpej 	 * if tbr is not specified in the config file and tbr is already
88595a65560Sthorpej 	 * configured, do not change.
88695a65560Sthorpej 	 */
88795a65560Sthorpej 	if (req.tb_prof.rate != 0) {
88895a65560Sthorpej 		LOG(LOG_INFO, 0,
88995a65560Sthorpej 		    "tbr is already installed on %s,\n"
890d5e1f166Sitojun 		    "  using the current setting (rate:%.2fM  size:%.2fK).",
89195a65560Sthorpej 		    info->ifname,
89295a65560Sthorpej 		    (double)req.tb_prof.rate/1000000.0,
89395a65560Sthorpej 		    (double)req.tb_prof.depth/1024.0);
89495a65560Sthorpej 		close (fd);
89595a65560Sthorpej 		return;
89695a65560Sthorpej 	}
89795a65560Sthorpej 
89895a65560Sthorpej 	/* if the new size is not specified, use heuristics */
89995a65560Sthorpej 	if (info->tb_prof.depth == 0) {
90095a65560Sthorpej 		u_int rate, size;
90195a65560Sthorpej 
90295a65560Sthorpej 		rate = info->tb_prof.rate;
90395a65560Sthorpej 		if (rate <= 1*1000*1000)
90495a65560Sthorpej 			size = 1;
90595a65560Sthorpej 		else if (rate <= 10*1000*1000)
90695a65560Sthorpej 			size = 4;
90795a65560Sthorpej 		else if (rate <= 200*1000*1000)
90895a65560Sthorpej 			size = 8;
90995a65560Sthorpej 		else
91095a65560Sthorpej 			size = 24;
91195a65560Sthorpej 		size = size * 1500;  /* assume the default mtu is 1500 */
91295a65560Sthorpej 		info->tb_prof.depth = size;
91395a65560Sthorpej 	}
91495a65560Sthorpej 
91595a65560Sthorpej 	/* install the new tbr */
91695a65560Sthorpej 	strncpy(req.ifname, ifname, IFNAMSIZ-1);
91795a65560Sthorpej 	req.tb_prof.rate = info->tb_prof.rate;
91895a65560Sthorpej 	req.tb_prof.depth = info->tb_prof.depth;
91995a65560Sthorpej 	if (ioctl(fd, ALTQTBRSET, &req) < 0)
92095a65560Sthorpej 		err(1, "ALTQTBRSET for interface %s", req.ifname);
92195a65560Sthorpej 	LOG(LOG_INFO, 0,
922d5e1f166Sitojun 	    "tbr installed on %s (rate:%.2fM  size:%.2fK)",
92395a65560Sthorpej 	    info->ifname,
92495a65560Sthorpej 	    (double)info->tb_prof.rate/1000000.0,
92595a65560Sthorpej 	    (double)info->tb_prof.depth/1024.0);
92695a65560Sthorpej 	close(fd);
92795a65560Sthorpej 	info->installed = 1;
92895a65560Sthorpej }
92995a65560Sthorpej 
93095a65560Sthorpej static void
93195a65560Sthorpej tbr_deinstall(const char *ifname)
93295a65560Sthorpej {
93395a65560Sthorpej 	struct tbrinfo *info;
93495a65560Sthorpej 	struct tbrreq req;
93595a65560Sthorpej 	int fd;
93695a65560Sthorpej 
93795a65560Sthorpej 	LIST_FOREACH(info, &tbr_list, link)
93895a65560Sthorpej 	    if (strcmp(info->ifname, ifname) == 0)
93995a65560Sthorpej 		    break;
94095a65560Sthorpej 	if (info == NULL)
94195a65560Sthorpej 		return;
94295a65560Sthorpej 
94395a65560Sthorpej 	/* if we installed tbr, restore the old values */
94495a65560Sthorpej 	if (info->installed != 0) {
94595a65560Sthorpej 		strncpy(req.ifname, ifname, IFNAMSIZ-1);
94695a65560Sthorpej 		req.tb_prof.rate = info->otb_prof.rate;
94795a65560Sthorpej 		req.tb_prof.depth = info->otb_prof.depth;
94895a65560Sthorpej 		if ((fd = open(ALTQ_DEVICE, O_RDWR)) < 0)
94995a65560Sthorpej 			err(1, "can't open altq device");
95095a65560Sthorpej 		if (ioctl(fd, ALTQTBRSET, &req) < 0)
95195a65560Sthorpej 			err(1, "ALTQTBRSET for interface %s", req.ifname);
95295a65560Sthorpej 		close(fd);
95395a65560Sthorpej 	}
95495a65560Sthorpej 	LIST_REMOVE(info, link);
95595a65560Sthorpej 	free(info);
95695a65560Sthorpej }
95795a65560Sthorpej 
95895a65560Sthorpej void
95995a65560Sthorpej print_filter(const struct flow_filter *filt)
96095a65560Sthorpej {
96195a65560Sthorpej 	if (filt->ff_flow.fi_family == AF_INET) {
96295a65560Sthorpej 		struct in_addr in_addr;
96395a65560Sthorpej 
96495a65560Sthorpej 		in_addr.s_addr = filt->ff_flow.fi_dst.s_addr;
96595a65560Sthorpej 		LOG(LOG_DEBUG, 0,
966d5e1f166Sitojun 		    " Filter Dest Addr: %s (mask %#x) Port: %d",
96795a65560Sthorpej 		    inet_ntoa(in_addr), ntoh32(filt->ff_mask.mask_dst.s_addr),
96895a65560Sthorpej 		    ntoh16(filt->ff_flow.fi_dport));
96995a65560Sthorpej 		in_addr.s_addr = filt->ff_flow.fi_src.s_addr;
97095a65560Sthorpej 		LOG(LOG_DEBUG, 0,
971d5e1f166Sitojun 		    "        Src Addr: %s (mask %#x) Port: %d",
97295a65560Sthorpej 		    inet_ntoa(in_addr), ntoh32(filt->ff_mask.mask_src.s_addr),
97395a65560Sthorpej 		    ntoh16(filt->ff_flow.fi_sport));
974d5e1f166Sitojun 		LOG(LOG_DEBUG, 0, "        Protocol: %d TOS %#x (mask %#x)",
97595a65560Sthorpej 		    filt->ff_flow.fi_proto, filt->ff_flow.fi_tos,
97695a65560Sthorpej 		    filt->ff_mask.mask_tos);
97795a65560Sthorpej 	}
97895a65560Sthorpej #ifdef INET6
97995a65560Sthorpej 	else if (filt->ff_flow.fi_family == AF_INET6) {
98095a65560Sthorpej 		char str1[INET6_ADDRSTRLEN], str2[INET6_ADDRSTRLEN];
98195a65560Sthorpej 		const struct flow_filter6 *sfilt6;
98295a65560Sthorpej 
98395a65560Sthorpej 		sfilt6 = (const struct flow_filter6 *)filt;
98495a65560Sthorpej 		LOG(LOG_DEBUG, 0, "Filter6 Dest Addr: %s (mask %s) Port: %d",
98595a65560Sthorpej 		    inet_ntop(AF_INET6, &sfilt6->ff_flow6.fi6_dst,
98695a65560Sthorpej 			      str1, sizeof(str1)),
98795a65560Sthorpej 		    inet_ntop(AF_INET6, &sfilt6->ff_mask6.mask6_dst,
98895a65560Sthorpej 			      str2, sizeof(str2)),
98995a65560Sthorpej 		    ntoh16(sfilt6->ff_flow6.fi6_dport));
99095a65560Sthorpej 		LOG(LOG_DEBUG, 0, "        Src Addr: %s (mask %s) Port: %d",
99195a65560Sthorpej 		    inet_ntop(AF_INET6, &sfilt6->ff_flow6.fi6_src,
99295a65560Sthorpej 			      str1, sizeof(str1)),
99395a65560Sthorpej 		    inet_ntop(AF_INET6, &sfilt6->ff_mask6.mask6_src,
99495a65560Sthorpej 			      str2, sizeof(str2)),
99595a65560Sthorpej 		    ntoh16(sfilt6->ff_flow6.fi6_sport));
996d5e1f166Sitojun 		LOG(LOG_DEBUG, 0, "        Protocol: %d TCLASS %#x (mask %#x)",
99795a65560Sthorpej 		    sfilt6->ff_flow6.fi6_proto, sfilt6->ff_flow6.fi6_tclass,
99895a65560Sthorpej 		    sfilt6->ff_mask6.mask6_tclass);
99995a65560Sthorpej 	}
100095a65560Sthorpej #endif /* INET6 */
100195a65560Sthorpej }
100295a65560Sthorpej 
100395a65560Sthorpej /*
100495a65560Sthorpej  * functions to check the filter-rules.
100595a65560Sthorpej  * when a new filter is added, we check the relation to the existing filters
100695a65560Sthorpej  * and if some inconsistency is found, produce an error or a warning message.
100795a65560Sthorpej  *
100895a65560Sthorpej  * filter matching is performed from the head of the list.
100995a65560Sthorpej  * let
101095a65560Sthorpej  *    S: a set of packets that filter s matches
101195a65560Sthorpej  *    T: a set of packets that filter t matches
101295a65560Sthorpej  * filter relations are:
101395a65560Sthorpej  *   disjoint: S ^ T = empty
101495a65560Sthorpej  *   subset:   S <= T
101595a65560Sthorpej  *   intersect: S ^ T = not empty
101695a65560Sthorpej  *
101795a65560Sthorpej  * a new filter is disjoint or subset of the existing filters --> ok
101895a65560Sthorpej  * a new filter is superset of an existing filter --> order problem
101995a65560Sthorpej  * a new filter intersect an existing filter --> warning
102095a65560Sthorpej  *
102195a65560Sthorpej  * port-intersect: a special case we don't make warning
102295a65560Sthorpej  *      - intersection is only port numbers
102395a65560Sthorpej  *	- one specifies src port and the other specifies dst port
102495a65560Sthorpej  * there must be no packet with well-known port numbers in
102595a65560Sthorpej  * both src and dst ports.  so this is ok.
102695a65560Sthorpej  */
102795a65560Sthorpej 
102895a65560Sthorpej #define FILT_DISJOINT		1
102995a65560Sthorpej #define FILT_SUBSET		2
103095a65560Sthorpej #define FILT_SUPERSET		3
103195a65560Sthorpej #define FILT_INTERSECT		4
103295a65560Sthorpej #define FILT_PORTINTERSECT	5
103395a65560Sthorpej 
103495a65560Sthorpej static int
103595a65560Sthorpej add_filter_rule(struct ifinfo *ifinfo, struct fltrinfo *fltrinfo,
103695a65560Sthorpej 		struct fltrinfo **conflict)
103795a65560Sthorpej {
103895a65560Sthorpej 	struct fltrinfo *fp, *front, *back, *prev = NULL;
103995a65560Sthorpej 	int relation;
104095a65560Sthorpej 
104195a65560Sthorpej 	LIST_FOREACH(fp, &ifinfo->fltr_rules, nextrule) {
104295a65560Sthorpej 		if (fp->fltr.ff_ruleno > fltrinfo->fltr.ff_ruleno) {
104395a65560Sthorpej 			front = fp;
104495a65560Sthorpej 			back = fltrinfo;
104595a65560Sthorpej 			prev = fp;
104695a65560Sthorpej 		} else {
104795a65560Sthorpej 			front = fltrinfo;
104895a65560Sthorpej 			back = fp;
104995a65560Sthorpej 		}
105095a65560Sthorpej 
105195a65560Sthorpej 		relation = filt_check_relation(&front->fltr, &back->fltr);
105295a65560Sthorpej 
105395a65560Sthorpej 		switch (relation) {
105495a65560Sthorpej 		case FILT_SUBSET:
105595a65560Sthorpej 		case FILT_DISJOINT:
105695a65560Sthorpej 			/* OK */
105795a65560Sthorpej 			break;
105895a65560Sthorpej 		case FILT_SUPERSET:
105995a65560Sthorpej 			if (front->dontwarn == 0 && back->dontwarn == 0)
106095a65560Sthorpej 				LOG(LOG_ERR, 0,
1061d5e1f166Sitojun 				    "filters for \"%s\" at line %d and for \"%s\" at line %d has an order problem!",
106295a65560Sthorpej 				    front->clinfo->clname, front->line_no,
106395a65560Sthorpej 				    back->clinfo->clname, back->line_no);
106495a65560Sthorpej 
106595a65560Sthorpej 			if (conflict != NULL)
106695a65560Sthorpej 				*conflict = fp;
106795a65560Sthorpej 			return (QOPERR_FILTER_SHADOW);
106895a65560Sthorpej 		case FILT_PORTINTERSECT:
106995a65560Sthorpej 			break;
107095a65560Sthorpej 		case FILT_INTERSECT:
107195a65560Sthorpej 			/*
10722fbcff73Sandvar 			 * if the intersecting two filters belonging to the
107395a65560Sthorpej 			 * same class, it's ok.
107495a65560Sthorpej 			 */
107595a65560Sthorpej 			if (front->clinfo == back->clinfo)
107695a65560Sthorpej 				break;
107795a65560Sthorpej 			if (front->dontwarn == 0 && back->dontwarn == 0)
107895a65560Sthorpej 				LOG(LOG_WARNING, 0,
1079d5e1f166Sitojun 				    "warning: filter for \"%s\" at line %d could override filter for \"%s\" at line %d",
108095a65560Sthorpej 				    front->clinfo->clname, front->line_no,
108195a65560Sthorpej 				    back->clinfo->clname, back->line_no);
108295a65560Sthorpej 			break;
108395a65560Sthorpej 		}
108495a65560Sthorpej 	}
108595a65560Sthorpej 
108695a65560Sthorpej 	if (prev == NULL)
108795a65560Sthorpej 		LIST_INSERT_HEAD(&ifinfo->fltr_rules, fltrinfo, nextrule);
108895a65560Sthorpej 	else
108995a65560Sthorpej 		LIST_INSERT_AFTER(prev, fltrinfo, nextrule);
109095a65560Sthorpej 	return (0);
109195a65560Sthorpej }
109295a65560Sthorpej 
109395a65560Sthorpej static int
109495a65560Sthorpej remove_filter_rule(struct ifinfo *ifinfo, struct fltrinfo *fltrinfo)
109595a65560Sthorpej {
109695a65560Sthorpej 	LIST_REMOVE(fltrinfo, nextrule);
109795a65560Sthorpej 	return (0);
109895a65560Sthorpej }
109995a65560Sthorpej 
110095a65560Sthorpej static int
110195a65560Sthorpej filt_check_relation(struct flow_filter *front, struct flow_filter *back)
110295a65560Sthorpej {
110395a65560Sthorpej 	int rval;
110495a65560Sthorpej 
110595a65560Sthorpej 	if (front->ff_flow.fi_family != back->ff_flow.fi_family)
110695a65560Sthorpej 		return (FILT_DISJOINT);
110795a65560Sthorpej 
110895a65560Sthorpej 	if (filt_disjoint(front, back))
110995a65560Sthorpej 		return (FILT_DISJOINT);
111095a65560Sthorpej 
111195a65560Sthorpej 	if ((rval = filt_subset(front, back)) == 1)
111295a65560Sthorpej 		return (FILT_SUBSET);
111395a65560Sthorpej 
111495a65560Sthorpej 	if (filt_subset(back, front) == 1)
111595a65560Sthorpej 		return (FILT_SUPERSET);
111695a65560Sthorpej 
111795a65560Sthorpej 	if (rval == 2)
111895a65560Sthorpej 		return (FILT_PORTINTERSECT);
111995a65560Sthorpej 
112095a65560Sthorpej 	return (FILT_INTERSECT);
112195a65560Sthorpej }
112295a65560Sthorpej 
112395a65560Sthorpej static int
112495a65560Sthorpej filt_disjoint(struct flow_filter *front, struct flow_filter *back)
112595a65560Sthorpej {
112695a65560Sthorpej 	u_int32_t mask;
112795a65560Sthorpej 	u_int8_t tosmask;
112895a65560Sthorpej 
112995a65560Sthorpej 	if (front->ff_flow.fi_family == AF_INET) {
113095a65560Sthorpej 		if (front->ff_flow.fi_proto != 0 && back->ff_flow.fi_proto != 0
113195a65560Sthorpej 		    && front->ff_flow.fi_proto != back->ff_flow.fi_proto)
113295a65560Sthorpej 			return (1);
113395a65560Sthorpej 		if (front->ff_flow.fi_sport != 0 && back->ff_flow.fi_sport != 0
113495a65560Sthorpej 		    && front->ff_flow.fi_sport != back->ff_flow.fi_sport)
113595a65560Sthorpej 			return (1);
113695a65560Sthorpej 		if (front->ff_flow.fi_dport != 0 && back->ff_flow.fi_dport != 0
113795a65560Sthorpej 		    && front->ff_flow.fi_dport != back->ff_flow.fi_dport)
113895a65560Sthorpej 			return (1);
113995a65560Sthorpej 		if (front->ff_flow.fi_gpi != 0 && back->ff_flow.fi_gpi != 0
114095a65560Sthorpej 		    && front->ff_flow.fi_gpi != back->ff_flow.fi_gpi)
114195a65560Sthorpej 			return (1);
114295a65560Sthorpej 		if (front->ff_flow.fi_src.s_addr != 0 &&
114395a65560Sthorpej 		    back->ff_flow.fi_src.s_addr != 0) {
114495a65560Sthorpej 			mask = front->ff_mask.mask_src.s_addr &
114595a65560Sthorpej 				back->ff_mask.mask_src.s_addr;
114695a65560Sthorpej 			if ((front->ff_flow.fi_src.s_addr & mask) !=
114795a65560Sthorpej 			    (back->ff_flow.fi_src.s_addr & mask))
114895a65560Sthorpej 				return (1);
114995a65560Sthorpej 		}
115095a65560Sthorpej 		if (front->ff_flow.fi_dst.s_addr != 0 &&
115195a65560Sthorpej 		    back->ff_flow.fi_dst.s_addr != 0) {
115295a65560Sthorpej 			mask = front->ff_mask.mask_dst.s_addr &
115395a65560Sthorpej 				back->ff_mask.mask_dst.s_addr;
115495a65560Sthorpej 			if ((front->ff_flow.fi_dst.s_addr & mask) !=
115595a65560Sthorpej 			    (back->ff_flow.fi_dst.s_addr & mask))
115695a65560Sthorpej 				return (1);
115795a65560Sthorpej 		}
115895a65560Sthorpej 		if (front->ff_flow.fi_tos != 0 && back->ff_flow.fi_tos != 0) {
115995a65560Sthorpej 			tosmask = front->ff_mask.mask_tos &
116095a65560Sthorpej 				back->ff_mask.mask_tos;
116195a65560Sthorpej 			if ((front->ff_flow.fi_tos & tosmask) !=
116295a65560Sthorpej 			    (back->ff_flow.fi_tos & tosmask))
116395a65560Sthorpej 				return (1);
116495a65560Sthorpej 		}
116595a65560Sthorpej 		return (0);
116695a65560Sthorpej 	}
116795a65560Sthorpej #ifdef INET6
116895a65560Sthorpej 	else if (front->ff_flow.fi_family == AF_INET6) {
116995a65560Sthorpej 		struct flow_filter6 *front6, *back6;
117095a65560Sthorpej 		int i;
117195a65560Sthorpej 
117295a65560Sthorpej 		front6 = (struct flow_filter6 *)front;
117395a65560Sthorpej 		back6 = (struct flow_filter6 *)back;
117495a65560Sthorpej 
117595a65560Sthorpej 		if (front6->ff_flow6.fi6_proto != 0 &&
117695a65560Sthorpej 		    back6->ff_flow6.fi6_proto != 0 &&
117795a65560Sthorpej 		    front6->ff_flow6.fi6_proto != back6->ff_flow6.fi6_proto)
117895a65560Sthorpej 			return (1);
117995a65560Sthorpej 		if (front6->ff_flow6.fi6_flowlabel != 0 &&
118095a65560Sthorpej 		    back6->ff_flow6.fi6_flowlabel != 0 &&
118195a65560Sthorpej 		    front6->ff_flow6.fi6_flowlabel !=
118295a65560Sthorpej 		    back6->ff_flow6.fi6_flowlabel)
118395a65560Sthorpej 			return (1);
118495a65560Sthorpej 		if (front6->ff_flow6.fi6_sport != 0 &&
118595a65560Sthorpej 		    back6->ff_flow6.fi6_sport != 0 &&
118695a65560Sthorpej 		    front6->ff_flow6.fi6_sport != back6->ff_flow6.fi6_sport)
118795a65560Sthorpej 			return (1);
118895a65560Sthorpej 		if (front6->ff_flow6.fi6_dport != 0 &&
118995a65560Sthorpej 		    back6->ff_flow6.fi6_dport != 0 &&
119095a65560Sthorpej 		    front6->ff_flow6.fi6_dport != back6->ff_flow6.fi6_dport)
119195a65560Sthorpej 			return (1);
119295a65560Sthorpej 		if (front6->ff_flow6.fi6_gpi != 0 &&
119395a65560Sthorpej 		    back6->ff_flow6.fi6_gpi != 0 &&
119495a65560Sthorpej 		    front6->ff_flow6.fi6_gpi != back6->ff_flow6.fi6_gpi)
119595a65560Sthorpej 			return (1);
119695a65560Sthorpej 		if (!IN6_IS_ADDR_UNSPECIFIED(&front6->ff_flow6.fi6_src) &&
119795a65560Sthorpej 		    !IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_src)) {
119895a65560Sthorpej 			for (i=0; i<4; i++) {
11990d33c75cSchristos 				mask = IN6ADDR32_GET(&front6->ff_mask6.mask6_src, i)
12000d33c75cSchristos 					& IN6ADDR32_GET(&back6->ff_mask6.mask6_src, i);
12010d33c75cSchristos 				if ((IN6ADDR32_GET(&front6->ff_flow6.fi6_src, i) & mask) !=
12020d33c75cSchristos 				    (IN6ADDR32_GET(&back6->ff_flow6.fi6_src, i) & mask))
120395a65560Sthorpej 					return (1);
120495a65560Sthorpej 			}
120595a65560Sthorpej 		}
120695a65560Sthorpej 		if (!IN6_IS_ADDR_UNSPECIFIED(&front6->ff_flow6.fi6_dst) &&
120795a65560Sthorpej 		    !IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_dst)) {
120895a65560Sthorpej 			for (i=0; i<4; i++) {
12090d33c75cSchristos 				mask = IN6ADDR32_GET(&front6->ff_mask6.mask6_dst, i)
12100d33c75cSchristos 					& IN6ADDR32_GET(&back6->ff_mask6.mask6_dst, i);
12110d33c75cSchristos 				if ((IN6ADDR32_GET(&front6->ff_flow6.fi6_dst, i) & mask) !=
12120d33c75cSchristos 				    (IN6ADDR32_GET(&back6->ff_flow6.fi6_dst, i) & mask))
121395a65560Sthorpej 				return (1);
121495a65560Sthorpej 			}
121595a65560Sthorpej 		}
121695a65560Sthorpej 		if (front6->ff_flow6.fi6_tclass != 0 &&
121795a65560Sthorpej 		    back6->ff_flow6.fi6_tclass != 0) {
121895a65560Sthorpej 			tosmask = front6->ff_mask6.mask6_tclass &
121995a65560Sthorpej 				back6->ff_mask6.mask6_tclass;
122095a65560Sthorpej 			if ((front6->ff_flow6.fi6_tclass & tosmask) !=
122195a65560Sthorpej 			    (back6->ff_flow6.fi6_tclass & tosmask))
122295a65560Sthorpej 				return (1);
122395a65560Sthorpej 		}
122495a65560Sthorpej 		return (0);
122595a65560Sthorpej 	}
122695a65560Sthorpej #endif /* INET6 */
122795a65560Sthorpej 	return (0);
122895a65560Sthorpej }
122995a65560Sthorpej 
123095a65560Sthorpej /*
123195a65560Sthorpej  * check if "front" is a subset of "back".  assumes they are not disjoint
123295a65560Sthorpej  * return value 0: not a subset
123395a65560Sthorpej  *              1: subset
123495a65560Sthorpej  *              2: subset except src & dst ports
123595a65560Sthorpej  *		   (possible port-intersect)
123695a65560Sthorpej  */
123795a65560Sthorpej static int
123895a65560Sthorpej filt_subset(struct flow_filter *front, struct flow_filter *back)
123995a65560Sthorpej {
124095a65560Sthorpej 	u_int16_t srcport, dstport;
124195a65560Sthorpej 
124295a65560Sthorpej 	if (front->ff_flow.fi_family == AF_INET) {
124395a65560Sthorpej 		if (front->ff_flow.fi_proto == 0 &&
124495a65560Sthorpej 		    back->ff_flow.fi_proto != 0)
124595a65560Sthorpej 			return (0);
124695a65560Sthorpej 		if (front->ff_flow.fi_gpi == 0 && back->ff_flow.fi_gpi != 0)
124795a65560Sthorpej 			return (0);
124895a65560Sthorpej 		if (front->ff_flow.fi_src.s_addr == 0) {
124995a65560Sthorpej 			if (back->ff_flow.fi_src.s_addr != 0)
125095a65560Sthorpej 				return (0);
125195a65560Sthorpej 		} else if (back->ff_flow.fi_src.s_addr != 0 &&
125295a65560Sthorpej 			 (~front->ff_mask.mask_src.s_addr &
125395a65560Sthorpej 			  back->ff_mask.mask_src.s_addr))
125495a65560Sthorpej 			return (0);
125595a65560Sthorpej 		if (front->ff_flow.fi_dst.s_addr == 0) {
125695a65560Sthorpej 			if (back->ff_flow.fi_dst.s_addr != 0)
125795a65560Sthorpej 				return (0);
125895a65560Sthorpej 		} else if (back->ff_flow.fi_dst.s_addr != 0 &&
125995a65560Sthorpej 			 (~front->ff_mask.mask_dst.s_addr &
126095a65560Sthorpej 			  back->ff_mask.mask_dst.s_addr))
126195a65560Sthorpej 			return (0);
126295a65560Sthorpej 		if (~front->ff_mask.mask_tos & back->ff_mask.mask_tos)
126395a65560Sthorpej 			return (0);
126495a65560Sthorpej 
126595a65560Sthorpej 		if (front->ff_flow.fi_sport == 0 &&
126695a65560Sthorpej 		    back->ff_flow.fi_sport != 0) {
126795a65560Sthorpej 			srcport = ntohs(back->ff_flow.fi_sport);
126895a65560Sthorpej 			dstport = ntohs(front->ff_flow.fi_dport);
126995a65560Sthorpej 			if (dstport > 0 /* && dstport < 1024 */ &&
127095a65560Sthorpej 			    srcport > 0 /* && srcport < 1024 */)
127195a65560Sthorpej 				return (2);
127295a65560Sthorpej 			return (0);
127395a65560Sthorpej 		}
127495a65560Sthorpej 		if (front->ff_flow.fi_dport == 0 &&
127595a65560Sthorpej 		    back->ff_flow.fi_dport != 0) {
127695a65560Sthorpej 			dstport = ntohs(back->ff_flow.fi_dport);
127795a65560Sthorpej 			srcport = ntohs(front->ff_flow.fi_sport);
127895a65560Sthorpej 			if (srcport > 0 /* && srcport < 1024 */ &&
127995a65560Sthorpej 			    dstport > 0 /* && dstport < 1024 */)
128095a65560Sthorpej 				return (2);
128195a65560Sthorpej 			return (0);
128295a65560Sthorpej 		}
128395a65560Sthorpej 
128495a65560Sthorpej 		return (1);
128595a65560Sthorpej 	}
128695a65560Sthorpej #ifdef INET6
128795a65560Sthorpej 	else if (front->ff_flow.fi_family == AF_INET6) {
128895a65560Sthorpej 		struct flow_filter6 *front6, *back6;
128995a65560Sthorpej 		int i;
129095a65560Sthorpej 
129195a65560Sthorpej 		front6 = (struct flow_filter6 *)front;
129295a65560Sthorpej 		back6 = (struct flow_filter6 *)back;
129395a65560Sthorpej 
129495a65560Sthorpej 		if (front6->ff_flow6.fi6_proto == 0 &&
129595a65560Sthorpej 		    back6->ff_flow6.fi6_proto != 0)
129695a65560Sthorpej 			return (0);
129795a65560Sthorpej 		if (front6->ff_flow6.fi6_flowlabel == 0 &&
129895a65560Sthorpej 		    back6->ff_flow6.fi6_flowlabel != 0)
129995a65560Sthorpej 			return (0);
130095a65560Sthorpej 		if (front6->ff_flow6.fi6_gpi == 0 &&
130195a65560Sthorpej 		    back6->ff_flow6.fi6_gpi != 0)
130295a65560Sthorpej 			return (0);
130395a65560Sthorpej 
130495a65560Sthorpej 		if (IN6_IS_ADDR_UNSPECIFIED(&front6->ff_flow6.fi6_src)) {
130595a65560Sthorpej 			if (!IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_src))
130695a65560Sthorpej 				return (0);
130795a65560Sthorpej 		} else if (!IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_src))
130895a65560Sthorpej 			for (i=0; i<4; i++)
13090d33c75cSchristos 				if (~IN6ADDR32_GET(&front6->ff_mask6.mask6_src, i) &
13100d33c75cSchristos 				    IN6ADDR32_GET(&back6->ff_mask6.mask6_src, i))
131195a65560Sthorpej 					return (0);
131295a65560Sthorpej 		if (IN6_IS_ADDR_UNSPECIFIED(&front6->ff_flow6.fi6_dst)) {
131395a65560Sthorpej 			if (!IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_dst))
131495a65560Sthorpej 				return (0);
131595a65560Sthorpej 		} else if (!IN6_IS_ADDR_UNSPECIFIED(&back6->ff_flow6.fi6_dst))
131695a65560Sthorpej 			for (i=0; i<4; i++)
13170d33c75cSchristos 				if (~IN6ADDR32_GET(&front6->ff_mask6.mask6_dst, i) &
13180d33c75cSchristos 				    IN6ADDR32_GET(&back6->ff_mask6.mask6_dst, i))
131995a65560Sthorpej 					return (0);
132095a65560Sthorpej 
132195a65560Sthorpej 		if (~front6->ff_mask6.mask6_tclass &
132295a65560Sthorpej 		    back6->ff_mask6.mask6_tclass)
132395a65560Sthorpej 			return (0);
132495a65560Sthorpej 
132595a65560Sthorpej 		if (front6->ff_flow6.fi6_sport == 0 &&
132695a65560Sthorpej 		    back6->ff_flow6.fi6_sport != 0) {
132795a65560Sthorpej 			srcport = ntohs(back6->ff_flow6.fi6_sport);
132895a65560Sthorpej 			dstport = ntohs(front6->ff_flow6.fi6_dport);
132995a65560Sthorpej 			if (dstport > 0 /* && dstport < 1024 */ &&
133095a65560Sthorpej 			    srcport > 0 /* && srcport < 1024 */)
133195a65560Sthorpej 				return (2);
133295a65560Sthorpej 			return (0);
133395a65560Sthorpej 		}
133495a65560Sthorpej 		if (front6->ff_flow6.fi6_dport == 0 &&
133595a65560Sthorpej 		    back6->ff_flow6.fi6_dport != 0) {
133695a65560Sthorpej 			dstport = ntohs(back6->ff_flow6.fi6_dport);
133795a65560Sthorpej 			srcport = ntohs(front6->ff_flow6.fi6_sport);
133895a65560Sthorpej 			if (srcport > 0 /* && srcport < 1024 */ &&
133995a65560Sthorpej 			    dstport > 0 /* && dstport < 1024 */)
134095a65560Sthorpej 				return (2);
134195a65560Sthorpej 			return (0);
134295a65560Sthorpej 		}
134395a65560Sthorpej 	}
134495a65560Sthorpej #endif /* INET6 */
134595a65560Sthorpej 	return (1);
134695a65560Sthorpej }
134795a65560Sthorpej 
134895a65560Sthorpej 
134995a65560Sthorpej /*
135095a65560Sthorpej  * setting RED or RIO default parameters
135195a65560Sthorpej  */
135295a65560Sthorpej int
135395a65560Sthorpej qop_red_set_defaults(int th_min, int th_max, int inv_pmax)
135495a65560Sthorpej {
135595a65560Sthorpej 	struct redparams params;
135695a65560Sthorpej 	int fd;
135795a65560Sthorpej 
135895a65560Sthorpej 	if ((fd = open(RED_DEVICE, O_RDWR)) < 0) {
1359d5e1f166Sitojun 		LOG(LOG_ERR, errno, "RED open");
136095a65560Sthorpej 		return (QOPERR_SYSCALL);
136195a65560Sthorpej 	}
136295a65560Sthorpej 
136395a65560Sthorpej 	params.th_min = th_min;
136495a65560Sthorpej 	params.th_max = th_max;
136595a65560Sthorpej 	params.inv_pmax = inv_pmax;
136695a65560Sthorpej 
136795a65560Sthorpej 	if (ioctl(fd, RED_SETDEFAULTS, &params) < 0) {
1368d5e1f166Sitojun 		LOG(LOG_ERR, errno, "RED_SETDEFAULTS");
1369baacf5e0Swiz 		(void)close(fd);
137095a65560Sthorpej 		return (QOPERR_SYSCALL);
137195a65560Sthorpej 	}
137295a65560Sthorpej 
137395a65560Sthorpej 	(void)close(fd);
137495a65560Sthorpej 	return (0);
137595a65560Sthorpej }
137695a65560Sthorpej 
137795a65560Sthorpej int
137895a65560Sthorpej qop_rio_set_defaults(struct redparams *params)
137995a65560Sthorpej {
138095a65560Sthorpej 	int i, fd;
138195a65560Sthorpej 
138295a65560Sthorpej 	/* sanity check */
138395a65560Sthorpej 	for (i = 1; i < RIO_NDROPPREC; i++) {
138495a65560Sthorpej 		if (params[i].th_max > params[i-1].th_min)
138595a65560Sthorpej 			LOG(LOG_WARNING, 0,
1386d5e1f166Sitojun 			    "warning: overlap found in RIO thresholds");
138795a65560Sthorpej 	}
138895a65560Sthorpej 
138995a65560Sthorpej 	if ((fd = open(RIO_DEVICE, O_RDWR)) < 0) {
1390d5e1f166Sitojun 		LOG(LOG_ERR, errno, "RIO open");
139195a65560Sthorpej 		return (QOPERR_SYSCALL);
139295a65560Sthorpej 	}
139395a65560Sthorpej 
139495a65560Sthorpej 	if (ioctl(fd, RIO_SETDEFAULTS, params) < 0) {
1395d5e1f166Sitojun 		LOG(LOG_ERR, errno, "RIO_SETDEFAULTS");
1396baacf5e0Swiz 		(void)close(fd);
139795a65560Sthorpej 		return (QOPERR_SYSCALL);
139895a65560Sthorpej 	}
139995a65560Sthorpej 
140095a65560Sthorpej 	(void)close(fd);
140195a65560Sthorpej 	return (0);
140295a65560Sthorpej }
140395a65560Sthorpej 
140495a65560Sthorpej /*
140595a65560Sthorpej  * try to load and open KLD module
1406d5e1f166Sitojun  * (also check the altq device file)
140795a65560Sthorpej  */
140895a65560Sthorpej int
14097097d2c1Sxtraeme open_module(const char *dvname, int flags)
141095a65560Sthorpej {
141195a65560Sthorpej #if defined(__FreeBSD__) && (__FreeBSD_version > 300000)
14128b8734d2Sitojun 	char modname[64], filename[MAXPATHLEN], *cp;
141395a65560Sthorpej 	int fd;
1414d5e1f166Sitojun #endif
141595a65560Sthorpej 	struct stat sbuf;
141695a65560Sthorpej 
1417d5e1f166Sitojun 	/* check if the altq device exists */
14187097d2c1Sxtraeme 	if (stat(dvname, &sbuf) < 0) {
14197097d2c1Sxtraeme 		LOG(LOG_ERR, errno, "can't access %s!", dvname);
1420d5e1f166Sitojun 		return (-1);
1421d5e1f166Sitojun 	}
1422d5e1f166Sitojun 
1423d5e1f166Sitojun #if defined(__FreeBSD__) && (__FreeBSD_version > 300000)
142495a65560Sthorpej 	/* turn discipline name into module name */
14258b8734d2Sitojun 	strlcpy(modname, "altq_", sizeof(modname));
142695a65560Sthorpej 	if ((cp = strrchr(devname, '/')) == NULL)
142795a65560Sthorpej 		return (-1);
14288b8734d2Sitojun 	strlcat(modname, cp + 1, sizeof(modname));
142995a65560Sthorpej 
143095a65560Sthorpej 	/* check if the kld module exists */
14318b8734d2Sitojun 	snprintf(filename, sizeof(filename), "/modules/%s.ko", modname);
143295a65560Sthorpej 	if (stat(filename, &sbuf) < 0) {
143395a65560Sthorpej 		/* module file doesn't exist */
143495a65560Sthorpej 		return (-1);
143595a65560Sthorpej 	}
143695a65560Sthorpej 
143795a65560Sthorpej 	if (kldload(modname) < 0) {
1438d5e1f166Sitojun 		LOG(LOG_ERR, errno, "kldload %s failed!", modname);
143995a65560Sthorpej 		return (-1);
144095a65560Sthorpej 	}
144195a65560Sthorpej 
144295a65560Sthorpej 	/* successfully loaded, open the device */
1443d5e1f166Sitojun 	LOG(LOG_INFO, 0, "kld module %s loaded", modname);
144495a65560Sthorpej 	fd = open(devname, flags);
144595a65560Sthorpej 	return (fd);
144695a65560Sthorpej #else
144795a65560Sthorpej 	return (-1);
144895a65560Sthorpej #endif
144995a65560Sthorpej }
1450