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:       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        stp BRIDGE 1/yes/on|0/no/off    STP on/off"
  57//usage:     "\n        setageing BRIDGE SECONDS        Set ageing time"
  58//usage:     "\n        setfd BRIDGE SECONDS            Set bridge forward delay"
  59//usage:     "\n        sethello BRIDGE SECONDS         Set hello time"
  60//usage:     "\n        setmaxage BRIDGE SECONDS        Set max message age"
  61//usage:     "\n        setbridgeprio BRIDGE PRIO       Set bridge priority"
  62//usage:     "\n        setportprio BRIDGE IFACE PRIO   Set port priority"
  63//usage:     "\n        setpathcost BRIDGE IFACE COST   Set path cost"
  64//usage:        )
  65// Not yet implemented:
  66//                      hairpin BRIDGE IFACE on|off     Hairpin on/off
  67//                      showmacs BRIDGE                 List mac addrs
  68//                      showstp BRIDGE                  Show stp info
  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
  89static 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
 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
 124/* NB: we are in /sys/class/net
 125 */
 126static 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") + 32];
 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", name);
 150        printf("%s\t", filedata);
 151
 152        strcpy(sfx, "stp_state");
 153        read_file(pathbuf);
 154        if (LONE_CHAR(filedata, '0'))
 155                strcpy(filedata, "no");
 156        else
 157        if (LONE_CHAR(filedata, '1'))
 158                strcpy(filedata, "yes");
 159        fputs(filedata, stdout);
 160
 161        strcpy(sfx - (sizeof("bridge/")-1), "brif");
 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
 183static 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        close(fd);
 198}
 199#endif
 200
 201int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 202int brctl_main(int argc UNUSED_PARAM, char **argv)
 203{
 204        static const char keywords[] ALIGN1 =
 205                "addbr\0" "delbr\0" "addif\0" "delif\0"
 206        IF_FEATURE_BRCTL_FANCY(
 207                "stp\0"
 208                "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
 209                "setpathcost\0" "setportprio\0"
 210                "setbridgeprio\0"
 211        )
 212        IF_FEATURE_BRCTL_SHOW("show\0");
 213        enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
 214                IF_FEATURE_BRCTL_FANCY(,
 215                        ARG_stp,
 216                        ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
 217                        ARG_setpathcost, ARG_setportprio,
 218                        ARG_setbridgeprio
 219                )
 220                IF_FEATURE_BRCTL_SHOW(, ARG_show)
 221        };
 222
 223        argv++;
 224        if (!*argv) {
 225                /* bare "brctl" shows --help */
 226                bb_show_usage();
 227        }
 228
 229        xchdir("/sys/class/net");
 230
 231//      while (*argv)
 232        {
 233                smallint key;
 234                char *br;
 235
 236                key = index_in_strings(keywords, *argv);
 237                if (key == -1) /* no match found in keywords array, bail out. */
 238                        bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
 239                argv++;
 240
 241#if ENABLE_FEATURE_BRCTL_SHOW
 242                if (key == ARG_show) { /* show [BR]... */
 243                        DIR *net;
 244                        struct dirent *ent;
 245                        int need_hdr = 1;
 246                        int exitcode = EXIT_SUCCESS;
 247
 248                        if (*argv) {
 249                                /* "show BR1 BR2 BR3" */
 250                                do {
 251                                        if (show_bridge(*argv, need_hdr) >= 0) {
 252                                                need_hdr = 0;
 253                                        } else {
 254                                                bb_error_msg("bridge %s does not exist", *argv);
 255//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6
 256//says this instead: "device eth0 is not a bridge"
 257                                                exitcode = EXIT_FAILURE;
 258                                        }
 259                                } while (*++argv != NULL);
 260                                return exitcode;
 261                        }
 262
 263                        /* "show" (if no ifaces, shows nothing, not even header) */
 264                        net = xopendir(".");
 265                        while ((ent = readdir(net)) != NULL) {
 266                                if (DOT_OR_DOTDOT(ent->d_name))
 267                                        continue; /* . or .. */
 268                                if (show_bridge(ent->d_name, need_hdr) >= 0)
 269                                        need_hdr = 0;
 270                        }
 271                        if (ENABLE_FEATURE_CLEAN_UP)
 272                                closedir(net);
 273                        return exitcode;
 274                }
 275#endif
 276
 277                if (!*argv) /* all but 'show' need at least one argument */
 278                        bb_show_usage();
 279
 280                br = *argv++;
 281
 282                if (key == ARG_addbr || key == ARG_delbr) {
 283                        /* addbr or delbr */
 284                        /* brctl from bridge-utils 1.6 still uses ioctl
 285                         * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
 286                         */
 287                        int fd = xsocket(AF_INET, SOCK_STREAM, 0);
 288                        ioctl_or_perror_and_die(fd,
 289                                key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
 290                                br, "bridge %s", br
 291                        );
 292                        //close(fd);
 293                        //goto done;
 294                        /* bridge-utils 1.6 simply ignores trailing args:
 295                         * "brctl addbr BR1 ARGS" ignores ARGS
 296                         */
 297                        if (ENABLE_FEATURE_CLEAN_UP)
 298                                close(fd);
 299                        return EXIT_SUCCESS;
 300                }
 301
 302                if (!*argv) /* all but 'addbr/delbr' need at least two arguments */
 303                        bb_show_usage();
 304
 305#if ENABLE_FEATURE_BRCTL_FANCY
 306                if (key == ARG_stp) { /* stp */
 307                        static const char no_yes[] ALIGN1 =
 308                                "0\0" "off\0" "n\0" "no\0"   /* 0 .. 3 */
 309                                "1\0" "on\0"  "y\0" "yes\0"; /* 4 .. 7 */
 310                        int onoff = index_in_strings(no_yes, *argv);
 311                        if (onoff < 0)
 312                                bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
 313                        onoff = (unsigned)onoff / 4;
 314                        write_uint(br, "bridge/stp_state", onoff);
 315                        //goto done_next_argv;
 316                        return EXIT_SUCCESS;
 317                }
 318
 319                if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
 320                        /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time
 321                         * setfd BR N:     "N*100\n" to /sys/class/net/BR/bridge/forward_delay
 322                         * sethello BR N:  "N*100\n" to /sys/class/net/BR/bridge/hello_time
 323                         * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age
 324                         */
 325                        write_uint(br,
 326                                nth_string(
 327                                        "bridge/ageing_time"  "\0" /* ARG_setageing */
 328                                        "bridge/forward_delay""\0" /* ARG_setfd     */
 329                                        "bridge/hello_time"   "\0" /* ARG_sethello  */
 330                                        "bridge/max_age",          /* ARG_setmaxage */
 331                                        key - ARG_setageing
 332                                ),
 333                                str_to_jiffies(*argv)
 334                        );
 335                        //goto done_next_argv;
 336                        return EXIT_SUCCESS;
 337                }
 338
 339                if (key == ARG_setbridgeprio) {
 340                        write_uint(br, "bridge/priority", xatoi_positive(*argv));
 341                        //goto done_next_argv;
 342                        return EXIT_SUCCESS;
 343                }
 344
 345                if (key == ARG_setpathcost
 346                 || key == ARG_setportprio
 347                ) {
 348                        if (!argv[1])
 349                                bb_show_usage();
 350                        /* BR is not used (and ignored!) for these commands:
 351                         * "setpathcost BR PORT N" writes "N\n" to
 352                         * /sys/class/net/PORT/brport/path_cost
 353                         * "setportprio BR PORT N" writes "N\n" to
 354                         * /sys/class/net/PORT/brport/priority
 355                         */
 356                        write_uint(argv[0],
 357                                nth_string(
 358                                        "brport/path_cost" "\0" /* ARG_setpathcost */
 359                                        "brport/priority",      /* ARG_setportprio */
 360                                        key - ARG_setpathcost
 361                                ),
 362                                xatoi_positive(argv[1])
 363                        );
 364                        //argv++;
 365                        //goto done_next_argv;
 366                        return EXIT_SUCCESS;
 367                }
 368
 369/* TODO: "showmacs BR"
 370 *      port no\tmac addr\t\tis local?\tageing timer
 371 *      <sp><sp>1\txx:xx:xx:xx:xx:xx\tno\t\t<sp><sp><sp>1.31
 372 *      port no mac addr                is local?       ageing timer
 373 *        1     xx:xx:xx:xx:xx:xx       no                 1.31
 374 * Read fixed-sized records from /sys/class/net/BR/brforward:
 375 *      struct __fdb_entry {
 376 *              uint8_t  mac_addr[ETH_ALEN];
 377 *              uint8_t  port_no; //lsb
 378 *              uint8_t  is_local;
 379 *              uint32_t ageing_timer_value;
 380 *              uint8_t  port_hi;
 381 *              uint8_t  pad0;
 382 *              uint16_t unused;
 383 *      };
 384 */
 385#endif
 386                /* always true: if (key == ARG_addif || key == ARG_delif) */ {
 387                        /* addif or delif */
 388                        struct ifreq ifr;
 389                        int fd = xsocket(AF_INET, SOCK_STREAM, 0);
 390
 391                        strncpy_IFNAMSIZ(ifr.ifr_name, br);
 392                        ifr.ifr_ifindex = if_nametoindex(*argv);
 393                        if (ifr.ifr_ifindex == 0) {
 394                                bb_perror_msg_and_die("iface %s", *argv);
 395                        }
 396                        ioctl_or_perror_and_die(fd,
 397                                key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
 398                                &ifr, "bridge %s", br
 399                        );
 400                        //close(fd);
 401                        //goto done_next_argv;
 402                        if (ENABLE_FEATURE_CLEAN_UP)
 403                                close(fd);
 404                        return EXIT_SUCCESS;
 405                }
 406
 407// done_next_argv:
 408//              argv++;
 409// done:
 410        }
 411
 412        return EXIT_SUCCESS;
 413}
 414