busybox/networking/libiproute/iplink.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
   4 *                      Patrick McHardy <kaber@trash.net>
   5 *
   6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   7 */
   8#include <net/if.h>
   9/*#include <net/if_packet.h> - not needed? */
  10#include <netpacket/packet.h>
  11#include <netinet/if_ether.h>
  12
  13#include <linux/if_vlan.h>
  14#include "ip_common.h"  /* #include "libbb.h" is inside */
  15#include "rt_names.h"
  16#include "utils.h"
  17
  18#undef  ETH_P_8021AD
  19#define ETH_P_8021AD            0x88A8
  20#undef  VLAN_FLAG_REORDER_HDR
  21#define VLAN_FLAG_REORDER_HDR   0x1
  22#undef  VLAN_FLAG_GVRP
  23#define VLAN_FLAG_GVRP          0x2
  24#undef  VLAN_FLAG_LOOSE_BINDING
  25#define VLAN_FLAG_LOOSE_BINDING 0x4
  26#undef  VLAN_FLAG_MVRP
  27#define VLAN_FLAG_MVRP          0x8
  28#undef  IFLA_VLAN_PROTOCOL
  29#define IFLA_VLAN_PROTOCOL      5
  30
  31#ifndef IFLA_LINKINFO
  32# define IFLA_LINKINFO 18
  33# define IFLA_INFO_KIND 1
  34# define IFLA_INFO_DATA 2
  35#endif
  36
  37#ifndef IFLA_VLAN_MAX
  38# define IFLA_VLAN_ID 1
  39# define IFLA_VLAN_FLAGS 2
  40struct ifla_vlan_flags {
  41        uint32_t        flags;
  42        uint32_t        mask;
  43};
  44#endif
  45
  46/* taken from linux/sockios.h */
  47#define SIOCSIFNAME  0x8923  /* set interface name */
  48
  49#if 0
  50# define dbg(...) bb_error_msg(__VA_ARGS__)
  51#else
  52# define dbg(...) ((void)0)
  53#endif
  54
  55
  56#define str_on_off "on\0""off\0"
  57
  58/* Exits on error */
  59static int get_ctl_fd(void)
  60{
  61        int fd;
  62
  63        fd = socket(PF_INET, SOCK_DGRAM, 0);
  64        if (fd >= 0)
  65                return fd;
  66        fd = socket(PF_PACKET, SOCK_DGRAM, 0);
  67        if (fd >= 0)
  68                return fd;
  69        return xsocket(PF_INET6, SOCK_DGRAM, 0);
  70}
  71
  72/* Exits on error */
  73static void do_chflags(char *dev, uint32_t flags, uint32_t mask)
  74{
  75        struct ifreq ifr;
  76        int fd;
  77
  78        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
  79        fd = get_ctl_fd();
  80        xioctl(fd, SIOCGIFFLAGS, &ifr);
  81        if ((ifr.ifr_flags ^ flags) & mask) {
  82                ifr.ifr_flags &= ~mask;
  83                ifr.ifr_flags |= mask & flags;
  84                xioctl(fd, SIOCSIFFLAGS, &ifr);
  85        }
  86        close(fd);
  87}
  88
  89/* Exits on error */
  90static void do_changename(char *dev, char *newdev)
  91{
  92        struct ifreq ifr;
  93        int fd;
  94
  95        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
  96        strncpy_IFNAMSIZ(ifr.ifr_newname, newdev);
  97        fd = get_ctl_fd();
  98        xioctl(fd, SIOCSIFNAME, &ifr);
  99        close(fd);
 100}
 101
 102/* Exits on error */
 103static void set_qlen(char *dev, int qlen)
 104{
 105        struct ifreq ifr;
 106        int s;
 107
 108        s = get_ctl_fd();
 109        memset(&ifr, 0, sizeof(ifr));
 110        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
 111        ifr.ifr_qlen = qlen;
 112        xioctl(s, SIOCSIFTXQLEN, &ifr);
 113        close(s);
 114}
 115
 116/* Exits on error */
 117static void set_mtu(char *dev, int mtu)
 118{
 119        struct ifreq ifr;
 120        int s;
 121
 122        s = get_ctl_fd();
 123        memset(&ifr, 0, sizeof(ifr));
 124        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
 125        ifr.ifr_mtu = mtu;
 126        xioctl(s, SIOCSIFMTU, &ifr);
 127        close(s);
 128}
 129
 130/* Exits on error */
 131static void set_master(char *dev, int master)
 132{
 133        struct rtnl_handle rth;
 134        struct {
 135                struct nlmsghdr  n;
 136                struct ifinfomsg i;
 137                char             buf[1024];
 138        } req;
 139
 140        memset(&req, 0, sizeof(req));
 141
 142        req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
 143        req.n.nlmsg_flags = NLM_F_REQUEST;
 144        req.n.nlmsg_type = RTM_NEWLINK;
 145        req.i.ifi_family = preferred_family;
 146
 147        xrtnl_open(&rth);
 148        req.i.ifi_index = xll_name_to_index(dev);
 149        //printf("master %i for %i\n", master, req.i.ifi_index);
 150        addattr_l(&req.n, sizeof(req), IFLA_MASTER, &master, 4);
 151        if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
 152                xfunc_die();
 153}
 154
 155/* Exits on error */
 156static int get_address(char *dev, int *htype)
 157{
 158        struct ifreq ifr;
 159        struct sockaddr_ll me;
 160        int s;
 161
 162        s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
 163
 164        /*memset(&ifr, 0, sizeof(ifr)); - SIOCGIFINDEX does not need to clear all */
 165        strncpy_IFNAMSIZ(ifr.ifr_name, dev);
 166        xioctl(s, SIOCGIFINDEX, &ifr);
 167
 168        memset(&me, 0, sizeof(me));
 169        me.sll_family = AF_PACKET;
 170        me.sll_ifindex = ifr.ifr_ifindex;
 171        me.sll_protocol = htons(ETH_P_LOOP);
 172        xbind(s, (struct sockaddr*)&me, sizeof(me));
 173        bb_getsockname(s, (struct sockaddr*)&me, sizeof(me));
 174        //never happens:
 175        //if (getsockname(s, (struct sockaddr*)&me, &alen) == -1)
 176        //      bb_perror_msg_and_die("getsockname");
 177        close(s);
 178        *htype = me.sll_hatype;
 179        return me.sll_halen;
 180}
 181
 182/* Exits on error */
 183static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
 184{
 185        int alen;
 186
 187        memset(ifr, 0, sizeof(*ifr));
 188        strncpy_IFNAMSIZ(ifr->ifr_name, dev);
 189        ifr->ifr_hwaddr.sa_family = hatype;
 190
 191        alen = hatype == 1/*ARPHRD_ETHER*/ ? 14/*ETH_HLEN*/ : 19/*INFINIBAND_HLEN*/;
 192        alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), alen, lla);
 193        if (alen < 0)
 194                exit(EXIT_FAILURE);
 195        if (alen != halen) {
 196                bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen);
 197        }
 198}
 199
 200/* Exits on error */
 201static void set_address(struct ifreq *ifr, int brd)
 202{
 203        int s;
 204
 205        s = get_ctl_fd();
 206        if (brd)
 207                xioctl(s, SIOCSIFHWBROADCAST, ifr);
 208        else
 209                xioctl(s, SIOCSIFHWADDR, ifr);
 210        close(s);
 211}
 212
 213
 214static void die_must_be_on_off(const char *msg) NORETURN;
 215static void die_must_be_on_off(const char *msg)
 216{
 217        bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg);
 218}
 219
 220/* Return value becomes exitcode. It's okay to not return at all */
 221static int do_set(char **argv)
 222{
 223        char *dev = NULL;
 224        uint32_t mask = 0;
 225        uint32_t flags = 0;
 226        int qlen = -1;
 227        int mtu = -1;
 228        int master = -1;
 229        char *newaddr = NULL;
 230        char *newbrd = NULL;
 231        struct ifreq ifr0, ifr1;
 232        char *newname = NULL;
 233        int htype, halen;
 234        /* If you add stuff here, update iplink_full_usage */
 235        static const char keywords[] ALIGN1 =
 236                "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0"
 237                "arp\0""promisc\0""address\0"
 238                "master\0""nomaster\0"
 239                "dev\0" /* must be last */;
 240        enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast,
 241                ARG_arp, ARG_promisc, ARG_addr,
 242                ARG_master, ARG_nomaster,
 243                ARG_dev };
 244        enum { PARM_on = 0, PARM_off };
 245        smalluint key;
 246
 247        while (*argv) {
 248                /* substring search ensures that e.g. "addr" and "address"
 249                 * are both accepted */
 250                key = index_in_substrings(keywords, *argv);
 251                if (key == ARG_up) {
 252                        mask |= IFF_UP;
 253                        flags |= IFF_UP;
 254                } else if (key == ARG_down) {
 255                        mask |= IFF_UP;
 256                        flags &= ~IFF_UP;
 257                } else if (key == ARG_name) {
 258                        NEXT_ARG();
 259                        newname = *argv;
 260                } else if (key == ARG_mtu) {
 261                        NEXT_ARG();
 262                        if (mtu != -1)
 263                                duparg("mtu", *argv);
 264                        mtu = get_unsigned(*argv, "mtu");
 265                } else if (key == ARG_qlen) {
 266//TODO: txqueuelen, txqlen are synonyms to qlen
 267                        NEXT_ARG();
 268                        if (qlen != -1)
 269                                duparg("qlen", *argv);
 270                        qlen = get_unsigned(*argv, "qlen");
 271                } else if (key == ARG_addr) {
 272                        NEXT_ARG();
 273                        newaddr = *argv;
 274                } else if (key == ARG_master) {
 275                        NEXT_ARG();
 276                        master = xll_name_to_index(*argv);
 277                } else if (key == ARG_nomaster) {
 278                        master = 0;
 279                } else if (key >= ARG_dev) {
 280                        /* ^^^^^^ ">=" here results in "dev IFACE" treated as default */
 281                        if (key == ARG_dev) {
 282                                NEXT_ARG();
 283                        }
 284                        if (dev)
 285                                duparg2("dev", *argv);
 286                        dev = *argv;
 287                } else {
 288                        /* "on|off" options */
 289                        int param;
 290                        NEXT_ARG();
 291                        param = index_in_strings(str_on_off, *argv);
 292                        if (key == ARG_multicast) {
 293                                if (param < 0)
 294                                        die_must_be_on_off("multicast");
 295                                mask |= IFF_MULTICAST;
 296                                if (param == PARM_on)
 297                                        flags |= IFF_MULTICAST;
 298                                else
 299                                        flags &= ~IFF_MULTICAST;
 300                        } else if (key == ARG_arp) {
 301                                if (param < 0)
 302                                        die_must_be_on_off("arp");
 303                                mask |= IFF_NOARP;
 304                                if (param == PARM_on)
 305                                        flags &= ~IFF_NOARP;
 306                                else
 307                                        flags |= IFF_NOARP;
 308                        } else if (key == ARG_promisc) {
 309                                if (param < 0)
 310                                        die_must_be_on_off("promisc");
 311                                mask |= IFF_PROMISC;
 312                                if (param == PARM_on)
 313                                        flags |= IFF_PROMISC;
 314                                else
 315                                        flags &= ~IFF_PROMISC;
 316                        }
 317                }
 318
 319/* Other keywords recognized by iproute2-3.12.0: */
 320#if 0
 321                } else if (matches(*argv, "broadcast") == 0 ||
 322                                strcmp(*argv, "brd") == 0) {
 323                        NEXT_ARG();
 324                        len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
 325                        if (len < 0)
 326                                return -1;
 327                        addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len);
 328                } else if (strcmp(*argv, "netns") == 0) {
 329                        NEXT_ARG();
 330                        if (netns != -1)
 331                                duparg("netns", *argv);
 332                        if ((netns = get_netns_fd(*argv)) >= 0)
 333                                addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4);
 334                        else if (get_integer(&netns, *argv, 0) == 0)
 335                                addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
 336                        else
 337                                invarg_1_to_2(*argv, "netns");
 338                } else if (strcmp(*argv, "allmulticast") == 0) {
 339                        NEXT_ARG();
 340                        req->i.ifi_change |= IFF_ALLMULTI;
 341                        if (strcmp(*argv, "on") == 0) {
 342                                req->i.ifi_flags |= IFF_ALLMULTI;
 343                        } else if (strcmp(*argv, "off") == 0) {
 344                                req->i.ifi_flags &= ~IFF_ALLMULTI;
 345                        } else
 346                                return on_off("allmulticast", *argv);
 347                } else if (strcmp(*argv, "trailers") == 0) {
 348                        NEXT_ARG();
 349                        req->i.ifi_change |= IFF_NOTRAILERS;
 350                        if (strcmp(*argv, "off") == 0) {
 351                                req->i.ifi_flags |= IFF_NOTRAILERS;
 352                        } else if (strcmp(*argv, "on") == 0) {
 353                                req->i.ifi_flags &= ~IFF_NOTRAILERS;
 354                        } else
 355                                return on_off("trailers", *argv);
 356                } else if (strcmp(*argv, "vf") == 0) {
 357                        struct rtattr *vflist;
 358                        NEXT_ARG();
 359                        if (get_integer(&vf,  *argv, 0)) {
 360                                invarg_1_to_2(*argv, "vf");
 361                        }
 362                        vflist = addattr_nest(&req->n, sizeof(*req),
 363                                              IFLA_VFINFO_LIST);
 364                        len = iplink_parse_vf(vf, &argc, &argv, req);
 365                        if (len < 0)
 366                                return -1;
 367                        addattr_nest_end(&req->n, vflist);
 368                } else if (matches(*argv, "master") == 0) {
 369                        int ifindex;
 370                        NEXT_ARG();
 371                        ifindex = ll_name_to_index(*argv);
 372                        if (!ifindex)
 373                                invarg_1_to_2(*argv, "master");
 374                        addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
 375                                  &ifindex, 4);
 376                } else if (matches(*argv, "nomaster") == 0) {
 377                        int ifindex = 0;
 378                        addattr_l(&req->n, sizeof(*req), IFLA_MASTER,
 379                                  &ifindex, 4);
 380                } else if (matches(*argv, "dynamic") == 0) {
 381                        NEXT_ARG();
 382                        req->i.ifi_change |= IFF_DYNAMIC;
 383                        if (strcmp(*argv, "on") == 0) {
 384                                req->i.ifi_flags |= IFF_DYNAMIC;
 385                        } else if (strcmp(*argv, "off") == 0) {
 386                                req->i.ifi_flags &= ~IFF_DYNAMIC;
 387                        } else
 388                                return on_off("dynamic", *argv);
 389                } else if (matches(*argv, "alias") == 0) {
 390                        NEXT_ARG();
 391                        addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS,
 392                                  *argv, strlen(*argv));
 393                        argc--; argv++;
 394                        break;
 395                } else if (strcmp(*argv, "group") == 0) {
 396                        NEXT_ARG();
 397                        if (*group != -1)
 398                                duparg("group", *argv);
 399                        if (rtnl_group_a2n(group, *argv))
 400                                invarg_1_to_2(*argv, "group");
 401                } else if (strcmp(*argv, "mode") == 0) {
 402                        int mode;
 403                        NEXT_ARG();
 404                        mode = get_link_mode(*argv);
 405                        if (mode < 0)
 406                                invarg_1_to_2(*argv, "mode");
 407                        addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode);
 408                } else if (strcmp(*argv, "state") == 0) {
 409                        int state;
 410                        NEXT_ARG();
 411                        state = get_operstate(*argv);
 412                        if (state < 0)
 413                                invarg_1_to_2(*argv, "state");
 414                        addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state);
 415                } else if (matches(*argv, "numtxqueues") == 0) {
 416                        NEXT_ARG();
 417                        if (numtxqueues != -1)
 418                                duparg("numtxqueues", *argv);
 419                        if (get_integer(&numtxqueues, *argv, 0))
 420                                invarg_1_to_2(*argv, "numtxqueues");
 421                        addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES,
 422                                  &numtxqueues, 4);
 423                } else if (matches(*argv, "numrxqueues") == 0) {
 424                        NEXT_ARG();
 425                        if (numrxqueues != -1)
 426                                duparg("numrxqueues", *argv);
 427                        if (get_integer(&numrxqueues, *argv, 0))
 428                                invarg_1_to_2(*argv, "numrxqueues");
 429                        addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES,
 430                                  &numrxqueues, 4);
 431                }
 432#endif
 433
 434                argv++;
 435        }
 436
 437        if (!dev) {
 438                bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
 439        }
 440
 441        if (newaddr || newbrd) {
 442                halen = get_address(dev, &htype);
 443                if (newaddr) {
 444                        parse_address(dev, htype, halen, newaddr, &ifr0);
 445                        set_address(&ifr0, 0);
 446                }
 447                if (newbrd) {
 448                        parse_address(dev, htype, halen, newbrd, &ifr1);
 449                        set_address(&ifr1, 1);
 450                }
 451        }
 452
 453        if (newname && strcmp(dev, newname)) {
 454                do_changename(dev, newname);
 455                dev = newname;
 456        }
 457        if (qlen != -1) {
 458                set_qlen(dev, qlen);
 459        }
 460        if (mtu != -1) {
 461                set_mtu(dev, mtu);
 462        }
 463        if (master != -1) {
 464                set_master(dev, master);
 465        }
 466        if (mask)
 467                do_chflags(dev, flags, mask);
 468        return 0;
 469}
 470
 471static int ipaddr_list_link(char **argv)
 472{
 473        preferred_family = AF_PACKET;
 474        return ipaddr_list_or_flush(argv, 0);
 475}
 476
 477static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size)
 478{
 479        static const char keywords[] ALIGN1 =
 480                "id\0"
 481                "protocol\0"
 482                "reorder_hdr\0"
 483                "gvrp\0"
 484                "mvrp\0"
 485                "loose_binding\0"
 486        ;
 487        static const char protocols[] ALIGN1 =
 488                "802.1q\0"
 489                "802.1ad\0"
 490        ;
 491        enum {
 492                ARG_id = 0,
 493                ARG_protocol,
 494                ARG_reorder_hdr,
 495                ARG_gvrp,
 496                ARG_mvrp,
 497                ARG_loose_binding,
 498        };
 499        enum {
 500                PROTO_8021Q = 0,
 501                PROTO_8021AD,
 502        };
 503        enum {
 504                PARM_on = 0,
 505                PARM_off
 506        };
 507        int arg;
 508        uint16_t id, proto;
 509        struct ifla_vlan_flags flags = {};
 510
 511        while (*argv) {
 512                arg = index_in_substrings(keywords, *argv);
 513                if (arg < 0)
 514                        invarg_1_to_2(*argv, "type vlan");
 515
 516                NEXT_ARG();
 517                if (arg == ARG_id) {
 518                        id = get_u16(*argv, "id");
 519                        addattr_l(n, size, IFLA_VLAN_ID, &id, sizeof(id));
 520                } else if (arg == ARG_protocol) {
 521                        arg = index_in_substrings(protocols, str_tolower(*argv));
 522                        if (arg == PROTO_8021Q)
 523                                proto = htons(ETH_P_8021Q);
 524                        else if (arg == PROTO_8021AD)
 525                                proto = htons(ETH_P_8021AD);
 526                        else
 527                                bb_error_msg_and_die("unknown VLAN encapsulation protocol '%s'",
 528                                                                     *argv);
 529                        addattr_l(n, size, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto));
 530                } else {
 531                        int param = index_in_strings(str_on_off, *argv);
 532                        if (param < 0)
 533                                die_must_be_on_off(nth_string(keywords, arg));
 534
 535                        if (arg == ARG_reorder_hdr) {
 536                                flags.mask |= VLAN_FLAG_REORDER_HDR;
 537                                flags.flags &= ~VLAN_FLAG_REORDER_HDR;
 538                                if (param == PARM_on)
 539                                        flags.flags |= VLAN_FLAG_REORDER_HDR;
 540                        } else if (arg == ARG_gvrp) {
 541                                flags.mask |= VLAN_FLAG_GVRP;
 542                                flags.flags &= ~VLAN_FLAG_GVRP;
 543                                if (param == PARM_on)
 544                                        flags.flags |= VLAN_FLAG_GVRP;
 545                        } else if (arg == ARG_mvrp) {
 546                                flags.mask |= VLAN_FLAG_MVRP;
 547                                flags.flags &= ~VLAN_FLAG_MVRP;
 548                                if (param == PARM_on)
 549                                        flags.flags |= VLAN_FLAG_MVRP;
 550                        } else { /*if (arg == ARG_loose_binding) */
 551                                flags.mask |= VLAN_FLAG_LOOSE_BINDING;
 552                                flags.flags &= ~VLAN_FLAG_LOOSE_BINDING;
 553                                if (param == PARM_on)
 554                                        flags.flags |= VLAN_FLAG_LOOSE_BINDING;
 555                        }
 556                }
 557                argv++;
 558        }
 559
 560        if (flags.mask)
 561                addattr_l(n, size, IFLA_VLAN_FLAGS, &flags, sizeof(flags));
 562}
 563
 564static void vrf_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size)
 565{
 566/* IFLA_VRF_TABLE is an enum, not a define -
 567 * can't test "defined(IFLA_VRF_TABLE)".
 568 */
 569#if !defined(IFLA_VRF_MAX)
 570# define IFLA_VRF_TABLE 1
 571#endif
 572        uint32_t table;
 573
 574        if (strcmp(*argv, "table") != 0)
 575                invarg_1_to_2(*argv, "type vrf");
 576
 577        NEXT_ARG();
 578        table = get_u32(*argv, "table");
 579        addattr_l(n, size, IFLA_VRF_TABLE, &table, sizeof(table));
 580}
 581
 582#ifndef NLMSG_TAIL
 583#define NLMSG_TAIL(nmsg) \
 584        ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
 585#endif
 586/* Return value becomes exitcode. It's okay to not return at all */
 587static int do_add_or_delete(char **argv, const unsigned rtm)
 588{
 589        static const char keywords[] ALIGN1 =
 590                "link\0""name\0""type\0""dev\0""address\0";
 591        enum {
 592                ARG_link,
 593                ARG_name,
 594                ARG_type,
 595                ARG_dev,
 596                ARG_address,
 597        };
 598        struct rtnl_handle rth;
 599        struct {
 600                struct nlmsghdr  n;
 601                struct ifinfomsg i;
 602                char             buf[1024];
 603        } req;
 604        smalluint arg;
 605        char *name_str = NULL;
 606        char *link_str = NULL;
 607        char *type_str = NULL;
 608        char *dev_str = NULL;
 609        char *address_str = NULL;
 610
 611        memset(&req, 0, sizeof(req));
 612
 613        req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
 614        req.n.nlmsg_flags = NLM_F_REQUEST;
 615        req.n.nlmsg_type = rtm;
 616        req.i.ifi_family = preferred_family;
 617        if (rtm == RTM_NEWLINK)
 618                req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL;
 619
 620        /* NB: update iplink_full_usage if you extend this code */
 621
 622        while (*argv) {
 623                arg = index_in_substrings(keywords, *argv);
 624                if (arg == ARG_type) {
 625                        NEXT_ARG();
 626                        type_str = *argv++;
 627                        dbg("type_str:'%s'", type_str);
 628                        break;
 629                }
 630                if (arg == ARG_link) {
 631                        NEXT_ARG();
 632                        link_str = *argv;
 633                        dbg("link_str:'%s'", link_str);
 634                } else if (arg == ARG_name) {
 635                        NEXT_ARG();
 636                        name_str = *argv;
 637                        dbg("name_str:'%s'", name_str);
 638                } else if (arg == ARG_address) {
 639                        NEXT_ARG();
 640                        address_str = *argv;
 641                        dbg("address_str:'%s'", address_str);
 642                } else {
 643                        if (arg == ARG_dev) {
 644                                if (dev_str)
 645                                        duparg(*argv, "dev");
 646                                NEXT_ARG();
 647                        }
 648                        dev_str = *argv;
 649                        dbg("dev_str:'%s'", dev_str);
 650                }
 651                argv++;
 652        }
 653        xrtnl_open(&rth);
 654        ll_init_map(&rth);
 655        if (type_str) {
 656                struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
 657
 658                addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
 659                addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str,
 660                                strlen(type_str));
 661
 662                if (*argv) {
 663                        struct rtattr *data = NLMSG_TAIL(&req.n);
 664                        addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
 665
 666                        if (strcmp(type_str, "vlan") == 0)
 667                                vlan_parse_opt(argv, &req.n, sizeof(req));
 668                        else if (strcmp(type_str, "vrf") == 0)
 669                                vrf_parse_opt(argv, &req.n, sizeof(req));
 670
 671                        data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
 672                }
 673
 674                linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
 675        }
 676        /* Allow "ip link add dev" and "ip link add name" */
 677        if (!name_str)
 678                name_str = dev_str;
 679        else if (!dev_str)
 680                dev_str = name_str;
 681        /* else if (!strcmp(name_str, dev_str))
 682                name_str = dev_str; */
 683
 684        if (rtm != RTM_NEWLINK) {
 685                if (!dev_str)
 686                        return 1; /* Need a device to delete */
 687                req.i.ifi_index = xll_name_to_index(dev_str);
 688        } else {
 689                if (link_str) {
 690                        int idx = xll_name_to_index(link_str);
 691                        addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4);
 692                }
 693                if (address_str) {
 694                        unsigned char abuf[32];
 695                        int len = ll_addr_a2n(abuf, sizeof(abuf), address_str);
 696                        dbg("address len:%d", len);
 697                        if (len < 0)
 698                                return -1;
 699                        addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, abuf, len);
 700                }
 701        }
 702        if (name_str) {
 703                const size_t name_len = strlen(name_str) + 1;
 704                if (name_len < 2 || name_len > IFNAMSIZ)
 705                        invarg_1_to_2(name_str, "name");
 706                addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len);
 707        }
 708        if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
 709                return 2;
 710        return 0;
 711}
 712
 713/* Return value becomes exitcode. It's okay to not return at all */
 714int FAST_FUNC do_iplink(char **argv)
 715{
 716        static const char keywords[] ALIGN1 =
 717                "add\0""delete\0""set\0""show\0""lst\0""list\0";
 718
 719        xfunc_error_retval = 2; //TODO: move up to "ip"? Is it the common rule for all "ip" tools?
 720        if (*argv) {
 721                int key = index_in_substrings(keywords, *argv);
 722                if (key < 0) /* invalid argument */
 723                        invarg_1_to_2(*argv, applet_name);
 724                argv++;
 725                if (key <= 1) /* add/delete */
 726                        return do_add_or_delete(argv, key ? RTM_DELLINK : RTM_NEWLINK);
 727                if (key == 2) /* set */
 728                        return do_set(argv);
 729        }
 730        /* show, lst, list */
 731        return ipaddr_list_link(argv);
 732}
 733