brctl: convert remaining commands to work via /sys
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 13 Apr 2019 11:58:06 +0000 (13:58 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 13 Apr 2019 11:58:06 +0000 (13:58 +0200)
function                                             old     new   delta
write_ulong                                            -      96     +96
show_bridge                                          310     338     +28
static.ops                                             3       -      -3
arm_ioctl                                             20       -     -20
packed_usage                                       33344   33315     -29
brctl_main                                           885     721    -164
------------------------------------------------------------------------------
(add/remove: 1/2 grow/shrink: 1/2 up/down: 124/-216)          Total: -92 bytes
   text    data     bss     dec     hex filename
 982112     485    7296  989893   f1ac5 busybox_old
 982157     485    7296  989938   f1af2 busybox_unstripped

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
networking/brctl.c

index 706ecfc07273ebd49044f6767dc78eeda8ca85b3..804728e3cca209d5dca6e5251c15ceb6af2a05a9 100644 (file)
 //kbuild:lib-$(CONFIG_BRCTL) += brctl.o
 
 //usage:#define brctl_trivial_usage
-//usage:       "COMMAND [BRIDGE [INTERFACE]]"
+//usage:       "COMMAND [BRIDGE [ARGS]]"
 //usage:#define brctl_full_usage "\n\n"
 //usage:       "Manage ethernet bridges\n"
 //usage:     "\nCommands:"
 //usage:       IF_FEATURE_BRCTL_SHOW(
-//usage:     "\n       show                    Show a list of bridges"
+//usage:     "\n       show [BRIDGE]...        Show bridges"
 //usage:       )
 //usage:     "\n       addbr BRIDGE            Create BRIDGE"
 //usage:     "\n       delbr BRIDGE            Delete BRIDGE"
 //usage:     "\n       addif BRIDGE IFACE      Add IFACE to BRIDGE"
 //usage:     "\n       delif BRIDGE IFACE      Delete IFACE from BRIDGE"
 //usage:       IF_FEATURE_BRCTL_FANCY(
-//usage:     "\n       setageing BRIDGE TIME           Set ageing time"
-//usage:     "\n       setfd BRIDGE TIME               Set bridge forward delay"
-//usage:     "\n       sethello BRIDGE TIME            Set hello time"
-//usage:     "\n       setmaxage BRIDGE TIME           Set max message age"
-//usage:     "\n       setpathcost BRIDGE COST         Set path cost"
-//usage:     "\n       setportprio BRIDGE PRIO         Set port priority"
+//usage:     "\n       stp BRIDGE 1/yes/on|0/no/off    STP on/off"
+//usage:     "\n       setageing BRIDGE SECONDS        Set ageing time"
+//usage:     "\n       setfd BRIDGE SECONDS            Set bridge forward delay"
+//usage:     "\n       sethello BRIDGE SECONDS         Set hello time"
+//usage:     "\n       setmaxage BRIDGE SECONDS        Set max message age"
 //usage:     "\n       setbridgeprio BRIDGE PRIO       Set bridge priority"
-//usage:     "\n       stp BRIDGE [1/yes/on|0/no/off]  STP on/off"
+//usage:     "\n       setportprio BRIDGE IFACE PRIO   Set port priority"
+//usage:     "\n       setpathcost BRIDGE IFACE COST   Set path cost"
 //usage:       )
+// Not yet implemented:
+//                     hairpin         BRIDGE IFACE on|off     Hairpin on/off
+//                     showmacs        BRIDGE                  List mac addrs
+//                     showstp         BRIDGE                  Show stp info
 
 #include "libbb.h"
 #include "common_bufsiz.h"
 # define SIOCBRDELIF BRCTL_DEL_IF
 #endif
 
-
-/* Maximum number of ports supported per bridge interface.  */
-#ifndef MAX_PORTS
-# define MAX_PORTS 32
-#endif
-
 /* Use internal number parsing and not the "exact" conversion.  */
 /* #define BRCTL_USE_INTERNAL 0 */ /* use exact conversion */
 #define BRCTL_USE_INTERNAL 1
 
 #if ENABLE_FEATURE_BRCTL_FANCY
-/* #include <linux/if_bridge.h>
- * breaks on musl: we already included netinet/in.h in libbb.h,
- * if we include <linux/if_bridge.h> here, we get this:
- * In file included from /usr/include/linux/if_bridge.h:18,
- *                  from networking/brctl.c:67:
- * /usr/include/linux/in6.h:32: error: redefinition of 'struct in6_addr'
- * /usr/include/linux/in6.h:49: error: redefinition of 'struct sockaddr_in6'
- * /usr/include/linux/in6.h:59: error: redefinition of 'struct ipv6_mreq'
- */
-/* From <linux/if_bridge.h> */
-#define BRCTL_GET_VERSION 0
-#define BRCTL_GET_BRIDGES 1
-#define BRCTL_ADD_BRIDGE 2
-#define BRCTL_DEL_BRIDGE 3
-#define BRCTL_ADD_IF 4
-#define BRCTL_DEL_IF 5
-#define BRCTL_GET_BRIDGE_INFO 6
-#define BRCTL_GET_PORT_LIST 7
-#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
-#define BRCTL_SET_BRIDGE_HELLO_TIME 9
-#define BRCTL_SET_BRIDGE_MAX_AGE 10
-#define BRCTL_SET_AGEING_TIME 11
-#define BRCTL_SET_GC_INTERVAL 12
-#define BRCTL_GET_PORT_INFO 13
-#define BRCTL_SET_BRIDGE_STP_STATE 14
-#define BRCTL_SET_BRIDGE_PRIORITY 15
-#define BRCTL_SET_PORT_PRIORITY 16
-#define BRCTL_SET_PATH_COST 17
-#define BRCTL_GET_FDB_ENTRIES 18
-struct __bridge_info {
-       uint64_t designated_root;
-       uint64_t bridge_id;
-       uint32_t root_path_cost;
-       uint32_t max_age;
-       uint32_t hello_time;
-       uint32_t forward_delay;
-       uint32_t bridge_max_age;
-       uint32_t bridge_hello_time;
-       uint32_t bridge_forward_delay;
-       uint8_t  topology_change;
-       uint8_t  topology_change_detected;
-       uint8_t  root_port;
-       uint8_t  stp_enabled;
-       uint32_t ageing_time;
-       uint32_t gc_interval;
-       uint32_t hello_timer_value;
-       uint32_t tcn_timer_value;
-       uint32_t topology_change_timer_value;
-       uint32_t gc_timer_value;
-};
-/* end <linux/if_bridge.h> */
-
 /* FIXME: These 4 funcs are not really clean and could be improved */
 static ALWAYS_INLINE void bb_strtotimeval(struct timeval *tv,
                const char *time_str)
@@ -188,18 +134,10 @@ static unsigned long str_to_jiffies(const char *time_str)
        bb_strtotimeval(&tv, time_str);
        return tv_to_jiffies(&tv);
 }
