diff options
Diffstat (limited to 'networking/brctl.c')
-rw-r--r-- | networking/brctl.c | 506 |
1 files changed, 260 insertions, 246 deletions
diff --git a/networking/brctl.c b/networking/brctl.c index ba4a714f8..f44ad9c8d 100644 --- a/networking/brctl.c +++ b/networking/brctl.c | |||
@@ -9,9 +9,6 @@ | |||
9 | * | 9 | * |
10 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 10 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
11 | */ | 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 | 12 | //config:config BRCTL |
16 | //config: bool "brctl (4.7 kb)" | 13 | //config: bool "brctl (4.7 kb)" |
17 | //config: default y | 14 | //config: default y |
@@ -44,29 +41,34 @@ | |||
44 | //kbuild:lib-$(CONFIG_BRCTL) += brctl.o | 41 | //kbuild:lib-$(CONFIG_BRCTL) += brctl.o |
45 | 42 | ||
46 | //usage:#define brctl_trivial_usage | 43 | //usage:#define brctl_trivial_usage |
47 | //usage: "COMMAND [BRIDGE [INTERFACE]]" | 44 | //usage: "COMMAND [BRIDGE [ARGS]]" |
48 | //usage:#define brctl_full_usage "\n\n" | 45 | //usage:#define brctl_full_usage "\n\n" |
49 | //usage: "Manage ethernet bridges\n" | 46 | //usage: "Manage ethernet bridges" |
50 | //usage: "\nCommands:" | 47 | //usage: "\nCommands:" |
51 | //usage: IF_FEATURE_BRCTL_SHOW( | 48 | //usage: IF_FEATURE_BRCTL_SHOW( |
52 | //usage: "\n show Show a list of bridges" | 49 | //usage: "\n show [BRIDGE]... Show bridges" |
53 | //usage: ) | 50 | //usage: ) |
54 | //usage: "\n addbr BRIDGE Create BRIDGE" | 51 | //usage: "\n addbr BRIDGE Create BRIDGE" |
55 | //usage: "\n delbr BRIDGE Delete BRIDGE" | 52 | //usage: "\n delbr BRIDGE Delete BRIDGE" |
56 | //usage: "\n addif BRIDGE IFACE Add IFACE to BRIDGE" | 53 | //usage: "\n addif BRIDGE IFACE Add IFACE to BRIDGE" |
57 | //usage: "\n delif BRIDGE IFACE Delete IFACE from BRIDGE" | 54 | //usage: "\n delif BRIDGE IFACE Delete IFACE from BRIDGE" |
58 | //usage: IF_FEATURE_BRCTL_FANCY( | 55 | //usage: IF_FEATURE_BRCTL_FANCY( |
59 | //usage: "\n setageing BRIDGE TIME Set ageing time" | 56 | //usage: "\n stp BRIDGE 1/yes/on|0/no/off STP on/off" |
60 | //usage: "\n setfd BRIDGE TIME Set bridge forward delay" | 57 | //usage: "\n setageing BRIDGE SECONDS Set ageing time" |
61 | //usage: "\n sethello BRIDGE TIME Set hello time" | 58 | //usage: "\n setfd BRIDGE SECONDS Set bridge forward delay" |
62 | //usage: "\n setmaxage BRIDGE TIME Set max message age" | 59 | //usage: "\n sethello BRIDGE SECONDS Set hello time" |
63 | //usage: "\n setpathcost BRIDGE COST Set path cost" | 60 | //usage: "\n setmaxage BRIDGE SECONDS Set max message age" |
64 | //usage: "\n setportprio BRIDGE PRIO Set port priority" | ||
65 | //usage: "\n setbridgeprio BRIDGE PRIO Set bridge priority" | 61 | //usage: "\n setbridgeprio BRIDGE PRIO Set bridge priority" |
66 | //usage: "\n stp BRIDGE [1/yes/on|0/no/off] STP on/off" | 62 | //usage: "\n setportprio BRIDGE IFACE PRIO Set port priority" |
63 | //usage: "\n setpathcost BRIDGE IFACE COST Set path cost" | ||
67 | //usage: ) | 64 | //usage: ) |
65 | // Not yet implemented: | ||
66 | // hairpin BRIDGE IFACE on|off Hairpin on/off | ||
67 | // showmacs BRIDGE List mac addrs | ||
68 | // showstp BRIDGE Show stp info | ||
68 | 69 | ||
69 | #include "libbb.h" | 70 | #include "libbb.h" |
71 | #include "common_bufsiz.h" | ||
70 | #include <linux/sockios.h> | 72 | #include <linux/sockios.h> |
71 | #include <net/if.h> | 73 | #include <net/if.h> |
72 | 74 | ||
@@ -83,121 +85,114 @@ | |||
83 | # define SIOCBRDELIF BRCTL_DEL_IF | 85 | # define SIOCBRDELIF BRCTL_DEL_IF |
84 | #endif | 86 | #endif |
85 | 87 | ||
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 | 88 | #if ENABLE_FEATURE_BRCTL_FANCY |
97 | /* #include <linux/if_bridge.h> | 89 | static unsigned str_to_jiffies(const char *time_str) |
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 | { | 90 | { |
153 | double secs; | 91 | double dd; |
154 | # if BRCTL_USE_INTERNAL | ||
155 | char *endptr; | 92 | char *endptr; |
156 | secs = /*bb_*/strtod(time_str, &endptr); | 93 | dd = /*bb_*/strtod(time_str, &endptr); |
157 | if (endptr == time_str) | 94 | if (endptr == time_str || dd < 0) |
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"); | 95 | 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 | 96 | ||
166 | static ALWAYS_INLINE unsigned long tv_to_jiffies(const struct timeval *tv) | 97 | dd *= 100; |
167 | { | 98 | /* For purposes of brctl, |
168 | unsigned long long jif; | 99 | * capping SECONDS by ~20 million seconds is quite enough: |
169 | 100 | */ | |
170 | jif = 1000000ULL * tv->tv_sec + tv->tv_usec; | 101 | if (dd > INT_MAX) |
102 | dd = INT_MAX; | ||
171 | 103 | ||
172 | return jif/10000; | 104 | return dd; |
173 | } | 105 | } |
174 | # if 0 | 106 | #endif |
175 | static void jiffies_to_tv(struct timeval *tv, unsigned long jiffies) | ||
176 | { | ||
177 | unsigned long long tvusec; | ||
178 | 107 | ||
179 | tvusec = 10000ULL*jiffies; | 108 | #define filedata bb_common_bufsiz1 |
180 | tv->tv_sec = tvusec/1000000; | 109 | |
181 | tv->tv_usec = tvusec - 1000000 * tv->tv_sec; | 110 | static int read_file(const char *name) |
182 | } | ||
183 | # endif | ||
184 | static unsigned long str_to_jiffies(const char *time_str) | ||
185 | { | 111 | { |
186 | struct timeval tv; | 112 | int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1); |
187 | bb_strtotimeval(&tv, time_str); | 113 | if (n < 0) { |
188 | return tv_to_jiffies(&tv); | 114 | filedata[0] = '\0'; |
115 | } else { | ||
116 | filedata[n] = '\0'; | ||
117 | if (n != 0 && filedata[n - 1] == '\n') | ||
118 | filedata[--n] = '\0'; | ||
119 | } | ||
120 | return n; | ||
189 | } | 121 | } |
190 | 122 | ||
191 | static void arm_ioctl(unsigned long *args, | 123 | /* NB: we are in /sys/class/net |
192 | unsigned long arg0, unsigned long arg1, unsigned long arg2) | 124 | */ |
125 | static int show_bridge(const char *name, int need_hdr) | ||
193 | { | 126 | { |
194 | args[0] = arg0; | 127 | /* Output: |
195 | args[1] = arg1; | 128 | *bridge name bridge id STP enabled interfaces |
196 | args[2] = arg2; | 129 | *br0 8000.000000000000 no eth0 |
197 | args[3] = 0; | 130 | */ |
198 | } | 131 | char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32]; |
132 | int tabs; | ||
133 | DIR *ifaces; | ||
134 | struct dirent *ent; | ||
135 | char *sfx; | ||
136 | |||
137 | #if IFNAMSIZ == 16 | ||
138 | sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name); | ||
139 | #else | ||
140 | sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name); | ||
199 | #endif | 141 | #endif |
142 | strcpy(sfx, "bridge_id"); | ||
143 | if (read_file(pathbuf) < 0) | ||
144 | return -1; /* this iface is not a bridge */ | ||
145 | |||
146 | if (need_hdr) | ||
147 | puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces"); | ||
148 | printf("%s\t\t", name); | ||
149 | printf("%s\t", filedata); | ||
150 | |||
151 | strcpy(sfx, "stp_state"); | ||
152 | read_file(pathbuf); | ||
153 | if (LONE_CHAR(filedata, '0')) | ||
154 | strcpy(filedata, "no"); | ||
155 | else | ||
156 | if (LONE_CHAR(filedata, '1')) | ||
157 | strcpy(filedata, "yes"); | ||
158 | fputs(filedata, stdout); | ||
159 | |||
160 | strcpy(sfx - (sizeof("bridge/")-1), "brif"); | ||
161 | tabs = 0; | ||
162 | ifaces = opendir(pathbuf); | ||
163 | if (ifaces) { | ||
164 | while ((ent = readdir(ifaces)) != NULL) { | ||
165 | if (DOT_OR_DOTDOT(ent->d_name)) | ||
166 | continue; /* . or .. */ | ||
167 | if (tabs) | ||
168 | printf("\t\t\t\t\t"); | ||
169 | else | ||
170 | tabs = 1; | ||
171 | printf("\t\t%s\n", ent->d_name); | ||
172 | } | ||
173 | closedir(ifaces); | ||
174 | } | ||
175 | if (!tabs) /* bridge has no interfaces */ | ||
176 | bb_putchar('\n'); | ||
177 | return 0; | ||
178 | } | ||
200 | 179 | ||
180 | static void write_uint(const char *name, const char *leaf, unsigned val) | ||
181 | { | ||
182 | char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32]; | ||
183 | int fd, n; | ||
184 | |||
185 | #if IFNAMSIZ == 16 | ||
186 | sprintf(pathbuf, "%.16s/%s", name, leaf); | ||
187 | #else | ||
188 | sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf); | ||
189 | #endif | ||
190 | fd = xopen(pathbuf, O_WRONLY); | ||
191 | n = sprintf(filedata, "%u\n", val); | ||
192 | if (write(fd, filedata, n) < 0) | ||
193 | bb_simple_perror_msg_and_die(name); | ||
194 | close(fd); | ||
195 | } | ||
201 | 196 | ||
202 | int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 197 | int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
203 | int brctl_main(int argc UNUSED_PARAM, char **argv) | 198 | int brctl_main(int argc UNUSED_PARAM, char **argv) |
@@ -207,90 +202,71 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) | |||
207 | IF_FEATURE_BRCTL_FANCY( | 202 | IF_FEATURE_BRCTL_FANCY( |
208 | "stp\0" | 203 | "stp\0" |
209 | "setageing\0" "setfd\0" "sethello\0" "setmaxage\0" | 204 | "setageing\0" "setfd\0" "sethello\0" "setmaxage\0" |
210 | "setpathcost\0" "setportprio\0" "setbridgeprio\0" | 205 | "setpathcost\0" "setportprio\0" |
206 | "setbridgeprio\0" | ||
211 | ) | 207 | ) |
212 | IF_FEATURE_BRCTL_SHOW("show\0"); | 208 | IF_FEATURE_BRCTL_SHOW("show\0"); |
213 | |||
214 | enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif | 209 | enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif |
215 | IF_FEATURE_BRCTL_FANCY(, | 210 | IF_FEATURE_BRCTL_FANCY(, |
216 | ARG_stp, | 211 | ARG_stp, |
217 | ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage, | 212 | ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage, |
218 | ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio | 213 | ARG_setpathcost, ARG_setportprio, |
214 | ARG_setbridgeprio | ||
219 | ) | 215 | ) |
220 | IF_FEATURE_BRCTL_SHOW(, ARG_show) | 216 | IF_FEATURE_BRCTL_SHOW(, ARG_show) |
221 | }; | 217 | }; |
222 | 218 | ||
223 | int fd; | ||
224 | smallint key; | ||
225 | struct ifreq ifr; | ||
226 | char *br, *brif; | ||
227 | |||
228 | argv++; | 219 | argv++; |
229 | while (*argv) { | 220 | if (!*argv) { |
230 | #if ENABLE_FEATURE_BRCTL_FANCY | 221 | /* bare "brctl" shows --help */ |
231 | int ifidx[MAX_PORTS]; | 222 | bb_show_usage(); |
232 | unsigned long args[4]; | 223 | } |
233 | ifr.ifr_data = (char *) &args; | 224 | |
234 | #endif | 225 | xchdir("/sys/class/net"); |
226 | |||
227 | // while (*argv) | ||
228 | { | ||
229 | smallint key; | ||
230 | char *br; | ||
235 | 231 | ||
236 | key = index_in_strings(keywords, *argv); | 232 | key = index_in_strings(keywords, *argv); |
237 | if (key == -1) /* no match found in keywords array, bail out. */ | 233 | 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); | 234 | bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name); |
239 | argv++; | 235 | argv++; |
240 | fd = xsocket(AF_INET, SOCK_STREAM, 0); | ||
241 | 236 | ||
242 | #if ENABLE_FEATURE_BRCTL_SHOW | 237 | #if ENABLE_FEATURE_BRCTL_SHOW |
243 | if (key == ARG_show) { /* show */ | 238 | if (key == ARG_show) { /* show [BR]... */ |
244 | char buf[IFNAMSIZ]; | 239 | DIR *net; |
245 | int bridx[MAX_PORTS]; | 240 | struct dirent *ent; |
246 | int i, num; | 241 | int need_hdr = 1; |
247 | arm_ioctl(args, BRCTL_GET_BRIDGES, | 242 | int exitcode = EXIT_SUCCESS; |
248 | (unsigned long) bridx, MAX_PORTS); | 243 | |
249 | num = xioctl(fd, SIOCGIFBR, args); | 244 | if (*argv) { |
250 | puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces"); | 245 | /* "show BR1 BR2 BR3" */ |
251 | for (i = 0; i < num; i++) { | 246 | do { |
252 | int j, tabs; | 247 | if (show_bridge(*argv, need_hdr) >= 0) { |
253 | struct __bridge_info bi; | 248 | need_hdr = 0; |
254 | unsigned char *x; | 249 | } else { |
255 | 250 | bb_error_msg("bridge %s does not exist", *argv); | |
256 | if (!if_indextoname(bridx[i], buf)) | 251 | //TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6 |
257 | bb_perror_msg_and_die("can't get bridge name for index %d", i); | 252 | //says this instead: "device eth0 is not a bridge" |
258 | strncpy_IFNAMSIZ(ifr.ifr_name, buf); | 253 | exitcode = EXIT_FAILURE; |
259 | 254 | } | |
260 | arm_ioctl(args, BRCTL_GET_BRIDGE_INFO, | 255 | } while (*++argv != NULL); |
261 | (unsigned long) &bi, 0); | 256 | return exitcode; |
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 | } | 257 | } |
293 | goto done; | 258 | |
259 | /* "show" (if no ifaces, shows nothing, not even header) */ | ||
260 | net = xopendir("."); | ||
261 | while ((ent = readdir(net)) != NULL) { | ||
262 | if (DOT_OR_DOTDOT(ent->d_name)) | ||
263 | continue; /* . or .. */ | ||
264 | if (show_bridge(ent->d_name, need_hdr) >= 0) | ||
265 | need_hdr = 0; | ||
266 | } | ||
267 | if (ENABLE_FEATURE_CLEAN_UP) | ||
268 | closedir(net); | ||
269 | return exitcode; | ||
294 | } | 270 | } |
295 | #endif | 271 | #endif |
296 | 272 | ||
@@ -299,28 +275,29 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) | |||
299 | 275 | ||
300 | br = *argv++; | 276 | br = *argv++; |
301 | 277 | ||
302 | if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */ | 278 | if (key == ARG_addbr || key == ARG_delbr) { |
279 | /* addbr or delbr */ | ||
280 | /* brctl from bridge-utils 1.6 still uses ioctl | ||
281 | * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses | ||
282 | */ | ||
283 | int fd = xsocket(AF_INET, SOCK_STREAM, 0); | ||
303 | ioctl_or_perror_and_die(fd, | 284 | ioctl_or_perror_and_die(fd, |
304 | key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR, | 285 | key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR, |
305 | br, "bridge %s", br); | 286 | br, "bridge %s", br |
306 | goto done; | 287 | ); |
288 | //close(fd); | ||
289 | //goto done; | ||
290 | /* bridge-utils 1.6 simply ignores trailing args: | ||
291 | * "brctl addbr BR1 ARGS" ignores ARGS | ||
292 | */ | ||
293 | if (ENABLE_FEATURE_CLEAN_UP) | ||
294 | close(fd); | ||
295 | return EXIT_SUCCESS; | ||
307 | } | 296 | } |
308 | 297 | ||
309 | if (!*argv) /* all but 'addbr/delbr' need at least two arguments */ | 298 | if (!*argv) /* all but 'addbr/delbr' need at least two arguments */ |
310 | bb_show_usage(); | 299 | bb_show_usage(); |
311 | 300 | ||
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 | 301 | #if ENABLE_FEATURE_BRCTL_FANCY |
325 | if (key == ARG_stp) { /* stp */ | 302 | if (key == ARG_stp) { /* stp */ |
326 | static const char no_yes[] ALIGN1 = | 303 | static const char no_yes[] ALIGN1 = |
@@ -330,65 +307,102 @@ int brctl_main(int argc UNUSED_PARAM, char **argv) | |||
330 | if (onoff < 0) | 307 | if (onoff < 0) |
331 | bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name); | 308 | bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name); |
332 | onoff = (unsigned)onoff / 4; | 309 | onoff = (unsigned)onoff / 4; |
333 | arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0); | 310 | write_uint(br, "bridge/stp_state", onoff); |
334 | goto fire; | 311 | //goto done_next_argv; |
312 | return EXIT_SUCCESS; | ||
335 | } | 313 | } |
314 | |||
336 | if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */ | 315 | if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */ |
337 | static const uint8_t ops[] ALIGN1 = { | 316 | /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time |
338 | BRCTL_SET_AGEING_TIME, /* ARG_setageing */ | 317 | * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay |
339 | BRCTL_SET_BRIDGE_FORWARD_DELAY, /* ARG_setfd */ | 318 | * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time |
340 | BRCTL_SET_BRIDGE_HELLO_TIME, /* ARG_sethello */ | 319 | * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age |
341 | BRCTL_SET_BRIDGE_MAX_AGE /* ARG_setmaxage */ | 320 | */ |
342 | }; | 321 | write_uint(br, |
343 | arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0); | 322 | nth_string( |
344 | goto fire; | 323 | "bridge/ageing_time" "\0" /* ARG_setageing */ |
324 | "bridge/forward_delay""\0" /* ARG_setfd */ | ||
325 | "bridge/hello_time" "\0" /* ARG_sethello */ | ||
326 | "bridge/max_age", /* ARG_setmaxage */ | ||
327 | key - ARG_setageing | ||
328 | ), | ||
329 | str_to_jiffies(*argv) | ||
330 | ); | ||
331 | //goto done_next_argv; | ||
332 | return EXIT_SUCCESS; | ||
345 | } | 333 | } |
334 | |||
335 | if (key == ARG_setbridgeprio) { | ||
336 | write_uint(br, "bridge/priority", xatoi_positive(*argv)); | ||
337 | //goto done_next_argv; | ||
338 | return EXIT_SUCCESS; | ||
339 | } | ||
340 | |||
346 | if (key == ARG_setpathcost | 341 | if (key == ARG_setpathcost |
347 | || key == ARG_setportprio | 342 | || key == ARG_setportprio |
348 | || key == ARG_setbridgeprio | ||
349 | ) { | 343 | ) { |
350 | static const uint8_t ops[] ALIGN1 = { | 344 | if (!argv[1]) |
351 | BRCTL_SET_PATH_COST, /* ARG_setpathcost */ | 345 | bb_show_usage(); |
352 | BRCTL_SET_PORT_PRIORITY, /* ARG_setportprio */ | 346 | /* BR is not used (and ignored!) for these commands: |
353 | BRCTL_SET_BRIDGE_PRIORITY /* ARG_setbridgeprio */ | 347 | * "setpathcost BR PORT N" writes "N\n" to |
354 | }; | 348 | * /sys/class/net/PORT/brport/path_cost |
355 | int port = -1; | 349 | * "setportprio BR PORT N" writes "N\n" to |
356 | unsigned arg1, arg2; | 350 | * /sys/class/net/PORT/brport/priority |
357 | 351 | */ | |
358 | if (key != ARG_setbridgeprio) { | 352 | write_uint(argv[0], |
359 | /* get portnum */ | 353 | nth_string( |
360 | unsigned i; | 354 | "brport/path_cost" "\0" /* ARG_setpathcost */ |
361 | 355 | "brport/priority", /* ARG_setportprio */ | |
362 | port = if_nametoindex(*argv++); | 356 | key - ARG_setpathcost |
363 | if (!port) | 357 | ), |
364 | bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, "port"); | 358 | xatoi_positive(argv[1]) |
365 | memset(ifidx, 0, sizeof ifidx); | 359 | ); |
366 | arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx, | 360 | //argv++; |
367 | MAX_PORTS); | 361 | //goto done_next_argv; |
368 | xioctl(fd, SIOCDEVPRIVATE, &ifr); | 362 | return EXIT_SUCCESS; |
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 | } | 363 | } |
384 | fire: | 364 | |
385 | /* Execute the previously set command */ | 365 | /* TODO: "showmacs BR" |
386 | xioctl(fd, SIOCDEVPRIVATE, &ifr); | 366 | * port no\tmac addr\t\tis local?\tageing timer |
367 | * <sp><sp>1\txx:xx:xx:xx:xx:xx\tno\t\t<sp><sp><sp>1.31 | ||
368 | * port no mac addr is local? ageing timer | ||
369 | * 1 xx:xx:xx:xx:xx:xx no 1.31 | ||
370 | * Read fixed-sized records from /sys/class/net/BR/brforward: | ||
371 | * struct __fdb_entry { | ||
372 | * uint8_t mac_addr[ETH_ALEN]; | ||
373 | * uint8_t port_no; //lsb | ||
374 | * uint8_t is_local; | ||
375 | * uint32_t ageing_timer_value; | ||
376 | * uint8_t port_hi; | ||
377 | * uint8_t pad0; | ||
378 | * uint16_t unused; | ||
379 | * }; | ||
380 | */ | ||
387 | #endif | 381 | #endif |
388 | done_next_argv: | 382 | /* always true: if (key == ARG_addif || key == ARG_delif) */ { |
389 | argv++; | 383 | /* addif or delif */ |
390 | done: | 384 | struct ifreq ifr; |
391 | close(fd); | 385 | int fd = xsocket(AF_INET, SOCK_STREAM, 0); |
386 | |||
387 | strncpy_IFNAMSIZ(ifr.ifr_name, br); | ||
388 | ifr.ifr_ifindex = if_nametoindex(*argv); | ||
389 | if (ifr.ifr_ifindex == 0) { | ||
390 | bb_perror_msg_and_die("iface %s", *argv); | ||
391 | } | ||
392 | ioctl_or_perror_and_die(fd, | ||
393 | key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF, | ||
394 | &ifr, "bridge %s", br | ||
395 | ); | ||
396 | //close(fd); | ||
397 | //goto done_next_argv; | ||
398 | if (ENABLE_FEATURE_CLEAN_UP) | ||
399 | close(fd); | ||
400 | return EXIT_SUCCESS; | ||
401 | } | ||
402 | |||
403 | // done_next_argv: | ||
404 | // argv++; | ||
405 | // done: | ||
392 | } | 406 | } |
393 | 407 | ||
394 | return EXIT_SUCCESS; | 408 | return EXIT_SUCCESS; |