xref: /netbsd-src/usr.sbin/altq/libaltq/parser.c (revision 3b01aba77a7a698587faaae455bbfe740923c1f5)
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 	struct in_addr 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 (inet_aton((char *)w, &tmp) != 1) {
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.s_addr;
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) != 1)
477 			return (0);
478 
479 		mask->s_addr = tmp.s_addr;
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 	snprintf(qdisc_name, sizeof(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) != 1)
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