/* $NetBSD: parser.c,v 1.11 2011/08/16 12:49:13 christos Exp $ */ /* $KAME: parser.c,v 1.16 2002/02/20 10:40:39 kjc Exp $ */ /* * Copyright (C) 1999-2002 * Sony Computer Science Laboratories, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "altq_qop.h" #include "qop_cdnr.h" static int is_qdisc_name(const char *); static int qdisc_interface_parser(const char *, const char *, int, char **); static int qdisc_class_parser(const char *, const char *, const char *, const char *, int, char **); static int next_word(char **, char *); static int get_ifname(char **, char **); static int get_addr(char **, struct in_addr *, struct in_addr *); static int get_port(const char *, u_int16_t *); static int get_proto(const char *, int *); static int get_fltr_opts(char **, char *, size_t, int *); static int interface_parser(char *); static int class_parser(char *) ; static int filter_parser(char *); #ifdef INET6 static int filter6_parser(char *); static int get_ip6addr(char **, struct in6_addr *, struct in6_addr *); #endif static int ctl_parser(char *); static int delete_parser(char *); static int red_parser(char *); static int rio_parser(char *); static int conditioner_parser(char *); static int tc_action_parser(char *, char **, struct tc_action *); #define MAX_LINE 1024 #define MAX_WORD 64 #define MAX_ARGS 64 #define MAX_ACTIONS 16 #ifndef MAX #define MAX(a,b) (((a)>(b))?(a):(b)) #endif #ifndef MIN #define MIN(a,b) (((a)<(b))?(a):(b)) #endif #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0) int line_no = 0; int filter_dontwarn; static char curifname[IFNAMSIZ]; static struct if_nameindex *if_namelist = NULL; struct cmd_tab { const char *cmd; int (*parser)(char *); const char *help; } cmd_tab[] = { {"?", NULL, "?"}, {"help", NULL, "help"}, {"quit", NULL, "quit"}, {"interface", interface_parser, "interface if_name [bandwidth bps] [cbq|hfsc]"}, {"class", class_parser, "class discipline if_name class_name [parent]"}, {"filter", filter_parser, "filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]"}, {"altq", ctl_parser, "altq if_name {enable|disable}"}, {"delete", delete_parser, "delete if_name class_name [filter_name]"}, #ifdef INET6 {"filter6", filter6_parser, "filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]"}, #endif {"red", red_parser, "red th_min th_max inv_pmax"}, {"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"}, {"conditioner", conditioner_parser, "conditioner if_name cdnr_name "}, {"debug", NULL, "debug"}, {NULL, NULL, NULL} /* termination */ }; /* * read one line from the specified stream. if it's a command, * execute the command. * returns 1 if OK, 0 if error or EOF. */ int do_command(FILE *fp) { char cmd_line[MAX_LINE], cmd[MAX_WORD], *cp; struct cmd_tab *tp; int len, rval; /* * read a line from the stream and make it a null-terminated string */ cp = cmd_line; read_line: if (fgets(cp, &cmd_line[MAX_LINE] - cp, fp) == NULL) /* EOF or error */ return(0); line_no++; /* null-terminate the line */ if ((len = strlen(cmd_line)) > 0) { cp = cmd_line + len - 1; if (*cp == '\n') { /* if escaped newline, read next line */ if (len > 1 && *(cp - 1) == '\\') goto read_line; *cp = '\0'; } else if (!feof(fp)) err(1, "LINE %d too long!", line_no); } /* trim comments */ if ((cp = strchr(cmd_line, '#')) != NULL) *cp = '\0'; cp = cmd_line; if ((len = next_word(&cp, cmd)) == 0) /* no command in this line */ return (1); /* fnind the corresponding parser */ rval = 0; for (tp = cmd_tab; tp->cmd != NULL; tp++) if (strncmp(cmd, tp->cmd, len) == 0) break; if (tp->cmd == NULL) { if (fp == stdin) { printf(" ?? %s\n", cmd); rval = 1; } else LOG(LOG_ERR, 0, "unknown command: %s", cmd); return (rval); } if (tp->parser != NULL) rval = (*tp->parser)(cp); else { /* handle other commands */ if (strcmp(tp->cmd, "quit") == 0) rval = 0; else if (strcmp(tp->cmd, "help") == 0 || strcmp(tp->cmd, "?") == 0) { for (tp = cmd_tab; tp->cmd != NULL; tp++) printf("%s\n", tp->help); rval = 1; } else if (strcmp(tp->cmd, "debug") == 0) { if (m_debug & DEBUG_ALTQ) { /* turn off verbose */ l_debug = LOG_INFO; m_debug &= ~DEBUG_ALTQ; } else { /* turn on verbose */ l_debug = LOG_DEBUG; m_debug |= DEBUG_ALTQ; } rval = 1; } } return (rval); } static int is_qdisc_name(const char *qname) { struct qdisc_parser *qp; for (qp = qdisc_parser; qp->qname != NULL; qp++) if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) return (1); return (0); } static int qdisc_interface_parser(const char * qname, const char *ifname, int argc, char **argv) { struct qdisc_parser *qp; for (qp = qdisc_parser; qp->qname != NULL; qp++) if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) return (*qp->interface_parser)(ifname, argc, argv); return (0); } static int qdisc_class_parser(const char *qname, const char *ifname, const char *class_name, const char *parent_name, int argc, char **argv) { struct qdisc_parser *qp; struct ifinfo *ifinfo; for (qp = qdisc_parser; qp->qname != NULL; qp++) if (strncmp(qp->qname, qname, strlen(qp->qname)) == 0) { if (qp->class_parser == NULL) { LOG(LOG_ERR, 0, "class can't be specified for %s", qp->qname); return (0); } if ((ifinfo = ifname2ifinfo(ifname)) == NULL) { LOG(LOG_ERR, 0, "no such interface"); return (0); } if (strncmp(ifinfo->qdisc->qname, qname, strlen(ifinfo->qdisc->qname)) != 0) { LOG(LOG_ERR, 0, "qname doesn't match the interface"); return (0); } return (*qp->class_parser)(ifname, class_name, parent_name, argc, argv); } return (0); } /* * read the config file */ int qcmd_config(void) { FILE *fp; int rval; if (if_namelist != NULL) if_freenameindex(if_namelist); if_namelist = if_nameindex(); curifname[0] = '\0'; LOG(LOG_INFO, 0, "ALTQ config file is %s", altqconfigfile); fp = fopen(altqconfigfile, "r"); if (fp == NULL) { LOG(LOG_ERR, errno, "can't open %s", altqconfigfile, 0); return (QOPERR_INVAL); } line_no = 0; rval = 1; while (rval) rval = do_command(fp); if (!feof(fp)) { LOG(LOG_ERR, 0, "Error in %s, line %d. config failed.", altqconfigfile, line_no); (void) qcmd_destroyall(); rval = QOPERR_INVAL; } else rval = 0; (void)fclose(fp); line_no = 0; return (rval); } static int next_word(char **cpp, char *b) { char *cp; int i; cp = *cpp; while (*cp == ' ' || *cp == '\t') cp++; for (i = 0; i < MAX_WORD - 1; i++) { if (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\0') break; *b++ = *cp++; } *b = '\0'; *cpp = cp; return (i); } char * cur_ifname(void) { return (curifname); } u_int get_ifindex(const char *ifname) { struct if_nameindex *ifnp; for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++) if (strcmp(ifname, ifnp->if_name) == 0) return (ifnp->if_index); return (0); } static int get_ifname(char **cpp, char **ifnamep) { char w[MAX_WORD], *ocp; struct if_nameindex *ifnp; ocp = *cpp; if (next_word(&ocp, w) && if_namelist != NULL) for (ifnp = if_namelist; ifnp->if_name != NULL; ifnp++) if (strcmp(w, ifnp->if_name) == 0) { /* if_name found. advance the word pointer */ *cpp = ocp; strlcpy(curifname, w, sizeof(curifname)); *ifnamep = curifname; return (1); } /* this is not interface name. use one in the context. */ if (curifname[0] == '\0') return (0); *ifnamep = curifname; return (1); } /* set address and netmask in network byte order */ static int get_addr(char **cpp, struct in_addr *addr, struct in_addr *mask) { char w[MAX_WORD], *ocp; struct in_addr tmp; addr->s_addr = 0; mask->s_addr = 0xffffffff; if (!next_word(cpp, w)) return (0); if (inet_aton((char *)w, &tmp) != 1) { /* try gethostbyname */ struct hostent *h; if ((h = gethostbyname(w)) == NULL || h->h_addrtype != AF_INET || h->h_length != 4) return (0); bcopy(h->h_addr, &tmp, (size_t)h->h_length); } addr->s_addr = tmp.s_addr; /* check if netmask option is present */ ocp = *cpp; if (next_word(&ocp, w) && EQUAL(w, "netmask")) { if (!next_word(&ocp, w)) return (0); if (inet_aton((char *)w, (struct in_addr *)&tmp) != 1) return (0); mask->s_addr = tmp.s_addr; *cpp = ocp; return (1); } /* no netmask option */ return (1); } /* returns service number in network byte order */ static int get_port(const char *name, u_int16_t *port_no) { struct servent *s; u_int16_t num; if (isdigit((unsigned char)name[0])) { num = (u_int16_t)strtol(name, NULL, 0); *port_no = htons(num); return (1); } if ((s = getservbyname(name, 0)) == NULL) return (0); *port_no = (u_int16_t)s->s_port; return (1); } static int get_proto(const char *name, int *proto_no) { struct protoent *p; if (isdigit((unsigned char)name[0])) { *proto_no = (int)strtol(name, NULL, 0); return (1); } if ((p = getprotobyname(name)) == NULL) return (0); *proto_no = p->p_proto; return (1); } static int get_fltr_opts(char **cpp, char *fltr_name, size_t len, int *ruleno) { char w[MAX_WORD], *ocp; ocp = *cpp; while (next_word(&ocp, w)) { if (EQUAL(w, "name")) { if (!next_word(&ocp, w)) return (0); strlcpy(fltr_name, w, len); *cpp = ocp; } else if (EQUAL(w, "ruleno")) { if (!next_word(&ocp, w)) return (0); *ruleno = (int)strtol(w, NULL, 0); *cpp = ocp; } else break; } return (1); } #define DISCIPLINE_NONE 0 static int interface_parser(char *cmdbuf) { char w[MAX_WORD], *ap, *cp = cmdbuf; char *ifname, *argv[MAX_ARGS], qdisc_name[MAX_WORD]; int argc; if (!get_ifname(&cp, &ifname)) { LOG(LOG_ERR, 0, "missing interface name"); return (0); } /* create argment list & look for scheduling discipline options. */ snprintf(qdisc_name, sizeof qdisc_name, "null"); argc = 0; ap = w; while (next_word(&cp, ap)) { if (is_qdisc_name(ap)) strlcpy(qdisc_name, ap, sizeof qdisc_name); argv[argc] = ap; ap += strlen(ap) + 1; argc++; if (argc >= MAX_ARGS) { LOG(LOG_ERR, 0, "too many args"); return (0); } } return qdisc_interface_parser(qdisc_name, ifname, argc, argv); } static int class_parser(char *cmdbuf) { char w[MAX_WORD], *cp = cmdbuf; char *ifname, qdisc_name[MAX_WORD]; char class_name[MAX_WORD], parent_name[MAX_WORD]; char *clname = class_name; char *parent = NULL; char *argv[MAX_ARGS], *ap; int argc; /* get scheduling class */ if (!next_word(&cp, qdisc_name)) { LOG(LOG_ERR, 0, "missing discipline"); return (0); } if (!is_qdisc_name(qdisc_name)) { LOG(LOG_ERR, 0, "unknown discipline '%s'", qdisc_name); return (0); } /* get interface name */ if (!get_ifname(&cp, &ifname)) { LOG(LOG_ERR, 0, "missing interface name"); return (0); } /* get class name */ if (!next_word(&cp, class_name)) { LOG(LOG_ERR, 0, "missing class name"); return (0); } /* get parent name */ if (!next_word(&cp, parent_name)) { LOG(LOG_ERR, 0, "missing parent class"); return (0); } if (!EQUAL(parent_name, "null") && !EQUAL(parent_name, "NULL")) parent = parent_name; else parent = NULL; ap = w; argc = 0; while (next_word(&cp, ap)) { argv[argc] = ap; ap += strlen(ap) + 1; argc++; if (argc >= MAX_ARGS) { LOG(LOG_ERR, 0, "too many args"); return (0); } } return qdisc_class_parser(qdisc_name, ifname, clname, parent, argc, argv); } static int filter_parser(char *cmdbuf) { char w[MAX_WORD], *cp = cmdbuf; char *ifname, class_name[MAX_WORD], fltr_name[MAX_WORD]; char *flname = NULL; struct flow_filter sfilt; int protocol; u_char tos, tosmask; int ruleno; int dontwarn = 0; int error; memset(&sfilt, 0, sizeof(sfilt)); sfilt.ff_flow.fi_family = AF_INET; if (!get_ifname(&cp, &ifname)) { LOG(LOG_ERR, 0, "missing interface name in filter command"); return (0); } if (!next_word(&cp, class_name)) { LOG(LOG_ERR, 0, "missing class name in filter command"); return (0); } fltr_name[0] = '\0'; ruleno = 0; if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) { LOG(LOG_ERR, 0, "bad filter option"); return (0); } if (fltr_name[0] != '\0') flname = fltr_name; sfilt.ff_ruleno = ruleno; /* get filter destination Address */ if (!get_addr(&cp, &sfilt.ff_flow.fi_dst, &sfilt.ff_mask.mask_dst)) { LOG(LOG_ERR, 0, "bad filter destination address"); return (0); } /* get filter destination port */ if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing filter destination port"); return (0); } if (!get_port(w, &sfilt.ff_flow.fi_dport)) { LOG(LOG_ERR, 0, "bad filter destination port"); return (0); } /* get filter source address */ if (!get_addr(&cp, &sfilt.ff_flow.fi_src, &sfilt.ff_mask.mask_src)) { LOG(LOG_ERR, 0, "bad filter source address"); return (0); } /* get filter source port */ if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing filter source port"); return (0); } if (!get_port(w, &sfilt.ff_flow.fi_sport)) { LOG(LOG_ERR, 0, "bad filter source port"); return (0); } /* get filter protocol id */ if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing filter protocol"); return (0); } if (!get_proto(w, &protocol)) { LOG(LOG_ERR, 0, "bad protocol"); return (0); } sfilt.ff_flow.fi_proto = protocol; while (next_word(&cp, w)) { if (EQUAL(w, "tos")) { tos = 0; tosmask = 0xff; if (next_word(&cp, w)) { tos = (u_char)strtol(w, NULL, 0); if (next_word(&cp, w)) { if (EQUAL(w, "tosmask")) { next_word(&cp, w); tosmask = (u_char)strtol(w, NULL, 0); } } } sfilt.ff_flow.fi_tos = tos; sfilt.ff_mask.mask_tos = tosmask; } else if (EQUAL(w, "gpi")) { if (next_word(&cp, w)) { sfilt.ff_flow.fi_gpi = (u_int32_t)strtoul(w, NULL, 0); sfilt.ff_flow.fi_gpi = htonl(sfilt.ff_flow.fi_gpi); } } else if (EQUAL(w, "dontwarn")) dontwarn = 1; } /* * Add the filter. */ filter_dontwarn = dontwarn; /* XXX */ error = qcmd_add_filter(ifname, class_name, flname, &sfilt); filter_dontwarn = 0; /* XXX */ if (error) { LOG(LOG_ERR, 0, "can't add filter to class '%s' on interface '%s'", class_name, ifname); return (0); } return (1); } #ifdef INET6 static int filter6_parser(char *cmdbuf) { char w[MAX_WORD], *cp = cmdbuf; char *ifname, class_name[MAX_WORD], fltr_name[MAX_WORD]; char *flname = NULL; struct flow_filter6 sfilt; int protocol; u_char tclass, tclassmask; int ruleno; int dontwarn = 0; int ret; memset(&sfilt, 0, sizeof(sfilt)); sfilt.ff_flow6.fi6_family = AF_INET6; if (!get_ifname(&cp, &ifname)) { LOG(LOG_ERR, 0, "missing interface name"); return (0); } if (!next_word(&cp, class_name)) { LOG(LOG_ERR, 0, "missing class name"); return (0); } fltr_name[0] = '\0'; ruleno = 0; if (!get_fltr_opts(&cp, &fltr_name[0], sizeof(fltr_name), &ruleno)) { LOG(LOG_ERR, 0, "bad filter option"); return (0); } if (fltr_name[0] != '\0') flname = fltr_name; sfilt.ff_ruleno = ruleno; /* get filter destination address */ if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_dst, &sfilt.ff_mask6.mask6_dst)) { LOG(LOG_ERR, 0, "bad destination address"); return (0); } /* get filter destination port */ if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing filter destination port"); return (0); } if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) { LOG(LOG_ERR, 0, "bad filter destination port"); return (0); } /* get filter source address */ if (!get_ip6addr(&cp, &sfilt.ff_flow6.fi6_src, &sfilt.ff_mask6.mask6_src)) { LOG(LOG_ERR, 0, "bad source address"); return (0); } /* get filter source port */ if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing filter source port"); return (0); } if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) { LOG(LOG_ERR, 0, "bad filter source port"); return (0); } /* get filter protocol id */ if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing filter protocol"); return (0); } if (!get_proto(w, &protocol)) { LOG(LOG_ERR, 0, "bad protocol"); return (0); } sfilt.ff_flow6.fi6_proto = protocol; while (next_word(&cp, w)) { if (EQUAL(w, "tclass")) { tclass = 0; tclassmask = 0xff; if (next_word(&cp, w)) { tclass = (u_char)strtol(w, NULL, 0); if (next_word(&cp, w)) { if (EQUAL(w, "tclassmask")) { next_word(&cp, w); tclassmask = (u_char)strtol(w, NULL, 0); } } } sfilt.ff_flow6.fi6_tclass = tclass; sfilt.ff_mask6.mask6_tclass = tclassmask; } else if (EQUAL(w, "gpi")) { if (next_word(&cp, w)) { sfilt.ff_flow6.fi6_gpi = (u_int32_t)strtoul(w, NULL, 0); sfilt.ff_flow6.fi6_gpi = htonl(sfilt.ff_flow6.fi6_gpi); } } else if (EQUAL(w, "flowlabel")) { if (next_word(&cp, w)) { sfilt.ff_flow6.fi6_flowlabel = (u_int32_t)strtoul(w, NULL, 0) & 0x000fffff; sfilt.ff_flow6.fi6_flowlabel = htonl(sfilt.ff_flow6.fi6_flowlabel); } } else if (EQUAL(w, "dontwarn")) dontwarn = 1; } /* * Add the filter. */ filter_dontwarn = dontwarn; /* XXX */ ret = qcmd_add_filter(ifname, class_name, flname, (struct flow_filter *)&sfilt); filter_dontwarn = 0; /* XXX */ if (ret) { LOG(LOG_ERR, 0, "can't add filter to class '%s' on interface '%s'", class_name, ifname); return (0); } return (1); } static int get_ip6addr(char **cpp, struct in6_addr *addr, struct in6_addr *mask) { char w[MAX_WORD], *prefix; u_char *cp; int len; *addr = in6addr_any; /* set all 0 */ *mask = in6addr_any; /* set all 0 */ if (!next_word(cpp, w)) return (0); if (EQUAL(w, "0")) /* abbreviation of a wildcard (::0) */ return (1); if ((prefix = strchr(w, '/')) != NULL) { /* address has prefix length */ *prefix++ = '\0'; } if (inet_pton(AF_INET6, w, addr) != 1) return (0); if (IN6_IS_ADDR_UNSPECIFIED(addr) && prefix == NULL) /* wildcard */ return (1); /* convert address prefix length to address mask */ if (prefix != NULL) { len = (int)strtol(prefix, NULL, 0); if ((len < 0) || (len > 128)) return (0); for (cp = (u_char *)mask; len > 7; len -= 8) *cp++ = 0xff; if (len > 0) *cp = (0xff << (8 - len)) & 0xff; IN6ADDR32_SET(addr, 0, IN6ADDR32_GET(mask, 0) & IN6ADDR32_GET(addr, 0)); IN6ADDR32_SET(addr, 1, IN6ADDR32_GET(mask, 1) & IN6ADDR32_GET(addr, 1)); IN6ADDR32_SET(addr, 2, IN6ADDR32_GET(mask, 2) & IN6ADDR32_GET(addr, 2)); IN6ADDR32_SET(addr, 3, IN6ADDR32_GET(mask, 3) & IN6ADDR32_GET(addr, 3)); } else /* full mask */ memset(mask, 0xff, sizeof(struct in6_addr)); return (1); } #endif /* INET6 */ static int ctl_parser(char *cmdbuf) { char w[MAX_WORD], *cp = cmdbuf; char *ifname; int state; int rval; if (!get_ifname(&cp, &ifname)) { printf("missing interface name in %s, line %d", altqconfigfile, line_no); return (0); } if (!next_word(&cp, w)) { state = is_q_enabled(ifname); printf("altq %s on %s\n", state ? "enabled" : "disabled", ifname); return (1); } if (EQUAL(w, "enable")) { rval = qcmd_enable(ifname); printf("altq %s on %s\n", (rval == 0) ? "enabled" : "enable failed!", ifname); } else if (EQUAL(w, "disable")) { rval = qcmd_disable(ifname); printf("altq %s on %s\n", (rval == 0) ? "disabled" : "disable failed!", ifname); } else if (EQUAL(w, "reload")) { printf("reinitializing altq...\n"); qcmd_destroyall(); qcmd_init(); } else return (0); return (1); } static int delete_parser(char *cmdbuf) { char *cp = cmdbuf; char *ifname, class_name[MAX_WORD], filter_name[MAX_WORD]; int ret; if (!get_ifname(&cp, &ifname)) { LOG(LOG_ERR, 0, "missing interface name"); return (0); } if (!next_word(&cp, class_name)) { LOG(LOG_ERR, 0, "missing class name"); return (0); } /* check if filter is specified */ if (next_word(&cp, filter_name)) { ret = qcmd_delete_filter(ifname, class_name, filter_name); if (ret) { LOG(LOG_ERR, 0, "can't delete filter '%s' on interface '%s'", filter_name, ifname); return (0); } return (1); } ret = qcmd_delete_class(ifname, class_name); if (ret) { LOG(LOG_ERR, 0, "can't delete class '%s' on interface '%s'", class_name, ifname); return (0); } return (1); } static int red_parser(char *cmdbuf) { char w[MAX_WORD], *cp = cmdbuf; int th_min, th_max, inv_pmax; if (!next_word(&cp, w)) goto bad; th_min = (int)strtol(w, NULL, 0); if (!next_word(&cp, w)) goto bad; th_max = (int)strtol(w, NULL, 0); if (!next_word(&cp, w)) goto bad; inv_pmax = (int)strtol(w, NULL, 0); if (qop_red_set_defaults(th_min, th_max, inv_pmax) != 0) { LOG(LOG_ERR, 0, "can't set red default parameters"); return (0); } return (1); bad: LOG(LOG_ERR, 0, "bad red parameter"); return (0); } static int rio_parser(char *cmdbuf) { char w[MAX_WORD], *cp = cmdbuf; int i; struct redparams params[RIO_NDROPPREC]; for (i = 0; i < RIO_NDROPPREC; i++) { if (!next_word(&cp, w)) goto bad; params[i].th_min = (int)strtol(w, NULL, 0); if (!next_word(&cp, w)) goto bad; params[i].th_max = (int)strtol(w, NULL, 0); if (!next_word(&cp, w)) goto bad; params[i].inv_pmax = (int)strtol(w, NULL, 0); } if (qop_rio_set_defaults(¶ms[0]) != 0) { LOG(LOG_ERR, 0, "can't set rio default parameters"); return (0); } return (1); bad: LOG(LOG_ERR, 0, "bad rio parameter"); return (0); } static int conditioner_parser(char *cmdbuf) { char cdnr_name[MAX_WORD], *cp = cmdbuf; char *ifname; struct tc_action action[MAX_ACTIONS]; if (!get_ifname(&cp, &ifname)) { LOG(LOG_ERR, 0, "missing interface name"); return (0); } /* get conditioner name */ if (!next_word(&cp, cdnr_name)) { LOG(LOG_ERR, 0, "missing cdnr name"); return (0); } if (tc_action_parser(ifname, &cp, &action[0]) == 0) return (0); if (qcmd_cdnr_add_element(NULL, ifname, cdnr_name, &action[0]) != 0) return (0); return (1); } /* * recursively parse '<'tc_action'>' * note that array "action" grows during recursive parse. */ static int tc_action_parser(char *ifname, char **cpp, struct tc_action *action) { char *cp, *start, *end; char type[MAX_WORD], w[MAX_WORD]; int depth, i; struct tb_profile profile[2]; /* * find a possibly nested pair of '<' and '>', * make them pointed by 'start' and 'end'. */ start = strchr(*cpp, '<'); if (start == NULL) { LOG(LOG_ERR, 0, "conditioner action missing"); return (0); } depth = 1; cp = start + 1; do { end = strpbrk(cp, "<>"); if (end == NULL) { LOG(LOG_ERR, 0, "conditioner action delimiter mismatch"); return (0); } if (*end == '<') depth++; else if (*end == '>') depth--; cp = end + 1; } while (depth > 0); *end = '\0'; *cpp = end + 1; cp = start + 1; if (IsDebug(DEBUG_ALTQ)) { printf("tc_action_parser: [%s]\n", cp); } if (!next_word(&cp, type)) { LOG(LOG_ERR, 0, "missing conditioner action type"); return (0); } /* * action type specific process */ if (EQUAL(type, "conditioner")) { if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing conditioner name"); return (0); } action->tca_code = TCACODE_HANDLE; action->tca_handle = cdnr_name2handle(ifname, w); if (action->tca_handle == CDNR_NULL_HANDLE) { LOG(LOG_ERR, 0, "wrong conditioner name %s", w); return (0); } } else if (EQUAL(type, "pass")) { action->tca_code = TCACODE_PASS; } else if (EQUAL(type, "drop")) { action->tca_code = TCACODE_DROP; } else if (EQUAL(type, "mark")) { if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing dscp"); return (0); } action->tca_code = TCACODE_MARK; action->tca_dscp = (u_int8_t)strtol(w, NULL, 0); } else if (EQUAL(type, "tbmeter")) { if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing tb profile"); return (0); } profile[0].rate = atobps(w); if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing tb profile"); return (0); } profile[0].depth = atobytes(w); if (tc_action_parser(ifname, &cp, &action[1]) == 0) return (0); if (tc_action_parser(ifname, &cp, &action[2]) == 0) return (0); if (qcmd_cdnr_add_tbmeter(action, ifname, NULL, &profile[0], &action[1], &action[2]) != 0) return (0); } else if (EQUAL(type, "trtcm")) { int coloraware = 0; /* default is color-blind */ for (i=0; i<2; i++) { if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing tb profile"); return (0); } profile[i].rate = atobps(w); if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing tb profile"); return (0); } profile[i].depth = atobytes(w); } if (tc_action_parser(ifname, &cp, &action[1]) == 0) return (0); if (tc_action_parser(ifname, &cp, &action[2]) == 0) return (0); if (tc_action_parser(ifname, &cp, &action[3]) == 0) return (0); if (next_word(&cp, w)) { if (EQUAL(w, "coloraware")) coloraware = 1; else if (EQUAL(w, "colorblind")) coloraware = 0; } if (qcmd_cdnr_add_trtcm(action, ifname, NULL, &profile[0], &profile[1], &action[1], &action[2], &action[3], coloraware) != 0) return (0); } else if (EQUAL(type, "tswtcm")) { u_int32_t cmtd_rate, peak_rate, avg_interval; if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing cmtd rate"); return (0); } cmtd_rate = atobps(w); if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing peak rate"); return (0); } peak_rate = atobps(w); if (!next_word(&cp, w)) { LOG(LOG_ERR, 0, "missing avg interval"); return (0); } avg_interval = (u_int32_t)strtoul(w, NULL, 0); if (tc_action_parser(ifname, &cp, &action[1]) == 0) return (0); if (tc_action_parser(ifname, &cp, &action[2]) == 0) return (0); if (tc_action_parser(ifname, &cp, &action[3]) == 0) return (0); if (qcmd_cdnr_add_tswtcm(action, ifname, NULL, cmtd_rate, peak_rate, avg_interval, &action[1], &action[2], &action[3]) != 0) return (0); } else { LOG(LOG_ERR, 0, "unknown action type %s"); return (0); } *end = '>'; /* restore the end delimiter */ return (1); }