-
-static void arm_ioctl(unsigned long *args,
-               unsigned long arg0, unsigned long arg1, unsigned long arg2)
-{
-       args[0] = arg0;
-       args[1] = arg1;
-       args[2] = arg2;
-       args[3] = 0;
-}
 #endif
 
 #define filedata bb_common_bufsiz1
+
 static int read_file(const char *name)
 {
        int n = open_read_close(name, filedata, COMMON_BUFSIZE - 1);
@@ -217,16 +155,21 @@ static int read_file(const char *name)
  */
 static int show_bridge(const char *name, int need_hdr)
 {
-// Output:
-//bridge name  bridge id               STP enabled     interfaces
-//br0          8000.000000000000       no              eth0
+/* Output:
+ *bridge name  bridge id               STP enabled     interfaces
+ *br0          8000.000000000000       no              eth0
+ */
        char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
        int tabs;
        DIR *ifaces;
        struct dirent *ent;
        char *sfx;
 
-       sfx = pathbuf + sprintf(pathbuf, "%s/bridge/", name);
+#if IFNAMSIZ == 16
+       sfx = pathbuf + sprintf(pathbuf, "%.16s/bridge/", name);
+#else
+       sfx = pathbuf + sprintf(pathbuf, "%.*s/bridge/", (int)IFNAMSIZ, name);
+#endif
        strcpy(sfx, "bridge_id");
        if (read_file(pathbuf) < 0)
                return -1; /* this iface is not a bridge */
@@ -243,13 +186,15 @@ static int show_bridge(const char *name, int need_hdr)
        else
        if (LONE_CHAR(filedata, '1'))
                strcpy(filedata, "yes");
-       printf(filedata);
+       fputs(filedata, stdout);
 
-       strcpy(sfx, "brif");
+       strcpy(sfx - (sizeof("bridge/")-1), "brif");
        tabs = 0;
        ifaces = opendir(pathbuf);
        if (ifaces) {
                while ((ent = readdir(ifaces)) != NULL) {
+                       if (DOT_OR_DOTDOT(ent->d_name))
+                               continue; /* . or .. */
                        if (tabs)
                                printf("\t\t\t\t\t");
                        else
@@ -263,6 +208,23 @@ static int show_bridge(const char *name, int need_hdr)
        return 0;
 }
 
+static void write_ulong(const char *name, const char *leaf, unsigned long val)
+{
+       char pathbuf[IFNAMSIZ + sizeof("/bridge/bridge_id") + 32];
+       int fd, n;
+
+#if IFNAMSIZ == 16
+       sprintf(pathbuf, "%.16s/%s", name, leaf);
+#else
+       sprintf(pathbuf, "%.*s/%s", (int)IFNAMSIZ, name, leaf);
+#endif
+       fd = xopen(pathbuf, O_WRONLY);
+       n = sprintf(filedata, "%lu\n", val);
+       if (write(fd, filedata, n) < 0)
+               bb_simple_perror_msg_and_die(name);
+       close(fd);
+}
+
 int brctl_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int brctl_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -271,24 +233,20 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
        IF_FEATURE_BRCTL_FANCY(
                "stp\0"
                "setageing\0" "setfd\0" "sethello\0" "setmaxage\0"
-               "setpathcost\0" "setportprio\0" "setbridgeprio\0"
+               "setpathcost\0" "setportprio\0"
+               "setbridgeprio\0"
        )
        IF_FEATURE_BRCTL_SHOW("show\0");
-
        enum { ARG_addbr = 0, ARG_delbr, ARG_addif, ARG_delif
                IF_FEATURE_BRCTL_FANCY(,
                        ARG_stp,
                        ARG_setageing, ARG_setfd, ARG_sethello, ARG_setmaxage,
-                       ARG_setpathcost, ARG_setportprio, ARG_setbridgeprio
+                       ARG_setpathcost, ARG_setportprio,
+                       ARG_setbridgeprio
                )
                IF_FEATURE_BRCTL_SHOW(, ARG_show)
        };
 
-       int fd;
-       smallint key;
-       struct ifreq ifr;
-       char *br, *brif;
-
        argv++;
        if (!*argv) {
                /* bare "brctl" shows --help */
@@ -297,12 +255,10 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
 
        xchdir("/sys/class/net");
 
-       while (*argv) {
-#if ENABLE_FEATURE_BRCTL_FANCY
-               int ifidx[MAX_PORTS];
-               unsigned long args[4];
-               ifr.ifr_data = (char *) &args;
-#endif
+//     while (*argv)
+       {
+               smallint key;
+               char *br;
 
                key = index_in_strings(keywords, *argv);
                if (key == -1) /* no match found in keywords array, bail out. */
@@ -310,28 +266,28 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                argv++;
 
 #if ENABLE_FEATURE_BRCTL_SHOW
-               if (key == ARG_show) { /* show */
+               if (key == ARG_show) { /* show [BR]... */
                        DIR *net;
                        struct dirent *ent;
                        int need_hdr = 1;
                        int exitcode = EXIT_SUCCESS;
 
                        if (*argv) {
-                               /* "brctl show BR1 BR2 BR3" */
+                               /* "show BR1 BR2 BR3" */
                                do {
                                        if (show_bridge(*argv, need_hdr) >= 0) {
                                                need_hdr = 0;
                                        } else {
                                                bb_error_msg("bridge %s does not exist", *argv);
-//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6 says this instead:
-// "device eth0 is not a bridge"
+//TODO: if device exists, but is not a BR, brctl from bridge-utils 1.6
+//says this instead: "device eth0 is not a bridge"
                                                exitcode = EXIT_FAILURE;
                                        }
                                } while (*++argv != NULL);
                                return exitcode;
                        }
 
-                       /* "brctl show" (if no ifaces, shows nothing, not even header) */
+                       /* "show" (if no ifaces, shows nothing, not even header) */
                        net = xopendir(".");
                        while ((ent = readdir(net)) != NULL) {
                                if (DOT_OR_DOTDOT(ent->d_name))
@@ -339,7 +295,8 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                                if (show_bridge(ent->d_name, need_hdr) >= 0)
                                        need_hdr = 0;
                        }
-                       closedir(net);
+                       if (ENABLE_FEATURE_CLEAN_UP)
+                               closedir(net);
                        return exitcode;
                }
 #endif
@@ -347,33 +304,31 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                if (!*argv) /* all but 'show' need at least one argument */
                        bb_show_usage();
 
-               fd = xsocket(AF_INET, SOCK_STREAM, 0);
                br = *argv++;
 
-//brctl from bridge-utils 1.6 also still uses ioctl
-//for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
-               if (key == ARG_addbr || key == ARG_delbr) { /* addbr or delbr */
+               if (key == ARG_addbr || key == ARG_delbr) {
+                       /* addbr or delbr */
+                       /* brctl from bridge-utils 1.6 still uses ioctl
+                        * for SIOCBRADDBR / SIOCBRDELBR, not /sys accesses
+                        */
+                       int fd = xsocket(AF_INET, SOCK_STREAM, 0);
                        ioctl_or_perror_and_die(fd,
-                                       key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
-                                       br, "bridge %s", br);
-                       goto done;
+                               key == ARG_addbr ? SIOCBRADDBR : SIOCBRDELBR,
+                               br, "bridge %s", br
+                       );
+                       //close(fd);
+                       //goto done;
+                       /* bridge-utils 1.6 simply ignores trailing args:
+                        * "brctl addbr BR1 ARGS" ignores ARGS
+                        */
+                       if (ENABLE_FEATURE_CLEAN_UP)
+                               close(fd);
+                       return EXIT_SUCCESS;
                }
 
                if (!*argv) /* all but 'addbr/delbr' need at least two arguments */
                        bb_show_usage();
 
-               strncpy_IFNAMSIZ(ifr.ifr_name, br);
-               if (key == ARG_addif || key == ARG_delif) { /* addif or delif */
-                       brif = *argv;
-                       ifr.ifr_ifindex = if_nametoindex(brif);
-                       if (!ifr.ifr_ifindex) {
-                               bb_perror_msg_and_die("iface %s", brif);
-                       }
-                       ioctl_or_perror_and_die(fd,
-                                       key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
-                                       &ifr, "bridge %s", br);
-                       goto done_next_argv;
-               }
 #if ENABLE_FEATURE_BRCTL_FANCY
                if (key == ARG_stp) { /* stp */
                        static const char no_yes[] ALIGN1 =
@@ -382,78 +337,86 @@ int brctl_main(int argc UNUSED_PARAM, char **argv)
                        int onoff = index_in_strings(no_yes, *argv);
                        if (onoff < 0)
                                bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, applet_name);
-//TODO: replace with:
-//write "0\n" or "1\n" to /sys/class/net/BR/bridge/stp_state
                        onoff = (unsigned)onoff / 4;
-                       arm_ioctl(args, BRCTL_SET_BRIDGE_STP_STATE, onoff, 0);
-                       goto fire;
+                       write_ulong(br, "bridge/stp_state", onoff);
+                       //goto done_next_argv;
+                       return EXIT_SUCCESS;
                }
+
                if ((unsigned)(key - ARG_setageing) < 4) { /* time related ops */
-                       static const uint8_t ops[] ALIGN1 = {
-                               BRCTL_SET_AGEING_TIME,          /* ARG_setageing */
-                               BRCTL_SET_BRIDGE_FORWARD_DELAY, /* ARG_setfd     */
-                               BRCTL_SET_BRIDGE_HELLO_TIME,    /* ARG_sethello  */
-                               BRCTL_SET_BRIDGE_MAX_AGE        /* ARG_setmaxage */
-                       };
-//TODO: replace with:
-//setageing BR N: write "N*100\n" to /sys/class/net/BR/bridge/ageing_time
-//setfd BR N: write "N*100\n" to /sys/class/net/BR/bridge/forward_delay
-//sethello BR N: write "N*100\n" to /sys/class/net/BR/bridge/hello_time
-//setmaxage BR N: write "N*100\n" to /sys/class/net/BR/bridge/max_age
-                       arm_ioctl(args, ops[key - ARG_setageing], str_to_jiffies(*argv), 0);
-                       goto fire;
+                       /* setageing BR N: "N*100\n" to /sys/class/net/BR/bridge/ageing_time
+                        * setfd BR N:     "N*100\n" to /sys/class/net/BR/bridge/forward_delay
+                        * sethello BR N:  "N*100\n" to /sys/class/net/BR/bridge/hello_time
+                        * setmaxage BR N: "N*100\n" to /sys/class/net/BR/bridge/max_age
+                        */
+                       write_ulong(br,
+                               nth_string(
+                                       "bridge/ageing_time"  "\0" /* ARG_setageing */
+                                       "bridge/forward_delay""\0" /* ARG_setfd     */
+                                       "bridge/hello_time"   "\0" /* ARG_sethello  */
+                                       "bridge/max_age",          /* ARG_setmaxage */
+                                       key - ARG_setageing
+                               ),
+                               str_to_jiffies(*argv)
+                       );
+                       //goto done_next_argv;
+                       return EXIT_SUCCESS;
+               }
+
+               if (key == ARG_setbridgeprio) {
+                       write_ulong(br, "bridge/priority", xatoi_positive(*argv));
+                       //goto done_next_argv;
+                       return EXIT_SUCCESS;
                }
+
                if (key == ARG_setpathcost
                 || key == ARG_setportprio
-                || key == ARG_setbridgeprio
                ) {
-                       static const uint8_t ops[] ALIGN1 = {
-                               BRCTL_SET_PATH_COST,      /* ARG_setpathcost   */
-                               BRCTL_SET_PORT_PRIORITY,  /* ARG_setportprio   */
-                               BRCTL_SET_BRIDGE_PRIORITY /* ARG_setbridgeprio */
-                       };
-                       int port = -1;
-                       unsigned arg1, arg2;
-
-//TODO: replace with:
-//setbridgeprio BR N: write "N\n" to /sys/class/net/BR/bridge/priority
-//setpathcost BR PORT N: ??
-//setportprio BR PORT N: ??
-
-                       if (key != ARG_setbridgeprio) {
-                               /* get portnum */
-                               unsigned i;
-
-                               port = if_nametoindex(*argv++);
-                               if (!port)
-                                       bb_error_msg_and_die(bb_msg_invalid_arg_to, *argv, "port");
-                               memset(ifidx, 0, sizeof ifidx);
-                               arm_ioctl(args, BRCTL_GET_PORT_LIST, (unsigned long)ifidx,
-                                               MAX_PORTS);
-                               xioctl(fd, SIOCDEVPRIVATE, &ifr);
-                               for (i = 0; i < MAX_PORTS; i++) {
-                                       if (ifidx[i] == port) {
-                                               port = i;
-                                               break;
-                                       }
-                               }
-                       }
-                       arg1 = port;
-                       arg2 = xatoi_positive(*argv);
-                       if (key == ARG_setbridgeprio) {
-                               arg1 = arg2;
-                               arg2 = 0;
-                       }
-                       arm_ioctl(args, ops[key - ARG_setpathcost], arg1, arg2);
+                       if (!argv[1])
+                               bb_show_usage();
+                       /* BR is not used (and ignored!) for these commands:
+                        * "setpathcost BR PORT N" writes "N\n" to
+                        * /sys/class/net/PORT/brport/path_cost
+                        * "setportprio BR PORT N" writes "N\n" to
+                        * /sys/class/net/PORT/brport/priority
+                        */
+                       write_ulong(argv[0],
+                               nth_string(
+                                       "brport/path_cost" "\0" /* ARG_setpathcost */
+                                       "brport/priority",      /* ARG_setportprio */
+                                       key - ARG_setpathcost
+                               ),
+                               xatoi_positive(argv[1])
+                       );
+                       //argv++;
+                       //goto done_next_argv;
+                       return EXIT_SUCCESS;
                }
- fire:
-               /* Execute the previously set command */
-               xioctl(fd, SIOCDEVPRIVATE, &ifr);
 #endif
- done_next_argv:
-               argv++;
- done:
-               close(fd);
+               /* always true: if (key == ARG_addif || key == ARG_delif) */ {
+                       /* addif or delif */
+                       struct ifreq ifr;
+                       int fd = xsocket(AF_INET, SOCK_STREAM, 0);
+
+                       strncpy_IFNAMSIZ(ifr.ifr_name, br);
+                       ifr.ifr_ifindex = if_nametoindex(*argv);
+                       if (ifr.ifr_ifindex == 0) {
+                               bb_perror_msg_and_die("iface %s", *argv);
+                       }
+                       ioctl_or_perror_and_die(fd,
+                               key == ARG_addif ? SIOCBRADDIF : SIOCBRDELIF,
+                               &ifr, "bridge %s", br
+                       );
+                       //close(fd);
+                       //goto done_next_argv;
+                       if (ENABLE_FEATURE_CLEAN_UP)
+                               close(fd);
+                       return EXIT_SUCCESS;
+               }
+
+// done_next_argv:
+//             argv++;
+// done:
        }
 
        return EXIT_SUCCESS;