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