iplink: support add/delete
authorBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Tue, 25 May 2010 08:34:27 +0000 (10:34 +0200)
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Tue, 25 May 2010 11:39:39 +0000 (13:39 +0200)
function                                             old     new   delta
do_change                                              -     490    +490
.rodata                                           135193  135225     +32
do_iplink                                           1146    1169     +23
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/0 up/down: 545/0)             Total: 545 bytes

Signed-off-by: Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
networking/libiproute/iplink.c

index df8a354a1274ecbba56baffccecfb472f25159a4..a28df6e7970e9d776ffe890d2400b2f73f973616 100644 (file)
@@ -283,11 +283,98 @@ static int ipaddr_list_link(char **argv)
        return ipaddr_list_or_flush(argv, 0);
 }
 
+#ifndef NLMSG_TAIL
+#define NLMSG_TAIL(nmsg) \
+       ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+#endif
+/* Return value becomes exitcode. It's okay to not return at all */
+static int do_change(char **argv, const unsigned rtm)
+{
+       static const char keywords[] ALIGN1 =
+               "link\0""name\0""type\0""dev\0";
+       enum {
+               ARG_link,
+               ARG_name,
+               ARG_type,
+               ARG_dev,
+       };
+       struct rtnl_handle rth;
+       struct {
+               struct nlmsghdr         n;
+               struct ifinfomsg        i;
+               char                    buf[1024];
+       } req;
+       int arg;
+       char *name_str = NULL, *link_str = NULL, *type_str = NULL, *dev_str = NULL;
+
+       memset(&req, 0, sizeof(req));
+
+       req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+       req.n.nlmsg_flags = NLM_F_REQUEST;
+       req.n.nlmsg_type = rtm;
+       req.i.ifi_family = preferred_family;
+       if (rtm == RTM_NEWLINK)
+               req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
+
+       while (*argv) {
+               arg = index_in_substrings(keywords, *argv);
+               if (arg == ARG_link) {
+                       NEXT_ARG();
+                       link_str = *argv;
+               } else if (arg == ARG_name) {
+                       NEXT_ARG();
+                       name_str = *argv;
+               } else if (arg == ARG_type) {
+                       NEXT_ARG();
+                       type_str = *argv;
+               } else {
+                       if (arg == ARG_dev) {
+                               if (dev_str)
+                                       duparg(*argv, "dev");
+                               NEXT_ARG();
+                       }
+                       dev_str = *argv;
+               }
+               argv++;
+       }
+       xrtnl_open(&rth);
+       ll_init_map(&rth);
+       if (type_str) {
+               struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
+
+               addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
+               addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str,
+                               strlen(type_str));
+               linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
+       }
+       if (rtm != RTM_NEWLINK) {
+               if (!dev_str)
+                       return 1; /* Need a device to delete */
+               req.i.ifi_index = xll_name_to_index(dev_str);
+       } else {
+               if (!name_str)
+                       name_str = dev_str;
+               if (link_str) {
+                       int idx = xll_name_to_index(link_str);
+                       addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4);
+               }
+       }
+       if (name_str) {
+               const size_t name_len = strlen(name_str) + 1;
+               if (name_len < 2 || name_len > IFNAMSIZ)
+                       invarg(name_str, "name");
+               addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len);
+       }
+       if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
+               return 2;
+       return 0;
+}
+
 /* Return value becomes exitcode. It's okay to not return at all */
 int do_iplink(char **argv)
 {
        static const char keywords[] ALIGN1 =
-               "set\0""show\0""lst\0""list\0";
+               "add\0""delete\0""set\0""show\0""lst\0""list\0";
        int key;
        if (!*argv)
                return ipaddr_list_link(argv);
@@ -295,7 +382,9 @@ int do_iplink(char **argv)
        if (key < 0)
                bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
        argv++;
-       if (key == 0) /* set */
+       if (key <= 1) /* add/delete */
+               return do_change(argv, key ? RTM_DELLINK : RTM_NEWLINK);
+       else if (key == 2) /* set */
                return do_set(argv);
        /* show, lst, list */
        return ipaddr_list_link(argv);