parser.c revision 1.1 1 /* $KAME: parser.c,v 1.5 2000/10/18 09:15:18 kjc Exp $ */
2 /*******************************************************************
3
4 Copyright (c) 1996 by the University of Southern California
5 All rights reserved.
6
7 Permission to use, copy, modify, and distribute this software and its
8 documentation in source and binary forms for any purpose and without
9 fee is hereby granted, provided that both the above copyright notice
10 and this permission notice appear in all copies. and that any
11 documentation, advertising materials, and other materials related to
12 such distribution and use acknowledge that the software was developed
13 in part by the University of Southern California, Information
14 Sciences Institute. The name of the University may not be used to
15 endorse or promote products derived from this software without
16 specific prior written permission.
17
18 THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
19 the suitability of this software for any purpose. THIS SOFTWARE IS
20 PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
21 INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
22 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23
24 Other copyrights might apply to parts of this software and are so
25 noted when applicable.
26
27 ********************************************************************/
28
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <stddef.h>
34 #include <string.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <syslog.h>
38 #include <sys/socket.h>
39 #include <netdb.h>
40 #include <net/if.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43
44 #include <altq/altq.h>
45 #include <altq/altq_cdnr.h>
46 #include <altq/altq_red.h>
47 #include <altq/altq_rio.h>
48 #include "altq_qop.h"
49 #include "qop_cdnr.h"
50 #include "if_nameindex.h"
51
52 #define show_help(op) printf(cmd_tab[op].cmd_help)
53
54 /*
55 * Forward & External Declarations
56 */
57 static int is_qdisc_name(const char *qname);
58 static int qdisc_interface_parser(const char * qname, const char *ifname,
59 int argc, char **argv);
60 static int qdisc_class_parser(const char *qname, const char *ifname,
61 const char *class_name, const char *parent_name,
62 int argc, char **argv);
63
64 static int pfxcmp(const char *s1, const char *s2);
65 static int next_word(char **cpp, char *b);
66
67 static int do_cmd(int op, char *cmdbuf);
68 static int get_ifname(char **cpp, char **ifnamep);
69 static int get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask);
70 static int get_port(const char *name, u_int16_t *port_no);
71 static int get_proto(const char *name, int *proto_no);
72 static int get_fltr_opts(char **cpp, char *fltr_name, int *ruleno);
73 static int interface_parser(char *cmdbuf);
74 static int class_parser(char *cmdbuf) ;
75 static int filter_parser(char *cmdbuf);
76 #ifdef INET6
77 static int filter6_parser(char *cmdbuf);
78 static int get_ip6addr(char **cpp, struct in6_addr *addr,
79 struct in6_addr *mask);
80 #endif
81 static int ctl_parser(char *cmdbuf);
82 static int delete_parser(char *cmdbuf);
83 static int red_parser(char *cmdbuf);
84 static int rio_parser(char *cmdbuf);
85 static int conditioner_parser(char *cmdbuf);
86 static int tc_action_parser(char *ifname, char **cpp,
87 struct tc_action *action);
88
89 /*
90 * Globals
91 */
92 #define MAX_NFLWDS 64
93 #define MAX_T 64
94
95 int TNO = 1; /* Current Thread number */
96 int line_no = 0;
97 int filter_dontwarn;
98
99 static char if_names[MAX_T][IFNAMSIZ];
100 static struct if_nameindex *if_namelist = NULL;
101
102 #ifndef MAX
103 #define MAX(a,b) (((a)>(b))?(a):(b))
104 #endif
105 #ifndef MIN
106 #define MIN(a,b) (((a)<(b))?(a):(b))
107 #endif
108
109 enum op_codes {
110 /* order must be same as entries cmd_tab[].cmd_op below!! */
111 OP_HELP = 1, OP_QUIT,
112 OP_IFACE, OP_CLASS, OP_FILTER,
113 OP_ALTQ, OP_DEL,
114 #ifdef INET6
115 OP_FILTER6,
116 #endif
117 OP_RED, OP_RIO,
118 OP_CDNR,
119 OP_NULL, OP_BUG
120 };
121
122 /* Following table MUST match enum order of op_codes !
123 */
124 struct cmds {
125 char *cmd_verb;
126 int cmd_op;
127 char *cmd_help;
128 } cmd_tab[] = {
129
130 { "?", OP_HELP, "Commands are:\n" },
131 { "help", OP_HELP, " help | ?\n" },
132 { "quit", OP_QUIT, " quit\n" },
133 { "interface", OP_IFACE, " interface if_name [bandwidth bps] [cbq|hfsc]\n" },
134 { "class", OP_CLASS, " class discipline if_name class_name [parent]\n" },
135 { "filter", OP_FILTER, " filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]\n" },
136 { "altq", OP_ALTQ, " disc if_name {enable|disable}\n" },
137 { "delete", OP_DEL, " delete if_name class_name\n" },
138 #ifdef INET6
139 { "filter6", OP_FILTER6, " filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]\n" },
140 #endif
141 { "red", OP_RED, " red th_min th_max inv_pmax\n" },
142 { "rio", OP_RIO, " 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\n" },
143 { "conditioner", OP_CDNR, " conditioner if_name cdnr_name <tc_action>\n" },
144 { "bug", OP_BUG, " bug (On/Off)\n" },
145 { "", OP_NULL, "" } /* MUST BE LAST IN CMD TABLE */
146 };
147
148 static int
149 is_qdisc_name(const char *qname)
150 {
151 struct qdisc_parser *qp;
152
153 for (qp = qdisc_parser; qp->qname != NULL; qp++)
154 if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
155 return (1);
156 return (0);
157 }
158
159 static int
160 qdisc_interface_parser(const char * qname, const char *ifname,
161 int argc, char **argv)
162 {
163 struct qdisc_parser *qp;
164
165 for (qp = qdisc_parser; qp->qname != NULL; qp++)
166 if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0)
167 return (*qp->interface_parser)(ifname, argc, argv);
168 return (0);
169 }
170
171 static int
172 qdisc_class_parser(const char *qname, const char *ifname,
173 const char *class_name, const char *parent_name,
174 int argc, char **argv)
175 {
176 struct qdisc_parser *qp;
177
178 for (qp = qdisc_parser; qp->qname != NULL; qp++)
179 if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) {
180 if (qp->class_parser == NULL) {
181 LOG(LOG_ERR, 0,
182 "class can't be specified for %s",
183 qp->qname);
184 return (0);
185 }
186 return (*qp->class_parser)(ifname, class_name,
187 parent_name, argc, argv);
188 }
189 return (0);
190 }
191
192
193 /*
194 * Read the config file to learn about tunnel vifs and non-default phyint
195 * parameters.
196 */
197 int
198 qcmd_config(void)
199 {
200 FILE *f;
201 int i, rc = 1;
202
203 if (if_namelist != NULL)
204 if_freenameindex(if_namelist);
205 if_namelist = if_nameindex();
206
207 for (i = 0; i < MAX_T; i++)
208 if_names[i][0] = '\0';
209
210 LOG(LOG_INFO, 0, "ALTQ config file is %s\n", altqconfigfile);
211
212 f = fopen(altqconfigfile, "r");
213 if (f == NULL) {
214 LOG(LOG_ERR, errno, "Can't open %s", altqconfigfile, 0);
215 return (QOPERR_INVAL);
216 }
217 line_no = 0;
218 while (rc)
219 rc = DoCommand(altqconfigfile, f);
220
221 (void) fclose(f);
222 line_no = 0;
223 return (0);
224 }
225
226 /*
227 * Do_Command(): Top-level routine to read the next line from a given
228 * file and execute the command it contains.
229 * returns 1 if OK, 0 if EOF.
230 */
231 int
232 DoCommand(char *infile, FILE *infp)
233 {
234 char cmd_line[256], cmd_op[80];
235 struct cmds *cmdp;
236 char *cp;
237 int rc;
238
239 if (fgets(cmd_line, sizeof(cmd_line), infp) == NULL)
240 /* EOF */
241 return(0);
242 line_no++;
243
244 /* check escaped newline */
245 while ((cp = strrchr(cmd_line, '\\')) != NULL && cp[1] == '\n') {
246 if (fgets(cp, &cmd_line[256] - cp, infp) != NULL)
247 line_no++;
248 }
249
250 /* remove trailing NL */
251 cp = cmd_line + strlen(cmd_line) - 1;
252 if (*cp == '\n')
253 *cp = '\0';
254 else if (!feof(infp)) {
255 printf("LINE %d > 255 CHARS: %s.\n", line_no, cmd_line);
256 exit(1);
257 }
258 /*** printf("DoCommand: %s\n", cmd_line); ***/
259
260 if (cmd_line[0] == '#') { /* Comment, skip this line */
261 return(1);
262 }
263 cp = cmd_line;
264 if (!next_word(&cp, cmd_op))
265 return(1);
266 if (cmd_op[0] == 'T') {
267 TNO = atoi(&cmd_op[1]);
268 if (!next_word(&cp, cmd_op))
269 return(1);
270 }
271 cmdp = cmd_tab;
272 while ((cmdp->cmd_op != OP_NULL) && pfxcmp(cmd_op, cmdp->cmd_verb))
273 cmdp++;
274
275 if (cmdp->cmd_op == OP_NULL) {
276 if (cmd_op[0])
277 printf(" ?? %s\n", cmd_op);
278 return(1);
279 }
280 rc = do_cmd(cmdp->cmd_op, cp);
281 if (rc == 0) {
282 if (infile) {
283 /* error in the config file. cleanup and exit. */
284 LOG(LOG_ERR, 0, "Config failed. Exiting.\n");
285 (void) qcmd_destroyall();
286 (void) fclose(infp);
287 exit(1);
288 } else {
289 /* interactive mode */
290 printf("error: usage :");
291 show_help(cmdp->cmd_op);
292 }
293 }
294 return(1);
295 }
296
297
298 /*
299 * Prefix string comparison: Return 0 if s1 string is prefix of s2 string, 1
300 * otherwise.
301 */
302 static int
303 pfxcmp(const char *s1, const char *s2)
304 {
305 while (*s1)
306 if (*s1++ != *s2++)
307 return (1);
308 return (0);
309 }
310
311 /*
312 * Skip leading blanks, then copy next word (delimited by blank or zero, but
313 * no longer than 63 bytes) into buffer b, set scan pointer to following
314 * non-blank (or end of string), and return 1. If there is no non-blank text,
315 * set scan ptr to point to 0 byte and return 0.
316 */
317 static int
318 next_word(char **cpp, char *b)
319 {
320 char *tp;
321 size_t L;
322
323 *cpp += strspn(*cpp, " \t");
324 if (**cpp == '\0' || **cpp == '\n' || **cpp == '#')
325 return(0);
326
327 tp = strpbrk(*cpp, " \t\n#");
328 L = MIN((tp)?(tp-*cpp):strlen(*cpp), 63);
329 strncpy(b, *cpp, L);
330 *(b + L) = '\0';
331 *cpp += L;
332 *cpp += strspn(*cpp, " \t");
333 return (1);
334 }
335
336 /*
337 * do_cmd executes a command input.
338 * returns 1 if OK, 0 if an error occurs.
339 */
340 static int
341 do_cmd(int op, char *cmdbuf)
342 {
343 int i, rval = 0;
344
345 switch (op) {
346 case OP_HELP:
347 for (i = 0; i < OP_NULL; i++)
348 show_help(i);
349 rval = 1;
350 break;
351 case OP_QUIT:
352 qcmd_destroyall();
353 exit(0);
354 break;
355 case OP_IFACE:
356 rval = interface_parser(cmdbuf);
357 break;
358 case OP_CLASS:
359 rval = class_parser(cmdbuf);
360 break;
361 case OP_FILTER:
362 rval = filter_parser(cmdbuf);
363 break;
364 case OP_ALTQ:
365 rval = ctl_parser(cmdbuf);
366 break;
367 case OP_DEL:
368 rval = delete_parser(cmdbuf);
369 break;
370 #ifdef INET6
371 case OP_FILTER6:
372 rval = filter6_parser(cmdbuf);
373 break;
374 #endif
375 case OP_RED:
376 rval = red_parser(cmdbuf);
377 break;
378 case OP_RIO:
379 rval = rio_parser(cmdbuf);
380 break;
381 case OP_CDNR:
382 rval = conditioner_parser(cmdbuf);
383 break;
384 case OP_BUG:
385 if (m_debug & DEBUG_ALTQ) {
386 /* turn off verbose */
387 l_debug = LOG_INFO;
388 m_debug &= ~DEBUG_ALTQ;
389 } else {
390 /* turn on verbose */
391 l_debug = LOG_DEBUG;
392 m_debug |= DEBUG_ALTQ;
393 }
394 break;
395 default:
396 printf("command %d not supported\n", op);
397 rval = 0;
398 break;
399 }
400 return(rval);
401 }
402
403 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
404
405 char *cur_ifname(void)
406 {
407 return (if_names[TNO]);
408 }
409
410 u_int
411 get_ifindex(const char *ifname)
412 {
413 struct if_nameindex *ifnp;
414
415 for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
416 if (strcmp(ifname, ifnp->if_name) == 0)
417 return (ifnp->if_index);
418 return (0);
419 }
420
421 static int
422 get_ifname(char **cpp, char **ifnamep)
423 {
424 char w[128], *ocp;
425 struct if_nameindex *ifnp;
426
427 ocp = *cpp;
428 if (next_word(&ocp, w) && if_namelist != NULL)
429 for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++)
430 if (strcmp(w, ifnp->if_name) == 0) {
431 /* if_name found. advance the word pointer */
432 *cpp = ocp;
433 strcpy(if_names[TNO], w);
434 *ifnamep = if_names[TNO];
435 return (1);
436 }
437
438 /* this is not interface name. use one in the context. */
439 if (if_names[TNO][0] == 0)
440 return (0);
441 *ifnamep = if_names[TNO];
442 return (1);
443 }
444
445 /* set address and netmask in network byte order */
446 static int
447 get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask)
448 {
449 char w[128], *ocp;
450 u_long tmp;
451
452 addr->s_addr = 0;
453 mask->s_addr = 0xffffffff;
454
455 if (!next_word(cpp, w))
456 return (0);
457
458 if ((tmp = inet_addr((char *)w)) == INADDR_NONE) {
459 /* try gethostbyname */
460 struct hostent *h;
461
462 if ((h = gethostbyname(w)) == NULL
463 || h->h_addrtype != AF_INET || h->h_length != 4)
464 return (0);
465
466 bcopy(h->h_addr, &tmp, (size_t)h->h_length);
467 }
468
469 addr->s_addr = tmp;
470
471 /* check if netmask option is present */
472 ocp = *cpp;
473 if (next_word(&ocp, w) && EQUAL(w, "netmask")) {
474 if (!next_word(&ocp, w))
475 return (0);
476
477 if (inet_aton((char *)w, (struct in_addr *)&tmp) == 0)
478 return (0);
479
480 mask->s_addr = tmp;
481 *cpp = ocp;
482 return (1);
483 }
484 /* no netmask option */
485 return (1);
486 }
487
488 /* returns service number in network byte order */
489 static int
490 get_port(const char *name, u_int16_t *port_no)
491 {
492 struct servent *s;
493 u_int16_t num;
494
495 if (isdigit(name[0])) {
496 num = (u_int16_t)strtol(name, NULL, 0);
497 *port_no = htons(num);
498 return (1);
499 }
500
501 if ((s = getservbyname(name, 0)) == NULL)
502 return (0);
503
504 *port_no = (u_int16_t)s->s_port;
505 return (1);
506 }
507
508 static int
509 get_proto(const char *name, int *proto_no)
510 {
511 struct protoent *p;
512
513 if (isdigit(name[0])) {
514 *proto_no = (int)strtol(name, NULL, 0);
515 return (1);
516 }
517
518 if ((p = getprotobyname(name)) == NULL)
519 return (0);
520
521 *proto_no = p->p_proto;
522 return (1);
523 }
524
525 static int
526 get_fltr_opts(char **cpp, char *fltr_name, int *ruleno)
527 {
528 char w[128], *ocp;
529
530 ocp = *cpp;
531 while (next_word(&ocp, w)) {
532 if (EQUAL(w, "name")) {
533 if (!next_word(&ocp, w))
534 return (0);
535 strcpy(fltr_name, w);
536 *cpp = ocp;
537 } else if (EQUAL(w, "ruleno")) {
538 if (!next_word(&ocp, w))
539 return (0);
540 *ruleno = (int)strtol(w, NULL, 0);
541 *cpp = ocp;
542 } else
543 break;
544 }
545 return (1);
546 }
547
548
549 #define DISCIPLINE_NONE 0
550
551 static int
552 interface_parser(char *cmdbuf)
553 {
554 char w[256], *ap, *cp = cmdbuf;
555 char *ifname, *argv[64], qdisc_name[64];
556 int argc, rval;
557
558 if (!get_ifname(&cp, &ifname)) {
559 LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
560 altqconfigfile, line_no);
561 return (0);
562 }
563
564 /*
565 * Create argment list & look for scheduling discipline options.
566 */
567 sprintf(qdisc_name, "null");
568 argc = 0;
569 ap = w;
570 while (next_word(&cp, ap)) {
571 if (is_qdisc_name(ap))
572 strcpy(qdisc_name, ap);
573
574 argv[argc] = ap;
575 ap += strlen(ap) + 1;
576 argc++;
577 }
578
579 rval = qdisc_interface_parser(qdisc_name, ifname, argc, argv);
580 if (rval == 0) {
581 LOG(LOG_ERR, 0, "Error in %s, line %d\n",
582 altqconfigfile, line_no);
583 return (0);
584 }
585 return (1);
586 }
587
588 static int
589 class_parser(char *cmdbuf)
590 {
591 char w[256], *cp = cmdbuf;
592 char *ifname, qdisc_name[128], class_name[128], parent_name[128];
593 char *clname = class_name;
594 char *parent = NULL;
595 char *argv[64], *ap;
596 int argc, rval;
597
598 /* get scheduling class */
599 if (!next_word(&cp, qdisc_name)) {
600 LOG(LOG_ERR, 0, "missing scheduling discipline in %s, line %d\n",
601 altqconfigfile, line_no);
602 return (0);
603 }
604 if (!is_qdisc_name(qdisc_name)) {
605 LOG(LOG_ERR, 0,
606 "unknown scheduling discipline '%s' in %s, line %d\n",
607 qdisc_name, altqconfigfile, line_no);
608 return (0);
609 }
610
611 /* get interface name */
612 if (!get_ifname(&cp, &ifname)) {
613 LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
614 altqconfigfile, line_no);
615 return (0);
616 }
617
618 /* get class name */
619 if (!next_word(&cp, class_name)) {
620 LOG(LOG_ERR, 0, "missing class name in %s, line %d\n",
621 altqconfigfile, line_no);
622 return (0);
623 }
624
625 /* get parent name */
626 if (!next_word(&cp, parent_name)) {
627 LOG(LOG_ERR, 0, "missing parent class in %s, line %d\n",
628 altqconfigfile, line_no);
629 return (0);
630 }
631 if (!EQUAL(parent_name, "null") && !EQUAL(parent_name, "NULL")) {
632 parent = parent_name;
633 } else {
634 parent = NULL;
635 }
636
637 ap = w;
638 argc = 0;
639 while (next_word(&cp, ap)) {
640 argv[argc] = ap;
641 ap += strlen(ap) + 1;
642 argc++;
643 }
644
645 rval = qdisc_class_parser(qdisc_name, ifname, clname, parent,
646 argc, argv);
647 if (rval == 0) {
648 LOG(LOG_ERR, 0, "can't add class '%s' on interface '%s'\n",
649 clname, ifname);
650 return (0);
651 }
652
653 return (1);
654 }
655
656 static int
657 filter_parser(char *cmdbuf)
658 {
659 char w[128], *cp = cmdbuf;
660 char *ifname, class_name[64], fltr_name[64], *flname = NULL;
661 struct flow_filter sfilt;
662 int protocol;
663 u_char tos, tosmask;
664 int ruleno;
665 int dontwarn = 0;
666 int error;
667
668 memset(&sfilt, 0, sizeof(sfilt));
669 sfilt.ff_flow.fi_family = AF_INET;
670
671 if (!get_ifname(&cp, &ifname)) {
672 LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
673 altqconfigfile, line_no);
674 return (0);
675 }
676
677 if (!next_word(&cp, class_name)) {
678 LOG(LOG_ERR, 0,
679 "missing class name in %s, line %d\n",
680 altqconfigfile, line_no);
681 return (0);
682 }
683
684 fltr_name[0] = '\0';
685 ruleno = 0;
686 if (!get_fltr_opts(&cp, &fltr_name[0], &ruleno)) {
687 LOG(LOG_ERR, 0,
688 "bad filter option in %s, line %d\n",
689 altqconfigfile, line_no);
690 return (0);
691 }
692 if (fltr_name[0] != '\0')
693 flname = fltr_name;
694 sfilt.ff_ruleno = ruleno;
695
696 /* get filter destination Address */
697 if (!get_addr(&cp, &sfilt.ff_flow.fi_dst, &sfilt.ff_mask.mask_dst)) {
698 LOG(LOG_ERR, 0,
699 "bad filter destination address in %s, line %d\n",
700 altqconfigfile, line_no);
701 return (0);
702 }
703
704 /* get filter destination port */
705 if (!next_word(&cp, w)) {
706 LOG(LOG_ERR, 0,
707 "missing filter destination port in %s, line %d\n",
708 altqconfigfile, line_no);
709 return (0);
710 }
711 if (!get_port(w, &sfilt.ff_flow.fi_dport)) {
712 LOG(LOG_ERR, 0, "bad filter destination port in %s, line %d\n",
713 altqconfigfile, line_no);
714 return (0);
715 }
716
717 /* get filter source address */
718 if (!get_addr(&cp, &sfilt.ff_flow.fi_src, &sfilt.ff_mask.mask_src)) {
719 LOG(LOG_ERR, 0, "bad filter source address in %s, line %d\n",
720 altqconfigfile, line_no);
721 return (0);
722 }
723
724 /* get filter source port */
725 if (!next_word(&cp, w)) {
726 LOG(LOG_ERR, 0, "missing filter source port in %s, line %d\n",
727 altqconfigfile, line_no);
728 return (0);
729 }
730 if (!get_port(w, &sfilt.ff_flow.fi_sport)) {
731 LOG(LOG_ERR, 0, "bad filter source port in %s, line %d\n",
732 altqconfigfile, line_no);
733 return (0);
734 }
735
736 /* get filter protocol id */
737 if (!next_word(&cp, w)) {
738 LOG(LOG_ERR, 0, "missing filter protocol id in %s, line %d\n",
739 altqconfigfile, line_no);
740 return (0);
741 }
742 if (!get_proto(w, &protocol)) {
743 LOG(LOG_ERR, 0, "bad protocol in %s, line %d\n",
744 altqconfigfile, line_no);
745 return (0);
746 }
747 sfilt.ff_flow.fi_proto = protocol;
748
749 while (next_word(&cp, w)) {
750 if (EQUAL(w, "tos")) {
751 tos = 0;
752 tosmask = 0xff;
753
754 if (next_word(&cp, w)) {
755 tos = (u_char)strtol(w, NULL, 0);
756 if (next_word(&cp, w)) {
757 if (EQUAL(w, "tosmask")) {
758 next_word(&cp, w);
759 tosmask = (u_char)strtol(w, NULL, 0);
760 }
761 }
762 }
763 sfilt.ff_flow.fi_tos = tos;
764 sfilt.ff_mask.mask_tos = tosmask;
765 } else if (EQUAL(w, "gpi")) {
766 if (next_word(&cp, w)) {
767 sfilt.ff_flow.fi_gpi =
768 (u_int32_t)strtoul(w, NULL, 0);
769 sfilt.ff_flow.fi_gpi =
770 htonl(sfilt.ff_flow.fi_gpi);
771 }
772 } else if (EQUAL(w, "dontwarn"))
773 dontwarn = 1;
774 }
775
776 /*
777 * Add the filter.
778 */
779 filter_dontwarn = dontwarn; /* XXX */
780 error = qcmd_add_filter(ifname, class_name, flname, &sfilt);
781 filter_dontwarn = 0; /* XXX */
782 if (error) {
783 LOG(LOG_ERR, 0,
784 "can't add filter to class '%s' on interface '%s'\n",
785 class_name, ifname);
786 return (0);
787 }
788
789 return (1);
790 }
791
792 #ifdef INET6
793 static int
794 filter6_parser(char *cmdbuf)
795 {
796 char w[128], *cp = cmdbuf;
797 char *ifname, class_name[128], fltr_name[64], *flname = NULL;
798 struct flow_filter6 sfilt;
799 int protocol;
800 u_char tclass, tclassmask;
801 int ruleno;
802 int dontwarn = 0;
803 int ret;
804
805 memset(&sfilt, 0, sizeof(sfilt));
806 sfilt.ff_flow6.fi6_family = AF_INET6;
807
808 if (!get_ifname(&cp, &ifname)) {
809 LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
810 altqconfigfile, line_no);
811 return (0);
812 }
813
814 if (!next_word(&cp, class_name)) {
815 LOG(LOG_ERR, 0, "missing class name in %s, line %d\n",
816 altqconfigfile, line_no);
817 return (0);
818 }
819
820 fltr_name[0] = '\0';
821 ruleno = 0;
822 if (!get_fltr_opts(&cp, &fltr_name[0], &ruleno)) {
823 LOG(LOG_ERR, 0,
824 "bad filter option in %s, line %d\n",
825 altqconfigfile, line_no);
826 return (0);
827 }
828 if (fltr_name[0] != '\0')
829 flname = fltr_name;
830 sfilt.ff_ruleno = ruleno;
831
832 /* get filter destination address */
833 if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_dst,
834 &sfilt.ff_mask6.mask6_dst)) {
835 LOG(LOG_ERR, 0, "bad destination address in %s, line %d\n",
836 altqconfigfile, line_no);
837 return (0);
838 }
839
840 /* get filter destination port */
841 if (!next_word(&cp, w)) {
842 LOG(LOG_ERR, 0,
843 "missing filter destination port in %s, line %d\n",
844 altqconfigfile, line_no);
845 return (0);
846 }
847 if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) {
848 LOG(LOG_ERR, 0, "bad filter destination port in %s, line %d\n",
849 altqconfigfile, line_no);
850 return (0);
851 }
852
853 /* get filter source address */
854 if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_src,
855 &sfilt.ff_mask6.mask6_src)) {
856 LOG(LOG_ERR, 0, "bad source address in %s, line %d\n",
857 altqconfigfile, line_no);
858 return (0);
859 }
860
861 /* get filter source port */
862 if (!next_word(&cp, w)) {
863 LOG(LOG_ERR, 0, "missing filter source port in %s, line %d\n",
864 altqconfigfile, line_no);
865 return (0);
866 }
867 if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) {
868 LOG(LOG_ERR, 0, "bad filter source port in %s, line %d\n",
869 altqconfigfile, line_no);
870 return (0);
871 }
872
873 /* get filter protocol id */
874 if (!next_word(&cp, w)) {
875 LOG(LOG_ERR, 0, "missing filter protocol id in %s, line %d\n",
876 altqconfigfile, line_no);
877 return (0);
878 }
879 if (!get_proto(w, &protocol)) {
880 LOG(LOG_ERR, 0, "bad protocol in %s, line %d\n",
881 altqconfigfile, line_no);
882 return (0);
883 }
884 sfilt.ff_flow6.fi6_proto = protocol;
885
886 while (next_word(&cp, w)) {
887 if (EQUAL(w, "tclass")) {
888 tclass = 0;
889 tclassmask = 0xff;
890
891 if (next_word(&cp, w)) {
892 tclass = (u_char)strtol(w, NULL, 0);
893 if (next_word(&cp, w)) {
894 if (EQUAL(w, "tclassmask")) {
895 next_word(&cp, w);
896 tclassmask =
897 (u_char)strtol(w, NULL, 0);
898 }
899 }
900 }
901 sfilt.ff_flow6.fi6_tclass = tclass;
902 sfilt.ff_mask6.mask6_tclass = tclassmask;
903 } else if (EQUAL(w, "gpi")) {
904 if (next_word(&cp, w)) {
905 sfilt.ff_flow6.fi6_gpi =
906 (u_int32_t)strtoul(w, NULL, 0);
907 sfilt.ff_flow6.fi6_gpi =
908 htonl(sfilt.ff_flow6.fi6_gpi);
909 }
910 } else if (EQUAL(w, "flowlabel")) {
911 if (next_word(&cp, w)) {
912 sfilt.ff_flow6.fi6_flowlabel =
913 (u_int32_t)strtoul(w, NULL, 0) & 0x000fffff;
914 sfilt.ff_flow6.fi6_flowlabel =
915 htonl(sfilt.ff_flow6.fi6_flowlabel);
916 }
917 } else if (EQUAL(w, "dontwarn"))
918 dontwarn = 1;
919 }
920
921 /*
922 * Add the filter.
923 */
924 filter_dontwarn = dontwarn; /* XXX */
925 ret = qcmd_add_filter(ifname, class_name, flname,
926 (struct flow_filter *)&sfilt);
927 filter_dontwarn = 0; /* XXX */
928 if (ret) {
929 LOG(LOG_ERR, 0,
930 "can't add filter to class '%s' on interface '%s'\n",
931 class_name, ifname);
932 return (0);
933 }
934
935 return (1);
936 }
937
938 static int
939 get_ip6addr(char **cpp, struct in6_addr *addr, struct in6_addr *mask)
940 {
941 char w[128], *prefix;
942 u_char *cp;
943 int len;
944
945 *addr = in6addr_any; /* set all 0 */
946 *mask = in6addr_any; /* set all 0 */
947
948 if (!next_word(cpp, w))
949 return (0);
950
951 if (EQUAL(w, "0"))
952 /* abbreviation of a wildcard (::0) */
953 return (1);
954
955 if ((prefix = strchr(w, '/')) != NULL) {
956 /* address has prefix length */
957 *prefix++ = '\0';
958 }
959
960 if (inet_pton(AF_INET6, w, addr) <= 0)
961 return (0);
962
963 if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefix == NULL)
964 /* wildcard */
965 return (1);
966
967 /* convert address prefix length to address mask */
968 if (prefix != NULL) {
969 len = (int)strtol(prefix, NULL, 0);
970 if ((len < 0) || (len > 128))
971 return (0);
972 for (cp = (u_char *)mask; len > 7; len -= 8)
973 *cp++ = 0xff;
974 if (len > 0)
975 *cp = (0xff << (8 - len)) & 0xff;
976
977 IN6ADDR32(addr, 0) &= IN6ADDR32(mask, 0);
978 IN6ADDR32(addr, 1) &= IN6ADDR32(mask, 1);
979 IN6ADDR32(addr, 2) &= IN6ADDR32(mask, 2);
980 IN6ADDR32(addr, 3) &= IN6ADDR32(mask, 3);
981 } else
982 /* full mask */
983 memset(mask, 0xff, sizeof(struct in6_addr));
984
985 return (1);
986 }
987
988 #endif /* INET6 */
989
990 static int
991 ctl_parser(char *cmdbuf)
992 {
993 char w[128], *cp = cmdbuf;
994 char *ifname;
995 int state;
996 int rval;
997
998 if (!get_ifname(&cp, &ifname)) {
999 printf("missing interface name in %s, line %d",
1000 altqconfigfile, line_no);
1001 return (0);
1002 }
1003
1004 if (!next_word(&cp, w)) {
1005 state = is_q_enabled(ifname);
1006 printf("altq %s on %s\n",
1007 state ? "enabled" : "disabled", ifname);
1008 return (1);
1009 }
1010
1011 if (EQUAL(w, "enable")) {
1012 rval = qcmd_enable(ifname);
1013 printf("altq %s on %s\n",
1014 (rval == 0) ? "enabled" : "enable failed!", ifname);
1015 } else if (EQUAL(w, "disable")) {
1016 rval = qcmd_disable(ifname);
1017 printf("altq %s on %s\n",
1018 (rval == 0) ? "disabled" : "disable failed!", ifname);
1019 } else if (EQUAL(w, "reload")) {
1020 printf("reinitializing altq...\n");
1021 qcmd_destroyall();
1022 qcmd_init();
1023 } else
1024 return (0);
1025 return (1);
1026 }
1027
1028
1029 static int
1030 delete_parser(char *cmdbuf)
1031 {
1032 char *cp = cmdbuf;
1033 char *ifname, class_name[128];
1034 int ret;
1035
1036 if (!get_ifname(&cp, &ifname)) {
1037 printf("missing interface name in %s, line %d",
1038 altqconfigfile, line_no);
1039 return (0);
1040 }
1041
1042 if (!next_word(&cp, class_name)) {
1043 LOG(LOG_ERR, 0,
1044 "missing class name in %s, line %d\n",
1045 altqconfigfile, line_no);
1046 return (0);
1047 }
1048
1049 ret = qcmd_delete_class(ifname, class_name);
1050 if (ret) {
1051 LOG(LOG_ERR, 0,
1052 "can't delete class '%s' on interface '%s'\n",
1053 class_name, ifname);
1054 return (0);
1055 }
1056
1057 return (1);
1058 }
1059
1060 static int
1061 red_parser(char *cmdbuf)
1062 {
1063 char w[128], *cp = cmdbuf;
1064 int th_min, th_max, inv_pmax;
1065
1066 if (!next_word(&cp, w))
1067 goto bad;
1068 th_min = (int)strtol(w, NULL, 0);
1069
1070 if (!next_word(&cp, w))
1071 goto bad;
1072 th_max = (int)strtol(w, NULL, 0);
1073
1074 if (!next_word(&cp, w))
1075 goto bad;
1076 inv_pmax = (int)strtol(w, NULL, 0);
1077
1078 if (qop_red_set_defaults(th_min, th_max, inv_pmax) != 0) {
1079 LOG(LOG_ERR, 0, "can't set red default parameters\n");
1080 return (0);
1081 }
1082
1083 return (1);
1084
1085 bad:
1086 LOG(LOG_ERR, 0, "bad red parameter in %s, line %d\n",
1087 altqconfigfile, line_no);
1088 return (0);
1089 }
1090
1091 static int
1092 rio_parser(char *cmdbuf)
1093 {
1094 char w[128], *cp = cmdbuf;
1095 int i;
1096 struct redparams params[RIO_NDROPPREC];
1097
1098 for (i = 0; i < RIO_NDROPPREC; i++) {
1099 if (!next_word(&cp, w))
1100 goto bad;
1101 params[i].th_min = (int)strtol(w, NULL, 0);
1102
1103 if (!next_word(&cp, w))
1104 goto bad;
1105 params[i].th_max = (int)strtol(w, NULL, 0);
1106
1107 if (!next_word(&cp, w))
1108 goto bad;
1109 params[i].inv_pmax = (int)strtol(w, NULL, 0);
1110 }
1111
1112 if (qop_rio_set_defaults(¶ms[0]) != 0) {
1113 LOG(LOG_ERR, 0, "can't set rio default parameters\n");
1114 return (0);
1115 }
1116
1117 return (1);
1118
1119 bad:
1120 LOG(LOG_ERR, 0, "bad rio parameter in %s, line %d\n",
1121 altqconfigfile, line_no);
1122 return (0);
1123 }
1124
1125 static int
1126 conditioner_parser(char *cmdbuf)
1127 {
1128 char cdnr_name[128], *cp = cmdbuf;
1129 char *ifname;
1130 struct tc_action action[64];
1131
1132 if (!get_ifname(&cp, &ifname)) {
1133 LOG(LOG_ERR, 0, "missing interface name in %s, line %d\n",
1134 altqconfigfile, line_no);
1135 return (0);
1136 }
1137
1138 /* get conditioner name */
1139 if (!next_word(&cp, cdnr_name)) {
1140 LOG(LOG_ERR, 0, "missing cdnr name in %s, line %d\n",
1141 altqconfigfile, line_no);
1142 return (0);
1143 }
1144
1145 if (tc_action_parser(ifname, &cp, &action[0]) == 0)
1146 return (0);
1147
1148 if (qcmd_cdnr_add_element(NULL, ifname, cdnr_name, &action[0]) != 0)
1149 return (0);
1150 return (1);
1151 }
1152
1153 /*
1154 * recursively parse '<'tc_action'>'
1155 * note that array "action" grows during recursive parse.
1156 */
1157 static int
1158 tc_action_parser(char *ifname, char **cpp, struct tc_action *action)
1159 {
1160 char *cp, *start, *end;
1161 char type[128], w[128];
1162 int depth, i;
1163 struct tb_profile profile[2];
1164
1165 /*
1166 * find a possibly nested pair of '<' and '>',
1167 * make them pointed by 'start' and 'end'.
1168 */
1169 start = strchr(*cpp, '<');
1170 if (start == NULL) {
1171 LOG(LOG_ERR, 0, "conditioner action missing in %s, line %d\n",
1172 altqconfigfile, line_no);
1173 return (0);
1174 }
1175 depth = 1;
1176 cp = start + 1;
1177 do {
1178 end = strpbrk(cp, "<>");
1179 if (end == NULL) {
1180 LOG(LOG_ERR, 0,
1181 "conditioner action delimiter mismatch in %s, line %d\n",
1182 altqconfigfile, line_no);
1183 return (0);
1184 }
1185 if (*end == '<')
1186 depth++;
1187 else if (*end == '>')
1188 depth--;
1189 cp = end + 1;
1190 } while (depth > 0);
1191 *end = '\0';
1192 *cpp = end + 1;
1193 cp = start + 1;
1194
1195 if (IsDebug(DEBUG_ALTQ)) {
1196 printf("tc_action_parser: [%s]\n", cp);
1197 }
1198
1199 if (!next_word(&cp, type)) {
1200 LOG(LOG_ERR, 0,
1201 "missing conditioner action type in %s, line %d\n",
1202 altqconfigfile, line_no);
1203 return (0);
1204 }
1205
1206 /*
1207 * action type specific process
1208 */
1209 if (EQUAL(type, "conditioner")) {
1210 if (!next_word(&cp, w)) {
1211 LOG(LOG_ERR, 0,
1212 "missing conditioner name in %s, line %d\n",
1213 altqconfigfile, line_no);
1214 return (0);
1215 }
1216 action->tca_code = TCACODE_HANDLE;
1217 action->tca_handle = cdnr_name2handle(ifname, w);
1218 if (action->tca_handle == CDNR_NULL_HANDLE) {
1219 LOG(LOG_ERR, 0,
1220 "wrong conditioner name %s in %s, line %d\n",
1221 w, altqconfigfile, line_no);
1222 return (0);
1223 }
1224 } else if (EQUAL(type, "pass")) {
1225 action->tca_code = TCACODE_PASS;
1226 } else if (EQUAL(type, "drop")) {
1227 action->tca_code = TCACODE_DROP;
1228 } else if (EQUAL(type, "mark")) {
1229 if (!next_word(&cp, w)) {
1230 LOG(LOG_ERR, 0, "missing dscp in %s, line %d\n",
1231 altqconfigfile, line_no);
1232 return (0);
1233 }
1234 action->tca_code = TCACODE_MARK;
1235 action->tca_dscp = (u_int8_t)strtol(w, NULL, 0);
1236 } else if (EQUAL(type, "tbmeter")) {
1237 if (!next_word(&cp, w)) {
1238 LOG(LOG_ERR, 0, "missing tb profile in %s, line %d\n",
1239 altqconfigfile, line_no);
1240 return (0);
1241 }
1242 profile[0].rate = atobps(w);
1243 if (!next_word(&cp, w)) {
1244 LOG(LOG_ERR, 0, "missing tb profile in %s, line %d\n",
1245 altqconfigfile, line_no);
1246 return (0);
1247 }
1248 profile[0].depth = atobytes(w);
1249 if (tc_action_parser(ifname, &cp, &action[1]) == 0)
1250 return (0);
1251 if (tc_action_parser(ifname, &cp, &action[2]) == 0)
1252 return (0);
1253
1254 if (qcmd_cdnr_add_tbmeter(action, ifname, NULL, &profile[0],
1255 &action[1], &action[2]) != 0)
1256 return (0);
1257 } else if (EQUAL(type, "trtcm")) {
1258 int coloraware = 0; /* default is color-blind */
1259
1260 for (i=0; i<2; i++) {
1261 if (!next_word(&cp, w)) {
1262 LOG(LOG_ERR, 0,
1263 "missing tb profile in %s, line %d\n",
1264 altqconfigfile, line_no);
1265 return (0);
1266 }
1267 profile[i].rate = atobps(w);
1268 if (!next_word(&cp, w)) {
1269 LOG(LOG_ERR, 0,
1270 "missing tb profile in %s, line %d\n",
1271 altqconfigfile, line_no);
1272 return (0);
1273 }
1274 profile[i].depth = atobytes(w);
1275 }
1276 if (tc_action_parser(ifname, &cp, &action[1]) == 0)
1277 return (0);
1278 if (tc_action_parser(ifname, &cp, &action[2]) == 0)
1279 return (0);
1280 if (tc_action_parser(ifname, &cp, &action[3]) == 0)
1281 return (0);
1282 if (next_word(&cp, w)) {
1283 if (EQUAL(w, "coloraware"))
1284 coloraware = 1;
1285 else if (EQUAL(w, "colorblind"))
1286 coloraware = 0;
1287 }
1288
1289 if (qcmd_cdnr_add_trtcm(action, ifname, NULL,
1290 &profile[0], &profile[1],
1291 &action[1], &action[2], &action[3],
1292 coloraware) != 0)
1293 return (0);
1294 } else if (EQUAL(type, "tswtcm")) {
1295 u_int32_t cmtd_rate, peak_rate, avg_interval;
1296
1297 if (!next_word(&cp, w)) {
1298 LOG(LOG_ERR, 0, "missing cmtd rate in %s, line %d\n",
1299 altqconfigfile, line_no);
1300 return (0);
1301 }
1302 cmtd_rate = atobps(w);
1303
1304 if (!next_word(&cp, w)) {
1305 LOG(LOG_ERR, 0, "missing peak rate in %s, line %d\n",
1306 altqconfigfile, line_no);
1307 return (0);
1308 }
1309 peak_rate = atobps(w);
1310
1311 if (!next_word(&cp, w)) {
1312 LOG(LOG_ERR, 0, "missing avg interval in %s, line %d\n",
1313 altqconfigfile, line_no);
1314 return (0);
1315 }
1316 avg_interval = (u_int32_t)strtoul(w, NULL, 0);
1317
1318 if (tc_action_parser(ifname, &cp, &action[1]) == 0)
1319 return (0);
1320 if (tc_action_parser(ifname, &cp, &action[2]) == 0)
1321 return (0);
1322 if (tc_action_parser(ifname, &cp, &action[3]) == 0)
1323 return (0);
1324
1325 if (qcmd_cdnr_add_tswtcm(action, ifname, NULL,
1326 cmtd_rate, peak_rate, avg_interval,
1327 &action[1], &action[2], &action[3])
1328 != 0)
1329 return (0);
1330 } else {
1331 LOG(LOG_ERR, 0,
1332 "Unkown action type %s in %s, line %d\n",
1333 type, altqconfigfile, line_no);
1334 return (0);
1335 }
1336
1337 *end = '>'; /* restore the end delimiter */
1338
1339 return (1);
1340 }
1341
1342