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(¶ms[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