busybox/networking/brctl.c
<<
>>
Prefs
   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
  88static 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
 111static 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 */
 128static 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
 185static 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
 206struct 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
 216static 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
 224static 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
 264static 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
 293static 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
 301static 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
 316static void printf_xstrtou(const char *fmt)
 317{
 318        printf(fmt, xstrtou(filedata, 0));
 319}
 320
 321static 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
 396static 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
 495int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 496int 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