brctl.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Small implementation of brctl for busybox.
  4. *
  5. * Copyright (C) 2008 by Bernhard Reutner-Fischer
  6. *
  7. * Some helper functions from bridge-utils are
  8. * Copyright (C) 2000 Lennert Buytenhek
  9. *
  10. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  11. */
  12. /* This applet currently uses only the ioctl interface and no sysfs at all.
  13. * At the time of this writing this was considered a feature.
  14. */
  15. //config:config BRCTL
  16. //config: bool "brctl"
  17. //config: default y
  18. //config: select PLATFORM_LINUX
  19. //config: help
  20. //config: Manage ethernet bridges.
  21. //config: Supports addbr/delbr and addif/delif.
  22. //config:
  23. //config:config FEATURE_BRCTL_FANCY
  24. //config: bool "Fancy options"
  25. //config: default y
  26. //config: depends on BRCTL
  27. //config: help
  28. //config: Add support for extended option like:
  29. //config: setageing, setfd, sethello, setmaxage,
  30. //config: setpathcost, setportprio, setbridgeprio,
  31. //config: stp
  32. //config: This adds about 600 bytes.
  33. //config:
  34. //config:config FEATURE_BRCTL_SHOW
  35. //config: bool "Support show"
  36. //config: default y
  37. //config: depends on BRCTL && FEATURE_BRCTL_FANCY
  38. //config: help
  39. //config: Add support for option which prints the current config:
  40. //config: show
  41. //applet:IF_BRCTL(APPLET(brctl, BB_DIR_USR_SBIN, BB_SUID_DROP))
  42. //kbuild:lib-$(CONFIG_BRCTL) += brctl.o
  43. //usage:#define brctl_trivial_usage
  44. //usage: "COMMAND [BRIDGE [INTERFACE]]"
  45. //usage:#define brctl_full_usage "\n\n"
  46. //usage: "Manage ethernet bridges\n"
  47. //usage: "\nCommands:"
  48. //usage: IF_FEATURE_BRCTL_SHOW(
  49. //usage: "\n show Show a list of bridges"
  50. //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(
  56. //usage: "\n setageing BRIDGE TIME Set ageing time"
  57. //usage: "\n setfd BRIDGE TIME Set bridge forward delay"
  58. //usage: "\n sethello BRIDGE TIME Set hello time"
  59. //usage: "\n setmaxage BRIDGE TIME Set max message age"
  60. //usage: "\n setpathcost BRIDGE COST Set path cost"
  61. //usage: "\n setportprio BRIDGE PRIO Set port priority"
  62. //usage: "\n setbridgeprio BRIDGE PRIO Set bridge priority"
  63. //usage: "\n stp BRIDGE [1/yes/on|0/no/off] STP on/off"
  64. //usage: )
  65. #include "libbb.h"
  66. #include <linux/sockios.h>
  67. #include <net/if.h>
  68. #ifndef SIOCBRADDBR
  69. # define SIOCBRADDBR BRCTL_ADD_BRIDGE
  70. #endif
  71. #ifndef SIOCBRDELBR
  72. # define SIOCBRDELBR BRCTL_DEL_BRIDGE
  73. #endif
  74. #ifndef SIOCBRADDIF
  75. # define SIOCBRADDIF BRCTL_ADD_IF
  76. #endif
  77. #ifndef SIOCBRDELIF
  78. # define SIOCBRDELIF BRCTL_DEL_IF
  79. #endif
  80. /* Maximum number of ports supported per bridge interface. */
  81. #ifndef MAX_PORTS
  82. # define MAX_PORTS 32
  83. #endif
  84. /* Use internal number parsing and not the "exact" conversion. */
  85. /* #define BRCTL_USE_INTERNAL 0 */ /* use exact conversion */
  86. #define BRCTL_USE_INTERNAL 1
  87. #if ENABLE_FEATURE_BRCTL_FANCY
  88. /* #include <linux/if_bridge.h>
  89. * breaks on musl: we already included netinet/in.h in libbb.h,
  90. * if we include <linux/if_bridge.h> here, we get this:
  91. * In file included from /usr/include/linux/if_bridge.h:18,
  92. * from networking/brctl.c:67:
  93. * /usr/include/linux/in6.h:32: error: redefinition of 'struct in6_addr'
  94. * /usr/include/linux/in6.h:49: error: redefinition of 'struct sockaddr_in6'
  95. * /usr/include/linux/in6.h:59: error: redefinition of 'struct ipv6_mreq'
  96. */
  97. /* From <linux/if_bridge.h> */
  98. #define BRCTL_GET_VERSION 0
  99. #define BRCTL_GET_BRIDGES 1
  100. #define BRCTL_ADD_BRIDGE 2
  101. #define BRCTL_DEL_BRIDGE 3
  102. #define BRCTL_ADD_IF 4
  103. #define BRCTL_DEL_IF 5
  104. #define BRCTL_GET_BRIDGE_INFO 6
  105. #define BRCTL_GET_PORT_LIST 7
  106. #define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
  107. #define BRCTL_SET_BRIDGE_HELLO_TIME 9
  108. #define BRCTL_SET_BRIDGE_MAX_AGE 10
  109. #define BRCTL_SET_AGEING_TIME 11
  110. #define BRCTL_SET_GC_INTERVAL 12
  111. #define BRCTL_GET_PORT_INFO 13
  112. #define BRCTL_SET_BRIDGE_STP_STATE 14
  113. #define BRCTL_SET_BRIDGE_PRIORITY 15
  114. #define BRCTL_SET_PORT_PRIORITY 16
  115. #define BRCTL_SET_PATH_COST 17
  116. #define BRCTL_GET_FDB_ENTRIES 18
  117. struct __bridge_info {
  118. uint64_t designated_root;
  119. uint64_t bridge_id;
  120. uint32_t root_path_cost;
  121. uint32_t max_age;
  122. uint32_t hello_time;
  123. uint32_t forward_delay;
  124. uint32_t bridge_max_age;
  125. uint32_t bridge_hello_time;
  126. uint32_t bridge_forward_delay;
  127. uint8_t topology_change;
  128. uint8_t topology_change_detected;
  129. uint8_t root_port;
  130. uint8_t stp_enabled;
  131. uint32_t ageing_time;
  132. uint32_t gc_interval;
  133. uint32_t hello_timer_value;
  134. uint32_t tcn_timer_value;
  135. uint32_t topology_change_timer_value;
  136. uint32_t gc_timer_value;
  137. };
  138. /* end <linux/if_bridge.h> */
  139. /* FIXME: These 4 funcs are not really clean and could be improved */
  140. static ALWAYS_INLINE void bb_strtotimeval(struct timeval *tv,
  141. const char *time_str)
  142. {
  143. double secs;
  144. # if BRCTL_USE_INTERNAL
  145. char *endptr;
  146. secs = /*bb_*/strtod(time_str, &endptr);
  147. if (endptr == time_str)
  148. # else
  149. if (sscanf(time_str, "%lf", &secs) != 1)
  150. # endif
  151. bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec");
  152. tv->tv_sec = secs;
  153. tv->tv_usec = 1000000 * (secs - tv->tv_sec);
  154. }
  155. static ALWAYS_INLINE unsigned long tv_to_jiffies(const struct timeval *tv)
  156. {
  157. unsigned long long jif;
  158. jif = 1000000ULL * tv->tv_sec + tv->tv_usec;
  159. return jif/10000;
  160. }
  161. # if 0
  162. static void jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
  163. {
  164. unsigned long long tvusec;
  165. tvusec = 10000ULL*jiffies;
  166. tv->tv_sec = tvusec/1000000;
  167. tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
  168. }
  169. # endif
  170. static unsigned long str_to_jiffies(const char *time_str)
  171. {
  172. struct timeval tv;
  173. bb_strtotimeval(&tv, time_str);
  174. return tv_to_jiffies(&tv);
  175. }
  176. static void arm_ioctl(unsigned long *args,
  177. unsigned long arg0, unsigned long arg1, unsigned long arg2)
  178. {
  179. args[0] = arg0;
  180. args[1] = arg1;
  181. args[2] = arg2;
  182. args[3] = 0;
  183. }
  184. #endif
  185. int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  186. int brctl_main(int argc UNUSED_PARAM, char **argv)
  187. {
  188. static const char keywords[] ALIGN1 =
  189. "addbr\0" "delbr\0" "addif\0" "delif\0"
  190. IF_FEATURE_BRCTL_FANCY(
  191. "stp\0"
  192. "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
  193. "setpathcost\0" "setportprio\0" "setbridgeprio\0"
  194. )
  195. IF_FEATURE_BRCTL_SHOW("show\0");
  196. enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
  197. IF_FEATURE_BRCTL_FANCY(,
  198. ARG_stp,
  199. ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
  200. ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio
  201. )
  202. IF_FEATURE_BRCTL_SHOW(, ARG_show)
  203. };
  204. int fd;
  205. smallint key;
  206. struct ifreq ifr;
  207. char *br, *brif;
  208. argv++;
  209. while (*argv) {
  210. #if ENABLE_FEATURE_BRCTL_FANCY
  211. int ifidx[MAX_PORTS];
  212. unsigned long args[4];
  213. ifr.ifr_data = (char *) &args;
  214. #endif
  215. key = index_in_strings(keywords, *argv);
  216. if (key == -1) /* no match found in keywords array, bail out. */
  217. bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
  218. argv++;
  219. fd = xsocket(AF_INET, SOCK_STREAM, 0);
  220. #if ENABLE_FEATURE_BRCTL_SHOW
  221. if (key == ARG_show) { /* show */
  222. char brname[IFNAMSIZ];
  223. int bridx[MAX_PORTS];
  224. int i, num;
  225. arm_ioctl(args, BRCTL_GET_BRIDGES,
  226. (unsigned long) bridx, MAX_PORTS);
  227. num = xioctl(fd, SIOCGIFBR, args);
  228. puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
  229. for (i = 0; i < num; i++) {
  230. char ifname[IFNAMSIZ];
  231. int j, tabs;
  232. struct __bridge_info bi;
  233. unsigned char *x;
  234. if (!if_indextoname(bridx[i], brname))
  235. bb_perror_msg_and_die("can't get bridge name for index %d", i);
  236. strncpy_IFNAMSIZ(ifr.ifr_name, brname);
  237. arm_ioctl(args, BRCTL_GET_BRIDGE_INFO,
  238. (unsigned long) &bi, 0);
  239. xioctl(fd, SIOCDEVPRIVATE, &ifr);
  240. printf("%s\t\t", brname);
  241. /* print bridge id */
  242. x = (unsigned char *) &bi.bridge_id;
  243. for (j = 0; j < 8; j++) {
  244. printf("%02x", x[j]);
  245. if (j == 1)
  246. bb_putchar('.');
  247. }
  248. printf(bi.stp_enabled ? "\tyes" : "\tno");
  249. /* print interface list */
  250. arm_ioctl(args, BRCTL_GET_PORT_LIST,
  251. (unsigned long) ifidx, MAX_PORTS);
  252. xioctl(fd, SIOCDEVPRIVATE, &ifr);
  253. tabs = 0;
  254. for (j = 0; j < MAX_PORTS; j++) {
  255. if (!ifidx[j])
  256. continue;
  257. if (!if_indextoname(ifidx[j], ifname))
  258. bb_perror_msg_and_die("can't get interface name for index %d", j);
  259. if (tabs)
  260. printf("\t\t\t\t\t");
  261. else
  262. tabs = 1;
  263. printf("\t\t%s\n", ifname);
  264. }
  265. if (!tabs) /* bridge has no interfaces */
  266. bb_putchar('\n');
  267. }
  268. goto done;
  269. }
  270. #endif
  271. if (!*argv) /* all but 'show' need at least one argument */
  272. bb_show_usage();
  273. br = *argv++;
  274. if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
  275. ioctl_or_perror_and_die(fd,
  276. key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
  277. br, "bridge %s", br);
  278. goto done;
  279. }
  280. if (!*argv) /* all but 'addbr/delbr' need at least two arguments */
  281. bb_show_usage();
  282. strncpy_IFNAMSIZ(ifr.ifr_name, br);
  283. if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
  284. brif = *argv;
  285. ifr.ifr_ifindex = if_nametoindex(brif);
  286. if (!ifr.ifr_ifindex) {
  287. bb_perror_msg_and_die("iface %s", brif);
  288. }
  289. ioctl_or_perror_and_die(fd,
  290. key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
  291. &ifr, "bridge %s", br);
  292. goto done_next_argv;
  293. }
  294. #if ENABLE_FEATURE_BRCTL_FANCY
  295. if (key == ARG_stp) { /* stp */
  296. static const char no_yes[] ALIGN1 =
  297. "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
  298. "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
  299. int onoff = index_in_strings(no_yes, *argv);
  300. if (onoff < 0)
  301. bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
  302. onoff = (unsigned)onoff / 4;
  303. arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0);
  304. goto fire;
  305. }
  306. if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
  307. static const uint8_t ops[] ALIGN1 = {
  308. BRCTL_SET_AGEING_TIME, /* ARG_setageing */
  309. BRCTL_SET_BRIDGE_FORWARD_DELAY, /* ARG_setfd */
  310. BRCTL_SET_BRIDGE_HELLO_TIME, /* ARG_sethello */
  311. BRCTL_SET_BRIDGE_MAX_AGE /* ARG_setmaxage */
  312. };
  313. arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0);
  314. goto fire;
  315. }
  316. if (key == ARG_setpathcost
  317. || key == ARG_setportprio
  318. || key == ARG_setbridgeprio
  319. ) {
  320. static const uint8_t ops[] ALIGN1 = {
  321. BRCTL_SET_PATH_COST, /* ARG_setpathcost */
  322. BRCTL_SET_PORT_PRIORITY, /* ARG_setportprio */
  323. BRCTL_SET_BRIDGE_PRIORITY /* ARG_setbridgeprio */
  324. };
  325. int port = -1;
  326. unsigned arg1, arg2;
  327. if (key != ARG_setbridgeprio) {
  328. /* get portnum */
  329. unsigned i;
  330. port = if_nametoindex(*argv++);
  331. if (!port)
  332. bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, "port");
  333. memset(ifidx, 0, sizeof ifidx);
  334. arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx,
  335. MAX_PORTS);
  336. xioctl(fd, SIOCDEVPRIVATE, &ifr);
  337. for (i = 0; i < MAX_PORTS; i++) {
  338. if (ifidx[i] == port) {
  339. port = i;
  340. break;
  341. }
  342. }
  343. }
  344. arg1 = port;
  345. arg2 = xatoi_positive(*argv);
  346. if (key == ARG_setbridgeprio) {
  347. arg1 = arg2;
  348. arg2 = 0;
  349. }
  350. arm_ioctl(args, ops[key - ARG_setpathcost], arg1, arg2);
  351. }
  352. fire:
  353. /* Execute the previously set command */
  354. xioctl(fd, SIOCDEVPRIVATE, &ifr);
  355. #endif
  356. done_next_argv:
  357. argv++;
  358. done:
  359. close(fd);
  360. }
  361. return EXIT_SUCCESS;
  362. }