1 /* $NetBSD: parser.c,v 1.7 2002/03/05 04:11:53 itojun Exp $ */ 2 /* $KAME: parser.c,v 1.16 2002/02/20 10:40:39 kjc Exp $ */ 3 /* 4 * Copyright (C) 1999-2002 5 * Sony Computer Science Laboratories, Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/socket.h> 31 #include <net/if.h> 32 #include <netinet/in.h> 33 #include <arpa/inet.h> 34 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <unistd.h> 38 #include <stddef.h> 39 #include <string.h> 40 #include <ctype.h> 41 #include <errno.h> 42 #include <syslog.h> 43 #include <netdb.h> 44 #include <err.h> 45 46 #include <altq/altq.h> 47 #include <altq/altq_cdnr.h> 48 #include <altq/altq_red.h> 49 #include <altq/altq_rio.h> 50 #include "altq_qop.h" 51 #include "qop_cdnr.h" 52 53 static int is_qdisc_name(const char *); 54 static int qdisc_interface_parser(const char *, const char *, int, char **); 55 static int qdisc_class_parser(const char *, const char *, const char *, 56 const char *, int, char **); 57 static int next_word(char **, char *); 58 59 static int get_ifname(char **, char **); 60 static int get_addr(char **, struct in_addr *, struct in_addr *); 61 static int get_port(const char *, u_int16_t *); 62 static int get_proto(const char *, int *); 63 static int get_fltr_opts(char **, char *, size_t, int *); 64 static int interface_parser(char *); 65 static int class_parser(char *) ; 66 static int filter_parser(char *); 67 #ifdef INET6 68 static int filter6_parser(char *); 69 static int get_ip6addr(char **, struct in6_addr *, struct in6_addr *); 70 #endif 71 static int ctl_parser(char *); 72 static int delete_parser(char *); 73 static int red_parser(char *); 74 static int rio_parser(char *); 75 static int conditioner_parser(char *); 76 static int tc_action_parser(char *, char **, struct tc_action *); 77 78 #define MAX_LINE 1024 79 #define MAX_WORD 64 80 #define MAX_ARGS 64 81 #define MAX_ACTIONS 16 82 83 #ifndef MAX 84 #define MAX(a,b) (((a)>(b))?(a):(b)) 85 #endif 86 #ifndef MIN 87 #define MIN(a,b) (((a)<(b))?(a):(b)) 88 #endif 89 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) 90 91 int line_no = 0; 92 int filter_dontwarn; 93 94 static char curifname[IFNAMSIZ]; 95 static struct if_nameindex *if_namelist = NULL; 96 97 struct cmd_tab { 98 const char *cmd; 99 int (*parser)(char *); 100 const char *help; 101 } cmd_tab[] = { 102 {"help", NULL, "help | ?"}, 103 {"quit", NULL, "quit"}, 104 {"interface", interface_parser, "interface if_name [bandwidth bps] [cbq|hfsc]"}, 105 {"class", class_parser, "class discipline if_name class_name [parent]"}, 106 {"filter", filter_parser, "filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]"}, 107 {"altq", ctl_parser, "altq if_name {enable|disable}"}, 108 {"delete", delete_parser, "delete if_name class_name [filter_name]"}, 109 #ifdef INET6 110 {"filter6", filter6_parser, "filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]"}, 111 #endif 112 {"red", red_parser, "red th_min th_max inv_pmax"}, 113 {"rio", rio_parser, "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"}, 114 {"conditioner", conditioner_parser, "conditioner if_name cdnr_name <tc_action>"}, 115 {"debug", NULL, "debug"}, 116 {NULL, NULL, NULL} /* termination */ 117 }; 118 119 /* 120 * read one line from the specified stream. if it's a command, 121 * execute the command. 122 * returns 1 if OK, 0 if error or EOF. 123 */ 124 int 125 do_command(FILE *fp) 126 { 127 char cmd_line[MAX_LINE], cmd[MAX_WORD], *cp; 128 struct cmd_tab *tp; 129 int len, rval; 130 131 /* 132 * read a line from the stream and make it a null-terminated string 133 */ 134 cp = cmd_line; 135 read_line: 136 if (fgets(cp, &cmd_line[MAX_LINE] - cp, fp) == NULL) 137 /* EOF or error */ 138 return(0); 139 line_no++; 140 141 /* null-terminate the line */ 142 if ((len = strlen(cmd_line)) > 0) { 143 cp = cmd_line + len - 1; 144 if (*cp == '\n') { 145 /* if escaped newline, read next line */ 146 if (len > 1 && *(cp - 1) == '\\') 147 goto read_line; 148 *cp = '\0'; 149 } else if (!feof(fp)) 150 err(1, "LINE %d too long!", line_no); 151 } 152 /* trim comments */ 153 if ((cp = strchr(cmd_line, '#')) != NULL) 154 *cp = '\0'; 155 156 cp = cmd_line; 157 if ((len = next_word(&cp, cmd)) == 0) 158 /* no command in this line */ 159 return (1); 160 161 /* fnind the corresponding parser */ 162 rval = 0; 163 for (tp = cmd_tab; tp->cmd != NULL; tp++) 164 if (strncmp(cmd, tp->cmd, len) == 0) 165 break; 166 167 if (tp->cmd == NULL) { 168 if (fp == stdin) { 169 printf(" ?? %s\n", cmd); 170 rval = 1; 171 } else 172 LOG(LOG_ERR, 0, "unknown command: %s", cmd); 173 return (rval); 174 } 175 176 if (tp->parser != NULL) 177 rval = (*tp->parser)(cp); 178 else { 179 /* handle other commands */ 180 if (strcmp(tp->cmd, "quit") == 0) 181 rval = 0; 182 else if (strcmp(tp->cmd, "help") == 0 || 183 strcmp(tp->cmd, "?") == 0) { 184 for (tp = cmd_tab; tp->cmd != NULL; tp++) 185 printf("%s\n", tp->help); 186 rval = 1; 187 } else if (strcmp(tp->cmd, "debug") == 0) { 188 if (m_debug & DEBUG_ALTQ) { 189 /* turn off verbose */ 190 l_debug = LOG_INFO; 191 m_debug &= ~DEBUG_ALTQ; 192 } else { 193 /* turn on verbose */ 194 l_debug = LOG_DEBUG; 195 m_debug |= DEBUG_ALTQ; 196 } 197 rval = 1; 198 } 199 } 200 return (rval); 201 } 202 203 static int 204 is_qdisc_name(const char *qname) 205 { 206 struct qdisc_parser *qp; 207 208 for (qp = qdisc_parser; qp->qname != NULL; qp++) 209 if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) 210 return (1); 211 return (0); 212 } 213 214 static int 215 qdisc_interface_parser(const char * qname, const char *ifname, 216 int argc, char **argv) 217 { 218 struct qdisc_parser *qp; 219 220 for (qp = qdisc_parser; qp->qname != NULL; qp++) 221 if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) 222 return (*qp->interface_parser)(ifname, argc, argv); 223 return (0); 224 } 225 226 static int 227 qdisc_class_parser(const char *qname, const char *ifname, 228 const char *class_name, const char *parent_name, 229 int argc, char **argv) 230 { 231 struct qdisc_parser *qp; 232 struct ifinfo *ifinfo; 233 234 for (qp = qdisc_parser; qp->qname != NULL; qp++) 235 if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) { 236 if (qp->class_parser == NULL) { 237 LOG(LOG_ERR, 0, 238 "class can't be specified for %s", qp->qname); 239 return (0); 240 } 241 if ((ifinfo = ifname2ifinfo(ifname)) == NULL) { 242 LOG(LOG_ERR, 0, "no such interface"); 243 return (0); 244 } 245 if (strncmp(ifinfo->qdisc->qname, qname, 246 strlen(ifinfo->qdisc->qname)) != 0) { 247 LOG(LOG_ERR, 0, 248 "qname doesn't match the interface"); 249 return (0); 250 } 251 return (*qp->class_parser)(ifname, class_name, 252 parent_name, argc, argv); 253 } 254 return (0); 255 } 256 257 /* 258 * read the config file 259 */ 260 int 261 qcmd_config(void) 262 { 263 FILE *fp; 264 int rval; 265 266 if (if_namelist != NULL) 267 if_freenameindex(if_namelist); 268 if_namelist = if_nameindex(); 269 curifname[0] = '\0'; 270 271 LOG(LOG_INFO, 0, "ALTQ config file is %s", altqconfigfile); 272 273 fp = fopen(altqconfigfile, "r"); 274 if (fp == NULL) { 275 LOG(LOG_ERR, errno, "can't open %s", altqconfigfile, 0); 276 return (QOPERR_INVAL); 277 } 278 line_no = 0; 279 rval = 1; 280 while (rval) 281 rval = do_command(fp); 282 283 if (!feof(fp)) { 284 LOG(LOG_ERR, 0, "Error in %s, line %d. config failed.", 285 altqconfigfile, line_no); 286 (void) qcmd_destroyall(); 287 rval = QOPERR_INVAL; 288 } else 289 rval = 0; 290 291 (void)fclose(fp); 292 line_no = 0; 293 return (rval); 294 } 295 296 static int 297 next_word(char **cpp, char *b) 298 { 299 char *cp; 300 int i; 301 302 cp = *cpp; 303 while (*cp == ' ' || *cp == '\t') 304 cp++; 305 for (i = 0; i < MAX_WORD - 1; i++) { 306 if (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\0') 307 break; 308 *b++ = *cp++; 309 } 310 *b = '\0'; 311 *cpp = cp; 312 return (i); 313 } 314 315 char * 316 cur_ifname(void) 317 { 318 return (curifname); 319 } 320 321 u_int 322 get_ifindex(const char *ifname) 323 { 324 struct if_nameindex *ifnp; 325 326 for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++) 327 if (strcmp(ifname, ifnp->if_name) == 0) 328 return (ifnp->if_index); 329 return (0); 330 } 331 332 static int 333 get_ifname(char **cpp, char **ifnamep) 334 { 335 char w[MAX_WORD], *ocp; 336 struct if_nameindex *ifnp; 337 338 ocp = *cpp; 339 if (next_word(&ocp, w) && if_namelist != NULL) 340 for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++) 341 if (strcmp(w, ifnp->if_name) == 0) { 342 /* if_name found. advance the word pointer */ 343 *cpp = ocp; 344 strlcpy(curifname, w, sizeof(curifname)); 345 *ifnamep = curifname; 346 return (1); 347 } 348 349 /* this is not interface name. use one in the context. */ 350 if (curifname[0] == '\0') 351 return (0); 352 *ifnamep = curifname; 353 return (1); 354 } 355 356 /* set address and netmask in network byte order */ 357 static int 358 get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask) 359 { 360 char w[MAX_WORD], *ocp; 361 struct in_addr tmp; 362 363 addr->s_addr = 0; 364 mask->s_addr = 0xffffffff; 365 366 if (!next_word(cpp, w)) 367 return (0); 368 369 if (inet_aton((char *)w, &tmp) != 1) { 370 /* try gethostbyname */ 371 struct hostent *h; 372 373 if ((h = gethostbyname(w)) == NULL || 374 h->h_addrtype != AF_INET || h->h_length != 4) 375 return (0); 376 bcopy(h->h_addr, &tmp, (size_t)h->h_length); 377 } 378 addr->s_addr = tmp.s_addr; 379 380 /* check if netmask option is present */ 381 ocp = *cpp; 382 if (next_word(&ocp, w) && EQUAL(w, "netmask")) { 383 if (!next_word(&ocp, w)) 384 return (0); 385 if (inet_aton((char *)w, (struct in_addr *)&tmp) != 1) 386 return (0); 387 388 mask->s_addr = tmp.s_addr; 389 *cpp = ocp; 390 return (1); 391 } 392 /* no netmask option */ 393 return (1); 394 } 395 396 /* returns service number in network byte order */ 397 static int 398 get_port(const char *name, u_int16_t *port_no) 399 { 400 struct servent *s; 401 u_int16_t num; 402 403 if (isdigit(name[0])) { 404 num = (u_int16_t)strtol(name, NULL, 0); 405 *port_no = htons(num); 406 return (1); 407 } 408 409 if ((s = getservbyname(name, 0)) == NULL) 410 return (0); 411 412 *port_no = (u_int16_t)s->s_port; 413 return (1); 414 } 415 416 static int 417 get_proto(const char *name, int *proto_no) 418 { 419 struct protoent *p; 420 421 if (isdigit(name[0])) { 422 *proto_no = (int)strtol(name, NULL, 0); 423 return (1); 424 } 425 426 if ((p = getprotobyname(name)) == NULL) 427 return (0); 428 429 *proto_no = p->p_proto; 430 return (1); 431 } 432 433 static int 434 get_fltr_opts(char **cpp, char *fltr_name, size_t len, int *ruleno) 435 { 436 char w[MAX_WORD], *ocp; 437 438 ocp = *cpp; 439 while (next_word(&ocp, w)) { 440 if (EQUAL(w, "name")) { 441 if (!next_word(&ocp, w)) 442 return (0); 443 strlcpy(fltr_name, w, len); 444 *cpp = ocp; 445 } else if (EQUAL(w, "ruleno")) { 446 if (!next_word(&ocp, w)) 447 return (0); 448 *ruleno = (int)strtol(w, NULL, 0); 449 *cpp = ocp; 450 } else 451 break; 452 } 453 return (1); 454 } 455 456 457 #define DISCIPLINE_NONE 0 458 459 static int 460 interface_parser(char *cmdbuf) 461 { 462 char w[MAX_WORD], *ap, *cp = cmdbuf; 463 char *ifname, *argv[MAX_ARGS], qdisc_name[MAX_WORD]; 464 int argc; 465 466 if (!get_ifname(&cp, &ifname)) { 467 LOG(LOG_ERR, 0, "missing interface name"); 468 return (0); 469 } 470 471 /* create argment list & look for scheduling discipline options. */ 472 snprintf(qdisc_name, sizeof qdisc_name, "null"); 473 argc = 0; 474 ap = w; 475 while (next_word(&cp, ap)) { 476 if (is_qdisc_name(ap)) 477 strlcpy(qdisc_name, ap, sizeof qdisc_name); 478 479 argv[argc] = ap; 480 ap += strlen(ap) + 1; 481 argc++; 482 if (argc >= MAX_ARGS) { 483 LOG(LOG_ERR, 0, "too many args"); 484 return (0); 485 } 486 } 487 488 return qdisc_interface_parser(qdisc_name, ifname, argc, argv); 489 } 490 491 492 static int 493 class_parser(char *cmdbuf) 494 { 495 char w[MAX_WORD], *cp = cmdbuf; 496 char *ifname, qdisc_name[MAX_WORD]; 497 char class_name[MAX_WORD], parent_name[MAX_WORD]; 498 char *clname = class_name; 499 char *parent = NULL; 500 char *argv[MAX_ARGS], *ap; 501 int argc; 502 503 /* get scheduling class */ 504 if (!next_word(&cp, qdisc_name)) { 505 LOG(LOG_ERR, 0, "missing discipline"); 506 return (0); 507 } 508 if (!is_qdisc_name(qdisc_name)) { 509 LOG(LOG_ERR, 0, "unknown discipline '%s'", qdisc_name); 510 return (0); 511 } 512 513 /* get interface name */ 514 if (!get_ifname(&cp, &ifname)) { 515 LOG(LOG_ERR, 0, "missing interface name"); 516 return (0); 517 } 518 519 /* get class name */ 520 if (!next_word(&cp, class_name)) { 521 LOG(LOG_ERR, 0, "missing class name"); 522 return (0); 523 } 524 525 /* get parent name */ 526 if (!next_word(&cp, parent_name)) { 527 LOG(LOG_ERR, 0, "missing parent class"); 528 return (0); 529 } 530 if (!EQUAL(parent_name, "null") && !EQUAL(parent_name, "NULL")) 531 parent = parent_name; 532 else 533 parent = NULL; 534 535 ap = w; 536 argc = 0; 537 while (next_word(&cp, ap)) { 538 argv[argc] = ap; 539 ap += strlen(ap) + 1; 540 argc++; 541 if (argc >= MAX_ARGS) { 542 LOG(LOG_ERR, 0, "too many args"); 543 return (0); 544 } 545 } 546 547 return qdisc_class_parser(qdisc_name, ifname, clname, parent, 548 argc, argv); 549 } 550 551 static int 552 filter_parser(char *cmdbuf) 553 { 554 char w[MAX_WORD], *cp = cmdbuf; 555 char *ifname, class_name[MAX_WORD], fltr_name[MAX_WORD]; 556 char *flname = NULL; 557 struct flow_filter sfilt; 558 int protocol; 559 u_char tos, tosmask; 560 int ruleno; 561 int dontwarn = 0; 562 int error; 563 564 memset(&sfilt, 0, sizeof(sfilt)); 565 sfilt.ff_flow.fi_family = AF_INET; 566 567 if (!get_ifname(&cp, &ifname)) { 568 LOG(LOG_ERR, 0, "missing interface name in filter command"); 569 return (0); 570 } 571 572 if (!next_word(&cp, class_name)) { 573 LOG(LOG_ERR, 0, "missing class name in filter command"); 574 return (0); 575 } 576 577 fltr_name[0] = '\0'; 578 ruleno = 0; 579 if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) { 580 LOG(LOG_ERR, 0, "bad filter option"); 581 return (0); 582 } 583 if (fltr_name[0] != '\0') 584 flname = fltr_name; 585 sfilt.ff_ruleno = ruleno; 586 587 /* get filter destination Address */ 588 if (!get_addr(&cp, &sfilt.ff_flow.fi_dst, &sfilt.ff_mask.mask_dst)) { 589 LOG(LOG_ERR, 0, "bad filter destination address"); 590 return (0); 591 } 592 593 /* get filter destination port */ 594 if (!next_word(&cp, w)) { 595 LOG(LOG_ERR, 0, "missing filter destination port"); 596 return (0); 597 } 598 if (!get_port(w, &sfilt.ff_flow.fi_dport)) { 599 LOG(LOG_ERR, 0, "bad filter destination port"); 600 return (0); 601 } 602 603 /* get filter source address */ 604 if (!get_addr(&cp, &sfilt.ff_flow.fi_src, &sfilt.ff_mask.mask_src)) { 605 LOG(LOG_ERR, 0, "bad filter source address"); 606 return (0); 607 } 608 609 /* get filter source port */ 610 if (!next_word(&cp, w)) { 611 LOG(LOG_ERR, 0, "missing filter source port"); 612 return (0); 613 } 614 if (!get_port(w, &sfilt.ff_flow.fi_sport)) { 615 LOG(LOG_ERR, 0, "bad filter source port"); 616 return (0); 617 } 618 619 /* get filter protocol id */ 620 if (!next_word(&cp, w)) { 621 LOG(LOG_ERR, 0, "missing filter protocol"); 622 return (0); 623 } 624 if (!get_proto(w, &protocol)) { 625 LOG(LOG_ERR, 0, "bad protocol"); 626 return (0); 627 } 628 sfilt.ff_flow.fi_proto = protocol; 629 630 while (next_word(&cp, w)) { 631 if (EQUAL(w, "tos")) { 632 tos = 0; 633 tosmask = 0xff; 634 635 if (next_word(&cp, w)) { 636 tos = (u_char)strtol(w, NULL, 0); 637 if (next_word(&cp, w)) { 638 if (EQUAL(w, "tosmask")) { 639 next_word(&cp, w); 640 tosmask = (u_char)strtol(w, NULL, 0); 641 } 642 } 643 } 644 sfilt.ff_flow.fi_tos = tos; 645 sfilt.ff_mask.mask_tos = tosmask; 646 } else if (EQUAL(w, "gpi")) { 647 if (next_word(&cp, w)) { 648 sfilt.ff_flow.fi_gpi = 649 (u_int32_t)strtoul(w, NULL, 0); 650 sfilt.ff_flow.fi_gpi = 651 htonl(sfilt.ff_flow.fi_gpi); 652 } 653 } else if (EQUAL(w, "dontwarn")) 654 dontwarn = 1; 655 } 656 657 /* 658 * Add the filter. 659 */ 660 filter_dontwarn = dontwarn; /* XXX */ 661 error = qcmd_add_filter(ifname, class_name, flname, &sfilt); 662 filter_dontwarn = 0; /* XXX */ 663 if (error) { 664 LOG(LOG_ERR, 0, 665 "can't add filter to class '%s' on interface '%s'", 666 class_name, ifname); 667 return (0); 668 } 669 return (1); 670 } 671 672 #ifdef INET6 673 static int 674 filter6_parser(char *cmdbuf) 675 { 676 char w[MAX_WORD], *cp = cmdbuf; 677 char *ifname, class_name[MAX_WORD], fltr_name[MAX_WORD]; 678 char *flname = NULL; 679 struct flow_filter6 sfilt; 680 int protocol; 681 u_char tclass, tclassmask; 682 int ruleno; 683 int dontwarn = 0; 684 int ret; 685 686 memset(&sfilt, 0, sizeof(sfilt)); 687 sfilt.ff_flow6.fi6_family = AF_INET6; 688 689 if (!get_ifname(&cp, &ifname)) { 690 LOG(LOG_ERR, 0, "missing interface name"); 691 return (0); 692 } 693 694 if (!next_word(&cp, class_name)) { 695 LOG(LOG_ERR, 0, "missing class name"); 696 return (0); 697 } 698 699 fltr_name[0] = '\0'; 700 ruleno = 0; 701 if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) { 702 LOG(LOG_ERR, 0, "bad filter option"); 703 return (0); 704 } 705 if (fltr_name[0] != '\0') 706 flname = fltr_name; 707 sfilt.ff_ruleno = ruleno; 708 709 /* get filter destination address */ 710 if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_dst, 711 &sfilt.ff_mask6.mask6_dst)) { 712 LOG(LOG_ERR, 0, "bad destination address"); 713 return (0); 714 } 715 716 /* get filter destination port */ 717 if (!next_word(&cp, w)) { 718 LOG(LOG_ERR, 0, "missing filter destination port"); 719 return (0); 720 } 721 if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) { 722 LOG(LOG_ERR, 0, "bad filter destination port"); 723 return (0); 724 } 725 726 /* get filter source address */ 727 if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_src, 728 &sfilt.ff_mask6.mask6_src)) { 729 LOG(LOG_ERR, 0, "bad source address"); 730 return (0); 731 } 732 733 /* get filter source port */ 734 if (!next_word(&cp, w)) { 735 LOG(LOG_ERR, 0, "missing filter source port"); 736 return (0); 737 } 738 if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) { 739 LOG(LOG_ERR, 0, "bad filter source port"); 740 return (0); 741 } 742 743 /* get filter protocol id */ 744 if (!next_word(&cp, w)) { 745 LOG(LOG_ERR, 0, "missing filter protocol"); 746 return (0); 747 } 748 if (!get_proto(w, &protocol)) { 749 LOG(LOG_ERR, 0, "bad protocol"); 750 return (0); 751 } 752 sfilt.ff_flow6.fi6_proto = protocol; 753 754 while (next_word(&cp, w)) { 755 if (EQUAL(w, "tclass")) { 756 tclass = 0; 757 tclassmask = 0xff; 758 759 if (next_word(&cp, w)) { 760 tclass = (u_char)strtol(w, NULL, 0); 761 if (next_word(&cp, w)) { 762 if (EQUAL(w, "tclassmask")) { 763 next_word(&cp, w); 764 tclassmask = 765 (u_char)strtol(w, NULL, 0); 766 } 767 } 768 } 769 sfilt.ff_flow6.fi6_tclass = tclass; 770 sfilt.ff_mask6.mask6_tclass = tclassmask; 771 } else if (EQUAL(w, "gpi")) { 772 if (next_word(&cp, w)) { 773 sfilt.ff_flow6.fi6_gpi = 774 (u_int32_t)strtoul(w, NULL, 0); 775 sfilt.ff_flow6.fi6_gpi = 776 htonl(sfilt.ff_flow6.fi6_gpi); 777 } 778 } else if (EQUAL(w, "flowlabel")) { 779 if (next_word(&cp, w)) { 780 sfilt.ff_flow6.fi6_flowlabel = 781 (u_int32_t)strtoul(w, NULL, 0) & 0x000fffff; 782 sfilt.ff_flow6.fi6_flowlabel = 783 htonl(sfilt.ff_flow6.fi6_flowlabel); 784 } 785 } else if (EQUAL(w, "dontwarn")) 786 dontwarn = 1; 787 } 788 789 /* 790 * Add the filter. 791 */ 792 filter_dontwarn = dontwarn; /* XXX */ 793 ret = qcmd_add_filter(ifname, class_name, flname, 794 (struct flow_filter *)&sfilt); 795 filter_dontwarn = 0; /* XXX */ 796 if (ret) { 797 LOG(LOG_ERR, 0, 798 "can't add filter to class '%s' on interface '%s'", 799 class_name, ifname); 800 return (0); 801 } 802 803 return (1); 804 } 805 806 static int 807 get_ip6addr(char **cpp, struct in6_addr *addr, struct in6_addr *mask) 808 { 809 char w[MAX_WORD], *prefix; 810 u_char *cp; 811 int len; 812 813 *addr = in6addr_any; /* set all 0 */ 814 *mask = in6addr_any; /* set all 0 */ 815 816 if (!next_word(cpp, w)) 817 return (0); 818 819 if (EQUAL(w, "0")) 820 /* abbreviation of a wildcard (::0) */ 821 return (1); 822 823 if ((prefix = strchr(w, '/')) != NULL) { 824 /* address has prefix length */ 825 *prefix++ = '\0'; 826 } 827 828 if (inet_pton(AF_INET6, w, addr) != 1) 829 return (0); 830 831 if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefix == NULL) 832 /* wildcard */ 833 return (1); 834 835 /* convert address prefix length to address mask */ 836 if (prefix != NULL) { 837 len = (int)strtol(prefix, NULL, 0); 838 if ((len < 0) || (len > 128)) 839 return (0); 840 for (cp = (u_char *)mask; len > 7; len -= 8) 841 *cp++ = 0xff; 842 if (len > 0) 843 *cp = (0xff << (8 - len)) & 0xff; 844 845 IN6ADDR32(addr, 0) &= IN6ADDR32(mask, 0); 846 IN6ADDR32(addr, 1) &= IN6ADDR32(mask, 1); 847 IN6ADDR32(addr, 2) &= IN6ADDR32(mask, 2); 848 IN6ADDR32(addr, 3) &= IN6ADDR32(mask, 3); 849 } else 850 /* full mask */ 851 memset(mask, 0xff, sizeof(struct in6_addr)); 852 853 return (1); 854 } 855 856 #endif /* INET6 */ 857 858 static int 859 ctl_parser(char *cmdbuf) 860 { 861 char w[MAX_WORD], *cp = cmdbuf; 862 char *ifname; 863 int state; 864 int rval; 865 866 if (!get_ifname(&cp, &ifname)) { 867 printf("missing interface name in %s, line %d", 868 altqconfigfile, line_no); 869 return (0); 870 } 871 872 if (!next_word(&cp, w)) { 873 state = is_q_enabled(ifname); 874 printf("altq %s on %s\n", 875 state ? "enabled" : "disabled", ifname); 876 return (1); 877 } 878 879 if (EQUAL(w, "enable")) { 880 rval = qcmd_enable(ifname); 881 printf("altq %s on %s\n", 882 (rval == 0) ? "enabled" : "enable failed!", ifname); 883 } else if (EQUAL(w, "disable")) { 884 rval = qcmd_disable(ifname); 885 printf("altq %s on %s\n", 886 (rval == 0) ? "disabled" : "disable failed!", ifname); 887 } else if (EQUAL(w, "reload")) { 888 printf("reinitializing altq...\n"); 889 qcmd_destroyall(); 890 qcmd_init(); 891 } else 892 return (0); 893 return (1); 894 } 895 896 static int 897 delete_parser(char *cmdbuf) 898 { 899 char *cp = cmdbuf; 900 char *ifname, class_name[MAX_WORD], filter_name[MAX_WORD]; 901 int ret; 902 903 if (!get_ifname(&cp, &ifname)) { 904 LOG(LOG_ERR, 0, "missing interface name"); 905 return (0); 906 } 907 908 if (!next_word(&cp, class_name)) { 909 LOG(LOG_ERR, 0, "missing class name"); 910 return (0); 911 } 912 913 /* check if filter is specified */ 914 if (next_word(&cp, filter_name)) { 915 ret = qcmd_delete_filter(ifname, class_name, filter_name); 916 if (ret) { 917 LOG(LOG_ERR, 0, 918 "can't delete filter '%s' on interface '%s'", 919 filter_name, ifname); 920 return (0); 921 } 922 return (1); 923 } 924 925 ret = qcmd_delete_class(ifname, class_name); 926 if (ret) { 927 LOG(LOG_ERR, 0, 928 "can't delete class '%s' on interface '%s'", 929 class_name, ifname); 930 return (0); 931 } 932 933 return (1); 934 } 935 936 static int 937 red_parser(char *cmdbuf) 938 { 939 char w[MAX_WORD], *cp = cmdbuf; 940 int th_min, th_max, inv_pmax; 941 942 if (!next_word(&cp, w)) 943 goto bad; 944 th_min = (int)strtol(w, NULL, 0); 945 946 if (!next_word(&cp, w)) 947 goto bad; 948 th_max = (int)strtol(w, NULL, 0); 949 950 if (!next_word(&cp, w)) 951 goto bad; 952 inv_pmax = (int)strtol(w, NULL, 0); 953 954 if (qop_red_set_defaults(th_min, th_max, inv_pmax) != 0) { 955 LOG(LOG_ERR, 0, "can't set red default parameters"); 956 return (0); 957 } 958 959 return (1); 960 961 bad: 962 LOG(LOG_ERR, 0, "bad red parameter"); 963 return (0); 964 } 965 966 static int 967 rio_parser(char *cmdbuf) 968 { 969 char w[MAX_WORD], *cp = cmdbuf; 970 int i; 971 struct redparams params[RIO_NDROPPREC]; 972 973 for (i = 0; i < RIO_NDROPPREC; i++) { 974 if (!next_word(&cp, w)) 975 goto bad; 976 params[i].th_min = (int)strtol(w, NULL, 0); 977 978 if (!next_word(&cp, w)) 979 goto bad; 980 params[i].th_max = (int)strtol(w, NULL, 0); 981 982 if (!next_word(&cp, w)) 983 goto bad; 984 params[i].inv_pmax = (int)strtol(w, NULL, 0); 985 } 986 987 if (qop_rio_set_defaults(¶ms[0]) != 0) { 988 LOG(LOG_ERR, 0, "can't set rio default parameters"); 989 return (0); 990 } 991 992 return (1); 993 994 bad: 995 LOG(LOG_ERR, 0, "bad rio parameter"); 996 return (0); 997 } 998 999 static int 1000 conditioner_parser(char *cmdbuf) 1001 { 1002 char cdnr_name[MAX_WORD], *cp = cmdbuf; 1003 char *ifname; 1004 struct tc_action action[MAX_ACTIONS]; 1005 1006 if (!get_ifname(&cp, &ifname)) { 1007 LOG(LOG_ERR, 0, "missing interface name"); 1008 return (0); 1009 } 1010 1011 /* get conditioner name */ 1012 if (!next_word(&cp, cdnr_name)) { 1013 LOG(LOG_ERR, 0, "missing cdnr name"); 1014 return (0); 1015 } 1016 1017 if (tc_action_parser(ifname, &cp, &action[0]) == 0) 1018 return (0); 1019 1020 if (qcmd_cdnr_add_element(NULL, ifname, cdnr_name, &action[0]) != 0) 1021 return (0); 1022 return (1); 1023 } 1024 1025 /* 1026 * recursively parse '<'tc_action'>' 1027 * note that array "action" grows during recursive parse. 1028 */ 1029 static int 1030 tc_action_parser(char *ifname, char **cpp, struct tc_action *action) 1031 { 1032 char *cp, *start, *end; 1033 char type[MAX_WORD], w[MAX_WORD]; 1034 int depth, i; 1035 struct tb_profile profile[2]; 1036 1037 /* 1038 * find a possibly nested pair of '<' and '>', 1039 * make them pointed by 'start' and 'end'. 1040 */ 1041 start = strchr(*cpp, '<'); 1042 if (start == NULL) { 1043 LOG(LOG_ERR, 0, "conditioner action missing"); 1044 return (0); 1045 } 1046 depth = 1; 1047 cp = start + 1; 1048 do { 1049 end = strpbrk(cp, "<>"); 1050 if (end == NULL) { 1051 LOG(LOG_ERR, 0, 1052 "conditioner action delimiter mismatch"); 1053 return (0); 1054 } 1055 if (*end == '<') 1056 depth++; 1057 else if (*end == '>') 1058 depth--; 1059 cp = end + 1; 1060 } while (depth > 0); 1061 *end = '\0'; 1062 *cpp = end + 1; 1063 cp = start + 1; 1064 1065 if (IsDebug(DEBUG_ALTQ)) { 1066 printf("tc_action_parser: [%s]\n", cp); 1067 } 1068 1069 if (!next_word(&cp, type)) { 1070 LOG(LOG_ERR, 0, "missing conditioner action type"); 1071 return (0); 1072 } 1073 1074 /* 1075 * action type specific process 1076 */ 1077 if (EQUAL(type, "conditioner")) { 1078 if (!next_word(&cp, w)) { 1079 LOG(LOG_ERR, 0, 1080 "missing conditioner name"); 1081 return (0); 1082 } 1083 action->tca_code = TCACODE_HANDLE; 1084 action->tca_handle = cdnr_name2handle(ifname, w); 1085 if (action->tca_handle == CDNR_NULL_HANDLE) { 1086 LOG(LOG_ERR, 0, 1087 "wrong conditioner name %s", w); 1088 return (0); 1089 } 1090 } else if (EQUAL(type, "pass")) { 1091 action->tca_code = TCACODE_PASS; 1092 } else if (EQUAL(type, "drop")) { 1093 action->tca_code = TCACODE_DROP; 1094 } else if (EQUAL(type, "mark")) { 1095 if (!next_word(&cp, w)) { 1096 LOG(LOG_ERR, 0, "missing dscp"); 1097 return (0); 1098 } 1099 action->tca_code = TCACODE_MARK; 1100 action->tca_dscp = (u_int8_t)strtol(w, NULL, 0); 1101 } else if (EQUAL(type, "tbmeter")) { 1102 if (!next_word(&cp, w)) { 1103 LOG(LOG_ERR, 0, "missing tb profile"); 1104 return (0); 1105 } 1106 profile[0].rate = atobps(w); 1107 if (!next_word(&cp, w)) { 1108 LOG(LOG_ERR, 0, "missing tb profile"); 1109 return (0); 1110 } 1111 profile[0].depth = atobytes(w); 1112 if (tc_action_parser(ifname, &cp, &action[1]) == 0) 1113 return (0); 1114 if (tc_action_parser(ifname, &cp, &action[2]) == 0) 1115 return (0); 1116 1117 if (qcmd_cdnr_add_tbmeter(action, ifname, NULL, &profile[0], 1118 &action[1], &action[2]) != 0) 1119 return (0); 1120 } else if (EQUAL(type, "trtcm")) { 1121 int coloraware = 0; /* default is color-blind */ 1122 1123 for (i=0; i<2; i++) { 1124 if (!next_word(&cp, w)) { 1125 LOG(LOG_ERR, 0, "missing tb profile"); 1126 return (0); 1127 } 1128 profile[i].rate = atobps(w); 1129 if (!next_word(&cp, w)) { 1130 LOG(LOG_ERR, 0, "missing tb profile"); 1131 return (0); 1132 } 1133 profile[i].depth = atobytes(w); 1134 } 1135 if (tc_action_parser(ifname, &cp, &action[1]) == 0) 1136 return (0); 1137 if (tc_action_parser(ifname, &cp, &action[2]) == 0) 1138 return (0); 1139 if (tc_action_parser(ifname, &cp, &action[3]) == 0) 1140 return (0); 1141 if (next_word(&cp, w)) { 1142 if (EQUAL(w, "coloraware")) 1143 coloraware = 1; 1144 else if (EQUAL(w, "colorblind")) 1145 coloraware = 0; 1146 } 1147 1148 if (qcmd_cdnr_add_trtcm(action, ifname, NULL, 1149 &profile[0], &profile[1], 1150 &action[1], &action[2], &action[3], 1151 coloraware) != 0) 1152 return (0); 1153 } else if (EQUAL(type, "tswtcm")) { 1154 u_int32_t cmtd_rate, peak_rate, avg_interval; 1155 1156 if (!next_word(&cp, w)) { 1157 LOG(LOG_ERR, 0, "missing cmtd rate"); 1158 return (0); 1159 } 1160 cmtd_rate = atobps(w); 1161 1162 if (!next_word(&cp, w)) { 1163 LOG(LOG_ERR, 0, "missing peak rate"); 1164 return (0); 1165 } 1166 peak_rate = atobps(w); 1167 1168 if (!next_word(&cp, w)) { 1169 LOG(LOG_ERR, 0, "missing avg interval"); 1170 return (0); 1171 } 1172 avg_interval = (u_int32_t)strtoul(w, NULL, 0); 1173 1174 if (tc_action_parser(ifname, &cp, &action[1]) == 0) 1175 return (0); 1176 if (tc_action_parser(ifname, &cp, &action[2]) == 0) 1177 return (0); 1178 if (tc_action_parser(ifname, &cp, &action[3]) == 0) 1179 return (0); 1180 1181 if (qcmd_cdnr_add_tswtcm(action, ifname, NULL, 1182 cmtd_rate, peak_rate, avg_interval, 1183 &action[1], &action[2], &action[3]) 1184 != 0) 1185 return (0); 1186 } else { 1187 LOG(LOG_ERR, 0, "unkown action type %s"); 1188 return (0); 1189 } 1190 1191 *end = '>'; /* restore the end delimiter */ 1192 1193 return (1); 1194 } 1195