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