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