root/usr.sbin/altq/libaltq/qop_cdnr.c
/*      $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);
}