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