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/* This applet currently uses only the ioctl interface and no sysfs at all.
  13 * At the time of this writing this was considered a feature.
  14 */
  15
  16//usage:#define brctl_trivial_usage
  17//usage:       "COMMAND [BRIDGE [INTERFACE]]"
  18//usage:#define brctl_full_usage "\n\n"
  19//usage:       "Manage ethernet bridges\n"
  20//usage:     "\nCommands:"
  21//usage:        IF_FEATURE_BRCTL_SHOW(
  22//usage:     "\n        show                    Show a list of bridges"
  23//usage:        )
  24//usage:     "\n        addbr BRIDGE            Create BRIDGE"
  25//usage:     "\n        delbr BRIDGE            Delete BRIDGE"
  26//usage:     "\n        addif BRIDGE IFACE      Add IFACE to BRIDGE"
  27//usage:     "\n        delif BRIDGE IFACE      Delete IFACE from BRIDGE"
  28//usage:        IF_FEATURE_BRCTL_FANCY(
  29//usage:     "\n        setageing BRIDGE TIME           Set ageing time"
  30//usage:     "\n        setfd BRIDGE TIME               Set bridge forward delay"
  31//usage:     "\n        sethello BRIDGE TIME            Set hello time"
  32//usage:     "\n        setmaxage BRIDGE TIME           Set max message age"
  33//usage:     "\n        setpathcost BRIDGE COST         Set path cost"
  34//usage:     "\n        setportprio BRIDGE PRIO         Set port priority"
  35//usage:     "\n        setbridgeprio BRIDGE PRIO       Set bridge priority"
  36//usage:     "\n        stp BRIDGE [1/yes/on|0/no/off]  STP on/off"
  37//usage:        )
  38
  39#include "libbb.h"
  40#include <linux/sockios.h>
  41#include <net/if.h>
  42
  43#ifndef SIOCBRADDBR
  44# define SIOCBRADDBR BRCTL_ADD_BRIDGE
  45#endif
  46#ifndef SIOCBRDELBR
  47# define SIOCBRDELBR BRCTL_DEL_BRIDGE
  48#endif
  49#ifndef SIOCBRADDIF
  50# define SIOCBRADDIF BRCTL_ADD_IF
  51#endif
  52#ifndef SIOCBRDELIF
  53# define SIOCBRDELIF BRCTL_DEL_IF
  54#endif
  55
  56
  57/* Maximum number of ports supported per bridge interface.  */
  58#ifndef MAX_PORTS
  59# define MAX_PORTS 32
  60#endif
  61
  62/* Use internal number parsing and not the "exact" conversion.  */
  63/* #define BRCTL_USE_INTERNAL 0 */ /* use exact conversion */
  64#define BRCTL_USE_INTERNAL 1
  65
  66#if ENABLE_FEATURE_BRCTL_FANCY
  67# include <linux/if_bridge.h>
  68
  69/* FIXME: These 4 funcs are not really clean and could be improved */
  70static ALWAYS_INLINE void bb_strtotimeval(struct timeval *tv,
  71                const char *time_str)
  72{
  73        double secs;
  74# if BRCTL_USE_INTERNAL
  75        char *endptr;
  76        secs = /*bb_*/strtod(time_str, &endptr);
  77        if (endptr == time_str)
  78# else
  79        if (sscanf(time_str, "%lf", &secs) != 1)
  80# endif
  81                bb_error_msg_and_die(bb_msg_invalid_arg, time_str, "timespec");
  82        tv->tv_sec = secs;
  83        tv->tv_usec = 1000000 * (secs - tv->tv_sec);
  84}
  85
  86static ALWAYS_INLINE unsigned long tv_to_jiffies(const struct timeval *tv)
  87{
  88        unsigned long long jif;
  89
  90        jif = 1000000ULL * tv->tv_sec + tv->tv_usec;
  91
  92        return jif/10000;
  93}
  94# if 0
  95static void jiffies_to_tv(struct timeval *tv, unsigned long jiffies)
  96{
  97        unsigned long long tvusec;
  98
  99        tvusec = 10000ULL*jiffies;
 100        tv->tv_sec = tvusec/1000000;
 101        tv->tv_usec = tvusec - 1000000 * tv->tv_sec;
 102}
 103# endif
 104static unsigned long str_to_jiffies(const char *time_str)
 105{
 106        struct timeval tv;
 107        bb_strtotimeval(&tv, time_str);
 108        return tv_to_jiffies(&tv);
 109}
 110
 111static void arm_ioctl(unsigned long *args,
 112                unsigned long arg0, unsigned long arg1, unsigned long arg2)
 113{
 114        args[0] = arg0;
 115        args[1] = arg1;
 116        args[2] = arg2;
 117        args[3] = 0;
 118}
 119#endif
 120
 121
 122int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 123int brctl_main(int argc UNUSED_PARAM, char **argv)
 124{
 125        static const char keywords[] ALIGN1 =
 126                "addbr\0" "delbr\0" "addif\0" "delif\0"
 127        IF_FEATURE_BRCTL_FANCY(
 128                "stp\0"
 129                "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
 130                "setpathcost\0" "setportprio\0" "setbridgeprio\0"
 131        )
 132        IF_FEATURE_BRCTL_SHOW("show\0");
 133
 134        enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
 135                IF_FEATURE_BRCTL_FANCY(,
 136                        ARG_stp,
 137                        ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
 138                        ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio
 139                )
 140                IF_FEATURE_BRCTL_SHOW(, ARG_show)
 141        };
 142
 143        int fd;
 144        smallint key;
 145        struct ifreq ifr;
 146        char *br, *brif;
 147
 148        argv++;
 149        while (*argv) {
 150#if ENABLE_FEATURE_BRCTL_FANCY
 151                int ifidx[MAX_PORTS];
 152                unsigned long args[4];
 153                ifr.ifr_data = (char *) &args;
 154#endif
 155
 156                key = index_in_strings(keywords, *argv);
 157                if (key == -1) /* no match found in keywords array, bail out. */
 158                        bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
 159                argv++;
 160                fd = xsocket(AF_INET, SOCK_STREAM, 0);
 161
 162#if ENABLE_FEATURE_BRCTL_SHOW
 163                if (key == ARG_show) { /* show */
 164                        char brname[IFNAMSIZ];
 165                        int bridx[MAX_PORTS];
 166                        int i, num;
 167                        arm_ioctl(args, BRCTL_GET_BRIDGES,
 168                                                (unsigned long) bridx, MAX_PORTS);
 169                        num = xioctl(fd, SIOCGIFBR, args);
 170                        printf("bridge name\tbridge id\t\tSTP enabled\tinterfaces\n");
 171                        for (i = 0; i < num; i++) {
 172                                char ifname[IFNAMSIZ];
 173                                int j, tabs;
 174                                struct __bridge_info bi;
 175                                unsigned char *x;
 176
 177                                if (!if_indextoname(bridx[i], brname))
 178                                        bb_perror_msg_and_die("can't get bridge name for index %d", i);
 179                                strncpy_IFNAMSIZ(ifr.ifr_name, brname);
 180
 181                                arm_ioctl(args, BRCTL_GET_BRIDGE_INFO,
 182                                                        (unsigned long) &bi, 0);
 183                                xioctl(fd, SIOCDEVPRIVATE, &ifr);
 184                                printf("%s\t\t", brname);
 185
 186                                /* print bridge id */
 187                                x = (unsigned char *) &bi.bridge_id;
 188                                for (j = 0; j < 8; j++) {
 189                                        printf("%.2x", x[j]);
 190                                        if (j == 1)
 191                                                bb_putchar('.');
 192                                }
 193                                printf(bi.stp_enabled ? "\tyes" : "\tno");
 194
 195                                /* print interface list */
 196                                arm_ioctl(args, BRCTL_GET_PORT_LIST,
 197                                                        (unsigned long) ifidx, MAX_PORTS);
 198                                xioctl(fd, SIOCDEVPRIVATE, &ifr);
 199                                tabs = 0;
 200                                for (j = 0; j < MAX_PORTS; j++) {
 201                                        if (!ifidx[j])
 202                                                continue;
 203                                        if (!if_indextoname(ifidx[j], ifname))
 204                                                bb_perror_msg_and_die("can't get interface name for index %d", j);
 205                                        if (tabs)
 206                                                printf("\t\t\t\t\t");
 207                                        else
 208                                                tabs = 1;
 209                                        printf("\t\t%s\n", ifname);
 210                                }
 211                                if (!tabs)  /* bridge has no interfaces */
 212                                        bb_putchar('\n');
 213                        }
 214                        goto done;
 215                }
 216#endif
 217
 218                if (!*argv) /* all but 'show' need at least one argument */
 219                        bb_show_usage();
 220
 221                br = *argv++;
 222
 223                if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
 224                        ioctl_or_perror_and_die(fd,
 225                                        key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
 226                                        br, "bridge %s", br);
 227                        goto done;
 228                }
 229
 230                if (!*argv) /* all but 'addbr/delbr' need at least two arguments */
 231                        bb_show_usage();
 232
 233                strncpy_IFNAMSIZ(ifr.ifr_name, br);
 234                if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
 235                        brif = *argv;
 236                        ifr.ifr_ifindex = if_nametoindex(brif);
 237                        if (!ifr.ifr_ifindex) {
 238                                bb_perror_msg_and_die("iface %s", brif);
 239                        }
 240                        ioctl_or_perror_and_die(fd,
 241                                        key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
 242                                        &ifr, "bridge %s", br);
 243                        goto done_next_argv;
 244                }
 245#if ENABLE_FEATURE_BRCTL_FANCY
 246                if (key == ARG_stp) { /* stp */
 247                        static const char no_yes[] ALIGN1 =
 248                                "0\0" "off\0" "n\0" "no\0"   /* 0 .. 3 */
 249                                "1\0" "on\0"  "y\0" "yes\0"; /* 4 .. 7 */
 250                        int onoff = index_in_strings(no_yes, *argv);
 251                        if (onoff < 0)
 252                                bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
 253                        onoff = (unsigned)onoff / 4;
 254                        arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0);
 255                        goto fire;
 256                }
 257                if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
 258                        static const uint8_t ops[] ALIGN1 = {
 259                                BRCTL_SET_AGEING_TIME,          /* ARG_setageing */
 260                                BRCTL_SET_BRIDGE_FORWARD_DELAY, /* ARG_setfd     */
 261                                BRCTL_SET_BRIDGE_HELLO_TIME,    /* ARG_sethello  */
 262                                BRCTL_SET_BRIDGE_MAX_AGE        /* ARG_setmaxage */
 263                        };
 264                        arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0);
 265                        goto fire;
 266                }
 267                if (key == ARG_setpathcost
 268                 || key == ARG_setportprio
 269                 || key == ARG_setbridgeprio
 270                ) {
 271                        static const uint8_t ops[] ALIGN1 = {
 272                                BRCTL_SET_PATH_COST,      /* ARG_setpathcost   */
 273                                BRCTL_SET_PORT_PRIORITY,  /* ARG_setportprio   */
 274                                BRCTL_SET_BRIDGE_PRIORITY /* ARG_setbridgeprio */
 275                        };
 276                        int port = -1;
 277                        unsigned arg1, arg2;
 278
 279                        if (key != ARG_setbridgeprio) {
 280                                /* get portnum */
 281                                unsigned i;
 282
 283                                port = if_nametoindex(*argv++);
 284                                if (!port)
 285                                        bb_error_msg_and_die(bb_msg_invalid_arg, *argv, "port");
 286                                memset(ifidx, 0, sizeof ifidx);
 287                                arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx,
 288                                                MAX_PORTS);
 289                                xioctl(fd, SIOCDEVPRIVATE, &ifr);
 290                                for (i = 0; i < MAX_PORTS; i++) {
 291                                        if (ifidx[i] == port) {
 292                                                port = i;
 293                                                break;
 294                                        }
 295                                }
 296                        }
 297                        arg1 = port;
 298                        arg2 = xatoi_positive(*argv);
 299                        if (key == ARG_setbridgeprio) {
 300                                arg1 = arg2;
 301                                arg2 = 0;
 302                        }
 303                        arm_ioctl(args, ops[key - ARG_setpathcost], arg1, arg2);
 304                }
 305 fire:
 306                /* Execute the previously set command */
 307                xioctl(fd, SIOCDEVPRIVATE, &ifr);
 308#endif
 309 done_next_argv:
 310                argv++;
 311 done:
 312                close(fd);
 313        }
 314
 315        return EXIT_SUCCESS;
 316}
 317