Home | History | Annotate | Line # | Download | only in libaltq
parser.c revision 1.4
      1 /*	$KAME: parser.c,v 1.10 2001/08/15 12:51:57 kjc Exp $	*/
      2 /*******************************************************************
      3 
      4   Copyright (c) 1996 by the University of Southern California
      5   All rights reserved.
      6 
      7   Permission to use, copy, modify, and distribute this software and its
      8   documentation in source and binary forms for any purpose and without
      9   fee is hereby granted, provided that both the above copyright notice
     10   and this permission notice appear in all copies. and that any
     11   documentation, advertising materials, and other materials related to
     12   such distribution and use acknowledge that the software was developed
     13   in part by the University of Southern California, Information
     14   Sciences Institute.  The name of the University may not be used to
     15   endorse or promote products derived from this software without
     16   specific prior written permission.
     17 
     18   THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
     19   the suitability of this software for any purpose.  THIS SOFTWARE IS
     20   PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
     21   INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
     22   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
     23 
     24   Other copyrights might apply to parts of this software and are so
     25   noted when applicable.
     26 
     27 ********************************************************************/
     28 
     29 
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <unistd.h>
     33 #include <stddef.h>
     34 #include <string.h>
     35 #include <ctype.h>
     36 #include <errno.h>
     37 #include <syslog.h>
     38 #include <sys/socket.h>
     39 #include <netdb.h>
     40 #include <net/if.h>
     41 #include <netinet/in.h>
     42 #include <arpa/inet.h>
     43 
     44 #include <altq/altq.h>
     45 #include <altq/altq_cdnr.h>
     46 #include <altq/altq_red.h>
     47 #include <altq/altq_rio.h>
     48 #include "altq_qop.h"
     49 #include "qop_cdnr.h"
     50 
     51 #define show_help(op)  printf(cmd_tab[op].cmd_help)
     52 
     53 /*
     54  * Forward & External Declarations
     55  */
     56 static int is_qdisc_name(const char *);
     57 static int qdisc_interface_parser(const char *, const char *, int, char **);
     58 static int qdisc_class_parser(const char *, const char *, const char *,
     59 	const char *, int, char **);
     60 
     61 static int pfxcmp(const char *, const char *);
     62 static int next_word(char **, char *);
     63 
     64 static int do_cmd(int, char *);
     65 static int get_ifname(char **, char **);
     66 static int get_addr(char **, struct in_addr *, struct in_addr *);
     67 static int get_port(const char *, u_int16_t *);
     68 static int get_proto(const char *, int *);
     69 static int get_fltr_opts(char **, char *, size_t, int *);
     70 static int interface_parser(char *);
     71 static int class_parser(char *) ;
     72 static int filter_parser(char *);
     73 #ifdef INET6
     74 static int filter6_parser(char *);
     75 static int get_ip6addr(char **, struct in6_addr *, struct in6_addr *);
     76 #endif
     77 static int ctl_parser(char *);
     78 static int delete_parser(char *);
     79 static int red_parser(char *);
     80 static int rio_parser(char *);
     81 static int conditioner_parser(char *);
     82 static int tc_action_parser(char *, char **, struct tc_action *);
     83 
     84 /*
     85  * Globals
     86  */
     87 #define MAX_NFLWDS      64
     88 #define MAX_T           64
     89 
     90 int             TNO = 1;  	/* Current Thread number */
     91 int		line_no = 0;
     92 int		filter_dontwarn;
     93 
     94 static char	if_names[MAX_T][IFNAMSIZ];
     95 static struct if_nameindex *if_namelist = NULL;
     96 
     97 #ifndef MAX
     98 #define MAX(a,b) (((a)>(b))?(a):(b))
     99 #endif
    100 #ifndef MIN
    101 #define MIN(a,b) (((a)<(b))?(a):(b))
    102 #endif
    103 
    104 enum op_codes {
    105         /* order must be same as entries cmd_tab[].cmd_op below!! */
    106         OP_HELP = 1, 	OP_QUIT,
    107 	OP_IFACE,	OP_CLASS,	OP_FILTER,
    108 	OP_ALTQ,		OP_DEL,
    109 #ifdef INET6
    110 	OP_FILTER6,
    111 #endif
    112 	OP_RED,		OP_RIO,
    113 	OP_CDNR,
    114         OP_NULL, 	OP_BUG
    115 };
    116 
    117 /*	Following table MUST match enum order of op_codes !
    118  */
    119 struct cmds {
    120 	char           *cmd_verb;
    121 	int             cmd_op;
    122 	char           *cmd_help;
    123 }		cmd_tab[] = {
    124 
    125   	{ "?",		OP_HELP, 	"Commands are:\n" },
    126 	{ "help",	OP_HELP, 	" help | ?\n" },
    127 	{ "quit",	OP_QUIT, 	" quit\n" },
    128 	{ "interface",	OP_IFACE,	" interface if_name [bandwidth bps] [cbq|hfsc]\n" },
    129 	{ "class",	OP_CLASS,	" class discipline if_name class_name [parent]\n" },
    130 	{ "filter",	OP_FILTER,	" filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]\n" },
    131 	{ "altq",	OP_ALTQ,	" disc if_name {enable|disable}\n" },
    132 	{ "delete",	OP_DEL,		" delete if_name class_name\n" },
    133 #ifdef INET6
    134 	{ "filter6",	OP_FILTER6,	" filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]\n" },
    135 #endif
    136 	{ "red", 	OP_RED,		" red th_min th_max inv_pmax\n" },
    137 	{ "rio", 	OP_RIO,		" 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\n" },
    138 	{ "conditioner", OP_CDNR,	" conditioner if_name cdnr_name <tc_action>\n" },
    139 	{ "bug",	OP_BUG,		" bug (On/Off)\n" },
    140 	{ "",		OP_NULL,	"" } /* MUST BE LAST IN CMD TABLE */
    141 };
    142 
    143 static int
    144 is_qdisc_name(const char *qname)
    145 {
    146 	struct qdisc_parser *qp;
    147 
    148 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
    149 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
    150 			return (1);
    151 	return (0);
    152 }
    153 
    154 static int
    155 qdisc_interface_parser(const char * qname, const char *ifname,
    156 		       int argc, char **argv)
    157 {
    158 	struct qdisc_parser *qp;
    159 
    160 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
    161 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
    162 			return (*qp->interface_parser)(ifname, argc, argv);
    163 	return (0);
    164 }
    165 
    166 static int
    167 qdisc_class_parser(const char *qname, const char *ifname,
    168 		   const char *class_name, const char *parent_name,
    169 		   int argc, char **argv)
    170 {
    171 	struct qdisc_parser *qp;
    172 	struct ifinfo	*ifinfo;
    173 
    174 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
    175 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) {
    176 			if (qp->class_parser == NULL) {
    177 				LOG(LOG_ERR, 0,
    178 				    "class can't be specified for %s", qp->qname);
    179 				return (0);
    180 			}
    181 			if ((ifinfo = ifname2ifinfo(ifname)) == NULL) {
    182 				LOG(LOG_ERR, 0,
    183 				    "no such interface, line %d\n", line_no);
    184 				return (0);
    185 			}
    186 			if (strncmp(ifinfo->qdisc->qname, qname,
    187 				    strlen(ifinfo->qdisc->qname)) != 0) {
    188 				LOG(LOG_ERR, 0,
    189 				    "qname doesn't match the interface, line %d\n",
    190 				    line_no);
    191 				return (0);
    192 			}
    193 			return (*qp->class_parser)(ifname, class_name,
    194 						   parent_name, argc, argv);
    195 		}
    196 	return (0);
    197 }
    198 
    199 
    200 /*
    201  * Read the config file to learn about tunnel vifs and non-default phyint
    202  * parameters.
    203  */
    204 int
    205 qcmd_config(void)
    206 {
    207 	FILE		*f;
    208 	int		i, rc = 1;
    209 
    210 	if (if_namelist != NULL)
    211 		if_freenameindex(if_namelist);
    212 	if_namelist = if_nameindex();
    213 
    214 	for (i = 0; i < MAX_T; i++)
    215 		if_names[i][0] = '\0';
    216 
    217 	LOG(LOG_INFO, 0, "ALTQ config file is %s\n", altqconfigfile);
    218 
    219 	f = fopen(altqconfigfile, "r");
    220 	if (f == NULL) {
    221 		LOG(LOG_ERR, errno, "Can't open %s", altqconfigfile, 0);
    222 		return (QOPERR_INVAL);
    223 	}
    224 	line_no = 0;
    225 	while (rc)
    226 		rc = DoCommand(altqconfigfile, f);
    227 
    228 	(void) fclose(f);
    229 	line_no = 0;
    230 	return (0);
    231 }
    232 
    233 /*
    234  *  Do_Command(): Top-level routine to read the next line from a given
    235  *	file and execute the command it contains.
    236  *	returns 1 if OK, 0 if EOF.
    237  */
    238 int
    239 DoCommand(char *infile, FILE *infp)
    240 {
    241 	char	cmd_line[256], cmd_op[80];
    242 	struct	cmds *cmdp;
    243 	char	*cp;
    244 	int	rc;
    245 
    246 	if (fgets(cmd_line, sizeof(cmd_line), infp) == NULL)
    247 		/* EOF */
    248 		return(0);
    249 	line_no++;
    250 
    251 	/* check escaped newline */
    252 	while ((cp = strrchr(cmd_line, '\\')) != NULL && cp[1] == '\n') {
    253 		if (fgets(cp, &cmd_line[256] - cp, infp) != NULL)
    254 			line_no++;
    255 	}
    256 
    257 	/* remove trailing NL */
    258 	cp = cmd_line + strlen(cmd_line) - 1;
    259 	if (*cp == '\n')
    260 		*cp = '\0';
    261 	else if (!feof(infp)) {
    262 		printf("LINE %d > 255 CHARS: %s.\n", line_no, cmd_line);
    263 		exit(1);
    264 	}
    265 	/*** printf("DoCommand: %s\n", cmd_line); ***/
    266 
    267 	if (cmd_line[0] == '#') {	/* Comment, skip this line */
    268 		return(1);
    269 	}
    270 	cp = cmd_line;
    271 	if (!next_word(&cp, cmd_op))
    272 		return(1);
    273 	if (cmd_op[0] == 'T') {
    274 		TNO = atoi(&cmd_op[1]);
    275 		if (!next_word(&cp, cmd_op))
    276 			return(1);
    277 	}
    278 	cmdp = cmd_tab;
    279 	while ((cmdp->cmd_op != OP_NULL) && pfxcmp(cmd_op, cmdp->cmd_verb))
    280 		cmdp++;
    281 
    282 	if (cmdp->cmd_op == OP_NULL) {
    283 		if (cmd_op[0])
    284 			printf(" ?? %s\n", cmd_op);
    285 		return(1);
    286 	}
    287 	rc = do_cmd(cmdp->cmd_op, cp);
    288 	if (rc == 0) {
    289 		if (infile) {
    290 			/* error in the config file.  cleanup and exit. */
    291 			LOG(LOG_ERR, 0, "Config failed. Exiting.\n");
    292 			(void) qcmd_destroyall();
    293 			(void) fclose(infp);
    294 			exit(1);
    295 		} else {
    296 			/* interactive mode */
    297 			printf("error: usage :");
    298 			show_help(cmdp->cmd_op);
    299 		}
    300 	}
    301 	return(1);
    302 }
    303 
    304 
    305 /*
    306  * Prefix string comparison: Return 0 if s1 string is prefix of s2 string, 1
    307  * otherwise.
    308  */
    309 static int
    310 pfxcmp(const char *s1, const char *s2)
    311 {
    312 	while (*s1)
    313 		if (*s1++ != *s2++)
    314 			return (1);
    315 	return (0);
    316 }
    317 
    318 /*
    319  * Skip leading blanks, then copy next word (delimited by blank or zero, but
    320  * no longer than 63 bytes) into buffer b, set scan pointer to following
    321  * non-blank (or end of string), and return 1.  If there is no non-blank text,
    322  * set scan ptr to point to 0 byte and return 0.
    323  */
    324 static int
    325 next_word(char **cpp, char *b)
    326 {
    327 	char           *tp;
    328 	size_t		L;
    329 
    330 	*cpp += strspn(*cpp, " \t");
    331 	if (**cpp == '\0' || **cpp == '\n' || **cpp == '#')
    332 		return(0);
    333 
    334 	tp = strpbrk(*cpp, " \t\n#");
    335 	L = MIN((tp)?(tp-*cpp):strlen(*cpp), 63);
    336 	strncpy(b, *cpp, L);
    337 	*(b + L) = '\0';
    338 	*cpp += L;
    339 	*cpp += strspn(*cpp, " \t");
    340 	return (1);
    341 }
    342 
    343 /*
    344  * do_cmd executes a command input.
    345  * returns 1 if OK, 0 if an error occurs.
    346  */
    347 static int
    348 do_cmd(int op, char *cmdbuf)
    349 {
    350 	int i, rval = 0;
    351 
    352 	switch (op) {
    353 	case OP_HELP:
    354 		for (i = 0; i < OP_NULL; i++)
    355 			show_help(i);
    356 		rval = 1;
    357 		break;
    358 	case OP_QUIT:
    359 		qcmd_destroyall();
    360 		exit(0);
    361 		break;
    362 	case OP_IFACE:
    363 		rval = interface_parser(cmdbuf);
    364 		break;
    365 	case OP_CLASS:
    366 		rval = class_parser(cmdbuf);
    367 		break;
    368 	case OP_FILTER:
    369 		rval = filter_parser(cmdbuf);
    370 		break;
    371 	case OP_ALTQ:
    372 		rval = ctl_parser(cmdbuf);
    373 		break;
    374 	case OP_DEL:
    375 		rval = delete_parser(cmdbuf);
    376 		break;
    377 #ifdef INET6
    378 	case OP_FILTER6:
    379 		rval = filter6_parser(cmdbuf);
    380 		break;
    381 #endif
    382 	case OP_RED:
    383 		rval = red_parser(cmdbuf);
    384 		break;
    385 	case OP_RIO:
    386 		rval = rio_parser(cmdbuf);
    387 		break;
    388 	case OP_CDNR:
    389 		rval = conditioner_parser(cmdbuf);
    390 		break;
    391 	case OP_BUG:
    392 		if (m_debug & DEBUG_ALTQ) {
    393 			/* turn off verbose */
    394 			l_debug = LOG_INFO;
    395 			m_debug &= ~DEBUG_ALTQ;
    396 		} else {
    397 			/* turn on verbose */
    398 			l_debug = LOG_DEBUG;
    399 			m_debug |= DEBUG_ALTQ;
    400 		}
    401 		break;
    402 	default:
    403 		printf("command %d not supported\n", op);
    404 		rval = 0;
    405 		break;
    406 	}
    407 	return(rval);
    408 }
    409 
    410 #define EQUAL(s1, s2)	(strcmp((s1), (s2)) == 0)
    411 
    412 char *cur_ifname(void)
    413 {
    414 	return (if_names[TNO]);
    415 }
    416 
    417 u_int
    418 get_ifindex(const char *ifname)
    419 {
    420 	struct if_nameindex *ifnp;
    421 
    422 	for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
    423 		if (strcmp(ifname, ifnp->if_name) == 0)
    424 			return (ifnp->if_index);
    425 	return (0);
    426 }
    427 
    428 static int
    429 get_ifname(char **cpp, char **ifnamep)
    430 {
    431 	char w[128], *ocp;
    432 	struct if_nameindex *ifnp;
    433 
    434 	ocp = *cpp;
    435 	if (next_word(&ocp, w) && if_namelist != NULL)
    436 		for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
    437 			if (strcmp(w, ifnp->if_name) == 0) {
    438 				/* if_name found. advance the word pointer */
    439 				*cpp = ocp;
    440 				strlcpy(if_names[TNO], w, sizeof(if_names[TNO]));
    441 				*ifnamep = if_names[TNO];
    442 				return (1);
    443 			}
    444 
    445 	/* this is not interface name. use one in the context. */
    446 	if (if_names[TNO][0] == 0)
    447 		return (0);
    448 	*ifnamep = if_names[TNO];
    449 	return (1);
    450 }
    451 
    452 /* set address and netmask in network byte order */
    453 static int
    454 get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask)
    455 {
    456 	char w[128], *ocp;
    457 	struct in_addr tmp;
    458 
    459 	addr->s_addr = 0;
    460 	mask->s_addr = 0xffffffff;
    461 
    462 	if (!next_word(cpp, w))
    463 		return (0);
    464 
    465 	if (inet_aton((char *)w, &tmp) != 1) {
    466 		/* try gethostbyname */
    467 		struct hostent *h;
    468 
    469 		if ((h = gethostbyname(w)) == NULL
    470 		    || h->h_addrtype != AF_INET || h->h_length != 4)
    471 			return (0);
    472 
    473 		bcopy(h->h_addr, &tmp, (size_t)h->h_length);
    474 	}
    475 
    476 	addr->s_addr = tmp.s_addr;
    477 
    478 	/* check if netmask option is present */
    479 	ocp = *cpp;
    480 	if (next_word(&ocp, w) && EQUAL(w, "netmask")) {
    481 		if (!next_word(&ocp, w))
    482 			return (0);
    483 
    484 		if (inet_aton((char *)w, (struct in_addr *)&tmp) != 1)
    485 			return (0);
    486 
    487 		mask->s_addr = tmp.s_addr;
    488 		*cpp = ocp;
    489 		return (1);
    490 	}
    491 	/* no netmask option */
    492 	return (1);
    493 }
    494 
    495 /* returns service number in network byte order */
    496 static int
    497 get_port(const char *name, u_int16_t *port_no)
    498 {
    499 	struct servent *s;
    500 	u_int16_t num;
    501 
    502 	if (isdigit(name[0])) {
    503 		num = (u_int16_t)strtol(name, NULL, 0);
    504 		*port_no = htons(num);
    505 		return (1);
    506 	}
    507 
    508 	if ((s = getservbyname(name, 0)) == NULL)
    509 		return (0);
    510 
    511 	*port_no = (u_int16_t)s->s_port;
    512 	return (1);
    513 }
    514 
    515 static int
    516 get_proto(const char *name, int *proto_no)
    517 {
    518 	struct protoent *p;
    519 
    520 	if (isdigit(name[0])) {
    521 		*proto_no = (int)strtol(name, NULL, 0);
    522 		return (1);
    523 	}
    524 
    525 	if ((p = getprotobyname(name)) == NULL)
    526 		return (0);
    527 
    528 	*proto_no = p->p_proto;
    529 	return (1);
    530 }
    531 
    532 static int
    533 get_fltr_opts(char **cpp, char *fltr_name, size_t len, int *ruleno)
    534 {
    535 	char w[128], *ocp;
    536 
    537 	ocp = *cpp;
    538 	while (next_word(&ocp, w)) {
    539 		if (EQUAL(w, "name")) {
    540 			if (!next_word(&ocp, w))
    541 				return (0);
    542 			strlcpy(fltr_name, w, len);
    543 			*cpp = ocp;
    544 		} else if (EQUAL(w, "ruleno")) {
    545 			if (!next_word(&ocp, w))
    546 				return (0);
    547 			*ruleno = (int)strtol(w, NULL, 0);
    548 			*cpp = ocp;
    549 		} else
    550 			break;
    551 	}
    552 	return (1);
    553 }
    554 
    555 
    556 #define	DISCIPLINE_NONE		0
    557 
    558 static int
    559 interface_parser(char *cmdbuf)
    560 {
    561 	char	w[256], *ap, *cp = cmdbuf;
    562 	char	*ifname, *argv[64], qdisc_name[64];
    563 	int     argc, rval;
    564 
    565 	if (!get_ifname(&cp, &ifname)) {
    566 		LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
    567 		    altqconfigfile, line_no);
    568 		return (0);
    569 	}
    570 
    571 	/*
    572 	 * Create argment list & look for scheduling discipline options.
    573 	 */
    574 	snprintf(qdisc_name, sizeof(qdisc_name), "null");
    575 	argc = 0;
    576 	ap = w;
    577 	while (next_word(&cp, ap)) {
    578 		if (is_qdisc_name(ap))
    579 			strlcpy(qdisc_name, ap, sizeof(qdisc_name));
    580 
    581 		argv[argc] = ap;
    582 		ap += strlen(ap) + 1;
    583 		argc++;
    584 	}
    585 
    586 	rval = qdisc_interface_parser(qdisc_name, ifname, argc, argv);
    587 	if (rval == 0) {
    588 		LOG(LOG_ERR, 0, "Error in %s, line %d\n",
    589 		    altqconfigfile, line_no);
    590 		return (0);
    591 	}
    592 	return (1);
    593 }
    594 
    595 static int
    596 class_parser(char *cmdbuf)
    597 {
    598 	char	w[256], *cp = cmdbuf;
    599 	char 	*ifname, qdisc_name[128], class_name[128], parent_name[128];
    600 	char	*clname = class_name;
    601 	char	*parent = NULL;
    602 	char	*argv[64], *ap;
    603 	int	argc, rval;
    604 
    605 	/* get scheduling class */
    606 	if (!next_word(&cp, qdisc_name)) {
    607 		LOG(LOG_ERR, 0, "missing scheduling discipline in %s, line %d\n",
    608 		    altqconfigfile, line_no);
    609 		return (0);
    610 	}
    611 	if (!is_qdisc_name(qdisc_name)) {
    612 		LOG(LOG_ERR, 0,
    613 		    "unknown scheduling discipline '%s' in %s, line %d\n",
    614 		    qdisc_name, altqconfigfile, line_no);
    615 		return (0);
    616 	}
    617 
    618 	/* get interface name */
    619 	if (!get_ifname(&cp, &ifname)) {
    620 		LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
    621 		    altqconfigfile, line_no);
    622 		return (0);
    623 	}
    624 
    625 	/* get class name */
    626 	if (!next_word(&cp, class_name)) {
    627 		LOG(LOG_ERR, 0, "missing class name in %s, line %d\n",
    628 		    altqconfigfile, line_no);
    629 		return (0);
    630 	}
    631 
    632 	/* get parent name */
    633 	if (!next_word(&cp, parent_name)) {
    634 		LOG(LOG_ERR, 0, "missing parent class in %s, line %d\n",
    635 		    altqconfigfile, line_no);
    636 		return (0);
    637 	}
    638 	if (!EQUAL(parent_name, "null") && !EQUAL(parent_name, "NULL")) {
    639 		parent = parent_name;
    640 	} else {
    641 		parent = NULL;
    642 	}
    643 
    644 	ap = w;
    645 	argc = 0;
    646 	while (next_word(&cp, ap)) {
    647 		argv[argc] = ap;
    648 		ap += strlen(ap) + 1;
    649 		argc++;
    650 	}
    651 
    652 	rval = qdisc_class_parser(qdisc_name, ifname, clname, parent,
    653 				 argc, argv);
    654     	if (rval == 0) {
    655 		LOG(LOG_ERR, 0, "can't add class '%s' on interface '%s'\n",
    656 		    clname, ifname);
    657 		return (0);
    658 	}
    659 
    660 	return (1);
    661 }
    662 
    663 static int
    664 filter_parser(char *cmdbuf)
    665 {
    666 	char 	w[128], *cp = cmdbuf;
    667 	char 	*ifname, class_name[64], fltr_name[64], *flname = NULL;
    668 	struct flow_filter	sfilt;
    669 	int	protocol;
    670 	u_char	tos, tosmask;
    671 	int	ruleno;
    672 	int	dontwarn = 0;
    673 	int	error;
    674 
    675 	memset(&sfilt, 0, sizeof(sfilt));
    676 	sfilt.ff_flow.fi_family = AF_INET;
    677 
    678 	if (!get_ifname(&cp, &ifname)) {
    679 		LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
    680 		    altqconfigfile, line_no);
    681 		return (0);
    682 	}
    683 
    684 	if (!next_word(&cp, class_name)) {
    685 		LOG(LOG_ERR, 0,
    686 		    "missing class name in %s, line %d\n",
    687 		    altqconfigfile, line_no);
    688 		return (0);
    689 	}
    690 
    691 	fltr_name[0] = '\0';
    692 	ruleno = 0;
    693 	if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) {
    694 		LOG(LOG_ERR, 0,
    695 		    "bad filter option in %s, line %d\n",
    696 		    altqconfigfile, line_no);
    697 		return (0);
    698 	}
    699 	if (fltr_name[0] != '\0')
    700 		flname = fltr_name;
    701 	sfilt.ff_ruleno = ruleno;
    702 
    703 	/* get filter destination Address */
    704 	if (!get_addr(&cp, &sfilt.ff_flow.fi_dst, &sfilt.ff_mask.mask_dst)) {
    705 		LOG(LOG_ERR, 0,
    706 		    "bad filter destination address in %s, line %d\n",
    707 		    altqconfigfile, line_no);
    708 		return (0);
    709 	}
    710 
    711 	/* get filter destination port */
    712 	if (!next_word(&cp, w)) {
    713 		LOG(LOG_ERR, 0,
    714 		    "missing filter destination port in %s, line %d\n",
    715 		    altqconfigfile, line_no);
    716 		return (0);
    717 	}
    718 	if (!get_port(w, &sfilt.ff_flow.fi_dport)) {
    719 		LOG(LOG_ERR, 0, "bad filter destination port in %s, line %d\n",
    720 		    altqconfigfile, line_no);
    721 		return (0);
    722 	}
    723 
    724 	/* get filter source address */
    725 	if (!get_addr(&cp, &sfilt.ff_flow.fi_src, &sfilt.ff_mask.mask_src)) {
    726 		LOG(LOG_ERR, 0, "bad filter source address in %s, line %d\n",
    727 		    altqconfigfile, line_no);
    728 		return (0);
    729 	}
    730 
    731 	/* get filter source port */
    732 	if (!next_word(&cp, w)) {
    733 		LOG(LOG_ERR, 0, "missing filter source port in %s, line %d\n",
    734 		    altqconfigfile, line_no);
    735 		return (0);
    736 	}
    737 	if (!get_port(w, &sfilt.ff_flow.fi_sport)) {
    738 		LOG(LOG_ERR, 0, "bad filter source port in %s, line %d\n",
    739 		    altqconfigfile, line_no);
    740 		return (0);
    741 	}
    742 
    743 	/* get filter protocol id */
    744 	if (!next_word(&cp, w)) {
    745 		LOG(LOG_ERR, 0, "missing filter protocol id in %s, line %d\n",
    746 		    altqconfigfile, line_no);
    747 		return (0);
    748 	}
    749 	if (!get_proto(w, &protocol)) {
    750 		LOG(LOG_ERR, 0, "bad protocol in %s, line %d\n",
    751 		    altqconfigfile, line_no);
    752 		return (0);
    753 	}
    754 	sfilt.ff_flow.fi_proto = protocol;
    755 
    756 	while (next_word(&cp, w)) {
    757 		if (EQUAL(w, "tos")) {
    758 			tos = 0;
    759 			tosmask = 0xff;
    760 
    761 			if (next_word(&cp, w)) {
    762 				tos = (u_char)strtol(w, NULL, 0);
    763 				if (next_word(&cp, w)) {
    764 					if (EQUAL(w, "tosmask")) {
    765 						next_word(&cp, w);
    766 						tosmask = (u_char)strtol(w, NULL, 0);
    767 					}
    768 				}
    769 			}
    770 			sfilt.ff_flow.fi_tos = tos;
    771 			sfilt.ff_mask.mask_tos = tosmask;
    772 		} else if (EQUAL(w, "gpi")) {
    773 			if (next_word(&cp, w)) {
    774 				sfilt.ff_flow.fi_gpi =
    775 					(u_int32_t)strtoul(w, NULL, 0);
    776 				sfilt.ff_flow.fi_gpi =
    777 					htonl(sfilt.ff_flow.fi_gpi);
    778 			}
    779 		} else if (EQUAL(w, "dontwarn"))
    780 			dontwarn = 1;
    781 	}
    782 
    783 	/*
    784 	 * Add the filter.
    785 	 */
    786 	filter_dontwarn = dontwarn;	/* XXX */
    787 	error = qcmd_add_filter(ifname, class_name, flname, &sfilt);
    788 	filter_dontwarn = 0;		/* XXX */
    789 	if (error) {
    790 		LOG(LOG_ERR, 0,
    791 		    "can't add filter to class '%s' on interface '%s'\n",
    792 		    class_name, ifname);
    793 		return (0);
    794 	}
    795 
    796 	return (1);
    797 }
    798 
    799 #ifdef INET6
    800 static int
    801 filter6_parser(char *cmdbuf)
    802 {
    803 	char 	w[128], *cp = cmdbuf;
    804 	char 	*ifname, class_name[128], fltr_name[64], *flname = NULL;
    805 	struct flow_filter6	sfilt;
    806 	int	protocol;
    807 	u_char	tclass, tclassmask;
    808 	int	ruleno;
    809 	int	dontwarn = 0;
    810 	int	ret;
    811 
    812 	memset(&sfilt, 0, sizeof(sfilt));
    813 	sfilt.ff_flow6.fi6_family = AF_INET6;
    814 
    815 	if (!get_ifname(&cp, &ifname)) {
    816 		LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
    817 		    altqconfigfile, line_no);
    818 		return (0);
    819 	}
    820 
    821 	if (!next_word(&cp, class_name)) {
    822 		LOG(LOG_ERR, 0, "missing class name in %s, line %d\n",
    823 		    altqconfigfile, line_no);
    824 		return (0);
    825 	}
    826 
    827 	fltr_name[0] = '\0';
    828 	ruleno = 0;
    829 	if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) {
    830 		LOG(LOG_ERR, 0,
    831 		    "bad filter option in %s, line %d\n",
    832 		    altqconfigfile, line_no);
    833 		return (0);
    834 	}
    835 	if (fltr_name[0] != '\0')
    836 		flname = fltr_name;
    837 	sfilt.ff_ruleno = ruleno;
    838 
    839 	/* get filter destination address */
    840 	if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_dst,
    841 			 &sfilt.ff_mask6.mask6_dst)) {
    842 		LOG(LOG_ERR, 0, "bad destination address in %s, line %d\n",
    843 		    altqconfigfile, line_no);
    844 		return (0);
    845 	}
    846 
    847 	/* get filter destination port */
    848 	if (!next_word(&cp, w)) {
    849 		LOG(LOG_ERR, 0,
    850 		    "missing filter destination port in %s, line %d\n",
    851 		    altqconfigfile, line_no);
    852 		return (0);
    853 	}
    854 	if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) {
    855 		LOG(LOG_ERR, 0, "bad filter destination port in %s, line %d\n",
    856 		    altqconfigfile, line_no);
    857 		return (0);
    858 	}
    859 
    860 	/* get filter source address */
    861 	if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_src,
    862 			 &sfilt.ff_mask6.mask6_src)) {
    863 		LOG(LOG_ERR, 0, "bad source address in %s, line %d\n",
    864 		    altqconfigfile, line_no);
    865 		return (0);
    866 	}
    867 
    868 	/* get filter source port */
    869 	if (!next_word(&cp, w)) {
    870 		LOG(LOG_ERR, 0, "missing filter source port in %s, line %d\n",
    871 		    altqconfigfile, line_no);
    872 		return (0);
    873 	}
    874 	if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) {
    875 		LOG(LOG_ERR, 0, "bad filter source port in %s, line %d\n",
    876 		    altqconfigfile, line_no);
    877 		return (0);
    878 	}
    879 
    880 	/* get filter protocol id */
    881 	if (!next_word(&cp, w)) {
    882 		LOG(LOG_ERR, 0, "missing filter protocol id in %s, line %d\n",
    883 		    altqconfigfile, line_no);
    884 		return (0);
    885 	}
    886 	if (!get_proto(w, &protocol)) {
    887 		LOG(LOG_ERR, 0, "bad protocol in %s, line %d\n",
    888 		    altqconfigfile, line_no);
    889 		return (0);
    890 	}
    891 	sfilt.ff_flow6.fi6_proto = protocol;
    892 
    893 	while (next_word(&cp, w)) {
    894 		if (EQUAL(w, "tclass")) {
    895 			tclass = 0;
    896 			tclassmask = 0xff;
    897 
    898 			if (next_word(&cp, w)) {
    899 				tclass = (u_char)strtol(w, NULL, 0);
    900 				if (next_word(&cp, w)) {
    901 					if (EQUAL(w, "tclassmask")) {
    902 						next_word(&cp, w);
    903 						tclassmask =
    904 						    (u_char)strtol(w, NULL, 0);
    905 					}
    906 				}
    907 			}
    908 			sfilt.ff_flow6.fi6_tclass = tclass;
    909 			sfilt.ff_mask6.mask6_tclass = tclassmask;
    910 		} else if (EQUAL(w, "gpi")) {
    911 			if (next_word(&cp, w)) {
    912 				sfilt.ff_flow6.fi6_gpi =
    913 					(u_int32_t)strtoul(w, NULL, 0);
    914 				sfilt.ff_flow6.fi6_gpi =
    915 					htonl(sfilt.ff_flow6.fi6_gpi);
    916 			}
    917 		} else if (EQUAL(w, "flowlabel")) {
    918 			if (next_word(&cp, w)) {
    919 				sfilt.ff_flow6.fi6_flowlabel =
    920 				   (u_int32_t)strtoul(w, NULL, 0) & 0x000fffff;
    921 				sfilt.ff_flow6.fi6_flowlabel =
    922 					htonl(sfilt.ff_flow6.fi6_flowlabel);
    923 			}
    924 		} else if (EQUAL(w, "dontwarn"))
    925 			dontwarn = 1;
    926 	}
    927 
    928 	/*
    929 	 * Add the filter.
    930 	 */
    931 	filter_dontwarn = dontwarn;	/* XXX */
    932 	ret = qcmd_add_filter(ifname, class_name, flname,
    933 			      (struct flow_filter *)&sfilt);
    934 	filter_dontwarn = 0;		/* XXX */
    935 	if (ret) {
    936 		LOG(LOG_ERR, 0,
    937 		    "can't add filter to class '%s' on interface '%s'\n",
    938 		    class_name, ifname);
    939 		return (0);
    940 	}
    941 
    942 	return (1);
    943 }
    944 
    945 static int
    946 get_ip6addr(char **cpp, struct in6_addr *addr, struct in6_addr *mask)
    947 {
    948 	char w[128], *prefix;
    949 	u_char *cp;
    950 	int len;
    951 
    952 	*addr = in6addr_any;  /* set all 0 */
    953 	*mask = in6addr_any;  /* set all 0 */
    954 
    955 	if (!next_word(cpp, w))
    956 		return (0);
    957 
    958 	if (EQUAL(w, "0"))
    959 		/* abbreviation of a wildcard (::0) */
    960 		return (1);
    961 
    962 	if ((prefix = strchr(w, '/')) != NULL) {
    963 		/* address has prefix length */
    964 		*prefix++ = '\0';
    965 	}
    966 
    967 	if (inet_pton(AF_INET6, w, addr) != 1)
    968 		return (0);
    969 
    970 	if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefix == NULL)
    971 		/* wildcard */
    972 		return (1);
    973 
    974 	/* convert address prefix length to address mask */
    975 	if (prefix != NULL) {
    976 		len = (int)strtol(prefix, NULL, 0);
    977 		if ((len < 0) || (len > 128))
    978 			return (0);
    979 		for (cp = (u_char *)mask; len > 7; len -= 8)
    980 			*cp++ = 0xff;
    981 		if (len > 0)
    982 			*cp = (0xff << (8 - len)) & 0xff;
    983 
    984 		IN6ADDR32(addr, 0) &= IN6ADDR32(mask, 0);
    985 		IN6ADDR32(addr, 1) &= IN6ADDR32(mask, 1);
    986 		IN6ADDR32(addr, 2) &= IN6ADDR32(mask, 2);
    987 		IN6ADDR32(addr, 3) &= IN6ADDR32(mask, 3);
    988 	} else
    989 		/* full mask */
    990 		memset(mask, 0xff, sizeof(struct in6_addr));
    991 
    992 	return (1);
    993 }
    994 
    995 #endif /* INET6 */
    996 
    997 static int
    998 ctl_parser(char *cmdbuf)
    999 {
   1000 	char	w[128], *cp = cmdbuf;
   1001 	char	*ifname;
   1002 	int	state;
   1003 	int	rval;
   1004 
   1005 	if (!get_ifname(&cp, &ifname)) {
   1006 		printf("missing interface name in %s, line %d",
   1007 		       altqconfigfile, line_no);
   1008 		return (0);
   1009 	}
   1010 
   1011 	if (!next_word(&cp, w)) {
   1012 		state = is_q_enabled(ifname);
   1013 		printf("altq %s on %s\n",
   1014 		       state ? "enabled" : "disabled", ifname);
   1015 		return (1);
   1016 	}
   1017 
   1018 	if (EQUAL(w, "enable")) {
   1019 		rval = qcmd_enable(ifname);
   1020 		printf("altq %s on %s\n",
   1021 		       (rval == 0) ? "enabled" : "enable failed!", ifname);
   1022 	} else if (EQUAL(w, "disable")) {
   1023 		rval = qcmd_disable(ifname);
   1024 		printf("altq %s on %s\n",
   1025 		       (rval == 0) ? "disabled" : "disable failed!", ifname);
   1026 	} else if (EQUAL(w, "reload")) {
   1027 		printf("reinitializing altq...\n");
   1028 		qcmd_destroyall();
   1029 		qcmd_init();
   1030 	} else
   1031 		return (0);
   1032 	return (1);
   1033 }
   1034 
   1035 
   1036 static int
   1037 delete_parser(char *cmdbuf)
   1038 {
   1039 	char	*cp = cmdbuf;
   1040 	char	*ifname, class_name[128];
   1041 	int	ret;
   1042 
   1043 	if (!get_ifname(&cp, &ifname)) {
   1044 		printf("missing interface name in %s, line %d",
   1045 		       altqconfigfile, line_no);
   1046 		return (0);
   1047 	}
   1048 
   1049 	if (!next_word(&cp, class_name)) {
   1050 		LOG(LOG_ERR, 0,
   1051 		    "missing class name in %s, line %d\n",
   1052 		    altqconfigfile, line_no);
   1053 		return (0);
   1054 	}
   1055 
   1056 	ret = qcmd_delete_class(ifname, class_name);
   1057 	if (ret) {
   1058 		LOG(LOG_ERR, 0,
   1059 		    "can't delete class '%s' on interface '%s'\n",
   1060 		    class_name, ifname);
   1061 		return (0);
   1062 	}
   1063 
   1064 	return (1);
   1065 }
   1066 
   1067 static int
   1068 red_parser(char *cmdbuf)
   1069 {
   1070 	char	w[128], *cp = cmdbuf;
   1071 	int th_min, th_max, inv_pmax;
   1072 
   1073 	if (!next_word(&cp, w))
   1074 		goto bad;
   1075 	th_min = (int)strtol(w, NULL, 0);
   1076 
   1077 	if (!next_word(&cp, w))
   1078 		goto bad;
   1079 	th_max = (int)strtol(w, NULL, 0);
   1080 
   1081 	if (!next_word(&cp, w))
   1082 		goto bad;
   1083 	inv_pmax = (int)strtol(w, NULL, 0);
   1084 
   1085 	if (qop_red_set_defaults(th_min, th_max, inv_pmax) != 0) {
   1086 		LOG(LOG_ERR, 0, "can't set red default parameters\n");
   1087 		return (0);
   1088 	}
   1089 
   1090 	return (1);
   1091 
   1092  bad:
   1093 	LOG(LOG_ERR, 0, "bad red parameter in %s, line %d\n",
   1094 	    altqconfigfile, line_no);
   1095 	return (0);
   1096 }
   1097 
   1098 static int
   1099 rio_parser(char *cmdbuf)
   1100 {
   1101 	char	w[128], *cp = cmdbuf;
   1102 	int	i;
   1103 	struct redparams params[RIO_NDROPPREC];
   1104 
   1105 	for (i = 0; i < RIO_NDROPPREC; i++) {
   1106 		if (!next_word(&cp, w))
   1107 			goto bad;
   1108 		params[i].th_min = (int)strtol(w, NULL, 0);
   1109 
   1110 		if (!next_word(&cp, w))
   1111 			goto bad;
   1112 		params[i].th_max = (int)strtol(w, NULL, 0);
   1113 
   1114 		if (!next_word(&cp, w))
   1115 			goto bad;
   1116 		params[i].inv_pmax = (int)strtol(w, NULL, 0);
   1117 	}
   1118 
   1119 	if (qop_rio_set_defaults(&params[0]) != 0) {
   1120 		LOG(LOG_ERR, 0, "can't set rio default parameters\n");
   1121 		return (0);
   1122 	}
   1123 
   1124 	return (1);
   1125 
   1126  bad:
   1127 	LOG(LOG_ERR, 0, "bad rio parameter in %s, line %d\n",
   1128 	    altqconfigfile, line_no);
   1129 	return (0);
   1130 }
   1131 
   1132 static int
   1133 conditioner_parser(char *cmdbuf)
   1134 {
   1135 	char	cdnr_name[128], *cp = cmdbuf;
   1136 	char	*ifname;
   1137 	struct tc_action action[64];
   1138 
   1139 	if (!get_ifname(&cp, &ifname)) {
   1140 		LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
   1141 		    altqconfigfile, line_no);
   1142 		return (0);
   1143 	}
   1144 
   1145 	/* get conditioner name */
   1146 	if (!next_word(&cp, cdnr_name)) {
   1147 		LOG(LOG_ERR, 0, "missing cdnr name in %s, line %d\n",
   1148 		    altqconfigfile, line_no);
   1149 		return (0);
   1150 	}
   1151 
   1152 	if (tc_action_parser(ifname, &cp, &action[0]) == 0)
   1153 		return (0);
   1154 
   1155 	if (qcmd_cdnr_add_element(NULL, ifname, cdnr_name, &action[0]) != 0)
   1156 		return (0);
   1157 	return (1);
   1158 }
   1159 
   1160 /*
   1161  * recursively parse '<'tc_action'>'
   1162  * note that array "action" grows during recursive parse.
   1163  */
   1164 static int
   1165 tc_action_parser(char *ifname, char **cpp, struct tc_action *action)
   1166 {
   1167 	char	*cp, *start, *end;
   1168 	char	type[128], w[128];
   1169 	int	depth, i;
   1170 	struct tb_profile profile[2];
   1171 
   1172 	/*
   1173 	 * find a possibly nested pair of '<' and '>',
   1174 	 * make them pointed by 'start' and 'end'.
   1175 	 */
   1176 	start = strchr(*cpp, '<');
   1177 	if (start == NULL) {
   1178 		LOG(LOG_ERR, 0, "conditioner action missing in %s, line %d\n",
   1179 		    altqconfigfile, line_no);
   1180 		return (0);
   1181 	}
   1182 	depth = 1;
   1183 	cp = start + 1;
   1184 	do {
   1185 		end = strpbrk(cp, "<>");
   1186 		if (end == NULL) {
   1187 			LOG(LOG_ERR, 0,
   1188 			    "conditioner action delimiter mismatch in %s, line %d\n",
   1189 			    altqconfigfile, line_no);
   1190 			return (0);
   1191 		}
   1192 		if (*end == '<')
   1193 			depth++;
   1194 		else if (*end == '>')
   1195 			depth--;
   1196 		cp = end + 1;
   1197 	} while (depth > 0);
   1198 	*end = '\0';
   1199 	*cpp = end + 1;
   1200 	cp = start + 1;
   1201 
   1202 	if (IsDebug(DEBUG_ALTQ)) {
   1203 		printf("tc_action_parser: [%s]\n", cp);
   1204 	}
   1205 
   1206 	if (!next_word(&cp, type)) {
   1207 		LOG(LOG_ERR, 0,
   1208 		    "missing conditioner action type in %s, line %d\n",
   1209 		    altqconfigfile, line_no);
   1210 		return (0);
   1211 	}
   1212 
   1213 	/*
   1214 	 * action type specific process
   1215 	 */
   1216 	if (EQUAL(type, "conditioner")) {
   1217 		if (!next_word(&cp, w)) {
   1218 			LOG(LOG_ERR, 0,
   1219 			    "missing conditioner name in %s, line %d\n",
   1220 			    altqconfigfile, line_no);
   1221 			return (0);
   1222 		}
   1223 		action->tca_code = TCACODE_HANDLE;
   1224 		action->tca_handle = cdnr_name2handle(ifname, w);
   1225 		if (action->tca_handle == CDNR_NULL_HANDLE) {
   1226 			LOG(LOG_ERR, 0,
   1227 			    "wrong conditioner name %s in %s, line %d\n",
   1228 			    w, altqconfigfile, line_no);
   1229 			return (0);
   1230 		}
   1231 	} else if (EQUAL(type, "pass")) {
   1232 		action->tca_code = TCACODE_PASS;
   1233 	} else if (EQUAL(type, "drop")) {
   1234 		action->tca_code = TCACODE_DROP;
   1235 	} else if (EQUAL(type, "mark")) {
   1236 		if (!next_word(&cp, w)) {
   1237 			LOG(LOG_ERR, 0, "missing dscp in %s, line %d\n",
   1238 			    altqconfigfile, line_no);
   1239 			return (0);
   1240 		}
   1241 		action->tca_code = TCACODE_MARK;
   1242 		action->tca_dscp = (u_int8_t)strtol(w, NULL, 0);
   1243 	} else if (EQUAL(type, "tbmeter")) {
   1244 		if (!next_word(&cp, w)) {
   1245 			LOG(LOG_ERR, 0, "missing tb profile in %s, line %d\n",
   1246 			    altqconfigfile, line_no);
   1247 			return (0);
   1248 		}
   1249 		profile[0].rate = atobps(w);
   1250 		if (!next_word(&cp, w)) {
   1251 			LOG(LOG_ERR, 0, "missing tb profile in %s, line %d\n",
   1252 			    altqconfigfile, line_no);
   1253 			return (0);
   1254 		}
   1255 		profile[0].depth = atobytes(w);
   1256 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
   1257 			return (0);
   1258 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
   1259 			return (0);
   1260 
   1261 		if (qcmd_cdnr_add_tbmeter(action, ifname, NULL, &profile[0],
   1262 					  &action[1], &action[2]) != 0)
   1263 			return (0);
   1264 	} else if (EQUAL(type, "trtcm")) {
   1265 		int coloraware = 0;	/* default is color-blind */
   1266 
   1267 		for (i=0; i<2; i++) {
   1268 			if (!next_word(&cp, w)) {
   1269 				LOG(LOG_ERR, 0,
   1270 				    "missing tb profile in %s, line %d\n",
   1271 				    altqconfigfile, line_no);
   1272 				return (0);
   1273 			}
   1274 			profile[i].rate = atobps(w);
   1275 			if (!next_word(&cp, w)) {
   1276 				LOG(LOG_ERR, 0,
   1277 				    "missing tb profile in %s, line %d\n",
   1278 				    altqconfigfile, line_no);
   1279 				return (0);
   1280 			}
   1281 			profile[i].depth = atobytes(w);
   1282 		}
   1283 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
   1284 			return (0);
   1285 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
   1286 			return (0);
   1287 		if (tc_action_parser(ifname, &cp, &action[3]) == 0)
   1288 			return (0);
   1289 		if (next_word(&cp, w)) {
   1290 			if (EQUAL(w, "coloraware"))
   1291 				coloraware = 1;
   1292 			else if (EQUAL(w, "colorblind"))
   1293 				coloraware = 0;
   1294 		}
   1295 
   1296 		if (qcmd_cdnr_add_trtcm(action, ifname, NULL,
   1297 					&profile[0], &profile[1],
   1298 					&action[1], &action[2], &action[3],
   1299 					coloraware) != 0)
   1300 			return (0);
   1301 	} else if (EQUAL(type, "tswtcm")) {
   1302 		u_int32_t cmtd_rate, peak_rate, avg_interval;
   1303 
   1304 		if (!next_word(&cp, w)) {
   1305 			LOG(LOG_ERR, 0, "missing cmtd rate in %s, line %d\n",
   1306 			    altqconfigfile, line_no);
   1307 			return (0);
   1308 		}
   1309 		cmtd_rate = atobps(w);
   1310 
   1311 		if (!next_word(&cp, w)) {
   1312 			LOG(LOG_ERR, 0, "missing peak rate in %s, line %d\n",
   1313 			    altqconfigfile, line_no);
   1314 			return (0);
   1315 		}
   1316 		peak_rate = atobps(w);
   1317 
   1318 		if (!next_word(&cp, w)) {
   1319 			LOG(LOG_ERR, 0, "missing avg interval in %s, line %d\n",
   1320 			    altqconfigfile, line_no);
   1321 			return (0);
   1322 		}
   1323 		avg_interval = (u_int32_t)strtoul(w, NULL, 0);
   1324 
   1325 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
   1326 			return (0);
   1327 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
   1328 			return (0);
   1329 		if (tc_action_parser(ifname, &cp, &action[3]) == 0)
   1330 			return (0);
   1331 
   1332 		if (qcmd_cdnr_add_tswtcm(action, ifname, NULL,
   1333 					 cmtd_rate, peak_rate, avg_interval,
   1334 					 &action[1], &action[2], &action[3])
   1335 		    != 0)
   1336 			return (0);
   1337 	} else {
   1338 		LOG(LOG_ERR, 0,
   1339 		    "Unkown action type %s in %s, line %d\n",
   1340 		    type, altqconfigfile, line_no);
   1341 		return (0);
   1342 	}
   1343 
   1344 	*end = '>';	/* restore the end delimiter */
   1345 
   1346 	return (1);
   1347 }
   1348 
   1349