blob: 29cf1abc7cf9172e5c430f2b176aaaca9fb8e0bb
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 | #include <linux/if_ether.h> |
13 | |
14 | #include <linux/if_vlan.h> |
15 | #include "ip_common.h" /* #include "libbb.h" is inside */ |
16 | #include "rt_names.h" |
17 | #include "utils.h" |
18 | |
19 | #undef ETH_P_8021AD |
20 | #define ETH_P_8021AD 0x88A8 |
21 | #undef VLAN_FLAG_REORDER_HDR |
22 | #define VLAN_FLAG_REORDER_HDR 0x1 |
23 | #undef VLAN_FLAG_GVRP |
24 | #define VLAN_FLAG_GVRP 0x2 |
25 | #undef VLAN_FLAG_LOOSE_BINDING |
26 | #define VLAN_FLAG_LOOSE_BINDING 0x4 |
27 | #undef VLAN_FLAG_MVRP |
28 | #define VLAN_FLAG_MVRP 0x8 |
29 | #undef IFLA_VLAN_PROTOCOL |
30 | #define IFLA_VLAN_PROTOCOL 5 |
31 | |
32 | #ifndef IFLA_LINKINFO |
33 | # define IFLA_LINKINFO 18 |
34 | # define IFLA_INFO_KIND 1 |
35 | # define IFLA_INFO_DATA 2 |
36 | #endif |
37 | |
38 | #ifndef IFLA_VLAN_MAX |
39 | # define IFLA_VLAN_ID 1 |
40 | # define IFLA_VLAN_FLAGS 2 |
41 | struct ifla_vlan_flags { |
42 | uint32_t flags; |
43 | uint32_t mask; |
44 | }; |
45 | #endif |
46 | |
47 | /* taken from linux/sockios.h */ |
48 | #define SIOCSIFNAME 0x8923 /* set interface name */ |
49 | |
50 | #if 0 |
51 | # define dbg(...) bb_error_msg(__VA_ARGS__) |
52 | #else |
53 | # define dbg(...) ((void)0) |
54 | #endif |
55 | |
56 | /* Exits on error */ |
57 | static int get_ctl_fd(void) |
58 | { |
59 | int fd; |
60 | |
61 | fd = socket(PF_INET, SOCK_DGRAM, 0); |
62 | if (fd >= 0) |
63 | return fd; |
64 | fd = socket(PF_PACKET, SOCK_DGRAM, 0); |
65 | if (fd >= 0) |
66 | return fd; |
67 | return xsocket(PF_INET6, SOCK_DGRAM, 0); |
68 | } |
69 | |
70 | /* Exits on error */ |
71 | static void do_chflags(char *dev, uint32_t flags, uint32_t mask) |
72 | { |
73 | struct ifreq ifr; |
74 | int fd; |
75 | |
76 | strncpy_IFNAMSIZ(ifr.ifr_name, dev); |
77 | fd = get_ctl_fd(); |
78 | xioctl(fd, SIOCGIFFLAGS, &ifr); |
79 | if ((ifr.ifr_flags ^ flags) & mask) { |
80 | ifr.ifr_flags &= ~mask; |
81 | ifr.ifr_flags |= mask & flags; |
82 | xioctl(fd, SIOCSIFFLAGS, &ifr); |
83 | } |
84 | close(fd); |
85 | } |
86 | |
87 | /* Exits on error */ |
88 | static void do_changename(char *dev, char *newdev) |
89 | { |
90 | struct ifreq ifr; |
91 | int fd; |
92 | |
93 | strncpy_IFNAMSIZ(ifr.ifr_name, dev); |
94 | strncpy_IFNAMSIZ(ifr.ifr_newname, newdev); |
95 | fd = get_ctl_fd(); |
96 | xioctl(fd, SIOCSIFNAME, &ifr); |
97 | close(fd); |
98 | } |
99 | |
100 | /* Exits on error */ |
101 | static void set_qlen(char *dev, int qlen) |
102 | { |
103 | struct ifreq ifr; |
104 | int s; |
105 | |
106 | s = get_ctl_fd(); |
107 | memset(&ifr, 0, sizeof(ifr)); |
108 | strncpy_IFNAMSIZ(ifr.ifr_name, dev); |
109 | ifr.ifr_qlen = qlen; |
110 | xioctl(s, SIOCSIFTXQLEN, &ifr); |
111 | close(s); |
112 | } |
113 | |
114 | /* Exits on error */ |
115 | static void set_mtu(char *dev, int mtu) |
116 | { |
117 | struct ifreq ifr; |
118 | int s; |
119 | |
120 | s = get_ctl_fd(); |
121 | memset(&ifr, 0, sizeof(ifr)); |
122 | strncpy_IFNAMSIZ(ifr.ifr_name, dev); |
123 | ifr.ifr_mtu = mtu; |
124 | xioctl(s, SIOCSIFMTU, &ifr); |
125 | close(s); |
126 | } |
127 | |
128 | /* Exits on error */ |
129 | static int get_address(char *dev, int *htype) |
130 | { |
131 | struct ifreq ifr; |
132 | struct sockaddr_ll me; |
133 | socklen_t alen; |
134 | int s; |
135 | |
136 | s = xsocket(PF_PACKET, SOCK_DGRAM, 0); |
137 | |
138 | memset(&ifr, 0, sizeof(ifr)); |
139 | strncpy_IFNAMSIZ(ifr.ifr_name, dev); |
140 | xioctl(s, SIOCGIFINDEX, &ifr); |
141 | |
142 | memset(&me, 0, sizeof(me)); |
143 | me.sll_family = AF_PACKET; |
144 | me.sll_ifindex = ifr.ifr_ifindex; |
145 | me.sll_protocol = htons(ETH_P_LOOP); |
146 | xbind(s, (struct sockaddr*)&me, sizeof(me)); |
147 | alen = sizeof(me); |
148 | getsockname(s, (struct sockaddr*)&me, &alen); |
149 | //never happens: |
150 | //if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) |
151 | // bb_perror_msg_and_die("getsockname"); |
152 | close(s); |
153 | *htype = me.sll_hatype; |
154 | return me.sll_halen; |
155 | } |
156 | |
157 | /* Exits on error */ |
158 | static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr) |
159 | { |
160 | int alen; |
161 | |
162 | memset(ifr, 0, sizeof(*ifr)); |
163 | strncpy_IFNAMSIZ(ifr->ifr_name, dev); |
164 | ifr->ifr_hwaddr.sa_family = hatype; |
165 | |
166 | alen = hatype == 1/*ARPHRD_ETHER*/ ? 14/*ETH_HLEN*/ : 19/*INFINIBAND_HLEN*/; |
167 | alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), alen, lla); |
168 | if (alen < 0) |
169 | exit(EXIT_FAILURE); |
170 | if (alen != halen) { |
171 | bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen); |
172 | } |
173 | } |
174 | |
175 | /* Exits on error */ |
176 | static void set_address(struct ifreq *ifr, int brd) |
177 | { |
178 | int s; |
179 | |
180 | s = get_ctl_fd(); |
181 | if (brd) |
182 | xioctl(s, SIOCSIFHWBROADCAST, ifr); |
183 | else |
184 | xioctl(s, SIOCSIFHWADDR, ifr); |
185 | close(s); |
186 | } |
187 | |
188 | |
189 | static void die_must_be_on_off(const char *msg) NORETURN; |
190 | static void die_must_be_on_off(const char *msg) |
191 | { |
192 | bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg); |
193 | } |
194 | |
195 | /* Return value becomes exitcode. It's okay to not return at all */ |
196 | static int do_set(char **argv) |
197 | { |
198 | char *dev = NULL; |
199 | uint32_t mask = 0; |
200 | uint32_t flags = 0; |
201 | int qlen = -1; |
202 | int mtu = -1; |
203 | char *newaddr = NULL; |
204 | char *newbrd = NULL; |
205 | struct ifreq ifr0, ifr1; |
206 | char *newname = NULL; |
207 | int htype, halen; |
208 | static const char keywords[] ALIGN1 = |
209 | "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0" |
210 | "arp\0""address\0""dev\0"; |
211 | enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast, |
212 | ARG_arp, ARG_addr, ARG_dev }; |
213 | static const char str_on_off[] ALIGN1 = "on\0""off\0"; |
214 | enum { PARM_on = 0, PARM_off }; |
215 | smalluint key; |
216 | |
217 | while (*argv) { |
218 | /* substring search ensures that e.g. "addr" and "address" |
219 | * are both accepted */ |
220 | key = index_in_substrings(keywords, *argv); |
221 | if (key == ARG_up) { |
222 | mask |= IFF_UP; |
223 | flags |= IFF_UP; |
224 | } else if (key == ARG_down) { |
225 | mask |= IFF_UP; |
226 | flags &= ~IFF_UP; |
227 | } else if (key == ARG_name) { |
228 | NEXT_ARG(); |
229 | newname = *argv; |
230 | } else if (key == ARG_mtu) { |
231 | NEXT_ARG(); |
232 | if (mtu != -1) |
233 | duparg("mtu", *argv); |
234 | mtu = get_unsigned(*argv, "mtu"); |
235 | } else if (key == ARG_qlen) { |
236 | NEXT_ARG(); |
237 | if (qlen != -1) |
238 | duparg("qlen", *argv); |
239 | qlen = get_unsigned(*argv, "qlen"); |
240 | } else if (key == ARG_addr) { |
241 | NEXT_ARG(); |
242 | newaddr = *argv; |
243 | } else if (key >= ARG_dev) { |
244 | if (key == ARG_dev) { |
245 | NEXT_ARG(); |
246 | } |
247 | if (dev) |
248 | duparg2("dev", *argv); |
249 | dev = *argv; |
250 | } else { |
251 | int param; |
252 | NEXT_ARG(); |
253 | param = index_in_strings(str_on_off, *argv); |
254 | if (key == ARG_multicast) { |
255 | if (param < 0) |
256 | die_must_be_on_off("multicast"); |
257 | mask |= IFF_MULTICAST; |
258 | if (param == PARM_on) |
259 | flags |= IFF_MULTICAST; |
260 | else |
261 | flags &= ~IFF_MULTICAST; |
262 | } else if (key == ARG_arp) { |
263 | if (param < 0) |
264 | die_must_be_on_off("arp"); |
265 | mask |= IFF_NOARP; |
266 | if (param == PARM_on) |
267 | flags &= ~IFF_NOARP; |
268 | else |
269 | flags |= IFF_NOARP; |
270 | } |
271 | } |
272 | argv++; |
273 | } |
274 | |
275 | if (!dev) { |
276 | bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\""); |
277 | } |
278 | |
279 | if (newaddr || newbrd) { |
280 | halen = get_address(dev, &htype); |
281 | if (newaddr) { |
282 | parse_address(dev, htype, halen, newaddr, &ifr0); |
283 | set_address(&ifr0, 0); |
284 | } |
285 | if (newbrd) { |
286 | parse_address(dev, htype, halen, newbrd, &ifr1); |
287 | set_address(&ifr1, 1); |
288 | } |
289 | } |
290 | |
291 | if (newname && strcmp(dev, newname)) { |
292 | do_changename(dev, newname); |
293 | dev = newname; |
294 | } |
295 | if (qlen != -1) { |
296 | set_qlen(dev, qlen); |
297 | } |
298 | if (mtu != -1) { |
299 | set_mtu(dev, mtu); |
300 | } |
301 | if (mask) |
302 | do_chflags(dev, flags, mask); |
303 | return 0; |
304 | } |
305 | |
306 | static int ipaddr_list_link(char **argv) |
307 | { |
308 | preferred_family = AF_PACKET; |
309 | return ipaddr_list_or_flush(argv, 0); |
310 | } |
311 | |
312 | static void vlan_parse_opt(char **argv, struct nlmsghdr *n, unsigned int size) |
313 | { |
314 | static const char keywords[] ALIGN1 = |
315 | "id\0" |
316 | "protocol\0" |
317 | "reorder_hdr\0" |
318 | "gvrp\0" |
319 | "mvrp\0" |
320 | "loose_binding\0" |
321 | ; |
322 | static const char protocols[] ALIGN1 = |
323 | "802.1q\0" |
324 | "802.1ad\0" |
325 | ; |
326 | static const char str_on_off[] ALIGN1 = |
327 | "on\0" |
328 | "off\0" |
329 | ; |
330 | enum { |
331 | ARG_id = 0, |
332 | ARG_reorder_hdr, |
333 | ARG_gvrp, |
334 | ARG_mvrp, |
335 | ARG_loose_binding, |
336 | ARG_protocol, |
337 | }; |
338 | enum { |
339 | PROTO_8021Q = 0, |
340 | PROTO_8021AD, |
341 | }; |
342 | enum { |
343 | PARM_on = 0, |
344 | PARM_off |
345 | }; |
346 | int arg; |
347 | uint16_t id, proto; |
348 | struct ifla_vlan_flags flags = { 0, 0 }; |
349 | |
350 | while (*argv) { |
351 | arg = index_in_substrings(keywords, *argv); |
352 | if (arg < 0) |
353 | invarg_1_to_2(*argv, "type vlan"); |
354 | |
355 | NEXT_ARG(); |
356 | if (arg == ARG_id) { |
357 | id = get_u16(*argv, "id"); |
358 | addattr_l(n, size, IFLA_VLAN_ID, &id, sizeof(id)); |
359 | } else if (arg == ARG_protocol) { |
360 | arg = index_in_substrings(protocols, *argv); |
361 | if (arg == PROTO_8021Q) |
362 | proto = ETH_P_8021Q; |
363 | else if (arg == PROTO_8021AD) |
364 | proto = ETH_P_8021AD; |
365 | else |
366 | bb_error_msg_and_die("unknown VLAN encapsulation protocol '%s'", |
367 | *argv); |
368 | addattr_l(n, size, IFLA_VLAN_PROTOCOL, &proto, sizeof(proto)); |
369 | } else { |
370 | int param = index_in_strings(str_on_off, *argv); |
371 | if (param < 0) |
372 | die_must_be_on_off(nth_string(keywords, arg)); |
373 | |
374 | if (arg == ARG_reorder_hdr) { |
375 | flags.mask |= VLAN_FLAG_REORDER_HDR; |
376 | flags.flags &= ~VLAN_FLAG_REORDER_HDR; |
377 | if (param == PARM_on) |
378 | flags.flags |= VLAN_FLAG_REORDER_HDR; |
379 | } else if (arg == ARG_gvrp) { |
380 | flags.mask |= VLAN_FLAG_GVRP; |
381 | flags.flags &= ~VLAN_FLAG_GVRP; |
382 | if (param == PARM_on) |
383 | flags.flags |= VLAN_FLAG_GVRP; |
384 | } else if (arg == ARG_mvrp) { |
385 | flags.mask |= VLAN_FLAG_MVRP; |
386 | flags.flags &= ~VLAN_FLAG_MVRP; |
387 | if (param == PARM_on) |
388 | flags.flags |= VLAN_FLAG_MVRP; |
389 | } else { /*if (arg == ARG_loose_binding) */ |
390 | flags.mask |= VLAN_FLAG_LOOSE_BINDING; |
391 | flags.flags &= ~VLAN_FLAG_LOOSE_BINDING; |
392 | if (param == PARM_on) |
393 | flags.flags |= VLAN_FLAG_LOOSE_BINDING; |
394 | } |
395 | } |
396 | argv++; |
397 | } |
398 | |
399 | if (flags.mask) |
400 | addattr_l(n, size, IFLA_VLAN_FLAGS, &flags, sizeof(flags)); |
401 | } |
402 | |
403 | #ifndef NLMSG_TAIL |
404 | #define NLMSG_TAIL(nmsg) \ |
405 | ((struct rtattr *) ((void *) ((nmsg) + NLMSG_ALIGN((nmsg)->nlmsg_len)))) |
406 | #endif |
407 | /* Return value becomes exitcode. It's okay to not return at all */ |
408 | static int do_add_or_delete(char **argv, const unsigned rtm) |
409 | { |
410 | static const char keywords[] ALIGN1 = |
411 | "link\0""name\0""type\0""dev\0""address\0"; |
412 | enum { |
413 | ARG_link, |
414 | ARG_name, |
415 | ARG_type, |
416 | ARG_dev, |
417 | ARG_address, |
418 | }; |
419 | struct rtnl_handle rth; |
420 | struct { |
421 | struct nlmsghdr n; |
422 | struct ifinfomsg i; |
423 | char buf[1024]; |
424 | } req; |
425 | smalluint arg; |
426 | char *name_str = NULL; |
427 | char *link_str = NULL; |
428 | char *type_str = NULL; |
429 | char *dev_str = NULL; |
430 | char *address_str = NULL; |
431 | |
432 | memset(&req, 0, sizeof(req)); |
433 | |
434 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); |
435 | req.n.nlmsg_flags = NLM_F_REQUEST; |
436 | req.n.nlmsg_type = rtm; |
437 | req.i.ifi_family = preferred_family; |
438 | if (rtm == RTM_NEWLINK) |
439 | req.n.nlmsg_flags |= NLM_F_CREATE|NLM_F_EXCL; |
440 | |
441 | while (*argv) { |
442 | arg = index_in_substrings(keywords, *argv); |
443 | if (arg == ARG_type) { |
444 | NEXT_ARG(); |
445 | type_str = *argv++; |
446 | dbg("type_str:'%s'", type_str); |
447 | break; |
448 | } |
449 | if (arg == ARG_link) { |
450 | NEXT_ARG(); |
451 | link_str = *argv; |
452 | dbg("link_str:'%s'", link_str); |
453 | } else if (arg == ARG_name) { |
454 | NEXT_ARG(); |
455 | name_str = *argv; |
456 | dbg("name_str:'%s'", name_str); |
457 | } else if (arg == ARG_address) { |
458 | NEXT_ARG(); |
459 | address_str = *argv; |
460 | dbg("address_str:'%s'", name_str); |
461 | } else { |
462 | if (arg == ARG_dev) { |
463 | if (dev_str) |
464 | duparg(*argv, "dev"); |
465 | NEXT_ARG(); |
466 | } |
467 | dev_str = *argv; |
468 | dbg("dev_str:'%s'", dev_str); |
469 | } |
470 | argv++; |
471 | } |
472 | xrtnl_open(&rth); |
473 | ll_init_map(&rth); |
474 | if (type_str) { |
475 | struct rtattr *linkinfo = NLMSG_TAIL(&req.n); |
476 | |
477 | addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); |
478 | addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type_str, |
479 | strlen(type_str)); |
480 | |
481 | if (*argv) { |
482 | struct rtattr *data = NLMSG_TAIL(&req.n); |
483 | addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); |
484 | |
485 | if (strcmp(type_str, "vlan") == 0) |
486 | vlan_parse_opt(argv, &req.n, sizeof(req)); |
487 | |
488 | data->rta_len = (NLMSG_TAIL(&req.n) - (data)); |
489 | } |
490 | |
491 | linkinfo->rta_len = (NLMSG_TAIL(&req.n) - (linkinfo)); |
492 | } |
493 | if (rtm != RTM_NEWLINK) { |
494 | if (!dev_str) |
495 | return 1; /* Need a device to delete */ |
496 | req.i.ifi_index = xll_name_to_index(dev_str); |
497 | } else { |
498 | if (!name_str) |
499 | name_str = dev_str; |
500 | if (link_str) { |
501 | int idx = xll_name_to_index(link_str); |
502 | addattr_l(&req.n, sizeof(req), IFLA_LINK, &idx, 4); |
503 | } |
504 | if (address_str) { |
505 | unsigned char abuf[32]; |
506 | int len = ll_addr_a2n(abuf, sizeof(abuf), address_str); |
507 | dbg("address len:%d", len); |
508 | if (len < 0) |
509 | return -1; |
510 | addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, abuf, len); |
511 | } |
512 | } |
513 | if (name_str) { |
514 | const size_t name_len = strlen(name_str) + 1; |
515 | if (name_len < 2 || name_len > IFNAMSIZ) |
516 | invarg_1_to_2(name_str, "name"); |
517 | addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name_str, name_len); |
518 | } |
519 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) |
520 | return 2; |
521 | return 0; |
522 | } |
523 | |
524 | /* Other keywords recognized by iproute2-3.12.0: */ |
525 | #if 0 |
526 | } else if (matches(*argv, "broadcast") == 0 || |
527 | strcmp(*argv, "brd") == 0) { |
528 | NEXT_ARG(); |
529 | len = ll_addr_a2n(abuf, sizeof(abuf), *argv); |
530 | if (len < 0) |
531 | return -1; |
532 | addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len); |
533 | } else if (matches(*argv, "txqueuelen") == 0 || |
534 | strcmp(*argv, "qlen") == 0 || |
535 | matches(*argv, "txqlen") == 0) { |
536 | NEXT_ARG(); |
537 | if (qlen != -1) |
538 | duparg("txqueuelen", *argv); |
539 | if (get_integer(&qlen, *argv, 0)) |
540 | invarg_1_to_2(*argv, "txqueuelen"); |
541 | addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4); |
542 | } else if (strcmp(*argv, "mtu") == 0) { |
543 | NEXT_ARG(); |
544 | if (mtu != -1) |
545 | duparg("mtu", *argv); |
546 | if (get_integer(&mtu, *argv, 0)) |
547 | invarg_1_to_2(*argv, "mtu"); |
548 | addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4); |
549 | } else if (strcmp(*argv, "netns") == 0) { |
550 | NEXT_ARG(); |
551 | if (netns != -1) |
552 | duparg("netns", *argv); |
553 | if ((netns = get_netns_fd(*argv)) >= 0) |
554 | addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4); |
555 | else if (get_integer(&netns, *argv, 0) == 0) |
556 | addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4); |
557 | else |
558 | invarg_1_to_2(*argv, "netns"); |
559 | } else if (strcmp(*argv, "multicast") == 0) { |
560 | NEXT_ARG(); |
561 | req->i.ifi_change |= IFF_MULTICAST; |
562 | if (strcmp(*argv, "on") == 0) { |
563 | req->i.ifi_flags |= IFF_MULTICAST; |
564 | } else if (strcmp(*argv, "off") == 0) { |
565 | req->i.ifi_flags &= ~IFF_MULTICAST; |
566 | } else |
567 | return on_off("multicast", *argv); |
568 | } else if (strcmp(*argv, "allmulticast") == 0) { |
569 | NEXT_ARG(); |
570 | req->i.ifi_change |= IFF_ALLMULTI; |
571 | if (strcmp(*argv, "on") == 0) { |
572 | req->i.ifi_flags |= IFF_ALLMULTI; |
573 | } else if (strcmp(*argv, "off") == 0) { |
574 | req->i.ifi_flags &= ~IFF_ALLMULTI; |
575 | } else |
576 | return on_off("allmulticast", *argv); |
577 | } else if (strcmp(*argv, "promisc") == 0) { |
578 | NEXT_ARG(); |
579 | req->i.ifi_change |= IFF_PROMISC; |
580 | if (strcmp(*argv, "on") == 0) { |
581 | req->i.ifi_flags |= IFF_PROMISC; |
582 | } else if (strcmp(*argv, "off") == 0) { |
583 | req->i.ifi_flags &= ~IFF_PROMISC; |
584 | } else |
585 | return on_off("promisc", *argv); |
586 | } else if (strcmp(*argv, "trailers") == 0) { |
587 | NEXT_ARG(); |
588 | req->i.ifi_change |= IFF_NOTRAILERS; |
589 | if (strcmp(*argv, "off") == 0) { |
590 | req->i.ifi_flags |= IFF_NOTRAILERS; |
591 | } else if (strcmp(*argv, "on") == 0) { |
592 | req->i.ifi_flags &= ~IFF_NOTRAILERS; |
593 | } else |
594 | return on_off("trailers", *argv); |
595 | } else if (strcmp(*argv, "arp") == 0) { |
596 | NEXT_ARG(); |
597 | req->i.ifi_change |= IFF_NOARP; |
598 | if (strcmp(*argv, "on") == 0) { |
599 | req->i.ifi_flags &= ~IFF_NOARP; |
600 | } else if (strcmp(*argv, "off") == 0) { |
601 | req->i.ifi_flags |= IFF_NOARP; |
602 | } else |
603 | return on_off("noarp", *argv); |
604 | } else if (strcmp(*argv, "vf") == 0) { |
605 | struct rtattr *vflist; |
606 | NEXT_ARG(); |
607 | if (get_integer(&vf, *argv, 0)) { |
608 | invarg_1_to_2(*argv, "vf"); |
609 | } |
610 | vflist = addattr_nest(&req->n, sizeof(*req), |
611 | IFLA_VFINFO_LIST); |
612 | len = iplink_parse_vf(vf, &argc, &argv, req); |
613 | if (len < 0) |
614 | return -1; |
615 | addattr_nest_end(&req->n, vflist); |
616 | } else if (matches(*argv, "master") == 0) { |
617 | int ifindex; |
618 | NEXT_ARG(); |
619 | ifindex = ll_name_to_index(*argv); |
620 | if (!ifindex) |
621 | invarg_1_to_2(*argv, "master"); |
622 | addattr_l(&req->n, sizeof(*req), IFLA_MASTER, |
623 | &ifindex, 4); |
624 | } else if (matches(*argv, "nomaster") == 0) { |
625 | int ifindex = 0; |
626 | addattr_l(&req->n, sizeof(*req), IFLA_MASTER, |
627 | &ifindex, 4); |
628 | } else if (matches(*argv, "dynamic") == 0) { |
629 | NEXT_ARG(); |
630 | req->i.ifi_change |= IFF_DYNAMIC; |
631 | if (strcmp(*argv, "on") == 0) { |
632 | req->i.ifi_flags |= IFF_DYNAMIC; |
633 | } else if (strcmp(*argv, "off") == 0) { |
634 | req->i.ifi_flags &= ~IFF_DYNAMIC; |
635 | } else |
636 | return on_off("dynamic", *argv); |
637 | } else if (matches(*argv, "alias") == 0) { |
638 | NEXT_ARG(); |
639 | addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS, |
640 | *argv, strlen(*argv)); |
641 | argc--; argv++; |
642 | break; |
643 | } else if (strcmp(*argv, "group") == 0) { |
644 | NEXT_ARG(); |
645 | if (*group != -1) |
646 | duparg("group", *argv); |
647 | if (rtnl_group_a2n(group, *argv)) |
648 | invarg_1_to_2(*argv, "group"); |
649 | } else if (strcmp(*argv, "mode") == 0) { |
650 | int mode; |
651 | NEXT_ARG(); |
652 | mode = get_link_mode(*argv); |
653 | if (mode < 0) |
654 | invarg_1_to_2(*argv, "mode"); |
655 | addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode); |
656 | } else if (strcmp(*argv, "state") == 0) { |
657 | int state; |
658 | NEXT_ARG(); |
659 | state = get_operstate(*argv); |
660 | if (state < 0) |
661 | invarg_1_to_2(*argv, "state"); |
662 | addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state); |
663 | } else if (matches(*argv, "numtxqueues") == 0) { |
664 | NEXT_ARG(); |
665 | if (numtxqueues != -1) |
666 | duparg("numtxqueues", *argv); |
667 | if (get_integer(&numtxqueues, *argv, 0)) |
668 | invarg_1_to_2(*argv, "numtxqueues"); |
669 | addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES, |
670 | &numtxqueues, 4); |
671 | } else if (matches(*argv, "numrxqueues") == 0) { |
672 | NEXT_ARG(); |
673 | if (numrxqueues != -1) |
674 | duparg("numrxqueues", *argv); |
675 | if (get_integer(&numrxqueues, *argv, 0)) |
676 | invarg_1_to_2(*argv, "numrxqueues"); |
677 | addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES, |
678 | &numrxqueues, 4); |
679 | } |
680 | #endif |
681 | |
682 | /* Return value becomes exitcode. It's okay to not return at all */ |
683 | int FAST_FUNC do_iplink(char **argv) |
684 | { |
685 | static const char keywords[] ALIGN1 = |
686 | "add\0""delete\0""set\0""show\0""lst\0""list\0"; |
687 | if (*argv) { |
688 | int key = index_in_substrings(keywords, *argv); |
689 | if (key < 0) /* invalid argument */ |
690 | invarg_1_to_2(*argv, applet_name); |
691 | argv++; |
692 | if (key <= 1) /* add/delete */ |
693 | return do_add_or_delete(argv, key ? RTM_DELLINK : RTM_NEWLINK); |
694 | if (key == 2) /* set */ |
695 | return do_set(argv); |
696 | } |
697 | /* show, lst, list */ |
698 | return ipaddr_list_link(argv); |
699 | } |
700 |