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