xref: /netbsd/usr.sbin/altq/libaltq/qop_cdnr.c (revision bf9ec67e)
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