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