ip link: add support for "address ETHADDR". Closes 4862
[oweals/busybox.git] / networking / libiproute / iprule.c
index 4b3130307f2b3b13869f1d793ec7f5cf590b51fb..8dbe6bd92a5a1306423ee046dd202a235208b94f 100644 (file)
@@ -1,31 +1,26 @@
 /* vi: set sw=4 ts=4: */
 /*
- * iprule.c            "ip rule".
- *
- *             This program is free software; you can redistribute it and/or
- *             modify it under the terms of the GNU General Public License
- *             as published by the Free Software Foundation; either version
- *             2 of the License, or (at your option) any later version.
- *
- * Authors:    Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
  *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Changes:
  *
- * Rani Assaf <rani@magic.metawire.com> 980929:        resolve addresses
- * initially integrated into busybox by Bernhard Fischer
+ * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
+ * initially integrated into busybox by Bernhard Reutner-Fischer
  */
 
-#include "libbb.h"
-#include <syslog.h>
-#include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/ip.h>
 #include <arpa/inet.h>
 
+#include "ip_common.h"  /* #include "libbb.h" is inside */
 #include "rt_names.h"
 #include "utils.h"
-#include "ip_common.h"
+
 /*
 static void usage(void) __attribute__((noreturn));
 
@@ -41,10 +36,10 @@ static void usage(void)
        exit(-1);
 }
 */
