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