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