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 */
get_ctl_fd(void)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 */
do_chflags(char * dev,uint32_t flags,uint32_t mask)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 */
do_changename(char * dev,char * newdev)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 */
set_qlen(char * dev,int qlen)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 */
set_mtu(char * dev,int mtu)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 */
get_address(char * dev,int * htype)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 */
parse_address(char * dev,int hatype,int halen,char * lla,struct ifreq * ifr)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 */
set_address(struct ifreq * ifr,int brd)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;
die_must_be_on_off(const char * msg)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 */
do_set(char ** argv)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 
ipaddr_list_link(char ** argv)305 static int ipaddr_list_link(char **argv)
306 {
307 	preferred_family = AF_PACKET;
308 	return ipaddr_list_or_flush(argv, 0);
309 }
310 
vlan_parse_opt(char ** argv,struct nlmsghdr * n,unsigned int size)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 */
do_add_or_delete(char ** argv,const unsigned rtm)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 */
do_iplink(char ** argv)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 }
699