1 /* $NetBSD: qop_cbq.c,v 1.8 2009/04/15 00:13:51 lukem Exp $ */
2 /* $KAME: qop_cbq.c,v 1.7 2002/05/31 06:03:35 kjc Exp $ */
4 * Copyright (c) Sun Microsystems, Inc. 1993-1998 All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
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.
30 * These notices must be retained in any copies of any part of this software.
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>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
53 #include <altq/altq.h>
54 #include <altq/altq_cbq.h>
58 static int qcmd_cbq_add_ctl_filters(const char *, const char *);
60 static int qop_cbq_enable_hook(struct ifinfo
*);
61 static int qop_cbq_delete_class_hook(struct classinfo
*);
63 static int cbq_class_spec(struct ifinfo
*, u_long
, u_long
, u_int
, int,
64 u_int
, u_int
, u_int
, u_int
, u_int
,
65 u_int
, cbq_class_spec_t
*);
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
*);
78 #define CTL_PBANDWIDTH 2
79 #define NS_PER_MS (1000000.0)
80 #define NS_PER_SEC (NS_PER_MS*1000.0)
81 #define RM_FILTER_GAIN 5
83 #define CBQ_DEVICE "/dev/altq/cbq"
85 static int cbq_fd
= -1;
86 static int cbq_refcount
= 0;
88 static struct qdisc_ops cbq_qdisc
= {
103 #define EQUAL(s1, s2) (strcmp((s1), (s2)) == 0)
109 cbq_interface_parser(const char *ifname
, int argc
, char **argv
)
111 u_int bandwidth
= 100000000; /* 100Mbps */
113 u_int is_efficient
= 0;
114 u_int is_wrr
= 1; /* weighted round-robin is default */
120 if (EQUAL(*argv
, "bandwidth")) {
123 bandwidth
= atobps(*argv
);
124 } else if (EQUAL(*argv
, "tbrsize")) {
127 tbrsize
= atobytes(*argv
);
128 } else if (EQUAL(*argv
, "efficient")) {
130 } else if (EQUAL(*argv
, "cbq")) {
132 } else if (EQUAL(*argv
, "cbq-wrr")) {
134 } else if (EQUAL(*argv
, "cbq-prr")) {
137 LOG(LOG_ERR
, 0, "Unknown keyword '%s'", *argv
);
143 if (qcmd_tbr_register(ifname
, bandwidth
, tbrsize
) != 0)
146 if (qcmd_cbq_add_if(ifname
, bandwidth
,
147 is_wrr
, is_efficient
) != 0)
153 cbq_class_parser(const char *ifname
, const char *class_name
,
154 const char *parent_name
, int argc
, char **argv
)
156 const char *borrow
= NULL
;
158 u_int pbandwidth
= 0;
160 u_int maxdelay
= 0; /* 0 means default */
161 u_int maxburst
= 0; /* 0 means default */
162 u_int minburst
= 0; /* 0 means default */
163 u_int av_pkt_size
= 0; /* 0 means use if mtu as default */
164 u_int max_pkt_size
= 0; /* 0 means use if mtu as default */
166 cbq_tos_t admission_type
= CBQ_QOS_NONE
;
169 if (parent_name
== NULL
)
170 flags
|= CBQCLF_ROOTCLASS
;
173 if (EQUAL(*argv
, "priority")) {
176 pri
= strtoul(*argv
, NULL
, 0);
177 } else if (EQUAL(*argv
, "default")) {
178 flags
|= CBQCLF_DEFCLASS
;
179 } else if (EQUAL(*argv
, "control")) {
180 flags
|= CBQCLF_CTLCLASS
;
181 } else if (EQUAL(*argv
, "admission")) {
184 if (EQUAL(*argv
, "guaranteed"))
185 admission_type
= CBQ_QOS_GUARANTEED
;
186 else if (EQUAL(*argv
, "predictive"))
187 admission_type
= CBQ_QOS_PREDICTIVE
;
188 else if (EQUAL(*argv
, "cntlload"))
189 admission_type
= CBQ_QOS_CNTR_LOAD
;
190 else if (EQUAL(*argv
, "cntldelay"))
191 admission_type
= CBQ_QOS_CNTR_DELAY
;
192 else if (EQUAL(*argv
, "none"))
193 admission_type
= CBQ_QOS_NONE
;
196 "unknown admission type - %s, line %d",
201 } else if (EQUAL(*argv
, "maxdelay")) {
204 maxdelay
= strtoul(*argv
, NULL
, 0);
205 } else if (EQUAL(*argv
, "borrow")) {
206 borrow
= parent_name
;
208 /* support old style "borrow [parent]" */
210 EQUAL(*(argv
+ 1), parent_name
)) {
211 /* old style, skip borrow_name */
215 } else if (EQUAL(*argv
, "pbandwidth")) {
218 pbandwidth
= strtoul(*argv
, NULL
, 0);
219 if (pbandwidth
> 100) {
221 "bad pbandwidth %d for %s!",
222 pbandwidth
, class_name
);
225 } else if (EQUAL(*argv
, "exactbandwidth")) {
228 bandwidth
= atobps(*argv
);
229 } else if (EQUAL(*argv
, "maxburst")) {
232 maxburst
= strtoul(*argv
, NULL
, 0);
233 } else if (EQUAL(*argv
, "minburst")) {
236 minburst
= strtoul(*argv
, NULL
, 0);
237 } else if (EQUAL(*argv
, "packetsize")) {
240 av_pkt_size
= atobytes(*argv
);
241 } else if (EQUAL(*argv
, "maxpacketsize")) {
244 max_pkt_size
= atobytes(*argv
);
245 } else if (EQUAL(*argv
, "red")) {
247 } else if (EQUAL(*argv
, "ecn")) {
249 } else if (EQUAL(*argv
, "flowvalve")) {
250 flags
|= CBQCLF_FLOWVALVE
;
251 } else if (EQUAL(*argv
, "rio")) {
253 } else if (EQUAL(*argv
, "cleardscp")) {
254 flags
|= CBQCLF_CLEARDSCP
;
257 "Unknown keyword '%s' in %s, line %d",
258 *argv
, altqconfigfile
, line_no
);
265 if ((flags
& (CBQCLF_RED
|CBQCLF_RIO
)) == (CBQCLF_RED
|CBQCLF_RIO
)) {
267 "both red and rio defined on interface '%s'",
271 if ((flags
& (CBQCLF_ECN
|CBQCLF_FLOWVALVE
))
272 && (flags
& (CBQCLF_RED
|CBQCLF_RIO
)) == 0)
275 if (strcmp("ctl_class", class_name
) == 0)
276 flags
|= CBQCLF_CTLCLASS
;
278 if (bandwidth
== 0 && pbandwidth
!= 0) {
279 struct ifinfo
*ifinfo
;
281 if ((ifinfo
= ifname2ifinfo(ifname
)) != NULL
)
282 bandwidth
= ifinfo
->bandwidth
/ 100 * pbandwidth
;
285 error
= qcmd_cbq_add_class(ifname
, class_name
, parent_name
, borrow
,
287 maxdelay
, maxburst
, minburst
,
288 av_pkt_size
, max_pkt_size
,
289 admission_type
, flags
);
299 qcmd_cbq_add_if(const char *ifname
, u_int bandwidth
, int is_wrr
, int efficient
)
303 error
= qop_cbq_add_if(NULL
, ifname
, bandwidth
, is_wrr
, efficient
);
305 LOG(LOG_ERR
, errno
, "%s: can't add cbq on interface '%s'",
306 qoperror(error
), ifname
);
311 qcmd_cbq_add_class(const char *ifname
, const char *class_name
,
312 const char *parent_name
, const char *borrow_name
,
313 u_int pri
, u_int bandwidth
,
314 u_int maxdelay
, u_int maxburst
, u_int minburst
,
315 u_int av_pkt_size
, u_int max_pkt_size
,
316 int admission_type
, int flags
)
318 struct ifinfo
*ifinfo
;
319 struct cbq_ifinfo
*cbq_ifinfo
;
320 struct classinfo
*parent
= NULL
, *borrow
= NULL
;
321 u_int ctl_bandwidth
= 0;
324 if ((ifinfo
= ifname2ifinfo(ifname
)) == NULL
)
325 error
= QOPERR_BADIF
;
326 cbq_ifinfo
= ifinfo
->private;
328 if (error
== 0 && parent_name
!= NULL
&&
329 (parent
= clname2clinfo(ifinfo
, parent_name
)) == NULL
)
330 error
= QOPERR_BADCLASS
;
332 if (error
== 0 && borrow_name
!= NULL
&&
333 (borrow
= clname2clinfo(ifinfo
, borrow_name
)) == NULL
)
334 error
= QOPERR_BADCLASS
;
336 if (flags
& CBQCLF_DEFCLASS
) {
338 * if this is a default class and no ctl_class is defined,
339 * we will create a ctl_class.
341 if (cbq_ifinfo
->ctl_class
== NULL
) {
342 /* reserve bandwidth for ctl_class */
344 ifinfo
->bandwidth
/ 100 * CTL_PBANDWIDTH
;
345 if (bandwidth
<= ctl_bandwidth
)
347 "bandwidth for default class too small!");
348 bandwidth
-= ctl_bandwidth
;
353 error
= qop_cbq_add_class(NULL
, class_name
, ifinfo
, parent
,
354 borrow
, pri
, bandwidth
,
355 maxdelay
, maxburst
, minburst
,
356 av_pkt_size
, max_pkt_size
,
357 admission_type
, flags
);
360 "cbq: %s: can't add class '%s' on interface '%s'",
361 qoperror(error
), class_name
, ifname
);
363 if (ctl_bandwidth
!= 0) {
365 * If were adding the default traffic class and
366 * no ctl_class is defined, also add the ctl traffic class.
367 * This is for RSVP and IGMP packets.
369 if (qcmd_cbq_add_class(ifname
, "ctl_class", parent_name
,
370 borrow_name
, 6, ctl_bandwidth
,
371 maxdelay
, maxburst
, minburst
, av_pkt_size
,
372 max_pkt_size
, admission_type
, CBQCLF_CTLCLASS
) != 0) {
373 LOG(LOG_ERR
, errno
, "can't create ctl_class!");
374 return (QOPERR_CLASS
);
379 * if this is a ctl class, add the default filters for backward
382 if (flags
& CBQCLF_CTLCLASS
)
383 qcmd_cbq_add_ctl_filters(ifname
, class_name
);
389 qcmd_cbq_modify_class(const char *ifname
, const char *class_name
,
390 u_int pri
, u_int bandwidth
,
391 u_int maxdelay
, u_int maxburst
, u_int minburst
,
392 u_int av_pkt_size
, u_int max_pkt_size
, int flags
)
394 struct ifinfo
*ifinfo
;
395 struct classinfo
*clinfo
;
397 if ((ifinfo
= ifname2ifinfo(ifname
)) == NULL
)
398 return (QOPERR_BADIF
);
400 if ((clinfo
= clname2clinfo(ifinfo
, class_name
)) == NULL
)
401 return (QOPERR_BADCLASS
);
403 return qop_cbq_modify_class(clinfo
, pri
, bandwidth
,
404 maxdelay
, maxburst
, minburst
,
405 av_pkt_size
, max_pkt_size
, flags
);
409 * add the default filters for ctl_class (for backward compatibility).
412 #define IPPROTO_RSVP 46
416 qcmd_cbq_add_ctl_filters(const char *ifname
, const char *clname
)
418 struct flow_filter sfilt
;
419 u_int8_t ctl_protos
[3] = {IPPROTO_ICMP
, IPPROTO_IGMP
, IPPROTO_RSVP
};
421 struct flow_filter6 sfilt6
;
422 u_int8_t ctl6_protos
[3] = {IPPROTO_ICMPV6
, IPPROTO_IGMP
, IPPROTO_RSVP
};
427 for (i
= 0; i
< sizeof(ctl_protos
); i
++) {
428 memset(&sfilt
, 0, sizeof(sfilt
));
429 sfilt
.ff_flow
.fi_family
= AF_INET
;
430 sfilt
.ff_flow
.fi_proto
= ctl_protos
[i
];
432 filter_dontwarn
= 1; /* XXX */
433 error
= qcmd_add_filter(ifname
, clname
, NULL
, &sfilt
);
434 filter_dontwarn
= 0; /* XXX */
437 "can't add ctl class filter on interface '%s'",
444 for (i
= 0; i
< sizeof(ctl6_protos
); i
++) {
445 memset(&sfilt6
, 0, sizeof(sfilt6
));
446 sfilt6
.ff_flow6
.fi6_family
= AF_INET6
;
447 sfilt6
.ff_flow6
.fi6_proto
= ctl6_protos
[i
];
449 error
= qcmd_add_filter(ifname
, clname
, NULL
,
450 (struct flow_filter
*)&sfilt6
);
453 "can't add ctl class IPv6 filter on interface '%s'",
466 qop_cbq_add_if(struct ifinfo
**rp
, const char *ifname
,
467 u_int bandwidth
, int is_wrr
, int efficient
)
469 struct ifinfo
*ifinfo
= NULL
;
470 struct cbq_ifinfo
*cbq_ifinfo
= NULL
;
473 if ((cbq_ifinfo
= calloc(1, sizeof(*cbq_ifinfo
))) == NULL
)
474 return (QOPERR_NOMEM
);
476 cbq_ifinfo
->nsPerByte
=
477 (1.0 / (double)bandwidth
) * NS_PER_SEC
* 8;
478 cbq_ifinfo
->is_wrr
= is_wrr
;
479 cbq_ifinfo
->is_efficient
= efficient
;
481 error
= qop_add_if(&ifinfo
, ifname
, bandwidth
,
482 &cbq_qdisc
, cbq_ifinfo
);
486 /* set enable hook */
487 ifinfo
->enable_hook
= qop_cbq_enable_hook
;
494 if (cbq_ifinfo
!= NULL
) {
497 ifinfo
->private = NULL
;
502 #define is_sc_null(sc) (((sc) == NULL) || ((sc)->m1 == 0 && (sc)->m2 == 0))
505 qop_cbq_add_class(struct classinfo
**rp
, const char *class_name
,
506 struct ifinfo
*ifinfo
, struct classinfo
*parent
,
507 struct classinfo
*borrow
, u_int pri
, u_int bandwidth
,
508 u_int maxdelay
, u_int maxburst
, u_int minburst
,
509 u_int av_pkt_size
, u_int max_pkt_size
,
510 int admission_type
, int flags
)
512 struct classinfo
*clinfo
;
513 struct cbq_ifinfo
*cbq_ifinfo
;
514 struct cbq_classinfo
*cbq_clinfo
, *parent_clinfo
;
515 u_int parent_handle
, borrow_handle
;
518 cbq_ifinfo
= ifinfo
->private;
520 if (parent
== NULL
) {
521 if (cbq_ifinfo
->root_class
!= NULL
)
522 return (QOPERR_CLASS_INVAL
);
523 flags
|= CBQCLF_ROOTCLASS
;
525 if ((flags
& CBQCLF_DEFCLASS
) && cbq_ifinfo
->default_class
!= NULL
)
526 return (QOPERR_CLASS_INVAL
);
527 if ((flags
& CBQCLF_CTLCLASS
) && cbq_ifinfo
->ctl_class
!= NULL
)
528 return (QOPERR_CLASS_INVAL
);
530 /* admission control */
531 if (parent
!= NULL
) {
532 parent_clinfo
= parent
->private;
534 parent_clinfo
->bandwidth
- parent_clinfo
->allocated
) {
535 #ifdef ALLOW_OVERCOMMIT
537 "bandwidth overcommitted %uK requested but only %dK available (%uK already allocated)",
539 ((int)parent_clinfo
->bandwidth
-
540 parent_clinfo
->allocated
) / 1000,
541 parent_clinfo
->allocated
/ 1000);
542 #else /* !ALLOW_OVERCOMMIT */
544 "cbq admission failed! %uK requested but only %uK available (%uK already allocated)",
546 (parent_clinfo
->bandwidth
-
547 parent_clinfo
->allocated
) / 1000,
548 parent_clinfo
->allocated
/ 1000);
549 return (QOPERR_ADMISSION_NOBW
);
550 #endif /* !ALLOW_OVERCOMMIT */
554 if ((cbq_clinfo
= calloc(1, sizeof(*cbq_clinfo
))) == NULL
)
555 return (QOPERR_NOMEM
);
557 cbq_clinfo
->bandwidth
= bandwidth
;
558 cbq_clinfo
->allocated
= 0;
560 /* if average packet size isn't specified, set if mtu. */
561 if (av_pkt_size
== 0) { /* use default */
562 av_pkt_size
= ifinfo
->ifmtu
;
563 if (av_pkt_size
> MCLBYTES
) /* do what TCP does */
564 av_pkt_size
&= ~MCLBYTES
;
565 } else if (av_pkt_size
> ifinfo
->ifmtu
)
566 av_pkt_size
= ifinfo
->ifmtu
;
568 if (max_pkt_size
== 0) /* use default */
569 max_pkt_size
= ifinfo
->ifmtu
;
570 else if (max_pkt_size
> ifinfo
->ifmtu
)
571 max_pkt_size
= ifinfo
->ifmtu
;
573 cbq_clinfo
->maxdelay
= maxdelay
;
574 cbq_clinfo
->maxburst
= maxburst
;
575 cbq_clinfo
->minburst
= minburst
;
576 cbq_clinfo
->av_pkt_size
= av_pkt_size
;
577 cbq_clinfo
->max_pkt_size
= max_pkt_size
;
579 parent_handle
= parent
!= NULL
? parent
->handle
: NULL_CLASS_HANDLE
;
580 borrow_handle
= borrow
!= NULL
? borrow
->handle
: NULL_CLASS_HANDLE
;
582 if (cbq_class_spec(ifinfo
, parent_handle
, borrow_handle
, pri
, flags
,
583 bandwidth
, maxdelay
, maxburst
, minburst
,
584 av_pkt_size
, max_pkt_size
,
585 &cbq_clinfo
->class_spec
) < 0) {
586 error
= QOPERR_INVAL
;
591 error
= qop_add_class(&clinfo
, class_name
, ifinfo
, parent
, cbq_clinfo
);
595 /* set delete hook */
596 clinfo
->delete_hook
= qop_cbq_delete_class_hook
;
599 cbq_ifinfo
->root_class
= clinfo
;
601 parent_clinfo
= parent
->private;
602 parent_clinfo
->allocated
+= bandwidth
;
604 if (flags
& CBQCLF_DEFCLASS
)
605 cbq_ifinfo
->default_class
= clinfo
;
606 if (flags
& CBQCLF_CTLCLASS
)
607 cbq_ifinfo
->ctl_class
= clinfo
;
609 switch (admission_type
) {
610 case CBQ_QOS_CNTR_LOAD
:
611 case CBQ_QOS_GUARANTEED
:
612 case CBQ_QOS_PREDICTIVE
:
613 case CBQ_QOS_CNTR_DELAY
:
614 if (ifinfo
->resv_class
!= NULL
) {
616 "%s: duplicate resv meta class", class_name
);
617 return (QOPERR_CLASS
);
619 ifinfo
->resv_class
= clinfo
;
627 if (cbq_clinfo
!= NULL
) {
630 clinfo
->private = NULL
;
636 * this is called from qop_delete_class() before a class is destroyed
637 * for discipline specific cleanup.
640 qop_cbq_delete_class_hook(struct classinfo
*clinfo
)
642 struct cbq_classinfo
*cbq_clinfo
, *parent_clinfo
;
644 /* cancel admission control */
645 if (clinfo
->parent
!= NULL
) {
646 cbq_clinfo
= clinfo
->private;
647 parent_clinfo
= clinfo
->parent
->private;
649 parent_clinfo
->allocated
-= cbq_clinfo
->bandwidth
;
655 qop_cbq_modify_class(struct classinfo
*clinfo
, u_int pri
, u_int bandwidth
,
656 u_int maxdelay
, u_int maxburst
, u_int minburst
,
657 u_int av_pkt_size
, u_int max_pkt_size
, int flags
)
659 struct ifinfo
*ifinfo
;
660 struct cbq_classinfo
*cbq_clinfo
, *parent_clinfo
;
661 u_int parent_handle
, borrow_handle
;
665 ifinfo
= clinfo
->ifinfo
;
666 cbq_clinfo
= clinfo
->private;
668 /* admission control */
669 old_bandwidth
= cbq_clinfo
->bandwidth
;
670 if (clinfo
->parent
!= NULL
) {
671 parent_clinfo
= clinfo
->parent
->private;
672 if (bandwidth
> old_bandwidth
) {
673 /* increase bandwidth */
674 if (bandwidth
- old_bandwidth
>
675 parent_clinfo
->bandwidth
676 - parent_clinfo
->allocated
)
677 return (QOPERR_ADMISSION_NOBW
);
678 } else if (bandwidth
< old_bandwidth
) {
679 /* decrease bandwidth */
680 if (bandwidth
< cbq_clinfo
->allocated
)
681 return (QOPERR_ADMISSION
);
685 /* if average packet size isn't specified, set if mtu. */
686 if (av_pkt_size
== 0) { /* use default */
687 av_pkt_size
= ifinfo
->ifmtu
;
688 if (av_pkt_size
> MCLBYTES
) /* do what TCP does */
689 av_pkt_size
&= ~MCLBYTES
;
690 } else if (av_pkt_size
> ifinfo
->ifmtu
)
691 av_pkt_size
= ifinfo
->ifmtu
;
693 if (max_pkt_size
== 0) /* use default */
694 max_pkt_size
= ifinfo
->ifmtu
;
695 else if (max_pkt_size
> ifinfo
->ifmtu
)
696 max_pkt_size
= ifinfo
->ifmtu
;
698 cbq_clinfo
->maxdelay
= maxdelay
;
699 cbq_clinfo
->maxburst
= maxburst
;
700 cbq_clinfo
->minburst
= minburst
;
701 cbq_clinfo
->av_pkt_size
= av_pkt_size
;
702 cbq_clinfo
->max_pkt_size
= max_pkt_size
;
704 parent_handle
= cbq_clinfo
->class_spec
.parent_class_handle
;
705 borrow_handle
= cbq_clinfo
->class_spec
.borrow_class_handle
;
707 if (cbq_class_spec(ifinfo
, parent_handle
, borrow_handle
, pri
, flags
,
708 bandwidth
, maxdelay
, maxburst
, minburst
,
709 av_pkt_size
, max_pkt_size
,
710 &cbq_clinfo
->class_spec
) < 0) {
711 return (QOPERR_INVAL
);
714 error
= qop_modify_class(clinfo
, NULL
);
717 if (clinfo
->parent
!= NULL
) {
718 parent_clinfo
= clinfo
->parent
->private;
719 parent_clinfo
->allocated
-= old_bandwidth
;
720 parent_clinfo
->allocated
+= bandwidth
;
722 cbq_clinfo
->bandwidth
= bandwidth
;
728 * sanity check at enabling cbq:
729 * there must one root class and one default class for an interface
732 qop_cbq_enable_hook(struct ifinfo
*ifinfo
)
734 struct cbq_ifinfo
*cbq_ifinfo
;
736 cbq_ifinfo
= ifinfo
->private;
737 if (cbq_ifinfo
->root_class
== NULL
) {
738 LOG(LOG_ERR
, 0, "cbq: no root class on interface %s!",
740 return (QOPERR_CLASS
);
742 if (cbq_ifinfo
->default_class
== NULL
) {
743 LOG(LOG_ERR
, 0, "cbq: no default class on interface %s!",
745 return (QOPERR_CLASS
);
751 cbq_class_spec(struct ifinfo
*ifinfo
, u_long parent_class
,
752 u_long borrow_class
, u_int pri
, int flags
,
753 u_int bandwidth
, u_int maxdelay
, u_int maxburst
,
754 u_int minburst
, u_int av_pkt_size
, u_int max_pkt_size
,
755 cbq_class_spec_t
*cl_spec
)
757 struct cbq_ifinfo
*cbq_ifinfo
= ifinfo
->private;
758 double maxq
, maxidle_s
, maxidle
, minidle
,
759 lofftime
, nsPerByte
, ptime
, cptime
;
760 double z
= (double)(1 << RM_FILTER_GAIN
);
761 double g
= (1.0 - 1.0 / z
);
767 /* Compute other class parameters */
769 f
= 0.0001; /* small enough? */
771 f
= ((double) bandwidth
/ (double) ifinfo
->bandwidth
);
773 if (av_pkt_size
== 0) { /* use default */
774 av_pkt_size
= ifinfo
->ifmtu
;
775 if (av_pkt_size
> MCLBYTES
) /* do what TCP does */
776 av_pkt_size
&= ~MCLBYTES
;
777 } else if (av_pkt_size
> ifinfo
->ifmtu
)
778 av_pkt_size
= ifinfo
->ifmtu
;
779 if (max_pkt_size
== 0) /* use default */
780 max_pkt_size
= ifinfo
->ifmtu
;
781 else if (max_pkt_size
> ifinfo
->ifmtu
)
782 max_pkt_size
= ifinfo
->ifmtu
;
784 nsPerByte
= cbq_ifinfo
->nsPerByte
/ f
;
785 ptime
= (double) av_pkt_size
* (double)cbq_ifinfo
->nsPerByte
;
786 maxrate
= f
* ((double)ifinfo
->bandwidth
/ 8.0);
787 cptime
= ptime
* (1.0 - f
) / f
;
789 if (nsPerByte
* (double)max_pkt_size
> (double)INT_MAX
) {
791 * this causes integer overflow in kernel!
792 * (bandwidth < 6Kbps when max_pkt_size=1500)
795 LOG(LOG_WARNING
, 0, "warning: class is too slow!!");
796 nsPerByte
= (double)(INT_MAX
/ max_pkt_size
);
799 if (maxburst
== 0) { /* use default */
800 if (cptime
> 10.0 * NS_PER_MS
)
805 if (minburst
== 0) /* use default */
807 if (minburst
> maxburst
)
810 if (IsDebug(DEBUG_ALTQ
)) {
813 "cbq_flowspec: maxburst=%d,minburst=%d,pkt_size=%d",
814 maxburst
, minburst
, av_pkt_size
);
816 " nsPerByte=%.2f ns, link's nsPerByte=%.2f, f=%.3f",
817 nsPerByte
, cbq_ifinfo
->nsPerByte
, f
);
818 packet_time
= av_pkt_size
* (int)nsPerByte
/ 1000;
820 " packet time=%d [us]\n", packet_time
);
821 if (maxburst
* packet_time
< 20000) {
823 "warning: maxburst smaller than timer granularity!");
825 " maxburst=%d, packet_time=%d [us]",
826 maxburst
, packet_time
);
829 gton
= pow(g
, (double)maxburst
);
830 gtom
= pow(g
, (double)(minburst
-1));
831 maxidle
= ((1.0 / f
- 1.0) * ((1.0 - gton
) / gton
));
832 maxidle_s
= (1.0 - g
);
833 if (maxidle
> maxidle_s
)
834 maxidle
= ptime
* maxidle
;
836 maxidle
= ptime
* maxidle_s
;
837 if (IsDebug(DEBUG_ALTQ
))
838 LOG(LOG_DEBUG
, 0, " maxidle=%.2f us", maxidle
/1000.0);
840 lofftime
= cptime
* (1.0 + 1.0/(1.0 - g
) * (1.0 - gtom
) / gtom
);
843 minidle
= -((double)max_pkt_size
* (double)nsPerByte
);
844 if (IsDebug(DEBUG_ALTQ
))
845 LOG(LOG_DEBUG
, 0, " lofftime=%.2f us minidle=%.2f us",
846 lofftime
/1000.0, minidle
/1000.0);
848 maxidle
= ((maxidle
* 8.0) / nsPerByte
) * pow(2, RM_FILTER_GAIN
);
850 /* also scale lofftime and minidle */
851 lofftime
= (lofftime
* 8.0) / nsPerByte
* pow(2, RM_FILTER_GAIN
);
852 minidle
= ((minidle
* 8.0) / nsPerByte
) * pow(2, RM_FILTER_GAIN
);
854 maxidle
= maxidle
/ 1000.0;
855 lofftime
= lofftime
/ 1000.0;
856 minidle
= minidle
/ 1000.0;
857 /* adjust queue size when maxdelay is specified.
858 queue size should be relative to its share */
860 if (flags
& (CBQCLF_RED
|CBQCLF_RIO
))
865 maxq
= ((double) maxdelay
* NS_PER_MS
) / (nsPerByte
* av_pkt_size
);
868 "warning: maxq (%d) is too small. set to %d",
873 if (bandwidth
== 0 && borrow_class
== NULL_CLASS_HANDLE
)
874 /* filter out this class by setting queue size to zero */
876 if (IsDebug(DEBUG_ALTQ
)) {
877 if ((u_int
)maxq
< maxburst
)
879 "warning: maxq (%d) is smaller than maxburst(%d)",
880 (int)maxq
, maxburst
);
881 else if (maxq
> 100.0)
883 "warning: maxq %d too large\n", (int)maxq
);
884 LOG(LOG_DEBUG
, 0, " maxq=%d", (int)maxq
);
887 if (parent_class
== NULL_CLASS_HANDLE
) {
888 if ((flags
& CBQCLF_ROOTCLASS
) == 0)
889 flags
|= CBQCLF_ROOTCLASS
;
890 if (cbq_ifinfo
->is_wrr
)
892 if (cbq_ifinfo
->is_efficient
)
893 flags
|= CBQCLF_EFFICIENT
;
896 memset((void *)cl_spec
, 0, sizeof(cbq_class_spec_t
));
897 cl_spec
->priority
= pri
;
898 cl_spec
->nano_sec_per_byte
= (u_int
) nsPerByte
;
899 cl_spec
->maxq
= (u_int
) maxq
;
900 cl_spec
->maxidle
= (u_int
) fabs(maxidle
);
901 cl_spec
->minidle
= (int)minidle
;
902 cl_spec
->offtime
= (u_int
) fabs(lofftime
);
904 cl_spec
->parent_class_handle
= parent_class
;
905 cl_spec
->borrow_class_handle
= borrow_class
;
907 cl_spec
->pktsize
= av_pkt_size
;
908 cl_spec
->flags
= flags
;
915 * system call interfaces for qdisc_ops
918 cbq_attach(struct ifinfo
*ifinfo
)
920 struct cbq_interface iface
;
923 (cbq_fd
= open(CBQ_DEVICE
, O_RDWR
)) < 0 &&
924 (cbq_fd
= open_module(CBQ_DEVICE
, O_RDWR
)) < 0) {
925 LOG(LOG_ERR
, errno
, "CBQ open");
926 return (QOPERR_SYSCALL
);
930 memset(&iface
, 0, sizeof(iface
));
931 strncpy(iface
.cbq_ifacename
, ifinfo
->ifname
, IFNAMSIZ
);
933 if (ioctl(cbq_fd
, CBQ_IF_ATTACH
, &iface
) < 0)
934 return (QOPERR_SYSCALL
);
939 cbq_detach(struct ifinfo
*ifinfo
)
941 struct cbq_interface iface
;
943 memset(&iface
, 0, sizeof(iface
));
944 strncpy(iface
.cbq_ifacename
, ifinfo
->ifname
, IFNAMSIZ
);
946 if (ioctl(cbq_fd
, CBQ_IF_DETACH
, &iface
) < 0)
947 return (QOPERR_SYSCALL
);
949 if (--cbq_refcount
== 0) {
957 cbq_clear(struct ifinfo
*ifinfo
)
959 struct cbq_interface iface
;
961 memset(&iface
, 0, sizeof(iface
));
962 strncpy(iface
.cbq_ifacename
, ifinfo
->ifname
, IFNAMSIZ
);
964 if (ioctl(cbq_fd
, CBQ_CLEAR_HIERARCHY
, &iface
) < 0)
965 return (QOPERR_SYSCALL
);
970 cbq_enable(struct ifinfo
*ifinfo
)
972 struct cbq_interface iface
;
974 memset(&iface
, 0, sizeof(iface
));
975 strncpy(iface
.cbq_ifacename
, ifinfo
->ifname
, IFNAMSIZ
);
977 if (ioctl(cbq_fd
, CBQ_ENABLE
, &iface
) < 0)
978 return (QOPERR_SYSCALL
);
983 cbq_disable(struct ifinfo
*ifinfo
)
985 struct cbq_interface iface
;
987 memset(&iface
, 0, sizeof(iface
));
988 strncpy(iface
.cbq_ifacename
, ifinfo
->ifname
, IFNAMSIZ
);
990 if (ioctl(cbq_fd
, CBQ_DISABLE
, &iface
) < 0)
991 return (QOPERR_SYSCALL
);
996 cbq_add_class(struct classinfo
*clinfo
)
998 struct cbq_add_class class_add
;
999 struct cbq_classinfo
*cbq_clinfo
;
1000 struct cbq_ifinfo
*cbq_ifinfo
;
1002 cbq_ifinfo
= clinfo
->ifinfo
->private;
1003 cbq_clinfo
= clinfo
->private;
1005 memset(&class_add
, 0, sizeof(class_add
));
1006 strncpy(class_add
.cbq_iface
.cbq_ifacename
,
1007 clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
1009 class_add
.cbq_class
= cbq_clinfo
->class_spec
;
1011 if (ioctl(cbq_fd
, CBQ_ADD_CLASS
, &class_add
) < 0)
1012 return (QOPERR_SYSCALL
);
1014 clinfo
->handle
= class_add
.cbq_class_handle
;
1019 cbq_modify_class(struct classinfo
*clinfo
, void *arg
)
1021 struct cbq_modify_class class_mod
;
1022 struct cbq_classinfo
*cbq_clinfo
;
1024 cbq_clinfo
= clinfo
->private;
1026 memset(&class_mod
, 0, sizeof(class_mod
));
1027 strncpy(class_mod
.cbq_iface
.cbq_ifacename
,
1028 clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
1029 class_mod
.cbq_class_handle
= clinfo
->handle
;
1030 class_mod
.cbq_class
= cbq_clinfo
->class_spec
;
1032 if (ioctl(cbq_fd
, CBQ_MODIFY_CLASS
, &class_mod
) < 0)
1033 return (QOPERR_SYSCALL
);
1038 cbq_delete_class(struct classinfo
*clinfo
)
1040 struct cbq_delete_class class_delete
;
1042 memset(&class_delete
, 0, sizeof(class_delete
));
1043 strncpy(class_delete
.cbq_iface
.cbq_ifacename
,
1044 clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
1045 class_delete
.cbq_class_handle
= clinfo
->handle
;
1047 if (ioctl(cbq_fd
, CBQ_DEL_CLASS
, &class_delete
) < 0)
1048 return (QOPERR_SYSCALL
);
1053 cbq_add_filter(struct fltrinfo
*fltrinfo
)
1055 struct cbq_add_filter fltr_add
;
1057 memset(&fltr_add
, 0, sizeof(fltr_add
));
1058 strncpy(fltr_add
.cbq_iface
.cbq_ifacename
,
1059 fltrinfo
->clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
1060 fltr_add
.cbq_class_handle
= fltrinfo
->clinfo
->handle
;
1061 fltr_add
.cbq_filter
= fltrinfo
->fltr
;
1063 if (ioctl(cbq_fd
, CBQ_ADD_FILTER
, &fltr_add
) < 0)
1064 return (QOPERR_SYSCALL
);
1065 fltrinfo
->handle
= fltr_add
.cbq_filter_handle
;
1070 cbq_delete_filter(struct fltrinfo
*fltrinfo
)
1072 struct cbq_delete_filter fltr_del
;
1074 memset(&fltr_del
, 0, sizeof(fltr_del
));
1075 strncpy(fltr_del
.cbq_iface
.cbq_ifacename
,
1076 fltrinfo
->clinfo
->ifinfo
->ifname
, IFNAMSIZ
);
1077 fltr_del
.cbq_filter_handle
= fltrinfo
->handle
;
1079 if (ioctl(cbq_fd
, CBQ_DEL_FILTER
, &fltr_del
) < 0)
1080 return (QOPERR_SYSCALL
);