Home | History | Annotate | Line # | Download | only in libaltq
      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