blob: 25640246d13b6a19d3b571c24f6689006f9c0dc7 [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 Vlasenko37a90082019-10-15 12:31:54 +0200160 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
161 sfx[-4] = 'f'; sfx[-3] = '\0';
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200162 tabs = 0;
163 ifaces = opendir(pathbuf);
164 if (ifaces) {
165 while ((ent = readdir(ifaces)) != NULL) {
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200166 if (DOT_OR_DOTDOT(ent->d_name))
167 continue; /* . or .. */
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200168 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) /* bridge has no interfaces */
177 bb_putchar('\n');
178 return 0;
179}
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200180#endif
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000181
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200182#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenko94356082019-04-13 14:17:55 +0200183static void write_uint(const char *name, const char *leaf, unsigned val)
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200184{
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);
Denys Vlasenko94356082019-04-13 14:17:55 +0200194 n = sprintf(filedata, "%u\n", val);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200195 if (write(fd, filedata, n) < 0)
196 bb_simple_perror_msg_and_die(name);
Denys Vlasenko33987532019-10-12 19:24:38 +0200197 /* So far all callers exit very soon after calling us.
198 * Do not bother closing fd (unless debugging):
199 */
200 if (ENABLE_FEATURE_CLEAN_UP)
201 close(fd);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200202}
Martin Lewis6c1af282019-09-15 18:04:49 +0200203
204struct fdb_entry {
205 uint8_t mac_addr[6];
206 uint8_t port_no;
207 uint8_t is_local;
208 uint32_t ageing_timer_value;
209 uint8_t port_hi;
210 uint8_t pad0;
211 uint16_t unused;
212};
213
214static int compare_fdbs(const void *_f0, const void *_f1)
215{
216 const struct fdb_entry *f0 = _f0;
217 const struct fdb_entry *f1 = _f1;
218
219 return memcmp(f0->mac_addr, f1->mac_addr, 6);
220}
221
222static size_t read_bridge_forward_db(const char *name, struct fdb_entry **_fdb)
223{
Denys Vlasenko33987532019-10-12 19:24:38 +0200224 char pathbuf[IFNAMSIZ + sizeof("/brforward") + 8];
Martin Lewis6c1af282019-09-15 18:04:49 +0200225 struct fdb_entry *fdb;
226 size_t nentries;
Martin Lewis6c1af282019-09-15 18:04:49 +0200227 int fd;
228 ssize_t cc;
229
Denys Vlasenko33987532019-10-12 19:24:38 +0200230#if IFNAMSIZ == 16
231 sprintf(pathbuf, "%.16s/brforward", name);
232#else
233 sprintf(pathbuf, "%.*s/brforward", (int)IFNAMSIZ, name);
234#endif
235 fd = open(pathbuf, O_RDONLY);
Martin Lewis6c1af282019-09-15 18:04:49 +0200236 if (fd < 0)
237 bb_error_msg_and_die("bridge %s does not exist", name);
238
239 fdb = NULL;
240 nentries = 0;
241 for (;;) {
242 fdb = xrealloc_vector(fdb, 4, nentries);
243 cc = full_read(fd, &fdb[nentries], sizeof(*fdb));
244 if (cc == 0) {
245 break;
246 }
247 if (cc != sizeof(*fdb)) {
248 bb_perror_msg_and_die("can't read bridge %s forward db", name);
249 }
250 ++nentries;
251 }
252
Denys Vlasenko33987532019-10-12 19:24:38 +0200253 if (ENABLE_FEATURE_CLEAN_UP)
254 close(fd);
Martin Lewis6c1af282019-09-15 18:04:49 +0200255
256 qsort(fdb, nentries, sizeof(*fdb), compare_fdbs);
257
258 *_fdb = fdb;
259 return nentries;
260}
261
262static void show_bridge_macs(const char *name)
263{
264 struct fdb_entry *fdb;
265 size_t nentries;
266 size_t i;
267
268 nentries = read_bridge_forward_db(name, &fdb);
269
270 printf("port no\tmac addr\t\tis local?\tageing timer\n");
271 for (i = 0; i < nentries; ++i) {
272 const struct fdb_entry *f = &fdb[i];
Denys Vlasenko33987532019-10-12 19:24:38 +0200273 unsigned tv_sec = f->ageing_timer_value / 100;
274 unsigned tv_csec = f->ageing_timer_value % 100;
Martin Lewis6c1af282019-09-15 18:04:49 +0200275 printf("%3u\t"
276 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t"
277 "%s\t\t"
278 "%4u.%.2u\n",
279 f->port_no,
280 f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
281 f->mac_addr[3], f->mac_addr[4], f->mac_addr[5],
282 (f->is_local ? "yes" : "no"),
Denys Vlasenko33987532019-10-12 19:24:38 +0200283 tv_sec, tv_csec
Martin Lewis6c1af282019-09-15 18:04:49 +0200284 );
285 }
286
Denys Vlasenko33987532019-10-12 19:24:38 +0200287 if (ENABLE_FEATURE_CLEAN_UP)
288 free(fdb);
Martin Lewis6c1af282019-09-15 18:04:49 +0200289}
Martin Lewis6dcf5632019-10-10 16:00:19 -0500290
291static void show_bridge_timer(const char *msg)
292{
Denys Vlasenko33987532019-10-12 19:24:38 +0200293 unsigned long long centisec = xstrtoull(filedata, 0);
294 unsigned tv_sec = centisec / 100;
295 unsigned tv_csec = centisec % 100;
296 printf("%s%4u.%.2u", msg, tv_sec, tv_csec);
Martin Lewis6dcf5632019-10-10 16:00:19 -0500297}
298
299static const char *show_bridge_state(unsigned state)
300{
301 /* See linux/if_bridge.h, BR_STATE_ constants */
Denys Vlasenko33987532019-10-12 19:24:38 +0200302 static const char state_names[] ALIGN1 =
Martin Lewis6dcf5632019-10-10 16:00:19 -0500303 "disabled\0" //BR_STATE_DISABLED 0
304 "listening\0" //BR_STATE_LISTENING 1
305 "learning\0" //BR_STATE_LEARNING 2
306 "forwarding\0" //BR_STATE_FORWARDING 3
307 "blocking" //BR_STATE_BLOCKING 4
308 ;
309 if (state < 5)
310 return nth_string(state_names, state);
311 return utoa(state);
312}
313
314static void printf_xstrtou(const char *fmt)
315{
316 printf(fmt, xstrtou(filedata, 0));
317}
318
319static void show_bridge_port(const char *name)
320{
Denys Vlasenko33987532019-10-12 19:24:38 +0200321 char pathbuf[IFNAMSIZ + sizeof("/brport/forward_delay_timer") + 8];
Martin Lewis6dcf5632019-10-10 16:00:19 -0500322 char *sfx;
323
324#if IFNAMSIZ == 16
325 sfx = pathbuf + sprintf(pathbuf, "%.16s/brport/", name);
326#else
327 sfx = pathbuf + sprintf(pathbuf, "%.*s/brport/", (int)IFNAMSIZ, name);
328#endif
329
330 strcpy(sfx, "port_no");
331 read_file(pathbuf);
332 printf("%s (%u)\n", name, xstrtou(filedata, 0));
333
334 strcpy(sfx + 5, "id"); // "port_id"
335 read_file(pathbuf);
336 printf_xstrtou(" port id\t\t%.4x");
337
338 strcpy(sfx, "state");
339 read_file(pathbuf);
340 printf("\t\t\tstate\t\t%15s\n", show_bridge_state(xstrtou(filedata, 0)));
341
342 strcpy(sfx, "designated_root");
343 read_file(pathbuf);
344 printf(" designated root\t%s", filedata);
345
346 strcpy(sfx, "path_cost");
347 read_file(pathbuf);
348 printf_xstrtou("\tpath cost\t\t%4u\n");
349
350 strcpy(sfx, "designated_bridge");
351 read_file(pathbuf);
352 printf(" designated bridge\t%s", filedata);
353
354 strcpy(sfx, "message_age_timer");
355 read_file(pathbuf);
356 show_bridge_timer("\tmessage age timer\t");
357
358 strcpy(sfx, "designated_port");
359 read_file(pathbuf);
360 printf_xstrtou("\n designated port\t%.4x");
361
362 strcpy(sfx, "forward_delay_timer");
363 read_file(pathbuf);
364 show_bridge_timer("\t\t\tforward delay timer\t");
365
366 strcpy(sfx, "designated_cost");
367 read_file(pathbuf);
368 printf_xstrtou("\n designated cost\t%4u");
369
370 strcpy(sfx, "hold_timer");
371 read_file(pathbuf);
372 show_bridge_timer("\t\t\thold timer\t\t");
373
374 printf("\n flags\t\t\t");
375
376 strcpy(sfx, "config_pending");
377 read_file(pathbuf);
378 if (!LONE_CHAR(filedata, '0'))
379 printf("CONFIG_PENDING ");
380
381 strcpy(sfx, "change_ack");
382 read_file(pathbuf);
383 if (!LONE_CHAR(filedata, '0'))
384 printf("TOPOLOGY_CHANGE_ACK ");
385
386 strcpy(sfx, "hairpin_mode");
387 read_file(pathbuf);
388 if (!LONE_CHAR(filedata, '0'))
389 printf_xstrtou("\n hairpin mode\t\t%4u");
390
391 printf("\n\n");
392}
393
Martin Lewis6dcf5632019-10-10 16:00:19 -0500394static void show_bridge_stp(const char *name)
395{
Denys Vlasenko33987532019-10-12 19:24:38 +0200396 char pathbuf[IFNAMSIZ + sizeof("/bridge/topology_change_timer") + 8];
Martin Lewis6dcf5632019-10-10 16:00:19 -0500397 char *sfx;
398
399#if IFNAMSIZ == 16
400 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
401#else
402 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
403#endif
404
405 strcpy(sfx, "bridge_id");
406 if (read_file(pathbuf) < 0)
407 bb_error_msg_and_die("bridge %s does not exist", name);
408
409 printf("%s\n"
410 " bridge id\t\t%s", name, filedata);
411
412 strcpy(sfx, "root_id");
413 read_file(pathbuf);
414 printf("\n designated root\t%s", filedata);
415
416 strcpy(sfx + 5, "port"); // "root_port"
417 read_file(pathbuf);
418 printf_xstrtou("\n root port\t\t%4u\t\t\t");
419
420 strcpy(sfx + 6, "ath_cost"); // "root_path_cost"
421 read_file(pathbuf);
422 printf_xstrtou("path cost\t\t%4u\n");
423
424 strcpy(sfx, "max_age");
425 read_file(pathbuf);
426 show_bridge_timer(" max age\t\t");
427 show_bridge_timer("\t\t\tbridge max age\t\t");
428
429 strcpy(sfx, "hello_time");
430 read_file(pathbuf);
431 show_bridge_timer("\n hello time\t\t");
432 show_bridge_timer("\t\t\tbridge hello time\t");
433
434 strcpy(sfx, "forward_delay");
435 read_file(pathbuf);
436 show_bridge_timer("\n forward delay\t\t");
437 show_bridge_timer("\t\t\tbridge forward delay\t");
438
439 strcpy(sfx, "ageing_time");
440 read_file(pathbuf);
441 show_bridge_timer("\n ageing time\t\t");
442
443 strcpy(sfx, "hello_timer");
444 read_file(pathbuf);
445 show_bridge_timer("\n hello timer\t\t");
446
447 strcpy(sfx, "tcn_timer");
448 read_file(pathbuf);
449 show_bridge_timer("\t\t\ttcn timer\t\t");
450
451 strcpy(sfx, "topology_change_timer");
452 read_file(pathbuf);
453 show_bridge_timer("\n topology change timer\t");
454
455 strcpy(sfx, "gc_timer");
456 read_file(pathbuf);
457 show_bridge_timer("\t\t\tgc timer\t\t");
458
459 printf("\n flags\t\t\t");
460
461 strcpy(sfx, "topology_change");
462 read_file(pathbuf);
463 if (!LONE_CHAR(filedata, '0'))
464 printf("TOPOLOGY_CHANGE ");
465
466 strcpy(sfx, "topology_change_detected");
467 read_file(pathbuf);
468 if (!LONE_CHAR(filedata, '0'))
469 printf("TOPOLOGY_CHANGE_DETECTED ");
470 printf("\n\n\n");
471
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200472 /* Show bridge ports */
473 {
474 DIR *ifaces;
475
Denys Vlasenko37a90082019-10-15 12:31:54 +0200476 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
477 sfx[-4] = 'f'; sfx[-3] = '\0';
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200478 ifaces = opendir(pathbuf);
479 if (ifaces) {
480 struct dirent *ent;
481 while ((ent = readdir(ifaces)) != NULL) {
482 if (DOT_OR_DOTDOT(ent->d_name))
483 continue; /* . or .. */
484 show_bridge_port(ent->d_name);
485 }
486 if (ENABLE_FEATURE_CLEAN_UP)
487 closedir(ifaces);
488 }
489 }
Martin Lewis6dcf5632019-10-10 16:00:19 -0500490}
Denys Vlasenko3e463e12019-06-08 12:35:06 +0200491#endif
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200492
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000493int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000494int brctl_main(int argc UNUSED_PARAM, char **argv)
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000495{
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000496 static const char keywords[] ALIGN1 =
497 "addbr\0" "delbr\0" "addif\0" "delif\0"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000498 IF_FEATURE_BRCTL_FANCY(
Bernhard Reutner-Fischer2b11fb42008-01-14 16:10:11 +0000499 "stp\0"
Martin Lewis6dcf5632019-10-10 16:00:19 -0500500 "showstp\0"
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000501 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200502 "setpathcost\0" "setportprio\0"
503 "setbridgeprio\0"
Martin Lewis6c1af282019-09-15 18:04:49 +0200504 "showmacs\0"
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000505 )
Nicolas Thillf47ce072012-09-25 14:06:01 +0200506 IF_FEATURE_BRCTL_SHOW("show\0");
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000507 enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000508 IF_FEATURE_BRCTL_FANCY(,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100509 ARG_stp,
Martin Lewis6dcf5632019-10-10 16:00:19 -0500510 ARG_showstp,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100511 ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200512 ARG_setpathcost, ARG_setportprio,
Martin Lewis6c1af282019-09-15 18:04:49 +0200513 ARG_setbridgeprio,
514 ARG_showmacs
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000515 )
Nicolas Thillf47ce072012-09-25 14:06:01 +0200516 IF_FEATURE_BRCTL_SHOW(, ARG_show)
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000517 };
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200518 int key;
519 char *br;
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000520
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000521 argv++;
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200522 if (!*argv) {
523 /* bare "brctl" shows --help */
524 bb_show_usage();
525 }
526
527 xchdir("/sys/class/net");
528
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200529 key = index_in_strings(keywords, *argv);
530 if (key == -1) /* no match found in keywords array, bail out. */
531 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
532 argv++;
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000533
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000534#if ENABLE_FEATURE_BRCTL_SHOW
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200535 if (key == ARG_show) { /* show [BR]... */
536 DIR *net;
537 struct dirent *ent;
538 int need_hdr = 1;
539 int exitcode = EXIT_SUCCESS;
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000540
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200541 if (*argv) {
542 /* "show BR1 BR2 BR3" */
543 do {
544 if (show_bridge(*argv, need_hdr) >= 0) {
545 need_hdr = 0;
546 } else {
547 bb_error_msg("bridge %s does not exist", *argv);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200548//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6
549//says this instead: "device eth0 is not a bridge"
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200550 exitcode = EXIT_FAILURE;
551 }
552 } while (*++argv != NULL);
Denys Vlasenkoc5150e92019-04-12 18:52:31 +0200553 return exitcode;
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000554 }
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200555
556 /* "show" (if no ifaces, shows nothing, not even header) */
557 net = xopendir(".");
558 while ((ent = readdir(net)) != NULL) {
559 if (DOT_OR_DOTDOT(ent->d_name))
560 continue; /* . or .. */
561 if (show_bridge(ent->d_name, need_hdr) >= 0)
562 need_hdr = 0;
563 }
564 if (ENABLE_FEATURE_CLEAN_UP)
565 closedir(net);
566 return exitcode;
567 }
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000568#endif
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000569
Denys Vlasenkoeb139512019-10-12 19:51:46 +0200570 if (!*argv) /* All of the below need at least one argument */
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200571 bb_show_usage();
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000572
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200573 br = *argv++;
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000574
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200575 if (key == ARG_addbr || key == ARG_delbr) {
576 /* brctl from bridge-utils 1.6 still uses ioctl
577 * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
578 */
579 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
580 ioctl_or_perror_and_die(fd,
581 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
582 br, "bridge %s", br
583 );
584 //close(fd);
585 //goto done;
586 /* bridge-utils 1.6 simply ignores trailing args:
587 * "brctl addbr BR1 ARGS" ignores ARGS
588 */
589 if (ENABLE_FEATURE_CLEAN_UP)
590 close(fd);
591 return EXIT_SUCCESS;
592 }
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000593
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200594 if (key == ARG_showmacs) {
595 show_bridge_macs(br);
596 return EXIT_SUCCESS;
597 }
598 if (key == ARG_showstp) {
599 show_bridge_stp(br);
600 return EXIT_SUCCESS;
601 }
Martin Lewis6c1af282019-09-15 18:04:49 +0200602
Denys Vlasenkoeb139512019-10-12 19:51:46 +0200603 if (!*argv) /* All of the below need at least two arguments */
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200604 bb_show_usage();
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000605
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000606#if ENABLE_FEATURE_BRCTL_FANCY
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200607 if (key == ARG_stp) {
608 static const char no_yes[] ALIGN1 =
609 "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
610 "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
611 int onoff = index_in_strings(no_yes, *argv);
612 if (onoff < 0)
613 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
614 onoff = (unsigned)onoff / 4;
615 write_uint(br, "bridge/stp_state", onoff);
616 return EXIT_SUCCESS;
617 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200618
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200619 if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
620 /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time
621 * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay
622 * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time
623 * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age
624 */
625 write_uint(br,
626 nth_string(
627 "bridge/ageing_time" "\0" /* ARG_setageing */
628 "bridge/forward_delay""\0" /* ARG_setfd */
629 "bridge/hello_time" "\0" /* ARG_sethello */
630 "bridge/max_age", /* ARG_setmaxage */
631 key - ARG_setageing
632 ),
633 str_to_jiffies(*argv)
634 );
635 return EXIT_SUCCESS;
636 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200637
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200638 if (key == ARG_setbridgeprio) {
639 write_uint(br, "bridge/priority", xatoi_positive(*argv));
640 return EXIT_SUCCESS;
641 }
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200642
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200643 if (key == ARG_setpathcost
644 || key == ARG_setportprio
645 ) {
646 if (!argv[1])
647 bb_show_usage();
648 /* BR is not used (and ignored!) for these commands:
649 * "setpathcost BR PORT N" writes "N\n" to
650 * /sys/class/net/PORT/brport/path_cost
651 * "setportprio BR PORT N" writes "N\n" to
652 * /sys/class/net/PORT/brport/priority
653 */
654 write_uint(argv[0],
655 nth_string(
656 "brport/path_cost" "\0" /* ARG_setpathcost */
657 "brport/priority", /* ARG_setportprio */
658 key - ARG_setpathcost
659 ),
660 xatoi_positive(argv[1])
661 );
662 return EXIT_SUCCESS;
663 }
Bernhard Reutner-Fischer1aac3ab2008-01-13 18:43:50 +0000664#endif
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200665 /* always true: if (key == ARG_addif || key == ARG_delif) */ {
666 struct ifreq ifr;
667 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200668
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200669 strncpy_IFNAMSIZ(ifr.ifr_name, br);
670 ifr.ifr_ifindex = if_nametoindex(*argv);
671 if (ifr.ifr_ifindex == 0) {
672 bb_perror_msg_and_die("iface %s", *argv);
Denys Vlasenkodc1b2d42019-04-13 13:58:06 +0200673 }
Denys Vlasenkob4fa16d2019-10-12 19:42:37 +0200674 ioctl_or_perror_and_die(fd,
675 key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
676 &ifr, "bridge %s", br
677 );
678 if (ENABLE_FEATURE_CLEAN_UP)
679 close(fd);
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000680 }
Denis Vlasenko278a1c22008-04-06 07:17:02 +0000681
Bernhard Reutner-Fischerd27d9252008-01-13 15:23:27 +0000682 return EXIT_SUCCESS;
683}