microcom: read more than 1 byte from device, if possible
[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 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 tarball for details.
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 #include "libbb.h"
16 #include <linux/sockios.h>
17 #include <net/if.h>
18
19 /* Maximum number of ports supported per bridge interface.  */
20 #ifndef MAX_PORTS
21 #define MAX_PORTS 32
22 #endif
23
24 /* Use internal number parsing and not the "exact" conversion.  */
25 /* #define BRCTL_USE_INTERNAL 0 */ /* use exact conversion */
26 #define BRCTL_USE_INTERNAL 1
27
28 #ifdef ENABLE_FEATURE_BRCTL_SHOW
29 #error Remove these
30 #endif
31 #define ENABLE_FEATURE_BRCTL_SHOW 0
32 #define USE_FEATURE_BRCTL_SHOW(...)
33
34 #if ENABLE_FEATURE_BRCTL_FANCY
35 #include <linux/if_bridge.h>
36 /* FIXME: These 4 funcs are not really clean and could be improved */
37 static inline ALWAYS_INLINE void strtotimeval(struct timeval *tv,
38                                                                                           const char *time_str)
39 {
40         double secs;
41 #if BRCTL_USE_INTERNAL
42         if (!(secs = /*bb_*/strtod(time_str, NULL)))
43 #else
44         if (sscanf(time_str, "%lf", &secs) != 1)
45 #endif
46                 bb_error_msg_and_die (bb_msg_invalid_arg, time_str, "timespec");
47         tv->tv_sec = secs;
48         tv->tv_usec = 1000000 * (secs - tv->tv_sec);
49 }
50
51 static inline ALWAYS_INLINE unsigned long __tv_to_jiffies(const struct timeval *tv)
52 {
53         unsigned long long jif;
54
55         jif = 1000000ULL * tv->tv_sec + tv->tv_usec;
56
57         return jif/10000;
58 }
59 # if 00
60 static void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
61 {
62         unsigned long long tvusec;
63
64         tvusec = 10000ULL*jiffies;
65         tv->tv_sec = tvusec/1000000;
66         tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
67 }
68 # endif
69 static unsigned long str_to_jiffies(const char *time_str)
70 {
71         struct timeval tv;
72         strtotimeval(&tv, time_str);
73         return __tv_to_jiffies(&tv);
74 }
75
76 static void arm_ioctl(unsigned long *args,
77                                  unsigned long arg0, unsigned long arg1, unsigned long arg2)
78 {
79         args[0] = arg0;
80         args[1] = arg1;
81         args[2] = arg2;
82         args[3] = 0;
83 }
84 #endif
85
86
87 int brctl_main(int argc ATTRIBUTE_UNUSED, char **argv) MAIN_EXTERNALLY_VISIBLE;
88 int brctl_main(int argc ATTRIBUTE_UNUSED, char **argv)
89 {
90         int fd;
91         static const char keywords[] ALIGN1 =
92                 "addbr\0" "delbr\0" "addif\0" "delif\0"
93         USE_FEATURE_BRCTL_FANCY(
94                 "stp\0"
95                 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
96                 "setpathcost\0" "setportprio\0" "setbridgeprio\0"
97         )
98                 USE_FEATURE_BRCTL_SHOW("showmacs\0" "show\0");
99         enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
100                 USE_FEATURE_BRCTL_FANCY(,
101                    ARG_stp,
102                    ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
103                    ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio
104                 )
105                   USE_FEATURE_BRCTL_SHOW(, ARG_showmacs, ARG_show) };
106         smallint key;
107         struct ifreq ifr;
108         static char info[] = "bridge %s\0 iface %s";
109         char *br, *brif;
110 #if ENABLE_FEATURE_BRCTL_FANCY
111         unsigned long args[4] = {0, 0, 0, 0};
112         int port;
113         int tmp;
114 #endif
115
116         argv++;
117         while (*argv) {
118                 key = index_in_strings(keywords, *argv);
119                 if (key == -1) /* no match found in keywords array, bail out. */
120                         bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
121                 argv++;
122 #if ENABLE_FEATURE_BRCTL_SHOW
123                 if (key == ARG_show) { /* show */
124                         goto out; /* FIXME: implement me! :) */
125                 }
126 #endif
127                 fd = xsocket(AF_INET, SOCK_STREAM, 0);
128                 br = *(argv++);
129
130                 if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
131                         ioctl_or_perror_and_die(fd,
132                                                                 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
133                                                                 br, info, br);
134                         goto done;
135                 }
136                 if (!*argv) /* all but 'show' need at least one argument */
137                         bb_show_usage();
138                 safe_strncpy(ifr.ifr_name, br, IFNAMSIZ);
139                 if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
140                         brif = *(argv++);
141                         if (!(ifr.ifr_ifindex = if_nametoindex(brif))) {
142                                 bb_perror_msg_and_die(info+11, brif);
143                         }
144                         ioctl_or_perror_and_die(fd,
145                                                                   key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
146                                                                   &ifr, info, br);
147                         goto done;
148                 }
149 #if ENABLE_FEATURE_BRCTL_FANCY
150                 ifr.ifr_data = (char *) &args;
151                 if (key == ARG_stp) { /* stp */
152                         /* FIXME: parsing yes/y/on/1 versus no/n/off/0 is too involved */
153                         arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE,
154                                           (unsigned)(**argv - '0'), 0);
155                         goto fire;
156                 }
157                 if ((unsigned)(key - ARG_stp) < 5) { /* time related ops */
158                         unsigned long op = (key == ARG_setageing) ? BRCTL_SET_AGEING_TIME :
159                                                         (key == ARG_setfd) ? BRCTL_SET_BRIDGE_FORWARD_DELAY:
160                                                         (key == ARG_sethello) ? BRCTL_SET_BRIDGE_HELLO_TIME:
161                                                         (key == ARG_setmaxage) ? BRCTL_SET_BRIDGE_MAX_AGE :
162                                                         -1/* will never be used */;
163                         arm_ioctl(args, op, str_to_jiffies (*argv), 0);
164                         goto fire;
165                 }
166                 port = -1;
167                 if (key == ARG_setpathcost || key == ARG_setportprio) {/* get portnum */
168                         int ifidx[MAX_PORTS];
169                         unsigned i;
170
171                         if (!(port = if_nametoindex(*argv)))
172                                 bb_error_msg_and_die(bb_msg_invalid_arg, *argv, "port");
173                         argv++;
174                         memset(ifidx, 0, sizeof ifidx);
175                         arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx,
176                                           MAX_PORTS);
177                         xioctl(fd, SIOCDEVPRIVATE, &ifr);
178                         for (i = 0; i < MAX_PORTS; i++)
179                                 if (ifidx[i] == port) {
180                                         port = i;
181                                         break;
182                                 }
183                 }
184                 if (key == ARG_setpathcost
185                         || key == ARG_setportprio || key == ARG_setbridgeprio) {
186                         unsigned long op = (key == ARG_setpathcost) ? BRCTL_SET_PATH_COST :
187                                                 (key == ARG_setportprio) ? BRCTL_SET_PORT_PRIORITY :
188                                                 (key == ARG_setbridgeprio) ? BRCTL_SET_BRIDGE_PRIORITY :
189                                                 -1/* will never be used */;
190                         unsigned long arg1 = port;
191                         unsigned long arg2;
192 # if BRCTL_USE_INTERNAL
193                         tmp = xatoi(*argv);
194 # else
195                         if (sscanf(*argv, "%i", &tmp) != 1)
196                                 bb_error_msg_and_die(bb_msg_invalid_arg, *argv,
197                                                                          key == ARG_setpathcost ? "cost" : "prio");
198 # endif
199                         if (key == ARG_setbridgeprio) {
200                                 arg1 = tmp;
201                                 arg2 = 0;
202                         } else
203                                 arg2 = tmp;
204                         arm_ioctl(args, op, arg1, arg2);
205                 }
206  fire:
207                 /* Execute the previously set command.  */
208                 xioctl(fd, SIOCDEVPRIVATE, &ifr);
209                 argv++;
210 #endif
211  done:
212                 if (ENABLE_FEATURE_CLEAN_UP)
213                         close(fd);
214         }
215 USE_FEATURE_BRCTL_SHOW(out:)
216         return EXIT_SUCCESS;
217 }