iplink: implement support for selecting a master interface
[oweals/busybox.git] / networking / libiproute / iplink.c
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
40 struct 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 */
59 static 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 */
73 static 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 */
90 static 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 */
103 static 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 */
117 static 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 */
131 static 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 */
156 static 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));
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 */
183 static 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 */
201 static 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
214 static void die_must_be_on_off(const char *msg) NORETURN;
215 static 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 */
221 static 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
471 static int ipaddr_list_link(char **argv)
472 {
473         preferred_family = AF_PACKET;
474         return ipaddr_list_or_flush(argv, 0);
475 }
476
477 static 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_reorder_hdr,
494                 ARG_gvrp,
495                 ARG_mvrp,
496                 ARG_loose_binding,
497                 ARG_protocol,
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, *argv);
522                         if (arg == PROTO_8021Q)
523                                 proto = ETH_P_8021Q;
524                         else if (arg == PROTO_8021AD)
525                                 proto = 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
564 static 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 */
587 static 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         if (rtm != RTM_NEWLINK) {
677                 if (!dev_str)
678                         return 1; /* Need a device to delete */
679                 req.i.ifi_index = xll_name_to_index(dev_str);
680         } else {
681                 if (!name_str)
682                         name_str = dev_str;
683                 if (link_str) {
684                         int idx = xll_name_to_index(link_str);
685                         addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4);
686                 }
687                 if (address_str) {
688                         unsigned char abuf[32];
689                         int len = ll_addr_a2n(abuf, sizeof(abuf), address_str);
690                         dbg("address len:%d", len);
691                         if (len < 0)
692                                 return -1;
693                         addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, abuf, len);
694                 }
695         }
696         if (name_str) {
697                 const size_t name_len = strlen(name_str) + 1;
698                 if (name_len < 2 || name_len > IFNAMSIZ)
699                         invarg_1_to_2(name_str, "name");
700                 addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len);
701         }
702         if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
703                 return 2;
704         return 0;
705 }
706
707 /* Return value becomes exitcode. It's okay to not return at all */
708 int FAST_FUNC do_iplink(char **argv)
709 {
710         static const char keywords[] ALIGN1 =
711                 "add\0""delete\0""set\0""show\0""lst\0""list\0";
712
713         xfunc_error_retval = 2; //TODO: move up to "ip"? Is it the common rule for all "ip" tools?
714         if (*argv) {
715                 int key = index_in_substrings(keywords, *argv);
716                 if (key < 0) /* invalid argument */
717                         invarg_1_to_2(*argv, applet_name);
718                 argv++;
719                 if (key <= 1) /* add/delete */
720                         return do_add_or_delete(argv, key ? RTM_DELLINK : RTM_NEWLINK);
721                 if (key == 2) /* set */
722                         return do_set(argv);
723         }
724         /* show, lst, list */
725         return ipaddr_list_link(argv);
726 }