1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70#include "libbb.h"
71#include "common_bufsiz.h"
72#include <linux/sockios.h>
73#include <net/if.h>
74
75#ifndef SIOCBRADDBR
76# define SIOCBRADDBR BRCTL_ADD_BRIDGE
77#endif
78#ifndef SIOCBRDELBR
79# define SIOCBRDELBR BRCTL_DEL_BRIDGE
80#endif
81#ifndef SIOCBRADDIF
82# define SIOCBRADDIF BRCTL_ADD_IF
83#endif
84#ifndef SIOCBRDELIF
85# define SIOCBRDELIF BRCTL_DEL_IF
86#endif
87
88#if ENABLE_FEATURE_BRCTL_FANCY
89static unsigned str_to_jiffies(const char *time_str)
90{
91 double dd;
92 char *endptr;
93 dd = strtod(time_str, &endptr);
94 if (endptr == time_str || dd < 0)
95 bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec");
96
97 dd *= 100;
98
99
100
101 if (dd > INT_MAX)
102 dd = INT_MAX;
103
104 return dd;
105}
106#endif
107
108#define filedata bb_common_bufsiz1
109
110#if ENABLE_FEATURE_BRCTL_SHOW
111static int read_file(const char *name)
112{
113 int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1);
114 if (n < 0) {
115 filedata[0] = '\0';
116 } else {
117 filedata[n] = '\0';
118 if (n != 0 && filedata[n - 1] == '\n')
119 filedata[--n] = '\0';
120 }
121 return n;
122}
123
124
125
126static int show_bridge(const char *name, int need_hdr)
127{
128
129
130
131
132 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
133 int tabs;
134 DIR *ifaces;
135 struct dirent *ent;
136 char *sfx;
137
138#if IFNAMSIZ == 16
139 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
140#else
141 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
142#endif
143 strcpy(sfx, "bridge_id");
144 if (read_file(pathbuf) < 0)
145 return -1;
146
147 if (need_hdr)
148 puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
149 printf("%s\t\t", name);
150 printf("%s\t", filedata);
151
152 strcpy(sfx, "stp_state");
153 read_file(pathbuf);
154 if (LONE_CHAR(filedata, '0'))
155 strcpy(filedata, "no");
156 else
157 if (LONE_CHAR(filedata, '1'))
158 strcpy(filedata, "yes");
159 fputs(filedata, stdout);
160
161 strcpy(sfx - (sizeof("bridge/")-1), "brif");
162 tabs = 0;
163 ifaces = opendir(pathbuf);
164 if (ifaces) {
165 while ((ent = readdir(ifaces)) != NULL) {
166 if (DOT_OR_DOTDOT(ent->d_name))
167 continue;
168 if (tabs)
169 printf("\t\t\t\t\t");
170 else
171 tabs = 1;
172 printf("\t\t%s\n", ent->d_name);
173 }
174 closedir(ifaces);
175 }
176 if (!tabs)
177 bb_putchar('\n');
178 return 0;
179}
180#endif
181
182#if ENABLE_FEATURE_BRCTL_FANCY
183static void write_uint(const char *name, const char *leaf, unsigned val)
184{
185 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
186 int fd, n;
187
188#if IFNAMSIZ == 16
189 sprintf(pathbuf, "%.16s/%s", name, leaf);
190#else
191 sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf);
192#endif
193 fd = xopen(pathbuf, O_WRONLY);
194 n = sprintf(filedata, "%u\n", val);
195 if (write(fd, filedata, n) < 0)
196 bb_simple_perror_msg_and_die(name);
197 close(fd);
198}
199#endif
200
201int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
202int brctl_main(int argc UNUSED_PARAM, char **argv)
203{
204 static const char keywords[] ALIGN1 =
205 "addbr\0" "delbr\0" "addif\0" "delif\0"
206 IF_FEATURE_BRCTL_FANCY(
207 "stp\0"
208 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
209 "setpathcost\0" "setportprio\0"
210 "setbridgeprio\0"
211 )
212 IF_FEATURE_BRCTL_SHOW("show\0");
213 enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
214 IF_FEATURE_BRCTL_FANCY(,
215 ARG_stp,
216 ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
217 ARG_setpathcost, ARG_setportprio,
218 ARG_setbridgeprio
219 )
220 IF_FEATURE_BRCTL_SHOW(, ARG_show)
221 };
222
223 argv++;
224 if (!*argv) {
225
226 bb_show_usage();
227 }
228
229 xchdir("/sys/class/net");
230
231
232 {
233 smallint key;
234 char *br;
235
236 key = index_in_strings(keywords, *argv);
237 if (key == -1)
238 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
239 argv++;
240
241#if ENABLE_FEATURE_BRCTL_SHOW
242 if (key == ARG_show) {
243 DIR *net;
244 struct dirent *ent;
245 int need_hdr = 1;
246 int exitcode = EXIT_SUCCESS;
247
248 if (*argv) {
249
250 do {
251 if (show_bridge(*argv, need_hdr) >= 0) {
252 need_hdr = 0;
253 } else {
254 bb_error_msg("bridge %s does not exist", *argv);
255
256
257 exitcode = EXIT_FAILURE;
258 }
259 } while (*++argv != NULL);
260 return exitcode;
261 }
262
263
264 net = xopendir(".");
265 while ((ent = readdir(net)) != NULL) {
266 if (DOT_OR_DOTDOT(ent->d_name))
267 continue;
268 if (show_bridge(ent->d_name, need_hdr) >= 0)
269 need_hdr = 0;
270 }
271 if (ENABLE_FEATURE_CLEAN_UP)
272 closedir(net);
273 return exitcode;
274 }
275#endif
276
277 if (!*argv)
278 bb_show_usage();
279
280 br = *argv++;
281
282 if (key == ARG_addbr || key == ARG_delbr) {
283
284
285
286
287 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
288 ioctl_or_perror_and_die(fd,
289 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
290 br, "bridge %s", br
291 );
292
293
294
295
296
297 if (ENABLE_FEATURE_CLEAN_UP)
298 close(fd);
299 return EXIT_SUCCESS;
300 }
301
302 if (!*argv)
303 bb_show_usage();
304
305#if ENABLE_FEATURE_BRCTL_FANCY
306 if (key == ARG_stp) {
307 static const char no_yes[] ALIGN1 =
308 "0\0" "off\0" "n\0" "no\0"
309 "1\0" "on\0" "y\0" "yes\0";
310 int onoff = index_in_strings(no_yes, *argv);
311 if (onoff < 0)
312 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
313 onoff = (unsigned)onoff / 4;
314 write_uint(br, "bridge/stp_state", onoff);
315
316 return EXIT_SUCCESS;
317 }
318
319 if ((unsigned)(key - ARG_setageing) < 4) {
320
321
322
323
324
325 write_uint(br,
326 nth_string(
327 "bridge/ageing_time" "\0"
328 "bridge/forward_delay""\0"
329 "bridge/hello_time" "\0"
330 "bridge/max_age",
331 key - ARG_setageing
332 ),
333 str_to_jiffies(*argv)
334 );
335
336 return EXIT_SUCCESS;
337 }
338
339 if (key == ARG_setbridgeprio) {
340 write_uint(br, "bridge/priority", xatoi_positive(*argv));
341
342 return EXIT_SUCCESS;
343 }
344
345 if (key == ARG_setpathcost
346 || key == ARG_setportprio
347 ) {
348 if (!argv[1])
349 bb_show_usage();
350
351
352
353
354
355
356 write_uint(argv[0],
357 nth_string(
358 "brport/path_cost" "\0"
359 "brport/priority",
360 key - ARG_setpathcost
361 ),
362 xatoi_positive(argv[1])
363 );
364
365
366 return EXIT_SUCCESS;
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385#endif
386 {
387
388 struct ifreq ifr;
389 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
390
391 strncpy_IFNAMSIZ(ifr.ifr_name, br);
392 ifr.ifr_ifindex = if_nametoindex(*argv);
393 if (ifr.ifr_ifindex == 0) {
394 bb_perror_msg_and_die("iface %s", *argv);
395 }
396 ioctl_or_perror_and_die(fd,
397 key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
398 &ifr, "bridge %s", br
399 );
400
401
402 if (ENABLE_FEATURE_CLEAN_UP)
403 close(fd);
404 return EXIT_SUCCESS;
405 }
406
407
408
409
410 }
411
412 return EXIT_SUCCESS;
413}
414