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