Home | History | Annotate | Line # | Download | only in libaltq
      1 /*	$NetBSD: qop_cdnr.c,v 1.4 2001/08/22 08:52:37 itojun Exp $	*/
      2 /*	$KAME: qop_cdnr.c,v 1.9 2001/08/16 10:39:14 kjc Exp $	*/
      3 /*
      4  * Copyright (C) 1999-2000
      5  *	Sony Computer Science Laboratories, Inc.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/param.h>
     30 #include <sys/socket.h>
     31 #include <sys/sockio.h>
     32 #include <sys/ioctl.h>
     33 #include <sys/fcntl.h>
     34 #include <net/if.h>
     35 #include <netinet/in.h>
     36 #include <arpa/inet.h>
     37 
     38 #include <stdio.h>
     39 #include <stdlib.h>
     40 #include <unistd.h>
     41 #include <stddef.h>
     42 #include <string.h>
     43 #include <ctype.h>
     44 #include <errno.h>
     45 #include <syslog.h>
     46 #include <netdb.h>
     47 
     48 #include <altq/altq.h>
     49 #include <altq/altq_cdnr.h>
     50 #include "altq_qop.h"
     51 #include "qop_cdnr.h"
     52 /*
     53  * diffserve traffic conditioner support
     54  *
     55  * we use the existing qop interface to support conditioner.
     56  */
     57 
     58 static struct ifinfo *cdnr_ifname2ifinfo(const char *);
     59 static int cdnr_attach(struct ifinfo *);
     60 static int cdnr_detach(struct ifinfo *);
     61 static int cdnr_enable(struct ifinfo *);
     62 static int cdnr_disable(struct ifinfo *);
     63 static int cdnr_add_class(struct classinfo *);
     64 static int cdnr_modify_class(struct classinfo *, void *);
     65 static int cdnr_delete_class(struct classinfo *);
     66 static int cdnr_add_filter(struct fltrinfo *);
     67 static int cdnr_delete_filter(struct fltrinfo *);
     68 static int verify_tbprofile(struct tb_profile *, const char *);
     69 
     70 #define CDNR_DEVICE	"/dev/altq/cdnr"
     71 
     72 static int cdnr_fd = -1;
     73 static int cdnr_refcount = 0;
     74 
     75 static struct qdisc_ops cdnr_qdisc = {
     76 	ALTQT_CDNR,
     77 	"cdnr",
     78 	cdnr_attach,
     79 	cdnr_detach,
     80 	NULL,			/* clear */
     81 	cdnr_enable,
     82 	cdnr_disable,
     83 	cdnr_add_class,
     84 	cdnr_modify_class,
     85 	cdnr_delete_class,
     86 	cdnr_add_filter,
     87 	cdnr_delete_filter,
     88 };
     89 
     90 u_long
     91 cdnr_name2handle(const char *ifname, const char *cdnr_name)
     92 {
     93 	struct ifinfo		*ifinfo;
     94 	struct classinfo	*clinfo;
     95 
     96 	if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
     97 		return (CDNR_NULL_HANDLE);
     98 
     99 	if ((clinfo = clname2clinfo(ifinfo, cdnr_name)) == NULL)
    100 		return (CDNR_NULL_HANDLE);
    101 
    102 	return (clinfo->handle);
    103 }
    104 
    105 static struct ifinfo *
    106 cdnr_ifname2ifinfo(const char *ifname)
    107 {
    108 	struct ifinfo	*ifinfo;
    109 	char input_ifname[64];
    110 
    111 	/*
    112 	 * search for an existing input interface
    113 	 */
    114 	if ((ifinfo = input_ifname2ifinfo(ifname)) != NULL)
    115 		return (ifinfo);
    116 
    117 	/*
    118 	 * if there is a corresponding output interface,
    119 	 * create an input interface by prepending "_" to
    120 	 * its name.
    121 	 */
    122 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
    123 		return (NULL);
    124 
    125 	input_ifname[0] = '_';
    126 	strlcpy(input_ifname+1, ifname, sizeof(input_ifname)-1);
    127 	if (qop_add_if(&ifinfo, input_ifname, 0, &cdnr_qdisc, NULL) != 0) {
    128 		LOG(LOG_ERR, errno,
    129 		    "cdnr_ifname2ifinfo: can't add a input interface %s",
    130 		    ifname);
    131 		return (NULL);
    132 	}
    133 	return (ifinfo);
    134 }
    135 
    136 int
    137 qcmd_cdnr_add_element(struct tc_action *rp, const char *ifname,
    138 		   const char *cdnr_name, struct tc_action *action)
    139 {
    140 	struct ifinfo		*ifinfo;
    141 	struct classinfo	*clinfo;
    142 	int error;
    143 
    144 	if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
    145 		return (QOPERR_BADIF);
    146 
    147 	if ((error = qop_cdnr_add_element(&clinfo, cdnr_name, ifinfo,
    148 					  action)) != 0) {
    149 		LOG(LOG_ERR, errno, "%s: add element failed!",
    150 		    qoperror(error));
    151 		return (error);
    152 	}
    153 
    154 	if (rp != NULL) {
    155 		rp->tca_code = TCACODE_HANDLE;
    156 		rp->tca_handle = clinfo->handle;
    157 	}
    158 	return (0);
    159 }
    160 
    161 int
    162 qcmd_cdnr_add_tbmeter(struct tc_action *rp, const char *ifname,
    163 		      const char *cdnr_name,
    164 		      struct tb_profile *profile,
    165 		      struct tc_action *in_action,
    166 		      struct tc_action *out_action)
    167 {
    168 	struct ifinfo		*ifinfo;
    169 	struct classinfo	*clinfo;
    170 	int error;
    171 
    172 	if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
    173 		return (QOPERR_BADIF);
    174 
    175 	verify_tbprofile(profile, cdnr_name);
    176 
    177 	if ((error = qop_cdnr_add_tbmeter(&clinfo, cdnr_name, ifinfo,
    178 				  profile, in_action, out_action)) != 0) {
    179 		LOG(LOG_ERR, errno, "%s: add tbmeter failed!",
    180 		    qoperror(error));
    181 		return (error);
    182 	}
    183 
    184 	if (rp != NULL) {
    185 		rp->tca_code = TCACODE_HANDLE;
    186 		rp->tca_handle = clinfo->handle;
    187 	}
    188 	return (0);
    189 }
    190 
    191 int
    192 qcmd_cdnr_add_trtcm(struct tc_action *rp, const char *ifname,
    193 		    const char *cdnr_name,
    194 		    struct tb_profile *cmtd_profile,
    195 		    struct tb_profile *peak_profile,
    196 		    struct tc_action *green_action,
    197 		    struct tc_action *yellow_action,
    198 		    struct tc_action *red_action, int coloraware)
    199 {
    200 	struct ifinfo		*ifinfo;
    201 	struct classinfo	*clinfo;
    202 	int error;
    203 
    204 	if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
    205 		return (QOPERR_BADIF);
    206 
    207 	verify_tbprofile(cmtd_profile, cdnr_name);
    208 	verify_tbprofile(peak_profile, cdnr_name);
    209 
    210 	if ((error = qop_cdnr_add_trtcm(&clinfo, cdnr_name, ifinfo,
    211 			  cmtd_profile, peak_profile,
    212 			  green_action, yellow_action, red_action,
    213 	     		  coloraware)) != 0) {
    214 		LOG(LOG_ERR, errno, "%s: add trtcm failed!",
    215 		    qoperror(error));
    216 		return (error);
    217 	}
    218 
    219 	if (rp != NULL) {
    220 		rp->tca_code = TCACODE_HANDLE;
    221 		rp->tca_handle = clinfo->handle;
    222 	}
    223 	return (0);
    224 }
    225 
    226 int
    227 qcmd_cdnr_add_tswtcm(struct tc_action *rp, const char *ifname,
    228 		     const char *cdnr_name, const u_int32_t cmtd_rate,
    229 		     const u_int32_t peak_rate, const u_int32_t avg_interval,
    230 		     struct tc_action *green_action,
    231 		     struct tc_action *yellow_action,
    232 		     struct tc_action *red_action)
    233 {
    234 	struct ifinfo		*ifinfo;
    235 	struct classinfo	*clinfo;
    236 	int error;
    237 
    238 	if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
    239 		return (QOPERR_BADIF);
    240 
    241 	if (cmtd_rate > peak_rate) {
    242 		LOG(LOG_ERR, 0,
    243 		    "add tswtcm: cmtd_rate larger than peak_rate!");
    244 		return (QOPERR_INVAL);
    245 	}
    246 
    247 	if ((error = qop_cdnr_add_tswtcm(&clinfo, cdnr_name, ifinfo,
    248 					cmtd_rate, peak_rate, avg_interval,
    249 					green_action, yellow_action,
    250 					red_action)) != 0) {
    251 		LOG(LOG_ERR, errno, "%s: add tswtcm failed!",
    252 		    qoperror(error));
    253 		return (error);
    254 	}
    255 
    256 	if (rp != NULL) {
    257 		rp->tca_code = TCACODE_HANDLE;
    258 		rp->tca_handle = clinfo->handle;
    259 	}
    260 	return (0);
    261 }
    262 
    263 int
    264 qcmd_cdnr_delete(const char *ifname, const char *cdnr_name)
    265 {
    266 	struct ifinfo		*ifinfo;
    267 	struct classinfo	*clinfo;
    268 
    269 	if ((ifinfo = cdnr_ifname2ifinfo(ifname)) == NULL)
    270 		return (QOPERR_BADIF);
    271 
    272 	if ((clinfo = clname2clinfo(ifinfo, cdnr_name)) == NULL)
    273 		return (QOPERR_BADCLASS);
    274 
    275 	return qop_delete_cdnr(clinfo);
    276 }
    277 
    278 /*
    279  * class operations:
    280  *	class structure is used to hold conditioners.
    281  *	XXX
    282  *	conditioners has dependencies in the reverse order; parent nodes
    283  *	refere to child nodes, and thus, a child is created first and
    284  *	parents should be removed first.
    285  *	qop_add_cdnr() and qop_delete_cdnr() are wrapper functions
    286  *	of qop_add_class() and qop_delete_class(), and takes care
    287  *	of dependencies.
    288  *	1. when adding a conditioner, it is created as a child of a
    289  *	   dummy root class.  then, the child conditioners are made
    290  *	   as its children.
    291  *	2. when deleting a conditioner, its child conditioners are made
    292  *	   as children of the dummy root class.  then, the conditioner
    293  *	   is deleted.
    294  */
    295 
    296 int
    297 qop_add_cdnr(struct classinfo **rp, const char *cdnr_name,
    298 	     struct ifinfo *ifinfo, struct classinfo **childlist,
    299 	     void *cdnr_private)
    300 {
    301 	struct classinfo	*clinfo, *root, *cl, *prev;
    302 	int error;
    303 
    304 	/*
    305 	 * if there is no root cdnr, create one.
    306 	 */
    307 	if ((root = get_rootclass(ifinfo)) == NULL) {
    308 		if ((error = qop_add_class(&root, "cdnr_root",
    309 					   ifinfo, NULL, NULL)) != 0) {
    310 			LOG(LOG_ERR, errno,
    311 			    "cdnr: %s: can't create dummy root cdnr on %s!",
    312 			    qoperror(error), ifinfo->ifname);
    313 			return (QOPERR_CLASS);
    314 		}
    315 	}
    316 
    317 	/*
    318 	 * create a class as a child of a root class.
    319 	 */
    320 	if ((error = qop_add_class(&clinfo, cdnr_name,
    321 				   ifinfo, root, cdnr_private)) != 0)
    322 		return (error);
    323 	/*
    324 	 * move child nodes
    325 	 */
    326 	for (cl = *childlist; cl != NULL; cl = *++childlist) {
    327 		if (cl->parent != root) {
    328 			/*
    329 			 * this conditioner already has a non-root parent.
    330 			 * we can't track down a multi-parent node by a
    331 			 * tree structure; leave it as it is.
    332 			 * (we need a mechanism similar to a symbolic link
    333 			 * in a file system)
    334 			 */
    335 			continue;
    336 		}
    337 		/* remove this child from the root */
    338 		if (root->child == cl)
    339 			root->child = cl->sibling;
    340 		else for (prev = root->child;
    341 			  prev->sibling != NULL; prev = prev->sibling)
    342 			if (prev->sibling == cl) {
    343 				prev->sibling = cl->sibling;
    344 				break;
    345 			}
    346 
    347 		/* add as a child */
    348 		cl->sibling = clinfo->child;
    349 		clinfo->child = cl;
    350 		cl->parent = clinfo;
    351 	}
    352 
    353 	if (rp != NULL)
    354 		*rp = clinfo;
    355 	return (0);
    356 }
    357 
    358 int
    359 qop_delete_cdnr(struct classinfo *clinfo)
    360 {
    361 	struct classinfo *cl, *root;
    362 	int error;
    363 
    364 	if ((root = get_rootclass(clinfo->ifinfo)) == NULL) {
    365 		LOG(LOG_ERR, 0, "qop_delete_cdnr: no root cdnr!");
    366 		return (QOPERR_CLASS);
    367 	}
    368 
    369 	if (clinfo->parent != root)
    370 		return (QOPERR_CLASS_PERM);
    371 
    372 	if ((cl = clinfo->child) != NULL) {
    373 		/* change child's parent to root, find the last child */
    374 		while (cl->sibling != NULL) {
    375 			cl->parent = root;
    376 			cl = cl->sibling;
    377 		}
    378 		cl->parent = root;
    379 
    380 		/* move children to siblings */
    381 		cl->sibling = clinfo->sibling;
    382 		clinfo->sibling = cl;
    383 		clinfo->child = NULL;
    384 	}
    385 
    386 	error = qop_delete_class(clinfo);
    387 
    388 	if (error) {
    389 		/* ick! restore the class tree */
    390 		if (cl != NULL) {
    391 			clinfo->child = clinfo->sibling;
    392 			clinfo->sibling = cl->sibling;
    393 			cl->sibling = NULL;
    394 			/* restore parent field */
    395 			for (cl = clinfo->child; cl != NULL; cl = cl->sibling)
    396 				cl->parent = clinfo;
    397 		}
    398 	}
    399 	return (error);
    400 }
    401 
    402 int
    403 qop_cdnr_add_element(struct classinfo **rp, const char *cdnr_name,
    404 		     struct ifinfo *ifinfo, struct tc_action *action)
    405 {
    406 	struct classinfo *clinfo, *clist[2];
    407 	struct cdnrinfo *cdnrinfo = NULL;
    408 	int error;
    409 
    410 	if (action->tca_code == TCACODE_HANDLE) {
    411 		clinfo = clhandle2clinfo(ifinfo, action->tca_handle);
    412 		if (clinfo == NULL)
    413 			return (QOPERR_BADCLASS);
    414 		clist[0] = clinfo;
    415 		clist[1] = NULL;
    416 #if 1
    417 		/*
    418 		 * if the conditioner referred to doesn't have a name,
    419 		 * this is called just to add a name to it.
    420 		 * we can simply add the name to the existing conditioner
    421 		 * and return it.
    422 		 */
    423 		if (cdnr_name != NULL &&
    424 		    strcmp(clinfo->clname, "(null)") == 0) {
    425 			free(clinfo->clname);
    426 			clinfo->clname = strdup(cdnr_name);
    427 			if (rp != NULL)
    428 				*rp = clinfo;
    429 			return (0);
    430 		}
    431 #endif
    432 	} else
    433 		clist[0] = NULL;
    434 
    435 	if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
    436 		return (QOPERR_NOMEM);
    437 
    438 	cdnrinfo->tce_type = TCETYPE_ELEMENT;
    439 	cdnrinfo->tce_un.element.action = *action;
    440 
    441 	if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
    442 				  cdnrinfo)) != 0)
    443 		goto err_ret;
    444 
    445 	if (rp != NULL)
    446 		*rp = clinfo;
    447 	return (0);
    448 
    449  err_ret:
    450 	if (cdnrinfo != NULL)
    451 		free(cdnrinfo);
    452 	return (error);
    453 }
    454 
    455 int
    456 qop_cdnr_add_tbmeter(struct classinfo **rp, const char *cdnr_name,
    457 		     struct ifinfo *ifinfo,
    458 		     struct tb_profile *profile,
    459 		     struct tc_action *in_action,
    460 		     struct tc_action *out_action)
    461 {
    462 	struct classinfo *clinfo, *clist[3];
    463 	struct cdnrinfo *cdnrinfo = NULL;
    464 	int n, error;
    465 
    466 	n = 0;
    467 	if (in_action->tca_code == TCACODE_HANDLE) {
    468 		clist[n] = clhandle2clinfo(ifinfo, in_action->tca_handle);
    469 		if (clist[n] == NULL)
    470 			return (QOPERR_BADCLASS);
    471 		n++;
    472 	}
    473 	if (out_action->tca_code == TCACODE_HANDLE) {
    474 		clist[n] = clhandle2clinfo(ifinfo, out_action->tca_handle);
    475 		if (clist[n] == NULL)
    476 			return (QOPERR_BADCLASS);
    477 		n++;
    478 	}
    479 	clist[n] = NULL;
    480 
    481 	if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
    482 		return (QOPERR_NOMEM);
    483 
    484 	cdnrinfo->tce_type = TCETYPE_TBMETER;
    485 	cdnrinfo->tce_un.tbmeter.profile = *profile;
    486 	cdnrinfo->tce_un.tbmeter.in_action = *in_action;
    487 	cdnrinfo->tce_un.tbmeter.out_action = *out_action;
    488 
    489 	if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
    490 				   cdnrinfo)) != 0)
    491 		goto err_ret;
    492 
    493 	if (rp != NULL)
    494 		*rp = clinfo;
    495 	return (0);
    496 
    497  err_ret:
    498 	if (cdnrinfo != NULL)
    499 		free(cdnrinfo);
    500 	return (error);
    501 }
    502 
    503 int
    504 qop_cdnr_modify_tbmeter(struct classinfo *clinfo, struct tb_profile *profile)
    505 {
    506 	struct cdnrinfo *cdnrinfo = clinfo->private;
    507 
    508 	if (cdnrinfo->tce_type != TCETYPE_TBMETER)
    509 		return (QOPERR_CLASS_INVAL);
    510 	cdnrinfo->tce_un.tbmeter.profile = *profile;
    511 
    512 	return qop_modify_class(clinfo, NULL);
    513 }
    514 
    515 int
    516 qop_cdnr_add_trtcm(struct classinfo **rp, const char *cdnr_name,
    517 		   struct ifinfo *ifinfo,
    518 		   struct tb_profile *cmtd_profile,
    519 		   struct tb_profile *peak_profile,
    520 		   struct tc_action *green_action,
    521 		   struct tc_action *yellow_action,
    522 		   struct tc_action *red_action, int coloraware)
    523 {
    524 	struct classinfo *clinfo, *clist[4];
    525 	struct cdnrinfo *cdnrinfo = NULL;
    526 	int n, error;
    527 
    528 	n = 0;
    529 	if (green_action->tca_code == TCACODE_HANDLE) {
    530 		clist[n] = clhandle2clinfo(ifinfo, green_action->tca_handle);
    531 		if (clist[n] == NULL)
    532 			return (QOPERR_BADCLASS);
    533 		n++;
    534 	}
    535 	if (yellow_action->tca_code == TCACODE_HANDLE) {
    536 		clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
    537 		if (clist[n] == NULL)
    538 			return (QOPERR_BADCLASS);
    539 		n++;
    540 	}
    541 	if (red_action->tca_code == TCACODE_HANDLE) {
    542 		clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
    543 		if (clist[n] == NULL)
    544 			return (QOPERR_BADCLASS);
    545 		n++;
    546 	}
    547 	clist[n] = NULL;
    548 
    549 	if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
    550 		return (QOPERR_NOMEM);
    551 
    552 	cdnrinfo->tce_type = TCETYPE_TRTCM;
    553 	cdnrinfo->tce_un.trtcm.cmtd_profile = *cmtd_profile;
    554 	cdnrinfo->tce_un.trtcm.peak_profile = *peak_profile;
    555 	cdnrinfo->tce_un.trtcm.green_action = *green_action;
    556 	cdnrinfo->tce_un.trtcm.yellow_action = *yellow_action;
    557 	cdnrinfo->tce_un.trtcm.red_action = *red_action;
    558 	cdnrinfo->tce_un.trtcm.coloraware = coloraware;
    559 
    560 	if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
    561 				  cdnrinfo)) != 0)
    562 		goto err_ret;
    563 
    564 	if (rp != NULL)
    565 		*rp = clinfo;
    566 	return (0);
    567 
    568  err_ret:
    569 	if (cdnrinfo != NULL)
    570 		free(cdnrinfo);
    571 	return (error);
    572 }
    573 
    574 int
    575 qop_cdnr_modify_trtcm(struct classinfo *clinfo,
    576 		      struct tb_profile *cmtd_profile,
    577 		      struct tb_profile *peak_profile, int coloraware)
    578 {
    579 	struct cdnrinfo *cdnrinfo = clinfo->private;
    580 
    581 	if (cdnrinfo->tce_type != TCETYPE_TRTCM)
    582 		return (QOPERR_CLASS_INVAL);
    583 	cdnrinfo->tce_un.trtcm.cmtd_profile = *cmtd_profile;
    584 	cdnrinfo->tce_un.trtcm.peak_profile = *peak_profile;
    585 	cdnrinfo->tce_un.trtcm.coloraware = coloraware;
    586 
    587 	return qop_modify_class(clinfo, NULL);
    588 }
    589 
    590 int
    591 qop_cdnr_add_tswtcm(struct classinfo **rp, const char *cdnr_name,
    592 		    struct ifinfo *ifinfo, const u_int32_t cmtd_rate,
    593 		    const u_int32_t peak_rate, const u_int32_t avg_interval,
    594 		    struct tc_action *green_action,
    595 		    struct tc_action *yellow_action,
    596 		    struct tc_action *red_action)
    597 {
    598 	struct classinfo *clinfo, *clist[4];
    599 	struct cdnrinfo *cdnrinfo = NULL;
    600 	int n, error;
    601 
    602 	n = 0;
    603 	if (green_action->tca_code == TCACODE_HANDLE) {
    604 		clist[n] = clhandle2clinfo(ifinfo, green_action->tca_handle);
    605 		if (clist[n] == NULL)
    606 			return (QOPERR_BADCLASS);
    607 		n++;
    608 	}
    609 	if (yellow_action->tca_code == TCACODE_HANDLE) {
    610 		clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
    611 		if (clist[n] == NULL)
    612 			return (QOPERR_BADCLASS);
    613 		n++;
    614 	}
    615 	if (red_action->tca_code == TCACODE_HANDLE) {
    616 		clist[n] = clhandle2clinfo(ifinfo, yellow_action->tca_handle);
    617 		if (clist[n] == NULL)
    618 			return (QOPERR_BADCLASS);
    619 		n++;
    620 	}
    621 	clist[n] = NULL;
    622 
    623 	if ((cdnrinfo = calloc(1, sizeof(*cdnrinfo))) == NULL)
    624 		return (QOPERR_NOMEM);
    625 
    626 	cdnrinfo->tce_type = TCETYPE_TSWTCM;
    627 	cdnrinfo->tce_un.tswtcm.cmtd_rate = cmtd_rate;
    628 	cdnrinfo->tce_un.tswtcm.peak_rate = peak_rate;
    629 	cdnrinfo->tce_un.tswtcm.avg_interval = avg_interval;
    630 	cdnrinfo->tce_un.tswtcm.green_action = *green_action;
    631 	cdnrinfo->tce_un.tswtcm.yellow_action = *yellow_action;
    632 	cdnrinfo->tce_un.tswtcm.red_action = *red_action;
    633 
    634 	if ((error = qop_add_cdnr(&clinfo, cdnr_name, ifinfo, clist,
    635 				  cdnrinfo)) != 0)
    636 		goto err_ret;
    637 
    638 	if (rp != NULL)
    639 		*rp = clinfo;
    640 	return (0);
    641 
    642  err_ret:
    643 	if (cdnrinfo != NULL)
    644 		free(cdnrinfo);
    645 	return (error);
    646 }
    647 
    648 int
    649 qop_cdnr_modify_tswtcm(struct classinfo *clinfo, const u_int32_t cmtd_rate,
    650 		       const u_int32_t peak_rate, const u_int32_t avg_interval)
    651 {
    652 	struct cdnrinfo *cdnrinfo = clinfo->private;
    653 
    654 	if (cdnrinfo->tce_type != TCETYPE_TSWTCM)
    655 		return (QOPERR_CLASS_INVAL);
    656 	cdnrinfo->tce_un.tswtcm.cmtd_rate = cmtd_rate;
    657 	cdnrinfo->tce_un.tswtcm.peak_rate = peak_rate;
    658 	cdnrinfo->tce_un.tswtcm.avg_interval = avg_interval;
    659 
    660 	return qop_modify_class(clinfo, NULL);
    661 }
    662 
    663 /*
    664  *  system call interfaces for qdisc_ops
    665  */
    666 static int
    667 cdnr_attach(struct ifinfo *ifinfo)
    668 {
    669 	struct cdnr_interface iface;
    670 
    671 	if (cdnr_fd < 0 &&
    672 	    (cdnr_fd = open(CDNR_DEVICE, O_RDWR)) < 0 &&
    673 	    (cdnr_fd = open_module(CDNR_DEVICE, O_RDWR)) < 0) {
    674 		LOG(LOG_ERR, errno, "CDNR open");
    675 		return (QOPERR_SYSCALL);
    676 	}
    677 
    678 	cdnr_refcount++;
    679 	memset(&iface, 0, sizeof(iface));
    680 	strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
    681 
    682 	if (ioctl(cdnr_fd, CDNR_IF_ATTACH, &iface) < 0)
    683 		return (QOPERR_SYSCALL);
    684 #if 1
    685 	LOG(LOG_INFO, 0, "conditioner attached to %s", iface.cdnr_ifname);
    686 #endif
    687 	return (0);
    688 }
    689 
    690 static int
    691 cdnr_detach(struct ifinfo *ifinfo)
    692 {
    693 	struct cdnr_interface iface;
    694 
    695 	memset(&iface, 0, sizeof(iface));
    696 	strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
    697 
    698 	if (ioctl(cdnr_fd, CDNR_IF_DETACH, &iface) < 0)
    699 		return (QOPERR_SYSCALL);
    700 
    701 	if (--cdnr_refcount == 0) {
    702 		close(cdnr_fd);
    703 		cdnr_fd = -1;
    704 	}
    705 	return (0);
    706 }
    707 
    708 static int
    709 cdnr_enable(struct ifinfo *ifinfo)
    710 {
    711 	struct cdnr_interface iface;
    712 
    713 	memset(&iface, 0, sizeof(iface));
    714 	strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
    715 
    716 	if (ioctl(cdnr_fd, CDNR_ENABLE, &iface) < 0)
    717 		return (QOPERR_SYSCALL);
    718 	return (0);
    719 }
    720 
    721 static int
    722 cdnr_disable(struct ifinfo *ifinfo)
    723 {
    724 	struct cdnr_interface iface;
    725 
    726 	memset(&iface, 0, sizeof(iface));
    727 	strncpy(iface.cdnr_ifname, ifinfo->ifname+1, IFNAMSIZ);
    728 
    729 	if (ioctl(cdnr_fd, CDNR_DISABLE, &iface) < 0)
    730 		return (QOPERR_SYSCALL);
    731 	return (0);
    732 }
    733 
    734 static int
    735 cdnr_add_class(struct classinfo *clinfo)
    736 {
    737 	struct cdnr_add_element element_add;
    738 	struct cdnr_add_tbmeter tbmeter_add;
    739 	struct cdnr_add_trtcm   trtcm_add;
    740 	struct cdnr_add_tswtcm  tswtcm_add;
    741 	struct cdnrinfo *cdnrinfo;
    742 
    743 	cdnrinfo = clinfo->private;
    744 
    745 	/* root class is a dummy class */
    746 	if (clinfo->parent == NULL) {
    747 		clinfo->handle = 0;
    748 		return (0);
    749 	}
    750 
    751 	switch (cdnrinfo->tce_type) {
    752 	case TCETYPE_ELEMENT:
    753 		memset(&element_add, 0, sizeof(element_add));
    754 		strncpy(element_add.iface.cdnr_ifname,
    755 			clinfo->ifinfo->ifname+1, IFNAMSIZ);
    756 		element_add.action = cdnrinfo->tce_un.element.action;
    757 		if (ioctl(cdnr_fd, CDNR_ADD_ELEM, &element_add) < 0) {
    758 			clinfo->handle = CDNR_NULL_HANDLE;
    759 			return (QOPERR_SYSCALL);
    760 		}
    761 		clinfo->handle = element_add.cdnr_handle;
    762 		break;
    763 
    764 	case TCETYPE_TBMETER:
    765 		memset(&tbmeter_add, 0, sizeof(tbmeter_add));
    766 		strncpy(tbmeter_add.iface.cdnr_ifname,
    767 			clinfo->ifinfo->ifname+1, IFNAMSIZ);
    768 		tbmeter_add.profile = cdnrinfo->tce_un.tbmeter.profile;
    769 		tbmeter_add.in_action = cdnrinfo->tce_un.tbmeter.in_action;
    770 		tbmeter_add.out_action = cdnrinfo->tce_un.tbmeter.out_action;
    771 		if (ioctl(cdnr_fd, CDNR_ADD_TBM, &tbmeter_add) < 0) {
    772 			clinfo->handle = CDNR_NULL_HANDLE;
    773 			return (QOPERR_SYSCALL);
    774 		}
    775 		clinfo->handle = tbmeter_add.cdnr_handle;
    776 		break;
    777 
    778 	case TCETYPE_TRTCM:
    779 		memset(&trtcm_add, 0, sizeof(trtcm_add));
    780 		strncpy(trtcm_add.iface.cdnr_ifname,
    781 			clinfo->ifinfo->ifname+1, IFNAMSIZ);
    782 		trtcm_add.cmtd_profile = cdnrinfo->tce_un.trtcm.cmtd_profile;
    783 		trtcm_add.peak_profile = cdnrinfo->tce_un.trtcm.peak_profile;
    784 		trtcm_add.green_action = cdnrinfo->tce_un.trtcm.green_action;
    785 		trtcm_add.yellow_action = cdnrinfo->tce_un.trtcm.yellow_action;
    786 		trtcm_add.red_action = cdnrinfo->tce_un.trtcm.red_action;
    787 		trtcm_add.coloraware = cdnrinfo->tce_un.trtcm.coloraware;
    788 		if (ioctl(cdnr_fd, CDNR_ADD_TCM, &trtcm_add) < 0) {
    789 			clinfo->handle = CDNR_NULL_HANDLE;
    790 			return (QOPERR_SYSCALL);
    791 		}
    792 		clinfo->handle = trtcm_add.cdnr_handle;
    793 		break;
    794 
    795 	case TCETYPE_TSWTCM:
    796 		memset(&tswtcm_add, 0, sizeof(tswtcm_add));
    797 		strncpy(tswtcm_add.iface.cdnr_ifname,
    798 			clinfo->ifinfo->ifname+1, IFNAMSIZ);
    799 		tswtcm_add.cmtd_rate = cdnrinfo->tce_un.tswtcm.cmtd_rate;
    800 		tswtcm_add.peak_rate = cdnrinfo->tce_un.tswtcm.peak_rate;
    801 		tswtcm_add.avg_interval = cdnrinfo->tce_un.tswtcm.avg_interval;
    802 		tswtcm_add.green_action = cdnrinfo->tce_un.tswtcm.green_action;
    803 		tswtcm_add.yellow_action = cdnrinfo->tce_un.tswtcm.yellow_action;
    804 		tswtcm_add.red_action = cdnrinfo->tce_un.tswtcm.red_action;
    805 		if (ioctl(cdnr_fd, CDNR_ADD_TSW, &tswtcm_add) < 0) {
    806 			clinfo->handle = CDNR_NULL_HANDLE;
    807 			return (QOPERR_SYSCALL);
    808 		}
    809 		clinfo->handle = tswtcm_add.cdnr_handle;
    810 		break;
    811 
    812 	default:
    813 		return (QOPERR_CLASS_INVAL);
    814 	}
    815 	return (0);
    816 }
    817 
    818 static int
    819 cdnr_modify_class(struct classinfo *clinfo, void *arg)
    820 {
    821 	struct cdnr_modify_tbmeter tbmeter_modify;
    822 	struct cdnr_modify_trtcm   trtcm_modify;
    823 	struct cdnr_modify_tswtcm  tswtcm_modify;
    824 	struct cdnrinfo *cdnrinfo;
    825 
    826 	cdnrinfo = clinfo->private;
    827 
    828 	switch (cdnrinfo->tce_type) {
    829 	case TCETYPE_TBMETER:
    830 		memset(&tbmeter_modify, 0, sizeof(tbmeter_modify));
    831 		strncpy(tbmeter_modify.iface.cdnr_ifname,
    832 			clinfo->ifinfo->ifname+1, IFNAMSIZ);
    833 		tbmeter_modify.cdnr_handle = clinfo->handle;
    834 		tbmeter_modify.profile = cdnrinfo->tce_un.tbmeter.profile;
    835 		if (ioctl(cdnr_fd, CDNR_MOD_TBM, &tbmeter_modify) < 0)
    836 			return (QOPERR_SYSCALL);
    837 		break;
    838 
    839 	case TCETYPE_TRTCM:
    840 		memset(&trtcm_modify, 0, sizeof(trtcm_modify));
    841 		strncpy(trtcm_modify.iface.cdnr_ifname,
    842 			clinfo->ifinfo->ifname+1, IFNAMSIZ);
    843 		trtcm_modify.cdnr_handle = clinfo->handle;
    844 		trtcm_modify.cmtd_profile =
    845 			cdnrinfo->tce_un.trtcm.cmtd_profile;
    846 		trtcm_modify.peak_profile =
    847 			cdnrinfo->tce_un.trtcm.peak_profile;
    848 		trtcm_modify.coloraware = cdnrinfo->tce_un.trtcm.coloraware;
    849 		if (ioctl(cdnr_fd, CDNR_MOD_TCM, &trtcm_modify) < 0)
    850 			return (QOPERR_SYSCALL);
    851 		break;
    852 
    853 	case TCETYPE_TSWTCM:
    854 		memset(&tswtcm_modify, 0, sizeof(tswtcm_modify));
    855 		strncpy(tswtcm_modify.iface.cdnr_ifname,
    856 			clinfo->ifinfo->ifname+1, IFNAMSIZ);
    857 		tswtcm_modify.cdnr_handle = clinfo->handle;
    858 		tswtcm_modify.cmtd_rate = cdnrinfo->tce_un.tswtcm.cmtd_rate;
    859 		tswtcm_modify.peak_rate = cdnrinfo->tce_un.tswtcm.peak_rate;
    860 		tswtcm_modify.avg_interval = cdnrinfo->tce_un.tswtcm.avg_interval;
    861 		if (ioctl(cdnr_fd, CDNR_MOD_TSW, &tswtcm_modify) < 0)
    862 			return (QOPERR_SYSCALL);
    863 		break;
    864 
    865 	default:
    866 		return (QOPERR_CLASS_INVAL);
    867 	}
    868 	return (0);
    869 }
    870 
    871 static int
    872 cdnr_delete_class(struct classinfo *clinfo)
    873 {
    874 	struct cdnr_delete_element element_delete;
    875 
    876 	if (clinfo->handle == CDNR_NULL_HANDLE)
    877 		return (0);
    878 
    879 	memset(&element_delete, 0, sizeof(element_delete));
    880 	strncpy(element_delete.iface.cdnr_ifname, clinfo->ifinfo->ifname+1,
    881 		IFNAMSIZ);
    882 	element_delete.cdnr_handle = clinfo->handle;
    883 
    884 	if (ioctl(cdnr_fd, CDNR_DEL_ELEM, &element_delete) < 0)
    885 		return (QOPERR_SYSCALL);
    886 	return (0);
    887 }
    888 
    889 static int
    890 cdnr_add_filter(struct fltrinfo *fltrinfo)
    891 {
    892 	struct cdnr_add_filter fltr_add;
    893 
    894 	memset(&fltr_add, 0, sizeof(fltr_add));
    895 	strncpy(fltr_add.iface.cdnr_ifname,
    896 		fltrinfo->clinfo->ifinfo->ifname+1, IFNAMSIZ);
    897 	fltr_add.cdnr_handle = fltrinfo->clinfo->handle;
    898 	fltr_add.filter = fltrinfo->fltr;
    899 
    900 	if (ioctl(cdnr_fd, CDNR_ADD_FILTER, &fltr_add) < 0)
    901 		return (QOPERR_SYSCALL);
    902 	fltrinfo->handle = fltr_add.filter_handle;
    903 	return (0);
    904 }
    905 
    906 static int
    907 cdnr_delete_filter(struct fltrinfo *fltrinfo)
    908 {
    909 	struct cdnr_delete_filter fltr_del;
    910 
    911 	memset(&fltr_del, 0, sizeof(fltr_del));
    912 	strncpy(fltr_del.iface.cdnr_ifname,
    913 		fltrinfo->clinfo->ifinfo->ifname+1, IFNAMSIZ);
    914 	fltr_del.filter_handle = fltrinfo->handle;
    915 
    916 	if (ioctl(cdnr_fd, CDNR_DEL_FILTER, &fltr_del) < 0)
    917 		return (QOPERR_SYSCALL);
    918 	return (0);
    919 }
    920 
    921 
    922 static int
    923 verify_tbprofile(struct tb_profile *profile, const char *cdnr_name)
    924 {
    925 	if (profile->depth < 1500) {
    926 		LOG(LOG_WARNING, 0,
    927 		    "warning: token bucket depth for %s is too small (%d)",
    928 		    cdnr_name, profile->depth);
    929 		return (-1);
    930 	}
    931 	return (0);
    932 }
    933 
    934