NetBSD/usr.sbin/altq/libaltq/parser.c

1196 lines
27 KiB
C

/* $NetBSD: parser.c,v 1.9 2003/02/01 17:13:14 wiz 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 <sys/param.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <syslog.h>
#include <netdb.h>
#include <err.h>
#include <altq/altq.h>
#include <altq/altq_cdnr.h>
#include <altq/altq_red.h>
#include <altq/altq_rio.h>
#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 <tc_action>"},
{"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(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(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(addr, 0) &= IN6ADDR32(mask, 0);
IN6ADDR32(addr, 1) &= IN6ADDR32(mask, 1);
IN6ADDR32(addr, 2) &= IN6ADDR32(mask, 2);
IN6ADDR32(addr, 3) &= IN6ADDR32(mask, 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(&params[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);
}