source: MondoRescue/branches/3.3/mindi-busybox/networking/libiproute/iplink.c@ 3803

Last change on this file since 3803 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 17.4 KB
Line 
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
40struct 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 */
56static 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 */
70static 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 */
87static 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 */
100static 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 */
114static 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 */
128static 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 */
157static 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 */
175static 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
188static void die_must_be_on_off(const char *msg) NORETURN;
189static 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 */
195static 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
305static int ipaddr_list_link(char **argv)
306{
307 preferred_family = AF_PACKET;
308 return ipaddr_list_or_flush(argv, 0);
309}
310
311static 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 */
407static 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 */
682int 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}
Note: See TracBrowser for help on using the repository browser.