Home | History | Annotate | Line # | Download | only in libaltq
      1 /*	$NetBSD: parser.c,v 1.13 2024/01/15 19:44:07 andvar Exp $	*/
      2 /*	$KAME: parser.c,v 1.16 2002/02/20 10:40:39 kjc Exp $	*/
      3 /*
      4  * Copyright (C) 1999-2002
      5  *	Sony Computer Science Laboratories, Inc.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/param.h>
     30 #include <sys/socket.h>
     31 #include <net/if.h>
     32 #include <netinet/in.h>
     33 #include <arpa/inet.h>
     34 
     35 #include <stdio.h>
     36 #include <stdlib.h>
     37 #include <unistd.h>
     38 #include <stddef.h>
     39 #include <string.h>
     40 #include <ctype.h>
     41 #include <errno.h>
     42 #include <syslog.h>
     43 #include <netdb.h>
     44 #include <err.h>
     45 
     46 #include <altq/altq.h>
     47 #include <altq/altq_cdnr.h>
     48 #include <altq/altq_red.h>
     49 #include <altq/altq_rio.h>
     50 #include "altq_qop.h"
     51 #include "qop_cdnr.h"
     52 
     53 static int is_qdisc_name(const char *);
     54 static int qdisc_interface_parser(const char *, const char *, int, char **);
     55 static int qdisc_class_parser(const char *, const char *, const char *,
     56 			      const char *, int, char **);
     57 static int next_word(char **, char *);
     58 
     59 static int get_ifname(char **, char **);
     60 static int get_addr(char **, struct in_addr *, struct in_addr *);
     61 static int get_port(const char *, u_int16_t *);
     62 static int get_proto(const char *, int *);
     63 static int get_fltr_opts(char **, char *, size_t, int *);
     64 static int interface_parser(char *);
     65 static int class_parser(char *) ;
     66 static int filter_parser(char *);
     67 #ifdef INET6
     68 static int filter6_parser(char *);
     69 static int get_ip6addr(char **, struct in6_addr *, struct in6_addr *);
     70 #endif
     71 static int ctl_parser(char *);
     72 static int delete_parser(char *);
     73 static int red_parser(char *);
     74 static int rio_parser(char *);
     75 static int conditioner_parser(char *);
     76 static int tc_action_parser(char *, char **, struct tc_action *);
     77 
     78 #define MAX_LINE	1024
     79 #define MAX_WORD	128
     80 #define MAX_ARGS	64
     81 #define MAX_ACTIONS	16
     82 
     83 #ifndef MAX
     84 #define MAX(a,b) (((a)>(b))?(a):(b))
     85 #endif
     86 #ifndef MIN
     87 #define MIN(a,b) (((a)<(b))?(a):(b))
     88 #endif
     89 #define EQUAL(s1, s2)	(strcmp((s1), (s2)) == 0)
     90 
     91 int	line_no = 0;
     92 int	filter_dontwarn;
     93 
     94 static char	curifname[IFNAMSIZ];
     95 static struct if_nameindex *if_namelist = NULL;
     96 
     97 struct cmd_tab {
     98 	const char	*cmd;
     99 	int		(*parser)(char *);
    100 	const char	*help;
    101 } cmd_tab[] = {
    102 	{"?",		NULL,	"?"},
    103 	{"help",	NULL,	"help"},
    104 	{"quit",	NULL,	"quit"},
    105 	{"interface",	interface_parser,	"interface if_name [bandwidth bps] [cbq|hfsc]"},
    106 	{"class",	class_parser,	"class discipline if_name class_name [parent]"},
    107 	{"filter",	filter_parser,	"filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]"},
    108 	{"altq",	ctl_parser,	"altq if_name {enable|disable}"},
    109 	{"delete",	delete_parser,	"delete if_name class_name [filter_name]"},
    110 #ifdef INET6
    111 	{"filter6",	filter6_parser,	"filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]"},
    112 #endif
    113 	{"red",		red_parser,	"red th_min th_max inv_pmax"},
    114 	{"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"},
    115 	{"conditioner",	conditioner_parser,	"conditioner if_name cdnr_name <tc_action>"},
    116 	{"debug",	NULL,		"debug"},
    117 	{NULL,		NULL,		NULL}	/* termination */
    118 };
    119 
    120 /*
    121  * read one line from the specified stream. if it's a command,
    122  * execute the command.
    123  * returns 1 if OK, 0 if error or EOF.
    124  */
    125 int
    126 do_command(FILE *fp)
    127 {
    128 	char	cmd_line[MAX_LINE], cmd[MAX_WORD], *cp;
    129 	struct cmd_tab *tp;
    130 	int	len, rval;
    131 
    132 	/*
    133 	 * read a line from the stream and make it a null-terminated string
    134 	 */
    135 	cp = cmd_line;
    136 read_line:
    137 	if (fgets(cp, &cmd_line[MAX_LINE] - cp, fp) == NULL)
    138 		/* EOF or error */
    139 		return(0);
    140 	line_no++;
    141 
    142 	/* null-terminate the line */
    143 	if ((len = strlen(cmd_line)) > 0) {
    144 		cp = cmd_line + len - 1;
    145 		if (*cp == '\n') {
    146 			/* if escaped newline, read next line */
    147 			if (len > 1 &&  *(cp - 1) == '\\')
    148 				goto read_line;
    149 			*cp = '\0';
    150 		} else if (!feof(fp))
    151 			err(1, "LINE %d too long!", line_no);
    152 	}
    153 	/* trim comments */
    154 	if ((cp = strchr(cmd_line, '#')) != NULL)
    155 		*cp = '\0';
    156 
    157 	cp = cmd_line;
    158 	if ((len = next_word(&cp, cmd)) == 0)
    159 		/* no command in this line */
    160 		return (1);
    161 
    162 	/* fnind the corresponding parser */
    163 	rval = 0;
    164 	for (tp = cmd_tab; tp->cmd != NULL; tp++)
    165 		if (strncmp(cmd, tp->cmd, len) == 0)
    166 			break;
    167 
    168 	if (tp->cmd == NULL) {
    169 		if (fp == stdin) {
    170 			printf(" ?? %s\n", cmd);
    171 			rval = 1;
    172 		} else
    173 			LOG(LOG_ERR, 0, "unknown command: %s", cmd);
    174 		return (rval);
    175 	}
    176 
    177 	if (tp->parser != NULL)
    178 		rval = (*tp->parser)(cp);
    179 	else {
    180 		/* handle other commands */
    181 		if (strcmp(tp->cmd, "quit") == 0)
    182 			rval = 0;
    183 		else if (strcmp(tp->cmd, "help") == 0 ||
    184 			 strcmp(tp->cmd, "?") == 0) {
    185 			for (tp = cmd_tab; tp->cmd != NULL; tp++)
    186 				printf("%s\n", tp->help);
    187 			rval = 1;
    188 		} else if (strcmp(tp->cmd, "debug") == 0) {
    189 			if (m_debug & DEBUG_ALTQ) {
    190 				/* turn off verbose */
    191 				l_debug = LOG_INFO;
    192 				m_debug &= ~DEBUG_ALTQ;
    193 			} else {
    194 				/* turn on verbose */
    195 				l_debug = LOG_DEBUG;
    196 				m_debug |= DEBUG_ALTQ;
    197 			}
    198 			rval = 1;
    199 		}
    200 	}
    201 	return (rval);
    202 }
    203 
    204 static int
    205 is_qdisc_name(const char *qname)
    206 {
    207 	struct qdisc_parser *qp;
    208 
    209 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
    210 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
    211 			return (1);
    212 	return (0);
    213 }
    214 
    215 static int
    216 qdisc_interface_parser(const char * qname, const char *ifname,
    217 		       int argc, char **argv)
    218 {
    219 	struct qdisc_parser *qp;
    220 
    221 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
    222 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
    223 			return (*qp->interface_parser)(ifname, argc, argv);
    224 	return (0);
    225 }
    226 
    227 static int
    228 qdisc_class_parser(const char *qname, const char *ifname,
    229 		   const char *class_name, const char *parent_name,
    230 		   int argc, char **argv)
    231 {
    232 	struct qdisc_parser *qp;
    233 	struct ifinfo	*ifinfo;
    234 
    235 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
    236 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) {
    237 			if (qp->class_parser == NULL) {
    238 				LOG(LOG_ERR, 0,
    239 				    "class can't be specified for %s", qp->qname);
    240 				return (0);
    241 			}
    242 			if ((ifinfo = ifname2ifinfo(ifname)) == NULL) {
    243 				LOG(LOG_ERR, 0, "no such interface");
    244 				return (0);
    245 			}
    246 			if (strncmp(ifinfo->qdisc->qname, qname,
    247 				    strlen(ifinfo->qdisc->qname)) != 0) {
    248 				LOG(LOG_ERR, 0,
    249 				    "qname doesn't match the interface");
    250 				return (0);
    251 			}
    252 			return (*qp->class_parser)(ifname, class_name,
    253 						   parent_name, argc, argv);
    254 		}
    255 	return (0);
    256 }
    257 
    258 /*
    259  * read the config file
    260  */
    261 int
    262 qcmd_config(void)
    263 {
    264 	FILE	*fp;
    265 	int	rval;
    266 
    267 	if (if_namelist != NULL)
    268 		if_freenameindex(if_namelist);
    269 	if_namelist = if_nameindex();
    270 	curifname[0] = '\0';
    271 
    272 	LOG(LOG_INFO, 0, "ALTQ config file is %s", altqconfigfile);
    273 
    274 	fp = fopen(altqconfigfile, "r");
    275 	if (fp == NULL) {
    276 		LOG(LOG_ERR, errno, "can't open %s", altqconfigfile, 0);
    277 		return (QOPERR_INVAL);
    278 	}
    279 	line_no = 0;
    280 	rval = 1;
    281 	while (rval)
    282 		rval = do_command(fp);
    283 
    284 	if (!feof(fp)) {
    285 		LOG(LOG_ERR, 0, "Error in %s, line %d.  config failed.",
    286 		    altqconfigfile, line_no);
    287 		(void) qcmd_destroyall();
    288 		rval = QOPERR_INVAL;
    289 	} else
    290 		rval = 0;
    291 
    292 	(void)fclose(fp);
    293 	line_no = 0;
    294 	return (rval);
    295 }
    296 
    297 static int
    298 next_word(char **cpp, char *b)
    299 {
    300 	char	*cp;
    301 	int	i;
    302 
    303 	cp = *cpp;
    304 	while (*cp == ' ' || *cp == '\t')
    305 		cp++;
    306 	for (i = 0; i < MAX_WORD - 1; i++) {
    307 		if (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\0')
    308 			break;
    309 		*b++ = *cp++;
    310 	}
    311 	*b = '\0';
    312 	*cpp = cp;
    313 	return (i);
    314 }
    315 
    316 char *
    317 cur_ifname(void)
    318 {
    319 	return (curifname);
    320 }
    321 
    322 u_int
    323 get_ifindex(const char *ifname)
    324 {
    325 	struct if_nameindex *ifnp;
    326 
    327 	for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
    328 		if (strcmp(ifname, ifnp->if_name) == 0)
    329 			return (ifnp->if_index);
    330 	return (0);
    331 }
    332 
    333 static int
    334 get_ifname(char **cpp, char **ifnamep)
    335 {
    336 	char w[MAX_WORD], *ocp;
    337 	struct if_nameindex *ifnp;
    338 
    339 	ocp = *cpp;
    340 	if (next_word(&ocp, w) && if_namelist != NULL)
    341 		for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
    342 			if (strcmp(w, ifnp->if_name) == 0) {
    343 				/* if_name found. advance the word pointer */
    344 				*cpp = ocp;
    345 				strlcpy(curifname, w, sizeof(curifname));
    346 				*ifnamep = curifname;
    347 				return (1);
    348 			}
    349 
    350 	/* this is not interface name. use one in the context. */
    351 	if (curifname[0] == '\0')
    352 		return (0);
    353 	*ifnamep = curifname;
    354 	return (1);
    355 }
    356 
    357 /* set address and netmask in network byte order */
    358 static int
    359 get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask)
    360 {
    361 	char w[MAX_WORD], *ocp;
    362 	struct in_addr tmp;
    363 
    364 	addr->s_addr = 0;
    365 	mask->s_addr = 0xffffffff;
    366 
    367 	if (!next_word(cpp, w))
    368 		return (0);
    369 
    370 	if (inet_aton((char *)w, &tmp) != 1) {
    371 		/* try gethostbyname */
    372 		struct hostent *h;
    373 
    374 		if ((h = gethostbyname(w)) == NULL ||
    375 		    h->h_addrtype != AF_INET || h->h_length != 4)
    376 			return (0);
    377 		bcopy(h->h_addr, &tmp, (size_t)h->h_length);
    378 	}
    379 	addr->s_addr = tmp.s_addr;
    380 
    381 	/* check if netmask option is present */
    382 	ocp = *cpp;
    383 	if (next_word(&ocp, w) && EQUAL(w, "netmask")) {
    384 		if (!next_word(&ocp, w))
    385 			return (0);
    386 		if (inet_aton((char *)w, (struct in_addr *)&tmp) != 1)
    387 			return (0);
    388 
    389 		mask->s_addr = tmp.s_addr;
    390 		*cpp = ocp;
    391 		return (1);
    392 	}
    393 	/* no netmask option */
    394 	return (1);
    395 }
    396 
    397 /* returns service number in network byte order */
    398 static int
    399 get_port(const char *name, u_int16_t *port_no)
    400 {
    401 	struct servent *s;
    402 	u_int16_t num;
    403 
    404 	if (isdigit((unsigned char)name[0])) {
    405 		num = (u_int16_t)strtol(name, NULL, 0);
    406 		*port_no = htons(num);
    407 		return (1);
    408 	}
    409 
    410 	if ((s = getservbyname(name, 0)) == NULL)
    411 		return (0);
    412 
    413 	*port_no = (u_int16_t)s->s_port;
    414 	return (1);
    415 }
    416 
    417 static int
    418 get_proto(const char *name, int *proto_no)
    419 {
    420 	struct protoent *p;
    421 
    422 	if (isdigit((unsigned char)name[0])) {
    423 		*proto_no = (int)strtol(name, NULL, 0);
    424 		return (1);
    425 	}
    426 
    427 	if ((p = getprotobyname(name)) == NULL)
    428 		return (0);
    429 
    430 	*proto_no = p->p_proto;
    431 	return (1);
    432 }
    433 
    434 static int
    435 get_fltr_opts(char **cpp, char *fltr_name, size_t len, int *ruleno)
    436 {
    437 	char w[MAX_WORD], *ocp;
    438 
    439 	ocp = *cpp;
    440 	while (next_word(&ocp, w)) {
    441 		if (EQUAL(w, "name")) {
    442 			if (!next_word(&ocp, w))
    443 				return (0);
    444 			strlcpy(fltr_name, w, len);
    445 			*cpp = ocp;
    446 		} else if (EQUAL(w, "ruleno")) {
    447 			if (!next_word(&ocp, w))
    448 				return (0);
    449 			*ruleno = (int)strtol(w, NULL, 0);
    450 			*cpp = ocp;
    451 		} else
    452 			break;
    453 	}
    454 	return (1);
    455 }
    456 
    457 
    458 #define	DISCIPLINE_NONE		0
    459 
    460 static int
    461 interface_parser(char *cmdbuf)
    462 {
    463 	char	w[MAX_WORD], *ap, *cp = cmdbuf;
    464 	char	*ifname, *argv[MAX_ARGS], qdisc_name[MAX_WORD];
    465 	int     argc;
    466 
    467 	if (!get_ifname(&cp, &ifname)) {
    468 		LOG(LOG_ERR, 0, "missing interface name");
    469 		return (0);
    470 	}
    471 
    472 	/* create argument list & look for scheduling discipline options. */
    473 	snprintf(qdisc_name, sizeof qdisc_name, "null");
    474 	argc = 0;
    475 	ap = w;
    476 	while (next_word(&cp, ap)) {
    477 		if (is_qdisc_name(ap))
    478 			strlcpy(qdisc_name, ap, sizeof qdisc_name);
    479 
    480 		argv[argc] = ap;
    481 		ap += strlen(ap) + 1;
    482 		argc++;
    483 		if (argc >= MAX_ARGS) {
    484 			LOG(LOG_ERR, 0, "too many args");
    485 			return (0);
    486 		}
    487 	}
    488 
    489 	return qdisc_interface_parser(qdisc_name, ifname, argc, argv);
    490 }
    491 
    492 
    493 static int
    494 class_parser(char *cmdbuf)
    495 {
    496 	char	w[MAX_WORD], *cp = cmdbuf;
    497 	char 	*ifname, qdisc_name[MAX_WORD];
    498 	char	class_name[MAX_WORD], parent_name[MAX_WORD];
    499 	char	*clname = class_name;
    500 	char	*parent = NULL;
    501 	char	*argv[MAX_ARGS], *ap;
    502 	int	argc;
    503 
    504 	/* get scheduling class */
    505 	if (!next_word(&cp, qdisc_name)) {
    506 		LOG(LOG_ERR, 0, "missing discipline");
    507 		return (0);
    508 	}
    509 	if (!is_qdisc_name(qdisc_name)) {
    510 		LOG(LOG_ERR, 0, "unknown discipline '%s'", qdisc_name);
    511 		return (0);
    512 	}
    513 
    514 	/* get interface name */
    515 	if (!get_ifname(&cp, &ifname)) {
    516 		LOG(LOG_ERR, 0, "missing interface name");
    517 		return (0);
    518 	}
    519 
    520 	/* get class name */
    521 	if (!next_word(&cp, class_name)) {
    522 		LOG(LOG_ERR, 0, "missing class name");
    523 		return (0);
    524 	}
    525 
    526 	/* get parent name */
    527 	if (!next_word(&cp, parent_name)) {
    528 		LOG(LOG_ERR, 0, "missing parent class");
    529 		return (0);
    530 	}
    531 	if (!EQUAL(parent_name, "null") && !EQUAL(parent_name, "NULL"))
    532 		parent = parent_name;
    533 	else
    534 		parent = NULL;
    535 
    536 	ap = w;
    537 	argc = 0;
    538 	while (next_word(&cp, ap)) {
    539 		argv[argc] = ap;
    540 		ap += strlen(ap) + 1;
    541 		argc++;
    542 		if (argc >= MAX_ARGS) {
    543 			LOG(LOG_ERR, 0, "too many args");
    544 			return (0);
    545 		}
    546 	}
    547 
    548 	return qdisc_class_parser(qdisc_name, ifname, clname, parent,
    549 				  argc, argv);
    550 }
    551 
    552 static int
    553 filter_parser(char *cmdbuf)
    554 {
    555 	char 	w[MAX_WORD], *cp = cmdbuf;
    556 	char 	*ifname, class_name[MAX_WORD], fltr_name[MAX_WORD];
    557 	char	*flname = NULL;
    558 	struct flow_filter	sfilt;
    559 	int	protocol;
    560 	u_char	tos, tosmask;
    561 	int	ruleno;
    562 	int	dontwarn = 0;
    563 	int	error;
    564 
    565 	memset(&sfilt, 0, sizeof(sfilt));
    566 	sfilt.ff_flow.fi_family = AF_INET;
    567 
    568 	if (!get_ifname(&cp, &ifname)) {
    569 		LOG(LOG_ERR, 0, "missing interface name in filter command");
    570 		return (0);
    571 	}
    572 
    573 	if (!next_word(&cp, class_name)) {
    574 		LOG(LOG_ERR, 0, "missing class name in filter command");
    575 		return (0);
    576 	}
    577 
    578 	fltr_name[0] = '\0';
    579 	ruleno = 0;
    580 	if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) {
    581 		LOG(LOG_ERR, 0, "bad filter option");
    582 		return (0);
    583 	}
    584 	if (fltr_name[0] != '\0')
    585 		flname = fltr_name;
    586 	sfilt.ff_ruleno = ruleno;
    587 
    588 	/* get filter destination Address */
    589 	if (!get_addr(&cp, &sfilt.ff_flow.fi_dst, &sfilt.ff_mask.mask_dst)) {
    590 		LOG(LOG_ERR, 0, "bad filter destination address");
    591 		return (0);
    592 	}
    593 
    594 	/* get filter destination port */
    595 	if (!next_word(&cp, w)) {
    596 		LOG(LOG_ERR, 0, "missing filter destination port");
    597 		return (0);
    598 	}
    599 	if (!get_port(w, &sfilt.ff_flow.fi_dport)) {
    600 		LOG(LOG_ERR, 0, "bad filter destination port");
    601 		return (0);
    602 	}
    603 
    604 	/* get filter source address */
    605 	if (!get_addr(&cp, &sfilt.ff_flow.fi_src, &sfilt.ff_mask.mask_src)) {
    606 		LOG(LOG_ERR, 0, "bad filter source address");
    607 		return (0);
    608 	}
    609 
    610 	/* get filter source port */
    611 	if (!next_word(&cp, w)) {
    612 		LOG(LOG_ERR, 0, "missing filter source port");
    613 		return (0);
    614 	}
    615 	if (!get_port(w, &sfilt.ff_flow.fi_sport)) {
    616 		LOG(LOG_ERR, 0, "bad filter source port");
    617 		return (0);
    618 	}
    619 
    620 	/* get filter protocol id */
    621 	if (!next_word(&cp, w)) {
    622 		LOG(LOG_ERR, 0, "missing filter protocol");
    623 		return (0);
    624 	}
    625 	if (!get_proto(w, &protocol)) {
    626 		LOG(LOG_ERR, 0, "bad protocol");
    627 		return (0);
    628 	}
    629 	sfilt.ff_flow.fi_proto = protocol;
    630 
    631 	while (next_word(&cp, w)) {
    632 		if (EQUAL(w, "tos")) {
    633 			tos = 0;
    634 			tosmask = 0xff;
    635 
    636 			if (next_word(&cp, w)) {
    637 				tos = (u_char)strtol(w, NULL, 0);
    638 				if (next_word(&cp, w)) {
    639 					if (EQUAL(w, "tosmask")) {
    640 						next_word(&cp, w);
    641 						tosmask = (u_char)strtol(w, NULL, 0);
    642 					}
    643 				}
    644 			}
    645 			sfilt.ff_flow.fi_tos = tos;
    646 			sfilt.ff_mask.mask_tos = tosmask;
    647 		} else if (EQUAL(w, "gpi")) {
    648 			if (next_word(&cp, w)) {
    649 				sfilt.ff_flow.fi_gpi =
    650 					(u_int32_t)strtoul(w, NULL, 0);
    651 				sfilt.ff_flow.fi_gpi =
    652 					htonl(sfilt.ff_flow.fi_gpi);
    653 			}
    654 		} else if (EQUAL(w, "dontwarn"))
    655 			dontwarn = 1;
    656 	}
    657 
    658 	/*
    659 	 * Add the filter.
    660 	 */
    661 	filter_dontwarn = dontwarn;	/* XXX */
    662 	error = qcmd_add_filter(ifname, class_name, flname, &sfilt);
    663 	filter_dontwarn = 0;		/* XXX */
    664 	if (error) {
    665 		LOG(LOG_ERR, 0,
    666 		    "can't add filter to class '%s' on interface '%s'",
    667 		    class_name, ifname);
    668 		return (0);
    669 	}
    670 	return (1);
    671 }
    672 
    673 #ifdef INET6
    674 static int
    675 filter6_parser(char *cmdbuf)
    676 {
    677 	char 	w[MAX_WORD], *cp = cmdbuf;
    678 	char 	*ifname, class_name[MAX_WORD], fltr_name[MAX_WORD];
    679 	char	*flname = NULL;
    680 	struct flow_filter6	sfilt;
    681 	int	protocol;
    682 	u_char	tclass, tclassmask;
    683 	int	ruleno;
    684 	int	dontwarn = 0;
    685 	int	ret;
    686 
    687 	memset(&sfilt, 0, sizeof(sfilt));
    688 	sfilt.ff_flow6.fi6_family = AF_INET6;
    689 
    690 	if (!get_ifname(&cp, &ifname)) {
    691 		LOG(LOG_ERR, 0, "missing interface name");
    692 		return (0);
    693 	}
    694 
    695 	if (!next_word(&cp, class_name)) {
    696 		LOG(LOG_ERR, 0, "missing class name");
    697 		return (0);
    698 	}
    699 
    700 	fltr_name[0] = '\0';
    701 	ruleno = 0;
    702 	if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) {
    703 		LOG(LOG_ERR, 0, "bad filter option");
    704 		return (0);
    705 	}
    706 	if (fltr_name[0] != '\0')
    707 		flname = fltr_name;
    708 	sfilt.ff_ruleno = ruleno;
    709 
    710 	/* get filter destination address */
    711 	if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_dst,
    712 			 &sfilt.ff_mask6.mask6_dst)) {
    713 		LOG(LOG_ERR, 0, "bad destination address");
    714 		return (0);
    715 	}
    716 
    717 	/* get filter destination port */
    718 	if (!next_word(&cp, w)) {
    719 		LOG(LOG_ERR, 0, "missing filter destination port");
    720 		return (0);
    721 	}
    722 	if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) {
    723 		LOG(LOG_ERR, 0, "bad filter destination port");
    724 		return (0);
    725 	}
    726 
    727 	/* get filter source address */
    728 	if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_src,
    729 			 &sfilt.ff_mask6.mask6_src)) {
    730 		LOG(LOG_ERR, 0, "bad source address");
    731 		return (0);
    732 	}
    733 
    734 	/* get filter source port */
    735 	if (!next_word(&cp, w)) {
    736 		LOG(LOG_ERR, 0, "missing filter source port");
    737 		return (0);
    738 	}
    739 	if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) {
    740 		LOG(LOG_ERR, 0, "bad filter source port");
    741 		return (0);
    742 	}
    743 
    744 	/* get filter protocol id */
    745 	if (!next_word(&cp, w)) {
    746 		LOG(LOG_ERR, 0, "missing filter protocol");
    747 		return (0);
    748 	}
    749 	if (!get_proto(w, &protocol)) {
    750 		LOG(LOG_ERR, 0, "bad protocol");
    751 		return (0);
    752 	}
    753 	sfilt.ff_flow6.fi6_proto = protocol;
    754 
    755 	while (next_word(&cp, w)) {
    756 		if (EQUAL(w, "tclass")) {
    757 			tclass = 0;
    758 			tclassmask = 0xff;
    759 
    760 			if (next_word(&cp, w)) {
    761 				tclass = (u_char)strtol(w, NULL, 0);
    762 				if (next_word(&cp, w)) {
    763 					if (EQUAL(w, "tclassmask")) {
    764 						next_word(&cp, w);
    765 						tclassmask =
    766 						    (u_char)strtol(w, NULL, 0);
    767 					}
    768 				}
    769 			}
    770 			sfilt.ff_flow6.fi6_tclass = tclass;
    771 			sfilt.ff_mask6.mask6_tclass = tclassmask;
    772 		} else if (EQUAL(w, "gpi")) {
    773 			if (next_word(&cp, w)) {
    774 				sfilt.ff_flow6.fi6_gpi =
    775 					(u_int32_t)strtoul(w, NULL, 0);
    776 				sfilt.ff_flow6.fi6_gpi =
    777 					htonl(sfilt.ff_flow6.fi6_gpi);
    778 			}
    779 		} else if (EQUAL(w, "flowlabel")) {
    780 			if (next_word(&cp, w)) {
    781 				sfilt.ff_flow6.fi6_flowlabel =
    782 				   (u_int32_t)strtoul(w, NULL, 0) & 0x000fffff;
    783 				sfilt.ff_flow6.fi6_flowlabel =
    784 					htonl(sfilt.ff_flow6.fi6_flowlabel);
    785 			}
    786 		} else if (EQUAL(w, "dontwarn"))
    787 			dontwarn = 1;
    788 	}
    789 
    790 	/*
    791 	 * Add the filter.
    792 	 */
    793 	filter_dontwarn = dontwarn;	/* XXX */
    794 	ret = qcmd_add_filter(ifname, class_name, flname,
    795 			      (struct flow_filter *)&sfilt);
    796 	filter_dontwarn = 0;		/* XXX */
    797 	if (ret) {
    798 		LOG(LOG_ERR, 0,
    799 		    "can't add filter to class '%s' on interface '%s'",
    800 		    class_name, ifname);
    801 		return (0);
    802 	}
    803 
    804 	return (1);
    805 }
    806 
    807 static int
    808 get_ip6addr(char **cpp, struct in6_addr *addr, struct in6_addr *mask)
    809 {
    810 	char w[MAX_WORD], *prefix;
    811 	u_char *cp;
    812 	int len;
    813 
    814 	*addr = in6addr_any;  /* set all 0 */
    815 	*mask = in6addr_any;  /* set all 0 */
    816 
    817 	if (!next_word(cpp, w))
    818 		return (0);
    819 
    820 	if (EQUAL(w, "0"))
    821 		/* abbreviation of a wildcard (::0) */
    822 		return (1);
    823 
    824 	if ((prefix = strchr(w, '/')) != NULL) {
    825 		/* address has prefix length */
    826 		*prefix++ = '\0';
    827 	}
    828 
    829 	if (inet_pton(AF_INET6, w, addr) != 1)
    830 		return (0);
    831 
    832 	if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefix == NULL)
    833 		/* wildcard */
    834 		return (1);
    835 
    836 	/* convert address prefix length to address mask */
    837 	if (prefix != NULL) {
    838 		len = (int)strtol(prefix, NULL, 0);
    839 		if ((len < 0) || (len > 128))
    840 			return (0);
    841 		for (cp = (u_char *)mask; len > 7; len -= 8)
    842 			*cp++ = 0xff;
    843 		if (len > 0)
    844 			*cp = (0xff << (8 - len)) & 0xff;
    845 
    846 		IN6ADDR32_SET(addr, 0, IN6ADDR32_GET(mask, 0) &
    847 		    IN6ADDR32_GET(addr, 0));
    848 		IN6ADDR32_SET(addr, 1, IN6ADDR32_GET(mask, 1) &
    849 		    IN6ADDR32_GET(addr, 1));
    850 		IN6ADDR32_SET(addr, 2, IN6ADDR32_GET(mask, 2) &
    851 		    IN6ADDR32_GET(addr, 2));
    852 		IN6ADDR32_SET(addr, 3, IN6ADDR32_GET(mask, 3) &
    853 		    IN6ADDR32_GET(addr, 3));
    854 	} else
    855 		/* full mask */
    856 		memset(mask, 0xff, sizeof(struct in6_addr));
    857 
    858 	return (1);
    859 }
    860 
    861 #endif /* INET6 */
    862 
    863 static int
    864 ctl_parser(char *cmdbuf)
    865 {
    866 	char	w[MAX_WORD], *cp = cmdbuf;
    867 	char	*ifname;
    868 	int	state;
    869 	int	rval;
    870 
    871 	if (!get_ifname(&cp, &ifname)) {
    872 		printf("missing interface name in %s, line %d",
    873 		       altqconfigfile, line_no);
    874 		return (0);
    875 	}
    876 
    877 	if (!next_word(&cp, w)) {
    878 		state = is_q_enabled(ifname);
    879 		printf("altq %s on %s\n",
    880 		       state ? "enabled" : "disabled", ifname);
    881 		return (1);
    882 	}
    883 
    884 	if (EQUAL(w, "enable")) {
    885 		rval = qcmd_enable(ifname);
    886 		printf("altq %s on %s\n",
    887 		       (rval == 0) ? "enabled" : "enable failed!", ifname);
    888 	} else if (EQUAL(w, "disable")) {
    889 		rval = qcmd_disable(ifname);
    890 		printf("altq %s on %s\n",
    891 		       (rval == 0) ? "disabled" : "disable failed!", ifname);
    892 	} else if (EQUAL(w, "reload")) {
    893 		printf("reinitializing altq...\n");
    894 		qcmd_destroyall();
    895 		qcmd_init();
    896 	} else
    897 		return (0);
    898 	return (1);
    899 }
    900 
    901 static int
    902 delete_parser(char *cmdbuf)
    903 {
    904 	char	*cp = cmdbuf;
    905 	char	*ifname, class_name[MAX_WORD], filter_name[MAX_WORD];
    906 	int	ret;
    907 
    908 	if (!get_ifname(&cp, &ifname)) {
    909 		LOG(LOG_ERR, 0, "missing interface name");
    910 		return (0);
    911 	}
    912 
    913 	if (!next_word(&cp, class_name)) {
    914 		LOG(LOG_ERR, 0, "missing class name");
    915 		return (0);
    916 	}
    917 
    918 	/* check if filter is specified */
    919 	if (next_word(&cp, filter_name)) {
    920 		ret = qcmd_delete_filter(ifname, class_name, filter_name);
    921 		if (ret) {
    922 			LOG(LOG_ERR, 0,
    923 			    "can't delete filter '%s' on interface '%s'",
    924 			    filter_name, ifname);
    925 			return (0);
    926 		}
    927 		return (1);
    928 	}
    929 
    930 	ret = qcmd_delete_class(ifname, class_name);
    931 	if (ret) {
    932 		LOG(LOG_ERR, 0,
    933 		    "can't delete class '%s' on interface '%s'",
    934 		    class_name, ifname);
    935 		return (0);
    936 	}
    937 
    938 	return (1);
    939 }
    940 
    941 static int
    942 red_parser(char *cmdbuf)
    943 {
    944 	char	w[MAX_WORD], *cp = cmdbuf;
    945 	int th_min, th_max, inv_pmax;
    946 
    947 	if (!next_word(&cp, w))
    948 		goto bad;
    949 	th_min = (int)strtol(w, NULL, 0);
    950 
    951 	if (!next_word(&cp, w))
    952 		goto bad;
    953 	th_max = (int)strtol(w, NULL, 0);
    954 
    955 	if (!next_word(&cp, w))
    956 		goto bad;
    957 	inv_pmax = (int)strtol(w, NULL, 0);
    958 
    959 	if (qop_red_set_defaults(th_min, th_max, inv_pmax) != 0) {
    960 		LOG(LOG_ERR, 0, "can't set red default parameters");
    961 		return (0);
    962 	}
    963 
    964 	return (1);
    965 
    966  bad:
    967 	LOG(LOG_ERR, 0, "bad red parameter");
    968 	return (0);
    969 }
    970 
    971 static int
    972 rio_parser(char *cmdbuf)
    973 {
    974 	char	w[MAX_WORD], *cp = cmdbuf;
    975 	int	i;
    976 	struct redparams params[RIO_NDROPPREC];
    977 
    978 	for (i = 0; i < RIO_NDROPPREC; i++) {
    979 		if (!next_word(&cp, w))
    980 			goto bad;
    981 		params[i].th_min = (int)strtol(w, NULL, 0);
    982 
    983 		if (!next_word(&cp, w))
    984 			goto bad;
    985 		params[i].th_max = (int)strtol(w, NULL, 0);
    986 
    987 		if (!next_word(&cp, w))
    988 			goto bad;
    989 		params[i].inv_pmax = (int)strtol(w, NULL, 0);
    990 	}
    991 
    992 	if (qop_rio_set_defaults(&params[0]) != 0) {
    993 		LOG(LOG_ERR, 0, "can't set rio default parameters");
    994 		return (0);
    995 	}
    996 
    997 	return (1);
    998 
    999  bad:
   1000 	LOG(LOG_ERR, 0, "bad rio parameter");
   1001 	return (0);
   1002 }
   1003 
   1004 static int
   1005 conditioner_parser(char *cmdbuf)
   1006 {
   1007 	char	cdnr_name[MAX_WORD], *cp = cmdbuf;
   1008 	char	*ifname;
   1009 	struct tc_action action[MAX_ACTIONS];
   1010 
   1011 	if (!get_ifname(&cp, &ifname)) {
   1012 		LOG(LOG_ERR, 0, "missing interface name");
   1013 		return (0);
   1014 	}
   1015 
   1016 	/* get conditioner name */
   1017 	if (!next_word(&cp, cdnr_name)) {
   1018 		LOG(LOG_ERR, 0, "missing cdnr name");
   1019 		return (0);
   1020 	}
   1021 
   1022 	if (tc_action_parser(ifname, &cp, &action[0]) == 0)
   1023 		return (0);
   1024 
   1025 	if (qcmd_cdnr_add_element(NULL, ifname, cdnr_name, &action[0]) != 0)
   1026 		return (0);
   1027 	return (1);
   1028 }
   1029 
   1030 /*
   1031  * recursively parse '<'tc_action'>'
   1032  * note that array "action" grows during recursive parse.
   1033  */
   1034 static int
   1035 tc_action_parser(char *ifname, char **cpp, struct tc_action *action)
   1036 {
   1037 	char	*cp, *start, *end;
   1038 	char	type[MAX_WORD], w[MAX_WORD];
   1039 	int	depth, i;
   1040 	struct tb_profile profile[2];
   1041 
   1042 	/*
   1043 	 * find a possibly nested pair of '<' and '>',
   1044 	 * make them pointed by 'start' and 'end'.
   1045 	 */
   1046 	start = strchr(*cpp, '<');
   1047 	if (start == NULL) {
   1048 		LOG(LOG_ERR, 0, "conditioner action missing");
   1049 		return (0);
   1050 	}
   1051 	depth = 1;
   1052 	cp = start + 1;
   1053 	do {
   1054 		end = strpbrk(cp, "<>");
   1055 		if (end == NULL) {
   1056 			LOG(LOG_ERR, 0,
   1057 			    "conditioner action delimiter mismatch");
   1058 			return (0);
   1059 		}
   1060 		if (*end == '<')
   1061 			depth++;
   1062 		else if (*end == '>')
   1063 			depth--;
   1064 		cp = end + 1;
   1065 	} while (depth > 0);
   1066 	*end = '\0';
   1067 	*cpp = end + 1;
   1068 	cp = start + 1;
   1069 
   1070 	if (IsDebug(DEBUG_ALTQ)) {
   1071 		printf("tc_action_parser: [%s]\n", cp);
   1072 	}
   1073 
   1074 	if (!next_word(&cp, type)) {
   1075 		LOG(LOG_ERR, 0, "missing conditioner action type");
   1076 		return (0);
   1077 	}
   1078 
   1079 	/*
   1080 	 * action type specific process
   1081 	 */
   1082 	if (EQUAL(type, "conditioner")) {
   1083 		if (!next_word(&cp, w)) {
   1084 			LOG(LOG_ERR, 0,
   1085 			    "missing conditioner name");
   1086 			return (0);
   1087 		}
   1088 		action->tca_code = TCACODE_HANDLE;
   1089 		action->tca_handle = cdnr_name2handle(ifname, w);
   1090 		if (action->tca_handle == CDNR_NULL_HANDLE) {
   1091 			LOG(LOG_ERR, 0,
   1092 			    "wrong conditioner name %s", w);
   1093 			return (0);
   1094 		}
   1095 	} else if (EQUAL(type, "pass")) {
   1096 		action->tca_code = TCACODE_PASS;
   1097 	} else if (EQUAL(type, "drop")) {
   1098 		action->tca_code = TCACODE_DROP;
   1099 	} else if (EQUAL(type, "mark")) {
   1100 		if (!next_word(&cp, w)) {
   1101 			LOG(LOG_ERR, 0, "missing dscp");
   1102 			return (0);
   1103 		}
   1104 		action->tca_code = TCACODE_MARK;
   1105 		action->tca_dscp = (u_int8_t)strtol(w, NULL, 0);
   1106 	} else if (EQUAL(type, "tbmeter")) {
   1107 		if (!next_word(&cp, w)) {
   1108 			LOG(LOG_ERR, 0, "missing tb profile");
   1109 			return (0);
   1110 		}
   1111 		profile[0].rate = atobps(w);
   1112 		if (!next_word(&cp, w)) {
   1113 			LOG(LOG_ERR, 0, "missing tb profile");
   1114 			return (0);
   1115 		}
   1116 		profile[0].depth = atobytes(w);
   1117 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
   1118 			return (0);
   1119 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
   1120 			return (0);
   1121 
   1122 		if (qcmd_cdnr_add_tbmeter(action, ifname, NULL, &profile[0],
   1123 					  &action[1], &action[2]) != 0)
   1124 			return (0);
   1125 	} else if (EQUAL(type, "trtcm")) {
   1126 		int coloraware = 0;	/* default is color-blind */
   1127 
   1128 		for (i=0; i<2; i++) {
   1129 			if (!next_word(&cp, w)) {
   1130 				LOG(LOG_ERR, 0, "missing tb profile");
   1131 				return (0);
   1132 			}
   1133 			profile[i].rate = atobps(w);
   1134 			if (!next_word(&cp, w)) {
   1135 				LOG(LOG_ERR, 0, "missing tb profile");
   1136 				return (0);
   1137 			}
   1138 			profile[i].depth = atobytes(w);
   1139 		}
   1140 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
   1141 			return (0);
   1142 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
   1143 			return (0);
   1144 		if (tc_action_parser(ifname, &cp, &action[3]) == 0)
   1145 			return (0);
   1146 		if (next_word(&cp, w)) {
   1147 			if (EQUAL(w, "coloraware"))
   1148 				coloraware = 1;
   1149 			else if (EQUAL(w, "colorblind"))
   1150 				coloraware = 0;
   1151 		}
   1152 
   1153 		if (qcmd_cdnr_add_trtcm(action, ifname, NULL,
   1154 					&profile[0], &profile[1],
   1155 					&action[1], &action[2], &action[3],
   1156 					coloraware) != 0)
   1157 			return (0);
   1158 	} else if (EQUAL(type, "tswtcm")) {
   1159 		u_int32_t cmtd_rate, peak_rate, avg_interval;
   1160 
   1161 		if (!next_word(&cp, w)) {
   1162 			LOG(LOG_ERR, 0, "missing cmtd rate");
   1163 			return (0);
   1164 		}
   1165 		cmtd_rate = atobps(w);
   1166 
   1167 		if (!next_word(&cp, w)) {
   1168 			LOG(LOG_ERR, 0, "missing peak rate");
   1169 			return (0);
   1170 		}
   1171 		peak_rate = atobps(w);
   1172 
   1173 		if (!next_word(&cp, w)) {
   1174 			LOG(LOG_ERR, 0, "missing avg interval");
   1175 			return (0);
   1176 		}
   1177 		avg_interval = (u_int32_t)strtoul(w, NULL, 0);
   1178 
   1179 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
   1180 			return (0);
   1181 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
   1182 			return (0);
   1183 		if (tc_action_parser(ifname, &cp, &action[3]) == 0)
   1184 			return (0);
   1185 
   1186 		if (qcmd_cdnr_add_tswtcm(action, ifname, NULL,
   1187 					 cmtd_rate, peak_rate, avg_interval,
   1188 					 &action[1], &action[2], &action[3])
   1189 		    != 0)
   1190 			return (0);
   1191 	} else {
   1192 		LOG(LOG_ERR, 0, "unknown action type %s");
   1193 		return (0);
   1194 	}
   1195 
   1196 	*end = '>';	/* restore the end delimiter */
   1197 
   1198 	return (1);
   1199 }
   1200