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