blob: 8ed9d2096ccd97cee45999aca2e7f09af112d205 [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
15//config: select PLATFORM_LINUX
16//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020017//config: Manage ethernet bridges.
18//config: Supports addbr/delbr and addif/delif.
Denys Vlasenko47367e12016-11-23 09:05:14 +010019//config:
20//config:config FEATURE_BRCTL_FANCY
21//config: bool "Fancy options"
22//config: default y
23//config: depends on BRCTL
24//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020025//config: Add support for extended option like:
26//config: setageing, setfd, sethello, setmaxage,
27//config: setpathcost, setportprio, setbridgeprio,
28//config: stp
29//config: This adds about 600 bytes.
Denys Vlasenko47367e12016-11-23 09:05:14 +010030//config:
31//config:config FEATURE_BRCTL_SHOW
32//config: bool "Support show"
33//config: default y
34//config: depends on BRCTL && FEATURE_BRCTL_FANCY
35//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020036//config: Add support for option which prints the current config:
37//config: show
Denys Vlasenko47367e12016-11-23 09:05:14 +010038
Denys Vlasenko86e07f62017-08-06 20:14:02 +020039//applet:IF_BRCTL(APPLET_NOEXEC(brctl, brctl, BB_DIR_USR_SBIN, BB_SUID_DROP, brctl))
Denys Vlasenko47367e12016-11-23 09:05:14 +010040
41//kbuild:lib-$(CONFIG_BRCTL) += brctl.o
Pere Orga5bc8c002011-04-11 03:29:49 +020042
43//usage:#define brctl_trivial_usage
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020044//usage: "COMMAND [BRIDGE [ARGS]]"
Pere Orga5bc8c002011-04-11 03:29:49 +020045//usage:#define brctl_full_usage "\n\n"
Denys Vlasenko29458222019-04-13 15:48:31 +020046//usage: "Manage ethernet bridges"
Pere Orga5bc8c002011-04-11 03:29:49 +020047//usage: "\nCommands:"
48//usage: IF_FEATURE_BRCTL_SHOW(
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020049//usage: "\n show [BRIDGE]... Show bridges"
Pere Orga5bc8c002011-04-11 03:29:49 +020050//usage: )
51//usage: "\n addbr BRIDGE Create BRIDGE"
52//usage: "\n delbr BRIDGE Delete BRIDGE"
53//usage: "\n addif BRIDGE IFACE Add IFACE to BRIDGE"
54//usage: "\n delif BRIDGE IFACE Delete IFACE from BRIDGE"
55//usage: IF_FEATURE_BRCTL_FANCY(
Denys Vlasenkoeb139512019-10-12 19:51:46 +020056//usage: "\n showmacs BRIDGE List MAC addresses"
Denys Vlasenko33987532019-10-12 19:24:38 +020057//usage: "\n showstp BRIDGE Show STP info"
Denys Vlasenkoeb139512019-10-12 19:51:46 +020058//usage: "\n stp BRIDGE 1/yes/on|0/no/off Set STP on/off"
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020059//usage: "\n setageing BRIDGE SECONDS Set ageing time"
60//usage: "\n setfd BRIDGE SECONDS Set bridge forward delay"
61//usage: "\n sethello BRIDGE SECONDS Set hello time"
62//usage: "\n setmaxage BRIDGE SECONDS Set max message age"
Pere Orga5bc8c002011-04-11 03:29:49 +020063//usage: "\n setbridgeprio BRIDGE PRIO Set bridge priority"
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020064//usage: "\n setportprio BRIDGE IFACE PRIO Set port priority"
65//usage: "\n setpathcost BRIDGE IFACE COST Set path cost"
Pere Orga5bc8c002011-04-11 03:29:49 +020066//usage: )
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +020067// Not yet implemented:
Denys Vlasenkoeb139512019-10-12 19:51:46 +020068// hairpin BRIDGE IFACE on|off Set hairpin on/off
Martin Lewis6dcf5632019-10-10 16:00:19 -050069
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +000070#include "libbb.h"
Denys Vlasenkoc5150e92019-04-12 18:52:31 +020071#include "common_bufsiz.h"
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +000072#include <linux/sockios.h>
73#include <net/if.h>
74
Denis Vlasenko802cab12009-01-31 20:08:21 +000075#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
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +000088#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenko94356082019-04-13 14:17:55 +020089static unsigned str_to_jiffies(const char *time_str)
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +000090{
Denys Vlasenko94356082019-04-13 14:17:55 +020091 double dd;
Maciek Borzecki46abfc02010-03-16 12:41:29 +010092 char *endptr;
Denys Vlasenko94356082019-04-13 14:17:55 +020093 dd = /*bb_*/strtod(time_str, &endptr);
94 if (endptr == time_str || dd < 0)
Denys Vlasenko0f296a32015-10-14 13:21:01 +020095 bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec");
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +000096
Denys Vlasenko94356082019-04-13 14:17:55 +020097 dd *= 100;
98 /* For purposes of brctl,
99 * capping SECONDS by ~20 million seconds is quite enough:
100 */
101 if (dd > INT_MAX)
102 dd = INT_MAX;
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000103
Denys Vlasenko94356082019-04-13 14:17:55 +0200104 return dd;
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000105}
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000106#endif
107
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200108#define filedata bb_common_bufsiz1
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200109
Denys Vlasenko04a0b7f2019-06-08 12:58:16 +0200110#if ENABLE_FEATURE_BRCTL_SHOW
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200111static 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/* NB: we are in /sys/class/net
125 */
126static int show_bridge(const char *name, int need_hdr)
127{
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200128/* Output:
129 *bridge name bridge id STP enabled interfaces
130 *br0 8000.000000000000 no eth0
131 */
Denys Vlasenko33987532019-10-12 19:24:38 +0200132 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 8];
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200133 int tabs;
134 DIR *ifaces;
135 struct dirent *ent;
136 char *sfx;
137
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200138#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
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200143 strcpy(sfx, "bridge_id");
144 if (read_file(pathbuf) < 0)
145 return -1; /* this iface is not a bridge */
146
147 if (need_hdr)
148 puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
Martin Lewis6dcf5632019-10-10 16:00:19 -0500149 printf("%s\t\t%s\t", name, filedata);
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200150
151 strcpy(sfx, "stp_state");
152 read_file(pathbuf);
153 if (LONE_CHAR(filedata, '0'))
154 strcpy(filedata, "no");
155 else
156 if (LONE_CHAR(filedata, '1'))
157 strcpy(filedata, "yes");
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200158 fputs(filedata, stdout);
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200159
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200160 strcpy(sfx - (sizeof("bridge/")-1), "brif");
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200161 tabs = 0;
162 ifaces = opendir(pathbuf);
163 if (ifaces) {
164 while ((ent = readdir(ifaces)) != NULL) {
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200165 if (DOT_OR_DOTDOT(ent->d_name))
166 continue; /* . or .. */
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200167 if (tabs)
168 printf("\t\t\t\t\t");
169 else
170 tabs = 1;
171 printf("\t\t%s\n", ent->d_name);
172 }
173 closedir(ifaces);
174 }
175 if (!tabs) /* bridge has no interfaces */
176 bb_putchar('\n');
177 return 0;
178}
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200179#endif
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000180
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200181#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenko94356082019-04-13 14:17:55 +0200182static void write_uint(const char *name, const char *leaf, unsigned val)
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200183{
184 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
185 int fd, n;
186
187#if IFNAMSIZ == 16
188 sprintf(pathbuf, "%.16s/%s", name, leaf);
189#else
190 sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf);
191#endif
192 fd = xopen(pathbuf, O_WRONLY);
Denys Vlasenko94356082019-04-13 14:17:55 +0200193 n = sprintf(filedata, "%u\n", val);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200194 if (write(fd, filedata, n) < 0)
195 bb_simple_perror_msg_and_die(name);
Denys Vlasenko33987532019-10-12 19:24:38 +0200196 /* So far all callers exit very soon after calling us.
197 * Do not bother closing fd (unless debugging):
198 */
199 if (ENABLE_FEATURE_CLEAN_UP)
200 close(fd);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200201}
Martin Lewis6c1af282019-09-15 18:04:49 +0200202
203struct fdb_entry {
204 uint8_t mac_addr[6];
205 uint8_t port_no;
206 uint8_t is_local;
207 uint32_t ageing_timer_value;
208 uint8_t port_hi;
209 uint8_t pad0;
210 uint16_t unused;
211};
212
213static int compare_fdbs(const void *_f0, const void *_f1)
214{
215 const struct fdb_entry *f0 = _f0;
216 const struct fdb_entry *f1 = _f1;
217
218 return memcmp(f0->mac_addr, f1->mac_addr, 6);
219}
220
221static size_t read_bridge_forward_db(const char *name, struct fdb_entry **_fdb)
222{
Denys Vlasenko33987532019-10-12 19:24:38 +0200223 char pathbuf[IFNAMSIZ + sizeof("/brforward") + 8];
Martin Lewis6c1af282019-09-15 18:04:49 +0200224 struct fdb_entry *fdb;
225 size_t nentries;
Martin Lewis6c1af282019-09-15 18:04:49 +0200226 int fd;
227 ssize_t cc;
228
Denys Vlasenko33987532019-10-12 19:24:38 +0200229#if IFNAMSIZ == 16
230 sprintf(pathbuf, "%.16s/brforward", name);
231#else
232 sprintf(pathbuf, "%.*s/brforward", (int)IFNAMSIZ, name);
233#endif
234 fd = open(pathbuf, O_RDONLY);
Martin Lewis6c1af282019-09-15 18:04:49 +0200235 if (fd < 0)
236 bb_error_msg_and_die("bridge %s does not exist", name);
237
238 fdb = NULL;
239 nentries = 0;
240 for (;;) {
241 fdb = xrealloc_vector(fdb, 4, nentries);
242 cc = full_read(fd, &fdb[nentries], sizeof(*fdb));
243 if (cc == 0) {
244 break;
245 }
246 if (cc != sizeof(*fdb)) {
247 bb_perror_msg_and_die("can't read bridge %s forward db", name);
248 }
249 ++nentries;
250 }
251
Denys Vlasenko33987532019-10-12 19:24:38 +0200252 if (ENABLE_FEATURE_CLEAN_UP)
253 close(fd);
Martin Lewis6c1af282019-09-15 18:04:49 +0200254
255 qsort(fdb, nentries, sizeof(*fdb), compare_fdbs);
256
257 *_fdb = fdb;
258 return nentries;
259}
260
261static void show_bridge_macs(const char *name)
262{
263 struct fdb_entry *fdb;
264 size_t nentries;
265 size_t i;
266
267 nentries = read_bridge_forward_db(name, &fdb);
268
269 printf("port no\tmac addr\t\tis local?\tageing timer\n");
270 for (i = 0; i < nentries; ++i) {
271 const struct fdb_entry *f = &fdb[i];
Denys Vlasenko33987532019-10-12 19:24:38 +0200272 unsigned tv_sec = f->ageing_timer_value / 100;
273 unsigned tv_csec = f->ageing_timer_value % 100;
Martin Lewis6c1af282019-09-15 18:04:49 +0200274 printf("%3u\t"
275 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t"
276 "%s\t\t"
277 "%4u.%.2u\n",
278 f->port_no,
279 f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
280 f->mac_addr[3], f->mac_addr[4], f->mac_addr[5],
281 (f->is_local ? "yes" : "no"),
Denys Vlasenko33987532019-10-12 19:24:38 +0200282 tv_sec, tv_csec
Martin Lewis6c1af282019-09-15 18:04:49 +0200283 );
284 }
285
Denys Vlasenko33987532019-10-12 19:24:38 +0200286 if (ENABLE_FEATURE_CLEAN_UP)
287 free(fdb);
Martin Lewis6c1af282019-09-15 18:04:49 +0200288}
Martin Lewis6dcf5632019-10-10 16:00:19 -0500289
290static void show_bridge_timer(const char *msg)
291{
Denys Vlasenko33987532019-10-12 19:24:38 +0200292 unsigned long long centisec = xstrtoull(filedata, 0);
293 unsigned tv_sec = centisec / 100;
294 unsigned tv_csec = centisec % 100;
295 printf("%s%4u.%.2u", msg, tv_sec, tv_csec);
Martin Lewis6dcf5632019-10-10 16:00:19 -0500296}
297
298static const char *show_bridge_state(unsigned state)
299{
300 /* See linux/if_bridge.h, BR_STATE_ constants */
Denys Vlasenko33987532019-10-12 19:24:38 +0200301 static const char state_names[] ALIGN1 =
Martin Lewis6dcf5632019-10-10 16:00:19 -0500302 "disabled\0" //BR_STATE_DISABLED 0
303 "listening\0" //BR_STATE_LISTENING 1
304 "learning\0" //BR_STATE_LEARNING 2
305 "forwarding\0" //BR_STATE_FORWARDING 3
306 "blocking" //BR_STATE_BLOCKING 4
307 ;
308 if (state < 5)
309 return nth_string(state_names, state);
310 return utoa(state);
311}
312
313static void printf_xstrtou(const char *fmt)
314{
315 printf(fmt, xstrtou(filedata, 0));
316}
317
318static void show_bridge_port(const char *name)
319{
Denys Vlasenko33987532019-10-12 19:24:38 +0200320 char pathbuf[IFNAMSIZ + sizeof("/brport/forward_delay_timer") + 8];
Martin Lewis6dcf5632019-10-10 16:00:19 -0500321 char *sfx;
322
323#if IFNAMSIZ == 16
324 sfx = pathbuf + sprintf(pathbuf, "%.16s/brport/", name);
325#else
326 sfx = pathbuf + sprintf(pathbuf, "%.*s/brport/", (int)IFNAMSIZ, name);
327#endif
328
329 strcpy(sfx, "port_no");
330 read_file(pathbuf);
331 printf("%s (%u)\n", name, xstrtou(filedata, 0));
332
333 strcpy(sfx + 5, "id"); // "port_id"
334 read_file(pathbuf);
335 printf_xstrtou(" port id\t\t%.4x");
336
337 strcpy(sfx, "state");
338 read_file(pathbuf);
339 printf("\t\t\tstate\t\t%15s\n", show_bridge_state(xstrtou(filedata, 0)));
340
341 strcpy(sfx, "designated_root");
342 read_file(pathbuf);
343 printf(" designated root\t%s", filedata);
344
345 strcpy(sfx, "path_cost");
346 read_file(pathbuf);
347 printf_xstrtou("\tpath cost\t\t%4u\n");
348
349 strcpy(sfx, "designated_bridge");
350 read_file(pathbuf);
351 printf(" designated bridge\t%s", filedata);
352
353 strcpy(sfx, "message_age_timer");
354 read_file(pathbuf);
355 show_bridge_timer("\tmessage age timer\t");
356
357 strcpy(sfx, "designated_port");
358 read_file(pathbuf);
359 printf_xstrtou("\n designated port\t%.4x");
360
361 strcpy(sfx, "forward_delay_timer");
362 read_file(pathbuf);
363 show_bridge_timer("\t\t\tforward delay timer\t");
364
365 strcpy(sfx, "designated_cost");
366 read_file(pathbuf);
367 printf_xstrtou("\n designated cost\t%4u");
368
369 strcpy(sfx, "hold_timer");
370 read_file(pathbuf);
371 show_bridge_timer("\t\t\thold timer\t\t");
372
373 printf("\n flags\t\t\t");
374
375 strcpy(sfx, "config_pending");
376 read_file(pathbuf);
377 if (!LONE_CHAR(filedata, '0'))
378 printf("CONFIG_PENDING ");
379
380 strcpy(sfx, "change_ack");
381 read_file(pathbuf);
382 if (!LONE_CHAR(filedata, '0'))
383 printf("TOPOLOGY_CHANGE_ACK ");
384
385 strcpy(sfx, "hairpin_mode");
386 read_file(pathbuf);
387 if (!LONE_CHAR(filedata, '0'))
388 printf_xstrtou("\n hairpin mode\t\t%4u");
389
390 printf("\n\n");
391}
392
Martin Lewis6dcf5632019-10-10 16:00:19 -0500393static void show_bridge_stp(const char *name)
394{
Denys Vlasenko33987532019-10-12 19:24:38 +0200395 char pathbuf[IFNAMSIZ + sizeof("/bridge/topology_change_timer") + 8];
Martin Lewis6dcf5632019-10-10 16:00:19 -0500396 char *sfx;
397
398#if IFNAMSIZ == 16
399 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
400#else
401 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
402#endif
403
404 strcpy(sfx, "bridge_id");
405 if (read_file(pathbuf) < 0)
406 bb_error_msg_and_die("bridge %s does not exist", name);
407
408 printf("%s\n"
409 " bridge id\t\t%s", name, filedata);
410
411 strcpy(sfx, "root_id");
412 read_file(pathbuf);
413 printf("\n designated root\t%s", filedata);
414
415 strcpy(sfx + 5, "port"); // "root_port"
416 read_file(pathbuf);
417 printf_xstrtou("\n root port\t\t%4u\t\t\t");
418
419 strcpy(sfx + 6, "ath_cost"); // "root_path_cost"
420 read_file(pathbuf);
421 printf_xstrtou("path cost\t\t%4u\n");
422
423 strcpy(sfx, "max_age");
424 read_file(pathbuf);
425 show_bridge_timer(" max age\t\t");
426 show_bridge_timer("\t\t\tbridge max age\t\t");
427
428 strcpy(sfx, "hello_time");
429 read_file(pathbuf);
430 show_bridge_timer("\n hello time\t\t");
431 show_bridge_timer("\t\t\tbridge hello time\t");
432
433 strcpy(sfx, "forward_delay");
434 read_file(pathbuf);
435 show_bridge_timer("\n forward delay\t\t");
436 show_bridge_timer("\t\t\tbridge forward delay\t");
437
438 strcpy(sfx, "ageing_time");
439 read_file(pathbuf);
440 show_bridge_timer("\n ageing time\t\t");
441
442 strcpy(sfx, "hello_timer");
443 read_file(pathbuf);
444 show_bridge_timer("\n hello timer\t\t");
445
446 strcpy(sfx, "tcn_timer");
447 read_file(pathbuf);
448 show_bridge_timer("\t\t\ttcn timer\t\t");
449
450 strcpy(sfx, "topology_change_timer");
451 read_file(pathbuf);
452 show_bridge_timer("\n topology change timer\t");
453
454 strcpy(sfx, "gc_timer");
455 read_file(pathbuf);
456 show_bridge_timer("\t\t\tgc timer\t\t");
457
458 printf("\n flags\t\t\t");
459
460 strcpy(sfx, "topology_change");
461 read_file(pathbuf);
462 if (!LONE_CHAR(filedata, '0'))
463 printf("TOPOLOGY_CHANGE ");
464
465 strcpy(sfx, "topology_change_detected");
466 read_file(pathbuf);
467 if (!LONE_CHAR(filedata, '0'))
468 printf("TOPOLOGY_CHANGE_DETECTED ");
469 printf("\n\n\n");
470
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200471 /* Show bridge ports */
472 {
473 DIR *ifaces;
474
475 /* sfx points past "BR/bridge/", turn it to "BR/brif": */
476 strcpy(sfx - 4, "f");
477 ifaces = opendir(pathbuf);
478 if (ifaces) {
479 struct dirent *ent;
480 while ((ent = readdir(ifaces)) != NULL) {
481 if (DOT_OR_DOTDOT(ent->d_name))
482 continue; /* . or .. */
483 show_bridge_port(ent->d_name);
484 }
485 if (ENABLE_FEATURE_CLEAN_UP)
486 closedir(ifaces);
487 }
488 }
Martin Lewis6dcf5632019-10-10 16:00:19 -0500489}
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200490#endif
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200491
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000492int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000493int brctl_main(int argc UNUSED_PARAM, char **argv)
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000494{
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000495 static const char keywords[] ALIGN1 =
496 "addbr\0" "delbr\0" "addif\0" "delif\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000497 IF_FEATURE_BRCTL_FANCY(
Bernhard Reutner-Fischer2b11fb42008-01-14 16:10:11 +0000498 "stp\0"
Martin Lewis6dcf5632019-10-10 16:00:19 -0500499 "showstp\0"
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000500 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200501 "setpathcost\0" "setportprio\0"
502 "setbridgeprio\0"
Martin Lewis6c1af282019-09-15 18:04:49 +0200503 "showmacs\0"
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000504 )
Nicolas Thillf47ce072012-09-25 14:06:01 +0200505 IF_FEATURE_BRCTL_SHOW("show\0");
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000506 enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000507 IF_FEATURE_BRCTL_FANCY(,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100508 ARG_stp,
Martin Lewis6dcf5632019-10-10 16:00:19 -0500509 ARG_showstp,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100510 ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200511 ARG_setpathcost, ARG_setportprio,
Martin Lewis6c1af282019-09-15 18:04:49 +0200512 ARG_setbridgeprio,
513 ARG_showmacs
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000514 )
Nicolas Thillf47ce072012-09-25 14:06:01 +0200515 IF_FEATURE_BRCTL_SHOW(, ARG_show)
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000516 };
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200517 int key;
518 char *br;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000519
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000520 argv++;
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200521 if (!*argv) {
522 /* bare "brctl" shows --help */
523 bb_show_usage();
524 }
525
526 xchdir("/sys/class/net");
527
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200528 key = index_in_strings(keywords, *argv);
529 if (key == -1) /* no match found in keywords array, bail out. */
530 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
531 argv++;
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000532
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000533#if ENABLE_FEATURE_BRCTL_SHOW
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200534 if (key == ARG_show) { /* show [BR]... */
535 DIR *net;
536 struct dirent *ent;
537 int need_hdr = 1;
538 int exitcode = EXIT_SUCCESS;
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000539
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200540 if (*argv) {
541 /* "show BR1 BR2 BR3" */
542 do {
543 if (show_bridge(*argv, need_hdr) >= 0) {
544 need_hdr = 0;
545 } else {
546 bb_error_msg("bridge %s does not exist", *argv);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200547//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6
548//says this instead: "device eth0 is not a bridge"
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200549 exitcode = EXIT_FAILURE;
550 }
551 } while (*++argv != NULL);
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200552 return exitcode;
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000553 }
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200554
555 /* "show" (if no ifaces, shows nothing, not even header) */
556 net = xopendir(".");
557 while ((ent = readdir(net)) != NULL) {
558 if (DOT_OR_DOTDOT(ent->d_name))
559 continue; /* . or .. */
560 if (show_bridge(ent->d_name, need_hdr) >= 0)
561 need_hdr = 0;
562 }
563 if (ENABLE_FEATURE_CLEAN_UP)
564 closedir(net);
565 return exitcode;
566 }
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000567#endif
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000568
Denys Vlasenkoeb139512019-10-12 19:51:46 +0200569 if (!*argv) /* All of the below need at least one argument */
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200570 bb_show_usage();
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000571
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200572 br = *argv++;
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000573
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200574 if (key == ARG_addbr || key == ARG_delbr) {
575 /* brctl from bridge-utils 1.6 still uses ioctl
576 * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
577 */
578 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
579 ioctl_or_perror_and_die(fd,
580 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
581 br, "bridge %s", br
582 );
583 //close(fd);
584 //goto done;
585 /* bridge-utils 1.6 simply ignores trailing args:
586 * "brctl addbr BR1 ARGS" ignores ARGS
587 */
588 if (ENABLE_FEATURE_CLEAN_UP)
589 close(fd);
590 return EXIT_SUCCESS;
591 }
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000592
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200593 if (key == ARG_showmacs) {
594 show_bridge_macs(br);
595 return EXIT_SUCCESS;
596 }
597 if (key == ARG_showstp) {
598 show_bridge_stp(br);
599 return EXIT_SUCCESS;
600 }
Martin Lewis6c1af282019-09-15 18:04:49 +0200601
Denys Vlasenkoeb139512019-10-12 19:51:46 +0200602 if (!*argv) /* All of the below need at least two arguments */
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200603 bb_show_usage();
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000604
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000605#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200606 if (key == ARG_stp) {
607 static const char no_yes[] ALIGN1 =
608 "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
609 "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
610 int onoff = index_in_strings(no_yes, *argv);
611 if (onoff < 0)
612 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
613 onoff = (unsigned)onoff / 4;
614 write_uint(br, "bridge/stp_state", onoff);
615 return EXIT_SUCCESS;
616 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200617
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200618 if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
619 /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time
620 * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay
621 * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time
622 * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age
623 */
624 write_uint(br,
625 nth_string(
626 "bridge/ageing_time" "\0" /* ARG_setageing */
627 "bridge/forward_delay""\0" /* ARG_setfd */
628 "bridge/hello_time" "\0" /* ARG_sethello */
629 "bridge/max_age", /* ARG_setmaxage */
630 key - ARG_setageing
631 ),
632 str_to_jiffies(*argv)
633 );
634 return EXIT_SUCCESS;
635 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200636
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200637 if (key == ARG_setbridgeprio) {
638 write_uint(br, "bridge/priority", xatoi_positive(*argv));
639 return EXIT_SUCCESS;
640 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200641
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200642 if (key == ARG_setpathcost
643 || key == ARG_setportprio
644 ) {
645 if (!argv[1])
646 bb_show_usage();
647 /* BR is not used (and ignored!) for these commands:
648 * "setpathcost BR PORT N" writes "N\n" to
649 * /sys/class/net/PORT/brport/path_cost
650 * "setportprio BR PORT N" writes "N\n" to
651 * /sys/class/net/PORT/brport/priority
652 */
653 write_uint(argv[0],
654 nth_string(
655 "brport/path_cost" "\0" /* ARG_setpathcost */
656 "brport/priority", /* ARG_setportprio */
657 key - ARG_setpathcost
658 ),
659 xatoi_positive(argv[1])
660 );
661 return EXIT_SUCCESS;
662 }
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000663#endif
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200664 /* always true: if (key == ARG_addif || key == ARG_delif) */ {
665 struct ifreq ifr;
666 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200667
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200668 strncpy_IFNAMSIZ(ifr.ifr_name, br);
669 ifr.ifr_ifindex = if_nametoindex(*argv);
670 if (ifr.ifr_ifindex == 0) {
671 bb_perror_msg_and_die("iface %s", *argv);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200672 }
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200673 ioctl_or_perror_and_die(fd,
674 key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
675 &ifr, "bridge %s", br
676 );
677 if (ENABLE_FEATURE_CLEAN_UP)
678 close(fd);
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000679 }
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000680
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000681 return EXIT_SUCCESS;
682}