-static int print_rule(struct sockaddr_nl *who ATTRIBUTE_UNUSED,
-                                       struct nlmsghdr *n, void *arg)
+
+static int FAST_FUNC print_rule(const struct sockaddr_nl *who UNUSED_PARAM,
+                                       struct nlmsghdr *n, void *arg UNUSED_PARAM)
 {
-       FILE *fp = (FILE*)arg;
        struct rtmsg *r = NLMSG_DATA(n);
        int len = n->nlmsg_len;
        int host_len = -1;
@@ -71,96 +66,94 @@ static int print_rule(struct sockaddr_nl *who ATTRIBUTE_UNUSED,
        else if (r->rtm_family == AF_IPX)
                host_len = 80;
 */
-       if (tb[RTA_PRIORITY])
-               fprintf(fp, "%u:\t", *(unsigned*)RTA_DATA(tb[RTA_PRIORITY]));
-       else
-               fprintf(fp, "0:\t");
-
-       fprintf(fp, "from ");
+       printf("%u:\t", tb[RTA_PRIORITY] ?
+                                       *(unsigned*)RTA_DATA(tb[RTA_PRIORITY])
+                                       : 0);
+       printf("from ");
        if (tb[RTA_SRC]) {
                if (r->rtm_src_len != host_len) {
-                       fprintf(fp, "%s/%u", rt_addr_n2a(r->rtm_family,
-                                                        RTA_PAYLOAD(tb[RTA_SRC]),
-                                                        RTA_DATA(tb[RTA_SRC]),
-                                                        abuf, sizeof(abuf)),
+                       printf("%s/%u", rt_addr_n2a(r->rtm_family,
+                                                       RTA_DATA(tb[RTA_SRC]),
+                                                       abuf, sizeof(abuf)),
                                r->rtm_src_len
-                               );
+                       );
                } else {
-                       fprintf(fp, "%s", format_host(r->rtm_family,
-                                                      RTA_PAYLOAD(tb[RTA_SRC]),
-                                                      RTA_DATA(tb[RTA_SRC]),
-                                                      abuf, sizeof(abuf))
-                               );
+                       fputs(format_host(r->rtm_family,
+                                               RTA_PAYLOAD(tb[RTA_SRC]),
+                                               RTA_DATA(tb[RTA_SRC]),
+                                               abuf, sizeof(abuf)),
+                               stdout
+                       );
                }
        } else if (r->rtm_src_len) {
-               fprintf(fp, "0/%d", r->rtm_src_len);
+               printf("0/%d", r->rtm_src_len);
        } else {
-               fprintf(fp, "all");
+               printf("all");
        }
-       fprintf(fp, " ");
+       bb_putchar(' ');
 
        if (tb[RTA_DST]) {
                if (r->rtm_dst_len != host_len) {
-                       fprintf(fp, "to %s/%u ", rt_addr_n2a(r->rtm_family,
-                                                        RTA_PAYLOAD(tb[RTA_DST]),
+                       printf("to %s/%u ", rt_addr_n2a(r->rtm_family,
                                                         RTA_DATA(tb[RTA_DST]),
                                                         abuf, sizeof(abuf)),
                                r->rtm_dst_len
                                );
                } else {
-                       fprintf(fp, "to %s ", format_host(r->rtm_family,
+                       printf("to %s ", format_host(r->rtm_family,
                                                       RTA_PAYLOAD(tb[RTA_DST]),
                                                       RTA_DATA(tb[RTA_DST]),
                                                       abuf, sizeof(abuf)));
                }
        } else if (r->rtm_dst_len) {
-               fprintf(fp, "to 0/%d ", r->rtm_dst_len);
+               printf("to 0/%d ", r->rtm_dst_len);
        }
 
        if (r->rtm_tos) {
-               fprintf(fp, "tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1, sizeof(b1)));
+               printf("tos %s ", rtnl_dsfield_n2a(r->rtm_tos, b1));
        }
        if (tb[RTA_PROTOINFO]) {
-               fprintf(fp, "fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
+               printf("fwmark %#x ", *(uint32_t*)RTA_DATA(tb[RTA_PROTOINFO]));
        }
 
        if (tb[RTA_IIF]) {
-               fprintf(fp, "iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
+               printf("iif %s ", (char*)RTA_DATA(tb[RTA_IIF]));
        }
 
        if (r->rtm_table)
-               fprintf(fp, "lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1, sizeof(b1)));
+               printf("lookup %s ", rtnl_rttable_n2a(r->rtm_table, b1));
 
        if (tb[RTA_FLOW]) {
                uint32_t to = *(uint32_t*)RTA_DATA(tb[RTA_FLOW]);
                uint32_t from = to>>16;
                to &= 0xFFFF;
                if (from) {
-                       fprintf(fp, "realms %s/",
-                               rtnl_rtrealm_n2a(from, b1, sizeof(b1)));
+                       printf("realms %s/",
+                               rtnl_rtrealm_n2a(from, b1));
                }
-               fprintf(fp, "%s ",
-                       rtnl_rtrealm_n2a(to, b1, sizeof(b1)));
+               printf("%s ",
+                       rtnl_rtrealm_n2a(to, b1));
        }
 
        if (r->rtm_type == RTN_NAT) {
                if (tb[RTA_GATEWAY]) {
-                       fprintf(fp, "map-to %s ",
+                       printf("map-to %s ",
                                format_host(r->rtm_family,
                                            RTA_PAYLOAD(tb[RTA_GATEWAY]),
                                            RTA_DATA(tb[RTA_GATEWAY]),
                                            abuf, sizeof(abuf)));
                } else
-                       fprintf(fp, "masquerade");
+                       printf("masquerade");
        } else if (r->rtm_type != RTN_UNICAST)
-               fprintf(fp, "%s", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1)));
+               fputs(rtnl_rtntype_n2a(r->rtm_type, b1), stdout);
 
-       fprintf(fp, "\n");
-       fflush(fp);
+       bb_putchar('\n');
+       /*fflush_all();*/
        return 0;
 }
 
-static int iprule_list(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iprule_list(char **argv)
 {
        struct rtnl_handle rth;
        int af = preferred_family;
@@ -168,38 +161,40 @@ static int iprule_list(int argc, char **argv)
        if (af == AF_UNSPEC)
                af = AF_INET;
 
-       if (argc > 0) {
+       if (*argv) {
                //bb_error_msg("\"rule show\" needs no arguments");
-               bb_warn_ignoring_args(argc);
+               bb_warn_ignoring_args(*argv);
                return -1;
        }
 
-       if (rtnl_open(&rth, 0) < 0)
-               return 1;
-
-       if (rtnl_wilddump_request(&rth, af, RTM_GETRULE) < 0) {
-               bb_perror_msg("Cannot send dump request");
-               return 1;
-       }
+       xrtnl_open(&rth);
 
-       if (rtnl_dump_filter(&rth, print_rule, stdout, NULL, NULL) < 0) {
-               bb_error_msg("Dump terminated");
-               return 1;
-       }
+       xrtnl_wilddump_request(&rth, af, RTM_GETRULE);
+       xrtnl_dump_filter(&rth, print_rule, NULL);
 
        return 0;
 }
 
-
-static int iprule_modify(int cmd, int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+static int iprule_modify(int cmd, char **argv)
 {
-       int table_ok = 0;
+       static const char keywords[] ALIGN1 =
+               "from\0""to\0""preference\0""order\0""priority\0"
+               "tos\0""fwmark\0""realms\0""table\0""lookup\0""dev\0"
+               "iif\0""nat\0""map-to\0""type\0""help\0";
+       enum {
+               ARG_from = 1, ARG_to, ARG_preference, ARG_order, ARG_priority,
+               ARG_tos, ARG_fwmark, ARG_realms, ARG_table, ARG_lookup, ARG_dev,
+               ARG_iif, ARG_nat, ARG_map_to, ARG_type, ARG_help
+       };
+       bool table_ok = 0;
        struct rtnl_handle rth;
        struct {
-               struct nlmsghdr n;
-               struct rtmsg    r;
-               char            buf[1024];
+               struct nlmsghdr n;
+               struct rtmsg    r;
+               char            buf[1024];
        } req;
+       smalluint key;
 
        memset(&req, 0, sizeof(req));
 
@@ -217,75 +212,79 @@ static int iprule_modify(int cmd, int argc, char **argv)
                req.r.rtm_type = RTN_UNICAST;
        }
 
-       while (argc > 0) {
-               if (strcmp(*argv, "from") == 0) {
+       while (*argv) {
+               key = index_in_substrings(keywords, *argv) + 1;
+               if (key == 0) /* no match found in keywords array, bail out. */
+                       invarg(*argv, applet_name);
+               if (key == ARG_from) {
                        inet_prefix dst;
                        NEXT_ARG();
                        get_prefix(&dst, *argv, req.r.rtm_family);
                        req.r.rtm_src_len = dst.bitlen;
                        addattr_l(&req.n, sizeof(req), RTA_SRC, &dst.data, dst.bytelen);
-               } else if (strcmp(*argv, "to") == 0) {
+               } else if (key == ARG_to) {
                        inet_prefix dst;
                        NEXT_ARG();
                        get_prefix(&dst, *argv, req.r.rtm_family);
                        req.r.rtm_dst_len = dst.bitlen;
                        addattr_l(&req.n, sizeof(req), RTA_DST, &dst.data, dst.bytelen);
-               } else if (matches(*argv, "preference") == 0 ||
-                          matches(*argv, "order") == 0 ||
-                          matches(*argv, "priority") == 0) {
+               } else if (key == ARG_preference ||
+                          key == ARG_order ||
+                          key == ARG_priority
+               ) {
                        uint32_t pref;
                        NEXT_ARG();
-                       if (get_u32(&pref, *argv, 0))
-                               invarg("preference value", *argv);
+                       pref = get_u32(*argv, "preference");
                        addattr32(&req.n, sizeof(req), RTA_PRIORITY, pref);
-               } else if (strcmp(*argv, "tos") == 0) {
+               } else if (key == ARG_tos) {
                        uint32_t tos;
                        NEXT_ARG();
                        if (rtnl_dsfield_a2n(&tos, *argv))
-                               invarg("TOS value", *argv);
+                               invarg(*argv, "TOS");
                        req.r.rtm_tos = tos;
-               } else if (strcmp(*argv, "fwmark") == 0) {
+               } else if (key == ARG_fwmark) {
                        uint32_t fwmark;
                        NEXT_ARG();
-                       if (get_u32(&fwmark, *argv, 0))
-                               invarg("fwmark value", *argv);
+                       fwmark = get_u32(*argv, "fwmark");
                        addattr32(&req.n, sizeof(req), RTA_PROTOINFO, fwmark);
-               } else if (matches(*argv, "realms") == 0) {
+               } else if (key == ARG_realms) {
                        uint32_t realm;
                        NEXT_ARG();
                        if (get_rt_realms(&realm, *argv))
-                               invarg("realms", *argv);
+                               invarg(*argv, "realms");
                        addattr32(&req.n, sizeof(req), RTA_FLOW, realm);
-               } else if (matches(*argv, "table") == 0 ||
-                          strcmp(*argv, "lookup") == 0) {
+               } else if (key == ARG_table ||
+                          key == ARG_lookup
+               ) {
                        uint32_t tid;
                        NEXT_ARG();
                        if (rtnl_rttable_a2n(&tid, *argv))
-                               invarg("table ID", *argv);
+                               invarg(*argv, "table ID");
                        req.r.rtm_table = tid;
                        table_ok = 1;
-               } else if (strcmp(*argv, "dev") == 0 ||
-                          strcmp(*argv, "iif") == 0) {
+               } else if (key == ARG_dev ||
+                          key == ARG_iif
+               ) {
                        NEXT_ARG();
                        addattr_l(&req.n, sizeof(req), RTA_IIF, *argv, strlen(*argv)+1);
-               } else if (strcmp(*argv, "nat") == 0 ||
-                          matches(*argv, "map-to") == 0) {
+               } else if (key == ARG_nat ||
+                          key == ARG_map_to
+               ) {
                        NEXT_ARG();
                        addattr32(&req.n, sizeof(req), RTA_GATEWAY, get_addr32(*argv));
                        req.r.rtm_type = RTN_NAT;
                } else {
                        int type;
 
-                       if (strcmp(*argv, "type") == 0) {
+                       if (key == ARG_type) {
                                NEXT_ARG();
                        }
-                       if (matches(*argv, "help") == 0)
+                       if (key == ARG_help)
                                bb_show_usage();
                        if (rtnl_rtntype_a2n(&type, *argv))
-                               invarg("Failed to parse rule type", *argv);
+                               invarg(*argv, "type");
                        req.r.rtm_type = type;
                }
-               argc--;
                argv++;
        }
 
@@ -295,8 +294,7 @@ static int iprule_modify(int cmd, int argc, char **argv)
        if (!table_ok && cmd == RTM_NEWRULE)
                req.r.rtm_table = RT_TABLE_MAIN;
 
-       if (rtnl_open(&rth, 0) < 0)
-               return 1;
+       xrtnl_open(&rth);
 
        if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
                return 2;
@@ -304,31 +302,18 @@ static int iprule_modify(int cmd, int argc, char **argv)
        return 0;
 }
 
-int do_iprule(int argc, char **argv)
+/* Return value becomes exitcode. It's okay to not return at all */
+int FAST_FUNC do_iprule(char **argv)
 {
-       static const char * const ip_rule_commands[] =
-               {"add", "delete", "list", "show", 0};
-       int cmd = 2; /* list */
-
-       if (argc < 1)
-               return iprule_list(0, NULL);
-       if (*argv)
-               cmd = index_in_substr_array(ip_rule_commands, *argv);
-
-       switch (cmd) {
-               case 0: /* add */
-                       cmd = RTM_NEWRULE;
-                       break;
-               case 1: /* delete */
-                       cmd = RTM_DELRULE;
-                       break;
-               case 2: /* list */
-               case 3: /* show */
-                       return iprule_list(argc-1, argv+1);
-                       break;
-               default:
-                       bb_error_msg_and_die("unknown command %s", *argv);
+       static const char ip_rule_commands[] ALIGN1 =
+               "add\0""delete\0""list\0""show\0";
+       if (*argv) {
+               int cmd = index_in_substrings(ip_rule_commands, *argv);
+               if (cmd < 0)
+                       invarg(*argv, applet_name);
+               argv++;
+               if (cmd < 2)
+                       return iprule_modify((cmd == 0) ? RTM_NEWRULE : RTM_DELRULE, argv);
        }
-       return iprule_modify(cmd, argc-1, argv+1);
+       return iprule_list(argv);
 }
-