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