xref: /netbsd-src/usr.sbin/altq/libaltq/parser.c (revision 5e4c038a45edbc7d63b7c2daa76e29f88b64a4e3)
1 /*	$NetBSD: parser.c,v 1.7 2002/03/05 04:11:53 itojun Exp $	*/
2 /*	$KAME: parser.c,v 1.16 2002/02/20 10:40:39 kjc Exp $	*/
3 /*
4  * Copyright (C) 1999-2002
5  *	Sony Computer Science Laboratories, Inc.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <net/if.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <stddef.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <syslog.h>
43 #include <netdb.h>
44 #include <err.h>
45 
46 #include <altq/altq.h>
47 #include <altq/altq_cdnr.h>
48 #include <altq/altq_red.h>
49 #include <altq/altq_rio.h>
50 #include "altq_qop.h"
51 #include "qop_cdnr.h"
52 
53 static int is_qdisc_name(const char *);
54 static int qdisc_interface_parser(const char *, const char *, int, char **);
55 static int qdisc_class_parser(const char *, const char *, const char *,
56 			      const char *, int, char **);
57 static int next_word(char **, char *);
58 
59 static int get_ifname(char **, char **);
60 static int get_addr(char **, struct in_addr *, struct in_addr *);
61 static int get_port(const char *, u_int16_t *);
62 static int get_proto(const char *, int *);
63 static int get_fltr_opts(char **, char *, size_t, int *);
64 static int interface_parser(char *);
65 static int class_parser(char *) ;
66 static int filter_parser(char *);
67 #ifdef INET6
68 static int filter6_parser(char *);
69 static int get_ip6addr(char **, struct in6_addr *, struct in6_addr *);
70 #endif
71 static int ctl_parser(char *);
72 static int delete_parser(char *);
73 static int red_parser(char *);
74 static int rio_parser(char *);
75 static int conditioner_parser(char *);
76 static int tc_action_parser(char *, char **, struct tc_action *);
77 
78 #define MAX_LINE	1024
79 #define MAX_WORD	64
80 #define MAX_ARGS	64
81 #define MAX_ACTIONS	16
82 
83 #ifndef MAX
84 #define MAX(a,b) (((a)>(b))?(a):(b))
85 #endif
86 #ifndef MIN
87 #define MIN(a,b) (((a)<(b))?(a):(b))
88 #endif
89 #define EQUAL(s1, s2)	(strcmp((s1), (s2)) == 0)
90 
91 int	line_no = 0;
92 int	filter_dontwarn;
93 
94 static char	curifname[IFNAMSIZ];
95 static struct if_nameindex *if_namelist = NULL;
96 
97 struct cmd_tab {
98 	const char	*cmd;
99 	int		(*parser)(char *);
100 	const char	*help;
101 } cmd_tab[] = {
102 	{"help",	NULL,	"help | ?"},
103 	{"quit",	NULL,	"quit"},
104 	{"interface",	interface_parser,	"interface if_name [bandwidth bps] [cbq|hfsc]"},
105 	{"class",	class_parser,	"class discipline if_name class_name [parent]"},
106 	{"filter",	filter_parser,	"filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]"},
107 	{"altq",	ctl_parser,	"altq if_name {enable|disable}"},
108 	{"delete",	delete_parser,	"delete if_name class_name [filter_name]"},
109 #ifdef INET6
110 	{"filter6",	filter6_parser,	"filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]"},
111 #endif
112 	{"red",		red_parser,	"red th_min th_max inv_pmax"},
113 	{"rio",		rio_parser,	"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"},
114 	{"conditioner",	conditioner_parser,	"conditioner if_name cdnr_name <tc_action>"},
115 	{"debug",	NULL,		"debug"},
116 	{NULL,		NULL,		NULL}	/* termination */
117 };
118 
119 /*
120  * read one line from the specified stream. if it's a command,
121  * execute the command.
122  * returns 1 if OK, 0 if error or EOF.
123  */
124 int
125 do_command(FILE *fp)
126 {
127 	char	cmd_line[MAX_LINE], cmd[MAX_WORD], *cp;
128 	struct cmd_tab *tp;
129 	int	len, rval;
130 
131 	/*
132 	 * read a line from the stream and make it a null-terminated string
133 	 */
134 	cp = cmd_line;
135 read_line:
136 	if (fgets(cp, &cmd_line[MAX_LINE] - cp, fp) == NULL)
137 		/* EOF or error */
138 		return(0);
139 	line_no++;
140 
141 	/* null-terminate the line */
142 	if ((len = strlen(cmd_line)) > 0) {
143 		cp = cmd_line + len - 1;
144 		if (*cp == '\n') {
145 			/* if escaped newline, read next line */
146 			if (len > 1 &&  *(cp - 1) == '\\')
147 				goto read_line;
148 			*cp = '\0';
149 		} else if (!feof(fp))
150 			err(1, "LINE %d too long!", line_no);
151 	}
152 	/* trim comments */
153 	if ((cp = strchr(cmd_line, '#')) != NULL)
154 		*cp = '\0';
155 
156 	cp = cmd_line;
157 	if ((len = next_word(&cp, cmd)) == 0)
158 		/* no command in this line */
159 		return (1);
160 
161 	/* fnind the corresponding parser */
162 	rval = 0;
163 	for (tp = cmd_tab; tp->cmd != NULL; tp++)
164 		if (strncmp(cmd, tp->cmd, len) == 0)
165 			break;
166 
167 	if (tp->cmd == NULL) {
168 		if (fp == stdin) {
169 			printf(" ?? %s\n", cmd);
170 			rval = 1;
171 		} else
172 			LOG(LOG_ERR, 0, "unknown command: %s", cmd);
173 		return (rval);
174 	}
175 
176 	if (tp->parser != NULL)
177 		rval = (*tp->parser)(cp);
178 	else {
179 		/* handle other commands */
180 		if (strcmp(tp->cmd, "quit") == 0)
181 			rval = 0;
182 		else if (strcmp(tp->cmd, "help") == 0 ||
183 			 strcmp(tp->cmd, "?") == 0) {
184 			for (tp = cmd_tab; tp->cmd != NULL; tp++)
185 				printf("%s\n", tp->help);
186 			rval = 1;
187 		} else if (strcmp(tp->cmd, "debug") == 0) {
188 			if (m_debug & DEBUG_ALTQ) {
189 				/* turn off verbose */
190 				l_debug = LOG_INFO;
191 				m_debug &= ~DEBUG_ALTQ;
192 			} else {
193 				/* turn on verbose */
194 				l_debug = LOG_DEBUG;
195 				m_debug |= DEBUG_ALTQ;
196 			}
197 			rval = 1;
198 		}
199 	}
200 	return (rval);
201 }
202 
203 static int
204 is_qdisc_name(const char *qname)
205 {
206 	struct qdisc_parser *qp;
207 
208 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
209 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
210 			return (1);
211 	return (0);
212 }
213 
214 static int
215 qdisc_interface_parser(const char * qname, const char *ifname,
216 		       int argc, char **argv)
217 {
218 	struct qdisc_parser *qp;
219 
220 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
221 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
222 			return (*qp->interface_parser)(ifname, argc, argv);
223 	return (0);
224 }
225 
226 static int
227 qdisc_class_parser(const char *qname, const char *ifname,
228 		   const char *class_name, const char *parent_name,
229 		   int argc, char **argv)
230 {
231 	struct qdisc_parser *qp;
232 	struct ifinfo	*ifinfo;
233 
234 	for (qp = qdisc_parser; qp->qname != NULL; qp++)
235 		if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) {
236 			if (qp->class_parser == NULL) {
237 				LOG(LOG_ERR, 0,
238 				    "class can't be specified for %s", qp->qname);
239 				return (0);
240 			}
241 			if ((ifinfo = ifname2ifinfo(ifname)) == NULL) {
242 				LOG(LOG_ERR, 0, "no such interface");
243 				return (0);
244 			}
245 			if (strncmp(ifinfo->qdisc->qname, qname,
246 				    strlen(ifinfo->qdisc->qname)) != 0) {
247 				LOG(LOG_ERR, 0,
248 				    "qname doesn't match the interface");
249 				return (0);
250 			}
251 			return (*qp->class_parser)(ifname, class_name,
252 						   parent_name, argc, argv);
253 		}
254 	return (0);
255 }
256 
257 /*
258  * read the config file
259  */
260 int
261 qcmd_config(void)
262 {
263 	FILE	*fp;
264 	int	rval;
265 
266 	if (if_namelist != NULL)
267 		if_freenameindex(if_namelist);
268 	if_namelist = if_nameindex();
269 	curifname[0] = '\0';
270 
271 	LOG(LOG_INFO, 0, "ALTQ config file is %s", altqconfigfile);
272 
273 	fp = fopen(altqconfigfile, "r");
274 	if (fp == NULL) {
275 		LOG(LOG_ERR, errno, "can't open %s", altqconfigfile, 0);
276 		return (QOPERR_INVAL);
277 	}
278 	line_no = 0;
279 	rval = 1;
280 	while (rval)
281 		rval = do_command(fp);
282 
283 	if (!feof(fp)) {
284 		LOG(LOG_ERR, 0, "Error in %s, line %d.  config failed.",
285 		    altqconfigfile, line_no);
286 		(void) qcmd_destroyall();
287 		rval = QOPERR_INVAL;
288 	} else
289 		rval = 0;
290 
291 	(void)fclose(fp);
292 	line_no = 0;
293 	return (rval);
294 }
295 
296 static int
297 next_word(char **cpp, char *b)
298 {
299 	char	*cp;
300 	int	i;
301 
302 	cp = *cpp;
303 	while (*cp == ' ' || *cp == '\t')
304 		cp++;
305 	for (i = 0; i < MAX_WORD - 1; i++) {
306 		if (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\0')
307 			break;
308 		*b++ = *cp++;
309 	}
310 	*b = '\0';
311 	*cpp = cp;
312 	return (i);
313 }
314 
315 char *
316 cur_ifname(void)
317 {
318 	return (curifname);
319 }
320 
321 u_int
322 get_ifindex(const char *ifname)
323 {
324 	struct if_nameindex *ifnp;
325 
326 	for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
327 		if (strcmp(ifname, ifnp->if_name) == 0)
328 			return (ifnp->if_index);
329 	return (0);
330 }
331 
332 static int
333 get_ifname(char **cpp, char **ifnamep)
334 {
335 	char w[MAX_WORD], *ocp;
336 	struct if_nameindex *ifnp;
337 
338 	ocp = *cpp;
339 	if (next_word(&ocp, w) && if_namelist != NULL)
340 		for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
341 			if (strcmp(w, ifnp->if_name) == 0) {
342 				/* if_name found. advance the word pointer */
343 				*cpp = ocp;
344 				strlcpy(curifname, w, sizeof(curifname));
345 				*ifnamep = curifname;
346 				return (1);
347 			}
348 
349 	/* this is not interface name. use one in the context. */
350 	if (curifname[0] == '\0')
351 		return (0);
352 	*ifnamep = curifname;
353 	return (1);
354 }
355 
356 /* set address and netmask in network byte order */
357 static int
358 get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask)
359 {
360 	char w[MAX_WORD], *ocp;
361 	struct in_addr tmp;
362 
363 	addr->s_addr = 0;
364 	mask->s_addr = 0xffffffff;
365 
366 	if (!next_word(cpp, w))
367 		return (0);
368 
369 	if (inet_aton((char *)w, &tmp) != 1) {
370 		/* try gethostbyname */
371 		struct hostent *h;
372 
373 		if ((h = gethostbyname(w)) == NULL ||
374 		    h->h_addrtype != AF_INET || h->h_length != 4)
375 			return (0);
376 		bcopy(h->h_addr, &tmp, (size_t)h->h_length);
377 	}
378 	addr->s_addr = tmp.s_addr;
379 
380 	/* check if netmask option is present */
381 	ocp = *cpp;
382 	if (next_word(&ocp, w) && EQUAL(w, "netmask")) {
383 		if (!next_word(&ocp, w))
384 			return (0);
385 		if (inet_aton((char *)w, (struct in_addr *)&tmp) != 1)
386 			return (0);
387 
388 		mask->s_addr = tmp.s_addr;
389 		*cpp = ocp;
390 		return (1);
391 	}
392 	/* no netmask option */
393 	return (1);
394 }
395 
396 /* returns service number in network byte order */
397 static int
398 get_port(const char *name, u_int16_t *port_no)
399 {
400 	struct servent *s;
401 	u_int16_t num;
402 
403 	if (isdigit(name[0])) {
404 		num = (u_int16_t)strtol(name, NULL, 0);
405 		*port_no = htons(num);
406 		return (1);
407 	}
408 
409 	if ((s = getservbyname(name, 0)) == NULL)
410 		return (0);
411 
412 	*port_no = (u_int16_t)s->s_port;
413 	return (1);
414 }
415 
416 static int
417 get_proto(const char *name, int *proto_no)
418 {
419 	struct protoent *p;
420 
421 	if (isdigit(name[0])) {
422 		*proto_no = (int)strtol(name, NULL, 0);
423 		return (1);
424 	}
425 
426 	if ((p = getprotobyname(name)) == NULL)
427 		return (0);
428 
429 	*proto_no = p->p_proto;
430 	return (1);
431 }
432 
433 static int
434 get_fltr_opts(char **cpp, char *fltr_name, size_t len, int *ruleno)
435 {
436 	char w[MAX_WORD], *ocp;
437 
438 	ocp = *cpp;
439 	while (next_word(&ocp, w)) {
440 		if (EQUAL(w, "name")) {
441 			if (!next_word(&ocp, w))
442 				return (0);
443 			strlcpy(fltr_name, w, len);
444 			*cpp = ocp;
445 		} else if (EQUAL(w, "ruleno")) {
446 			if (!next_word(&ocp, w))
447 				return (0);
448 			*ruleno = (int)strtol(w, NULL, 0);
449 			*cpp = ocp;
450 		} else
451 			break;
452 	}
453 	return (1);
454 }
455 
456 
457 #define	DISCIPLINE_NONE		0
458 
459 static int
460 interface_parser(char *cmdbuf)
461 {
462 	char	w[MAX_WORD], *ap, *cp = cmdbuf;
463 	char	*ifname, *argv[MAX_ARGS], qdisc_name[MAX_WORD];
464 	int     argc;
465 
466 	if (!get_ifname(&cp, &ifname)) {
467 		LOG(LOG_ERR, 0, "missing interface name");
468 		return (0);
469 	}
470 
471 	/* create argment list & look for scheduling discipline options. */
472 	snprintf(qdisc_name, sizeof qdisc_name, "null");
473 	argc = 0;
474 	ap = w;
475 	while (next_word(&cp, ap)) {
476 		if (is_qdisc_name(ap))
477 			strlcpy(qdisc_name, ap, sizeof qdisc_name);
478 
479 		argv[argc] = ap;
480 		ap += strlen(ap) + 1;
481 		argc++;
482 		if (argc >= MAX_ARGS) {
483 			LOG(LOG_ERR, 0, "too many args");
484 			return (0);
485 		}
486 	}
487 
488 	return qdisc_interface_parser(qdisc_name, ifname, argc, argv);
489 }
490 
491 
492 static int
493 class_parser(char *cmdbuf)
494 {
495 	char	w[MAX_WORD], *cp = cmdbuf;
496 	char 	*ifname, qdisc_name[MAX_WORD];
497 	char	class_name[MAX_WORD], parent_name[MAX_WORD];
498 	char	*clname = class_name;
499 	char	*parent = NULL;
500 	char	*argv[MAX_ARGS], *ap;
501 	int	argc;
502 
503 	/* get scheduling class */
504 	if (!next_word(&cp, qdisc_name)) {
505 		LOG(LOG_ERR, 0, "missing discipline");
506 		return (0);
507 	}
508 	if (!is_qdisc_name(qdisc_name)) {
509 		LOG(LOG_ERR, 0, "unknown discipline '%s'", qdisc_name);
510 		return (0);
511 	}
512 
513 	/* get interface name */
514 	if (!get_ifname(&cp, &ifname)) {
515 		LOG(LOG_ERR, 0, "missing interface name");
516 		return (0);
517 	}
518 
519 	/* get class name */
520 	if (!next_word(&cp, class_name)) {
521 		LOG(LOG_ERR, 0, "missing class name");
522 		return (0);
523 	}
524 
525 	/* get parent name */
526 	if (!next_word(&cp, parent_name)) {
527 		LOG(LOG_ERR, 0, "missing parent class");
528 		return (0);
529 	}
530 	if (!EQUAL(parent_name, "null") && !EQUAL(parent_name, "NULL"))
531 		parent = parent_name;
532 	else
533 		parent = NULL;
534 
535 	ap = w;
536 	argc = 0;
537 	while (next_word(&cp, ap)) {
538 		argv[argc] = ap;
539 		ap += strlen(ap) + 1;
540 		argc++;
541 		if (argc >= MAX_ARGS) {
542 			LOG(LOG_ERR, 0, "too many args");
543 			return (0);
544 		}
545 	}
546 
547 	return qdisc_class_parser(qdisc_name, ifname, clname, parent,
548 				  argc, argv);
549 }
550 
551 static int
552 filter_parser(char *cmdbuf)
553 {
554 	char 	w[MAX_WORD], *cp = cmdbuf;
555 	char 	*ifname, class_name[MAX_WORD], fltr_name[MAX_WORD];
556 	char	*flname = NULL;
557 	struct flow_filter	sfilt;
558 	int	protocol;
559 	u_char	tos, tosmask;
560 	int	ruleno;
561 	int	dontwarn = 0;
562 	int	error;
563 
564 	memset(&sfilt, 0, sizeof(sfilt));
565 	sfilt.ff_flow.fi_family = AF_INET;
566 
567 	if (!get_ifname(&cp, &ifname)) {
568 		LOG(LOG_ERR, 0, "missing interface name in filter command");
569 		return (0);
570 	}
571 
572 	if (!next_word(&cp, class_name)) {
573 		LOG(LOG_ERR, 0, "missing class name in filter command");
574 		return (0);
575 	}
576 
577 	fltr_name[0] = '\0';
578 	ruleno = 0;
579 	if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) {
580 		LOG(LOG_ERR, 0, "bad filter option");
581 		return (0);
582 	}
583 	if (fltr_name[0] != '\0')
584 		flname = fltr_name;
585 	sfilt.ff_ruleno = ruleno;
586 
587 	/* get filter destination Address */
588 	if (!get_addr(&cp, &sfilt.ff_flow.fi_dst, &sfilt.ff_mask.mask_dst)) {
589 		LOG(LOG_ERR, 0, "bad filter destination address");
590 		return (0);
591 	}
592 
593 	/* get filter destination port */
594 	if (!next_word(&cp, w)) {
595 		LOG(LOG_ERR, 0, "missing filter destination port");
596 		return (0);
597 	}
598 	if (!get_port(w, &sfilt.ff_flow.fi_dport)) {
599 		LOG(LOG_ERR, 0, "bad filter destination port");
600 		return (0);
601 	}
602 
603 	/* get filter source address */
604 	if (!get_addr(&cp, &sfilt.ff_flow.fi_src, &sfilt.ff_mask.mask_src)) {
605 		LOG(LOG_ERR, 0, "bad filter source address");
606 		return (0);
607 	}
608 
609 	/* get filter source port */
610 	if (!next_word(&cp, w)) {
611 		LOG(LOG_ERR, 0, "missing filter source port");
612 		return (0);
613 	}
614 	if (!get_port(w, &sfilt.ff_flow.fi_sport)) {
615 		LOG(LOG_ERR, 0, "bad filter source port");
616 		return (0);
617 	}
618 
619 	/* get filter protocol id */
620 	if (!next_word(&cp, w)) {
621 		LOG(LOG_ERR, 0, "missing filter protocol");
622 		return (0);
623 	}
624 	if (!get_proto(w, &protocol)) {
625 		LOG(LOG_ERR, 0, "bad protocol");
626 		return (0);
627 	}
628 	sfilt.ff_flow.fi_proto = protocol;
629 
630 	while (next_word(&cp, w)) {
631 		if (EQUAL(w, "tos")) {
632 			tos = 0;
633 			tosmask = 0xff;
634 
635 			if (next_word(&cp, w)) {
636 				tos = (u_char)strtol(w, NULL, 0);
637 				if (next_word(&cp, w)) {
638 					if (EQUAL(w, "tosmask")) {
639 						next_word(&cp, w);
640 						tosmask = (u_char)strtol(w, NULL, 0);
641 					}
642 				}
643 			}
644 			sfilt.ff_flow.fi_tos = tos;
645 			sfilt.ff_mask.mask_tos = tosmask;
646 		} else if (EQUAL(w, "gpi")) {
647 			if (next_word(&cp, w)) {
648 				sfilt.ff_flow.fi_gpi =
649 					(u_int32_t)strtoul(w, NULL, 0);
650 				sfilt.ff_flow.fi_gpi =
651 					htonl(sfilt.ff_flow.fi_gpi);
652 			}
653 		} else if (EQUAL(w, "dontwarn"))
654 			dontwarn = 1;
655 	}
656 
657 	/*
658 	 * Add the filter.
659 	 */
660 	filter_dontwarn = dontwarn;	/* XXX */
661 	error = qcmd_add_filter(ifname, class_name, flname, &sfilt);
662 	filter_dontwarn = 0;		/* XXX */
663 	if (error) {
664 		LOG(LOG_ERR, 0,
665 		    "can't add filter to class '%s' on interface '%s'",
666 		    class_name, ifname);
667 		return (0);
668 	}
669 	return (1);
670 }
671 
672 #ifdef INET6
673 static int
674 filter6_parser(char *cmdbuf)
675 {
676 	char 	w[MAX_WORD], *cp = cmdbuf;
677 	char 	*ifname, class_name[MAX_WORD], fltr_name[MAX_WORD];
678 	char	*flname = NULL;
679 	struct flow_filter6	sfilt;
680 	int	protocol;
681 	u_char	tclass, tclassmask;
682 	int	ruleno;
683 	int	dontwarn = 0;
684 	int	ret;
685 
686 	memset(&sfilt, 0, sizeof(sfilt));
687 	sfilt.ff_flow6.fi6_family = AF_INET6;
688 
689 	if (!get_ifname(&cp, &ifname)) {
690 		LOG(LOG_ERR, 0, "missing interface name");
691 		return (0);
692 	}
693 
694 	if (!next_word(&cp, class_name)) {
695 		LOG(LOG_ERR, 0, "missing class name");
696 		return (0);
697 	}
698 
699 	fltr_name[0] = '\0';
700 	ruleno = 0;
701 	if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) {
702 		LOG(LOG_ERR, 0, "bad filter option");
703 		return (0);
704 	}
705 	if (fltr_name[0] != '\0')
706 		flname = fltr_name;
707 	sfilt.ff_ruleno = ruleno;
708 
709 	/* get filter destination address */
710 	if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_dst,
711 			 &sfilt.ff_mask6.mask6_dst)) {
712 		LOG(LOG_ERR, 0, "bad destination address");
713 		return (0);
714 	}
715 
716 	/* get filter destination port */
717 	if (!next_word(&cp, w)) {
718 		LOG(LOG_ERR, 0, "missing filter destination port");
719 		return (0);
720 	}
721 	if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) {
722 		LOG(LOG_ERR, 0, "bad filter destination port");
723 		return (0);
724 	}
725 
726 	/* get filter source address */
727 	if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_src,
728 			 &sfilt.ff_mask6.mask6_src)) {
729 		LOG(LOG_ERR, 0, "bad source address");
730 		return (0);
731 	}
732 
733 	/* get filter source port */
734 	if (!next_word(&cp, w)) {
735 		LOG(LOG_ERR, 0, "missing filter source port");
736 		return (0);
737 	}
738 	if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) {
739 		LOG(LOG_ERR, 0, "bad filter source port");
740 		return (0);
741 	}
742 
743 	/* get filter protocol id */
744 	if (!next_word(&cp, w)) {
745 		LOG(LOG_ERR, 0, "missing filter protocol");
746 		return (0);
747 	}
748 	if (!get_proto(w, &protocol)) {
749 		LOG(LOG_ERR, 0, "bad protocol");
750 		return (0);
751 	}
752 	sfilt.ff_flow6.fi6_proto = protocol;
753 
754 	while (next_word(&cp, w)) {
755 		if (EQUAL(w, "tclass")) {
756 			tclass = 0;
757 			tclassmask = 0xff;
758 
759 			if (next_word(&cp, w)) {
760 				tclass = (u_char)strtol(w, NULL, 0);
761 				if (next_word(&cp, w)) {
762 					if (EQUAL(w, "tclassmask")) {
763 						next_word(&cp, w);
764 						tclassmask =
765 						    (u_char)strtol(w, NULL, 0);
766 					}
767 				}
768 			}
769 			sfilt.ff_flow6.fi6_tclass = tclass;
770 			sfilt.ff_mask6.mask6_tclass = tclassmask;
771 		} else if (EQUAL(w, "gpi")) {
772 			if (next_word(&cp, w)) {
773 				sfilt.ff_flow6.fi6_gpi =
774 					(u_int32_t)strtoul(w, NULL, 0);
775 				sfilt.ff_flow6.fi6_gpi =
776 					htonl(sfilt.ff_flow6.fi6_gpi);
777 			}
778 		} else if (EQUAL(w, "flowlabel")) {
779 			if (next_word(&cp, w)) {
780 				sfilt.ff_flow6.fi6_flowlabel =
781 				   (u_int32_t)strtoul(w, NULL, 0) & 0x000fffff;
782 				sfilt.ff_flow6.fi6_flowlabel =
783 					htonl(sfilt.ff_flow6.fi6_flowlabel);
784 			}
785 		} else if (EQUAL(w, "dontwarn"))
786 			dontwarn = 1;
787 	}
788 
789 	/*
790 	 * Add the filter.
791 	 */
792 	filter_dontwarn = dontwarn;	/* XXX */
793 	ret = qcmd_add_filter(ifname, class_name, flname,
794 			      (struct flow_filter *)&sfilt);
795 	filter_dontwarn = 0;		/* XXX */
796 	if (ret) {
797 		LOG(LOG_ERR, 0,
798 		    "can't add filter to class '%s' on interface '%s'",
799 		    class_name, ifname);
800 		return (0);
801 	}
802 
803 	return (1);
804 }
805 
806 static int
807 get_ip6addr(char **cpp, struct in6_addr *addr, struct in6_addr *mask)
808 {
809 	char w[MAX_WORD], *prefix;
810 	u_char *cp;
811 	int len;
812 
813 	*addr = in6addr_any;  /* set all 0 */
814 	*mask = in6addr_any;  /* set all 0 */
815 
816 	if (!next_word(cpp, w))
817 		return (0);
818 
819 	if (EQUAL(w, "0"))
820 		/* abbreviation of a wildcard (::0) */
821 		return (1);
822 
823 	if ((prefix = strchr(w, '/')) != NULL) {
824 		/* address has prefix length */
825 		*prefix++ = '\0';
826 	}
827 
828 	if (inet_pton(AF_INET6, w, addr) != 1)
829 		return (0);
830 
831 	if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefix == NULL)
832 		/* wildcard */
833 		return (1);
834 
835 	/* convert address prefix length to address mask */
836 	if (prefix != NULL) {
837 		len = (int)strtol(prefix, NULL, 0);
838 		if ((len < 0) || (len > 128))
839 			return (0);
840 		for (cp = (u_char *)mask; len > 7; len -= 8)
841 			*cp++ = 0xff;
842 		if (len > 0)
843 			*cp = (0xff << (8 - len)) & 0xff;
844 
845 		IN6ADDR32(addr, 0) &= IN6ADDR32(mask, 0);
846 		IN6ADDR32(addr, 1) &= IN6ADDR32(mask, 1);
847 		IN6ADDR32(addr, 2) &= IN6ADDR32(mask, 2);
848 		IN6ADDR32(addr, 3) &= IN6ADDR32(mask, 3);
849 	} else
850 		/* full mask */
851 		memset(mask, 0xff, sizeof(struct in6_addr));
852 
853 	return (1);
854 }
855 
856 #endif /* INET6 */
857 
858 static int
859 ctl_parser(char *cmdbuf)
860 {
861 	char	w[MAX_WORD], *cp = cmdbuf;
862 	char	*ifname;
863 	int	state;
864 	int	rval;
865 
866 	if (!get_ifname(&cp, &ifname)) {
867 		printf("missing interface name in %s, line %d",
868 		       altqconfigfile, line_no);
869 		return (0);
870 	}
871 
872 	if (!next_word(&cp, w)) {
873 		state = is_q_enabled(ifname);
874 		printf("altq %s on %s\n",
875 		       state ? "enabled" : "disabled", ifname);
876 		return (1);
877 	}
878 
879 	if (EQUAL(w, "enable")) {
880 		rval = qcmd_enable(ifname);
881 		printf("altq %s on %s\n",
882 		       (rval == 0) ? "enabled" : "enable failed!", ifname);
883 	} else if (EQUAL(w, "disable")) {
884 		rval = qcmd_disable(ifname);
885 		printf("altq %s on %s\n",
886 		       (rval == 0) ? "disabled" : "disable failed!", ifname);
887 	} else if (EQUAL(w, "reload")) {
888 		printf("reinitializing altq...\n");
889 		qcmd_destroyall();
890 		qcmd_init();
891 	} else
892 		return (0);
893 	return (1);
894 }
895 
896 static int
897 delete_parser(char *cmdbuf)
898 {
899 	char	*cp = cmdbuf;
900 	char	*ifname, class_name[MAX_WORD], filter_name[MAX_WORD];
901 	int	ret;
902 
903 	if (!get_ifname(&cp, &ifname)) {
904 		LOG(LOG_ERR, 0, "missing interface name");
905 		return (0);
906 	}
907 
908 	if (!next_word(&cp, class_name)) {
909 		LOG(LOG_ERR, 0, "missing class name");
910 		return (0);
911 	}
912 
913 	/* check if filter is specified */
914 	if (next_word(&cp, filter_name)) {
915 		ret = qcmd_delete_filter(ifname, class_name, filter_name);
916 		if (ret) {
917 			LOG(LOG_ERR, 0,
918 			    "can't delete filter '%s' on interface '%s'",
919 			    filter_name, ifname);
920 			return (0);
921 		}
922 		return (1);
923 	}
924 
925 	ret = qcmd_delete_class(ifname, class_name);
926 	if (ret) {
927 		LOG(LOG_ERR, 0,
928 		    "can't delete class '%s' on interface '%s'",
929 		    class_name, ifname);
930 		return (0);
931 	}
932 
933 	return (1);
934 }
935 
936 static int
937 red_parser(char *cmdbuf)
938 {
939 	char	w[MAX_WORD], *cp = cmdbuf;
940 	int th_min, th_max, inv_pmax;
941 
942 	if (!next_word(&cp, w))
943 		goto bad;
944 	th_min = (int)strtol(w, NULL, 0);
945 
946 	if (!next_word(&cp, w))
947 		goto bad;
948 	th_max = (int)strtol(w, NULL, 0);
949 
950 	if (!next_word(&cp, w))
951 		goto bad;
952 	inv_pmax = (int)strtol(w, NULL, 0);
953 
954 	if (qop_red_set_defaults(th_min, th_max, inv_pmax) != 0) {
955 		LOG(LOG_ERR, 0, "can't set red default parameters");
956 		return (0);
957 	}
958 
959 	return (1);
960 
961  bad:
962 	LOG(LOG_ERR, 0, "bad red parameter");
963 	return (0);
964 }
965 
966 static int
967 rio_parser(char *cmdbuf)
968 {
969 	char	w[MAX_WORD], *cp = cmdbuf;
970 	int	i;
971 	struct redparams params[RIO_NDROPPREC];
972 
973 	for (i = 0; i < RIO_NDROPPREC; i++) {
974 		if (!next_word(&cp, w))
975 			goto bad;
976 		params[i].th_min = (int)strtol(w, NULL, 0);
977 
978 		if (!next_word(&cp, w))
979 			goto bad;
980 		params[i].th_max = (int)strtol(w, NULL, 0);
981 
982 		if (!next_word(&cp, w))
983 			goto bad;
984 		params[i].inv_pmax = (int)strtol(w, NULL, 0);
985 	}
986 
987 	if (qop_rio_set_defaults(&params[0]) != 0) {
988 		LOG(LOG_ERR, 0, "can't set rio default parameters");
989 		return (0);
990 	}
991 
992 	return (1);
993 
994  bad:
995 	LOG(LOG_ERR, 0, "bad rio parameter");
996 	return (0);
997 }
998 
999 static int
1000 conditioner_parser(char *cmdbuf)
1001 {
1002 	char	cdnr_name[MAX_WORD], *cp = cmdbuf;
1003 	char	*ifname;
1004 	struct tc_action action[MAX_ACTIONS];
1005 
1006 	if (!get_ifname(&cp, &ifname)) {
1007 		LOG(LOG_ERR, 0, "missing interface name");
1008 		return (0);
1009 	}
1010 
1011 	/* get conditioner name */
1012 	if (!next_word(&cp, cdnr_name)) {
1013 		LOG(LOG_ERR, 0, "missing cdnr name");
1014 		return (0);
1015 	}
1016 
1017 	if (tc_action_parser(ifname, &cp, &action[0]) == 0)
1018 		return (0);
1019 
1020 	if (qcmd_cdnr_add_element(NULL, ifname, cdnr_name, &action[0]) != 0)
1021 		return (0);
1022 	return (1);
1023 }
1024 
1025 /*
1026  * recursively parse '<'tc_action'>'
1027  * note that array "action" grows during recursive parse.
1028  */
1029 static int
1030 tc_action_parser(char *ifname, char **cpp, struct tc_action *action)
1031 {
1032 	char	*cp, *start, *end;
1033 	char	type[MAX_WORD], w[MAX_WORD];
1034 	int	depth, i;
1035 	struct tb_profile profile[2];
1036 
1037 	/*
1038 	 * find a possibly nested pair of '<' and '>',
1039 	 * make them pointed by 'start' and 'end'.
1040 	 */
1041 	start = strchr(*cpp, '<');
1042 	if (start == NULL) {
1043 		LOG(LOG_ERR, 0, "conditioner action missing");
1044 		return (0);
1045 	}
1046 	depth = 1;
1047 	cp = start + 1;
1048 	do {
1049 		end = strpbrk(cp, "<>");
1050 		if (end == NULL) {
1051 			LOG(LOG_ERR, 0,
1052 			    "conditioner action delimiter mismatch");
1053 			return (0);
1054 		}
1055 		if (*end == '<')
1056 			depth++;
1057 		else if (*end == '>')
1058 			depth--;
1059 		cp = end + 1;
1060 	} while (depth > 0);
1061 	*end = '\0';
1062 	*cpp = end + 1;
1063 	cp = start + 1;
1064 
1065 	if (IsDebug(DEBUG_ALTQ)) {
1066 		printf("tc_action_parser: [%s]\n", cp);
1067 	}
1068 
1069 	if (!next_word(&cp, type)) {
1070 		LOG(LOG_ERR, 0, "missing conditioner action type");
1071 		return (0);
1072 	}
1073 
1074 	/*
1075 	 * action type specific process
1076 	 */
1077 	if (EQUAL(type, "conditioner")) {
1078 		if (!next_word(&cp, w)) {
1079 			LOG(LOG_ERR, 0,
1080 			    "missing conditioner name");
1081 			return (0);
1082 		}
1083 		action->tca_code = TCACODE_HANDLE;
1084 		action->tca_handle = cdnr_name2handle(ifname, w);
1085 		if (action->tca_handle == CDNR_NULL_HANDLE) {
1086 			LOG(LOG_ERR, 0,
1087 			    "wrong conditioner name %s", w);
1088 			return (0);
1089 		}
1090 	} else if (EQUAL(type, "pass")) {
1091 		action->tca_code = TCACODE_PASS;
1092 	} else if (EQUAL(type, "drop")) {
1093 		action->tca_code = TCACODE_DROP;
1094 	} else if (EQUAL(type, "mark")) {
1095 		if (!next_word(&cp, w)) {
1096 			LOG(LOG_ERR, 0, "missing dscp");
1097 			return (0);
1098 		}
1099 		action->tca_code = TCACODE_MARK;
1100 		action->tca_dscp = (u_int8_t)strtol(w, NULL, 0);
1101 	} else if (EQUAL(type, "tbmeter")) {
1102 		if (!next_word(&cp, w)) {
1103 			LOG(LOG_ERR, 0, "missing tb profile");
1104 			return (0);
1105 		}
1106 		profile[0].rate = atobps(w);
1107 		if (!next_word(&cp, w)) {
1108 			LOG(LOG_ERR, 0, "missing tb profile");
1109 			return (0);
1110 		}
1111 		profile[0].depth = atobytes(w);
1112 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
1113 			return (0);
1114 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
1115 			return (0);
1116 
1117 		if (qcmd_cdnr_add_tbmeter(action, ifname, NULL, &profile[0],
1118 					  &action[1], &action[2]) != 0)
1119 			return (0);
1120 	} else if (EQUAL(type, "trtcm")) {
1121 		int coloraware = 0;	/* default is color-blind */
1122 
1123 		for (i=0; i<2; i++) {
1124 			if (!next_word(&cp, w)) {
1125 				LOG(LOG_ERR, 0, "missing tb profile");
1126 				return (0);
1127 			}
1128 			profile[i].rate = atobps(w);
1129 			if (!next_word(&cp, w)) {
1130 				LOG(LOG_ERR, 0, "missing tb profile");
1131 				return (0);
1132 			}
1133 			profile[i].depth = atobytes(w);
1134 		}
1135 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
1136 			return (0);
1137 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
1138 			return (0);
1139 		if (tc_action_parser(ifname, &cp, &action[3]) == 0)
1140 			return (0);
1141 		if (next_word(&cp, w)) {
1142 			if (EQUAL(w, "coloraware"))
1143 				coloraware = 1;
1144 			else if (EQUAL(w, "colorblind"))
1145 				coloraware = 0;
1146 		}
1147 
1148 		if (qcmd_cdnr_add_trtcm(action, ifname, NULL,
1149 					&profile[0], &profile[1],
1150 					&action[1], &action[2], &action[3],
1151 					coloraware) != 0)
1152 			return (0);
1153 	} else if (EQUAL(type, "tswtcm")) {
1154 		u_int32_t cmtd_rate, peak_rate, avg_interval;
1155 
1156 		if (!next_word(&cp, w)) {
1157 			LOG(LOG_ERR, 0, "missing cmtd rate");
1158 			return (0);
1159 		}
1160 		cmtd_rate = atobps(w);
1161 
1162 		if (!next_word(&cp, w)) {
1163 			LOG(LOG_ERR, 0, "missing peak rate");
1164 			return (0);
1165 		}
1166 		peak_rate = atobps(w);
1167 
1168 		if (!next_word(&cp, w)) {
1169 			LOG(LOG_ERR, 0, "missing avg interval");
1170 			return (0);
1171 		}
1172 		avg_interval = (u_int32_t)strtoul(w, NULL, 0);
1173 
1174 		if (tc_action_parser(ifname, &cp, &action[1]) == 0)
1175 			return (0);
1176 		if (tc_action_parser(ifname, &cp, &action[2]) == 0)
1177 			return (0);
1178 		if (tc_action_parser(ifname, &cp, &action[3]) == 0)
1179 			return (0);
1180 
1181 		if (qcmd_cdnr_add_tswtcm(action, ifname, NULL,
1182 					 cmtd_rate, peak_rate, avg_interval,
1183 					 &action[1], &action[2], &action[3])
1184 		    != 0)
1185 			return (0);
1186 	} else {
1187 		LOG(LOG_ERR, 0, "unkown action type %s");
1188 		return (0);
1189 	}
1190 
1191 	*end = '>';	/* restore the end delimiter */
1192 
1193 	return (1);
1194 }
1195