1 /* $NetBSD: tbrconfig.c,v 1.5 2023/08/03 20:45:50 andvar 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 __dead 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
usage(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
main(int argc,char ** argv)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
list_all(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
atobps(const char * s)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
atobytes(const char * s)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 determine the bucket size
241 */
242 static u_int
size_bucket(const char * ifname,const u_int rate)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
autosize_bucket(const char * ifname,const u_int rate)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
get_clockfreq(void)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
get_ifmtu(const char * ifname)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