xref: /netbsd-src/usr.sbin/altq/libaltq/parser.c (revision 915340d5d241e5f0a8d9654a50b2a9f091efe4a1)
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 
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	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 
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 	{"?",		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  */
125 int
do_command(FILE * fp)126 do_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;
136 read_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 
204 static int
is_qdisc_name(const char * qname)205 is_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 
215 static int
qdisc_interface_parser(const char * qname,const char * ifname,int argc,char ** argv)216 qdisc_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 
227 static int
qdisc_class_parser(const char * qname,const char * ifname,const char * class_name,const char * parent_name,int argc,char ** argv)228 qdisc_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  */
261 int
qcmd_config(void)262 qcmd_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 
297 static int
next_word(char ** cpp,char * b)298 next_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 
316 char *
cur_ifname(void)317 cur_ifname(void)
318 {
319 	return (curifname);
320 }
321 
322 u_int
get_ifindex(const char * ifname)323 get_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 
333 static int
get_ifname(char ** cpp,char ** ifnamep)334 get_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 */
358 static int
get_addr(char ** cpp,struct in_addr * addr,struct in_addr * mask)359 get_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 */
398 static int
get_port(const char * name,u_int16_t * port_no)399 get_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 
417 static int
get_proto(const char * name,int * proto_no)418 get_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 
434 static int
get_fltr_opts(char ** cpp,char * fltr_name,size_t len,int * ruleno)435 get_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 
460 static int
interface_parser(char * cmdbuf)461 interface_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 
493 static int
class_parser(char * cmdbuf)494 class_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 
552 static int
filter_parser(char * cmdbuf)553 filter_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
674 static int
filter6_parser(char * cmdbuf)675 filter6_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 
807 static int
get_ip6addr(char ** cpp,struct in6_addr * addr,struct in6_addr * mask)808 get_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 
863 static int
ctl_parser(char * cmdbuf)864 ctl_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 
901 static int
delete_parser(char * cmdbuf)902 delete_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 
941 static int
red_parser(char * cmdbuf)942 red_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 
971 static int
rio_parser(char * cmdbuf)972 rio_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 
1004 static int
conditioner_parser(char * cmdbuf)1005 conditioner_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  */
1034 static int
tc_action_parser(char * ifname,char ** cpp,struct tc_action * action)1035 tc_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