Home | History | Annotate | Line # | Download | only in tbrconfig
tbrconfig.c revision 1.1
      1 /*	$KAME: tbrconfig.c,v 1.2 2000/12/03 05:04:55 kjc Exp $	*/
      2 /*
      3  * Copyright (C) 2000
      4  *	Sony Computer Science Laboratories Inc.  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  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY SONY CSL AND CONTRIBUTORS ``AS IS'' AND
     16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     18  * ARE DISCLAIMED.  IN NO EVENT SHALL SONY CSL OR CONTRIBUTORS BE LIABLE
     19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     25  * SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/param.h>
     29 #include <sys/time.h>
     30 #include <sys/socket.h>
     31 #include <sys/ioctl.h>
     32 #include <sys/fcntl.h>
     33 #include <sys/sysctl.h>
     34 #include <net/if.h>
     35 
     36 #include <stdio.h>
     37 #include <stdlib.h>
     38 #include <unistd.h>
     39 #include <string.h>
     40 #include <err.h>
     41 
     42 #include <altq/altq.h>
     43 
     44 #define	ALTQ_DEVICE	"/dev/altq/altq"
     45 
     46 static void usage(void);
     47 static u_long atobps(const char *s);
     48 static u_long atobytes(const char *s);
     49 static u_int size_bucket(const char *ifname, const u_int rate);
     50 static u_int autosize_bucket(const char *ifname, const u_int rate);
     51 static int get_clockfreq(void);
     52 static int get_ifmtu(const char *ifname);
     53 static void list_all(void);
     54 
     55 static void
     56 usage(void)
     57 {
     58 	fprintf(stderr, "usage: tbrconfig interface [tokenrate [bucketsize]\n");
     59 	fprintf(stderr, "       tbrconfig -d interface\n");
     60 	fprintf(stderr, "       tbrconfig -a\n");
     61 	exit(1);
     62 }
     63 
     64 int
     65 main(int argc, char **argv)
     66 {
     67 	struct tbrreq req;
     68 	u_int rate, depth;
     69 	int fd, ch, delete;
     70 
     71 	delete = 0;
     72 	rate = 0;
     73 	depth = 0;
     74 
     75 	while ((ch = getopt(argc, argv, "ad")) != EOF) {
     76 		switch (ch) {
     77 		case 'a':
     78 			list_all();
     79 			return (0);
     80 		case 'd':
     81 			delete = 1;
     82 			break;
     83 		}
     84 	}
     85 
     86 	argc -= optind;
     87 	argv += optind;
     88 	if (argc < 1)
     89 		usage();
     90 
     91 	req.ifname[IFNAMSIZ-1] = '\0';
     92 	strncpy(req.ifname, argv[0], IFNAMSIZ-1);
     93 	if (argc > 1)
     94 		rate = (u_int)atobps(argv[1]);
     95 	if (argc > 2) {
     96 		if (strncmp(argv[2], "auto", strlen("auto")) == 0)
     97 			depth = autosize_bucket(req.ifname, rate);
     98 		else
     99 			depth = (u_int)atobytes(argv[2]);
    100 	}
    101 	if (argc > 3)
    102 		usage();
    103 
    104 	if (delete || rate > 0) {
    105 		/* set token bucket regulator */
    106 		if (delete)
    107 			rate = 0;
    108 		else if (depth == 0)
    109 			depth = size_bucket(req.ifname, rate);
    110 
    111 		req.tb_prof.rate = rate;
    112 		req.tb_prof.depth = depth;
    113 
    114 		if ((fd = open(ALTQ_DEVICE, O_RDWR)) < 0)
    115 			err(1, "can't open altq device");
    116 
    117 		if (ioctl(fd, ALTQTBRSET, &req) < 0)
    118 			err(1, "ALTQTBRSET for interface %s", req.ifname);
    119 
    120 		close(fd);
    121 
    122 		if (delete) {
    123 			printf("deleted token bucket regulator on %s\n",
    124 			       req.ifname);
    125 			return (0);
    126 		}
    127 	}
    128 
    129 	/* get token bucket regulator */
    130 	if ((fd = open(ALTQ_DEVICE, O_RDONLY)) < 0)
    131 		err(1, "can't open altq device");
    132 	if (ioctl(fd, ALTQTBRGET, &req) < 0)
    133 		err(1, "ALTQTBRGET for interface %s", req.ifname);
    134 	if (req.tb_prof.rate == 0)
    135 		printf("no token bucket regulater found on %s\n", req.ifname);
    136 	else {
    137 		char rate_str[64], size_str[64];
    138 
    139 		if (req.tb_prof.rate < 999999)
    140 			sprintf(rate_str, "%.2fK",
    141 				(double)req.tb_prof.rate/1000.0);
    142 		else
    143 			sprintf(rate_str, "%.2fM",
    144 				(double)req.tb_prof.rate/1000000.0);
    145 		if (req.tb_prof.depth < 10240)
    146 			sprintf(size_str, "%u", req.tb_prof.depth);
    147 		else
    148 			sprintf(size_str, "%.2fK",
    149 				(double)req.tb_prof.depth/1024.0);
    150 		printf("%s: tokenrate %s(bps)  bucketsize %s(bytes)\n",
    151 		       req.ifname, rate_str, size_str);
    152 	}
    153 	close(fd);
    154 	return (0);
    155 }
    156 
    157 static void
    158 list_all(void)
    159 {
    160 	struct if_nameindex *ifn_list, *ifnp;
    161 	struct tbrreq req;
    162 	char rate_str[64], size_str[64];
    163 	int fd, ntbr;
    164 
    165 	if ((ifn_list = if_nameindex()) == NULL)
    166 		err(1, "if_nameindex failed");
    167 
    168 	if ((fd = open(ALTQ_DEVICE, O_RDONLY)) < 0)
    169 		err(1, "can't open altq device");
    170 
    171 	ntbr = 0;
    172 	for (ifnp = ifn_list; ifnp->if_name != NULL; ifnp++) {
    173 		req.ifname[IFNAMSIZ-1] = '\0';
    174 		strncpy(req.ifname, ifnp->if_name, IFNAMSIZ-1);
    175 		if (ioctl(fd, ALTQTBRGET, &req) < 0)
    176 			err(1, "ALTQTBRGET");
    177 		if (req.tb_prof.rate == 0)
    178 			continue;
    179 
    180 		if (req.tb_prof.rate < 999999)
    181 			sprintf(rate_str, "%.2fK",
    182 				(double)req.tb_prof.rate/1000.0);
    183 		else
    184 			sprintf(rate_str, "%.2fM",
    185 				(double)req.tb_prof.rate/1000000.0);
    186 		if (req.tb_prof.depth < 10240)
    187 			sprintf(size_str, "%u", req.tb_prof.depth);
    188 		else
    189 			sprintf(size_str, "%.2fK",
    190 				(double)req.tb_prof.depth/1024.0);
    191 		printf("%s: tokenrate %s(bps)  bucketsize %s(bytes)\n",
    192 		       req.ifname, rate_str, size_str);
    193 		ntbr++;
    194 	}
    195 	if (ntbr == 0)
    196 		printf("no active token bucket regulator\n");
    197 
    198 	close(fd);
    199 	if_freenameindex(ifn_list);
    200 }
    201 
    202 static u_long
    203 atobps(const char *s)
    204 {
    205 	u_long bandwidth;
    206 	char *cp;
    207 
    208 	bandwidth = strtoul(s, &cp, 0);
    209 	if (cp != NULL) {
    210 		if (*cp == 'K' || *cp == 'k')
    211 			bandwidth *= 1000;
    212 		else if (*cp == 'M' || *cp == 'm')
    213 			bandwidth *= 1000000;
    214 		else if (*cp == 'G' || *cp == 'g')
    215 			bandwidth *= 1000000000;
    216 	}
    217 	return (bandwidth);
    218 }
    219 
    220 static u_long
    221 atobytes(const char *s)
    222 {
    223 	u_long bytes;
    224 	char *cp;
    225 
    226 	bytes = strtoul(s, &cp, 0);
    227 	if (cp != NULL) {
    228 		if (*cp == 'K' || *cp == 'k')
    229 			bytes *= 1024;
    230 		else if (*cp == 'M' || *cp == 'm')
    231 			bytes *= 1024 * 1024;
    232 		else if (*cp == 'G' || *cp == 'g')
    233 			bytes *= 1024 * 1024 * 1024;
    234 	}
    235 	return (bytes);
    236 }
    237 
    238 /*
    239  * use heuristics to determin the bucket size
    240  */
    241 static u_int
    242 size_bucket(const char *ifname, const u_int rate)
    243 {
    244 	u_int size, mtu;
    245 
    246 	mtu = get_ifmtu(ifname);
    247 	if (mtu > 1500)
    248 		mtu = 1500;	/* assume that the path mtu is still 1500 */
    249 
    250 	if (rate <= 1*1000*1000)
    251 		size = 1;
    252 	else if (rate <= 10*1000*1000)
    253 		size = 4;
    254 	else if (rate <= 200*1000*1000)
    255 		size = 8;
    256 	else
    257 		size = 24;
    258 
    259 	size = size * mtu;
    260 	return (size);
    261 }
    262 
    263 /*
    264  * compute the bucket size to be required to fill the rate
    265  * even when the rate is controlled only by the kernel timer.
    266  */
    267 static u_int
    268 autosize_bucket(const char *ifname, const u_int rate)
    269 {
    270 	u_int size, freq, mtu;
    271 
    272 	mtu = get_ifmtu(ifname);
    273 	freq = get_clockfreq();
    274 	size = rate / 8 / freq;
    275 	if (size < mtu)
    276 		size = mtu;
    277 	return (size);
    278 }
    279 
    280 static int
    281 get_clockfreq(void)
    282 {
    283 	struct clockinfo clkinfo;
    284 	int mib[2];
    285 	size_t len;
    286 
    287 	clkinfo.hz = 100; /* default Hz */
    288 
    289 	mib[0] = CTL_KERN;
    290 	mib[1] = KERN_CLOCKRATE;
    291 	len = sizeof(struct clockinfo);
    292 	if (sysctl(mib, 2, &clkinfo, &len, NULL, 0) == -1)
    293 		warnx("can't get clockrate via sysctl! use %dHz", clkinfo.hz);
    294 	return (clkinfo.hz);
    295 }
    296 
    297 static int
    298 get_ifmtu(const char *ifname)
    299 {
    300 	int s, mtu;
    301 	struct ifreq ifr;
    302 #ifdef __OpenBSD__
    303 	struct if_data ifdata;
    304 #endif
    305 
    306 	mtu = 512; /* default MTU */
    307 
    308 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    309 		return (mtu);
    310 	strncpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name);
    311 #ifdef __OpenBSD__
    312 	ifr.ifr_data = (caddr_t)&ifdata;
    313 	if (ioctl(s, SIOCGIFDATA, (caddr_t)&ifr) == 0)
    314 		mtu = ifdata.ifi_mtu;
    315 #else
    316 	if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) == 0)
    317 		mtu = ifr.ifr_mtu;
    318 #endif
    319 	close(s);
    320 	return (mtu);
    321 }
    322