1/*	$NetBSD: qop_cdnr.c,v 1.3 2001/08/16 07:48:13 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
58static struct ifinfo *cdnr_ifname2ifinfo(const char *);
59static int cdnr_attach(struct ifinfo *);
60static int cdnr_detach(struct ifinfo *);
61static int cdnr_enable(struct ifinfo *);
62static int cdnr_disable(struct ifinfo *);
63static int cdnr_add_class(struct classinfo *);
64static int cdnr_modify_class(struct classinfo *, void *);
65static int cdnr_delete_class(struct classinfo *);
66static int cdnr_add_filter(struct fltrinfo *);
67static int cdnr_delete_filter(struct fltrinfo *);
68static int verify_tbprofile(struct tb_profile *, const char *);
69
70#define CDNR_DEVICE	"/dev/altq/cdnr"
71
72static int cdnr_fd = -1;
73static int cdnr_refcount = 0;
74
75static 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
90u_long
91cdnr_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
105static struct ifinfo *
106cdnr_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
136int
137qcmd_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
161int
162qcmd_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
191int
192qcmd_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
226int
227qcmd_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
263int
264qcmd_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
296int
297qop_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
358int
359qop_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
402int
403qop_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
455int
456qop_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
503int
504qop_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
515int
516qop_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
574int
575qop_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
590int
591qop_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
648int
649qop_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 */
666static int
667cdnr_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
690static int
691cdnr_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
708static int
709cdnr_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
721static int
722cdnr_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
734static int
735cdnr_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
818static int
819cdnr_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
871static int
872cdnr_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
889static int
890cdnr_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
906static int
907cdnr_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
922static int
923verify_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