xref: /netbsd-src/usr.sbin/altq/libaltq/parser.c (revision 915340d5d241e5f0a8d9654a50b2a9f091efe4a1)
1*915340d5Sandvar /*	$NetBSD: parser.c,v 1.13 2024/01/15 19:44:07 andvar Exp $	*/
269881cf6Sitojun /*	$KAME: parser.c,v 1.16 2002/02/20 10:40:39 kjc Exp $	*/
369881cf6Sitojun /*
469881cf6Sitojun  * Copyright (C) 1999-2002
569881cf6Sitojun  *	Sony Computer Science Laboratories, Inc.  All rights reserved.
669881cf6Sitojun  *
769881cf6Sitojun  * Redistribution and use in source and binary forms, with or without
869881cf6Sitojun  * modification, are permitted provided that the following conditions
969881cf6Sitojun  * are met:
1069881cf6Sitojun  * 1. Redistributions of source code must retain the above copyright
1169881cf6Sitojun  *    notice, this list of conditions and the following disclaimer.
1269881cf6Sitojun  * 2. Redistributions in binary form must reproduce the above copyright
1369881cf6Sitojun  *    notice, this list of conditions and the following disclaimer in the
1469881cf6Sitojun  *    documentation and/or other materials provided with the distribution.
1569881cf6Sitojun  *
1669881cf6Sitojun  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
1769881cf6Sitojun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1869881cf6Sitojun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1969881cf6Sitojun  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
2069881cf6Sitojun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2169881cf6Sitojun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2269881cf6Sitojun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2369881cf6Sitojun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2469881cf6Sitojun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2569881cf6Sitojun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2669881cf6Sitojun  * SUCH DAMAGE.
2769881cf6Sitojun  */
2895a65560Sthorpej 
2969881cf6Sitojun #include <sys/param.h>
3069881cf6Sitojun #include <sys/socket.h>
3169881cf6Sitojun #include <net/if.h>
3269881cf6Sitojun #include <netinet/in.h>
3369881cf6Sitojun #include <arpa/inet.h>
3495a65560Sthorpej 
3595a65560Sthorpej #include <stdio.h>
3695a65560Sthorpej #include <stdlib.h>
3795a65560Sthorpej #include <unistd.h>
3895a65560Sthorpej #include <stddef.h>
3995a65560Sthorpej #include <string.h>
4095a65560Sthorpej #include <ctype.h>
4195a65560Sthorpej #include <errno.h>
4295a65560Sthorpej #include <syslog.h>
4395a65560Sthorpej #include <netdb.h>
4469881cf6Sitojun #include <err.h>
4595a65560Sthorpej 
4695a65560Sthorpej #include <altq/altq.h>
4795a65560Sthorpej #include <altq/altq_cdnr.h>
4895a65560Sthorpej #include <altq/altq_red.h>
4995a65560Sthorpej #include <altq/altq_rio.h>
5095a65560Sthorpej #include "altq_qop.h"
5195a65560Sthorpej #include "qop_cdnr.h"
5295a65560Sthorpej 
538b8734d2Sitojun static int is_qdisc_name(const char *);
548b8734d2Sitojun static int qdisc_interface_parser(const char *, const char *, int, char **);
558b8734d2Sitojun static int qdisc_class_parser(const char *, const char *, const char *,
568b8734d2Sitojun 			      const char *, int, char **);
578b8734d2Sitojun static int next_word(char **, char *);
5895a65560Sthorpej 
598b8734d2Sitojun static int get_ifname(char **, char **);
608b8734d2Sitojun static int get_addr(char **, struct in_addr *, struct in_addr *);
618b8734d2Sitojun static int get_port(const char *, u_int16_t *);
628b8734d2Sitojun static int get_proto(const char *, int *);
638b8734d2Sitojun static int get_fltr_opts(char **, char *, size_t, int *);
648b8734d2Sitojun static int interface_parser(char *);
658b8734d2Sitojun static int class_parser(char *) ;
668b8734d2Sitojun static int filter_parser(char *);
6795a65560Sthorpej #ifdef INET6
688b8734d2Sitojun static int filter6_parser(char *);
698b8734d2Sitojun static int get_ip6addr(char **, struct in6_addr *, struct in6_addr *);
7095a65560Sthorpej #endif
718b8734d2Sitojun static int ctl_parser(char *);
728b8734d2Sitojun static int delete_parser(char *);
738b8734d2Sitojun static int red_parser(char *);
748b8734d2Sitojun static int rio_parser(char *);
758b8734d2Sitojun static int conditioner_parser(char *);
768b8734d2Sitojun static int tc_action_parser(char *, char **, struct tc_action *);
7795a65560Sthorpej 
7869881cf6Sitojun #define MAX_LINE	1024
79aeaca9dbSozaki-r #define MAX_WORD	128
8069881cf6Sitojun #define MAX_ARGS	64
8169881cf6Sitojun #define MAX_ACTIONS	16
8295a65560Sthorpej 
8395a65560Sthorpej #ifndef MAX
8495a65560Sthorpej #define MAX(a,b) (((a)>(b))?(a):(b))
8595a65560Sthorpej #endif
8695a65560Sthorpej #ifndef MIN
8795a65560Sthorpej #define MIN(a,b) (((a)<(b))?(a):(b))
8895a65560Sthorpej #endif
8969881cf6Sitojun #define EQUAL(s1, s2)	(strcmp((s1), (s2)) == 0)
9095a65560Sthorpej 
9169881cf6Sitojun int	line_no = 0;
9269881cf6Sitojun int	filter_dontwarn;
9395a65560Sthorpej 
9469881cf6Sitojun static char	curifname[IFNAMSIZ];
9569881cf6Sitojun static struct if_nameindex *if_namelist = NULL;
9669881cf6Sitojun 
9769881cf6Sitojun struct cmd_tab {
9869881cf6Sitojun 	const char	*cmd;
9969881cf6Sitojun 	int		(*parser)(char *);
10069881cf6Sitojun 	const char	*help;
10195a65560Sthorpej } cmd_tab[] = {
102b7e50f13Swiz 	{"?",		NULL,	"?"},
103b7e50f13Swiz 	{"help",	NULL,	"help"},
10469881cf6Sitojun 	{"quit",	NULL,	"quit"},
10569881cf6Sitojun 	{"interface",	interface_parser,	"interface if_name [bandwidth bps] [cbq|hfsc]"},
10669881cf6Sitojun 	{"class",	class_parser,	"class discipline if_name class_name [parent]"},
10769881cf6Sitojun 	{"filter",	filter_parser,	"filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]"},
10869881cf6Sitojun 	{"altq",	ctl_parser,	"altq if_name {enable|disable}"},
10969881cf6Sitojun 	{"delete",	delete_parser,	"delete if_name class_name [filter_name]"},
11095a65560Sthorpej #ifdef INET6
11169881cf6Sitojun 	{"filter6",	filter6_parser,	"filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]"},
11295a65560Sthorpej #endif
11369881cf6Sitojun 	{"red",		red_parser,	"red th_min th_max inv_pmax"},
11469881cf6Sitojun 	{"rio",		rio_parser,	"rio low_th_min low_th_max low_inv_pmax med_th_min med_th_max med_inv_pmax high_th_min high_th_max high_inv_pmax"},
11569881cf6Sitojun 	{"conditioner",	conditioner_parser,	"conditioner if_name cdnr_name <tc_action>"},
11669881cf6Sitojun 	{"debug",	NULL,		"debug"},
11769881cf6Sitojun 	{NULL,		NULL,		NULL}	/* termination */
11895a65560Sthorpej };
11995a65560Sthorpej 
12069881cf6Sitojun /*
12169881cf6Sitojun  * read one line from the specified stream. if it's a command,
12269881cf6Sitojun  * execute the command.
12369881cf6Sitojun  * returns 1 if OK, 0 if error or EOF.
12469881cf6Sitojun  */
12569881cf6Sitojun int
do_command(FILE * fp)12669881cf6Sitojun do_command(FILE *fp)
12769881cf6Sitojun {
12869881cf6Sitojun 	char	cmd_line[MAX_LINE], cmd[MAX_WORD], *cp;
12969881cf6Sitojun 	struct cmd_tab *tp;
13069881cf6Sitojun 	int	len, rval;
13169881cf6Sitojun 
13269881cf6Sitojun 	/*
13369881cf6Sitojun 	 * read a line from the stream and make it a null-terminated string
13469881cf6Sitojun 	 */
13569881cf6Sitojun 	cp = cmd_line;
13669881cf6Sitojun read_line:
13769881cf6Sitojun 	if (fgets(cp, &cmd_line[MAX_LINE] - cp, fp) == NULL)
13869881cf6Sitojun 		/* EOF or error */
13969881cf6Sitojun 		return(0);
14069881cf6Sitojun 	line_no++;
14169881cf6Sitojun 
14269881cf6Sitojun 	/* null-terminate the line */
14369881cf6Sitojun 	if ((len = strlen(cmd_line)) > 0) {
14469881cf6Sitojun 		cp = cmd_line + len - 1;
14569881cf6Sitojun 		if (*cp == '\n') {
14669881cf6Sitojun 			/* if escaped newline, read next line */
14769881cf6Sitojun 			if (len > 1 &&  *(cp - 1) == '\\')
14869881cf6Sitojun 				goto read_line;
14969881cf6Sitojun 			*cp = '\0';
15069881cf6Sitojun 		} else if (!feof(fp))
15169881cf6Sitojun 			err(1, "LINE %d too long!", line_no);
15269881cf6Sitojun 	}
15369881cf6Sitojun 	/* trim comments */
15469881cf6Sitojun 	if ((cp = strchr(cmd_line, '#')) != NULL)
15569881cf6Sitojun 		*cp = '\0';
15669881cf6Sitojun 
15769881cf6Sitojun 	cp = cmd_line;
15869881cf6Sitojun 	if ((len = next_word(&cp, cmd)) == 0)
15969881cf6Sitojun 		/* no command in this line */
16069881cf6Sitojun 		return (1);
16169881cf6Sitojun 
16269881cf6Sitojun 	/* fnind the corresponding parser */
16369881cf6Sitojun 	rval = 0;
16469881cf6Sitojun 	for (tp = cmd_tab; tp->cmd != NULL; tp++)
16569881cf6Sitojun 		if (strncmp(cmd, tp->cmd, len) == 0)
16669881cf6Sitojun 			break;
16769881cf6Sitojun 
16869881cf6Sitojun 	if (tp->cmd == NULL) {
16969881cf6Sitojun 		if (fp == stdin) {
17069881cf6Sitojun 			printf(" ?? %s\n", cmd);
17169881cf6Sitojun 			rval = 1;
17269881cf6Sitojun 		} else
17369881cf6Sitojun 			LOG(LOG_ERR, 0, "unknown command: %s", cmd);
17469881cf6Sitojun 		return (rval);
17569881cf6Sitojun 	}
17669881cf6Sitojun 
17769881cf6Sitojun 	if (tp->parser != NULL)
17869881cf6Sitojun 		rval = (*tp->parser)(cp);
17969881cf6Sitojun 	else {
18069881cf6Sitojun 		/* handle other commands */
18169881cf6Sitojun 		if (strcmp(tp->cmd, "quit") == 0)
18269881cf6Sitojun 			rval = 0;
18369881cf6Sitojun 		else if (strcmp(tp->cmd, "help") == 0 ||
18469881cf6Sitojun 			 strcmp(tp->cmd, "?") == 0) {
18569881cf6Sitojun 			for (tp = cmd_tab; tp->cmd != NULL; tp++)
18669881cf6Sitojun 				printf("%s\n", tp->help);
18769881cf6Sitojun 			rval = 1;
18869881cf6Sitojun 		} else if (strcmp(tp->cmd, "debug") == 0) {
18969881cf6Sitojun 			if (m_debug & DEBUG_ALTQ) {
19069881cf6Sitojun 				/* turn off verbose */
19169881cf6Sitojun 				l_debug = LOG_INFO;
19269881cf6Sitojun 				m_debug &= ~DEBUG_ALTQ;
19369881cf6Sitojun 			} else {
19469881cf6Sitojun 				/* turn on verbose */
19569881cf6Sitojun 				l_debug = LOG_DEBUG;
19669881cf6Sitojun 				m_debug |= DEBUG_ALTQ;
19769881cf6Sitojun 			}
19869881cf6Sitojun 			rval = 1;
19969881cf6Sitojun 		}
20069881cf6Sitojun 	}
20169881cf6Sitojun 	return (rval);
20269881cf6Sitojun }
20369881cf6Sitojun 
20495a65560Sthorpej static int
is_qdisc_name(const char * qname)20595a65560Sthorpej is_qdisc_name(const char *qname)
20695a65560Sthorpej {
20795a65560Sthorpej 	struct qdisc_parser *qp;
20895a65560Sthorpej 
20995a65560Sthorpej 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
21095a65560Sthorpej 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
21195a65560Sthorpej 			return (1);
21295a65560Sthorpej 	return (0);
21395a65560Sthorpej }
21495a65560Sthorpej 
21595a65560Sthorpej static int
qdisc_interface_parser(const char * qname,const char * ifname,int argc,char ** argv)21695a65560Sthorpej qdisc_interface_parser(const char * qname, const char *ifname,
21795a65560Sthorpej 		       int argc, char **argv)
21895a65560Sthorpej {
21995a65560Sthorpej 	struct qdisc_parser *qp;
22095a65560Sthorpej 
22195a65560Sthorpej 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
22295a65560Sthorpej 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
22395a65560Sthorpej 			return (*qp->interface_parser)(ifname, argc, argv);
22495a65560Sthorpej 	return (0);
22595a65560Sthorpej }
22695a65560Sthorpej 
22795a65560Sthorpej static int
qdisc_class_parser(const char * qname,const char * ifname,const char * class_name,const char * parent_name,int argc,char ** argv)22895a65560Sthorpej qdisc_class_parser(const char *qname, const char *ifname,
22995a65560Sthorpej 		   const char *class_name, const char *parent_name,
23095a65560Sthorpej 		   int argc, char **argv)
23195a65560Sthorpej {
23295a65560Sthorpej 	struct qdisc_parser *qp;
2338b8734d2Sitojun 	struct ifinfo	*ifinfo;
23495a65560Sthorpej 
23595a65560Sthorpej 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
23695a65560Sthorpej 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) {
23795a65560Sthorpej 			if (qp->class_parser == NULL) {
23895a65560Sthorpej 				LOG(LOG_ERR, 0,
2398b8734d2Sitojun 				    "class can't be specified for %s", qp->qname);
2408b8734d2Sitojun 				return (0);
2418b8734d2Sitojun 			}
2428b8734d2Sitojun 			if ((ifinfo = ifname2ifinfo(ifname)) == NULL) {
24369881cf6Sitojun 				LOG(LOG_ERR, 0, "no such interface");
2448b8734d2Sitojun 				return (0);
2458b8734d2Sitojun 			}
2468b8734d2Sitojun 			if (strncmp(ifinfo->qdisc->qname, qname,
2478b8734d2Sitojun 				    strlen(ifinfo->qdisc->qname)) != 0) {
2488b8734d2Sitojun 				LOG(LOG_ERR, 0,
24969881cf6Sitojun 				    "qname doesn't match the interface");
25095a65560Sthorpej 				return (0);
25195a65560Sthorpej 			}
25295a65560Sthorpej 			return (*qp->class_parser)(ifname, class_name,
25395a65560Sthorpej 						   parent_name, argc, argv);
25495a65560Sthorpej 		}
25595a65560Sthorpej 	return (0);
25695a65560Sthorpej }
25795a65560Sthorpej 
25895a65560Sthorpej /*
25969881cf6Sitojun  * read the config file
26095a65560Sthorpej  */
26195a65560Sthorpej int
qcmd_config(void)26295a65560Sthorpej qcmd_config(void)
26395a65560Sthorpej {
26469881cf6Sitojun 	FILE	*fp;
26569881cf6Sitojun 	int	rval;
26695a65560Sthorpej 
26795a65560Sthorpej 	if (if_namelist != NULL)
26895a65560Sthorpej 		if_freenameindex(if_namelist);
26995a65560Sthorpej 	if_namelist = if_nameindex();
27069881cf6Sitojun 	curifname[0] = '\0';
27195a65560Sthorpej 
272d5e1f166Sitojun 	LOG(LOG_INFO, 0, "ALTQ config file is %s", altqconfigfile);
27395a65560Sthorpej 
27469881cf6Sitojun 	fp = fopen(altqconfigfile, "r");
27569881cf6Sitojun 	if (fp == NULL) {
276d5e1f166Sitojun 		LOG(LOG_ERR, errno, "can't open %s", altqconfigfile, 0);
27795a65560Sthorpej 		return (QOPERR_INVAL);
27895a65560Sthorpej 	}
27995a65560Sthorpej 	line_no = 0;
28095a65560Sthorpej 	rval = 1;
28169881cf6Sitojun 	while (rval)
28269881cf6Sitojun 		rval = do_command(fp);
28369881cf6Sitojun 
28469881cf6Sitojun 	if (!feof(fp)) {
28569881cf6Sitojun 		LOG(LOG_ERR, 0, "Error in %s, line %d.  config failed.",
28669881cf6Sitojun 		    altqconfigfile, line_no);
28769881cf6Sitojun 		(void) qcmd_destroyall();
28869881cf6Sitojun 		rval = QOPERR_INVAL;
28969881cf6Sitojun 	} else
29095a65560Sthorpej 		rval = 0;
29169881cf6Sitojun 
29269881cf6Sitojun 	(void)fclose(fp);
29369881cf6Sitojun 	line_no = 0;
29495a65560Sthorpej 	return (rval);
29595a65560Sthorpej }
29695a65560Sthorpej 
29769881cf6Sitojun static int
next_word(char ** cpp,char * b)29869881cf6Sitojun next_word(char **cpp, char *b)
29995a65560Sthorpej {
30069881cf6Sitojun 	char	*cp;
30169881cf6Sitojun 	int	i;
30269881cf6Sitojun 
30369881cf6Sitojun 	cp = *cpp;
30469881cf6Sitojun 	while (*cp == ' ' || *cp == '\t')
30569881cf6Sitojun 		cp++;
30669881cf6Sitojun 	for (i = 0; i < MAX_WORD - 1; i++) {
30769881cf6Sitojun 		if (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\0')
30869881cf6Sitojun 			break;
30969881cf6Sitojun 		*b++ = *cp++;
31069881cf6Sitojun 	}
31169881cf6Sitojun 	*b = '\0';
31269881cf6Sitojun 	*cpp = cp;
31369881cf6Sitojun 	return (i);
31469881cf6Sitojun }
31569881cf6Sitojun 
31669881cf6Sitojun char *
cur_ifname(void)31769881cf6Sitojun cur_ifname(void)
31869881cf6Sitojun {
31969881cf6Sitojun 	return (curifname);
32095a65560Sthorpej }
32195a65560Sthorpej 
32295a65560Sthorpej u_int
get_ifindex(const char * ifname)32395a65560Sthorpej get_ifindex(const char *ifname)
32495a65560Sthorpej {
32595a65560Sthorpej 	struct if_nameindex *ifnp;
32695a65560Sthorpej 
32795a65560Sthorpej 	for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
32895a65560Sthorpej 		if (strcmp(ifname, ifnp->if_name) == 0)
32995a65560Sthorpej 			return (ifnp->if_index);
33095a65560Sthorpej 	return (0);
33195a65560Sthorpej }
33295a65560Sthorpej 
33395a65560Sthorpej static int
get_ifname(char ** cpp,char ** ifnamep)33495a65560Sthorpej get_ifname(char **cpp, char **ifnamep)
33595a65560Sthorpej {
33669881cf6Sitojun 	char w[MAX_WORD], *ocp;
33795a65560Sthorpej 	struct if_nameindex *ifnp;
33895a65560Sthorpej 
33995a65560Sthorpej 	ocp = *cpp;
34095a65560Sthorpej 	if (next_word(&ocp, w) && if_namelist != NULL)
34195a65560Sthorpej 		for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
34295a65560Sthorpej 			if (strcmp(w, ifnp->if_name) == 0) {
34395a65560Sthorpej 				/* if_name found. advance the word pointer */
34495a65560Sthorpej 				*cpp = ocp;
34569881cf6Sitojun 				strlcpy(curifname, w, sizeof(curifname));
34669881cf6Sitojun 				*ifnamep = curifname;
34795a65560Sthorpej 				return (1);
34895a65560Sthorpej 			}
34995a65560Sthorpej 
35095a65560Sthorpej 	/* this is not interface name. use one in the context. */
35169881cf6Sitojun 	if (curifname[0] == '\0')
35295a65560Sthorpej 		return (0);
35369881cf6Sitojun 	*ifnamep = curifname;
35495a65560Sthorpej 	return (1);
35595a65560Sthorpej }
35695a65560Sthorpej 
35795a65560Sthorpej /* set address and netmask in network byte order */
35895a65560Sthorpej static int
get_addr(char ** cpp,struct in_addr * addr,struct in_addr * mask)35995a65560Sthorpej get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask)
36095a65560Sthorpej {
36169881cf6Sitojun 	char w[MAX_WORD], *ocp;
36292b62a25Sitojun 	struct in_addr tmp;
36395a65560Sthorpej 
36495a65560Sthorpej 	addr->s_addr = 0;
36595a65560Sthorpej 	mask->s_addr = 0xffffffff;
36695a65560Sthorpej 
36795a65560Sthorpej 	if (!next_word(cpp, w))
36895a65560Sthorpej 		return (0);
36995a65560Sthorpej 
37092b62a25Sitojun 	if (inet_aton((char *)w, &tmp) != 1) {
37195a65560Sthorpej 		/* try gethostbyname */
37295a65560Sthorpej 		struct hostent *h;
37395a65560Sthorpej 
37469881cf6Sitojun 		if ((h = gethostbyname(w)) == NULL ||
37569881cf6Sitojun 		    h->h_addrtype != AF_INET || h->h_length != 4)
37695a65560Sthorpej 			return (0);
37795a65560Sthorpej 		bcopy(h->h_addr, &tmp, (size_t)h->h_length);
37895a65560Sthorpej 	}
37992b62a25Sitojun 	addr->s_addr = tmp.s_addr;
38095a65560Sthorpej 
38195a65560Sthorpej 	/* check if netmask option is present */
38295a65560Sthorpej 	ocp = *cpp;
38395a65560Sthorpej 	if (next_word(&ocp, w) && EQUAL(w, "netmask")) {
38495a65560Sthorpej 		if (!next_word(&ocp, w))
38595a65560Sthorpej 			return (0);
38692b62a25Sitojun 		if (inet_aton((char *)w, (struct in_addr *)&tmp) != 1)
38795a65560Sthorpej 			return (0);
38895a65560Sthorpej 
38992b62a25Sitojun 		mask->s_addr = tmp.s_addr;
39095a65560Sthorpej 		*cpp = ocp;
39195a65560Sthorpej 		return (1);
39295a65560Sthorpej 	}
39395a65560Sthorpej 	/* no netmask option */
39495a65560Sthorpej 	return (1);
39595a65560Sthorpej }
39695a65560Sthorpej 
39795a65560Sthorpej /* returns service number in network byte order */
39895a65560Sthorpej static int
get_port(const char * name,u_int16_t * port_no)39995a65560Sthorpej get_port(const char *name, u_int16_t *port_no)
40095a65560Sthorpej {
40195a65560Sthorpej 	struct servent *s;
40295a65560Sthorpej 	u_int16_t num;
40395a65560Sthorpej 
404238960afSdsl 	if (isdigit((unsigned char)name[0])) {
40595a65560Sthorpej 		num = (u_int16_t)strtol(name, NULL, 0);
40695a65560Sthorpej 		*port_no = htons(num);
40795a65560Sthorpej 		return (1);
40895a65560Sthorpej 	}
40995a65560Sthorpej 
41095a65560Sthorpej 	if ((s = getservbyname(name, 0)) == NULL)
41195a65560Sthorpej 		return (0);
41295a65560Sthorpej 
41395a65560Sthorpej 	*port_no = (u_int16_t)s->s_port;
41495a65560Sthorpej 	return (1);
41595a65560Sthorpej }
41695a65560Sthorpej 
41795a65560Sthorpej static int
get_proto(const char * name,int * proto_no)41895a65560Sthorpej get_proto(const char *name, int *proto_no)
41995a65560Sthorpej {
42095a65560Sthorpej 	struct protoent *p;
42195a65560Sthorpej 
422238960afSdsl 	if (isdigit((unsigned char)name[0])) {
42395a65560Sthorpej 		*proto_no = (int)strtol(name, NULL, 0);
42495a65560Sthorpej 		return (1);
42595a65560Sthorpej 	}
42695a65560Sthorpej 
42795a65560Sthorpej 	if ((p = getprotobyname(name)) == NULL)
42895a65560Sthorpej 		return (0);
42995a65560Sthorpej 
43095a65560Sthorpej 	*proto_no = p->p_proto;
43195a65560Sthorpej 	return (1);
43295a65560Sthorpej }
43395a65560Sthorpej 
43495a65560Sthorpej static int
get_fltr_opts(char ** cpp,char * fltr_name,size_t len,int * ruleno)4358b8734d2Sitojun get_fltr_opts(char **cpp, char *fltr_name, size_t len, int *ruleno)
43695a65560Sthorpej {
43769881cf6Sitojun 	char w[MAX_WORD], *ocp;
43895a65560Sthorpej 
43995a65560Sthorpej 	ocp = *cpp;
44095a65560Sthorpej 	while (next_word(&ocp, w)) {
44195a65560Sthorpej 		if (EQUAL(w, "name")) {
44295a65560Sthorpej 			if (!next_word(&ocp, w))
44395a65560Sthorpej 				return (0);
4448b8734d2Sitojun 			strlcpy(fltr_name, w, len);
44595a65560Sthorpej 			*cpp = ocp;
44695a65560Sthorpej 		} else if (EQUAL(w, "ruleno")) {
44795a65560Sthorpej 			if (!next_word(&ocp, w))
44895a65560Sthorpej 				return (0);
44995a65560Sthorpej 			*ruleno = (int)strtol(w, NULL, 0);
45095a65560Sthorpej 			*cpp = ocp;
45195a65560Sthorpej 		} else
45295a65560Sthorpej 			break;
45395a65560Sthorpej 	}
45495a65560Sthorpej 	return (1);
45595a65560Sthorpej }
45695a65560Sthorpej 
45795a65560Sthorpej 
45895a65560Sthorpej #define	DISCIPLINE_NONE		0
45995a65560Sthorpej 
46095a65560Sthorpej static int
interface_parser(char * cmdbuf)46195a65560Sthorpej interface_parser(char *cmdbuf)
46295a65560Sthorpej {
46369881cf6Sitojun 	char	w[MAX_WORD], *ap, *cp = cmdbuf;
46469881cf6Sitojun 	char	*ifname, *argv[MAX_ARGS], qdisc_name[MAX_WORD];
46569881cf6Sitojun 	int     argc;
46695a65560Sthorpej 
46795a65560Sthorpej 	if (!get_ifname(&cp, &ifname)) {
46869881cf6Sitojun 		LOG(LOG_ERR, 0, "missing interface name");
46995a65560Sthorpej 		return (0);
47095a65560Sthorpej 	}
47195a65560Sthorpej 
472*915340d5Sandvar 	/* create argument list & look for scheduling discipline options. */
47369881cf6Sitojun 	snprintf(qdisc_name, sizeof qdisc_name, "null");
47495a65560Sthorpej 	argc = 0;
47595a65560Sthorpej 	ap = w;
47695a65560Sthorpej 	while (next_word(&cp, ap)) {
47795a65560Sthorpej 		if (is_qdisc_name(ap))
47869881cf6Sitojun 			strlcpy(qdisc_name, ap, sizeof qdisc_name);
47995a65560Sthorpej 
48095a65560Sthorpej 		argv[argc] = ap;
48195a65560Sthorpej 		ap += strlen(ap) + 1;
48295a65560Sthorpej 		argc++;
48369881cf6Sitojun 		if (argc >= MAX_ARGS) {
48469881cf6Sitojun 			LOG(LOG_ERR, 0, "too many args");
48595a65560Sthorpej 			return (0);
48695a65560Sthorpej 		}
48795a65560Sthorpej 	}
48895a65560Sthorpej 
48969881cf6Sitojun 	return qdisc_interface_parser(qdisc_name, ifname, argc, argv);
49069881cf6Sitojun }
49169881cf6Sitojun 
49269881cf6Sitojun 
49395a65560Sthorpej static int
class_parser(char * cmdbuf)49495a65560Sthorpej class_parser(char *cmdbuf)
49595a65560Sthorpej {
49669881cf6Sitojun 	char	w[MAX_WORD], *cp = cmdbuf;
49769881cf6Sitojun 	char 	*ifname, qdisc_name[MAX_WORD];
49869881cf6Sitojun 	char	class_name[MAX_WORD], parent_name[MAX_WORD];
49995a65560Sthorpej 	char	*clname = class_name;
50095a65560Sthorpej 	char	*parent = NULL;
50169881cf6Sitojun 	char	*argv[MAX_ARGS], *ap;
50269881cf6Sitojun 	int	argc;
50395a65560Sthorpej 
50495a65560Sthorpej 	/* get scheduling class */
50595a65560Sthorpej 	if (!next_word(&cp, qdisc_name)) {
50669881cf6Sitojun 		LOG(LOG_ERR, 0, "missing discipline");
50795a65560Sthorpej 		return (0);
50895a65560Sthorpej 	}
50995a65560Sthorpej 	if (!is_qdisc_name(qdisc_name)) {
51069881cf6Sitojun 		LOG(LOG_ERR, 0, "unknown discipline '%s'", qdisc_name);
51195a65560Sthorpej 		return (0);
51295a65560Sthorpej 	}
51395a65560Sthorpej 
51495a65560Sthorpej 	/* get interface name */
51595a65560Sthorpej 	if (!get_ifname(&cp, &ifname)) {
51669881cf6Sitojun 		LOG(LOG_ERR, 0, "missing interface name");
51795a65560Sthorpej 		return (0);
51895a65560Sthorpej 	}
51995a65560Sthorpej 
52095a65560Sthorpej 	/* get class name */
52195a65560Sthorpej 	if (!next_word(&cp, class_name)) {
52269881cf6Sitojun 		LOG(LOG_ERR, 0, "missing class name");
52395a65560Sthorpej 		return (0);
52495a65560Sthorpej 	}
52595a65560Sthorpej 
52695a65560Sthorpej 	/* get parent name */
52795a65560Sthorpej 	if (!next_word(&cp, parent_name)) {
52869881cf6Sitojun 		LOG(LOG_ERR, 0, "missing parent class");
52995a65560Sthorpej 		return (0);
53095a65560Sthorpej 	}
53169881cf6Sitojun 	if (!EQUAL(parent_name, "null") && !EQUAL(parent_name, "NULL"))
53295a65560Sthorpej 		parent = parent_name;
53369881cf6Sitojun 	else
53495a65560Sthorpej 		parent = NULL;
53595a65560Sthorpej 
53695a65560Sthorpej 	ap = w;
53795a65560Sthorpej 	argc = 0;
53895a65560Sthorpej 	while (next_word(&cp, ap)) {
53995a65560Sthorpej 		argv[argc] = ap;
54095a65560Sthorpej 		ap += strlen(ap) + 1;
54195a65560Sthorpej 		argc++;
54269881cf6Sitojun 		if (argc >= MAX_ARGS) {
54369881cf6Sitojun 			LOG(LOG_ERR, 0, "too many args");
54495a65560Sthorpej 			return (0);
54595a65560Sthorpej 		}
54669881cf6Sitojun 	}
54795a65560Sthorpej 
54869881cf6Sitojun 	return qdisc_class_parser(qdisc_name, ifname, clname, parent,
54969881cf6Sitojun 				  argc, argv);
55095a65560Sthorpej }
55195a65560Sthorpej 
55295a65560Sthorpej static int
filter_parser(char * cmdbuf)55395a65560Sthorpej filter_parser(char *cmdbuf)
55495a65560Sthorpej {
55569881cf6Sitojun 	char 	w[MAX_WORD], *cp = cmdbuf;
55669881cf6Sitojun 	char 	*ifname, class_name[MAX_WORD], fltr_name[MAX_WORD];
55769881cf6Sitojun 	char	*flname = NULL;
55895a65560Sthorpej 	struct flow_filter	sfilt;
55995a65560Sthorpej 	int	protocol;
56095a65560Sthorpej 	u_char	tos, tosmask;
56195a65560Sthorpej 	int	ruleno;
56295a65560Sthorpej 	int	dontwarn = 0;
56395a65560Sthorpej 	int	error;
56495a65560Sthorpej 
56595a65560Sthorpej 	memset(&sfilt, 0, sizeof(sfilt));
56695a65560Sthorpej 	sfilt.ff_flow.fi_family = AF_INET;
56795a65560Sthorpej 
56895a65560Sthorpej 	if (!get_ifname(&cp, &ifname)) {
56969881cf6Sitojun 		LOG(LOG_ERR, 0, "missing interface name in filter command");
57095a65560Sthorpej 		return (0);
57195a65560Sthorpej 	}
57295a65560Sthorpej 
57395a65560Sthorpej 	if (!next_word(&cp, class_name)) {
57469881cf6Sitojun 		LOG(LOG_ERR, 0, "missing class name in filter command");
57595a65560Sthorpej 		return (0);
57695a65560Sthorpej 	}
57795a65560Sthorpej 
57895a65560Sthorpej 	fltr_name[0] = '\0';
57995a65560Sthorpej 	ruleno = 0;
5808b8734d2Sitojun 	if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) {
58169881cf6Sitojun 		LOG(LOG_ERR, 0, "bad filter option");
58295a65560Sthorpej 		return (0);
58395a65560Sthorpej 	}
58495a65560Sthorpej 	if (fltr_name[0] != '\0')
58595a65560Sthorpej 		flname = fltr_name;
58695a65560Sthorpej 	sfilt.ff_ruleno = ruleno;
58795a65560Sthorpej 
58895a65560Sthorpej 	/* get filter destination Address */
58995a65560Sthorpej 	if (!get_addr(&cp, &sfilt.ff_flow.fi_dst, &sfilt.ff_mask.mask_dst)) {
59069881cf6Sitojun 		LOG(LOG_ERR, 0, "bad filter destination address");
59195a65560Sthorpej 		return (0);
59295a65560Sthorpej 	}
59395a65560Sthorpej 
59495a65560Sthorpej 	/* get filter destination port */
59595a65560Sthorpej 	if (!next_word(&cp, w)) {
59669881cf6Sitojun 		LOG(LOG_ERR, 0, "missing filter destination port");
59795a65560Sthorpej 		return (0);
59895a65560Sthorpej 	}
59995a65560Sthorpej 	if (!get_port(w, &sfilt.ff_flow.fi_dport)) {
60069881cf6Sitojun 		LOG(LOG_ERR, 0, "bad filter destination port");
60195a65560Sthorpej 		return (0);
60295a65560Sthorpej 	}
60395a65560Sthorpej 
60495a65560Sthorpej 	/* get filter source address */
60595a65560Sthorpej 	if (!get_addr(&cp, &sfilt.ff_flow.fi_src, &sfilt.ff_mask.mask_src)) {
60669881cf6Sitojun 		LOG(LOG_ERR, 0, "bad filter source address");
60795a65560Sthorpej 		return (0);
60895a65560Sthorpej 	}
60995a65560Sthorpej 
61095a65560Sthorpej 	/* get filter source port */
61195a65560Sthorpej 	if (!next_word(&cp, w)) {
61269881cf6Sitojun 		LOG(LOG_ERR, 0, "missing filter source port");
61395a65560Sthorpej 		return (0);
61495a65560Sthorpej 	}
61595a65560Sthorpej 	if (!get_port(w, &sfilt.ff_flow.fi_sport)) {
61669881cf6Sitojun 		LOG(LOG_ERR, 0, "bad filter source port");
61795a65560Sthorpej 		return (0);
61895a65560Sthorpej 	}
61995a65560Sthorpej 
62095a65560Sthorpej 	/* get filter protocol id */
62195a65560Sthorpej 	if (!next_word(&cp, w)) {
62269881cf6Sitojun 		LOG(LOG_ERR, 0, "missing filter protocol");
62395a65560Sthorpej 		return (0);
62495a65560Sthorpej 	}
62595a65560Sthorpej 	if (!get_proto(w, &protocol)) {
62669881cf6Sitojun 		LOG(LOG_ERR, 0, "bad protocol");
62795a65560Sthorpej 		return (0);
62895a65560Sthorpej 	}
62995a65560Sthorpej 	sfilt.ff_flow.fi_proto = protocol;
63095a65560Sthorpej 
63195a65560Sthorpej 	while (next_word(&cp, w)) {
63295a65560Sthorpej 		if (EQUAL(w, "tos")) {
63395a65560Sthorpej 			tos = 0;
63495a65560Sthorpej 			tosmask = 0xff;
63595a65560Sthorpej 
63695a65560Sthorpej 			if (next_word(&cp, w)) {
63795a65560Sthorpej 				tos = (u_char)strtol(w, NULL, 0);
63895a65560Sthorpej 				if (next_word(&cp, w)) {
63995a65560Sthorpej 					if (EQUAL(w, "tosmask")) {
64095a65560Sthorpej 						next_word(&cp, w);
64195a65560Sthorpej 						tosmask = (u_char)strtol(w, NULL, 0);
64295a65560Sthorpej 					}
64395a65560Sthorpej 				}
64495a65560Sthorpej 			}
64595a65560Sthorpej 			sfilt.ff_flow.fi_tos = tos;
64695a65560Sthorpej 			sfilt.ff_mask.mask_tos = tosmask;
64795a65560Sthorpej 		} else if (EQUAL(w, "gpi")) {
64895a65560Sthorpej 			if (next_word(&cp, w)) {
64995a65560Sthorpej 				sfilt.ff_flow.fi_gpi =
65095a65560Sthorpej 					(u_int32_t)strtoul(w, NULL, 0);
65195a65560Sthorpej 				sfilt.ff_flow.fi_gpi =
65295a65560Sthorpej 					htonl(sfilt.ff_flow.fi_gpi);
65395a65560Sthorpej 			}
65495a65560Sthorpej 		} else if (EQUAL(w, "dontwarn"))
65595a65560Sthorpej 			dontwarn = 1;
65695a65560Sthorpej 	}
65795a65560Sthorpej 
65895a65560Sthorpej 	/*
65995a65560Sthorpej 	 * Add the filter.
66095a65560Sthorpej 	 */
66195a65560Sthorpej 	filter_dontwarn = dontwarn;	/* XXX */
66295a65560Sthorpej 	error = qcmd_add_filter(ifname, class_name, flname, &sfilt);
66395a65560Sthorpej 	filter_dontwarn = 0;		/* XXX */
66495a65560Sthorpej 	if (error) {
66595a65560Sthorpej 		LOG(LOG_ERR, 0,
666d5e1f166Sitojun 		    "can't add filter to class '%s' on interface '%s'",
66795a65560Sthorpej 		    class_name, ifname);
66895a65560Sthorpej 		return (0);
66995a65560Sthorpej 	}
67095a65560Sthorpej 	return (1);
67195a65560Sthorpej }
67295a65560Sthorpej 
67395a65560Sthorpej #ifdef INET6
67495a65560Sthorpej static int
filter6_parser(char * cmdbuf)67595a65560Sthorpej filter6_parser(char *cmdbuf)
67695a65560Sthorpej {
67769881cf6Sitojun 	char 	w[MAX_WORD], *cp = cmdbuf;
67869881cf6Sitojun 	char 	*ifname, class_name[MAX_WORD], fltr_name[MAX_WORD];
67969881cf6Sitojun 	char	*flname = NULL;
68095a65560Sthorpej 	struct flow_filter6	sfilt;
68195a65560Sthorpej 	int	protocol;
68295a65560Sthorpej 	u_char	tclass, tclassmask;
68395a65560Sthorpej 	int	ruleno;
68495a65560Sthorpej 	int	dontwarn = 0;
68595a65560Sthorpej 	int	ret;
68695a65560Sthorpej 
68795a65560Sthorpej 	memset(&sfilt, 0, sizeof(sfilt));
68895a65560Sthorpej 	sfilt.ff_flow6.fi6_family = AF_INET6;
68995a65560Sthorpej 
69095a65560Sthorpej 	if (!get_ifname(&cp, &ifname)) {
69169881cf6Sitojun 		LOG(LOG_ERR, 0, "missing interface name");
69295a65560Sthorpej 		return (0);
69395a65560Sthorpej 	}
69495a65560Sthorpej 
69595a65560Sthorpej 	if (!next_word(&cp, class_name)) {
69669881cf6Sitojun 		LOG(LOG_ERR, 0, "missing class name");
69795a65560Sthorpej 		return (0);
69895a65560Sthorpej 	}
69995a65560Sthorpej 
70095a65560Sthorpej 	fltr_name[0] = '\0';
70195a65560Sthorpej 	ruleno = 0;
7028b8734d2Sitojun 	if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) {
70369881cf6Sitojun 		LOG(LOG_ERR, 0, "bad filter option");
70495a65560Sthorpej 		return (0);
70595a65560Sthorpej 	}
70695a65560Sthorpej 	if (fltr_name[0] != '\0')
70795a65560Sthorpej 		flname = fltr_name;
70895a65560Sthorpej 	sfilt.ff_ruleno = ruleno;
70995a65560Sthorpej 
71095a65560Sthorpej 	/* get filter destination address */
71195a65560Sthorpej 	if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_dst,
71295a65560Sthorpej 			 &sfilt.ff_mask6.mask6_dst)) {
71369881cf6Sitojun 		LOG(LOG_ERR, 0, "bad destination address");
71495a65560Sthorpej 		return (0);
71595a65560Sthorpej 	}
71695a65560Sthorpej 
71795a65560Sthorpej 	/* get filter destination port */
71895a65560Sthorpej 	if (!next_word(&cp, w)) {
71969881cf6Sitojun 		LOG(LOG_ERR, 0, "missing filter destination port");
72095a65560Sthorpej 		return (0);
72195a65560Sthorpej 	}
72295a65560Sthorpej 	if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) {
72369881cf6Sitojun 		LOG(LOG_ERR, 0, "bad filter destination port");
72495a65560Sthorpej 		return (0);
72595a65560Sthorpej 	}
72695a65560Sthorpej 
72795a65560Sthorpej 	/* get filter source address */
72895a65560Sthorpej 	if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_src,
72995a65560Sthorpej 			 &sfilt.ff_mask6.mask6_src)) {
73069881cf6Sitojun 		LOG(LOG_ERR, 0, "bad source address");
73195a65560Sthorpej 		return (0);
73295a65560Sthorpej 	}
73395a65560Sthorpej 
73495a65560Sthorpej 	/* get filter source port */
73595a65560Sthorpej 	if (!next_word(&cp, w)) {
73669881cf6Sitojun 		LOG(LOG_ERR, 0, "missing filter source port");
73795a65560Sthorpej 		return (0);
73895a65560Sthorpej 	}
73995a65560Sthorpej 	if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) {
74069881cf6Sitojun 		LOG(LOG_ERR, 0, "bad filter source port");
74195a65560Sthorpej 		return (0);
74295a65560Sthorpej 	}
74395a65560Sthorpej 
74495a65560Sthorpej 	/* get filter protocol id */
74595a65560Sthorpej 	if (!next_word(&cp, w)) {
74669881cf6Sitojun 		LOG(LOG_ERR, 0, "missing filter protocol");
74795a65560Sthorpej 		return (0);
74895a65560Sthorpej 	}
74995a65560Sthorpej 	if (!get_proto(w, &protocol)) {
75069881cf6Sitojun 		LOG(LOG_ERR, 0, "bad protocol");
75195a65560Sthorpej 		return (0);
75295a65560Sthorpej 	}
75395a65560Sthorpej 	sfilt.ff_flow6.fi6_proto = protocol;
75495a65560Sthorpej 
75595a65560Sthorpej 	while (next_word(&cp, w)) {
75695a65560Sthorpej 		if (EQUAL(w, "tclass")) {
75795a65560Sthorpej 			tclass = 0;
75895a65560Sthorpej 			tclassmask = 0xff;
75995a65560Sthorpej 
76095a65560Sthorpej 			if (next_word(&cp, w)) {
76195a65560Sthorpej 				tclass = (u_char)strtol(w, NULL, 0);
76295a65560Sthorpej 				if (next_word(&cp, w)) {
76395a65560Sthorpej 					if (EQUAL(w, "tclassmask")) {
76495a65560Sthorpej 						next_word(&cp, w);
76595a65560Sthorpej 						tclassmask =
76695a65560Sthorpej 						    (u_char)strtol(w, NULL, 0);
76795a65560Sthorpej 					}
76895a65560Sthorpej 				}
76995a65560Sthorpej 			}
77095a65560Sthorpej 			sfilt.ff_flow6.fi6_tclass = tclass;
77195a65560Sthorpej 			sfilt.ff_mask6.mask6_tclass = tclassmask;
77295a65560Sthorpej 		} else if (EQUAL(w, "gpi")) {
77395a65560Sthorpej 			if (next_word(&cp, w)) {
77495a65560Sthorpej 				sfilt.ff_flow6.fi6_gpi =
77595a65560Sthorpej 					(u_int32_t)strtoul(w, NULL, 0);
77695a65560Sthorpej 				sfilt.ff_flow6.fi6_gpi =
77795a65560Sthorpej 					htonl(sfilt.ff_flow6.fi6_gpi);
77895a65560Sthorpej 			}
77995a65560Sthorpej 		} else if (EQUAL(w, "flowlabel")) {
78095a65560Sthorpej 			if (next_word(&cp, w)) {
78195a65560Sthorpej 				sfilt.ff_flow6.fi6_flowlabel =
78295a65560Sthorpej 				   (u_int32_t)strtoul(w, NULL, 0) & 0x000fffff;
78395a65560Sthorpej 				sfilt.ff_flow6.fi6_flowlabel =
78495a65560Sthorpej 					htonl(sfilt.ff_flow6.fi6_flowlabel);
78595a65560Sthorpej 			}
78695a65560Sthorpej 		} else if (EQUAL(w, "dontwarn"))
78795a65560Sthorpej 			dontwarn = 1;
78895a65560Sthorpej 	}
78995a65560Sthorpej 
79095a65560Sthorpej 	/*
79195a65560Sthorpej 	 * Add the filter.
79295a65560Sthorpej 	 */
79395a65560Sthorpej 	filter_dontwarn = dontwarn;	/* XXX */
79495a65560Sthorpej 	ret = qcmd_add_filter(ifname, class_name, flname,
79595a65560Sthorpej 			      (struct flow_filter *)&sfilt);
79695a65560Sthorpej 	filter_dontwarn = 0;		/* XXX */
79795a65560Sthorpej 	if (ret) {
79895a65560Sthorpej 		LOG(LOG_ERR, 0,
799d5e1f166Sitojun 		    "can't add filter to class '%s' on interface '%s'",
80095a65560Sthorpej 		    class_name, ifname);
80195a65560Sthorpej 		return (0);
80295a65560Sthorpej 	}
80395a65560Sthorpej 
80495a65560Sthorpej 	return (1);
80595a65560Sthorpej }
80695a65560Sthorpej 
80795a65560Sthorpej static int
get_ip6addr(char ** cpp,struct in6_addr * addr,struct in6_addr * mask)80895a65560Sthorpej get_ip6addr(char **cpp, struct in6_addr *addr, struct in6_addr *mask)
80995a65560Sthorpej {
81069881cf6Sitojun 	char w[MAX_WORD], *prefix;
81195a65560Sthorpej 	u_char *cp;
81295a65560Sthorpej 	int len;
81395a65560Sthorpej 
81495a65560Sthorpej 	*addr = in6addr_any;  /* set all 0 */
81595a65560Sthorpej 	*mask = in6addr_any;  /* set all 0 */
81695a65560Sthorpej 
81795a65560Sthorpej 	if (!next_word(cpp, w))
81895a65560Sthorpej 		return (0);
81995a65560Sthorpej 
82095a65560Sthorpej 	if (EQUAL(w, "0"))
82195a65560Sthorpej 		/* abbreviation of a wildcard (::0) */
82295a65560Sthorpej 		return (1);
82395a65560Sthorpej 
82495a65560Sthorpej 	if ((prefix = strchr(w, '/')) != NULL) {
82595a65560Sthorpej 		/* address has prefix length */
82695a65560Sthorpej 		*prefix++ = '\0';
82795a65560Sthorpej 	}
82895a65560Sthorpej 
82992b62a25Sitojun 	if (inet_pton(AF_INET6, w, addr) != 1)
83095a65560Sthorpej 		return (0);
83195a65560Sthorpej 
83295a65560Sthorpej 	if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefix == NULL)
83395a65560Sthorpej 		/* wildcard */
83495a65560Sthorpej 		return (1);
83595a65560Sthorpej 
83695a65560Sthorpej 	/* convert address prefix length to address mask */
83795a65560Sthorpej 	if (prefix != NULL) {
83895a65560Sthorpej 		len = (int)strtol(prefix, NULL, 0);
83995a65560Sthorpej 		if ((len < 0) || (len > 128))
84095a65560Sthorpej 			return (0);
84195a65560Sthorpej 		for (cp = (u_char *)mask; len > 7; len -= 8)
84295a65560Sthorpej 			*cp++ = 0xff;
84395a65560Sthorpej 		if (len > 0)
84495a65560Sthorpej 			*cp = (0xff << (8 - len)) & 0xff;
84595a65560Sthorpej 
8460d33c75cSchristos 		IN6ADDR32_SET(addr, 0, IN6ADDR32_GET(mask, 0) &
8470d33c75cSchristos 		    IN6ADDR32_GET(addr, 0));
8480d33c75cSchristos 		IN6ADDR32_SET(addr, 1, IN6ADDR32_GET(mask, 1) &
8490d33c75cSchristos 		    IN6ADDR32_GET(addr, 1));
8500d33c75cSchristos 		IN6ADDR32_SET(addr, 2, IN6ADDR32_GET(mask, 2) &
8510d33c75cSchristos 		    IN6ADDR32_GET(addr, 2));
8520d33c75cSchristos 		IN6ADDR32_SET(addr, 3, IN6ADDR32_GET(mask, 3) &
8530d33c75cSchristos 		    IN6ADDR32_GET(addr, 3));
85495a65560Sthorpej 	} else
85595a65560Sthorpej 		/* full mask */
85695a65560Sthorpej 		memset(mask, 0xff, sizeof(struct in6_addr));
85795a65560Sthorpej 
85895a65560Sthorpej 	return (1);
85995a65560Sthorpej }
86095a65560Sthorpej 
86195a65560Sthorpej #endif /* INET6 */
86295a65560Sthorpej 
86395a65560Sthorpej static int
ctl_parser(char * cmdbuf)86495a65560Sthorpej ctl_parser(char *cmdbuf)
86595a65560Sthorpej {
86669881cf6Sitojun 	char	w[MAX_WORD], *cp = cmdbuf;
86795a65560Sthorpej 	char	*ifname;
86895a65560Sthorpej 	int	state;
86995a65560Sthorpej 	int	rval;
87095a65560Sthorpej 
87195a65560Sthorpej 	if (!get_ifname(&cp, &ifname)) {
87295a65560Sthorpej 		printf("missing interface name in %s, line %d",
87395a65560Sthorpej 		       altqconfigfile, line_no);
87495a65560Sthorpej 		return (0);
87595a65560Sthorpej 	}
87695a65560Sthorpej 
87795a65560Sthorpej 	if (!next_word(&cp, w)) {
87895a65560Sthorpej 		state = is_q_enabled(ifname);
87995a65560Sthorpej 		printf("altq %s on %s\n",
88095a65560Sthorpej 		       state ? "enabled" : "disabled", ifname);
88195a65560Sthorpej 		return (1);
88295a65560Sthorpej 	}
88395a65560Sthorpej 
88495a65560Sthorpej 	if (EQUAL(w, "enable")) {
88595a65560Sthorpej 		rval = qcmd_enable(ifname);
88695a65560Sthorpej 		printf("altq %s on %s\n",
88795a65560Sthorpej 		       (rval == 0) ? "enabled" : "enable failed!", ifname);
88895a65560Sthorpej 	} else if (EQUAL(w, "disable")) {
88995a65560Sthorpej 		rval = qcmd_disable(ifname);
89095a65560Sthorpej 		printf("altq %s on %s\n",
89195a65560Sthorpej 		       (rval == 0) ? "disabled" : "disable failed!", ifname);
89295a65560Sthorpej 	} else if (EQUAL(w, "reload")) {
89395a65560Sthorpej 		printf("reinitializing altq...\n");
89495a65560Sthorpej 		qcmd_destroyall();
89595a65560Sthorpej 		qcmd_init();
89695a65560Sthorpej 	} else
89795a65560Sthorpej 		return (0);
89895a65560Sthorpej 	return (1);
89995a65560Sthorpej }
90095a65560Sthorpej 
90195a65560Sthorpej static int
delete_parser(char * cmdbuf)90295a65560Sthorpej delete_parser(char *cmdbuf)
90395a65560Sthorpej {
90495a65560Sthorpej 	char	*cp = cmdbuf;
90569881cf6Sitojun 	char	*ifname, class_name[MAX_WORD], filter_name[MAX_WORD];
90695a65560Sthorpej 	int	ret;
90795a65560Sthorpej 
90895a65560Sthorpej 	if (!get_ifname(&cp, &ifname)) {
90969881cf6Sitojun 		LOG(LOG_ERR, 0, "missing interface name");
91095a65560Sthorpej 		return (0);
91195a65560Sthorpej 	}
91295a65560Sthorpej 
91395a65560Sthorpej 	if (!next_word(&cp, class_name)) {
91469881cf6Sitojun 		LOG(LOG_ERR, 0, "missing class name");
91595a65560Sthorpej 		return (0);
91695a65560Sthorpej 	}
91795a65560Sthorpej 
91869881cf6Sitojun 	/* check if filter is specified */
91969881cf6Sitojun 	if (next_word(&cp, filter_name)) {
92069881cf6Sitojun 		ret = qcmd_delete_filter(ifname, class_name, filter_name);
92169881cf6Sitojun 		if (ret) {
92269881cf6Sitojun 			LOG(LOG_ERR, 0,
92369881cf6Sitojun 			    "can't delete filter '%s' on interface '%s'",
92469881cf6Sitojun 			    filter_name, ifname);
92569881cf6Sitojun 			return (0);
92669881cf6Sitojun 		}
92769881cf6Sitojun 		return (1);
92869881cf6Sitojun 	}
92969881cf6Sitojun 
93095a65560Sthorpej 	ret = qcmd_delete_class(ifname, class_name);
93195a65560Sthorpej 	if (ret) {
93295a65560Sthorpej 		LOG(LOG_ERR, 0,
933d5e1f166Sitojun 		    "can't delete class '%s' on interface '%s'",
93495a65560Sthorpej 		    class_name, ifname);
93595a65560Sthorpej 		return (0);
93695a65560Sthorpej 	}
93795a65560Sthorpej 
93895a65560Sthorpej 	return (1);
93995a65560Sthorpej }
94095a65560Sthorpej 
94195a65560Sthorpej static int
red_parser(char * cmdbuf)94295a65560Sthorpej red_parser(char *cmdbuf)
94395a65560Sthorpej {
94469881cf6Sitojun 	char	w[MAX_WORD], *cp = cmdbuf;
94595a65560Sthorpej 	int th_min, th_max, inv_pmax;
94695a65560Sthorpej 
94795a65560Sthorpej 	if (!next_word(&cp, w))
94895a65560Sthorpej 		goto bad;
94995a65560Sthorpej 	th_min = (int)strtol(w, NULL, 0);
95095a65560Sthorpej 
95195a65560Sthorpej 	if (!next_word(&cp, w))
95295a65560Sthorpej 		goto bad;
95395a65560Sthorpej 	th_max = (int)strtol(w, NULL, 0);
95495a65560Sthorpej 
95595a65560Sthorpej 	if (!next_word(&cp, w))
95695a65560Sthorpej 		goto bad;
95795a65560Sthorpej 	inv_pmax = (int)strtol(w, NULL, 0);
95895a65560Sthorpej 
95995a65560Sthorpej 	if (qop_red_set_defaults(th_min, th_max, inv_pmax) != 0) {
960d5e1f166Sitojun 		LOG(LOG_ERR, 0, "can't set red default parameters");
96195a65560Sthorpej 		return (0);
96295a65560Sthorpej 	}
96395a65560Sthorpej 
96495a65560Sthorpej 	return (1);
96595a65560Sthorpej 
96695a65560Sthorpej  bad:
96769881cf6Sitojun 	LOG(LOG_ERR, 0, "bad red parameter");
96895a65560Sthorpej 	return (0);
96995a65560Sthorpej }
97095a65560Sthorpej 
97195a65560Sthorpej static int
rio_parser(char * cmdbuf)97295a65560Sthorpej rio_parser(char *cmdbuf)
97395a65560Sthorpej {
97469881cf6Sitojun 	char	w[MAX_WORD], *cp = cmdbuf;
97595a65560Sthorpej 	int	i;
97695a65560Sthorpej 	struct redparams params[RIO_NDROPPREC];
97795a65560Sthorpej 
97895a65560Sthorpej 	for (i = 0; i < RIO_NDROPPREC; i++) {
97995a65560Sthorpej 		if (!next_word(&cp, w))
98095a65560Sthorpej 			goto bad;
98195a65560Sthorpej 		params[i].th_min = (int)strtol(w, NULL, 0);
98295a65560Sthorpej 
98395a65560Sthorpej 		if (!next_word(&cp, w))
98495a65560Sthorpej 			goto bad;
98595a65560Sthorpej 		params[i].th_max = (int)strtol(w, NULL, 0);
98695a65560Sthorpej 
98795a65560Sthorpej 		if (!next_word(&cp, w))
98895a65560Sthorpej 			goto bad;
98995a65560Sthorpej 		params[i].inv_pmax = (int)strtol(w, NULL, 0);
99095a65560Sthorpej 	}
99195a65560Sthorpej 
99295a65560Sthorpej 	if (qop_rio_set_defaults(&params[0]) != 0) {
993d5e1f166Sitojun 		LOG(LOG_ERR, 0, "can't set rio default parameters");
99495a65560Sthorpej 		return (0);
99595a65560Sthorpej 	}
99695a65560Sthorpej 
99795a65560Sthorpej 	return (1);
99895a65560Sthorpej 
99995a65560Sthorpej  bad:
100069881cf6Sitojun 	LOG(LOG_ERR, 0, "bad rio parameter");
100195a65560Sthorpej 	return (0);
100295a65560Sthorpej }
100395a65560Sthorpej 
100495a65560Sthorpej static int
conditioner_parser(char * cmdbuf)100595a65560Sthorpej conditioner_parser(char *cmdbuf)
100695a65560Sthorpej {
100769881cf6Sitojun 	char	cdnr_name[MAX_WORD], *cp = cmdbuf;
100895a65560Sthorpej 	char	*ifname;
100969881cf6Sitojun 	struct tc_action action[MAX_ACTIONS];
101095a65560Sthorpej 
101195a65560Sthorpej 	if (!get_ifname(&cp, &ifname)) {
101269881cf6Sitojun 		LOG(LOG_ERR, 0, "missing interface name");
101395a65560Sthorpej 		return (0);
101495a65560Sthorpej 	}
101595a65560Sthorpej 
101695a65560Sthorpej 	/* get conditioner name */
101795a65560Sthorpej 	if (!next_word(&cp, cdnr_name)) {
101869881cf6Sitojun 		LOG(LOG_ERR, 0, "missing cdnr name");
101995a65560Sthorpej 		return (0);
102095a65560Sthorpej 	}
102195a65560Sthorpej 
102295a65560Sthorpej 	if (tc_action_parser(ifname, &cp, &action[0]) == 0)
102395a65560Sthorpej 		return (0);
102495a65560Sthorpej 
102595a65560Sthorpej 	if (qcmd_cdnr_add_element(NULL, ifname, cdnr_name, &action[0]) != 0)
102695a65560Sthorpej 		return (0);
102795a65560Sthorpej 	return (1);
102895a65560Sthorpej }
102995a65560Sthorpej 
103095a65560Sthorpej /*
103195a65560Sthorpej  * recursively parse '<'tc_action'>'
103295a65560Sthorpej  * note that array "action" grows during recursive parse.
103395a65560Sthorpej  */
103495a65560Sthorpej static int
tc_action_parser(char * ifname,char ** cpp,struct tc_action * action)103595a65560Sthorpej tc_action_parser(char *ifname, char **cpp, struct tc_action *action)
103695a65560Sthorpej {
103795a65560Sthorpej 	char	*cp, *start, *end;
103869881cf6Sitojun 	char	type[MAX_WORD], w[MAX_WORD];
103995a65560Sthorpej 	int	depth, i;
104095a65560Sthorpej 	struct tb_profile profile[2];
104195a65560Sthorpej 
104295a65560Sthorpej 	/*
104395a65560Sthorpej 	 * find a possibly nested pair of '<' and '>',
104495a65560Sthorpej 	 * make them pointed by 'start' and 'end'.
104595a65560Sthorpej 	 */
104695a65560Sthorpej 	start = strchr(*cpp, '<');
104795a65560Sthorpej 	if (start == NULL) {
104869881cf6Sitojun 		LOG(LOG_ERR, 0, "conditioner action missing");
104995a65560Sthorpej 		return (0);
105095a65560Sthorpej 	}
105195a65560Sthorpej 	depth = 1;
105295a65560Sthorpej 	cp = start + 1;
105395a65560Sthorpej 	do {
105495a65560Sthorpej 		end = strpbrk(cp, "<>");
105595a65560Sthorpej 		if (end == NULL) {
105695a65560Sthorpej 			LOG(LOG_ERR, 0,
105769881cf6Sitojun 			    "conditioner action delimiter mismatch");
105895a65560Sthorpej 			return (0);
105995a65560Sthorpej 		}
106095a65560Sthorpej 		if (*end == '<')
106195a65560Sthorpej 			depth++;
106295a65560Sthorpej 		else if (*end == '>')
106395a65560Sthorpej 			depth--;
106495a65560Sthorpej 		cp = end + 1;
106595a65560Sthorpej 	} while (depth > 0);
106695a65560Sthorpej 	*end = '\0';
106795a65560Sthorpej 	*cpp = end + 1;
106895a65560Sthorpej 	cp = start + 1;
106995a65560Sthorpej 
107095a65560Sthorpej 	if (IsDebug(DEBUG_ALTQ)) {
107195a65560Sthorpej 		printf("tc_action_parser: [%s]\n", cp);
107295a65560Sthorpej 	}
107395a65560Sthorpej 
107495a65560Sthorpej 	if (!next_word(&cp, type)) {
107569881cf6Sitojun 		LOG(LOG_ERR, 0, "missing conditioner action type");
107695a65560Sthorpej 		return (0);
107795a65560Sthorpej 	}
107895a65560Sthorpej 
107995a65560Sthorpej 	/*
108095a65560Sthorpej 	 * action type specific process
108195a65560Sthorpej 	 */
108295a65560Sthorpej 	if (EQUAL(type, "conditioner")) {
108395a65560Sthorpej 		if (!next_word(&cp, w)) {
108495a65560Sthorpej 			LOG(LOG_ERR, 0,
108569881cf6Sitojun 			    "missing conditioner name");
108695a65560Sthorpej 			return (0);
108795a65560Sthorpej 		}
108895a65560Sthorpej 		action->tca_code = TCACODE_HANDLE;
108995a65560Sthorpej 		action->tca_handle = cdnr_name2handle(ifname, w);
109095a65560Sthorpej 		if (action->tca_handle == CDNR_NULL_HANDLE) {
109195a65560Sthorpej 			LOG(LOG_ERR, 0,
109269881cf6Sitojun 			    "wrong conditioner name %s", w);
109395a65560Sthorpej 			return (0);
109495a65560Sthorpej 		}
109595a65560Sthorpej 	} else if (EQUAL(type, "pass")) {
109695a65560Sthorpej 		action->tca_code = TCACODE_PASS;
109795a65560Sthorpej 	} else if (EQUAL(type, "drop")) {
109895a65560Sthorpej 		action->tca_code = TCACODE_DROP;
109995a65560Sthorpej 	} else if (EQUAL(type, "mark")) {
110095a65560Sthorpej 		if (!next_word(&cp, w)) {
110169881cf6Sitojun 			LOG(LOG_ERR, 0, "missing dscp");
110295a65560Sthorpej 			return (0);
110395a65560Sthorpej 		}
110495a65560Sthorpej 		action->tca_code = TCACODE_MARK;
110595a65560Sthorpej 		action->tca_dscp = (u_int8_t)strtol(w, NULL, 0);
110695a65560Sthorpej 	} else if (EQUAL(type, "tbmeter")) {
110795a65560Sthorpej 		if (!next_word(&cp, w)) {
110869881cf6Sitojun 			LOG(LOG_ERR, 0, "missing tb profile");
110995a65560Sthorpej 			return (0);
111095a65560Sthorpej 		}
111195a65560Sthorpej 		profile[0].rate = atobps(w);
111295a65560Sthorpej 		if (!next_word(&cp, w)) {
111369881cf6Sitojun 			LOG(LOG_ERR, 0, "missing tb profile");
111495a65560Sthorpej 			return (0);
111595a65560Sthorpej 		}
111695a65560Sthorpej 		profile[0].depth = atobytes(w);
111795a65560Sthorpej 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
111895a65560Sthorpej 			return (0);
111995a65560Sthorpej 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
112095a65560Sthorpej 			return (0);
112195a65560Sthorpej 
112295a65560Sthorpej 		if (qcmd_cdnr_add_tbmeter(action, ifname, NULL, &profile[0],
112395a65560Sthorpej 					  &action[1], &action[2]) != 0)
112495a65560Sthorpej 			return (0);
112595a65560Sthorpej 	} else if (EQUAL(type, "trtcm")) {
112695a65560Sthorpej 		int coloraware = 0;	/* default is color-blind */
112795a65560Sthorpej 
112895a65560Sthorpej 		for (i=0; i<2; i++) {
112995a65560Sthorpej 			if (!next_word(&cp, w)) {
113069881cf6Sitojun 				LOG(LOG_ERR, 0, "missing tb profile");
113195a65560Sthorpej 				return (0);
113295a65560Sthorpej 			}
113395a65560Sthorpej 			profile[i].rate = atobps(w);
113495a65560Sthorpej 			if (!next_word(&cp, w)) {
113569881cf6Sitojun 				LOG(LOG_ERR, 0, "missing tb profile");
113695a65560Sthorpej 				return (0);
113795a65560Sthorpej 			}
113895a65560Sthorpej 			profile[i].depth = atobytes(w);
113995a65560Sthorpej 		}
114095a65560Sthorpej 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
114195a65560Sthorpej 			return (0);
114295a65560Sthorpej 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
114395a65560Sthorpej 			return (0);
114495a65560Sthorpej 		if (tc_action_parser(ifname, &cp, &action[3]) == 0)
114595a65560Sthorpej 			return (0);
114695a65560Sthorpej 		if (next_word(&cp, w)) {
114795a65560Sthorpej 			if (EQUAL(w, "coloraware"))
114895a65560Sthorpej 				coloraware = 1;
114995a65560Sthorpej 			else if (EQUAL(w, "colorblind"))
115095a65560Sthorpej 				coloraware = 0;
115195a65560Sthorpej 		}
115295a65560Sthorpej 
115395a65560Sthorpej 		if (qcmd_cdnr_add_trtcm(action, ifname, NULL,
115495a65560Sthorpej 					&profile[0], &profile[1],
115595a65560Sthorpej 					&action[1], &action[2], &action[3],
115695a65560Sthorpej 					coloraware) != 0)
115795a65560Sthorpej 			return (0);
115895a65560Sthorpej 	} else if (EQUAL(type, "tswtcm")) {
115995a65560Sthorpej 		u_int32_t cmtd_rate, peak_rate, avg_interval;
116095a65560Sthorpej 
116195a65560Sthorpej 		if (!next_word(&cp, w)) {
116269881cf6Sitojun 			LOG(LOG_ERR, 0, "missing cmtd rate");
116395a65560Sthorpej 			return (0);
116495a65560Sthorpej 		}
116595a65560Sthorpej 		cmtd_rate = atobps(w);
116695a65560Sthorpej 
116795a65560Sthorpej 		if (!next_word(&cp, w)) {
116869881cf6Sitojun 			LOG(LOG_ERR, 0, "missing peak rate");
116995a65560Sthorpej 			return (0);
117095a65560Sthorpej 		}
117195a65560Sthorpej 		peak_rate = atobps(w);
117295a65560Sthorpej 
117395a65560Sthorpej 		if (!next_word(&cp, w)) {
117469881cf6Sitojun 			LOG(LOG_ERR, 0, "missing avg interval");
117595a65560Sthorpej 			return (0);
117695a65560Sthorpej 		}
117795a65560Sthorpej 		avg_interval = (u_int32_t)strtoul(w, NULL, 0);
117895a65560Sthorpej 
117995a65560Sthorpej 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
118095a65560Sthorpej 			return (0);
118195a65560Sthorpej 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
118295a65560Sthorpej 			return (0);
118395a65560Sthorpej 		if (tc_action_parser(ifname, &cp, &action[3]) == 0)
118495a65560Sthorpej 			return (0);
118595a65560Sthorpej 
118695a65560Sthorpej 		if (qcmd_cdnr_add_tswtcm(action, ifname, NULL,
118795a65560Sthorpej 					 cmtd_rate, peak_rate, avg_interval,
118895a65560Sthorpej 					 &action[1], &action[2], &action[3])
118995a65560Sthorpej 		    != 0)
119095a65560Sthorpej 			return (0);
119195a65560Sthorpej 	} else {
11927bd6fd35Swiz 		LOG(LOG_ERR, 0, "unknown action type %s");
119395a65560Sthorpej 		return (0);
119495a65560Sthorpej 	}
119595a65560Sthorpej 
119695a65560Sthorpej 	*end = '>';	/* restore the end delimiter */
119795a65560Sthorpej 
119895a65560Sthorpej 	return (1);
119995a65560Sthorpej }
1200