tbrconfig.c revision 1.2 1 /* $KAME: tbrconfig.c,v 1.3 2001/05/08 04:36:39 itojun 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")) != -1) {
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