brctl.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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 (4.7 kb)"
  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_NOEXEC(brctl, brctl, BB_DIR_USR_SBIN, BB_SUID_DROP, brctl))
  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 buf[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. int j, tabs;
  231. struct __bridge_info bi;
  232. unsigned char *x;
  233. if (!if_indextoname(bridx[i], buf))
  234. bb_perror_msg_and_die("can't get bridge name for index %d", i);
  235. strncpy_IFNAMSIZ(ifr.ifr_name, buf);
  236. arm_ioctl(args, BRCTL_GET_BRIDGE_INFO,
  237. (unsigned long) &bi, 0);
  238. xioctl(fd, SIOCDEVPRIVATE, &ifr);
  239. printf("%s\t\t", buf);
  240. /* print bridge id */
  241. x = (unsigned char *) &bi.bridge_id;
  242. for (j = 0; j < 8; j++) {
  243. printf("%02x", x[j]);
  244. if (j == 1)
  245. bb_putchar('.');
  246. }
  247. printf(bi.stp_enabled ? "\tyes" : "\tno");
  248. /* print interface list */
  249. arm_ioctl(args, BRCTL_GET_PORT_LIST,
  250. (unsigned long) ifidx, MAX_PORTS);
  251. xioctl(fd, SIOCDEVPRIVATE, &ifr);
  252. tabs = 0;
  253. for (j = 0; j < MAX_PORTS; j++) {
  254. if (!ifidx[j])
  255. continue;
  256. if (!if_indextoname(ifidx[j], buf))
  257. bb_perror_msg_and_die("can't get interface name for index %d", j);
  258. if (tabs)
  259. printf("\t\t\t\t\t");
  260. else
  261. tabs = 1;
  262. printf("\t\t%s\n", buf);
  263. }
  264. if (!tabs) /* bridge has no interfaces */
  265. bb_putchar('\n');
  266. }
  267. goto done;
  268. }
  269. #endif
  270. if (!*argv) /* all but 'show' need at least one argument */
  271. bb_show_usage();
  272. br = *argv++;
  273. if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
  274. ioctl_or_perror_and_die(fd,
  275. key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
  276. br, "bridge %s", br);
  277. goto done;
  278. }
  279. if (!*argv) /* all but 'addbr/delbr' need at least two arguments */
  280. bb_show_usage();
  281. strncpy_IFNAMSIZ(ifr.ifr_name, br);
  282. if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
  283. brif = *argv;
  284. ifr.ifr_ifindex = if_nametoindex(brif);
  285. if (!ifr.ifr_ifindex) {
  286. bb_perror_msg_and_die("iface %s", brif);
  287. }
  288. ioctl_or_perror_and_die(fd,
  289. key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
  290. &ifr, "bridge %s", br);
  291. goto done_next_argv;
  292. }
  293. #if ENABLE_FEATURE_BRCTL_FANCY
  294. if (key == ARG_stp) { /* stp */
  295. static const char no_yes[] ALIGN1 =
  296. "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
  297. "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
  298. int onoff = index_in_strings(no_yes, *argv);
  299. if (onoff < 0)
  300. bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
  301. onoff = (unsigned)onoff / 4;
  302. arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0);
  303. goto fire;
  304. }
  305. if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
  306. static const uint8_t ops[] ALIGN1 = {
  307. BRCTL_SET_AGEING_TIME, /* ARG_setageing */
  308. BRCTL_SET_BRIDGE_FORWARD_DELAY, /* ARG_setfd */
  309. BRCTL_SET_BRIDGE_HELLO_TIME, /* ARG_sethello */
  310. BRCTL_SET_BRIDGE_MAX_AGE /* ARG_setmaxage */
  311. };
  312. arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0);
  313. goto fire;
  314. }
  315. if (key == ARG_setpathcost
  316. || key == ARG_setportprio
  317. || key == ARG_setbridgeprio
  318. ) {
  319. static const uint8_t ops[] ALIGN1 = {
  320. BRCTL_SET_PATH_COST, /* ARG_setpathcost */
  321. BRCTL_SET_PORT_PRIORITY, /* ARG_setportprio */
  322. BRCTL_SET_BRIDGE_PRIORITY /* ARG_setbridgeprio */
  323. };
  324. int port = -1;
  325. unsigned arg1, arg2;
  326. if (key != ARG_setbridgeprio) {
  327. /* get portnum */
  328. unsigned i;
  329. port = if_nametoindex(*argv++);
  330. if (!port)
  331. bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, "port");
  332. memset(ifidx, 0, sizeof ifidx);
  333. arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx,
  334. MAX_PORTS);
  335. xioctl(fd, SIOCDEVPRIVATE, &ifr);
  336. for (i = 0; i < MAX_PORTS; i++) {
  337. if (ifidx[i] == port) {
  338. port = i;
  339. break;
  340. }
  341. }
  342. }
  343. arg1 = port;
  344. arg2 = xatoi_positive(*argv);
  345. if (key == ARG_setbridgeprio) {
  346. arg1 = arg2;
  347. arg2 = 0;
  348. }
  349. arm_ioctl(args, ops[key - ARG_setpathcost], arg1, arg2);
  350. }
  351. fire:
  352. /* Execute the previously set command */
  353. xioctl(fd, SIOCDEVPRIVATE, &ifr);
  354. #endif
  355. done_next_argv:
  356. argv++;
  357. done:
  358. close(fd);
  359. }
  360. return EXIT_SUCCESS;
  361. }