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