xref: /netbsd-src/usr.sbin/altq/libaltq/parser.c (revision 27578b9aac214cc7796ead81dcc5427e79d5f2a0)
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