/* $NetBSD: qop_cdnr.c,v 1.4 2001/08/22 08:52:37 itojun Exp $ */
/* $KAME: qop_cdnr.c,v 1.9 2001/08/16 10:39:14 kjc Exp $ */
/*
* Copyright (C) 1999-2000
* 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 <sys/sockio.h>
#include <sys/ioctl.h>
#include <sys/fcntl.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 <altq/altq.h>
#include <altq/altq_cdnr.h>
#include "altq_qop.h"
#include "qop_cdnr.h"
/*
* diffserve traffic conditioner support
*
* we use the existing qop interface to support conditioner.
*/
static struct ifinfo *cdnr_ifname2ifinfo(const char *);
static int cdnr_attach(struct ifinfo *);
static int cdnr_detach(struct ifinfo *);
static int cdnr_enable(struct ifinfo *);
static int cdnr_disable(struct ifinfo *);
static int cdnr_add_class(struct classinfo *);
static int cdnr_modify_class(struct classinfo *, void *);
static int cdnr_delete_class(struct classinfo *);
static int cdnr_add_filter(struct fltrinfo *);
static int cdnr_delete_filter(struct fltrinfo *);
static int verify_tbprofile(struct tb_profile *, const char *);
#define CDNR_DEVICE "/dev/altq/cdnr"
static int cdnr_fd = -1;
static int cdnr_refcount = 0;
static struct qdisc_ops cdnr_qdisc = {
ALTQT_CDNR,
"cdnr",
cdnr_attach,
cdnr_detach,
NULL, /* clear */
cdnr_enable,
cdnr_disable,
cdnr_add_class,
cdnr_modify_class,
cdnr_delete_class,
cdnr_add_filter,
cdnr_delete_filter,
};
u_long
cdnr_name2handle(const char *ifname, const char *cdnr_name)
{
struct ifinfo *ifinfo;
struct classinfo *clinfo;
if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
return (CDNR_NULL_HANDLE);
if ((clinfo = clname2clinfo(ifinfo, cdnr_name)) == NULL)
return (CDNR_NULL_HANDLE);
return (clinfo->handle);
}
static struct ifinfo *
cdnr_ifname2ifinfo(const char *ifname)
{
struct ifinfo *ifinfo;
char input_ifname[64];
/*
* search for an existing input interface
*/
if ((ifinfo = input_ifname2ifinfo(ifname)) != NULL)
return (ifinfo);
/*
* if there is a corresponding output interface,
* create an input interface by prepending "_" to
* its name.
*/
if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
return (NULL);
input_ifname[0] = '_';
strlcpy(input_ifname+1, ifname, sizeof(input_ifname)-1);
if (qop_add_if(&ifinfo, input_ifname, 0, &cdnr_qdisc, NULL) != 0) {
LOG(LOG_ERR, errno,
"cdnr_ifname2ifinfo: can't add a input interface %s",
ifname);
return (NULL);
}
return (ifinfo);
}
int
qcmd_cdnr_add_element(struct tc_action *rp, const char *ifname,
const char *cdnr_name, struct tc_action *action)
{
struct ifinfo *ifinfo;
struct classinfo *clinfo;
int error;
if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
return (QOPERR_BADIF);
if ((error = qop_cdnr_add_element(&clinfo, cdnr_name, ifinfo,
action)) != 0) {
LOG(LOG_ERR, errno, "%s: add element failed!",
qoperror(error));
return (error);
}
if (rp != NULL) {
rp->tca_code = TCACODE_HANDLE;
rp->tca_handle = clinfo->handle;
}
return (0);
}
int
qcmd_cdnr_add_tbmeter(struct tc_action *rp, const char *ifname,
const char *cdnr_name,
struct tb_profile *profile,
struct tc_action *in_action,
struct tc_action *out_action)
{
struct ifinfo *ifinfo;
struct classinfo *clinfo;
int error;
if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
return (QOPERR_BADIF);
verify_tbprofile(profile, cdnr_name);
if ((error = qop_cdnr_add_tbmeter(&clinfo, cdnr_name, ifinfo,
profile, in_action, out_action)) != 0) {
LOG(LOG_ERR, errno, "%s: add tbmeter failed!",
qoperror(error));
return (error);
}
if (rp != NULL) {
rp->tca_code = TCACODE_HANDLE;
rp->tca_handle = clinfo->handle;
}
return (0);
}
int
qcmd_cdnr_add_trtcm(struct tc_action *rp, const char *ifname,
const char *cdnr_name,
struct tb_profile *cmtd_profile,
struct tb_profile *peak_profile,
struct tc_action *green_action,
struct tc_action *yellow_action,
struct tc_action *red_action, int coloraware)
{
struct ifinfo *ifinfo;
struct classinfo *clinfo;
int error;
if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
return (QOPERR_BADIF);
verify_tbprofile(cmtd_profile, cdnr_name);
verify_tbprofile(peak_profile, cdnr_name);
if ((error = qop_cdnr_add_trtcm(&clinfo, cdnr_name, ifinfo,
cmtd_profile, peak_profile,
green_action, yellow_action, red_action,
coloraware)) != 0) {
LOG(LOG_ERR, errno, "%s: add trtcm failed!",
qoperror(error));
return (error);
}
if (rp != NULL) {
rp->tca_code = TCACODE_HANDLE;
rp->tca_handle = clinfo->handle;
}
return (0);
}
int
qcmd_cdnr_add_tswtcm(struct tc_action *rp, const char *ifname,
const char *cdnr_name, const u_int32_t cmtd_rate,
const u_int32_t peak_rate, const u_int32_t avg_interval,
struct tc_action *green_action,
struct tc_action *yellow_action,
struct tc_action *red_action)
{
struct ifinfo *ifinfo;
struct classinfo *clinfo;
int error;
if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
return (QOPERR_BADIF);
if (cmtd_rate > peak_rate) {
LOG(LOG_ERR, 0,
"add tswtcm: cmtd_rate larger than peak_rate!");
return (QOPERR_INVAL);
}
if ((error = qop_cdnr_add_tswtcm(&clinfo, cdnr_name, ifinfo,
cmtd_rate, peak_rate, avg_interval,
green_action, yellow_action,
red_action)) != 0) {
LOG(LOG_ERR, errno, "%s: add tswtcm failed!",
qoperror(error));
return (error);
}
if (rp != NULL) {
rp->tca_code = TCACODE_HANDLE;
rp->tca_handle = clinfo->handle;
}
return (0);
}
int
qcmd_cdnr_delete(const char *ifname, const char *cdnr_name)
{
struct ifinfo *ifinfo;
struct classinfo *clinfo;
if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
return (QOPERR_BADIF);
if ((clinfo = clname2clinfo(ifinfo, cdnr_name)) == NULL)
return (QOPERR_BADCLASS);
return qop_delete_cdnr(clinfo);
}
/*
* class operations:
* class structure is used to hold conditioners.
* XXX
* conditioners has dependencies in the reverse order; parent nodes
* refere to child nodes, and thus, a child is created first and
* parents should be removed first.
* qop_add_cdnr() and qop_delete_cdnr() are wrapper functions
* of qop_add_class() and qop_delete_class(), and takes care
* of dependencies.
* 1. when adding a conditioner, it is created as a child of a
* dummy root class. then, the child conditioners are made
* as its children.
* 2. when deleting a conditioner, its child conditioners are made
* as children of the dummy root class. then, the conditioner
* is deleted.
*/
int
qop_add_cdnr(struct classinfo **rp, const char *cdnr_name,
struct ifinfo *ifinfo, struct classinfo **childlist,
void *cdnr_private)
{
struct classinfo *clinfo, *root, *cl, *prev;
int error;
/*
* if there is no root cdnr, create one.
*/
if ((root = get_rootclass(ifinfo)) == NULL) {
if ((error = qop_add_class(&root, "cdnr_root",
ifinfo, NULL, NULL)) != 0) {
LOG(LOG_ERR, errno,
"cdnr: %s: can't create dummy root cdnr on %s!",
qoperror(error), ifinfo->ifname);
return (QOPERR_CLASS);
}
}
/*
* create a class as a child of a root class.
*/
if ((error = qop_add_class(&clinfo, cdnr_name,
ifinfo, root, cdnr_private)) != 0)
return (error);
/*
* move child nodes
*/
for (cl = *childlist; cl != NULL; cl = *++childlist) {
if (cl->parent != root) {
/*
* this conditioner already has a non-root parent.
* we can't track down a multi-parent node by a
* tree structure; leave it as it is.
* (we need a mechanism similar to a symbolic link
* in a file system)
*/
continue;
}
/* remove this child from the root */
if (root->child == cl)
root->child = cl->sibling;
else for (prev = root->child;
prev->sibling != NULL; prev = prev->sibling)
if (prev->sibling == cl) {
prev->sibling = cl->sibling;
break;
}
/* add as a child */
cl->sibling = clinfo->child;
clinfo->child = cl;
cl->parent = clinfo;
}
if (rp != NULL)
*rp = clinfo;
return (0);
}
int
qop_delete_cdnr(struct classinfo *clinfo)
{
struct classinfo *cl, *root;
int error;
if ((root = get_rootclass(clinfo->ifinfo)) == NULL) {
LOG(LOG_ERR, 0, "qop_delete_cdnr: no root cdnr!");
return (QOPERR_CLASS);
}
if (clinfo->parent != root)
return (QOPERR_CLASS_PERM);
if ((cl = clinfo->child) != NULL) {
/* change child's parent to root, find the last child */
while (cl->sibling != NULL) {
cl->parent = root;
cl = cl->sibling;
}
cl->parent = root;
/* move children to siblings */
cl->sibling = clinfo->sibling;
clinfo->sibling = cl;
clinfo->child = NULL;
}
error = qop_delete_class(clinfo);
if (error) {
/* ick! restore the class tree */
if (cl != NULL) {
clinfo->child = clinfo->sibling;
clinfo->sibling = cl->sibling;
cl->sibling = NULL;
/* restore parent field */
for (cl = clinfo->child; cl != NULL; cl = cl->sibling)
cl->parent = clinfo;
}
}
return (error);
}
int
qop_cdnr_add_element(struct classinfo **rp, const char *cdnr_name,
struct ifinfo *ifinfo, struct tc_action *action)
{
struct classinfo *clinfo, *clist[2];
struct cdnrinfo *cdnrinfo = NULL;
int error;
if (action->tca_code == TCACODE_HANDLE) {
clinfo = clhandle2clinfo(ifinfo, action->tca_handle);
if (clinfo == NULL)
return (QOPERR_BADCLASS);
clist[0] = clinfo;
clist[1] = NULL;
#if 1
/*
* if the conditioner referred to doesn't have a name,
* this is called just to add a name to it.
* we can simply add the name to the existing conditioner
* and return it.
*/
if (cdnr_name != NULL &&
strcmp(clinfo->clname, "(null)") == 0) {
free(clinfo->clname);
clinfo->clname = strdup(cdnr_name);
if (rp != NULL)
*rp = clinfo;
return (0);
}
#endif
} else
clist[0] = NULL;
if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
return (QOPERR_NOMEM);
cdnrinfo->tce_type = TCETYPE_ELEMENT;
cdnrinfo->tce_un.element.action = *action;
if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
cdnrinfo)) != 0)
goto err_ret;
if (rp != NULL)
*rp = clinfo;
return (0);
err_ret:
if (cdnrinfo != NULL)
free(cdnrinfo);
return (error);
}
int
qop_cdnr_add_tbmeter(struct classinfo **rp, const char *cdnr_name,
struct ifinfo *ifinfo,
struct tb_profile *profile,
struct tc_action *in_action,
struct tc_action *out_action)
{
struct classinfo *clinfo, *clist[3];
struct cdnrinfo *cdnrinfo = NULL;
int n, error;
n = 0;
if (in_action->tca_code == TCACODE_HANDLE) {
clist[n] = clhandle2clinfo(ifinfo, in_action->tca_handle);
if (clist[n] == NULL)
return (QOPERR_BADCLASS);
n++;
}
if (out_action->tca_code == TCACODE_HANDLE) {
clist[n] = clhandle2clinfo(ifinfo, out_action->tca_handle);
if (clist[n] == NULL)
return (QOPERR_BADCLASS);
n++;
}
clist[n] = NULL;
if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
return (QOPERR_NOMEM);
cdnrinfo->tce_type = TCETYPE_TBMETER;
cdnrinfo->tce_un.tbmeter.profile = *profile;
cdnrinfo->tce_un.tbmeter.in_action = *in_action;
cdnrinfo->tce_un.tbmeter.out_action = *out_action;
if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
cdnrinfo)) != 0)
goto err_ret;
if (rp != NULL)
*rp = clinfo;
return (0);
err_ret:
if (cdnrinfo != NULL)
free(cdnrinfo);
return (error);
}
int
qop_cdnr_modify_tbmeter(struct classinfo *clinfo, struct tb_profile *profile)
{
struct cdnrinfo *cdnrinfo = clinfo->private;
if (cdnrinfo->tce_type != TCETYPE_TBMETER)
return (QOPERR_CLASS_INVAL);
cdnrinfo->tce_un.tbmeter.profile = *profile;
return qop_modify_class(clinfo, NULL);
}
int
qop_cdnr_add_trtcm(struct classinfo **rp, const char *cdnr_name,
struct ifinfo *ifinfo,
struct tb_profile *cmtd_profile,
struct tb_profile *peak_profile,
struct tc_action *green_action,
struct tc_action *yellow_action,
struct tc_action *red_action, int coloraware)
{
struct classinfo *clinfo, *clist[4];
struct cdnrinfo *cdnrinfo = NULL;
int n, error;
n = 0;
if (green_action->tca_code == TCACODE_HANDLE) {
clist[n] = clhandle2clinfo(ifinfo, green_action->tca_handle);
if (clist[n] == NULL)
return (QOPERR_BADCLASS);
n++;
}
if (yellow_action->tca_code == TCACODE_HANDLE) {
clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
if (clist[n] == NULL)
return (QOPERR_BADCLASS);
n++;
}
if (red_action->tca_code == TCACODE_HANDLE) {
clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
if (clist[n] == NULL)
return (QOPERR_BADCLASS);
n++;
}
clist[n] = NULL;
if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
return (QOPERR_NOMEM);
cdnrinfo->tce_type = TCETYPE_TRTCM;
cdnrinfo->tce_un.trtcm.cmtd_profile = *cmtd_profile;
cdnrinfo->tce_un.trtcm.peak_profile = *peak_profile;
cdnrinfo->tce_un.trtcm.green_action = *green_action;
cdnrinfo->tce_un.trtcm.yellow_action = *yellow_action;
cdnrinfo->tce_un.trtcm.red_action = *red_action;
cdnrinfo->tce_un.trtcm.coloraware = coloraware;
if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
cdnrinfo)) != 0)
goto err_ret;
if (rp != NULL)
*rp = clinfo;
return (0);
err_ret:
if (cdnrinfo != NULL)
free(cdnrinfo);
return (error);
}
int
qop_cdnr_modify_trtcm(struct classinfo *clinfo,
struct tb_profile *cmtd_profile,
struct tb_profile *peak_profile, int coloraware)
{
struct cdnrinfo *cdnrinfo = clinfo->private;
if (cdnrinfo->tce_type != TCETYPE_TRTCM)
return (QOPERR_CLASS_INVAL);
cdnrinfo->tce_un.trtcm.cmtd_profile = *cmtd_profile;
cdnrinfo->tce_un.trtcm.peak_profile = *peak_profile;
cdnrinfo->tce_un.trtcm.coloraware = coloraware;
return qop_modify_class(clinfo, NULL);
}
int
qop_cdnr_add_tswtcm(struct classinfo **rp, const char *cdnr_name,
struct ifinfo *ifinfo, const u_int32_t cmtd_rate,
const u_int32_t peak_rate, const u_int32_t avg_interval,
struct tc_action *green_action,
struct tc_action *yellow_action,
struct tc_action *red_action)
{
struct classinfo *clinfo, *clist[4];
struct cdnrinfo *cdnrinfo = NULL;
int n, error;
n = 0;
if (green_action->tca_code == TCACODE_HANDLE) {
clist[n] = clhandle2clinfo(ifinfo, green_action->tca_handle);
if (clist[n] == NULL)
return (QOPERR_BADCLASS);
n++;
}
if (yellow_action->tca_code == TCACODE_HANDLE) {
clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
if (clist[n] == NULL)
return (QOPERR_BADCLASS);
n++;
}
if (red_action->tca_code == TCACODE_HANDLE) {
clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
if (clist[n] == NULL)
return (QOPERR_BADCLASS);
n++;
}
clist[n] = NULL;
if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
return (QOPERR_NOMEM);
cdnrinfo->tce_type = TCETYPE_TSWTCM;
cdnrinfo->tce_un.tswtcm.cmtd_rate = cmtd_rate;
cdnrinfo->tce_un.tswtcm.peak_rate = peak_rate;
cdnrinfo->tce_un.tswtcm.avg_interval = avg_interval;
cdnrinfo->tce_un.tswtcm.green_action = *green_action;
cdnrinfo->tce_un.tswtcm.yellow_action = *yellow_action;
cdnrinfo->tce_un.tswtcm.red_action = *red_action;
if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
cdnrinfo)) != 0)
goto err_ret;
if (rp != NULL)
*rp = clinfo;
return (0);
err_ret:
if (cdnrinfo != NULL)
free(cdnrinfo);
return (error);
}
int
qop_cdnr_modify_tswtcm(struct classinfo *clinfo, const u_int32_t cmtd_rate,
const u_int32_t peak_rate, const u_int32_t avg_interval)
{
struct cdnrinfo *cdnrinfo = clinfo->private;
if (cdnrinfo->tce_type != TCETYPE_TSWTCM)
return (QOPERR_CLASS_INVAL);
cdnrinfo->tce_un.tswtcm.cmtd_rate = cmtd_rate;
cdnrinfo->tce_un.tswtcm.peak_rate = peak_rate;
cdnrinfo->tce_un.tswtcm.avg_interval = avg_interval;
return qop_modify_class(clinfo, NULL);
}
/*
* system call interfaces for qdisc_ops
*/
static int
cdnr_attach(struct ifinfo *ifinfo)
{
struct cdnr_interface iface;
if (cdnr_fd < 0 &&
(cdnr_fd = open(CDNR_DEVICE, O_RDWR)) < 0 &&
(cdnr_fd = open_module(CDNR_DEVICE, O_RDWR)) < 0) {
LOG(LOG_ERR, errno, "CDNR open");
return (QOPERR_SYSCALL);
}
cdnr_refcount++;
memset(&iface, 0, sizeof(iface));
strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
if (ioctl(cdnr_fd, CDNR_IF_ATTACH, &iface) < 0)
return (QOPERR_SYSCALL);
#if 1
LOG(LOG_INFO, 0, "conditioner attached to %s", iface.cdnr_ifname);
#endif
return (0);
}
static int
cdnr_detach(struct ifinfo *ifinfo)
{
struct cdnr_interface iface;
memset(&iface, 0, sizeof(iface));
strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
if (ioctl(cdnr_fd, CDNR_IF_DETACH, &iface) < 0)
return (QOPERR_SYSCALL);
if (--cdnr_refcount == 0) {
close(cdnr_fd);
cdnr_fd = -1;
}
return (0);
}
static int
cdnr_enable(struct ifinfo *ifinfo)
{
struct cdnr_interface iface;
memset(&iface, 0, sizeof(iface));
strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
if (ioctl(cdnr_fd, CDNR_ENABLE, &iface) < 0)
return (QOPERR_SYSCALL);
return (0);
}
static int
cdnr_disable(struct ifinfo *ifinfo)
{
struct cdnr_interface iface;
memset(&iface, 0, sizeof(iface));
strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
if (ioctl(cdnr_fd, CDNR_DISABLE, &iface) < 0)
return (QOPERR_SYSCALL);
return (0);
}
static int
cdnr_add_class(struct classinfo *clinfo)
{
struct cdnr_add_element element_add;
struct cdnr_add_tbmeter tbmeter_add;
struct cdnr_add_trtcm trtcm_add;
struct cdnr_add_tswtcm tswtcm_add;
struct cdnrinfo *cdnrinfo;
cdnrinfo = clinfo->private;
/* root class is a dummy class */
if (clinfo->parent == NULL) {
clinfo->handle = 0;
return (0);
}
switch (cdnrinfo->tce_type) {
case TCETYPE_ELEMENT:
memset(&element_add, 0, sizeof(element_add));
strncpy(element_add.iface.cdnr_ifname,
clinfo->ifinfo->ifname+1, IFNAMSIZ);
element_add.action = cdnrinfo->tce_un.element.action;
if (ioctl(cdnr_fd, CDNR_ADD_ELEM, &element_add) < 0) {
clinfo->handle = CDNR_NULL_HANDLE;
return (QOPERR_SYSCALL);
}
clinfo->handle = element_add.cdnr_handle;
break;
case TCETYPE_TBMETER:
memset(&tbmeter_add, 0, sizeof(tbmeter_add));
strncpy(tbmeter_add.iface.cdnr_ifname,
clinfo->ifinfo->ifname+1, IFNAMSIZ);
tbmeter_add.profile = cdnrinfo->tce_un.tbmeter.profile;
tbmeter_add.in_action = cdnrinfo->tce_un.tbmeter.in_action;
tbmeter_add.out_action = cdnrinfo->tce_un.tbmeter.out_action;
if (ioctl(cdnr_fd, CDNR_ADD_TBM, &tbmeter_add) < 0) {
clinfo->handle = CDNR_NULL_HANDLE;
return (QOPERR_SYSCALL);
}
clinfo->handle = tbmeter_add.cdnr_handle;
break;
case TCETYPE_TRTCM:
memset(&trtcm_add, 0, sizeof(trtcm_add));
strncpy(trtcm_add.iface.cdnr_ifname,
clinfo->ifinfo->ifname+1, IFNAMSIZ);
trtcm_add.cmtd_profile = cdnrinfo->tce_un.trtcm.cmtd_profile;
trtcm_add.peak_profile = cdnrinfo->tce_un.trtcm.peak_profile;
trtcm_add.green_action = cdnrinfo->tce_un.trtcm.green_action;
trtcm_add.yellow_action = cdnrinfo->tce_un.trtcm.yellow_action;
trtcm_add.red_action = cdnrinfo->tce_un.trtcm.red_action;
trtcm_add.coloraware = cdnrinfo->tce_un.trtcm.coloraware;
if (ioctl(cdnr_fd, CDNR_ADD_TCM, &trtcm_add) < 0) {
clinfo->handle = CDNR_NULL_HANDLE;
return (QOPERR_SYSCALL);
}
clinfo->handle = trtcm_add.cdnr_handle;
break;
case TCETYPE_TSWTCM:
memset(&tswtcm_add, 0, sizeof(tswtcm_add));
strncpy(tswtcm_add.iface.cdnr_ifname,
clinfo->ifinfo->ifname+1, IFNAMSIZ);
tswtcm_add.cmtd_rate = cdnrinfo->tce_un.tswtcm.cmtd_rate;
tswtcm_add.peak_rate = cdnrinfo->tce_un.tswtcm.peak_rate;
tswtcm_add.avg_interval = cdnrinfo->tce_un.tswtcm.avg_interval;
tswtcm_add.green_action = cdnrinfo->tce_un.tswtcm.green_action;
tswtcm_add.yellow_action = cdnrinfo->tce_un.tswtcm.yellow_action;
tswtcm_add.red_action = cdnrinfo->tce_un.tswtcm.red_action;
if (ioctl(cdnr_fd, CDNR_ADD_TSW, &tswtcm_add) < 0) {
clinfo->handle = CDNR_NULL_HANDLE;
return (QOPERR_SYSCALL);
}
clinfo->handle = tswtcm_add.cdnr_handle;
break;
default:
return (QOPERR_CLASS_INVAL);
}
return (0);
}
static int
cdnr_modify_class(struct classinfo *clinfo, void *arg)
{
struct cdnr_modify_tbmeter tbmeter_modify;
struct cdnr_modify_trtcm trtcm_modify;
struct cdnr_modify_tswtcm tswtcm_modify;
struct cdnrinfo *cdnrinfo;
cdnrinfo = clinfo->private;
switch (cdnrinfo->tce_type) {
case TCETYPE_TBMETER:
memset(&tbmeter_modify, 0, sizeof(tbmeter_modify));
strncpy(tbmeter_modify.iface.cdnr_ifname,
clinfo->ifinfo->ifname+1, IFNAMSIZ);
tbmeter_modify.cdnr_handle = clinfo->handle;
tbmeter_modify.profile = cdnrinfo->tce_un.tbmeter.profile;
if (ioctl(cdnr_fd, CDNR_MOD_TBM, &tbmeter_modify) < 0)
return (QOPERR_SYSCALL);
break;
case TCETYPE_TRTCM:
memset(&trtcm_modify, 0, sizeof(trtcm_modify));
strncpy(trtcm_modify.iface.cdnr_ifname,
clinfo->ifinfo->ifname+1, IFNAMSIZ);
trtcm_modify.cdnr_handle = clinfo->handle;
trtcm_modify.cmtd_profile =
cdnrinfo->tce_un.trtcm.cmtd_profile;
trtcm_modify.peak_profile =
cdnrinfo->tce_un.trtcm.peak_profile;
trtcm_modify.coloraware = cdnrinfo->tce_un.trtcm.coloraware;
if (ioctl(cdnr_fd, CDNR_MOD_TCM, &trtcm_modify) < 0)
return (QOPERR_SYSCALL);
break;
case TCETYPE_TSWTCM:
memset(&tswtcm_modify, 0, sizeof(tswtcm_modify));
strncpy(tswtcm_modify.iface.cdnr_ifname,
clinfo->ifinfo->ifname+1, IFNAMSIZ);
tswtcm_modify.cdnr_handle = clinfo->handle;
tswtcm_modify.cmtd_rate = cdnrinfo->tce_un.tswtcm.cmtd_rate;
tswtcm_modify.peak_rate = cdnrinfo->tce_un.tswtcm.peak_rate;
tswtcm_modify.avg_interval = cdnrinfo->tce_un.tswtcm.avg_interval;
if (ioctl(cdnr_fd, CDNR_MOD_TSW, &tswtcm_modify) < 0)
return (QOPERR_SYSCALL);
break;
default:
return (QOPERR_CLASS_INVAL);
}
return (0);
}
static int
cdnr_delete_class(struct classinfo *clinfo)
{
struct cdnr_delete_element element_delete;
if (clinfo->handle == CDNR_NULL_HANDLE)
return (0);
memset(&element_delete, 0, sizeof(element_delete));
strncpy(element_delete.iface.cdnr_ifname, clinfo->ifinfo->ifname+1,
IFNAMSIZ);
element_delete.cdnr_handle = clinfo->handle;
if (ioctl(cdnr_fd, CDNR_DEL_ELEM, &element_delete) < 0)
return (QOPERR_SYSCALL);
return (0);
}
static int
cdnr_add_filter(struct fltrinfo *fltrinfo)
{
struct cdnr_add_filter fltr_add;
memset(&fltr_add, 0, sizeof(fltr_add));
strncpy(fltr_add.iface.cdnr_ifname,
fltrinfo->clinfo->ifinfo->ifname+1, IFNAMSIZ);
fltr_add.cdnr_handle = fltrinfo->clinfo->handle;
fltr_add.filter = fltrinfo->fltr;
if (ioctl(cdnr_fd, CDNR_ADD_FILTER, &fltr_add) < 0)
return (QOPERR_SYSCALL);
fltrinfo->handle = fltr_add.filter_handle;
return (0);
}
static int
cdnr_delete_filter(struct fltrinfo *fltrinfo)
{
struct cdnr_delete_filter fltr_del;
memset(&fltr_del, 0, sizeof(fltr_del));
strncpy(fltr_del.iface.cdnr_ifname,
fltrinfo->clinfo->ifinfo->ifname+1, IFNAMSIZ);
fltr_del.filter_handle = fltrinfo->handle;
if (ioctl(cdnr_fd, CDNR_DEL_FILTER, &fltr_del) < 0)
return (QOPERR_SYSCALL);
return (0);
}
static int
verify_tbprofile(struct tb_profile *profile, const char *cdnr_name)
{
if (profile->depth < 1500) {
LOG(LOG_WARNING, 0,
"warning: token bucket depth for %s is too small (%d)",
cdnr_name, profile->depth);
return (-1);
}
return (0);
}