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