d5e1f166e0
and such.
1350 lines
31 KiB
C
1350 lines
31 KiB
C
/* $NetBSD: parser.c,v 1.6 2001/08/22 08:52:36 itojun Exp $ */
|
|
/* $KAME: parser.c,v 1.12 2001/08/16 10:39:13 kjc Exp $ */
|
|
/*******************************************************************
|
|
|
|
Copyright (c) 1996 by the University of Southern California
|
|
All rights reserved.
|
|
|
|
Permission to use, copy, modify, and distribute this software and its
|
|
documentation in source and binary forms for any purpose and without
|
|
fee is hereby granted, provided that both the above copyright notice
|
|
and this permission notice appear in all copies. and that any
|
|
documentation, advertising materials, and other materials related to
|
|
such distribution and use acknowledge that the software was developed
|
|
in part by the University of Southern California, Information
|
|
Sciences Institute. The name of the University may not be used to
|
|
endorse or promote products derived from this software without
|
|
specific prior written permission.
|
|
|
|
THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
|
|
the suitability of this software for any purpose. THIS SOFTWARE IS
|
|
PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
Other copyrights might apply to parts of this software and are so
|
|
noted when applicable.
|
|
|
|
********************************************************************/
|
|
|
|
|
|
#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 <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.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"
|
|
|
|
#define show_help(op) printf(cmd_tab[op].cmd_help)
|
|
|
|
/*
|
|
* Forward & External Declarations
|
|
*/
|
|
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 pfxcmp(const char *, const char *);
|
|
static int next_word(char **, char *);
|
|
|
|
static int do_cmd(int, 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 *);
|
|
|
|
/*
|
|
* Globals
|
|
*/
|
|
#define MAX_NFLWDS 64
|
|
#define MAX_T 64
|
|
|
|
int TNO = 1; /* Current Thread number */
|
|
int line_no = 0;
|
|
int filter_dontwarn;
|
|
|
|
static char if_names[MAX_T][IFNAMSIZ];
|
|
static struct if_nameindex *if_namelist = NULL;
|
|
|
|
#ifndef MAX
|
|
#define MAX(a,b) (((a)>(b))?(a):(b))
|
|
#endif
|
|
#ifndef MIN
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
#endif
|
|
|
|
enum op_codes {
|
|
/* order must be same as entries cmd_tab[].cmd_op below!! */
|
|
OP_HELP = 1, OP_QUIT,
|
|
OP_IFACE, OP_CLASS, OP_FILTER,
|
|
OP_ALTQ, OP_DEL,
|
|
#ifdef INET6
|
|
OP_FILTER6,
|
|
#endif
|
|
OP_RED, OP_RIO,
|
|
OP_CDNR,
|
|
OP_NULL, OP_BUG
|
|
};
|
|
|
|
/* Following table MUST match enum order of op_codes !
|
|
*/
|
|
struct cmds {
|
|
char *cmd_verb;
|
|
int cmd_op;
|
|
char *cmd_help;
|
|
} cmd_tab[] = {
|
|
|
|
{ "?", OP_HELP, "Commands are:\n" },
|
|
{ "help", OP_HELP, " help | ?\n" },
|
|
{ "quit", OP_QUIT, " quit\n" },
|
|
{ "interface", OP_IFACE, " interface if_name [bandwidth bps] [cbq|hfsc]\n" },
|
|
{ "class", OP_CLASS, " class discipline if_name class_name [parent]\n" },
|
|
{ "filter", OP_FILTER, " filter if_name class_name [name filt_name] dst [netmask #] dport src [netmask #] sport proto [tos # [tosmask #] [gpi #] [dontwarn]\n" },
|
|
{ "altq", OP_ALTQ, " disc if_name {enable|disable}\n" },
|
|
{ "delete", OP_DEL, " delete if_name class_name\n" },
|
|
#ifdef INET6
|
|
{ "filter6", OP_FILTER6, " filter6 if_name class_name [name filt_name] dst[/prefix] dport src[/prefix] sport proto [flowlabel #][tclass # [tclassmask #]][gpi #] [dontwarn]\n" },
|
|
#endif
|
|
{ "red", OP_RED, " red th_min th_max inv_pmax\n" },
|
|
{ "rio", OP_RIO, " rio low_th_min low_th_max low_inv_pmax med_th_min med_th_max med_inv_pmax high_th_min high_th_max high_inv_pmax\n" },
|
|
{ "conditioner", OP_CDNR, " conditioner if_name cdnr_name <tc_action>\n" },
|
|
{ "bug", OP_BUG, " bug (On/Off)\n" },
|
|
{ "", OP_NULL, "" } /* MUST BE LAST IN CMD TABLE */
|
|
};
|
|
|
|
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, line %d", line_no);
|
|
return (0);
|
|
}
|
|
if (strncmp(ifinfo->qdisc->qname, qname,
|
|
strlen(ifinfo->qdisc->qname)) != 0) {
|
|
LOG(LOG_ERR, 0,
|
|
"qname doesn't match the interface, line %d",
|
|
line_no);
|
|
return (0);
|
|
}
|
|
return (*qp->class_parser)(ifname, class_name,
|
|
parent_name, argc, argv);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Read the config file to learn about tunnel vifs and non-default phyint
|
|
* parameters.
|
|
*/
|
|
int
|
|
qcmd_config(void)
|
|
{
|
|
FILE *f;
|
|
int i, rc = 1;
|
|
|
|
if (if_namelist != NULL)
|
|
if_freenameindex(if_namelist);
|
|
if_namelist = if_nameindex();
|
|
|
|
for (i = 0; i < MAX_T; i++)
|
|
if_names[i][0] = '\0';
|
|
|
|
LOG(LOG_INFO, 0, "ALTQ config file is %s", altqconfigfile);
|
|
|
|
f = fopen(altqconfigfile, "r");
|
|
if (f == NULL) {
|
|
LOG(LOG_ERR, errno, "can't open %s", altqconfigfile, 0);
|
|
return (QOPERR_INVAL);
|
|
}
|
|
line_no = 0;
|
|
while (rc)
|
|
rc = DoCommand(altqconfigfile, f);
|
|
|
|
(void) fclose(f);
|
|
line_no = 0;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Do_Command(): Top-level routine to read the next line from a given
|
|
* file and execute the command it contains.
|
|
* returns 1 if OK, 0 if EOF.
|
|
*/
|
|
int
|
|
DoCommand(char *infile, FILE *infp)
|
|
{
|
|
char cmd_line[256], cmd_op[80];
|
|
struct cmds *cmdp;
|
|
char *cp;
|
|
int rc;
|
|
|
|
if (fgets(cmd_line, sizeof(cmd_line), infp) == NULL)
|
|
/* EOF */
|
|
return(0);
|
|
line_no++;
|
|
|
|
/* check escaped newline */
|
|
while ((cp = strrchr(cmd_line, '\\')) != NULL && cp[1] == '\n') {
|
|
if (fgets(cp, &cmd_line[256] - cp, infp) != NULL)
|
|
line_no++;
|
|
}
|
|
|
|
/* remove trailing NL */
|
|
cp = cmd_line + strlen(cmd_line) - 1;
|
|
if (*cp == '\n')
|
|
*cp = '\0';
|
|
else if (!feof(infp)) {
|
|
printf("LINE %d > 255 CHARS: %s.\n", line_no, cmd_line);
|
|
exit(1);
|
|
}
|
|
/*** printf("DoCommand: %s\n", cmd_line); ***/
|
|
|
|
if (cmd_line[0] == '#') { /* Comment, skip this line */
|
|
return(1);
|
|
}
|
|
cp = cmd_line;
|
|
if (!next_word(&cp, cmd_op))
|
|
return(1);
|
|
if (cmd_op[0] == 'T') {
|
|
TNO = atoi(&cmd_op[1]);
|
|
if (!next_word(&cp, cmd_op))
|
|
return(1);
|
|
}
|
|
cmdp = cmd_tab;
|
|
while ((cmdp->cmd_op != OP_NULL) && pfxcmp(cmd_op, cmdp->cmd_verb))
|
|
cmdp++;
|
|
|
|
if (cmdp->cmd_op == OP_NULL) {
|
|
if (cmd_op[0])
|
|
printf(" ?? %s\n", cmd_op);
|
|
return(1);
|
|
}
|
|
rc = do_cmd(cmdp->cmd_op, cp);
|
|
if (rc == 0) {
|
|
if (infile) {
|
|
/* error in the config file. cleanup and exit. */
|
|
LOG(LOG_ERR, 0, "config failed. exiting...");
|
|
(void) qcmd_destroyall();
|
|
(void) fclose(infp);
|
|
exit(1);
|
|
} else {
|
|
/* interactive mode */
|
|
printf("error: usage :");
|
|
show_help(cmdp->cmd_op);
|
|
}
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Prefix string comparison: Return 0 if s1 string is prefix of s2 string, 1
|
|
* otherwise.
|
|
*/
|
|
static int
|
|
pfxcmp(const char *s1, const char *s2)
|
|
{
|
|
while (*s1)
|
|
if (*s1++ != *s2++)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Skip leading blanks, then copy next word (delimited by blank or zero, but
|
|
* no longer than 63 bytes) into buffer b, set scan pointer to following
|
|
* non-blank (or end of string), and return 1. If there is no non-blank text,
|
|
* set scan ptr to point to 0 byte and return 0.
|
|
*/
|
|
static int
|
|
next_word(char **cpp, char *b)
|
|
{
|
|
char *tp;
|
|
size_t L;
|
|
|
|
*cpp += strspn(*cpp, " \t");
|
|
if (**cpp == '\0' || **cpp == '\n' || **cpp == '#')
|
|
return(0);
|
|
|
|
tp = strpbrk(*cpp, " \t\n#");
|
|
L = MIN((tp)?(tp-*cpp):strlen(*cpp), 63);
|
|
strncpy(b, *cpp, L);
|
|
*(b + L) = '\0';
|
|
*cpp += L;
|
|
*cpp += strspn(*cpp, " \t");
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* do_cmd executes a command input.
|
|
* returns 1 if OK, 0 if an error occurs.
|
|
*/
|
|
static int
|
|
do_cmd(int op, char *cmdbuf)
|
|
{
|
|
int i, rval = 0;
|
|
|
|
switch (op) {
|
|
case OP_HELP:
|
|
for (i = 0; i < OP_NULL; i++)
|
|
show_help(i);
|
|
rval = 1;
|
|
break;
|
|
case OP_QUIT:
|
|
qcmd_destroyall();
|
|
exit(0);
|
|
break;
|
|
case OP_IFACE:
|
|
rval = interface_parser(cmdbuf);
|
|
break;
|
|
case OP_CLASS:
|
|
rval = class_parser(cmdbuf);
|
|
break;
|
|
case OP_FILTER:
|
|
rval = filter_parser(cmdbuf);
|
|
break;
|
|
case OP_ALTQ:
|
|
rval = ctl_parser(cmdbuf);
|
|
break;
|
|
case OP_DEL:
|
|
rval = delete_parser(cmdbuf);
|
|
break;
|
|
#ifdef INET6
|
|
case OP_FILTER6:
|
|
rval = filter6_parser(cmdbuf);
|
|
break;
|
|
#endif
|
|
case OP_RED:
|
|
rval = red_parser(cmdbuf);
|
|
break;
|
|
case OP_RIO:
|
|
rval = rio_parser(cmdbuf);
|
|
break;
|
|
case OP_CDNR:
|
|
rval = conditioner_parser(cmdbuf);
|
|
break;
|
|
case OP_BUG:
|
|
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;
|
|
}
|
|
break;
|
|
default:
|
|
printf("command %d not supported\n", op);
|
|
rval = 0;
|
|
break;
|
|
}
|
|
return(rval);
|
|
}
|
|
|
|
#define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
|
|
|
|
char *cur_ifname(void)
|
|
{
|
|
return (if_names[TNO]);
|
|
}
|
|
|
|
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[128], *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(if_names[TNO], w, sizeof(if_names[TNO]));
|
|
*ifnamep = if_names[TNO];
|
|
return (1);
|
|
}
|
|
|
|
/* this is not interface name. use one in the context. */
|
|
if (if_names[TNO][0] == 0)
|
|
return (0);
|
|
*ifnamep = if_names[TNO];
|
|
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[128], *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[128], *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[256], *ap, *cp = cmdbuf;
|
|
char *ifname, *argv[64], qdisc_name[64];
|
|
int argc, rval;
|
|
|
|
if (!get_ifname(&cp, &ifname)) {
|
|
LOG(LOG_ERR, 0, "missing interface name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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++;
|
|
}
|
|
|
|
rval = qdisc_interface_parser(qdisc_name, ifname, argc, argv);
|
|
if (rval == 0) {
|
|
LOG(LOG_ERR, 0, "Error in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
class_parser(char *cmdbuf)
|
|
{
|
|
char w[256], *cp = cmdbuf;
|
|
char *ifname, qdisc_name[128], class_name[128], parent_name[128];
|
|
char *clname = class_name;
|
|
char *parent = NULL;
|
|
char *argv[64], *ap;
|
|
int argc, rval;
|
|
|
|
/* get scheduling class */
|
|
if (!next_word(&cp, qdisc_name)) {
|
|
LOG(LOG_ERR, 0, "missing scheduling discipline in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
if (!is_qdisc_name(qdisc_name)) {
|
|
LOG(LOG_ERR, 0,
|
|
"unknown scheduling discipline '%s' in %s, line %d",
|
|
qdisc_name, altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get interface name */
|
|
if (!get_ifname(&cp, &ifname)) {
|
|
LOG(LOG_ERR, 0, "missing interface name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get class name */
|
|
if (!next_word(&cp, class_name)) {
|
|
LOG(LOG_ERR, 0, "missing class name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get parent name */
|
|
if (!next_word(&cp, parent_name)) {
|
|
LOG(LOG_ERR, 0, "missing parent class in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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++;
|
|
}
|
|
|
|
rval = qdisc_class_parser(qdisc_name, ifname, clname, parent,
|
|
argc, argv);
|
|
if (rval == 0) {
|
|
LOG(LOG_ERR, 0, "can't add class '%s' on interface '%s'",
|
|
clname, ifname);
|
|
return (0);
|
|
}
|
|
|
|
return (1);
|
|
}
|
|
|
|
static int
|
|
filter_parser(char *cmdbuf)
|
|
{
|
|
char w[128], *cp = cmdbuf;
|
|
char *ifname, class_name[64], fltr_name[64], *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 %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
if (!next_word(&cp, class_name)) {
|
|
LOG(LOG_ERR, 0,
|
|
"missing class name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get filter destination port */
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0,
|
|
"missing filter destination port in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
if (!get_port(w, &sfilt.ff_flow.fi_dport)) {
|
|
LOG(LOG_ERR, 0, "bad filter destination port in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get filter source port */
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0, "missing filter source port in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
if (!get_port(w, &sfilt.ff_flow.fi_sport)) {
|
|
LOG(LOG_ERR, 0, "bad filter source port in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get filter protocol id */
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0, "missing filter protocol id in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
if (!get_proto(w, &protocol)) {
|
|
LOG(LOG_ERR, 0, "bad protocol in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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[128], *cp = cmdbuf;
|
|
char *ifname, class_name[128], fltr_name[64], *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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
if (!next_word(&cp, class_name)) {
|
|
LOG(LOG_ERR, 0, "missing class name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get filter destination port */
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0,
|
|
"missing filter destination port in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
if (!get_port(w, &sfilt.ff_flow6.fi6_dport)) {
|
|
LOG(LOG_ERR, 0, "bad filter destination port in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get filter source port */
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0, "missing filter source port in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
if (!get_port(w, &sfilt.ff_flow6.fi6_sport)) {
|
|
LOG(LOG_ERR, 0, "bad filter source port in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get filter protocol id */
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0, "missing filter protocol id in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
if (!get_proto(w, &protocol)) {
|
|
LOG(LOG_ERR, 0, "bad protocol in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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[128], *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[128], *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[128];
|
|
int ret;
|
|
|
|
if (!get_ifname(&cp, &ifname)) {
|
|
printf("missing interface name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
if (!next_word(&cp, class_name)) {
|
|
LOG(LOG_ERR, 0,
|
|
"missing class name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
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[128], *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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
rio_parser(char *cmdbuf)
|
|
{
|
|
char w[128], *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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
conditioner_parser(char *cmdbuf)
|
|
{
|
|
char cdnr_name[128], *cp = cmdbuf;
|
|
char *ifname;
|
|
struct tc_action action[64];
|
|
|
|
if (!get_ifname(&cp, &ifname)) {
|
|
LOG(LOG_ERR, 0, "missing interface name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/* get conditioner name */
|
|
if (!next_word(&cp, cdnr_name)) {
|
|
LOG(LOG_ERR, 0, "missing cdnr name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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[128], w[128];
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
depth = 1;
|
|
cp = start + 1;
|
|
do {
|
|
end = strpbrk(cp, "<>");
|
|
if (end == NULL) {
|
|
LOG(LOG_ERR, 0,
|
|
"conditioner action delimiter mismatch in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* action type specific process
|
|
*/
|
|
if (EQUAL(type, "conditioner")) {
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0,
|
|
"missing conditioner name in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
w, altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
profile[0].rate = atobps(w);
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0, "missing tb profile in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
profile[i].rate = atobps(w);
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0,
|
|
"missing tb profile in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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 in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
cmtd_rate = atobps(w);
|
|
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0, "missing peak rate in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
peak_rate = atobps(w);
|
|
|
|
if (!next_word(&cp, w)) {
|
|
LOG(LOG_ERR, 0, "missing avg interval in %s, line %d",
|
|
altqconfigfile, line_no);
|
|
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,
|
|
"Unkown action type %s in %s, line %d",
|
|
type, altqconfigfile, line_no);
|
|
return (0);
|
|
}
|
|
|
|
*end = '>'; /* restore the end delimiter */
|
|
|
|
return (1);
|
|
}
|
|
|