Start 1.33.0 development cycle
[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 || ENABLE_FEATURE_BRCTL_FANCY
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  */
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(filedata, stdout);
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
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
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
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
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
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
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
316 static void printf_xstrtou(const char *fmt)
317 {
318         printf(fmt, xstrtou(filedata, 0));
319 }
320
321 static 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
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;
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 }