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