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 //config:config BRCTL
13 //config: bool "brctl (4.7 kb)"
14 //config: default y
15 //config: help
16 //config: Manage ethernet bridges.
17 //config: Supports addbr/delbr and addif/delif.
18 //config:
19 //config:config FEATURE_BRCTL_FANCY
20 //config: bool "Fancy options"
21 //config: default y
22 //config: depends on BRCTL
23 //config: help
24 //config: Add support for extended option like:
25 //config: setageing, setfd, sethello, setmaxage,
26 //config: setpathcost, setportprio, setbridgeprio,
27 //config: stp
28 //config: This adds about 600 bytes.
29 //config:
30 //config:config FEATURE_BRCTL_SHOW
31 //config: bool "Support show"
32 //config: default y
33 //config: depends on BRCTL && FEATURE_BRCTL_FANCY
34 //config: help
35 //config: Add support for option which prints the current config:
36 //config: show
37
38 //applet:IF_BRCTL(APPLET_NOEXEC(brctl, brctl, BB_DIR_USR_SBIN, BB_SUID_DROP, brctl))
39
40 //kbuild:lib-$(CONFIG_BRCTL) += brctl.o
41
42 //usage:#define brctl_trivial_usage
43 //usage: "COMMAND [BRIDGE [ARGS]]"
44 //usage:#define brctl_full_usage "\n\n"
45 //usage: "Manage ethernet bridges"
46 //usage: "\nCommands:"
47 //usage: IF_FEATURE_BRCTL_SHOW(
48 //usage: "\n show [BRIDGE]... Show bridges"
49 //usage: )
50 //usage: "\n addbr BRIDGE Create BRIDGE"
51 //usage: "\n delbr BRIDGE Delete BRIDGE"
52 //usage: "\n addif BRIDGE IFACE Add IFACE to BRIDGE"
53 //usage: "\n delif BRIDGE IFACE Delete IFACE from BRIDGE"
54 //usage: IF_FEATURE_BRCTL_FANCY(
55 //usage: "\n showmacs BRIDGE List MAC addresses"
56 //usage: "\n showstp BRIDGE Show STP info"
57 //usage: "\n stp BRIDGE 1/yes/on|0/no/off Set STP on/off"
58 //usage: "\n setageing BRIDGE SECONDS Set ageing time"
59 //usage: "\n setfd BRIDGE SECONDS Set bridge forward delay"
60 //usage: "\n sethello BRIDGE SECONDS Set hello time"
61 //usage: "\n setmaxage BRIDGE SECONDS Set max message age"
62 //usage: "\n setbridgeprio BRIDGE PRIO Set bridge priority"
63 //usage: "\n setportprio BRIDGE IFACE PRIO Set port priority"
64 //usage: "\n setpathcost BRIDGE IFACE COST Set path cost"
65 //usage: )
66 // Not yet implemented:
67 // hairpin BRIDGE IFACE on|off Set hairpin on/off
68
69 #include "libbb.h"
70 #include "common_bufsiz.h"
71 #include <linux/sockios.h>
72 #include <net/if.h>
73
74 #ifndef SIOCBRADDBR
75 # define SIOCBRADDBR BRCTL_ADD_BRIDGE
76 #endif
77 #ifndef SIOCBRDELBR
78 # define SIOCBRDELBR BRCTL_DEL_BRIDGE
79 #endif
80 #ifndef SIOCBRADDIF
81 # define SIOCBRADDIF BRCTL_ADD_IF
82 #endif
83 #ifndef SIOCBRDELIF
84 # define SIOCBRDELIF BRCTL_DEL_IF
85 #endif
86
87 #if ENABLE_FEATURE_BRCTL_FANCY
str_to_jiffies(const char * time_str)88 static unsigned str_to_jiffies(const char *time_str)
89 {
90 double dd;
91 char *endptr;
92 //TODO: needs setlocale(LC_NUMERIC, "C")?
93 dd = /*bb_*/strtod(time_str, &endptr);
94 if (endptr == time_str || dd < 0)
95 bb_error_msg_and_die(bb_msg_invalid_arg_to, time_str, "timespec");
96
97 dd *= 100;
98 /* For purposes of brctl,
99 * capping SECONDS by ~20 million seconds is quite enough:
100 */
101 if (dd > INT_MAX)
102 dd = INT_MAX;
103
104 return dd;
105 }
106 #endif
107
108 #define filedata bb_common_bufsiz1
109
110 #if ENABLE_FEATURE_BRCTL_SHOW || ENABLE_FEATURE_BRCTL_FANCY
read_file(const char * name)111 static int read_file(const char *name)
112 {
113 int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1);
114 if (n < 0) {
115 filedata[0] = '\0';
116 } else {
117 filedata[n] = '\0';
118 if (n != 0 && filedata[n - 1] == '\n')
119 filedata[--n] = '\0';
120 }
121 return n;
122 }
123 #endif
124
125 #if ENABLE_FEATURE_BRCTL_SHOW
126 /* NB: we are in /sys/class/net
127 */
show_bridge(const char * name,int need_hdr)128 static int show_bridge(const char *name, int need_hdr)
129 {
130 /* Output:
131 *bridge name bridge id STP enabled interfaces
132 *br0 8000.000000000000 no eth0
133 */
134 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 8];
135 int tabs;
136 DIR *ifaces;
137 struct dirent *ent;
138 char *sfx;
139
140 #if IFNAMSIZ == 16
141 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
142 #else
143 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
144 #endif
145 strcpy(sfx, "bridge_id");
146 if (read_file(pathbuf) < 0)
147 return -1; /* this iface is not a bridge */
148
149 if (need_hdr)
150 puts("bridge name\tbridge id\t\tSTP enabled\tinterfaces");
151 printf("%s\t\t%s\t", name, filedata);
152
153 strcpy(sfx, "stp_state");
154 read_file(pathbuf);
155 if (LONE_CHAR(filedata, '0'))
156 strcpy(filedata, "no");
157 else
158 if (LONE_CHAR(filedata, '1'))
159 strcpy(filedata, "yes");
160 fputs_stdout(filedata);
161
162 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
163 sfx[-4] = 'f'; sfx[-3] = '\0';
164 tabs = 0;
165 ifaces = opendir(pathbuf);
166 if (ifaces) {
167 while ((ent = readdir(ifaces)) != NULL) {
168 if (DOT_OR_DOTDOT(ent->d_name))
169 continue; /* . or .. */
170 if (tabs)
171 printf("\t\t\t\t\t");
172 else
173 tabs = 1;
174 printf("\t\t%s\n", ent->d_name);
175 }
176 closedir(ifaces);
177 }
178 if (!tabs) /* bridge has no interfaces */
179 bb_putchar('\n');
180 return 0;
181 }
182 #endif
183
184 #if ENABLE_FEATURE_BRCTL_FANCY
write_uint(const char * name,const char * leaf,unsigned val)185 static void write_uint(const char *name, const char *leaf, unsigned val)
186 {
187 char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
188 int fd, n;
189
190 #if IFNAMSIZ == 16
191 sprintf(pathbuf, "%.16s/%s", name, leaf);
192 #else
193 sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf);
194 #endif
195 fd = xopen(pathbuf, O_WRONLY);
196 n = sprintf(filedata, "%u\n", val);
197 if (write(fd, filedata, n) < 0)
198 bb_simple_perror_msg_and_die(name);
199 /* So far all callers exit very soon after calling us.
200 * Do not bother closing fd (unless debugging):
201 */
202 if (ENABLE_FEATURE_CLEAN_UP)
203 close(fd);
204 }
205
206 struct fdb_entry {
207 uint8_t mac_addr[6];
208 uint8_t port_no;
209 uint8_t is_local;
210 uint32_t ageing_timer_value;
211 uint8_t port_hi;
212 uint8_t pad0;
213 uint16_t unused;
214 };
215
compare_fdbs(const void * _f0,const void * _f1)216 static int compare_fdbs(const void *_f0, const void *_f1)
217 {
218 const struct fdb_entry *f0 = _f0;
219 const struct fdb_entry *f1 = _f1;
220
221 return memcmp(f0->mac_addr, f1->mac_addr, 6);
222 }
223
read_bridge_forward_db(const char * name,struct fdb_entry ** _fdb)224 static size_t read_bridge_forward_db(const char *name, struct fdb_entry **_fdb)
225 {
226 char pathbuf[IFNAMSIZ + sizeof("/brforward") + 8];
227 struct fdb_entry *fdb;
228 size_t nentries;
229 int fd;
230 ssize_t cc;
231
232 #if IFNAMSIZ == 16
233 sprintf(pathbuf, "%.16s/brforward", name);
234 #else
235 sprintf(pathbuf, "%.*s/brforward", (int)IFNAMSIZ, name);
236 #endif
237 fd = open(pathbuf, O_RDONLY);
238 if (fd < 0)
239 bb_error_msg_and_die("bridge %s does not exist", name);
240
241 fdb = NULL;
242 nentries = 0;
243 for (;;) {
244 fdb = xrealloc_vector(fdb, 4, nentries);
245 cc = full_read(fd, &fdb[nentries], sizeof(*fdb));
246 if (cc == 0) {
247 break;
248 }
249 if (cc != sizeof(*fdb)) {
250 bb_perror_msg_and_die("can't read bridge %s forward db", name);
251 }
252 ++nentries;
253 }
254
255 if (ENABLE_FEATURE_CLEAN_UP)
256 close(fd);
257
258 qsort(fdb, nentries, sizeof(*fdb), compare_fdbs);
259
260 *_fdb = fdb;
261 return nentries;
262 }
263
show_bridge_macs(const char * name)264 static void show_bridge_macs(const char *name)
265 {
266 struct fdb_entry *fdb;
267 size_t nentries;
268 size_t i;
269
270 nentries = read_bridge_forward_db(name, &fdb);
271
272 printf("port no\tmac addr\t\tis local?\tageing timer\n");
273 for (i = 0; i < nentries; ++i) {
274 const struct fdb_entry *f = &fdb[i];
275 unsigned tv_sec = f->ageing_timer_value / 100;
276 unsigned tv_csec = f->ageing_timer_value % 100;
277 printf("%3u\t"
278 "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\t"
279 "%s\t\t"
280 "%4u.%.2u\n",
281 f->port_no,
282 f->mac_addr[0], f->mac_addr[1], f->mac_addr[2],
283 f->mac_addr[3], f->mac_addr[4], f->mac_addr[5],
284 (f->is_local ? "yes" : "no"),
285 tv_sec, tv_csec
286 );
287 }
288
289 if (ENABLE_FEATURE_CLEAN_UP)
290 free(fdb);
291 }
292
show_bridge_timer(const char * msg)293 static void show_bridge_timer(const char *msg)
294 {
295 unsigned long long centisec = xstrtoull(filedata, 0);
296 unsigned tv_sec = centisec / 100;
297 unsigned tv_csec = centisec % 100;
298 printf("%s%4u.%.2u", msg, tv_sec, tv_csec);
299 }
300
show_bridge_state(unsigned state)301 static const char *show_bridge_state(unsigned state)
302 {
303 /* See linux/if_bridge.h, BR_STATE_ constants */
304 static const char state_names[] ALIGN1 =
305 "disabled\0" //BR_STATE_DISABLED 0
306 "listening\0" //BR_STATE_LISTENING 1
307 "learning\0" //BR_STATE_LEARNING 2
308 "forwarding\0" //BR_STATE_FORWARDING 3
309 "blocking" //BR_STATE_BLOCKING 4
310 ;
311 if (state < 5)
312 return nth_string(state_names, state);
313 return utoa(state);
314 }
315
printf_xstrtou(const char * fmt)316 static void printf_xstrtou(const char *fmt)
317 {
318 printf(fmt, xstrtou(filedata, 0));
319 }
320
show_bridge_port(const char * name)321 static NOINLINE void show_bridge_port(const char *name)
322 {
323 char pathbuf[IFNAMSIZ + sizeof("/brport/forward_delay_timer") + 8];
324 char *sfx;
325
326 #if IFNAMSIZ == 16
327 sfx = pathbuf + sprintf(pathbuf, "%.16s/brport/", name);
328 #else
329 sfx = pathbuf + sprintf(pathbuf, "%.*s/brport/", (int)IFNAMSIZ, name);
330 #endif
331
332 strcpy(sfx, "port_no");
333 read_file(pathbuf);
334 printf("%s (%u)\n", name, xstrtou(filedata, 0));
335
336 strcpy(sfx + 5, "id"); // "port_id"
337 read_file(pathbuf);
338 printf_xstrtou(" port id\t\t%.4x");
339
340 strcpy(sfx, "state");
341 read_file(pathbuf);
342 printf("\t\t\tstate\t\t%15s\n", show_bridge_state(xstrtou(filedata, 0)));
343
344 strcpy(sfx, "designated_root");
345 read_file(pathbuf);
346 printf(" designated root\t%s", filedata);
347
348 strcpy(sfx, "path_cost");
349 read_file(pathbuf);
350 printf_xstrtou("\tpath cost\t\t%4u\n");
351
352 strcpy(sfx, "designated_bridge");
353 read_file(pathbuf);
354 printf(" designated bridge\t%s", filedata);
355
356 strcpy(sfx, "message_age_timer");
357 read_file(pathbuf);
358 show_bridge_timer("\tmessage age timer\t");
359
360 strcpy(sfx, "designated_port");
361 read_file(pathbuf);
362 printf_xstrtou("\n designated port\t%.4x");
363
364 strcpy(sfx, "forward_delay_timer");
365 read_file(pathbuf);
366 show_bridge_timer("\t\t\tforward delay timer\t");
367
368 strcpy(sfx, "designated_cost");
369 read_file(pathbuf);
370 printf_xstrtou("\n designated cost\t%4u");
371
372 strcpy(sfx, "hold_timer");
373 read_file(pathbuf);
374 show_bridge_timer("\t\t\thold timer\t\t");
375
376 printf("\n flags\t\t\t");
377
378 strcpy(sfx, "config_pending");
379 read_file(pathbuf);
380 if (!LONE_CHAR(filedata, '0'))
381 printf("CONFIG_PENDING ");
382
383 strcpy(sfx, "change_ack");
384 read_file(pathbuf);
385 if (!LONE_CHAR(filedata, '0'))
386 printf("TOPOLOGY_CHANGE_ACK ");
387
388 strcpy(sfx, "hairpin_mode");
389 read_file(pathbuf);
390 if (!LONE_CHAR(filedata, '0'))
391 printf_xstrtou("\n hairpin mode\t\t%4u");
392
393 printf("\n\n");
394 }
395
show_bridge_stp(const char * name)396 static void show_bridge_stp(const char *name)
397 {
398 char pathbuf[IFNAMSIZ + sizeof("/bridge/topology_change_timer") + 8];
399 char *sfx;
400
401 #if IFNAMSIZ == 16
402 sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
403 #else
404 sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
405 #endif
406
407 strcpy(sfx, "bridge_id");
408 if (read_file(pathbuf) < 0)
409 bb_error_msg_and_die("bridge %s does not exist", name);
410
411 printf("%s\n"
412 " bridge id\t\t%s", name, filedata);
413
414 strcpy(sfx, "root_id");
415 read_file(pathbuf);
416 printf("\n designated root\t%s", filedata);
417
418 strcpy(sfx + 5, "port"); // "root_port"
419 read_file(pathbuf);
420 printf_xstrtou("\n root port\t\t%4u\t\t\t");
421
422 strcpy(sfx + 6, "ath_cost"); // "root_path_cost"
423 read_file(pathbuf);
424 printf_xstrtou("path cost\t\t%4u\n");
425
426 strcpy(sfx, "max_age");
427 read_file(pathbuf);
428 show_bridge_timer(" max age\t\t");
429 show_bridge_timer("\t\t\tbridge max age\t\t");
430
431 strcpy(sfx, "hello_time");
432 read_file(pathbuf);
433 show_bridge_timer("\n hello time\t\t");
434 show_bridge_timer("\t\t\tbridge hello time\t");
435
436 strcpy(sfx, "forward_delay");
437 read_file(pathbuf);
438 show_bridge_timer("\n forward delay\t\t");
439 show_bridge_timer("\t\t\tbridge forward delay\t");
440
441 strcpy(sfx, "ageing_time");
442 read_file(pathbuf);
443 show_bridge_timer("\n ageing time\t\t");
444
445 strcpy(sfx, "hello_timer");
446 read_file(pathbuf);
447 show_bridge_timer("\n hello timer\t\t");
448
449 strcpy(sfx, "tcn_timer");
450 read_file(pathbuf);
451 show_bridge_timer("\t\t\ttcn timer\t\t");
452
453 strcpy(sfx, "topology_change_timer");
454 read_file(pathbuf);
455 show_bridge_timer("\n topology change timer\t");
456
457 strcpy(sfx, "gc_timer");
458 read_file(pathbuf);
459 show_bridge_timer("\t\t\tgc timer\t\t");
460
461 printf("\n flags\t\t\t");
462
463 strcpy(sfx, "topology_change");
464 read_file(pathbuf);
465 if (!LONE_CHAR(filedata, '0'))
466 printf("TOPOLOGY_CHANGE ");
467
468 strcpy(sfx, "topology_change_detected");
469 read_file(pathbuf);
470 if (!LONE_CHAR(filedata, '0'))
471 printf("TOPOLOGY_CHANGE_DETECTED ");
472 printf("\n\n\n");
473
474 /* Show bridge ports */
475 {
476 DIR *ifaces;
477
478 /* sfx points past "BR/bridge/", turn it into "BR/brif": */
479 sfx[-4] = 'f'; sfx[-3] = '\0';
480 ifaces = opendir(pathbuf);
481 if (ifaces) {
482 struct dirent *ent;
483 while ((ent = readdir(ifaces)) != NULL) {
484 if (DOT_OR_DOTDOT(ent->d_name))
485 continue; /* . or .. */
486 show_bridge_port(ent->d_name);
487 }
488 if (ENABLE_FEATURE_CLEAN_UP)
489 closedir(ifaces);
490 }
491 }
492 }
493 #endif
494
495 int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
brctl_main(int argc UNUSED_PARAM,char ** argv)496 int brctl_main(int argc UNUSED_PARAM, char **argv)
497 {
498 static const char keywords[] ALIGN1 =
499 "addbr\0" "delbr\0" "addif\0" "delif\0"
500 IF_FEATURE_BRCTL_FANCY(
501 "stp\0"
502 "showstp\0"
503 "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
504 "setpathcost\0" "setportprio\0"
505 "setbridgeprio\0"
506 "showmacs\0"
507 )
508 IF_FEATURE_BRCTL_SHOW("show\0");
509 enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
510 IF_FEATURE_BRCTL_FANCY(,
511 ARG_stp,
512 ARG_showstp,
513 ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
514 ARG_setpathcost, ARG_setportprio,
515 ARG_setbridgeprio,
516 ARG_showmacs
517 )
518 IF_FEATURE_BRCTL_SHOW(, ARG_show)
519 };
520 int key;
521 char *br;
522
523 argv++;
524 if (!*argv) {
525 /* bare "brctl" shows --help */
526 bb_show_usage();
527 }
528
529 xchdir("/sys/class/net");
530
531 key = index_in_strings(keywords, *argv);
532 if (key == -1) /* no match found in keywords array, bail out. */
533 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
534 argv++;
535
536 #if ENABLE_FEATURE_BRCTL_SHOW
537 if (key == ARG_show) { /* show [BR]... */
538 DIR *net;
539 struct dirent *ent;
540 int need_hdr = 1;
541 int exitcode = EXIT_SUCCESS;
542
543 if (*argv) {
544 /* "show BR1 BR2 BR3" */
545 do {
546 if (show_bridge(*argv, need_hdr) >= 0) {
547 need_hdr = 0;
548 } else {
549 bb_error_msg("bridge %s does not exist", *argv);
550 //TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6
551 //says this instead: "device eth0 is not a bridge"
552 exitcode = EXIT_FAILURE;
553 }
554 } while (*++argv != NULL);
555 return exitcode;
556 }
557
558 /* "show" (if no ifaces, shows nothing, not even header) */
559 net = xopendir(".");
560 while ((ent = readdir(net)) != NULL) {
561 if (DOT_OR_DOTDOT(ent->d_name))
562 continue; /* . or .. */
563 if (show_bridge(ent->d_name, need_hdr) >= 0)
564 need_hdr = 0;
565 }
566 if (ENABLE_FEATURE_CLEAN_UP)
567 closedir(net);
568 return exitcode;
569 }
570 #endif
571
572 if (!*argv) /* All of the below need at least one argument */
573 bb_show_usage();
574
575 br = *argv++;
576
577 if (key == ARG_addbr || key == ARG_delbr) {
578 /* brctl from bridge-utils 1.6 still uses ioctl
579 * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
580 */
581 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
582 ioctl_or_perror_and_die(fd,
583 key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
584 br, "bridge %s", br
585 );
586 //close(fd);
587 //goto done;
588 /* bridge-utils 1.6 simply ignores trailing args:
589 * "brctl addbr BR1 ARGS" ignores ARGS
590 */
591 if (ENABLE_FEATURE_CLEAN_UP)
592 close(fd);
593 return EXIT_SUCCESS;
594 }
595
596 #if ENABLE_FEATURE_BRCTL_FANCY
597 if (key == ARG_showmacs) {
598 show_bridge_macs(br);
599 return EXIT_SUCCESS;
600 }
601 if (key == ARG_showstp) {
602 show_bridge_stp(br);
603 return EXIT_SUCCESS;
604 }
605 #endif
606
607 if (!*argv) /* All of the below need at least two arguments */
608 bb_show_usage();
609
610 #if ENABLE_FEATURE_BRCTL_FANCY
611 if (key == ARG_stp) {
612 static const char no_yes[] ALIGN1 =
613 "0\0" "off\0" "n\0" "no\0" /* 0 .. 3 */
614 "1\0" "on\0" "y\0" "yes\0"; /* 4 .. 7 */
615 int onoff = index_in_strings(no_yes, *argv);
616 if (onoff < 0)
617 bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
618 onoff = (unsigned)onoff / 4;
619 write_uint(br, "bridge/stp_state", onoff);
620 return EXIT_SUCCESS;
621 }
622
623 if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
624 /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time
625 * setfd BR N: "N*100\n" to /sys/class/net/BR/bridge/forward_delay
626 * sethello BR N: "N*100\n" to /sys/class/net/BR/bridge/hello_time
627 * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age
628 */
629 write_uint(br,
630 nth_string(
631 "bridge/ageing_time" "\0" /* ARG_setageing */
632 "bridge/forward_delay""\0" /* ARG_setfd */
633 "bridge/hello_time" "\0" /* ARG_sethello */
634 "bridge/max_age", /* ARG_setmaxage */
635 key - ARG_setageing
636 ),
637 str_to_jiffies(*argv)
638 );
639 return EXIT_SUCCESS;
640 }
641
642 if (key == ARG_setbridgeprio) {
643 write_uint(br, "bridge/priority", xatoi_positive(*argv));
644 return EXIT_SUCCESS;
645 }
646
647 if (key == ARG_setpathcost
648 || key == ARG_setportprio
649 ) {
650 if (!argv[1])
651 bb_show_usage();
652 /* BR is not used (and ignored!) for these commands:
653 * "setpathcost BR PORT N" writes "N\n" to
654 * /sys/class/net/PORT/brport/path_cost
655 * "setportprio BR PORT N" writes "N\n" to
656 * /sys/class/net/PORT/brport/priority
657 */
658 write_uint(argv[0],
659 nth_string(
660 "brport/path_cost" "\0" /* ARG_setpathcost */
661 "brport/priority", /* ARG_setportprio */
662 key - ARG_setpathcost
663 ),
664 xatoi_positive(argv[1])
665 );
666 return EXIT_SUCCESS;
667 }
668 #endif
669 /* always true: if (key == ARG_addif || key == ARG_delif) */ {
670 struct ifreq ifr;
671 int fd = xsocket(AF_INET, SOCK_STREAM, 0);
672
673 strncpy_IFNAMSIZ(ifr.ifr_name, br);
674 ifr.ifr_ifindex = if_nametoindex(*argv);
675 if (ifr.ifr_ifindex == 0) {
676 bb_perror_msg_and_die("iface %s", *argv);
677 }
678 ioctl_or_perror_and_die(fd,
679 key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
680 &ifr, "bridge %s", br
681 );
682 if (ENABLE_FEATURE_CLEAN_UP)
683 close(fd);
684 }
685
686 return EXIT_SUCCESS;
687 }
688