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