/* $NetBSD: qop_cbq.c,v 1.13 2024/12/24 08:35:28 ozaki-r Exp $ */
/* $KAME: qop_cbq.c,v 1.7 2002/05/31 06:03:35 kjc Exp $ */
/*
* Copyright (c) Sun Microsystems, Inc. 1993-1998 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.
*
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the SMCC Technology
* Development Group at Sun Microsystems, Inc.
*
* 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
* SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The software is
* provided "as is" without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this software.
*/
/*
* process options
*/
while (argc > 0) {
if (EQUAL(*argv, "bandwidth")) {
argc--; argv++;
if (argc > 0)
bandwidth = atobps(*argv);
} else if (EQUAL(*argv, "tbrsize")) {
argc--; argv++;
if (argc > 0)
tbrsize = atobytes(*argv);
} else if (EQUAL(*argv, "efficient")) {
is_efficient = 1;
} else if (EQUAL(*argv, "cbq")) {
/* just skip */
} else if (EQUAL(*argv, "cbq-wrr")) {
is_wrr = 1;
} else if (EQUAL(*argv, "cbq-prr")) {
is_wrr = 0;
} else if (EQUAL(*argv, "no-tbr")) {
no_tbr = true;
} else if (EQUAL(*argv, "no-control")) {
no_control = true;
} else {
LOG(LOG_ERR, 0, "Unknown keyword '%s'", *argv);
return (0);
}
argc--; argv++;
}
if (!no_tbr) {
if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
return (0);
}
if (qcmd_cbq_add_if(ifname, bandwidth,
is_wrr, is_efficient, no_control) != 0)
return (0);
return (1);
}
int
cbq_class_parser(const char *ifname, const char *class_name,
const char *parent_name, int argc, char **argv)
{
const char *borrow = NULL;
u_int pri = 1;
u_int pbandwidth = 0;
uint64_t bandwidth = 0;
u_int maxdelay = 0; /* 0 means default */
u_int maxburst = 0; /* 0 means default */
u_int minburst = 0; /* 0 means default */
u_int av_pkt_size = 0; /* 0 means use if mtu as default */
u_int max_pkt_size = 0; /* 0 means use if mtu as default */
int flags = 0;
cbq_tos_t admission_type = CBQ_QOS_NONE;
int error;
if (parent_name == NULL)
flags |= CBQCLF_ROOTCLASS;
while (argc > 0) {
if (EQUAL(*argv, "priority")) {
argc--; argv++;
if (argc > 0)
pri = strtoul(*argv, NULL, 0);
} else if (EQUAL(*argv, "default")) {
flags |= CBQCLF_DEFCLASS;
} else if (EQUAL(*argv, "control")) {
flags |= CBQCLF_CTLCLASS;
} else if (EQUAL(*argv, "admission")) {
argc--; argv++;
if (argc > 0) {
if (EQUAL(*argv, "guaranteed"))
admission_type = CBQ_QOS_GUARANTEED;
else if (EQUAL(*argv, "predictive"))
admission_type = CBQ_QOS_PREDICTIVE;
else if (EQUAL(*argv, "cntlload"))
admission_type = CBQ_QOS_CNTR_LOAD;
else if (EQUAL(*argv, "cntldelay"))
admission_type = CBQ_QOS_CNTR_DELAY;
else if (EQUAL(*argv, "none"))
admission_type = CBQ_QOS_NONE;
else {
LOG(LOG_ERR, 0,
"unknown admission type - %s, line %d",
*argv, line_no);
return (0);
}
}
} else if (EQUAL(*argv, "maxdelay")) {
argc--; argv++;
if (argc > 0)
maxdelay = strtoul(*argv, NULL, 0);
} else if (EQUAL(*argv, "borrow")) {
borrow = parent_name;
#if 1
/* support old style "borrow [parent]" */
if (argc > 1 &&
EQUAL(*(argv + 1), parent_name)) {
/* old style, skip borrow_name */
argc--; argv++;
}
#endif
} else if (EQUAL(*argv, "pbandwidth")) {
argc--; argv++;
if (argc > 0)
pbandwidth = strtoul(*argv, NULL, 0);
if (pbandwidth > 100) {
LOG(LOG_ERR, 0,
"bad pbandwidth %d for %s!",
pbandwidth, class_name);
return (0);
}
} else if (EQUAL(*argv, "exactbandwidth")) {
argc--; argv++;
if (argc > 0)
bandwidth = atobps(*argv);
} else if (EQUAL(*argv, "maxburst")) {
argc--; argv++;
if (argc > 0)
maxburst = strtoul(*argv, NULL, 0);
} else if (EQUAL(*argv, "minburst")) {
argc--; argv++;
if (argc > 0)
minburst = strtoul(*argv, NULL, 0);
} else if (EQUAL(*argv, "packetsize")) {
argc--; argv++;
if (argc > 0)
av_pkt_size = atobytes(*argv);
} else if (EQUAL(*argv, "maxpacketsize")) {
argc--; argv++;
if (argc > 0)
max_pkt_size = atobytes(*argv);
} else if (EQUAL(*argv, "red")) {
flags |= CBQCLF_RED;
} else if (EQUAL(*argv, "ecn")) {
flags |= CBQCLF_ECN;
} else if (EQUAL(*argv, "flowvalve")) {
flags |= CBQCLF_FLOWVALVE;
} else if (EQUAL(*argv, "rio")) {
flags |= CBQCLF_RIO;
} else if (EQUAL(*argv, "cleardscp")) {
flags |= CBQCLF_CLEARDSCP;
} else {
LOG(LOG_ERR, 0,
"Unknown keyword '%s' in %s, line %d",
*argv, altqconfigfile, line_no);
return (0);
}
argc--; argv++;
}
if ((flags & (CBQCLF_RED|CBQCLF_RIO)) == (CBQCLF_RED|CBQCLF_RIO)) {
LOG(LOG_ERR, 0,
"both red and rio defined on interface '%s'",
ifname);
return (0);
}
if ((flags & (CBQCLF_ECN|CBQCLF_FLOWVALVE))
&& (flags & (CBQCLF_RED|CBQCLF_RIO)) == 0)
flags |= CBQCLF_RED;
if (strcmp("ctl_class", class_name) == 0)
flags |= CBQCLF_CTLCLASS;
if (flags & CBQCLF_DEFCLASS && !cbq_ifinfo->no_control) {
/*
* if this is a default class and no ctl_class is defined,
* we will create a ctl_class.
*/
if (cbq_ifinfo->ctl_class == NULL) {
/* reserve bandwidth for ctl_class */
ctl_bandwidth =
ifinfo->bandwidth / 100 * CTL_PBANDWIDTH;
if (bandwidth <= ctl_bandwidth)
LOG(LOG_ERR, 0,
"bandwidth for default class too small!");
bandwidth -= ctl_bandwidth;
}
}
if (error == 0)
error = qop_cbq_add_class(NULL, class_name, ifinfo, parent,
borrow, pri, bandwidth,
maxdelay, maxburst, minburst,
av_pkt_size, max_pkt_size,
admission_type, flags);
if (error != 0)
LOG(LOG_ERR, errno,
"cbq: %s: can't add class '%s' on interface '%s'",
qoperror(error), class_name, ifname);
if (ctl_bandwidth != 0) {
/*
* If were adding the default traffic class and
* no ctl_class is defined, also add the ctl traffic class.
* This is for RSVP and IGMP packets.
*/
if (qcmd_cbq_add_class(ifname, "ctl_class", parent_name,
borrow_name, 6, ctl_bandwidth,
maxdelay, maxburst, minburst, av_pkt_size,
max_pkt_size, admission_type, CBQCLF_CTLCLASS) != 0) {
LOG(LOG_ERR, errno, "can't create ctl_class!");
return (QOPERR_CLASS);
}
}
/*
* if this is a ctl class, add the default filters for backward
* compatibility
*/
if (flags & CBQCLF_CTLCLASS)
qcmd_cbq_add_ctl_filters(ifname, class_name);
/* if average packet size isn't specified, set if mtu. */
if (av_pkt_size == 0) { /* use default */
av_pkt_size = ifinfo->ifmtu;
if (av_pkt_size > MCLBYTES) /* do what TCP does */
av_pkt_size &= ~MCLBYTES;
} else if (av_pkt_size > ifinfo->ifmtu)
av_pkt_size = ifinfo->ifmtu;
if (max_pkt_size == 0) /* use default */
max_pkt_size = ifinfo->ifmtu;
else if (max_pkt_size > ifinfo->ifmtu)
max_pkt_size = ifinfo->ifmtu;
/* set delete hook */
clinfo->delete_hook = qop_cbq_delete_class_hook;
if (parent == NULL)
cbq_ifinfo->root_class = clinfo;
else {
parent_clinfo = parent->private;
parent_clinfo->allocated += bandwidth;
}
if (flags & CBQCLF_DEFCLASS)
cbq_ifinfo->default_class = clinfo;
if (flags & CBQCLF_CTLCLASS)
cbq_ifinfo->ctl_class = clinfo;
switch (admission_type) {
case CBQ_QOS_CNTR_LOAD:
case CBQ_QOS_GUARANTEED:
case CBQ_QOS_PREDICTIVE:
case CBQ_QOS_CNTR_DELAY:
if (ifinfo->resv_class != NULL) {
LOG(LOG_ERR, 0,
"%s: duplicate resv meta class", class_name);
return (QOPERR_CLASS);
}
ifinfo->resv_class = clinfo;
}
if (rp != NULL)
*rp = clinfo;
return (0);
err_ret:
if (cbq_clinfo != NULL) {
free(cbq_clinfo);
if (clinfo != NULL)
clinfo->private = NULL;
}
return (error);
}
/*
* this is called from qop_delete_class() before a class is destroyed
* for discipline specific cleanup.
*/
static int
qop_cbq_delete_class_hook(struct classinfo *clinfo)
{
struct cbq_classinfo *cbq_clinfo, *parent_clinfo;
/* cancel admission control */
if (clinfo->parent != NULL) {
cbq_clinfo = clinfo->private;
parent_clinfo = clinfo->parent->private;
/* admission control */
old_bandwidth = cbq_clinfo->bandwidth;
if (clinfo->parent != NULL) {
parent_clinfo = clinfo->parent->private;
if (bandwidth > old_bandwidth) {
/* increase bandwidth */
if (bandwidth - old_bandwidth >
parent_clinfo->bandwidth
- parent_clinfo->allocated)
return (QOPERR_ADMISSION_NOBW);
} else if (bandwidth < old_bandwidth) {
/* decrease bandwidth */
if (bandwidth < cbq_clinfo->allocated)
return (QOPERR_ADMISSION);
}
}
/* if average packet size isn't specified, set if mtu. */
if (av_pkt_size == 0) { /* use default */
av_pkt_size = ifinfo->ifmtu;
if (av_pkt_size > MCLBYTES) /* do what TCP does */
av_pkt_size &= ~MCLBYTES;
} else if (av_pkt_size > ifinfo->ifmtu)
av_pkt_size = ifinfo->ifmtu;
if (max_pkt_size == 0) /* use default */
max_pkt_size = ifinfo->ifmtu;
else if (max_pkt_size > ifinfo->ifmtu)
max_pkt_size = ifinfo->ifmtu;
/*
* sanity check at enabling cbq:
* there must one root class and one default class for an interface
*/
static int
qop_cbq_enable_hook(struct ifinfo *ifinfo)
{
struct cbq_ifinfo *cbq_ifinfo;
cbq_ifinfo = ifinfo->private;
if (cbq_ifinfo->root_class == NULL) {
LOG(LOG_ERR, 0, "cbq: no root class on interface %s!",
ifinfo->ifname);
return (QOPERR_CLASS);
}
if (cbq_ifinfo->default_class == NULL) {
LOG(LOG_ERR, 0, "cbq: no default class on interface %s!",
ifinfo->ifname);
return (QOPERR_CLASS);
}
return (0);
}