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