xref: /netbsd-src/usr.sbin/altq/libaltq/qop_cbq.c (revision 89ff1cb68ca175c833d375b99777073afd0d05eb)
1 /*	$NetBSD: qop_cbq.c,v 1.13 2024/12/24 08:35:28 ozaki-r Exp $	*/
2 /*	$KAME: qop_cbq.c,v 1.7 2002/05/31 06:03:35 kjc Exp $	*/
3 /*
4  * Copyright (c) Sun Microsystems, Inc. 1993-1998 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  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed by the SMCC Technology
20  *      Development Group at Sun Microsystems, Inc.
21  *
22  * 4. The name of the Sun Microsystems, Inc nor may not be used to endorse or
23  *      promote products derived from this software without specific prior
24  *      written permission.
25  *
26  * SUN MICROSYSTEMS DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE OR THE
27  * SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE.  The software is
28  * provided "as is" without express or implied warranty of any kind.
29  *
30  * These notices must be retained in any copies of any part of this software.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/socket.h>
35 #include <sys/sockio.h>
36 #include <sys/ioctl.h>
37 #include <sys/fcntl.h>
38 #include <net/if.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <stddef.h>
46 #include <string.h>
47 #include <ctype.h>
48 #include <errno.h>
49 #include <syslog.h>
50 #include <netdb.h>
51 #include <math.h>
52 
53 #include <altq/altq.h>
54 #include <altq/altq_cbq.h>
55 #include "altq_qop.h"
56 #include "qop_cbq.h"
57 
58 static int qcmd_cbq_add_ctl_filters(const char *, const char *);
59 
60 static int qop_cbq_enable_hook(struct ifinfo *);
61 static int qop_cbq_delete_class_hook(struct classinfo *);
62 
63 static int cbq_class_spec(struct ifinfo *, u_long, u_long, u_int, int,
64 			  uint64_t, u_int, u_int, u_int, u_int,
65 			  u_int, cbq_class_spec_t *);
66 
67 static int cbq_attach(struct ifinfo *);
68 static int cbq_detach(struct ifinfo *);
69 static int cbq_clear(struct ifinfo *);
70 static int cbq_enable(struct ifinfo *);
71 static int cbq_disable(struct ifinfo *);
72 static int cbq_add_class(struct classinfo *);
73 static int cbq_modify_class(struct classinfo *, void *);
74 static int cbq_delete_class(struct classinfo *);
75 static int cbq_add_filter(struct fltrinfo *);
76 static int cbq_delete_filter(struct fltrinfo *);
77 
78 #define CTL_PBANDWIDTH	2
79 #define NS_PER_MS	(1000000.0)
80 #define NS_PER_SEC	(NS_PER_MS*1000.0)
81 #define PS_PER_MS	(1000000000.0)
82 #define PS_PER_SEC	(PS_PER_MS*1000.0)
83 #define RM_FILTER_GAIN	5
84 
85 #define PSEC_TO_USEC(ps)	((ps) / 1000.0 / 1000.0)
86 
87 #define CBQ_DEVICE	"/dev/altq/cbq"
88 
89 static int cbq_fd = -1;
90 static int cbq_refcount = 0;
91 
92 static struct qdisc_ops cbq_qdisc = {
93 	ALTQT_CBQ,
94 	"cbq",
95 	cbq_attach,
96 	cbq_detach,
97 	cbq_clear,
98 	cbq_enable,
99 	cbq_disable,
100 	cbq_add_class,
101 	cbq_modify_class,
102 	cbq_delete_class,
103 	cbq_add_filter,
104 	cbq_delete_filter,
105 };
106 
107 #define EQUAL(s1, s2)	(strcmp((s1), (s2)) == 0)
108 
109 /*
110  * parser interface
111  */
112 int
113 cbq_interface_parser(const char *ifname, int argc, char **argv)
114 {
115 	uint64_t  	bandwidth = 100000000;	/* 100Mbps */
116 	u_int	tbrsize = 0;
117 	u_int	is_efficient = 0;
118 	u_int	is_wrr = 1;	/* weighted round-robin is default */
119 	bool	no_control = false;
120 	bool	no_tbr = false;
121 
122 	/*
123 	 * process options
124 	 */
125 	while (argc > 0) {
126 		if (EQUAL(*argv, "bandwidth")) {
127 			argc--; argv++;
128 			if (argc > 0)
129 				bandwidth = atobps(*argv);
130 		} else if (EQUAL(*argv, "tbrsize")) {
131 			argc--; argv++;
132 			if (argc > 0)
133 				tbrsize = atobytes(*argv);
134 		} else if (EQUAL(*argv, "efficient")) {
135 			is_efficient = 1;
136 		} else if (EQUAL(*argv, "cbq")) {
137 			/* just skip */
138 		} else if (EQUAL(*argv, "cbq-wrr")) {
139 			is_wrr = 1;
140 		} else if (EQUAL(*argv, "cbq-prr")) {
141 			is_wrr = 0;
142 		} else if (EQUAL(*argv, "no-tbr")) {
143 			no_tbr = true;
144 		} else if (EQUAL(*argv, "no-control")) {
145 			no_control = true;
146 		} else {
147 			LOG(LOG_ERR, 0, "Unknown keyword '%s'", *argv);
148 			return (0);
149 		}
150 		argc--; argv++;
151 	}
152 
153 	if (!no_tbr) {
154 		if (qcmd_tbr_register(ifname, bandwidth, tbrsize) != 0)
155 			return (0);
156 	}
157 
158 	if (qcmd_cbq_add_if(ifname, bandwidth,
159 			    is_wrr, is_efficient, no_control) != 0)
160 		return (0);
161 
162 	return (1);
163 }
164 
165 int
166 cbq_class_parser(const char *ifname, const char *class_name,
167 		 const char *parent_name, int argc, char **argv)
168 {
169 	const char	*borrow = NULL;
170 	u_int   pri = 1;
171 	u_int	pbandwidth = 0;
172 	uint64_t	bandwidth = 0;
173 	u_int	maxdelay = 0;		/* 0 means default */
174 	u_int	maxburst = 0;		/* 0 means default */
175 	u_int	minburst = 0;		/* 0 means default */
176 	u_int	av_pkt_size = 0;  /* 0 means use if mtu as default */
177 	u_int	max_pkt_size = 0; /* 0 means use if mtu as default */
178 	int	flags = 0;
179 	cbq_tos_t	admission_type = CBQ_QOS_NONE;
180 	int	error;
181 
182 	if (parent_name == NULL)
183 		flags |= CBQCLF_ROOTCLASS;
184 
185 	while (argc > 0) {
186 		if (EQUAL(*argv, "priority")) {
187 			argc--; argv++;
188 			if (argc > 0)
189 				pri = strtoul(*argv, NULL, 0);
190 		} else if (EQUAL(*argv, "default")) {
191 			flags |= CBQCLF_DEFCLASS;
192 		} else if (EQUAL(*argv, "control")) {
193 			flags |= CBQCLF_CTLCLASS;
194 		} else if (EQUAL(*argv, "admission")) {
195 			argc--; argv++;
196 			if (argc > 0) {
197 				if (EQUAL(*argv, "guaranteed"))
198 					admission_type = CBQ_QOS_GUARANTEED;
199 				else if (EQUAL(*argv, "predictive"))
200 					admission_type = CBQ_QOS_PREDICTIVE;
201 				else if (EQUAL(*argv, "cntlload"))
202 					admission_type = CBQ_QOS_CNTR_LOAD;
203 				else if (EQUAL(*argv, "cntldelay"))
204 					admission_type = CBQ_QOS_CNTR_DELAY;
205 				else if (EQUAL(*argv, "none"))
206 					admission_type = CBQ_QOS_NONE;
207 				else {
208 					LOG(LOG_ERR, 0,
209 					    "unknown admission type - %s, line %d",
210 					    *argv, line_no);
211 					return (0);
212 				}
213 			}
214 		} else if (EQUAL(*argv, "maxdelay")) {
215 			argc--; argv++;
216 			if (argc > 0)
217 				maxdelay = strtoul(*argv, NULL, 0);
218 		} else if (EQUAL(*argv, "borrow")) {
219 			borrow = parent_name;
220 #if 1
221 			/* support old style "borrow [parent]" */
222 			if (argc > 1 &&
223 			    EQUAL(*(argv + 1), parent_name)) {
224 				/* old style, skip borrow_name */
225 				argc--; argv++;
226 			}
227 #endif
228 		} else if (EQUAL(*argv, "pbandwidth")) {
229 			argc--; argv++;
230 			if (argc > 0)
231 				pbandwidth = strtoul(*argv, NULL, 0);
232 			if (pbandwidth > 100) {
233 				LOG(LOG_ERR, 0,
234 				    "bad pbandwidth %d for %s!",
235 				    pbandwidth, class_name);
236 				return (0);
237 			}
238 		} else if (EQUAL(*argv, "exactbandwidth")) {
239 			argc--; argv++;
240 			if (argc > 0)
241 				bandwidth = atobps(*argv);
242 		} else if (EQUAL(*argv, "maxburst")) {
243 			argc--; argv++;
244 			if (argc > 0)
245 				maxburst = strtoul(*argv, NULL, 0);
246 		} else if (EQUAL(*argv, "minburst")) {
247 			argc--; argv++;
248 			if (argc > 0)
249 				minburst = strtoul(*argv, NULL, 0);
250 		} else if (EQUAL(*argv, "packetsize")) {
251 			argc--; argv++;
252 			if (argc > 0)
253 				av_pkt_size = atobytes(*argv);
254 		} else if (EQUAL(*argv, "maxpacketsize")) {
255 			argc--; argv++;
256 			if (argc > 0)
257 				max_pkt_size = atobytes(*argv);
258 		} else if (EQUAL(*argv, "red")) {
259 			flags |= CBQCLF_RED;
260 		} else if (EQUAL(*argv, "ecn")) {
261 			flags |= CBQCLF_ECN;
262 		} else if (EQUAL(*argv, "flowvalve")) {
263 			flags |= CBQCLF_FLOWVALVE;
264 		} else if (EQUAL(*argv, "rio")) {
265 			flags |= CBQCLF_RIO;
266 		} else if (EQUAL(*argv, "cleardscp")) {
267 			flags |= CBQCLF_CLEARDSCP;
268 		} else {
269 			LOG(LOG_ERR, 0,
270 			    "Unknown keyword '%s' in %s, line %d",
271 			    *argv, altqconfigfile, line_no);
272 			return (0);
273 		}
274 
275 		argc--; argv++;
276 	}
277 
278 	if ((flags & (CBQCLF_RED|CBQCLF_RIO)) == (CBQCLF_RED|CBQCLF_RIO)) {
279 		LOG(LOG_ERR, 0,
280 		    "both red and rio defined on interface '%s'",
281 		    ifname);
282 		return (0);
283 	}
284 	if ((flags & (CBQCLF_ECN|CBQCLF_FLOWVALVE))
285 	    && (flags & (CBQCLF_RED|CBQCLF_RIO)) == 0)
286 		flags |= CBQCLF_RED;
287 
288 	if (strcmp("ctl_class", class_name) == 0)
289 		flags |= CBQCLF_CTLCLASS;
290 
291 	if (bandwidth == 0 && pbandwidth != 0) {
292 		struct ifinfo	*ifinfo;
293 
294 		if ((ifinfo = ifname2ifinfo(ifname)) != NULL)
295 			bandwidth = ifinfo->bandwidth / 100 * pbandwidth;
296 	}
297 
298 	error = qcmd_cbq_add_class(ifname, class_name, parent_name, borrow,
299 				   pri, bandwidth,
300 				   maxdelay, maxburst, minburst,
301 				   av_pkt_size, max_pkt_size,
302 				   admission_type, flags);
303 	if (error)
304 		return (0);
305 	return (1);
306 }
307 
308 /*
309  * qcmd api
310  */
311 int
312 qcmd_cbq_add_if(const char *ifname, uint64_t bandwidth, int is_wrr, int efficient,
313     bool no_control)
314 {
315 	int error;
316 
317 	error = qop_cbq_add_if(NULL, ifname, bandwidth, is_wrr, efficient,
318 	    no_control);
319 	if (error != 0)
320 		LOG(LOG_ERR, errno, "%s: can't add cbq on interface '%s'",
321 		    qoperror(error), ifname);
322 	return (error);
323 }
324 
325 int
326 qcmd_cbq_add_class(const char *ifname, const char *class_name,
327 		   const char *parent_name, const char *borrow_name,
328 		   u_int pri, uint64_t bandwidth,
329 		   u_int maxdelay, u_int maxburst, u_int minburst,
330 		   u_int av_pkt_size, u_int max_pkt_size,
331 		   int admission_type, int flags)
332 {
333 	struct ifinfo *ifinfo;
334 	struct cbq_ifinfo *cbq_ifinfo;
335 	struct classinfo *parent = NULL, *borrow = NULL;
336 	uint64_t ctl_bandwidth = 0;
337 	int	error = 0;
338 
339 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
340 		error = QOPERR_BADIF;
341 	cbq_ifinfo = ifinfo->private;
342 
343 	if (error == 0 && parent_name != NULL &&
344 	    (parent = clname2clinfo(ifinfo, parent_name)) == NULL)
345 		error = QOPERR_BADCLASS;
346 
347 	if (error == 0 && borrow_name != NULL &&
348 	    (borrow = clname2clinfo(ifinfo, borrow_name)) == NULL)
349 		error = QOPERR_BADCLASS;
350 
351 	if (flags & CBQCLF_DEFCLASS && !cbq_ifinfo->no_control) {
352 		/*
353 		 * if this is a default class and no ctl_class is defined,
354 		 * we will create a ctl_class.
355 		 */
356 		if (cbq_ifinfo->ctl_class == NULL) {
357 			/* reserve bandwidth for ctl_class */
358 			ctl_bandwidth =
359 				ifinfo->bandwidth / 100 * CTL_PBANDWIDTH;
360 			if (bandwidth <= ctl_bandwidth)
361 				LOG(LOG_ERR, 0,
362 				    "bandwidth for default class too small!");
363 			bandwidth -= ctl_bandwidth;
364 		}
365 	}
366 
367 	if (error == 0)
368 		error = qop_cbq_add_class(NULL, class_name, ifinfo, parent,
369 					  borrow, pri, bandwidth,
370 					  maxdelay, maxburst, minburst,
371 					  av_pkt_size, max_pkt_size,
372 					  admission_type, flags);
373 	if (error != 0)
374 		LOG(LOG_ERR, errno,
375 		    "cbq: %s: can't add class '%s' on interface '%s'",
376 		    qoperror(error), class_name, ifname);
377 
378 	if (ctl_bandwidth != 0) {
379 		/*
380 		 * If were adding the default traffic class and
381 		 * no ctl_class is defined, also add the ctl traffic class.
382 		 * This is for RSVP and IGMP packets.
383 		 */
384 		if (qcmd_cbq_add_class(ifname, "ctl_class", parent_name,
385 		      borrow_name, 6, ctl_bandwidth,
386 		      maxdelay, maxburst, minburst, av_pkt_size,
387 		      max_pkt_size, admission_type, CBQCLF_CTLCLASS) != 0) {
388 			LOG(LOG_ERR, errno, "can't create ctl_class!");
389 			return (QOPERR_CLASS);
390 		}
391 	}
392 
393 	/*
394 	 * if this is a ctl class, add the default filters for backward
395 	 * compatibility
396 	 */
397 	if (flags & CBQCLF_CTLCLASS)
398 		qcmd_cbq_add_ctl_filters(ifname, class_name);
399 
400 	return (error);
401 }
402 
403 int
404 qcmd_cbq_modify_class(const char *ifname, const char *class_name,
405 		      u_int pri, uint64_t bandwidth,
406 		      u_int maxdelay, u_int maxburst, u_int minburst,
407 		      u_int av_pkt_size, u_int max_pkt_size, int flags)
408 {
409 	struct ifinfo *ifinfo;
410 	struct classinfo *clinfo;
411 
412 	if ((ifinfo = ifname2ifinfo(ifname)) == NULL)
413 		return (QOPERR_BADIF);
414 
415 	if ((clinfo = clname2clinfo(ifinfo, class_name)) == NULL)
416 		return (QOPERR_BADCLASS);
417 
418 	return qop_cbq_modify_class(clinfo, pri, bandwidth,
419 				    maxdelay, maxburst, minburst,
420 				    av_pkt_size, max_pkt_size, flags);
421 }
422 
423 /*
424  * add the default filters for ctl_class (for backward compatibility).
425  */
426 #ifndef IPPROTO_RSVP
427 #define IPPROTO_RSVP		46
428 #endif
429 
430 static int
431 qcmd_cbq_add_ctl_filters(const char *ifname, const char *clname)
432 {
433 	struct flow_filter	sfilt;
434 	u_int8_t ctl_protos[3] = {IPPROTO_ICMP, IPPROTO_IGMP, IPPROTO_RSVP};
435 #ifdef INET6
436 	struct flow_filter6	sfilt6;
437 	u_int8_t ctl6_protos[3] = {IPPROTO_ICMPV6, IPPROTO_IGMP, IPPROTO_RSVP};
438 #endif
439 	int error;
440 	size_t i;
441 
442 	for (i = 0; i < sizeof(ctl_protos); i++) {
443 		memset(&sfilt, 0, sizeof(sfilt));
444 		sfilt.ff_flow.fi_family = AF_INET;
445 		sfilt.ff_flow.fi_proto = ctl_protos[i];
446 
447 		filter_dontwarn = 1;		/* XXX */
448 		error = qcmd_add_filter(ifname, clname, NULL, &sfilt);
449 		filter_dontwarn = 0;		/* XXX */
450 		if (error) {
451 			LOG(LOG_ERR, 0,
452 			    "can't add ctl class filter on interface '%s'",
453 			    ifname);
454 			return (error);
455 		}
456 	}
457 
458 #ifdef INET6
459 	for (i = 0; i < sizeof(ctl6_protos); i++) {
460 		memset(&sfilt6, 0, sizeof(sfilt6));
461 		sfilt6.ff_flow6.fi6_family = AF_INET6;
462 		sfilt6.ff_flow6.fi6_proto = ctl6_protos[i];
463 
464 		error = qcmd_add_filter(ifname, clname, NULL,
465 					(struct flow_filter *)&sfilt6);
466 		if (error) {
467 			LOG(LOG_WARNING, 0,
468 			    "can't add ctl class IPv6 filter on interface '%s'",
469 			    ifname);
470 			return (error);
471 		}
472 	}
473 #endif
474 	return (0);
475 }
476 
477 /*
478  * qop api
479  */
480 int
481 qop_cbq_add_if(struct ifinfo **rp, const char *ifname,
482 	       uint64_t bandwidth, int is_wrr, int efficient, bool no_control)
483 {
484 	struct ifinfo *ifinfo = NULL;
485 	struct cbq_ifinfo *cbq_ifinfo = NULL;
486 	int error;
487 
488 	if ((cbq_ifinfo = calloc(1, sizeof(*cbq_ifinfo))) == NULL)
489 		return (QOPERR_NOMEM);
490 
491 	cbq_ifinfo->psPerByte =
492 		(1.0 / (double)bandwidth) * PS_PER_SEC * 8;
493 	cbq_ifinfo->is_wrr = is_wrr;
494 	cbq_ifinfo->is_efficient = efficient;
495 	cbq_ifinfo->no_control = no_control;
496 
497 	error = qop_add_if(&ifinfo, ifname, bandwidth,
498 			   &cbq_qdisc, cbq_ifinfo);
499 	if (error != 0)
500 		goto err_ret;
501 
502 	/* set enable hook */
503 	ifinfo->enable_hook = qop_cbq_enable_hook;
504 
505 	if (rp != NULL)
506 		*rp = ifinfo;
507 	return (0);
508 
509  err_ret:
510 	if (cbq_ifinfo != NULL) {
511 		free(cbq_ifinfo);
512 		if (ifinfo != NULL)
513 			ifinfo->private = NULL;
514 	}
515 	return (error);
516 }
517 
518 #define is_sc_null(sc)	(((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
519 
520 int
521 qop_cbq_add_class(struct classinfo **rp, const char *class_name,
522 		  struct ifinfo *ifinfo, struct classinfo *parent,
523 		  struct classinfo *borrow, u_int pri, uint64_t bandwidth,
524 		  u_int maxdelay, u_int maxburst, u_int minburst,
525 		  u_int av_pkt_size, u_int max_pkt_size,
526 		  int admission_type, int flags)
527 {
528 	struct classinfo *clinfo;
529 	struct cbq_ifinfo *cbq_ifinfo;
530 	struct cbq_classinfo *cbq_clinfo, *parent_clinfo;
531 	u_int parent_handle, borrow_handle;
532 	int error;
533 
534 	cbq_ifinfo = ifinfo->private;
535 
536 	if (parent == NULL) {
537 		if (cbq_ifinfo->root_class != NULL)
538 			return (QOPERR_CLASS_INVAL);
539 		flags |= CBQCLF_ROOTCLASS;
540 	}
541 	if ((flags & CBQCLF_DEFCLASS) && cbq_ifinfo->default_class != NULL)
542 		return (QOPERR_CLASS_INVAL);
543 	if ((flags & CBQCLF_CTLCLASS) && cbq_ifinfo->ctl_class != NULL)
544 		return (QOPERR_CLASS_INVAL);
545 
546 	/* admission control */
547 	if (parent != NULL) {
548 		parent_clinfo = parent->private;
549 		if (bandwidth >
550 		    parent_clinfo->bandwidth - parent_clinfo->allocated) {
551 #ifdef ALLOW_OVERCOMMIT
552 			LOG(LOG_WARNING, 0,
553 			    "bandwidth overcommitted %uK requested but only %dK available (%uK already allocated)",
554 			    bandwidth / 1000,
555 			    ((int)parent_clinfo->bandwidth -
556 			     parent_clinfo->allocated) / 1000,
557 			    parent_clinfo->allocated / 1000);
558 #else /* !ALLOW_OVERCOMMIT */
559 			LOG(LOG_ERR, 0,
560 			    "cbq admission failed! %uK requested but only %uK available (%uK already allocated)",
561 			    bandwidth / 1000,
562 			    (parent_clinfo->bandwidth -
563 			     parent_clinfo->allocated) / 1000,
564 			    parent_clinfo->allocated / 1000);
565 			return (QOPERR_ADMISSION_NOBW);
566 #endif /* !ALLOW_OVERCOMMIT */
567 		}
568 	}
569 
570 	if ((cbq_clinfo = calloc(1, sizeof(*cbq_clinfo))) == NULL)
571 		return (QOPERR_NOMEM);
572 
573 	cbq_clinfo->bandwidth = bandwidth;
574 	cbq_clinfo->allocated = 0;
575 
576 	/* if average packet size isn't specified, set if mtu. */
577 	if (av_pkt_size == 0) {	/* use default */
578 		av_pkt_size = ifinfo->ifmtu;
579 		if (av_pkt_size > MCLBYTES)	/* do what TCP does */
580 			av_pkt_size &= ~MCLBYTES;
581 	} else if (av_pkt_size > ifinfo->ifmtu)
582 		av_pkt_size = ifinfo->ifmtu;
583 
584 	if (max_pkt_size == 0)	/* use default */
585 		max_pkt_size = ifinfo->ifmtu;
586 	else if (max_pkt_size > ifinfo->ifmtu)
587 		max_pkt_size = ifinfo->ifmtu;
588 
589 	cbq_clinfo->maxdelay = maxdelay;
590 	cbq_clinfo->maxburst = maxburst;
591 	cbq_clinfo->minburst = minburst;
592 	cbq_clinfo->av_pkt_size = av_pkt_size;
593 	cbq_clinfo->max_pkt_size = max_pkt_size;
594 
595 	parent_handle = parent != NULL ? parent->handle : NULL_CLASS_HANDLE;
596 	borrow_handle = borrow != NULL ? borrow->handle : NULL_CLASS_HANDLE;
597 
598 	if (cbq_class_spec(ifinfo, parent_handle, borrow_handle, pri, flags,
599 			   bandwidth, maxdelay, maxburst, minburst,
600 			   av_pkt_size, max_pkt_size,
601 			   &cbq_clinfo->class_spec) < 0) {
602 		error = QOPERR_INVAL;
603 		goto err_ret;
604 	}
605 
606 	clinfo = NULL;
607 	error = qop_add_class(&clinfo, class_name, ifinfo, parent, cbq_clinfo);
608 	if (error != 0)
609 		goto err_ret;
610 
611 	/* set delete hook */
612 	clinfo->delete_hook = qop_cbq_delete_class_hook;
613 
614 	if (parent == NULL)
615 		cbq_ifinfo->root_class = clinfo;
616 	else {
617 		parent_clinfo = parent->private;
618 		parent_clinfo->allocated += bandwidth;
619 	}
620 	if (flags & CBQCLF_DEFCLASS)
621 		cbq_ifinfo->default_class = clinfo;
622 	if (flags & CBQCLF_CTLCLASS)
623 		cbq_ifinfo->ctl_class = clinfo;
624 
625 	switch (admission_type) {
626 	case CBQ_QOS_CNTR_LOAD:
627 	case CBQ_QOS_GUARANTEED:
628 	case CBQ_QOS_PREDICTIVE:
629 	case CBQ_QOS_CNTR_DELAY:
630 		if (ifinfo->resv_class != NULL) {
631 			LOG(LOG_ERR, 0,
632 			    "%s: duplicate resv meta class", class_name);
633 			return (QOPERR_CLASS);
634 		}
635 		ifinfo->resv_class = clinfo;
636 	}
637 
638 	if (rp != NULL)
639 		*rp = clinfo;
640 	return (0);
641 
642  err_ret:
643 	if (cbq_clinfo != NULL) {
644 		free(cbq_clinfo);
645 		if (clinfo != NULL)
646 		    clinfo->private = NULL;
647 	}
648 	return (error);
649 }
650 
651 /*
652  * this is called from qop_delete_class() before a class is destroyed
653  * for discipline specific cleanup.
654  */
655 static int
656 qop_cbq_delete_class_hook(struct classinfo *clinfo)
657 {
658 	struct cbq_classinfo *cbq_clinfo, *parent_clinfo;
659 
660 	/* cancel admission control */
661 	if (clinfo->parent != NULL) {
662 		cbq_clinfo = clinfo->private;
663 		parent_clinfo = clinfo->parent->private;
664 
665 		parent_clinfo->allocated -= cbq_clinfo->bandwidth;
666 	}
667 	return (0);
668 }
669 
670 int
671 qop_cbq_modify_class(struct classinfo *clinfo, u_int pri, uint64_t bandwidth,
672 		     u_int maxdelay, u_int maxburst, u_int minburst,
673 		     u_int av_pkt_size, u_int max_pkt_size, int flags)
674 {
675 	struct ifinfo *ifinfo;
676 	struct cbq_classinfo *cbq_clinfo, *parent_clinfo;
677 	u_int	parent_handle, borrow_handle;
678 	uint64_t	old_bandwidth;
679 	int	error;
680 
681 	ifinfo = clinfo->ifinfo;
682 	cbq_clinfo = clinfo->private;
683 
684 	/* admission control */
685 	old_bandwidth = cbq_clinfo->bandwidth;
686 	if (clinfo->parent != NULL) {
687 		parent_clinfo = clinfo->parent->private;
688 		if (bandwidth > old_bandwidth) {
689 			/* increase bandwidth */
690 			if (bandwidth - old_bandwidth >
691 			    parent_clinfo->bandwidth
692 			    - parent_clinfo->allocated)
693 				return (QOPERR_ADMISSION_NOBW);
694 		} else if (bandwidth < old_bandwidth) {
695 			/* decrease bandwidth */
696 			if (bandwidth < cbq_clinfo->allocated)
697 				return (QOPERR_ADMISSION);
698 		}
699 	}
700 
701 	/* if average packet size isn't specified, set if mtu. */
702 	if (av_pkt_size == 0) {	/* use default */
703 		av_pkt_size = ifinfo->ifmtu;
704 		if (av_pkt_size > MCLBYTES)	/* do what TCP does */
705 			av_pkt_size &= ~MCLBYTES;
706 	} else if (av_pkt_size > ifinfo->ifmtu)
707 		av_pkt_size = ifinfo->ifmtu;
708 
709 	if (max_pkt_size == 0)	/* use default */
710 		max_pkt_size = ifinfo->ifmtu;
711 	else if (max_pkt_size > ifinfo->ifmtu)
712 		max_pkt_size = ifinfo->ifmtu;
713 
714 	cbq_clinfo->maxdelay = maxdelay;
715 	cbq_clinfo->maxburst = maxburst;
716 	cbq_clinfo->minburst = minburst;
717 	cbq_clinfo->av_pkt_size = av_pkt_size;
718 	cbq_clinfo->max_pkt_size = max_pkt_size;
719 
720 	parent_handle = cbq_clinfo->class_spec.parent_class_handle;
721 	borrow_handle = cbq_clinfo->class_spec.borrow_class_handle;
722 
723 	if (cbq_class_spec(ifinfo, parent_handle, borrow_handle, pri, flags,
724 			   bandwidth, maxdelay, maxburst, minburst,
725 			   av_pkt_size, max_pkt_size,
726 			   &cbq_clinfo->class_spec) < 0) {
727 		return (QOPERR_INVAL);
728 	}
729 
730 	error = qop_modify_class(clinfo, NULL);
731 
732 	if (error == 0) {
733 		if (clinfo->parent != NULL) {
734 			parent_clinfo = clinfo->parent->private;
735 			parent_clinfo->allocated -= old_bandwidth;
736 			parent_clinfo->allocated += bandwidth;
737 		}
738 		cbq_clinfo->bandwidth = bandwidth;
739 	}
740 	return (error);
741 }
742 
743 /*
744  * sanity check at enabling cbq:
745  *  there must one root class and one default class for an interface
746  */
747 static int
748 qop_cbq_enable_hook(struct ifinfo *ifinfo)
749 {
750 	struct cbq_ifinfo *cbq_ifinfo;
751 
752 	cbq_ifinfo = ifinfo->private;
753 	if (cbq_ifinfo->root_class == NULL) {
754 		LOG(LOG_ERR, 0, "cbq: no root class on interface %s!",
755 		    ifinfo->ifname);
756 		return (QOPERR_CLASS);
757 	}
758 	if (cbq_ifinfo->default_class == NULL) {
759 		LOG(LOG_ERR, 0, "cbq: no default class on interface %s!",
760 		    ifinfo->ifname);
761 		return (QOPERR_CLASS);
762 	}
763 	return (0);
764 }
765 
766 static int
767 cbq_class_spec(struct ifinfo *ifinfo, u_long parent_class,
768 	       u_long borrow_class, u_int pri, int flags,
769 	       uint64_t bandwidth, u_int maxdelay, u_int maxburst,
770 	       u_int minburst, u_int av_pkt_size, u_int max_pkt_size,
771 	       cbq_class_spec_t *cl_spec)
772 {
773 	struct cbq_ifinfo *cbq_ifinfo = ifinfo->private;
774 	double          maxq, maxidle_s, maxidle, minidle,
775 			lofftime, psPerByte, ptime, cptime;
776 	double		z = (double)(1 << RM_FILTER_GAIN);
777 	double          g = (1.0 - 1.0 / z);
778 	double          f;
779  	double		gton;
780  	double		gtom;
781 
782  	/* Compute other class parameters */
783 	if (bandwidth == 0)
784 		f = 0.0001;	/* small enough? */
785 	else
786 		f = ((double) bandwidth / (double) ifinfo->bandwidth);
787 
788 	if (av_pkt_size == 0) {	/* use default */
789 		av_pkt_size = ifinfo->ifmtu;
790 		if (av_pkt_size > MCLBYTES)	/* do what TCP does */
791 			av_pkt_size &= ~MCLBYTES;
792 	} else if (av_pkt_size > ifinfo->ifmtu)
793 		av_pkt_size = ifinfo->ifmtu;
794 	if (max_pkt_size == 0)	/* use default */
795 		max_pkt_size = ifinfo->ifmtu;
796 	else if (max_pkt_size > ifinfo->ifmtu)
797 		max_pkt_size = ifinfo->ifmtu;
798 
799         psPerByte = cbq_ifinfo->psPerByte / f;
800 	ptime = (double) av_pkt_size * (double)cbq_ifinfo->psPerByte;
801 	cptime = ptime * (1.0 - f) / f;
802 
803 	if (maxburst == 0) {  /* use default */
804 		if (cptime > 10.0 * PS_PER_MS)
805 			maxburst = 4;
806 		else
807 			maxburst = 16;
808 	}
809 	if (minburst == 0)  /* use default */
810 		minburst = 2;
811 	if (minburst > maxburst)
812 		minburst = maxburst;
813 
814 	if (IsDebug(DEBUG_ALTQ)) {
815 		int packet_time;
816 		LOG(LOG_DEBUG, 0,
817 		    "cbq_flowspec: maxburst=%d,minburst=%d,pkt_size=%d",
818 		    maxburst, minburst, av_pkt_size);
819 		LOG(LOG_DEBUG, 0,
820 		    "  psPerByte=%.2f ps, link's psPerByte=%.2f, f=%.3f",
821 		    psPerByte, cbq_ifinfo->psPerByte, f);
822 		packet_time = av_pkt_size * (int)PSEC_TO_USEC(psPerByte);
823 		LOG(LOG_DEBUG, 0,
824 		    "  packet time=%d [us]\n", packet_time);
825 		if (maxburst * packet_time < 20000) {
826 			LOG(LOG_WARNING, 0,
827 			  "warning: maxburst smaller than timer granularity!");
828 			LOG(LOG_WARNING, 0,
829 			    "         maxburst=%d, packet_time=%d [us]",
830 			    maxburst, packet_time);
831 		}
832 	}
833  	gton = pow(g, (double)maxburst);
834  	gtom = pow(g, (double)(minburst-1));
835 	maxidle = ((1.0 / f - 1.0) * ((1.0 - gton) / gton));
836 	maxidle_s = (1.0 - g);
837 	if (maxidle > maxidle_s)
838 		maxidle = ptime * maxidle;
839 	else
840 		maxidle = ptime * maxidle_s;
841 	if (IsDebug(DEBUG_ALTQ))
842 		LOG(LOG_DEBUG, 0, "  maxidle=%.2f us", PSEC_TO_USEC(maxidle));
843 	if (minburst)
844 		lofftime = cptime * (1.0 + 1.0/(1.0 - g) * (1.0 - gtom) / gtom);
845 	else
846 		lofftime = cptime;
847 	minidle = -((double)max_pkt_size * (double)psPerByte);
848 	if (IsDebug(DEBUG_ALTQ))
849 		LOG(LOG_DEBUG, 0, "  lofftime=%.2f us minidle=%.2f us",
850 		    PSEC_TO_USEC(lofftime), PSEC_TO_USEC(minidle));
851 
852 	maxidle = ((maxidle * 8.0) / psPerByte) * pow(2, RM_FILTER_GAIN);
853 #if 1 /* ALTQ */
854 	/* also scale lofftime and minidle */
855 	lofftime = (lofftime * 8.0) / psPerByte * pow(2, RM_FILTER_GAIN);
856 	minidle = ((minidle * 8.0) / psPerByte) * pow(2, RM_FILTER_GAIN);
857 #endif
858 	maxidle = maxidle / 1000.0;
859 	lofftime = lofftime / 1000.0;
860 	minidle = minidle / 1000.0;
861 	/* adjust queue size when maxdelay is specified.
862 	   queue size should be relative to its share */
863 	if (maxdelay == 0) {
864 		if (flags & (CBQCLF_RED|CBQCLF_RIO))
865 			maxq = 60.0;
866 		else
867 			maxq = 30.0;
868 	} else {
869 		maxq = ((double) maxdelay * PS_PER_MS) / (psPerByte * av_pkt_size);
870 		if (maxq < 4) {
871 			LOG(LOG_WARNING, 0,
872 			    "warning: maxq (%d) is too small. set to %d",
873 			    (int)maxq, 4);
874 			maxq = 4;
875 		}
876 	}
877 	if (bandwidth == 0 && borrow_class == NULL_CLASS_HANDLE)
878 		/* filter out this class by setting queue size to zero */
879 		maxq = 0;
880 	if (IsDebug(DEBUG_ALTQ)) {
881 		if ((u_int)maxq < maxburst)
882 			LOG(LOG_WARNING, 0,
883 			   "warning: maxq (%d) is smaller than maxburst(%d)",
884 			    (int)maxq, maxburst);
885 		else if (maxq > 100.0)
886 			LOG(LOG_WARNING, 0,
887 			    "warning: maxq %d too large\n", (int)maxq);
888 		LOG(LOG_DEBUG, 0, "  maxq=%d", (int)maxq);
889 	}
890 
891 	if (parent_class == NULL_CLASS_HANDLE) {
892 		if ((flags & CBQCLF_ROOTCLASS) == 0)
893 			flags |= CBQCLF_ROOTCLASS;
894 		if (cbq_ifinfo->is_wrr)
895 			flags |= CBQCLF_WRR;
896 		if (cbq_ifinfo->is_efficient)
897 			flags |= CBQCLF_EFFICIENT;
898 	}
899 
900 	memset((void *)cl_spec, 0, sizeof(cbq_class_spec_t));
901 	cl_spec->priority = pri;
902 	cl_spec->pico_sec_per_byte = (u_long) psPerByte;
903 	cl_spec->maxq = (u_int) maxq;
904 	cl_spec->maxidle = (u_int) fabs(maxidle);
905 	cl_spec->minidle = (int)minidle;
906 	cl_spec->offtime = (u_int) fabs(lofftime);
907 
908 	cl_spec->parent_class_handle = parent_class;
909 	cl_spec->borrow_class_handle = borrow_class;
910 
911 	cl_spec->pktsize = av_pkt_size;
912 	cl_spec->flags = flags;
913 
914 	return (0);
915 }
916 
917 
918 /*
919  *  system call interfaces for qdisc_ops
920  */
921 static int
922 cbq_attach(struct ifinfo *ifinfo)
923 {
924 	struct cbq_interface iface;
925 
926 	if (cbq_fd < 0 &&
927 	    (cbq_fd = open(CBQ_DEVICE, O_RDWR)) < 0 &&
928 	    (cbq_fd = open_module(CBQ_DEVICE, O_RDWR)) < 0) {
929 		LOG(LOG_ERR, errno, "CBQ open");
930 		return (QOPERR_SYSCALL);
931 	}
932 
933 	cbq_refcount++;
934 	memset(&iface, 0, sizeof(iface));
935 	strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
936 
937 	if (ioctl(cbq_fd, CBQ_IF_ATTACH, &iface) < 0)
938 		return (QOPERR_SYSCALL);
939 	return (0);
940 }
941 
942 static int
943 cbq_detach(struct ifinfo *ifinfo)
944 {
945 	struct cbq_interface iface;
946 
947 	memset(&iface, 0, sizeof(iface));
948 	strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
949 
950 	if (ioctl(cbq_fd, CBQ_IF_DETACH, &iface) < 0)
951 		return (QOPERR_SYSCALL);
952 
953 	if (--cbq_refcount == 0) {
954 		close(cbq_fd);
955 		cbq_fd = -1;
956 	}
957 	return (0);
958 }
959 
960 static int
961 cbq_clear(struct ifinfo *ifinfo)
962 {
963 	struct cbq_interface iface;
964 
965 	memset(&iface, 0, sizeof(iface));
966 	strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
967 
968 	if (ioctl(cbq_fd, CBQ_CLEAR_HIERARCHY, &iface) < 0)
969 		return (QOPERR_SYSCALL);
970 	return (0);
971 }
972 
973 static int
974 cbq_enable(struct ifinfo *ifinfo)
975 {
976 	struct cbq_interface iface;
977 
978 	memset(&iface, 0, sizeof(iface));
979 	strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
980 
981 	if (ioctl(cbq_fd, CBQ_ENABLE, &iface) < 0)
982 		return (QOPERR_SYSCALL);
983 	return (0);
984 }
985 
986 static int
987 cbq_disable(struct ifinfo *ifinfo)
988 {
989 	struct cbq_interface iface;
990 
991 	memset(&iface, 0, sizeof(iface));
992 	strncpy(iface.cbq_ifacename, ifinfo->ifname, IFNAMSIZ);
993 
994 	if (ioctl(cbq_fd, CBQ_DISABLE, &iface) < 0)
995 		return (QOPERR_SYSCALL);
996 	return (0);
997 }
998 
999 static int
1000 cbq_add_class(struct classinfo *clinfo)
1001 {
1002 	struct cbq_add_class class_add;
1003 	struct cbq_classinfo *cbq_clinfo;
1004 
1005 	cbq_clinfo = clinfo->private;
1006 
1007 	memset(&class_add, 0, sizeof(class_add));
1008 	strncpy(class_add.cbq_iface.cbq_ifacename,
1009 		clinfo->ifinfo->ifname, IFNAMSIZ);
1010 
1011 	class_add.cbq_class = cbq_clinfo->class_spec;
1012 
1013 	if (ioctl(cbq_fd, CBQ_ADD_CLASS, &class_add) < 0)
1014 		return (QOPERR_SYSCALL);
1015 
1016 	clinfo->handle = class_add.cbq_class_handle;
1017 	return (0);
1018 }
1019 
1020 static int
1021 cbq_modify_class(struct classinfo *clinfo, void *arg)
1022 {
1023 	struct cbq_modify_class class_mod;
1024 	struct cbq_classinfo *cbq_clinfo;
1025 
1026 	cbq_clinfo = clinfo->private;
1027 
1028 	memset(&class_mod, 0, sizeof(class_mod));
1029 	strncpy(class_mod.cbq_iface.cbq_ifacename,
1030 		clinfo->ifinfo->ifname, IFNAMSIZ);
1031 	class_mod.cbq_class_handle = clinfo->handle;
1032 	class_mod.cbq_class = cbq_clinfo->class_spec;
1033 
1034 	if (ioctl(cbq_fd, CBQ_MODIFY_CLASS, &class_mod) < 0)
1035 		return (QOPERR_SYSCALL);
1036 	return (0);
1037 }
1038 
1039 static int
1040 cbq_delete_class(struct classinfo *clinfo)
1041 {
1042 	struct cbq_delete_class class_delete;
1043 
1044 	memset(&class_delete, 0, sizeof(class_delete));
1045 	strncpy(class_delete.cbq_iface.cbq_ifacename,
1046 		clinfo->ifinfo->ifname, IFNAMSIZ);
1047 	class_delete.cbq_class_handle = clinfo->handle;
1048 
1049 	if (ioctl(cbq_fd, CBQ_DEL_CLASS, &class_delete) < 0)
1050 		return (QOPERR_SYSCALL);
1051 	return (0);
1052 }
1053 
1054 static int
1055 cbq_add_filter(struct fltrinfo *fltrinfo)
1056 {
1057 	struct cbq_add_filter fltr_add;
1058 
1059 	memset(&fltr_add, 0, sizeof(fltr_add));
1060 	strncpy(fltr_add.cbq_iface.cbq_ifacename,
1061 		fltrinfo->clinfo->ifinfo->ifname, IFNAMSIZ);
1062 	fltr_add.cbq_class_handle = fltrinfo->clinfo->handle;
1063 	fltr_add.cbq_filter = fltrinfo->fltr;
1064 
1065 	if (ioctl(cbq_fd, CBQ_ADD_FILTER, &fltr_add) < 0)
1066 		return (QOPERR_SYSCALL);
1067 	fltrinfo->handle = fltr_add.cbq_filter_handle;
1068 	return (0);
1069 }
1070 
1071 static int
1072 cbq_delete_filter(struct fltrinfo *fltrinfo)
1073 {
1074 	struct cbq_delete_filter fltr_del;
1075 
1076 	memset(&fltr_del, 0, sizeof(fltr_del));
1077 	strncpy(fltr_del.cbq_iface.cbq_ifacename,
1078 		fltrinfo->clinfo->ifinfo->ifname, IFNAMSIZ);
1079 	fltr_del.cbq_filter_handle = fltrinfo->handle;
1080 
1081 	if (ioctl(cbq_fd, CBQ_DEL_FILTER, &fltr_del) < 0)
1082 		return (QOPERR_SYSCALL);
1083 	return (0);
1084 }
1085 
1086 
1087