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