1 /* vi: set sw=4 ts=4: */
3 * Small implementation of brctl for busybox.
5 * Copyright (C) 2008 by Bernhard Reutner-Fischer
7 * Some helper functions from bridge-utils are
8 * Copyright (C) 2000 Lennert Buytenhek
10 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
13 //config: bool "brctl (4.7 kb)"
15 //config: select PLATFORM_LINUX
17 //config: Manage ethernet bridges.
18 //config: Supports addbr/delbr and addif/delif.
20 //config:config FEATURE_BRCTL_FANCY
21 //config: bool "Fancy options"
23 //config: depends on BRCTL
25 //config: Add support for extended option like:
26 //config: setageing, setfd, sethello, setmaxage,
27 //config: setpathcost, setportprio, setbridgeprio,
29 //config: This adds about 600 bytes.
31 //config:config FEATURE_BRCTL_SHOW
32 //config: bool "Support show"
34 //config: depends on BRCTL && FEATURE_BRCTL_FANCY
36 //config: Add support for option which prints the current config:
39 //applet:IF_BRCTL(APPLET_NOEXEC(brctl, brctl, BB_DIR_USR_SBIN, BB_SUID_DROP, brctl))
41 //kbuild:lib-$(CONFIG_BRCTL) += brctl.o
43 //usage:#define brctl_trivial_usage
44 //usage: "COMMAND [BRIDGE [ARGS]]"
45 //usage:#define brctl_full_usage "\n\n"
46 //usage: "Manage ethernet bridges"
47 //usage: "\nCommands:"
48 //usage: IF_FEATURE_BRCTL_SHOW(
49 //usage: "\n show [BRIDGE]... Show bridges"
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(
56 //usage: "\n showmacs BRIDGE List MAC addresses"
57 //usage: "\n showstp BRIDGE Show STP info"
58 //usage: "\n stp BRIDGE 1/yes/on|0/no/off Set STP on/off"
59 //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"
63 //usage: "\n setbridgeprio BRIDGE PRIO Set bridge priority"
64 //usage: "\n setportprio BRIDGE IFACE PRIO Set port priority"
65 //usage: "\n setpathcost BRIDGE IFACE COST Set path cost"
67 // Not yet implemented:
68 // hairpin BRIDGE IFACE on|off Set hairpin on/off
71 #include "common_bufsiz.h"
72 #include <linux/sockios.h>
76 # define SIOCBRADDBR BRCTL_ADD_BRIDGE
79 # define SIOCBRDELBR BRCTL_DEL_BRIDGE
82 # define SIOCBRADDIF BRCTL_ADD_IF
85 # define SIOCBRDELIF BRCTL_DEL_IF
88 #if ENABLE_FEATURE_BRCTL_FANCY
89 static unsigned str_to_jiffies(const char *time_str)
93 dd = /*bb_*/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");
98 /* For purposes of brctl,
99 * capping SECONDS by ~20 million seconds is quite enough:
108 #define filedata bb_common_bufsiz1
110 #if ENABLE_FEATURE_BRCTL_SHOW || ENABLE_FEATURE_BRCTL_FANCY
111 static int read_file(const char *name)
113 int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1);
118 if (n != 0 && filedata[n - 1] == '\n')
119 filedata[--n] = '\0';
125 #if ENABLE_FEATURE_BRCTL_SHOW
126 /* NB: we are in /sys/class/net
128 static int show_bridge(const char *name, int need_hdr)
131 *bridge name bridge id STP enabled interfaces
132 *br0 8000.000000000000 no eth0
134 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 8];
141 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
143 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
145 strcpy(sfx, "bridge_id");
146 if (read_file(pathbuf) < 0)
147 return -1; /* this iface is not a bridge */
150 puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
151 printf("%s\t\t%s\t", name, filedata);
153 strcpy(sfx, "stp_state");
155 if (LONE_CHAR(filedata, '0'))
156 strcpy(filedata, "no");
158 if (LONE_CHAR(filedata, '1'))
159 strcpy(filedata, "yes");
160 fputs(filedata, stdout);
162 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
163 sfx[-4] = 'f'; sfx[-3] = '\0';
165 ifaces = opendir(pathbuf);
167 while ((ent = readdir(ifaces)) != NULL) {
168 if (DOT_OR_DOTDOT(ent->d_name))
169 continue; /* . or .. */
171 printf("\t\t\t\t\t");
174 printf("\t\t%s\n", ent->d_name);
178 if (!tabs) /* bridge has no interfaces */
184 #if ENABLE_FEATURE_BRCTL_FANCY
185 static void write_uint(const char *name, const char *leaf, unsigned val)
187 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
191 sprintf(pathbuf, "%.16s/%s", name, leaf);
193 sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf);
195 fd = xopen(pathbuf, O_WRONLY);
196 n = sprintf(filedata, "%u\n", val);
197 if (write(fd, filedata, n) < 0)
198 bb_simple_perror_msg_and_die(name);
199 /* So far all callers exit very soon after calling us.
200 * Do not bother closing fd (unless debugging):
202 if (ENABLE_FEATURE_CLEAN_UP)
210 uint32_t ageing_timer_value;
216 static int compare_fdbs(const void *_f0, const void *_f1)
218 const struct fdb_entry *f0 = _f0;
219 const struct fdb_entry *f1 = _f1;
221 return memcmp(f0->mac_addr, f1->mac_addr, 6);
224 static size_t read_bridge_forward_db(const char *name, struct fdb_entry **_fdb)
226 char pathbuf[IFNAMSIZ + sizeof("/brforward") + 8];
227 struct fdb_entry *fdb;
233 sprintf(pathbuf, "%.16s/brforward", name);
235 sprintf(pathbuf, "%.*s/brforward", (int)IFNAMSIZ, name);
237 fd = open(pathbuf, O_RDONLY);
239 bb_error_msg_and_die("bridge %s does not exist", name);
244 fdb = xrealloc_vector(fdb, 4, nentries);
245 cc = full_read(fd, &fdb[nentries], sizeof(*fdb));
249 if (cc != sizeof(*fdb)) {
250 bb_perror_msg_and_die("can't read bridge %s forward db", name);
255 if (ENABLE_FEATURE_CLEAN_UP)
258 qsort(fdb, nentries, sizeof(*fdb), compare_fdbs);
264 static void show_bridge_macs(const char *name)
266 struct fdb_entry *fdb;
270 nentries = read_bridge_forward_db(name, &fdb);
272 printf("port no\tmac addr\t\tis local?\tageing timer\n");
273 for (i = 0; i < nentries; ++i) {
274 const struct fdb_entry *f = &fdb[i];
275 unsigned tv_sec = f->ageing_timer_value / 100;
276 unsigned tv_csec = f->ageing_timer_value % 100;
278 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t"
282 f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
283 f->mac_addr[3], f->mac_addr[4], f->mac_addr[5],
284 (f->is_local ? "yes" : "no"),
289 if (ENABLE_FEATURE_CLEAN_UP)
293 static void show_bridge_timer(const char *msg)
295 unsigned long long centisec = xstrtoull(filedata, 0);
296 unsigned tv_sec = centisec / 100;
297 unsigned tv_csec = centisec % 100;
298 printf("%s%4u.%.2u", msg, tv_sec, tv_csec);
301 static const char *show_bridge_state(unsigned state)
303 /* See linux/if_bridge.h, BR_STATE_ constants */
304 static const char state_names[] ALIGN1 =
305 "disabled\0" //BR_STATE_DISABLED 0
306 "listening\0" //BR_STATE_LISTENING 1
307 "learning\0" //BR_STATE_LEARNING 2
308 "forwarding\0" //BR_STATE_FORWARDING 3
309 "blocking" //BR_STATE_BLOCKING 4
312 return nth_string(state_names, state);
316 static void printf_xstrtou(const char *fmt)
318 printf(fmt, xstrtou(filedata, 0));
321 static void show_bridge_port(const char *name)
323 char pathbuf[IFNAMSIZ + sizeof("/brport/forward_delay_timer") + 8];
327 sfx = pathbuf + sprintf(pathbuf, "%.16s/brport/", name);
329 sfx = pathbuf + sprintf(pathbuf, "%.*s/brport/", (int)IFNAMSIZ, name);
332 strcpy(sfx, "port_no");
334 printf("%s (%u)\n", name, xstrtou(filedata, 0));
336 strcpy(sfx + 5, "id"); // "port_id"
338 printf_xstrtou(" port id\t\t%.4x");
340 strcpy(sfx, "state");
342 printf("\t\t\tstate\t\t%15s\n", show_bridge_state(xstrtou(filedata, 0)));
344 strcpy(sfx, "designated_root");
346 printf(" designated root\t%s", filedata);
348 strcpy(sfx, "path_cost");
350 printf_xstrtou("\tpath cost\t\t%4u\n");
352 strcpy(sfx, "designated_bridge");
354 printf(" designated bridge\t%s", filedata);
356 strcpy(sfx, "message_age_timer");
358 show_bridge_timer("\tmessage age timer\t");
360 strcpy(sfx, "designated_port");
362 printf_xstrtou("\n designated port\t%.4x");
364 strcpy(sfx, "forward_delay_timer");
366 show_bridge_timer("\t\t\tforward delay timer\t");
368 strcpy(sfx, "designated_cost");
370 printf_xstrtou("\n designated cost\t%4u");
372 strcpy(sfx, "hold_timer");
374 show_bridge_timer("\t\t\thold timer\t\t");
376 printf("\n flags\t\t\t");
378 strcpy(sfx, "config_pending");
380 if (!LONE_CHAR(filedata, '0'))
381 printf("CONFIG_PENDING ");
383 strcpy(sfx, "change_ack");
385 if (!LONE_CHAR(filedata, '0'))
386 printf("TOPOLOGY_CHANGE_ACK ");
388 strcpy(sfx, "hairpin_mode");
390 if (!LONE_CHAR(filedata, '0'))
391 printf_xstrtou("\n hairpin mode\t\t%4u");
396 static void show_bridge_stp(const char *name)
398 char pathbuf[IFNAMSIZ + sizeof("/bridge/topology_change_timer") + 8];
402 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
404 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
407 strcpy(sfx, "bridge_id");
408 if (read_file(pathbuf) < 0)
409 bb_error_msg_and_die("bridge %s does not exist", name);
412 " bridge id\t\t%s", name, filedata);
414 strcpy(sfx, "root_id");
416 printf("\n designated root\t%s", filedata);
418 strcpy(sfx + 5, "port"); // "root_port"
420 printf_xstrtou("\n root port\t\t%4u\t\t\t");
422 strcpy(sfx + 6, "ath_cost"); // "root_path_cost"
424 printf_xstrtou("path cost\t\t%4u\n");
426 strcpy(sfx, "max_age");
428 show_bridge_timer(" max age\t\t");
429 show_bridge_timer("\t\t\tbridge max age\t\t");
431 strcpy(sfx, "hello_time");
433 show_bridge_timer("\n hello time\t\t");
434 show_bridge_timer("\t\t\tbridge hello time\t");
436 strcpy(sfx, "forward_delay");
438 show_bridge_timer("\n forward delay\t\t");
439 show_bridge_timer("\t\t\tbridge forward delay\t");
441 strcpy(sfx, "ageing_time");
443 show_bridge_timer("\n ageing time\t\t");
445 strcpy(sfx, "hello_timer");
447 show_bridge_timer("\n hello timer\t\t");
449 strcpy(sfx, "tcn_timer");
451 show_bridge_timer("\t\t\ttcn timer\t\t");
453 strcpy(sfx, "topology_change_timer");
455 show_bridge_timer("\n topology change timer\t");
457 strcpy(sfx, "gc_timer");
459 show_bridge_timer("\t\t\tgc timer\t\t");
461 printf("\n flags\t\t\t");
463 strcpy(sfx, "topology_change");
465 if (!LONE_CHAR(filedata, '0'))
466 printf("TOPOLOGY_CHANGE ");
468 strcpy(sfx, "topology_change_detected");
470 if (!LONE_CHAR(filedata, '0'))
471 printf("TOPOLOGY_CHANGE_DETECTED ");
474 /* Show bridge ports */
478 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
479 sfx[-4] = 'f'; sfx[-3] = '\0';
480 ifaces = opendir(pathbuf);
483 while ((ent = readdir(ifaces)) != NULL) {
484 if (DOT_OR_DOTDOT(ent->d_name))
485 continue; /* . or .. */
486 show_bridge_port(ent->d_name);
488 if (ENABLE_FEATURE_CLEAN_UP)
495 int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
496 int brctl_main(int argc UNUSED_PARAM, char **argv)
498 static const char keywords[] ALIGN1 =
499 "addbr\0" "delbr\0" "addif\0" "delif\0"
500 IF_FEATURE_BRCTL_FANCY(
503 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
504 "setpathcost\0" "setportprio\0"
508 IF_FEATURE_BRCTL_SHOW("show\0");
509 enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
510 IF_FEATURE_BRCTL_FANCY(,
513 ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
514 ARG_setpathcost, ARG_setportprio,
518 IF_FEATURE_BRCTL_SHOW(, ARG_show)
525 /* bare "brctl" shows --help */
529 xchdir("/sys/class/net");
531 key = index_in_strings(keywords, *argv);
532 if (key == -1) /* no match found in keywords array, bail out. */
533 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
536 #if ENABLE_FEATURE_BRCTL_SHOW
537 if (key == ARG_show) { /* show [BR]... */
541 int exitcode = EXIT_SUCCESS;
544 /* "show BR1 BR2 BR3" */
546 if (show_bridge(*argv, need_hdr) >= 0) {
549 bb_error_msg("bridge %s does not exist", *argv);
550 //TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6
551 //says this instead: "device eth0 is not a bridge"
552 exitcode = EXIT_FAILURE;
554 } while (*++argv != NULL);
558 /* "show" (if no ifaces, shows nothing, not even header) */
560 while ((ent = readdir(net)) != NULL) {
561 if (DOT_OR_DOTDOT(ent->d_name))
562 continue; /* . or .. */
563 if (show_bridge(ent->d_name, need_hdr) >= 0)
566 if (ENABLE_FEATURE_CLEAN_UP)
572 if (!*argv) /* All of the below need at least one argument */
577 if (key == ARG_addbr || key == ARG_delbr) {
578 /* brctl from bridge-utils 1.6 still uses ioctl
579 * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
581 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
582 ioctl_or_perror_and_die(fd,
583 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
588 /* bridge-utils 1.6 simply ignores trailing args:
589 * "brctl addbr BR1 ARGS" ignores ARGS
591 if (ENABLE_FEATURE_CLEAN_UP)
596 #if ENABLE_FEATURE_BRCTL_FANCY
597 if (key == ARG_showmacs) {
598 show_bridge_macs(br);
601 if (key == ARG_showstp) {
607 if (!*argv) /* All of the below need at least two arguments */
610 #if ENABLE_FEATURE_BRCTL_FANCY
611 if (key == ARG_stp) {
612 static const char no_yes[] ALIGN1 =
613 "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
614 "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
615 int onoff = index_in_strings(no_yes, *argv);
617 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
618 onoff = (unsigned)onoff / 4;
619 write_uint(br, "bridge/stp_state", onoff);
623 if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
624 /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time
625 * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay
626 * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time
627 * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age
631 "bridge/ageing_time" "\0" /* ARG_setageing */
632 "bridge/forward_delay""\0" /* ARG_setfd */
633 "bridge/hello_time" "\0" /* ARG_sethello */
634 "bridge/max_age", /* ARG_setmaxage */
637 str_to_jiffies(*argv)
642 if (key == ARG_setbridgeprio) {
643 write_uint(br, "bridge/priority", xatoi_positive(*argv));
647 if (key == ARG_setpathcost
648 || key == ARG_setportprio
652 /* BR is not used (and ignored!) for these commands:
653 * "setpathcost BR PORT N" writes "N\n" to
654 * /sys/class/net/PORT/brport/path_cost
655 * "setportprio BR PORT N" writes "N\n" to
656 * /sys/class/net/PORT/brport/priority
660 "brport/path_cost" "\0" /* ARG_setpathcost */
661 "brport/priority", /* ARG_setportprio */
662 key - ARG_setpathcost
664 xatoi_positive(argv[1])
669 /* always true: if (key == ARG_addif || key == ARG_delif) */ {
671 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
673 strncpy_IFNAMSIZ(ifr.ifr_name, br);
674 ifr.ifr_ifindex = if_nametoindex(*argv);
675 if (ifr.ifr_ifindex == 0) {
676 bb_perror_msg_and_die("iface %s", *argv);
678 ioctl_or_perror_and_die(fd,
679 key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
680 &ifr, "bridge %s", br
682 if (ENABLE_FEATURE_CLEAN_UP)