/* $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 <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;
/*
* 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);
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.
*/
/*
* 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);
}
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);