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