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