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