blob: f057f9b60957ecf4504f2a62f03b739bcb63d767 [file] [log] [blame]
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Small implementation of brctl for busybox.
4 *
Bernhard Reutner-Fischer6c4dade2008-09-25 12:13:34 +00005 * Copyright (C) 2008 by Bernhard Reutner-Fischer
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +00006 *
Bernhard Reutner-Fischer2b11fb42008-01-14 16:10:11 +00007 * Some helper functions from bridge-utils are
8 * Copyright (C) 2000 Lennert Buytenhek
9 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020010 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +000011 */
Denys Vlasenko47367e12016-11-23 09:05:14 +010012//config:config BRCTL
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020013//config: bool "brctl (4.7 kb)"
Denys Vlasenko47367e12016-11-23 09:05:14 +010014//config: default y
Denys Vlasenko47367e12016-11-23 09:05:14 +010015//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020016//config: Manage ethernet bridges.
17//config: Supports addbr/delbr and addif/delif.
Denys Vlasenko47367e12016-11-23 09:05:14 +010018//config:
19//config:config FEATURE_BRCTL_FANCY
20//config: bool "Fancy options"
21//config: default y
22//config: depends on BRCTL
23//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020024//config: Add support for extended option like:
25//config: setageing, setfd, sethello, setmaxage,
26//config: setpathcost, setportprio, setbridgeprio,
27//config: stp
28//config: This adds about 600 bytes.
Denys Vlasenko47367e12016-11-23 09:05:14 +010029//config:
30//config:config FEATURE_BRCTL_SHOW
31//config: bool "Support show"
32//config: default y
33//config: depends on BRCTL && FEATURE_BRCTL_FANCY
34//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020035//config: Add support for option which prints the current config:
36//config: show
Denys Vlasenko47367e12016-11-23 09:05:14 +010037
Denys Vlasenko86e07f62017-08-06 20:14:02 +020038//applet:IF_BRCTL(APPLET_NOEXEC(brctl, brctl, BB_DIR_USR_SBIN, BB_SUID_DROP, brctl))
Denys Vlasenko47367e12016-11-23 09:05:14 +010039
40//kbuild:lib-$(CONFIG_BRCTL) += brctl.o
Pere Orga5bc8c002011-04-11 03:29:49 +020041
42//usage:#define brctl_trivial_usage
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020043//usage: "COMMAND [BRIDGE [ARGS]]"
Pere Orga5bc8c002011-04-11 03:29:49 +020044//usage:#define brctl_full_usage "\n\n"
Denys Vlasenko29458222019-04-13 15:48:31 +020045//usage: "Manage ethernet bridges"
Pere Orga5bc8c002011-04-11 03:29:49 +020046//usage: "\nCommands:"
47//usage: IF_FEATURE_BRCTL_SHOW(
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020048//usage: "\n show [BRIDGE]... Show bridges"
Pere Orga5bc8c002011-04-11 03:29:49 +020049//usage: )
50//usage: "\n addbr BRIDGE Create BRIDGE"
51//usage: "\n delbr BRIDGE Delete BRIDGE"
52//usage: "\n addif BRIDGE IFACE Add IFACE to BRIDGE"
53//usage: "\n delif BRIDGE IFACE Delete IFACE from BRIDGE"
54//usage: IF_FEATURE_BRCTL_FANCY(
Denys Vlasenkoeb139512019-10-12 19:51:46 +020055//usage: "\n showmacs BRIDGE List MAC addresses"
Denys Vlasenko33987532019-10-12 19:24:38 +020056//usage: "\n showstp BRIDGE Show STP info"
Denys Vlasenkoeb139512019-10-12 19:51:46 +020057//usage: "\n stp BRIDGE 1/yes/on|0/no/off Set STP on/off"
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020058//usage: "\n setageing BRIDGE SECONDS Set ageing time"
59//usage: "\n setfd BRIDGE SECONDS Set bridge forward delay"
60//usage: "\n sethello BRIDGE SECONDS Set hello time"
61//usage: "\n setmaxage BRIDGE SECONDS Set max message age"
Pere Orga5bc8c002011-04-11 03:29:49 +020062//usage: "\n setbridgeprio BRIDGE PRIO Set bridge priority"
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020063//usage: "\n setportprio BRIDGE IFACE PRIO Set port priority"
64//usage: "\n setpathcost BRIDGE IFACE COST Set path cost"
Pere Orga5bc8c002011-04-11 03:29:49 +020065//usage: )
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020066// Not yet implemented:
Denys Vlasenkoeb139512019-10-12 19:51:46 +020067// hairpin BRIDGE IFACE on|off Set hairpin on/off
Martin Lewis6dcf5632019-10-10 16:00:19 -050068
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +000069#include "libbb.h"
Denys Vlasenkoc5150e92019-04-12 18:52:31 +020070#include "common_bufsiz.h"
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +000071#include <linux/sockios.h>
72#include <net/if.h>
73
Denis Vlasenko802cab12009-01-31 20:08:21 +000074#ifndef SIOCBRADDBR
75# define SIOCBRADDBR BRCTL_ADD_BRIDGE
76#endif
77#ifndef SIOCBRDELBR
78# define SIOCBRDELBR BRCTL_DEL_BRIDGE
79#endif
80#ifndef SIOCBRADDIF
81# define SIOCBRADDIF BRCTL_ADD_IF
82#endif
83#ifndef SIOCBRDELIF
84# define SIOCBRDELIF BRCTL_DEL_IF
85#endif
86
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +000087#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenko94356082019-04-13 14:17:55 +020088static unsigned str_to_jiffies(const char *time_str)
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +000089{
Denys Vlasenko94356082019-04-13 14:17:55 +020090 double dd;
Maciek Borzecki46abfc02010-03-16 12:41:29 +010091 char *endptr;
Denys Vlasenko94356082019-04-13 14:17:55 +020092 dd = /*bb_*/strtod(time_str, &endptr);
93 if (endptr == time_str || dd < 0)
Denys Vlasenko0f296a32015-10-14 13:21:01 +020094 bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec");
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +000095
Denys Vlasenko94356082019-04-13 14:17:55 +020096 dd *= 100;
97 /* For purposes of brctl,
98 * capping SECONDS by ~20 million seconds is quite enough:
99 */
100 if (dd > INT_MAX)
101 dd = INT_MAX;
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000102
Denys Vlasenko94356082019-04-13 14:17:55 +0200103 return dd;
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000104}
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000105#endif
106
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200107#define filedata bb_common_bufsiz1
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200108
Denys Vlasenkodf1f4792020-06-23 03:13:55 +0200109#if ENABLE_FEATURE_BRCTL_SHOW || ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200110static int read_file(const char *name)
111{
112 int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1);
113 if (n < 0) {
114 filedata[0] = '\0';
115 } else {
116 filedata[n] = '\0';
117 if (n != 0 && filedata[n - 1] == '\n')
118 filedata[--n] = '\0';
119 }
120 return n;
121}
Denys Vlasenkodf1f4792020-06-23 03:13:55 +0200122#endif
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200123
Denys Vlasenkodf1f4792020-06-23 03:13:55 +0200124#if ENABLE_FEATURE_BRCTL_SHOW
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200125/* NB: we are in /sys/class/net
126 */
127static int show_bridge(const char *name, int need_hdr)
128{
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200129/* Output:
130 *bridge name bridge id STP enabled interfaces
131 *br0 8000.000000000000 no eth0
132 */
Denys Vlasenko33987532019-10-12 19:24:38 +0200133 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 8];
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200134 int tabs;
135 DIR *ifaces;
136 struct dirent *ent;
137 char *sfx;
138
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200139#if IFNAMSIZ == 16
140 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
141#else
142 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
143#endif
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200144 strcpy(sfx, "bridge_id");
145 if (read_file(pathbuf) < 0)
146 return -1; /* this iface is not a bridge */
147
148 if (need_hdr)
149 puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
Martin Lewis6dcf5632019-10-10 16:00:19 -0500150 printf("%s\t\t%s\t", name, filedata);
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200151
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");
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200159 fputs(filedata, stdout);
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200160
Denys Vlasenko37a90082019-10-15 12:31:54 +0200161 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
162 sfx[-4] = 'f'; sfx[-3] = '\0';
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200163 tabs = 0;
164 ifaces = opendir(pathbuf);
165 if (ifaces) {
166 while ((ent = readdir(ifaces)) != NULL) {
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200167 if (DOT_OR_DOTDOT(ent->d_name))
168 continue; /* . or .. */
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200169 if (tabs)
170 printf("\t\t\t\t\t");
171 else
172 tabs = 1;
173 printf("\t\t%s\n", ent->d_name);
174 }
175 closedir(ifaces);
176 }
177 if (!tabs) /* bridge has no interfaces */
178 bb_putchar('\n');
179 return 0;
180}
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200181#endif
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000182
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200183#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenko94356082019-04-13 14:17:55 +0200184static void write_uint(const char *name, const char *leaf, unsigned val)
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200185{
186 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
187 int fd, n;
188
189#if IFNAMSIZ == 16
190 sprintf(pathbuf, "%.16s/%s", name, leaf);
191#else
192 sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf);
193#endif
194 fd = xopen(pathbuf, O_WRONLY);
Denys Vlasenko94356082019-04-13 14:17:55 +0200195 n = sprintf(filedata, "%u\n", val);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200196 if (write(fd, filedata, n) < 0)
197 bb_simple_perror_msg_and_die(name);
Denys Vlasenko33987532019-10-12 19:24:38 +0200198 /* So far all callers exit very soon after calling us.
199 * Do not bother closing fd (unless debugging):
200 */
201 if (ENABLE_FEATURE_CLEAN_UP)
202 close(fd);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200203}
Martin Lewis6c1af282019-09-15 18:04:49 +0200204
205struct fdb_entry {
206 uint8_t mac_addr[6];
207 uint8_t port_no;
208 uint8_t is_local;
209 uint32_t ageing_timer_value;
210 uint8_t port_hi;
211 uint8_t pad0;
212 uint16_t unused;
213};
214
215static int compare_fdbs(const void *_f0, const void *_f1)
216{
217 const struct fdb_entry *f0 = _f0;
218 const struct fdb_entry *f1 = _f1;
219
220 return memcmp(f0->mac_addr, f1->mac_addr, 6);
221}
222
223static size_t read_bridge_forward_db(const char *name, struct fdb_entry **_fdb)
224{
Denys Vlasenko33987532019-10-12 19:24:38 +0200225 char pathbuf[IFNAMSIZ + sizeof("/brforward") + 8];
Martin Lewis6c1af282019-09-15 18:04:49 +0200226 struct fdb_entry *fdb;
227 size_t nentries;
Martin Lewis6c1af282019-09-15 18:04:49 +0200228 int fd;
229 ssize_t cc;
230
Denys Vlasenko33987532019-10-12 19:24:38 +0200231#if IFNAMSIZ == 16
232 sprintf(pathbuf, "%.16s/brforward", name);
233#else
234 sprintf(pathbuf, "%.*s/brforward", (int)IFNAMSIZ, name);
235#endif
236 fd = open(pathbuf, O_RDONLY);
Martin Lewis6c1af282019-09-15 18:04:49 +0200237 if (fd < 0)
238 bb_error_msg_and_die("bridge %s does not exist", name);
239
240 fdb = NULL;
241 nentries = 0;
242 for (;;) {
243 fdb = xrealloc_vector(fdb, 4, nentries);
244 cc = full_read(fd, &fdb[nentries], sizeof(*fdb));
245 if (cc == 0) {
246 break;
247 }
248 if (cc != sizeof(*fdb)) {
249 bb_perror_msg_and_die("can't read bridge %s forward db", name);
250 }
251 ++nentries;
252 }
253
Denys Vlasenko33987532019-10-12 19:24:38 +0200254 if (ENABLE_FEATURE_CLEAN_UP)
255 close(fd);
Martin Lewis6c1af282019-09-15 18:04:49 +0200256
257 qsort(fdb, nentries, sizeof(*fdb), compare_fdbs);
258
259 *_fdb = fdb;
260 return nentries;
261}
262
263static void show_bridge_macs(const char *name)
264{
265 struct fdb_entry *fdb;
266 size_t nentries;
267 size_t i;
268
269 nentries = read_bridge_forward_db(name, &fdb);
270
271 printf("port no\tmac addr\t\tis local?\tageing timer\n");
272 for (i = 0; i < nentries; ++i) {
273 const struct fdb_entry *f = &fdb[i];
Denys Vlasenko33987532019-10-12 19:24:38 +0200274 unsigned tv_sec = f->ageing_timer_value / 100;
275 unsigned tv_csec = f->ageing_timer_value % 100;
Martin Lewis6c1af282019-09-15 18:04:49 +0200276 printf("%3u\t"
277 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t"
278 "%s\t\t"
279 "%4u.%.2u\n",
280 f->port_no,
281 f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
282 f->mac_addr[3], f->mac_addr[4], f->mac_addr[5],
283 (f->is_local ? "yes" : "no"),
Denys Vlasenko33987532019-10-12 19:24:38 +0200284 tv_sec, tv_csec
Martin Lewis6c1af282019-09-15 18:04:49 +0200285 );
286 }
287
Denys Vlasenko33987532019-10-12 19:24:38 +0200288 if (ENABLE_FEATURE_CLEAN_UP)
289 free(fdb);
Martin Lewis6c1af282019-09-15 18:04:49 +0200290}
Martin Lewis6dcf5632019-10-10 16:00:19 -0500291
292static void show_bridge_timer(const char *msg)
293{
Denys Vlasenko33987532019-10-12 19:24:38 +0200294 unsigned long long centisec = xstrtoull(filedata, 0);
295 unsigned tv_sec = centisec / 100;
296 unsigned tv_csec = centisec % 100;
297 printf("%s%4u.%.2u", msg, tv_sec, tv_csec);
Martin Lewis6dcf5632019-10-10 16:00:19 -0500298}
299
300static const char *show_bridge_state(unsigned state)
301{
302 /* See linux/if_bridge.h, BR_STATE_ constants */
Denys Vlasenko33987532019-10-12 19:24:38 +0200303 static const char state_names[] ALIGN1 =
Martin Lewis6dcf5632019-10-10 16:00:19 -0500304 "disabled\0" //BR_STATE_DISABLED 0
305 "listening\0" //BR_STATE_LISTENING 1
306 "learning\0" //BR_STATE_LEARNING 2
307 "forwarding\0" //BR_STATE_FORWARDING 3
308 "blocking" //BR_STATE_BLOCKING 4
309 ;
310 if (state < 5)
311 return nth_string(state_names, state);
312 return utoa(state);
313}
314
315static void printf_xstrtou(const char *fmt)
316{
317 printf(fmt, xstrtou(filedata, 0));
318}
319
320static void show_bridge_port(const char *name)
321{
Denys Vlasenko33987532019-10-12 19:24:38 +0200322 char pathbuf[IFNAMSIZ + sizeof("/brport/forward_delay_timer") + 8];
Martin Lewis6dcf5632019-10-10 16:00:19 -0500323 char *sfx;
324
325#if IFNAMSIZ == 16
326 sfx = pathbuf + sprintf(pathbuf, "%.16s/brport/", name);
327#else
328 sfx = pathbuf + sprintf(pathbuf, "%.*s/brport/", (int)IFNAMSIZ, name);
329#endif
330
331 strcpy(sfx, "port_no");
332 read_file(pathbuf);
333 printf("%s (%u)\n", name, xstrtou(filedata, 0));
334
335 strcpy(sfx + 5, "id"); // "port_id"
336 read_file(pathbuf);
337 printf_xstrtou(" port id\t\t%.4x");
338
339 strcpy(sfx, "state");
340 read_file(pathbuf);
341 printf("\t\t\tstate\t\t%15s\n", show_bridge_state(xstrtou(filedata, 0)));
342
343 strcpy(sfx, "designated_root");
344 read_file(pathbuf);
345 printf(" designated root\t%s", filedata);
346
347 strcpy(sfx, "path_cost");
348 read_file(pathbuf);
349 printf_xstrtou("\tpath cost\t\t%4u\n");
350
351 strcpy(sfx, "designated_bridge");
352 read_file(pathbuf);
353 printf(" designated bridge\t%s", filedata);
354
355 strcpy(sfx, "message_age_timer");
356 read_file(pathbuf);
357 show_bridge_timer("\tmessage age timer\t");
358
359 strcpy(sfx, "designated_port");
360 read_file(pathbuf);
361 printf_xstrtou("\n designated port\t%.4x");
362
363 strcpy(sfx, "forward_delay_timer");
364 read_file(pathbuf);
365 show_bridge_timer("\t\t\tforward delay timer\t");
366
367 strcpy(sfx, "designated_cost");
368 read_file(pathbuf);
369 printf_xstrtou("\n designated cost\t%4u");
370
371 strcpy(sfx, "hold_timer");
372 read_file(pathbuf);
373 show_bridge_timer("\t\t\thold timer\t\t");
374
375 printf("\n flags\t\t\t");
376
377 strcpy(sfx, "config_pending");
378 read_file(pathbuf);
379 if (!LONE_CHAR(filedata, '0'))
380 printf("CONFIG_PENDING ");
381
382 strcpy(sfx, "change_ack");
383 read_file(pathbuf);
384 if (!LONE_CHAR(filedata, '0'))
385 printf("TOPOLOGY_CHANGE_ACK ");
386
387 strcpy(sfx, "hairpin_mode");
388 read_file(pathbuf);
389 if (!LONE_CHAR(filedata, '0'))
390 printf_xstrtou("\n hairpin mode\t\t%4u");
391
392 printf("\n\n");
393}
394
Martin Lewis6dcf5632019-10-10 16:00:19 -0500395static void show_bridge_stp(const char *name)
396{
Denys Vlasenko33987532019-10-12 19:24:38 +0200397 char pathbuf[IFNAMSIZ + sizeof("/bridge/topology_change_timer") + 8];
Martin Lewis6dcf5632019-10-10 16:00:19 -0500398 char *sfx;
399
400#if IFNAMSIZ == 16
401 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
402#else
403 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
404#endif
405
406 strcpy(sfx, "bridge_id");
407 if (read_file(pathbuf) < 0)
408 bb_error_msg_and_die("bridge %s does not exist", name);
409
410 printf("%s\n"
411 " bridge id\t\t%s", name, filedata);
412
413 strcpy(sfx, "root_id");
414 read_file(pathbuf);
415 printf("\n designated root\t%s", filedata);
416
417 strcpy(sfx + 5, "port"); // "root_port"
418 read_file(pathbuf);
419 printf_xstrtou("\n root port\t\t%4u\t\t\t");
420
421 strcpy(sfx + 6, "ath_cost"); // "root_path_cost"
422 read_file(pathbuf);
423 printf_xstrtou("path cost\t\t%4u\n");
424
425 strcpy(sfx, "max_age");
426 read_file(pathbuf);
427 show_bridge_timer(" max age\t\t");
428 show_bridge_timer("\t\t\tbridge max age\t\t");
429
430 strcpy(sfx, "hello_time");
431 read_file(pathbuf);
432 show_bridge_timer("\n hello time\t\t");
433 show_bridge_timer("\t\t\tbridge hello time\t");
434
435 strcpy(sfx, "forward_delay");
436 read_file(pathbuf);
437 show_bridge_timer("\n forward delay\t\t");
438 show_bridge_timer("\t\t\tbridge forward delay\t");
439
440 strcpy(sfx, "ageing_time");
441 read_file(pathbuf);
442 show_bridge_timer("\n ageing time\t\t");
443
444 strcpy(sfx, "hello_timer");
445 read_file(pathbuf);
446 show_bridge_timer("\n hello timer\t\t");
447
448 strcpy(sfx, "tcn_timer");
449 read_file(pathbuf);
450 show_bridge_timer("\t\t\ttcn timer\t\t");
451
452 strcpy(sfx, "topology_change_timer");
453 read_file(pathbuf);
454 show_bridge_timer("\n topology change timer\t");
455
456 strcpy(sfx, "gc_timer");
457 read_file(pathbuf);
458 show_bridge_timer("\t\t\tgc timer\t\t");
459
460 printf("\n flags\t\t\t");
461
462 strcpy(sfx, "topology_change");
463 read_file(pathbuf);
464 if (!LONE_CHAR(filedata, '0'))
465 printf("TOPOLOGY_CHANGE ");
466
467 strcpy(sfx, "topology_change_detected");
468 read_file(pathbuf);
469 if (!LONE_CHAR(filedata, '0'))
470 printf("TOPOLOGY_CHANGE_DETECTED ");
471 printf("\n\n\n");
472
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200473 /* Show bridge ports */
474 {
475 DIR *ifaces;
476
Denys Vlasenko37a90082019-10-15 12:31:54 +0200477 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
478 sfx[-4] = 'f'; sfx[-3] = '\0';
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200479 ifaces = opendir(pathbuf);
480 if (ifaces) {
481 struct dirent *ent;
482 while ((ent = readdir(ifaces)) != NULL) {
483 if (DOT_OR_DOTDOT(ent->d_name))
484 continue; /* . or .. */
485 show_bridge_port(ent->d_name);
486 }
487 if (ENABLE_FEATURE_CLEAN_UP)
488 closedir(ifaces);
489 }
490 }
Martin Lewis6dcf5632019-10-10 16:00:19 -0500491}
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200492#endif
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200493
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000494int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000495int brctl_main(int argc UNUSED_PARAM, char **argv)
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000496{
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000497 static const char keywords[] ALIGN1 =
498 "addbr\0" "delbr\0" "addif\0" "delif\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000499 IF_FEATURE_BRCTL_FANCY(
Bernhard Reutner-Fischer2b11fb42008-01-14 16:10:11 +0000500 "stp\0"
Martin Lewis6dcf5632019-10-10 16:00:19 -0500501 "showstp\0"
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000502 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200503 "setpathcost\0" "setportprio\0"
504 "setbridgeprio\0"
Martin Lewis6c1af282019-09-15 18:04:49 +0200505 "showmacs\0"
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000506 )
Nicolas Thillf47ce072012-09-25 14:06:01 +0200507 IF_FEATURE_BRCTL_SHOW("show\0");
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000508 enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000509 IF_FEATURE_BRCTL_FANCY(,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100510 ARG_stp,
Martin Lewis6dcf5632019-10-10 16:00:19 -0500511 ARG_showstp,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100512 ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200513 ARG_setpathcost, ARG_setportprio,
Martin Lewis6c1af282019-09-15 18:04:49 +0200514 ARG_setbridgeprio,
515 ARG_showmacs
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000516 )
Nicolas Thillf47ce072012-09-25 14:06:01 +0200517 IF_FEATURE_BRCTL_SHOW(, ARG_show)
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000518 };
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200519 int key;
520 char *br;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000521
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000522 argv++;
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200523 if (!*argv) {
524 /* bare "brctl" shows --help */
525 bb_show_usage();
526 }
527
528 xchdir("/sys/class/net");
529
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200530 key = index_in_strings(keywords, *argv);
531 if (key == -1) /* no match found in keywords array, bail out. */
532 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
533 argv++;
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000534
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000535#if ENABLE_FEATURE_BRCTL_SHOW
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200536 if (key == ARG_show) { /* show [BR]... */
537 DIR *net;
538 struct dirent *ent;
539 int need_hdr = 1;
540 int exitcode = EXIT_SUCCESS;
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000541
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200542 if (*argv) {
543 /* "show BR1 BR2 BR3" */
544 do {
545 if (show_bridge(*argv, need_hdr) >= 0) {
546 need_hdr = 0;
547 } else {
548 bb_error_msg("bridge %s does not exist", *argv);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200549//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6
550//says this instead: "device eth0 is not a bridge"
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200551 exitcode = EXIT_FAILURE;
552 }
553 } while (*++argv != NULL);
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200554 return exitcode;
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000555 }
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200556
557 /* "show" (if no ifaces, shows nothing, not even header) */
558 net = xopendir(".");
559 while ((ent = readdir(net)) != NULL) {
560 if (DOT_OR_DOTDOT(ent->d_name))
561 continue; /* . or .. */
562 if (show_bridge(ent->d_name, need_hdr) >= 0)
563 need_hdr = 0;
564 }
565 if (ENABLE_FEATURE_CLEAN_UP)
566 closedir(net);
567 return exitcode;
568 }
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000569#endif
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000570
Denys Vlasenkoeb139512019-10-12 19:51:46 +0200571 if (!*argv) /* All of the below need at least one argument */
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200572 bb_show_usage();
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000573
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200574 br = *argv++;
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000575
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200576 if (key == ARG_addbr || key == ARG_delbr) {
577 /* brctl from bridge-utils 1.6 still uses ioctl
578 * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
579 */
580 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
581 ioctl_or_perror_and_die(fd,
582 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
583 br, "bridge %s", br
584 );
585 //close(fd);
586 //goto done;
587 /* bridge-utils 1.6 simply ignores trailing args:
588 * "brctl addbr BR1 ARGS" ignores ARGS
589 */
590 if (ENABLE_FEATURE_CLEAN_UP)
591 close(fd);
592 return EXIT_SUCCESS;
593 }
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000594
Denys Vlasenko6eb38fd2020-06-23 02:46:53 +0200595#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200596 if (key == ARG_showmacs) {
597 show_bridge_macs(br);
598 return EXIT_SUCCESS;
599 }
600 if (key == ARG_showstp) {
601 show_bridge_stp(br);
602 return EXIT_SUCCESS;
603 }
Denys Vlasenko6eb38fd2020-06-23 02:46:53 +0200604#endif
Martin Lewis6c1af282019-09-15 18:04:49 +0200605
Denys Vlasenkoeb139512019-10-12 19:51:46 +0200606 if (!*argv) /* All of the below need at least two arguments */
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200607 bb_show_usage();
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000608
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000609#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200610 if (key == ARG_stp) {
611 static const char no_yes[] ALIGN1 =
612 "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
613 "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
614 int onoff = index_in_strings(no_yes, *argv);
615 if (onoff < 0)
616 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
617 onoff = (unsigned)onoff / 4;
618 write_uint(br, "bridge/stp_state", onoff);
619 return EXIT_SUCCESS;
620 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200621
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200622 if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
623 /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time
624 * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay
625 * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time
626 * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age
627 */
628 write_uint(br,
629 nth_string(
630 "bridge/ageing_time" "\0" /* ARG_setageing */
631 "bridge/forward_delay""\0" /* ARG_setfd */
632 "bridge/hello_time" "\0" /* ARG_sethello */
633 "bridge/max_age", /* ARG_setmaxage */
634 key - ARG_setageing
635 ),
636 str_to_jiffies(*argv)
637 );
638 return EXIT_SUCCESS;
639 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200640
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200641 if (key == ARG_setbridgeprio) {
642 write_uint(br, "bridge/priority", xatoi_positive(*argv));
643 return EXIT_SUCCESS;
644 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200645
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200646 if (key == ARG_setpathcost
647 || key == ARG_setportprio
648 ) {
649 if (!argv[1])
650 bb_show_usage();
651 /* BR is not used (and ignored!) for these commands:
652 * "setpathcost BR PORT N" writes "N\n" to
653 * /sys/class/net/PORT/brport/path_cost
654 * "setportprio BR PORT N" writes "N\n" to
655 * /sys/class/net/PORT/brport/priority
656 */
657 write_uint(argv[0],
658 nth_string(
659 "brport/path_cost" "\0" /* ARG_setpathcost */
660 "brport/priority", /* ARG_setportprio */
661 key - ARG_setpathcost
662 ),
663 xatoi_positive(argv[1])
664 );
665 return EXIT_SUCCESS;
666 }
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000667#endif
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200668 /* always true: if (key == ARG_addif || key == ARG_delif) */ {
669 struct ifreq ifr;
670 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200671
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200672 strncpy_IFNAMSIZ(ifr.ifr_name, br);
673 ifr.ifr_ifindex = if_nametoindex(*argv);
674 if (ifr.ifr_ifindex == 0) {
675 bb_perror_msg_and_die("iface %s", *argv);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200676 }
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200677 ioctl_or_perror_and_die(fd,
678 key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
679 &ifr, "bridge %s", br
680 );
681 if (ENABLE_FEATURE_CLEAN_UP)
682 close(fd);
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000683 }
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000684
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000685 return EXIT_SUCCESS;
686}