]> rtime.felk.cvut.cz Git - lisovros/iproute2_canprio.git/blob - ip/iplink.c
iproute: DESTDIR vs LIBDIR.
[lisovros/iproute2_canprio.git] / ip / iplink.c
1 /*
2  * iplink.c             "ip link".
3  *
4  *              This program is free software; you can redistribute it and/or
5  *              modify it under the terms of the GNU General Public License
6  *              as published by the Free Software Foundation; either version
7  *              2 of the License, or (at your option) any later version.
8  *
9  * Authors:     Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10  *
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <syslog.h>
17 #include <fcntl.h>
18 #include <dlfcn.h>
19 #include <errno.h>
20 #include <sys/socket.h>
21 #include <linux/if.h>
22 #include <linux/if_packet.h>
23 #include <linux/if_ether.h>
24 #include <linux/sockios.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <string.h>
28 #include <sys/ioctl.h>
29 #include <linux/sockios.h>
30
31 #include "rt_names.h"
32 #include "utils.h"
33 #include "ip_common.h"
34
35 #define IPLINK_IOCTL_COMPAT     1
36 #ifndef LIBDIR
37 #define LIBDIR "/usr/lib/"
38 #endif
39
40 static void usage(void) __attribute__((noreturn));
41
42 void iplink_usage(void)
43 {
44         fprintf(stderr, "Usage: ip link set DEVICE { up | down |\n");
45         fprintf(stderr, "                            arp { on | off } |\n");
46         fprintf(stderr, "                            dynamic { on | off } |\n");
47         fprintf(stderr, "                            multicast { on | off } |\n");
48         fprintf(stderr, "                            allmulticast { on | off } |\n");
49         fprintf(stderr, "                            promisc { on | off } |\n");
50         fprintf(stderr, "                            trailers { on | off } |\n");
51         fprintf(stderr, "                            txqueuelen PACKETS |\n");
52         fprintf(stderr, "                            name NEWNAME |\n");
53         fprintf(stderr, "                            address LLADDR | broadcast LLADDR |\n");
54         fprintf(stderr, "                            mtu MTU }\n");
55         fprintf(stderr, "                            netns PID }\n");
56         fprintf(stderr, "       ip link show [ DEVICE ]\n");
57         exit(-1);
58 }
59
60 static void usage(void)
61 {
62         iplink_usage();
63 }
64
65 static int on_off(char *msg)
66 {
67         fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
68         return -1;
69 }
70
71 static void *BODY;              /* cached dlopen(NULL) handle */
72 static struct link_util *linkutil_list;
73
74 struct link_util *get_link_kind(const char *id)
75 {
76         void *dlh;
77         char buf[256];
78         struct link_util *l;
79
80         for (l = linkutil_list; l; l = l->next)
81                 if (strcmp(l->id, id) == 0)
82                         return l;
83
84         snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id);
85         dlh = dlopen(buf, RTLD_LAZY);
86         if (dlh == NULL) {
87                 /* look in current binary, only open once */
88                 dlh = BODY;
89                 if (dlh == NULL) {
90                         dlh = BODY = dlopen(NULL, RTLD_LAZY);
91                         if (dlh == NULL)
92                                 return NULL;
93                 }
94         }
95
96         snprintf(buf, sizeof(buf), "%s_link_util", id);
97         l = dlsym(dlh, buf);
98         if (l == NULL)
99                 return NULL;
100
101         l->next = linkutil_list;
102         linkutil_list = l;
103         return l;
104 }
105
106 #if IPLINK_IOCTL_COMPAT
107 static int have_rtnl_newlink = -1;
108
109 static int accept_msg(const struct sockaddr_nl *who,
110                       struct nlmsghdr *n, void *arg)
111 {
112         struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
113
114         if (n->nlmsg_type == NLMSG_ERROR &&
115             (err->error == -EOPNOTSUPP || err->error == -EINVAL))
116                 have_rtnl_newlink = 0;
117         else
118                 have_rtnl_newlink = 1;
119         return -1;
120 }
121
122 static int iplink_have_newlink(void)
123 {
124         struct {
125                 struct nlmsghdr         n;
126                 struct ifinfomsg        i;
127                 char                    buf[1024];
128         } req;
129
130         if (have_rtnl_newlink < 0) {
131                 memset(&req, 0, sizeof(req));
132
133                 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
134                 req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
135                 req.n.nlmsg_type = RTM_NEWLINK;
136                 req.i.ifi_family = AF_UNSPEC;
137
138                 rtnl_send(&rth, (char *)&req.n, req.n.nlmsg_len);
139                 rtnl_listen(&rth, accept_msg, NULL);
140         }
141         return have_rtnl_newlink;
142 }
143 #else /* IPLINK_IOCTL_COMPAT */
144 static int iplink_have_newlink(void)
145 {
146         return 1;
147 }
148 #endif /* ! IPLINK_IOCTL_COMPAT */
149
150 struct iplink_req {
151         struct nlmsghdr         n;
152         struct ifinfomsg        i;
153         char                    buf[1024];
154 };
155
156 int iplink_parse(int argc, char **argv, struct iplink_req *req,
157                 char **name, char **type, char **link, char **dev)
158 {
159         int ret, len;
160         char abuf[32];
161         int qlen = -1;
162         int mtu = -1;
163         int netns = -1;
164
165         ret = argc;
166
167         while (argc > 0) {
168                 if (strcmp(*argv, "up") == 0) {
169                         req->i.ifi_change |= IFF_UP;
170                         req->i.ifi_flags |= IFF_UP;
171                 } else if (strcmp(*argv, "down") == 0) {
172                         req->i.ifi_change |= IFF_UP;
173                         req->i.ifi_flags &= ~IFF_UP;
174                 } else if (strcmp(*argv, "name") == 0) {
175                         NEXT_ARG();
176                         *name = *argv;
177                 } else if (matches(*argv, "link") == 0) {
178                         NEXT_ARG();
179                         *link = *argv;
180                 } else if (matches(*argv, "address") == 0) {
181                         NEXT_ARG();
182                         len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
183                         addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len);
184                 } else if (matches(*argv, "broadcast") == 0 ||
185                                 strcmp(*argv, "brd") == 0) {
186                         NEXT_ARG();
187                         len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
188                         addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len);
189                 } else if (matches(*argv, "txqueuelen") == 0 ||
190                                 strcmp(*argv, "qlen") == 0 ||
191                                 matches(*argv, "txqlen") == 0) {
192                         NEXT_ARG();
193                         if (qlen != -1)
194                                 duparg("txqueuelen", *argv);
195                         if (get_integer(&qlen,  *argv, 0))
196                                 invarg("Invalid \"txqueuelen\" value\n", *argv);
197                         addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4);
198                 } else if (strcmp(*argv, "mtu") == 0) {
199                         NEXT_ARG();
200                         if (mtu != -1)
201                                 duparg("mtu", *argv);
202                         if (get_integer(&mtu, *argv, 0))
203                                 invarg("Invalid \"mtu\" value\n", *argv);
204                         addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4);
205                 } else if (strcmp(*argv, "netns") == 0) {
206                         NEXT_ARG();
207                         if (netns != -1)
208                                 duparg("netns", *argv);
209                         if (get_integer(&netns, *argv, 0))
210                                 invarg("Invalid \"netns\" value\n", *argv);
211                         addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4);
212                 } else if (strcmp(*argv, "multicast") == 0) {
213                         NEXT_ARG();
214                         req->i.ifi_change |= IFF_MULTICAST;
215                         if (strcmp(*argv, "on") == 0) {
216                                 req->i.ifi_flags |= IFF_MULTICAST;
217                         } else if (strcmp(*argv, "off") == 0) {
218                                 req->i.ifi_flags &= ~IFF_MULTICAST;
219                         } else
220                                 return on_off("multicast");
221                 } else if (strcmp(*argv, "allmulticast") == 0) {
222                         NEXT_ARG();
223                         req->i.ifi_change |= IFF_ALLMULTI;
224                         if (strcmp(*argv, "on") == 0) {
225                                 req->i.ifi_flags |= IFF_ALLMULTI;
226                         } else if (strcmp(*argv, "off") == 0) {
227                                 req->i.ifi_flags &= ~IFF_ALLMULTI;
228                         } else
229                                 return on_off("allmulticast");
230                 } else if (strcmp(*argv, "promisc") == 0) {
231                         NEXT_ARG();
232                         req->i.ifi_change |= IFF_PROMISC;
233                         if (strcmp(*argv, "on") == 0) {
234                                 req->i.ifi_flags |= IFF_PROMISC;
235                         } else if (strcmp(*argv, "off") == 0) {
236                                 req->i.ifi_flags &= ~IFF_PROMISC;
237                         } else
238                                 return on_off("promisc");
239                 } else if (strcmp(*argv, "trailers") == 0) {
240                         NEXT_ARG();
241                         req->i.ifi_change |= IFF_NOTRAILERS;
242                         if (strcmp(*argv, "off") == 0) {
243                                 req->i.ifi_flags |= IFF_NOTRAILERS;
244                         } else if (strcmp(*argv, "on") == 0) {
245                                 req->i.ifi_flags &= ~IFF_NOTRAILERS;
246                         } else
247                                 return on_off("trailers");
248                 } else if (strcmp(*argv, "arp") == 0) {
249                         NEXT_ARG();
250                         req->i.ifi_change |= IFF_NOARP;
251                         if (strcmp(*argv, "on") == 0) {
252                                 req->i.ifi_flags &= ~IFF_NOARP;
253                         } else if (strcmp(*argv, "off") == 0) {
254                                 req->i.ifi_flags |= IFF_NOARP;
255                         } else
256                                 return on_off("noarp");
257 #ifdef IFF_DYNAMIC
258                 } else if (matches(*argv, "dynamic") == 0) {
259                         NEXT_ARG();
260                         req->i.ifi_change |= IFF_DYNAMIC;
261                         if (strcmp(*argv, "on") == 0) {
262                                 req->i.ifi_flags |= IFF_DYNAMIC;
263                         } else if (strcmp(*argv, "off") == 0) {
264                                 req->i.ifi_flags &= ~IFF_DYNAMIC;
265                         } else
266                                 return on_off("dynamic");
267 #endif
268                 } else if (matches(*argv, "type") == 0) {
269                         NEXT_ARG();
270                         *type = *argv;
271                         argc--; argv++;
272                         break;
273                 } else {
274                         if (strcmp(*argv, "dev") == 0) {
275                                 NEXT_ARG();
276                         }
277                         if (*dev)
278                                 duparg2("dev", *argv);
279                         *dev = *argv;
280                 }
281                 argc--; argv++;
282         }
283
284         return ret - argc;
285 }
286
287 static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
288 {
289         int len;
290         char *dev = NULL;
291         char *name = NULL;
292         char *link = NULL;
293         char *type = NULL;
294         struct link_util *lu = NULL;
295         struct iplink_req req;
296         int ret;
297
298         memset(&req, 0, sizeof(req));
299
300         req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
301         req.n.nlmsg_flags = NLM_F_REQUEST|flags;
302         req.n.nlmsg_type = cmd;
303         req.i.ifi_family = preferred_family;
304
305         ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev);
306         if (ret < 0)
307                 return ret;
308
309         argc -= ret;
310         argv += ret;
311         ll_init_map(&rth);
312
313         if (type) {
314                 struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
315                 addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
316                 addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type,
317                          strlen(type));
318
319                 lu = get_link_kind(type);
320                 if (lu && argc) {
321                         struct rtattr * data = NLMSG_TAIL(&req.n);
322                         addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
323
324                         if (lu->parse_opt &&
325                             lu->parse_opt(lu, argc, argv, &req.n))
326                                 return -1;
327
328                         data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
329                 } else if (argc) {
330                         if (matches(*argv, "help") == 0)
331                                 usage();
332                         fprintf(stderr, "Garbage instead of arguments \"%s ...\". "
333                                         "Try \"ip link help\".\n", *argv);
334                         return -1;
335                 }
336                 linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
337         }
338
339         if (!(flags & NLM_F_CREATE)) {
340                 if (!dev) {
341                         fprintf(stderr, "Not enough information: \"dev\" "
342                                         "argument is required.\n");
343                         exit(-1);
344                 }
345
346                 req.i.ifi_index = ll_name_to_index(dev);
347                 if (req.i.ifi_index == 0) {
348                         fprintf(stderr, "Cannot find device \"%s\"\n", dev);
349                         return -1;
350                 }
351         } else {
352                 /* Allow "ip link add dev" and "ip link add name" */
353                 if (!name)
354                         name = dev;
355
356                 if (link) {
357                         int ifindex;
358
359                         ifindex = ll_name_to_index(link);
360                         if (ifindex == 0) {
361                                 fprintf(stderr, "Cannot find device \"%s\"\n",
362                                         link);
363                                 return -1;
364                         }
365                         addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4);
366                 }
367         }
368
369         if (name) {
370                 len = strlen(name) + 1;
371                 if (len == 1)
372                         invarg("\"\" is not a valid device identifier\n", "name");
373                 if (len > IFNAMSIZ)
374                         invarg("\"name\" too long\n", name);
375                 addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
376         }
377
378         if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
379                 exit(2);
380
381         return 0;
382 }
383
384 #if IPLINK_IOCTL_COMPAT
385 static int get_ctl_fd(void)
386 {
387         int s_errno;
388         int fd;
389
390         fd = socket(PF_INET, SOCK_DGRAM, 0);
391         if (fd >= 0)
392                 return fd;
393         s_errno = errno;
394         fd = socket(PF_PACKET, SOCK_DGRAM, 0);
395         if (fd >= 0)
396                 return fd;
397         fd = socket(PF_INET6, SOCK_DGRAM, 0);
398         if (fd >= 0)
399                 return fd;
400         errno = s_errno;
401         perror("Cannot create control socket");
402         return -1;
403 }
404
405 static int do_chflags(const char *dev, __u32 flags, __u32 mask)
406 {
407         struct ifreq ifr;
408         int fd;
409         int err;
410
411         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
412         fd = get_ctl_fd();
413         if (fd < 0)
414                 return -1;
415         err = ioctl(fd, SIOCGIFFLAGS, &ifr);
416         if (err) {
417                 perror("SIOCGIFFLAGS");
418                 close(fd);
419                 return -1;
420         }
421         if ((ifr.ifr_flags^flags)&mask) {
422                 ifr.ifr_flags &= ~mask;
423                 ifr.ifr_flags |= mask&flags;
424                 err = ioctl(fd, SIOCSIFFLAGS, &ifr);
425                 if (err)
426                         perror("SIOCSIFFLAGS");
427         }
428         close(fd);
429         return err;
430 }
431
432 static int do_changename(const char *dev, const char *newdev)
433 {
434         struct ifreq ifr;
435         int fd;
436         int err;
437
438         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
439         strncpy(ifr.ifr_newname, newdev, IFNAMSIZ);
440         fd = get_ctl_fd();
441         if (fd < 0)
442                 return -1;
443         err = ioctl(fd, SIOCSIFNAME, &ifr);
444         if (err) {
445                 perror("SIOCSIFNAME");
446                 close(fd);
447                 return -1;
448         }
449         close(fd);
450         return err;
451 }
452
453 static int set_qlen(const char *dev, int qlen)
454 {
455         struct ifreq ifr;
456         int s;
457
458         s = get_ctl_fd();
459         if (s < 0)
460                 return -1;
461
462         memset(&ifr, 0, sizeof(ifr));
463         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
464         ifr.ifr_qlen = qlen;
465         if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
466                 perror("SIOCSIFXQLEN");
467                 close(s);
468                 return -1;
469         }
470         close(s);
471
472         return 0;
473 }
474
475 static int set_mtu(const char *dev, int mtu)
476 {
477         struct ifreq ifr;
478         int s;
479
480         s = get_ctl_fd();
481         if (s < 0)
482                 return -1;
483
484         memset(&ifr, 0, sizeof(ifr));
485         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
486         ifr.ifr_mtu = mtu;
487         if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
488                 perror("SIOCSIFMTU");
489                 close(s);
490                 return -1;
491         }
492         close(s);
493
494         return 0;
495 }
496
497 static int get_address(const char *dev, int *htype)
498 {
499         struct ifreq ifr;
500         struct sockaddr_ll me;
501         socklen_t alen;
502         int s;
503
504         s = socket(PF_PACKET, SOCK_DGRAM, 0);
505         if (s < 0) {
506                 perror("socket(PF_PACKET)");
507                 return -1;
508         }
509
510         memset(&ifr, 0, sizeof(ifr));
511         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
512         if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
513                 perror("SIOCGIFINDEX");
514                 close(s);
515                 return -1;
516         }
517
518         memset(&me, 0, sizeof(me));
519         me.sll_family = AF_PACKET;
520         me.sll_ifindex = ifr.ifr_ifindex;
521         me.sll_protocol = htons(ETH_P_LOOP);
522         if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
523                 perror("bind");
524                 close(s);
525                 return -1;
526         }
527
528         alen = sizeof(me);
529         if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
530                 perror("getsockname");
531                 close(s);
532                 return -1;
533         }
534         close(s);
535         *htype = me.sll_hatype;
536         return me.sll_halen;
537 }
538
539 static int parse_address(const char *dev, int hatype, int halen,
540                 char *lla, struct ifreq *ifr)
541 {
542         int alen;
543
544         memset(ifr, 0, sizeof(*ifr));
545         strncpy(ifr->ifr_name, dev, IFNAMSIZ);
546         ifr->ifr_hwaddr.sa_family = hatype;
547         alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
548         if (alen < 0)
549                 return -1;
550         if (alen != halen) {
551                 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
552                 return -1;
553         }
554         return 0;
555 }
556
557 static int set_address(struct ifreq *ifr, int brd)
558 {
559         int s;
560
561         s = get_ctl_fd();
562         if (s < 0)
563                 return -1;
564         if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
565                 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
566                 close(s);
567                 return -1;
568         }
569         close(s);
570         return 0;
571 }
572
573
574 static int do_set(int argc, char **argv)
575 {
576         char *dev = NULL;
577         __u32 mask = 0;
578         __u32 flags = 0;
579         int qlen = -1;
580         int mtu = -1;
581         char *newaddr = NULL;
582         char *newbrd = NULL;
583         struct ifreq ifr0, ifr1;
584         char *newname = NULL;
585         int htype, halen;
586
587         while (argc > 0) {
588                 if (strcmp(*argv, "up") == 0) {
589                         mask |= IFF_UP;
590                         flags |= IFF_UP;
591                 } else if (strcmp(*argv, "down") == 0) {
592                         mask |= IFF_UP;
593                         flags &= ~IFF_UP;
594                 } else if (strcmp(*argv, "name") == 0) {
595                         NEXT_ARG();
596                         newname = *argv;
597                 } else if (matches(*argv, "address") == 0) {
598                         NEXT_ARG();
599                         newaddr = *argv;
600                 } else if (matches(*argv, "broadcast") == 0 ||
601                            strcmp(*argv, "brd") == 0) {
602                         NEXT_ARG();
603                         newbrd = *argv;
604                 } else if (matches(*argv, "txqueuelen") == 0 ||
605                            strcmp(*argv, "qlen") == 0 ||
606                            matches(*argv, "txqlen") == 0) {
607                         NEXT_ARG();
608                         if (qlen != -1)
609                                 duparg("txqueuelen", *argv);
610                         if (get_integer(&qlen,  *argv, 0))
611                                 invarg("Invalid \"txqueuelen\" value\n", *argv);
612                 } else if (strcmp(*argv, "mtu") == 0) {
613                         NEXT_ARG();
614                         if (mtu != -1)
615                                 duparg("mtu", *argv);
616                         if (get_integer(&mtu, *argv, 0))
617                                 invarg("Invalid \"mtu\" value\n", *argv);
618                 } else if (strcmp(*argv, "multicast") == 0) {
619                         NEXT_ARG();
620                         mask |= IFF_MULTICAST;
621                         if (strcmp(*argv, "on") == 0) {
622                                 flags |= IFF_MULTICAST;
623                         } else if (strcmp(*argv, "off") == 0) {
624                                 flags &= ~IFF_MULTICAST;
625                         } else
626                                 return on_off("multicast");
627                 } else if (strcmp(*argv, "allmulticast") == 0) {
628                         NEXT_ARG();
629                         mask |= IFF_ALLMULTI;
630                         if (strcmp(*argv, "on") == 0) {
631                                 flags |= IFF_ALLMULTI;
632                         } else if (strcmp(*argv, "off") == 0) {
633                                 flags &= ~IFF_ALLMULTI;
634                         } else
635                                 return on_off("allmulticast");
636                 } else if (strcmp(*argv, "promisc") == 0) {
637                         NEXT_ARG();
638                         mask |= IFF_PROMISC;
639                         if (strcmp(*argv, "on") == 0) {
640                                 flags |= IFF_PROMISC;
641                         } else if (strcmp(*argv, "off") == 0) {
642                                 flags &= ~IFF_PROMISC;
643                         } else
644                                 return on_off("promisc");
645                 } else if (strcmp(*argv, "trailers") == 0) {
646                         NEXT_ARG();
647                         mask |= IFF_NOTRAILERS;
648                         if (strcmp(*argv, "off") == 0) {
649                                 flags |= IFF_NOTRAILERS;
650                         } else if (strcmp(*argv, "on") == 0) {
651                                 flags &= ~IFF_NOTRAILERS;
652                         } else
653                                 return on_off("trailers");
654                 } else if (strcmp(*argv, "arp") == 0) {
655                         NEXT_ARG();
656                         mask |= IFF_NOARP;
657                         if (strcmp(*argv, "on") == 0) {
658                                 flags &= ~IFF_NOARP;
659                         } else if (strcmp(*argv, "off") == 0) {
660                                 flags |= IFF_NOARP;
661                         } else
662                                 return on_off("noarp");
663 #ifdef IFF_DYNAMIC
664                 } else if (matches(*argv, "dynamic") == 0) {
665                         NEXT_ARG();
666                         mask |= IFF_DYNAMIC;
667                         if (strcmp(*argv, "on") == 0) {
668                                 flags |= IFF_DYNAMIC;
669                         } else if (strcmp(*argv, "off") == 0) {
670                                 flags &= ~IFF_DYNAMIC;
671                         } else
672                                 return on_off("dynamic");
673 #endif
674                 } else {
675                         if (strcmp(*argv, "dev") == 0) {
676                                 NEXT_ARG();
677                         }
678                         if (matches(*argv, "help") == 0)
679                                 usage();
680                         if (dev)
681                                 duparg2("dev", *argv);
682                         dev = *argv;
683                 }
684                 argc--; argv++;
685         }
686
687         if (!dev) {
688                 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
689                 exit(-1);
690         }
691
692         if (newaddr || newbrd) {
693                 halen = get_address(dev, &htype);
694                 if (halen < 0)
695                         return -1;
696                 if (newaddr) {
697                         if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
698                                 return -1;
699                 }
700                 if (newbrd) {
701                         if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
702                                 return -1;
703                 }
704         }
705
706         if (newname && strcmp(dev, newname)) {
707                 if (strlen(newname) == 0)
708                         invarg("\"\" is not a valid device identifier\n", "name");
709                 if (do_changename(dev, newname) < 0)
710                         return -1;
711                 dev = newname;
712         }
713         if (qlen != -1) {
714                 if (set_qlen(dev, qlen) < 0)
715                         return -1;
716         }
717         if (mtu != -1) {
718                 if (set_mtu(dev, mtu) < 0)
719                         return -1;
720         }
721         if (newaddr || newbrd) {
722                 if (newbrd) {
723                         if (set_address(&ifr1, 1) < 0)
724                                 return -1;
725                 }
726                 if (newaddr) {
727                         if (set_address(&ifr0, 0) < 0)
728                                 return -1;
729                 }
730         }
731         if (mask)
732                 return do_chflags(dev, flags, mask);
733         return 0;
734 }
735 #endif /* IPLINK_IOCTL_COMPAT */
736
737 int do_iplink(int argc, char **argv)
738 {
739         if (argc > 0) {
740                 if (iplink_have_newlink()) {
741                         if (matches(*argv, "add") == 0)
742                                 return iplink_modify(RTM_NEWLINK,
743                                                      NLM_F_CREATE|NLM_F_EXCL,
744                                                      argc-1, argv+1);
745                         if (matches(*argv, "set") == 0 ||
746                             matches(*argv, "change") == 0)
747                                 return iplink_modify(RTM_NEWLINK, 0,
748                                                      argc-1, argv+1);
749                         if (matches(*argv, "replace") == 0)
750                                 return iplink_modify(RTM_NEWLINK,
751                                                      NLM_F_CREATE|NLM_F_REPLACE,
752                                                      argc-1, argv+1);
753                         if (matches(*argv, "delete") == 0)
754                                 return iplink_modify(RTM_DELLINK, 0,
755                                                      argc-1, argv+1);
756                 } else {
757 #if IPLINK_IOCTL_COMPAT
758                         if (matches(*argv, "set") == 0)
759                                 return do_set(argc-1, argv+1);
760 #endif
761                 }
762                 if (matches(*argv, "show") == 0 ||
763                     matches(*argv, "lst") == 0 ||
764                     matches(*argv, "list") == 0)
765                         return ipaddr_list_link(argc-1, argv+1);
766                 if (matches(*argv, "help") == 0)
767                         usage();
768         } else
769                 return ipaddr_list_link(0, NULL);
770
771         fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv);
772         exit(-1);
773 }