]>
Commit | Line | Data |
---|---|---|
aba5acdf SH |
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> | |
aba5acdf | 16 | #include <fcntl.h> |
1d934839 | 17 | #include <dlfcn.h> |
aba5acdf SH |
18 | #include <errno.h> |
19 | #include <sys/socket.h> | |
20 | #include <linux/if.h> | |
21 | #include <linux/if_packet.h> | |
22 | #include <linux/if_ether.h> | |
23 | #include <linux/sockios.h> | |
24 | #include <netinet/in.h> | |
25 | #include <arpa/inet.h> | |
26 | #include <string.h> | |
508f3c23 LB |
27 | #ifdef HAVE_LIBBSD |
28 | #include <bsd/string.h> | |
29 | #endif | |
aba5acdf | 30 | #include <sys/ioctl.h> |
fbea6115 | 31 | #include <stdbool.h> |
837552b4 | 32 | #include <linux/mpls.h> |
aba5acdf SH |
33 | |
34 | #include "rt_names.h" | |
35 | #include "utils.h" | |
36 | #include "ip_common.h" | |
c3087c10 | 37 | #include "namespace.h" |
aba5acdf | 38 | |
1d934839 | 39 | #define IPLINK_IOCTL_COMPAT 1 |
5e3bb534 | 40 | #ifndef LIBDIR |
5c434a9e | 41 | #define LIBDIR "/usr/lib" |
b514b358 | 42 | #endif |
aba5acdf | 43 | |
08f9d166 SH |
44 | #ifndef GSO_MAX_SIZE |
45 | #define GSO_MAX_SIZE 65536 | |
46 | #endif | |
47 | #ifndef GSO_MAX_SEGS | |
48 | #define GSO_MAX_SEGS 65535 | |
49 | #endif | |
50 | ||
51 | ||
aba5acdf | 52 | static void usage(void) __attribute__((noreturn)); |
750a405a | 53 | static int iplink_have_newlink(void); |
aba5acdf SH |
54 | |
55 | void iplink_usage(void) | |
56 | { | |
750a405a | 57 | if (iplink_have_newlink()) { |
6773bcc2 SH |
58 | fprintf(stderr, |
59 | "Usage: ip link add [link DEV] [ name ] NAME\n" | |
60 | " [ txqueuelen PACKETS ]\n" | |
61 | " [ address LLADDR ]\n" | |
62 | " [ broadcast LLADDR ]\n" | |
63 | " [ mtu MTU ] [index IDX ]\n" | |
64 | " [ numtxqueues QUEUE_COUNT ]\n" | |
65 | " [ numrxqueues QUEUE_COUNT ]\n" | |
66 | " type TYPE [ ARGS ]\n" | |
c7272ca7 | 67 | "\n" |
6773bcc2 SH |
68 | " ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n" |
69 | "\n" | |
70 | " ip link set { DEVICE | dev DEVICE | group DEVGROUP }\n" | |
71 | " [ { up | down } ]\n" | |
72 | " [ type TYPE ARGS ]\n"); | |
750a405a | 73 | } else |
bb6ab47b SH |
74 | fprintf(stderr, |
75 | "Usage: ip link set DEVICE [ { up | down } ]\n"); | |
750a405a | 76 | |
6773bcc2 SH |
77 | fprintf(stderr, |
78 | " [ arp { on | off } ]\n" | |
79 | " [ dynamic { on | off } ]\n" | |
80 | " [ multicast { on | off } ]\n" | |
81 | " [ allmulticast { on | off } ]\n" | |
82 | " [ promisc { on | off } ]\n" | |
83 | " [ trailers { on | off } ]\n" | |
432b92a7 | 84 | " [ carrier { on | off } ]\n" |
6773bcc2 SH |
85 | " [ txqueuelen PACKETS ]\n" |
86 | " [ name NEWNAME ]\n" | |
87 | " [ address LLADDR ]\n" | |
88 | " [ broadcast LLADDR ]\n" | |
89 | " [ mtu MTU ]\n" | |
90 | " [ netns { PID | NAME } ]\n" | |
974ef93b | 91 | " [ link-netns NAME | link-netnsid ID ]\n" |
6773bcc2 SH |
92 | " [ alias NAME ]\n" |
93 | " [ vf NUM [ mac LLADDR ]\n" | |
94 | " [ vlan VLANID [ qos VLAN-QOS ] [ proto VLAN-PROTO ] ]\n" | |
6773bcc2 SH |
95 | " [ rate TXRATE ]\n" |
96 | " [ max_tx_rate TXRATE ]\n" | |
97 | " [ min_tx_rate TXRATE ]\n" | |
6773bcc2 SH |
98 | " [ spoofchk { on | off} ]\n" |
99 | " [ query_rss { on | off} ]\n" | |
100 | " [ state { auto | enable | disable} ] ]\n" | |
101 | " [ trust { on | off} ] ]\n" | |
5a3ec4ba EC |
102 | " [ node_guid { eui64 } ]\n" |
103 | " [ port_guid { eui64 } ]\n" | |
c7272ca7 DB |
104 | " [ xdp { off |\n" |
105 | " object FILE [ section NAME ] [ verbose ] |\n" | |
106 | " pinned FILE } ]\n" | |
6773bcc2 SH |
107 | " [ master DEVICE ][ vrf NAME ]\n" |
108 | " [ nomaster ]\n" | |
109 | " [ addrgenmode { eui64 | none | stable_secret | random } ]\n" | |
c2db423f SH |
110 | " [ protodown { on | off } ]\n" |
111 | " [ gso_max_size BYTES ] | [ gso_max_segs PACKETS ]\n" | |
c7272ca7 | 112 | "\n" |
6773bcc2 | 113 | " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n"); |
750a405a | 114 | |
94f1a22a | 115 | fprintf(stderr, "\n ip link xstats type TYPE [ ARGS ]\n"); |
837552b4 | 116 | fprintf(stderr, "\n ip link afstats [ dev DEVICE ]\n"); |
94f1a22a | 117 | |
750a405a | 118 | if (iplink_have_newlink()) { |
6773bcc2 | 119 | fprintf(stderr, |
c7272ca7 DB |
120 | "\n" |
121 | " ip link help [ TYPE ]\n" | |
122 | "\n" | |
7827b376 | 123 | "TYPE := { vlan | veth | vcan | vxcan | dummy | ifb | macvlan | macvtap |\n" |
6bd1ea28 | 124 | " bridge | bond | team | ipoib | ip6tnl | ipip | sit | vxlan |\n" |
8595cc40 | 125 | " gre | gretap | erspan | ip6gre | ip6gretap | ip6erspan |\n" |
88272775 HL |
126 | " vti | nlmon | team_slave | bond_slave | bridge_slave |\n" |
127 | " ipvlan | ipvtap | geneve | vrf | macsec | netdevsim | rmnet }\n"); | |
750a405a | 128 | } |
aba5acdf SH |
129 | exit(-1); |
130 | } | |
131 | ||
132 | static void usage(void) | |
133 | { | |
134 | iplink_usage(); | |
135 | } | |
136 | ||
14645ec2 | 137 | static int on_off(const char *msg, const char *realval) |
aba5acdf | 138 | { |
6c5ffb9a SH |
139 | fprintf(stderr, |
140 | "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", | |
141 | msg, realval); | |
aba5acdf SH |
142 | return -1; |
143 | } | |
144 | ||
1d934839 PM |
145 | static void *BODY; /* cached dlopen(NULL) handle */ |
146 | static struct link_util *linkutil_list; | |
147 | ||
22a84711 | 148 | struct link_util *get_link_kind(const char *id) |
1d934839 PM |
149 | { |
150 | void *dlh; | |
151 | char buf[256]; | |
152 | struct link_util *l; | |
153 | ||
154 | for (l = linkutil_list; l; l = l->next) | |
22a84711 | 155 | if (strcmp(l->id, id) == 0) |
1d934839 PM |
156 | return l; |
157 | ||
5e3bb534 | 158 | snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id); |
1d934839 PM |
159 | dlh = dlopen(buf, RTLD_LAZY); |
160 | if (dlh == NULL) { | |
161 | /* look in current binary, only open once */ | |
162 | dlh = BODY; | |
163 | if (dlh == NULL) { | |
164 | dlh = BODY = dlopen(NULL, RTLD_LAZY); | |
165 | if (dlh == NULL) | |
166 | return NULL; | |
167 | } | |
168 | } | |
169 | ||
22a84711 | 170 | snprintf(buf, sizeof(buf), "%s_link_util", id); |
1d934839 PM |
171 | l = dlsym(dlh, buf); |
172 | if (l == NULL) | |
173 | return NULL; | |
174 | ||
175 | l->next = linkutil_list; | |
176 | linkutil_list = l; | |
177 | return l; | |
178 | } | |
179 | ||
d1f28cf1 | 180 | static int get_link_mode(const char *mode) |
82499282 | 181 | { |
4ccfb44d | 182 | if (strcasecmp(mode, "default") == 0) |
82499282 | 183 | return IF_LINK_MODE_DEFAULT; |
4ccfb44d | 184 | if (strcasecmp(mode, "dormant") == 0) |
82499282 SH |
185 | return IF_LINK_MODE_DORMANT; |
186 | return -1; | |
187 | } | |
188 | ||
ff7c2084 JP |
189 | static int get_addr_gen_mode(const char *mode) |
190 | { | |
191 | if (strcasecmp(mode, "eui64") == 0) | |
192 | return IN6_ADDR_GEN_MODE_EUI64; | |
193 | if (strcasecmp(mode, "none") == 0) | |
194 | return IN6_ADDR_GEN_MODE_NONE; | |
8e098dd8 BM |
195 | if (strcasecmp(mode, "stable_secret") == 0) |
196 | return IN6_ADDR_GEN_MODE_STABLE_PRIVACY; | |
8e12bc0a BM |
197 | if (strcasecmp(mode, "random") == 0) |
198 | return IN6_ADDR_GEN_MODE_RANDOM; | |
ff7c2084 JP |
199 | return -1; |
200 | } | |
201 | ||
1d934839 PM |
202 | #if IPLINK_IOCTL_COMPAT |
203 | static int have_rtnl_newlink = -1; | |
204 | ||
cd554f2c | 205 | static int accept_msg(struct rtnl_ctrl_data *ctrl, |
1d934839 PM |
206 | struct nlmsghdr *n, void *arg) |
207 | { | |
208 | struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n); | |
209 | ||
66e529f5 PM |
210 | if (n->nlmsg_type == NLMSG_ERROR && |
211 | (err->error == -EOPNOTSUPP || err->error == -EINVAL)) | |
1d934839 PM |
212 | have_rtnl_newlink = 0; |
213 | else | |
214 | have_rtnl_newlink = 1; | |
215 | return -1; | |
216 | } | |
217 | ||
218 | static int iplink_have_newlink(void) | |
219 | { | |
220 | struct { | |
221 | struct nlmsghdr n; | |
222 | struct ifinfomsg i; | |
223 | char buf[1024]; | |
d17b136f PS |
224 | } req = { |
225 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), | |
226 | .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, | |
227 | .n.nlmsg_type = RTM_NEWLINK, | |
228 | .i.ifi_family = AF_UNSPEC, | |
229 | }; | |
1d934839 PM |
230 | |
231 | if (have_rtnl_newlink < 0) { | |
d2468da0 SH |
232 | if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) { |
233 | perror("request send failed"); | |
234 | exit(1); | |
235 | } | |
1d934839 PM |
236 | rtnl_listen(&rth, accept_msg, NULL); |
237 | } | |
238 | return have_rtnl_newlink; | |
239 | } | |
240 | #else /* IPLINK_IOCTL_COMPAT */ | |
241 | static int iplink_have_newlink(void) | |
242 | { | |
243 | return 1; | |
244 | } | |
245 | #endif /* ! IPLINK_IOCTL_COMPAT */ | |
246 | ||
65083b5f | 247 | static int nl_get_ll_addr_len(const char *ifname) |
8fe58d58 PS |
248 | { |
249 | int len; | |
65083b5f | 250 | int dev_index = ll_name_to_index(ifname); |
8fe58d58 PS |
251 | struct iplink_req req = { |
252 | .n = { | |
253 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), | |
254 | .nlmsg_type = RTM_GETLINK, | |
255 | .nlmsg_flags = NLM_F_REQUEST | |
256 | }, | |
257 | .i = { | |
258 | .ifi_family = preferred_family, | |
259 | .ifi_index = dev_index, | |
260 | } | |
261 | }; | |
86bf43c7 | 262 | struct nlmsghdr *answer; |
8fe58d58 PS |
263 | struct rtattr *tb[IFLA_MAX+1]; |
264 | ||
65083b5f SH |
265 | if (dev_index == 0) |
266 | return -1; | |
267 | ||
86bf43c7 | 268 | if (rtnl_talk(&rth, &req.n, &answer) < 0) |
8fe58d58 PS |
269 | return -1; |
270 | ||
86bf43c7 HL |
271 | len = answer->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg)); |
272 | if (len < 0) { | |
273 | free(answer); | |
8fe58d58 | 274 | return -1; |
86bf43c7 | 275 | } |
8fe58d58 | 276 | |
86bf43c7 HL |
277 | parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(NLMSG_DATA(answer)), |
278 | len, NLA_F_NESTED); | |
279 | if (!tb[IFLA_ADDRESS]) { | |
280 | free(answer); | |
8fe58d58 | 281 | return -1; |
86bf43c7 | 282 | } |
8fe58d58 | 283 | |
06867c37 | 284 | len = RTA_PAYLOAD(tb[IFLA_ADDRESS]); |
86bf43c7 | 285 | free(answer); |
06867c37 | 286 | return len; |
8fe58d58 PS |
287 | } |
288 | ||
56e9f0ab MS |
289 | static void iplink_parse_vf_vlan_info(int vf, int *argcp, char ***argvp, |
290 | struct ifla_vf_vlan_info *ivvip) | |
291 | { | |
292 | int argc = *argcp; | |
293 | char **argv = *argvp; | |
ae8e1cb8 | 294 | unsigned int vci; |
56e9f0ab MS |
295 | |
296 | NEXT_ARG(); | |
ae8e1cb8 | 297 | if (get_unsigned(&vci, *argv, 0) || vci > 4095) |
56e9f0ab MS |
298 | invarg("Invalid \"vlan\" value\n", *argv); |
299 | ||
ae8e1cb8 | 300 | ivvip->vlan = vci; |
56e9f0ab MS |
301 | ivvip->vf = vf; |
302 | ivvip->qos = 0; | |
303 | ivvip->vlan_proto = htons(ETH_P_8021Q); | |
304 | if (NEXT_ARG_OK()) { | |
305 | NEXT_ARG(); | |
306 | if (matches(*argv, "qos") == 0) { | |
307 | NEXT_ARG(); | |
308 | if (get_unsigned(&ivvip->qos, *argv, 0)) | |
309 | invarg("Invalid \"qos\" value\n", *argv); | |
310 | } else { | |
311 | /* rewind arg */ | |
312 | PREV_ARG(); | |
313 | } | |
314 | } | |
315 | if (NEXT_ARG_OK()) { | |
316 | NEXT_ARG(); | |
317 | if (matches(*argv, "proto") == 0) { | |
318 | NEXT_ARG(); | |
319 | if (ll_proto_a2n(&ivvip->vlan_proto, *argv)) | |
320 | invarg("protocol is invalid\n", *argv); | |
321 | if (ivvip->vlan_proto != htons(ETH_P_8021AD) && | |
322 | ivvip->vlan_proto != htons(ETH_P_8021Q)) { | |
323 | SPRINT_BUF(b1); | |
324 | SPRINT_BUF(b2); | |
325 | char msg[64 + sizeof(b1) + sizeof(b2)]; | |
326 | ||
bb6ab47b SH |
327 | sprintf(msg, |
328 | "Invalid \"vlan protocol\" value - supported %s, %s\n", | |
56e9f0ab MS |
329 | ll_proto_n2a(htons(ETH_P_8021Q), |
330 | b1, sizeof(b1)), | |
331 | ll_proto_n2a(htons(ETH_P_8021AD), | |
332 | b2, sizeof(b2))); | |
333 | invarg(msg, *argv); | |
334 | } | |
335 | } else { | |
336 | /* rewind arg */ | |
337 | PREV_ARG(); | |
338 | } | |
339 | } | |
340 | ||
341 | *argcp = argc; | |
342 | *argvp = argv; | |
343 | } | |
344 | ||
d1f28cf1 | 345 | static int iplink_parse_vf(int vf, int *argcp, char ***argvp, |
65083b5f | 346 | struct iplink_req *req, const char *dev) |
3fd86630 | 347 | { |
f89a2a05 SC |
348 | char new_rate_api = 0, count = 0, override_legacy_rate = 0; |
349 | struct ifla_vf_rate tivt; | |
3fd86630 CW |
350 | int len, argc = *argcp; |
351 | char **argv = *argvp; | |
352 | struct rtattr *vfinfo; | |
1598b9ef | 353 | |
f89a2a05 SC |
354 | tivt.min_tx_rate = -1; |
355 | tivt.max_tx_rate = -1; | |
356 | ||
1598b9ef | 357 | vfinfo = addattr_nest(&req->n, sizeof(*req), IFLA_VF_INFO); |
3fd86630 | 358 | |
f89a2a05 SC |
359 | while (NEXT_ARG_OK()) { |
360 | NEXT_ARG(); | |
361 | count++; | |
362 | if (!matches(*argv, "max_tx_rate")) { | |
363 | /* new API in use */ | |
364 | new_rate_api = 1; | |
365 | /* override legacy rate */ | |
366 | override_legacy_rate = 1; | |
367 | } else if (!matches(*argv, "min_tx_rate")) { | |
368 | /* new API in use */ | |
369 | new_rate_api = 1; | |
370 | } | |
371 | } | |
372 | ||
373 | while (count--) { | |
374 | /* rewind arg */ | |
375 | PREV_ARG(); | |
376 | } | |
377 | ||
3fd86630 CW |
378 | while (NEXT_ARG_OK()) { |
379 | NEXT_ARG(); | |
1598b9ef | 380 | if (matches(*argv, "mac") == 0) { |
a89193a7 | 381 | struct ifla_vf_mac ivm = { 0 }; |
65083b5f | 382 | int halen = nl_get_ll_addr_len(dev); |
6c5ffb9a | 383 | |
3fd86630 | 384 | NEXT_ARG(); |
1598b9ef SH |
385 | ivm.vf = vf; |
386 | len = ll_addr_a2n((char *)ivm.mac, 32, *argv); | |
387 | if (len < 0) | |
388 | return -1; | |
8fe58d58 PS |
389 | if (halen > 0 && len != halen) { |
390 | fprintf(stderr, | |
391 | "Invalid address length %d - must be %d bytes\n", | |
392 | len, halen); | |
393 | return -1; | |
394 | } | |
ef0a738c SH |
395 | addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, |
396 | &ivm, sizeof(ivm)); | |
3fd86630 | 397 | } else if (matches(*argv, "vlan") == 0) { |
56e9f0ab | 398 | struct ifla_vf_vlan_info ivvi; |
6c5ffb9a | 399 | |
56e9f0ab MS |
400 | iplink_parse_vf_vlan_info(vf, &argc, &argv, &ivvi); |
401 | /* support the old interface in case of older kernel*/ | |
402 | if (ivvi.vlan_proto == htons(ETH_P_8021Q)) { | |
403 | struct ifla_vf_vlan ivv; | |
404 | ||
405 | ivv.vf = ivvi.vf; | |
406 | ivv.vlan = ivvi.vlan; | |
407 | ivv.qos = ivvi.qos; | |
408 | addattr_l(&req->n, sizeof(*req), | |
409 | IFLA_VF_VLAN, &ivv, sizeof(ivv)); | |
410 | } else { | |
411 | struct rtattr *vfvlanlist; | |
412 | ||
413 | vfvlanlist = addattr_nest(&req->n, sizeof(*req), | |
414 | IFLA_VF_VLAN_LIST); | |
415 | addattr_l(&req->n, sizeof(*req), | |
416 | IFLA_VF_VLAN_INFO, &ivvi, | |
417 | sizeof(ivvi)); | |
418 | ||
419 | while (NEXT_ARG_OK()) { | |
3fd86630 | 420 | NEXT_ARG(); |
56e9f0ab MS |
421 | if (matches(*argv, "vlan") != 0) { |
422 | PREV_ARG(); | |
423 | break; | |
424 | } | |
425 | iplink_parse_vf_vlan_info(vf, &argc, | |
426 | &argv, &ivvi); | |
427 | addattr_l(&req->n, sizeof(*req), | |
428 | IFLA_VF_VLAN_INFO, &ivvi, | |
429 | sizeof(ivvi)); | |
3fd86630 | 430 | } |
56e9f0ab | 431 | addattr_nest_end(&req->n, vfvlanlist); |
3fd86630 | 432 | } |
3fd86630 | 433 | } else if (matches(*argv, "rate") == 0) { |
1598b9ef | 434 | struct ifla_vf_tx_rate ivt; |
6c5ffb9a | 435 | |
3fd86630 | 436 | NEXT_ARG(); |
6c5ffb9a | 437 | if (get_unsigned(&ivt.rate, *argv, 0)) |
1598b9ef | 438 | invarg("Invalid \"rate\" value\n", *argv); |
6c5ffb9a | 439 | |
1598b9ef | 440 | ivt.vf = vf; |
f89a2a05 SC |
441 | if (!new_rate_api) |
442 | addattr_l(&req->n, sizeof(*req), | |
443 | IFLA_VF_TX_RATE, &ivt, sizeof(ivt)); | |
444 | else if (!override_legacy_rate) | |
445 | tivt.max_tx_rate = ivt.rate; | |
446 | ||
447 | } else if (matches(*argv, "max_tx_rate") == 0) { | |
448 | NEXT_ARG(); | |
449 | if (get_unsigned(&tivt.max_tx_rate, *argv, 0)) | |
450 | invarg("Invalid \"max tx rate\" value\n", | |
451 | *argv); | |
452 | tivt.vf = vf; | |
453 | ||
454 | } else if (matches(*argv, "min_tx_rate") == 0) { | |
455 | NEXT_ARG(); | |
456 | if (get_unsigned(&tivt.min_tx_rate, *argv, 0)) | |
457 | invarg("Invalid \"min tx rate\" value\n", | |
458 | *argv); | |
459 | tivt.vf = vf; | |
af89576d | 460 | |
7b8179c7 GR |
461 | } else if (matches(*argv, "spoofchk") == 0) { |
462 | struct ifla_vf_spoofchk ivs; | |
6c5ffb9a | 463 | |
7b8179c7 GR |
464 | NEXT_ARG(); |
465 | if (matches(*argv, "on") == 0) | |
466 | ivs.setting = 1; | |
467 | else if (matches(*argv, "off") == 0) | |
468 | ivs.setting = 0; | |
469 | else | |
ff1e35ed | 470 | return on_off("spoofchk", *argv); |
7b8179c7 | 471 | ivs.vf = vf; |
ef0a738c SH |
472 | addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, |
473 | &ivs, sizeof(ivs)); | |
7b8179c7 | 474 | |
6c55c8c4 VZ |
475 | } else if (matches(*argv, "query_rss") == 0) { |
476 | struct ifla_vf_rss_query_en ivs; | |
6c5ffb9a | 477 | |
6c55c8c4 VZ |
478 | NEXT_ARG(); |
479 | if (matches(*argv, "on") == 0) | |
480 | ivs.setting = 1; | |
481 | else if (matches(*argv, "off") == 0) | |
482 | ivs.setting = 0; | |
483 | else | |
ff1e35ed | 484 | return on_off("query_rss", *argv); |
6c55c8c4 | 485 | ivs.vf = vf; |
ef0a738c SH |
486 | addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, |
487 | &ivs, sizeof(ivs)); | |
6c55c8c4 | 488 | |
b6d77d9e HS |
489 | } else if (matches(*argv, "trust") == 0) { |
490 | struct ifla_vf_trust ivt; | |
56f5daac | 491 | |
b6d77d9e HS |
492 | NEXT_ARG(); |
493 | if (matches(*argv, "on") == 0) | |
494 | ivt.setting = 1; | |
495 | else if (matches(*argv, "off") == 0) | |
496 | ivt.setting = 0; | |
497 | else | |
498 | invarg("Invalid \"trust\" value\n", *argv); | |
499 | ivt.vf = vf; | |
ef0a738c SH |
500 | addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST, |
501 | &ivt, sizeof(ivt)); | |
b6d77d9e | 502 | |
07fa9c15 RE |
503 | } else if (matches(*argv, "state") == 0) { |
504 | struct ifla_vf_link_state ivl; | |
6c5ffb9a | 505 | |
07fa9c15 RE |
506 | NEXT_ARG(); |
507 | if (matches(*argv, "auto") == 0) | |
508 | ivl.link_state = IFLA_VF_LINK_STATE_AUTO; | |
509 | else if (matches(*argv, "enable") == 0) | |
510 | ivl.link_state = IFLA_VF_LINK_STATE_ENABLE; | |
511 | else if (matches(*argv, "disable") == 0) | |
512 | ivl.link_state = IFLA_VF_LINK_STATE_DISABLE; | |
513 | else | |
514 | invarg("Invalid \"state\" value\n", *argv); | |
515 | ivl.vf = vf; | |
ef0a738c SH |
516 | addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, |
517 | &ivl, sizeof(ivl)); | |
d91fb3f4 EC |
518 | } else if (matches(*argv, "node_guid") == 0) { |
519 | struct ifla_vf_guid ivg; | |
520 | ||
521 | NEXT_ARG(); | |
522 | ivg.vf = vf; | |
523 | if (get_guid(&ivg.guid, *argv)) { | |
524 | invarg("Invalid GUID format\n", *argv); | |
525 | return -1; | |
526 | } | |
ef0a738c SH |
527 | addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_NODE_GUID, |
528 | &ivg, sizeof(ivg)); | |
d91fb3f4 EC |
529 | } else if (matches(*argv, "port_guid") == 0) { |
530 | struct ifla_vf_guid ivg; | |
531 | ||
532 | NEXT_ARG(); | |
533 | ivg.vf = vf; | |
534 | if (get_guid(&ivg.guid, *argv)) { | |
535 | invarg("Invalid GUID format\n", *argv); | |
536 | return -1; | |
537 | } | |
ef0a738c SH |
538 | addattr_l(&req->n, sizeof(*req), IFLA_VF_IB_PORT_GUID, |
539 | &ivg, sizeof(ivg)); | |
3fd86630 CW |
540 | } else { |
541 | /* rewind arg */ | |
542 | PREV_ARG(); | |
543 | break; | |
544 | } | |
545 | } | |
546 | ||
f89a2a05 SC |
547 | if (new_rate_api) { |
548 | int tmin, tmax; | |
9a02651a | 549 | |
f89a2a05 | 550 | if (tivt.min_tx_rate == -1 || tivt.max_tx_rate == -1) { |
65083b5f | 551 | ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev); |
f89a2a05 SC |
552 | if (tivt.min_tx_rate == -1) |
553 | tivt.min_tx_rate = tmin; | |
554 | if (tivt.max_tx_rate == -1) | |
555 | tivt.max_tx_rate = tmax; | |
556 | } | |
04be08e0 GP |
557 | |
558 | if (tivt.max_tx_rate && tivt.min_tx_rate > tivt.max_tx_rate) { | |
559 | fprintf(stderr, | |
560 | "Invalid min_tx_rate %d - must be <= max_tx_rate %d\n", | |
561 | tivt.min_tx_rate, tivt.max_tx_rate); | |
562 | return -1; | |
563 | } | |
564 | ||
f89a2a05 SC |
565 | addattr_l(&req->n, sizeof(*req), IFLA_VF_RATE, &tivt, |
566 | sizeof(tivt)); | |
567 | } | |
568 | ||
3fd86630 CW |
569 | if (argc == *argcp) |
570 | incomplete_command(); | |
571 | ||
1598b9ef | 572 | addattr_nest_end(&req->n, vfinfo); |
3fd86630 CW |
573 | |
574 | *argcp = argc; | |
575 | *argvp = argv; | |
1598b9ef | 576 | return 0; |
3fd86630 CW |
577 | } |
578 | ||
c58213f6 | 579 | int iplink_parse(int argc, char **argv, struct iplink_req *req, char **type) |
1d934839 | 580 | { |
c58213f6 SP |
581 | char *name = NULL; |
582 | char *dev = NULL; | |
583 | char *link = NULL; | |
909dfe2c PE |
584 | int ret, len; |
585 | char abuf[32]; | |
1d934839 PM |
586 | int qlen = -1; |
587 | int mtu = -1; | |
e2613dc8 | 588 | int netns = -1; |
ae7229d5 | 589 | int vf = -1; |
d992f3e6 JP |
590 | int numtxqueues = -1; |
591 | int numrxqueues = -1; | |
ccdcbf35 | 592 | int link_netnsid = -1; |
c58213f6 SP |
593 | int index = 0; |
594 | int group = -1; | |
8fe58d58 | 595 | int addr_len = 0; |
1d934839 | 596 | |
909dfe2c | 597 | ret = argc; |
1d934839 PM |
598 | |
599 | while (argc > 0) { | |
600 | if (strcmp(*argv, "up") == 0) { | |
909dfe2c PE |
601 | req->i.ifi_change |= IFF_UP; |
602 | req->i.ifi_flags |= IFF_UP; | |
1d934839 | 603 | } else if (strcmp(*argv, "down") == 0) { |
909dfe2c PE |
604 | req->i.ifi_change |= IFF_UP; |
605 | req->i.ifi_flags &= ~IFF_UP; | |
1d934839 PM |
606 | } else if (strcmp(*argv, "name") == 0) { |
607 | NEXT_ARG(); | |
c58213f6 | 608 | if (name) |
a24315ba | 609 | duparg("name", *argv); |
625df645 PS |
610 | if (check_ifname(*argv)) |
611 | invarg("\"name\" not a valid ifname", *argv); | |
c58213f6 | 612 | name = *argv; |
65083b5f | 613 | if (!dev) |
c58213f6 | 614 | dev = name; |
5e25cf77 PE |
615 | } else if (strcmp(*argv, "index") == 0) { |
616 | NEXT_ARG(); | |
c58213f6 | 617 | if (index) |
b7ea12ae | 618 | duparg("index", *argv); |
c58213f6 SP |
619 | index = atoi(*argv); |
620 | if (index <= 0) | |
3c682146 | 621 | invarg("Invalid \"index\" value", *argv); |
1d934839 PM |
622 | } else if (matches(*argv, "link") == 0) { |
623 | NEXT_ARG(); | |
c58213f6 | 624 | link = *argv; |
1d934839 PM |
625 | } else if (matches(*argv, "address") == 0) { |
626 | NEXT_ARG(); | |
8fe58d58 | 627 | addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv); |
0aae2346 | 628 | if (addr_len < 0) |
cb2eb999 | 629 | return -1; |
bb6ab47b SH |
630 | addattr_l(&req->n, sizeof(*req), |
631 | IFLA_ADDRESS, abuf, addr_len); | |
1d934839 | 632 | } else if (matches(*argv, "broadcast") == 0 || |
6c5ffb9a | 633 | strcmp(*argv, "brd") == 0) { |
1d934839 PM |
634 | NEXT_ARG(); |
635 | len = ll_addr_a2n(abuf, sizeof(abuf), *argv); | |
cb2eb999 AH |
636 | if (len < 0) |
637 | return -1; | |
bb6ab47b SH |
638 | addattr_l(&req->n, sizeof(*req), |
639 | IFLA_BROADCAST, abuf, len); | |
1d934839 | 640 | } else if (matches(*argv, "txqueuelen") == 0 || |
6c5ffb9a SH |
641 | strcmp(*argv, "qlen") == 0 || |
642 | matches(*argv, "txqlen") == 0) { | |
1d934839 PM |
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); | |
bb6ab47b SH |
648 | addattr_l(&req->n, sizeof(*req), |
649 | IFLA_TXQLEN, &qlen, 4); | |
1d934839 PM |
650 | } else if (strcmp(*argv, "mtu") == 0) { |
651 | NEXT_ARG(); | |
652 | if (mtu != -1) | |
653 | duparg("mtu", *argv); | |
654 | if (get_integer(&mtu, *argv, 0)) | |
655 | invarg("Invalid \"mtu\" value\n", *argv); | |
909dfe2c | 656 | addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4); |
a872b870 | 657 | } else if (strcmp(*argv, "xdpgeneric") == 0 || |
14683814 | 658 | strcmp(*argv, "xdpdrv") == 0 || |
1b5e8094 | 659 | strcmp(*argv, "xdpoffload") == 0 || |
a872b870 DB |
660 | strcmp(*argv, "xdp") == 0) { |
661 | bool generic = strcmp(*argv, "xdpgeneric") == 0; | |
14683814 | 662 | bool drv = strcmp(*argv, "xdpdrv") == 0; |
1b5e8094 | 663 | bool offload = strcmp(*argv, "xdpoffload") == 0; |
a872b870 | 664 | |
c7272ca7 | 665 | NEXT_ARG(); |
65083b5f | 666 | if (xdp_parse(&argc, &argv, req, dev, |
4f2eb14f | 667 | generic, drv, offload)) |
c7272ca7 | 668 | exit(-1); |
a24315ba | 669 | |
c58213f6 SP |
670 | if (offload && name == dev) |
671 | dev = NULL; | |
4b726cb1 SH |
672 | } else if (strcmp(*argv, "netns") == 0) { |
673 | NEXT_ARG(); | |
674 | if (netns != -1) | |
675 | duparg("netns", *argv); | |
6c5ffb9a SH |
676 | netns = netns_get_fd(*argv); |
677 | if (netns >= 0) | |
ef0a738c SH |
678 | addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, |
679 | &netns, 4); | |
0dc34c77 | 680 | else if (get_integer(&netns, *argv, 0) == 0) |
bb6ab47b SH |
681 | addattr_l(&req->n, sizeof(*req), |
682 | IFLA_NET_NS_PID, &netns, 4); | |
0dc34c77 | 683 | else |
4b726cb1 | 684 | invarg("Invalid \"netns\" value\n", *argv); |
1d934839 PM |
685 | } else if (strcmp(*argv, "multicast") == 0) { |
686 | NEXT_ARG(); | |
909dfe2c | 687 | req->i.ifi_change |= IFF_MULTICAST; |
6c5ffb9a SH |
688 | |
689 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 690 | req->i.ifi_flags |= IFF_MULTICAST; |
6c5ffb9a | 691 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 692 | req->i.ifi_flags &= ~IFF_MULTICAST; |
6c5ffb9a | 693 | else |
14645ec2 | 694 | return on_off("multicast", *argv); |
1d934839 PM |
695 | } else if (strcmp(*argv, "allmulticast") == 0) { |
696 | NEXT_ARG(); | |
909dfe2c | 697 | req->i.ifi_change |= IFF_ALLMULTI; |
6c5ffb9a SH |
698 | |
699 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 700 | req->i.ifi_flags |= IFF_ALLMULTI; |
6c5ffb9a | 701 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 702 | req->i.ifi_flags &= ~IFF_ALLMULTI; |
6c5ffb9a | 703 | else |
14645ec2 | 704 | return on_off("allmulticast", *argv); |
1d934839 PM |
705 | } else if (strcmp(*argv, "promisc") == 0) { |
706 | NEXT_ARG(); | |
909dfe2c | 707 | req->i.ifi_change |= IFF_PROMISC; |
6c5ffb9a SH |
708 | |
709 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 710 | req->i.ifi_flags |= IFF_PROMISC; |
6c5ffb9a | 711 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 712 | req->i.ifi_flags &= ~IFF_PROMISC; |
6c5ffb9a | 713 | else |
14645ec2 | 714 | return on_off("promisc", *argv); |
1d934839 PM |
715 | } else if (strcmp(*argv, "trailers") == 0) { |
716 | NEXT_ARG(); | |
909dfe2c | 717 | req->i.ifi_change |= IFF_NOTRAILERS; |
6c5ffb9a SH |
718 | |
719 | if (strcmp(*argv, "off") == 0) | |
909dfe2c | 720 | req->i.ifi_flags |= IFF_NOTRAILERS; |
6c5ffb9a | 721 | else if (strcmp(*argv, "on") == 0) |
909dfe2c | 722 | req->i.ifi_flags &= ~IFF_NOTRAILERS; |
6c5ffb9a | 723 | else |
14645ec2 | 724 | return on_off("trailers", *argv); |
1d934839 PM |
725 | } else if (strcmp(*argv, "arp") == 0) { |
726 | NEXT_ARG(); | |
909dfe2c | 727 | req->i.ifi_change |= IFF_NOARP; |
6c5ffb9a SH |
728 | |
729 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 730 | req->i.ifi_flags &= ~IFF_NOARP; |
6c5ffb9a | 731 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 732 | req->i.ifi_flags |= IFF_NOARP; |
6c5ffb9a | 733 | else |
e543a6a8 | 734 | return on_off("arp", *argv); |
432b92a7 ZS |
735 | } else if (strcmp(*argv, "carrier") == 0) { |
736 | int carrier; | |
bb6ab47b | 737 | |
432b92a7 ZS |
738 | NEXT_ARG(); |
739 | if (strcmp(*argv, "on") == 0) | |
740 | carrier = 1; | |
741 | else if (strcmp(*argv, "off") == 0) | |
742 | carrier = 0; | |
743 | else | |
744 | return on_off("carrier", *argv); | |
745 | ||
746 | addattr8(&req->n, sizeof(*req), IFLA_CARRIER, carrier); | |
ae7229d5 | 747 | } else if (strcmp(*argv, "vf") == 0) { |
1598b9ef | 748 | struct rtattr *vflist; |
6c5ffb9a | 749 | |
ae7229d5 | 750 | NEXT_ARG(); |
6c5ffb9a | 751 | if (get_integer(&vf, *argv, 0)) |
ae7229d5 | 752 | invarg("Invalid \"vf\" value\n", *argv); |
6c5ffb9a | 753 | |
1598b9ef SH |
754 | vflist = addattr_nest(&req->n, sizeof(*req), |
755 | IFLA_VFINFO_LIST); | |
65083b5f | 756 | if (!dev) |
9a02651a SH |
757 | missarg("dev"); |
758 | ||
65083b5f | 759 | len = iplink_parse_vf(vf, &argc, &argv, req, dev); |
1598b9ef SH |
760 | if (len < 0) |
761 | return -1; | |
762 | addattr_nest_end(&req->n, vflist); | |
a24315ba | 763 | |
c58213f6 SP |
764 | if (name == dev) |
765 | dev = NULL; | |
a1e191b9 JP |
766 | } else if (matches(*argv, "master") == 0) { |
767 | int ifindex; | |
6c5ffb9a | 768 | |
a1e191b9 JP |
769 | NEXT_ARG(); |
770 | ifindex = ll_name_to_index(*argv); | |
771 | if (!ifindex) | |
772 | invarg("Device does not exist\n", *argv); | |
773 | addattr_l(&req->n, sizeof(*req), IFLA_MASTER, | |
774 | &ifindex, 4); | |
104444c2 DA |
775 | } else if (strcmp(*argv, "vrf") == 0) { |
776 | int ifindex; | |
777 | ||
778 | NEXT_ARG(); | |
779 | ifindex = ll_name_to_index(*argv); | |
780 | if (!ifindex) | |
781 | invarg("Not a valid VRF name\n", *argv); | |
782 | if (!name_is_vrf(*argv)) | |
783 | invarg("Not a valid VRF name\n", *argv); | |
784 | addattr_l(&req->n, sizeof(*req), IFLA_MASTER, | |
785 | &ifindex, sizeof(ifindex)); | |
a1e191b9 JP |
786 | } else if (matches(*argv, "nomaster") == 0) { |
787 | int ifindex = 0; | |
6c5ffb9a | 788 | |
a1e191b9 JP |
789 | addattr_l(&req->n, sizeof(*req), IFLA_MASTER, |
790 | &ifindex, 4); | |
1d934839 PM |
791 | } else if (matches(*argv, "dynamic") == 0) { |
792 | NEXT_ARG(); | |
909dfe2c | 793 | req->i.ifi_change |= IFF_DYNAMIC; |
6c5ffb9a SH |
794 | |
795 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 796 | req->i.ifi_flags |= IFF_DYNAMIC; |
6c5ffb9a | 797 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 798 | req->i.ifi_flags &= ~IFF_DYNAMIC; |
6c5ffb9a | 799 | else |
14645ec2 | 800 | return on_off("dynamic", *argv); |
1d934839 PM |
801 | } else if (matches(*argv, "type") == 0) { |
802 | NEXT_ARG(); | |
909dfe2c | 803 | *type = *argv; |
1d934839 PM |
804 | argc--; argv++; |
805 | break; | |
ace9c961 | 806 | } else if (matches(*argv, "alias") == 0) { |
c4fb35bd | 807 | NEXT_ARG(); |
f88becf3 SP |
808 | len = strlen(*argv); |
809 | if (len >= IFALIASZ) | |
810 | invarg("alias too long\n", *argv); | |
ace9c961 | 811 | addattr_l(&req->n, sizeof(*req), IFLA_IFALIAS, |
f88becf3 | 812 | *argv, len); |
db02608b VD |
813 | } else if (strcmp(*argv, "group") == 0) { |
814 | NEXT_ARG(); | |
c58213f6 | 815 | if (group != -1) |
db02608b | 816 | duparg("group", *argv); |
c58213f6 | 817 | if (rtnl_group_a2n(&group, *argv)) |
db02608b | 818 | invarg("Invalid \"group\" value\n", *argv); |
c58213f6 | 819 | addattr32(&req->n, sizeof(*req), IFLA_GROUP, group); |
82499282 SH |
820 | } else if (strcmp(*argv, "mode") == 0) { |
821 | int mode; | |
6c5ffb9a | 822 | |
82499282 | 823 | NEXT_ARG(); |
4ccfb44d | 824 | mode = get_link_mode(*argv); |
82499282 SH |
825 | if (mode < 0) |
826 | invarg("Invalid link mode\n", *argv); | |
827 | addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode); | |
4f2fdd44 SH |
828 | } else if (strcmp(*argv, "state") == 0) { |
829 | int state; | |
6c5ffb9a | 830 | |
4f2fdd44 SH |
831 | NEXT_ARG(); |
832 | state = get_operstate(*argv); | |
833 | if (state < 0) | |
834 | invarg("Invalid operstate\n", *argv); | |
835 | ||
836 | addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state); | |
d992f3e6 JP |
837 | } else if (matches(*argv, "numtxqueues") == 0) { |
838 | NEXT_ARG(); | |
839 | if (numtxqueues != -1) | |
840 | duparg("numtxqueues", *argv); | |
841 | if (get_integer(&numtxqueues, *argv, 0)) | |
bb6ab47b SH |
842 | invarg("Invalid \"numtxqueues\" value\n", |
843 | *argv); | |
d992f3e6 JP |
844 | addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES, |
845 | &numtxqueues, 4); | |
846 | } else if (matches(*argv, "numrxqueues") == 0) { | |
847 | NEXT_ARG(); | |
848 | if (numrxqueues != -1) | |
849 | duparg("numrxqueues", *argv); | |
850 | if (get_integer(&numrxqueues, *argv, 0)) | |
bb6ab47b SH |
851 | invarg("Invalid \"numrxqueues\" value\n", |
852 | *argv); | |
d992f3e6 JP |
853 | addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES, |
854 | &numrxqueues, 4); | |
ff7c2084 JP |
855 | } else if (matches(*argv, "addrgenmode") == 0) { |
856 | struct rtattr *afs, *afs6; | |
857 | int mode; | |
6c5ffb9a | 858 | |
ff7c2084 JP |
859 | NEXT_ARG(); |
860 | mode = get_addr_gen_mode(*argv); | |
861 | if (mode < 0) | |
bb6ab47b SH |
862 | invarg("Invalid address generation mode\n", |
863 | *argv); | |
ff7c2084 JP |
864 | afs = addattr_nest(&req->n, sizeof(*req), IFLA_AF_SPEC); |
865 | afs6 = addattr_nest(&req->n, sizeof(*req), AF_INET6); | |
ef0a738c SH |
866 | addattr8(&req->n, sizeof(*req), |
867 | IFLA_INET6_ADDR_GEN_MODE, mode); | |
ff7c2084 JP |
868 | addattr_nest_end(&req->n, afs6); |
869 | addattr_nest_end(&req->n, afs); | |
974ef93b ND |
870 | } else if (matches(*argv, "link-netns") == 0) { |
871 | NEXT_ARG(); | |
872 | if (link_netnsid != -1) | |
873 | duparg("link-netns/link-netnsid", *argv); | |
874 | link_netnsid = get_netnsid_from_name(*argv); | |
875 | /* No nsid? Try to assign one. */ | |
876 | if (link_netnsid < 0) | |
877 | set_netnsid_from_name(*argv, -1); | |
878 | link_netnsid = get_netnsid_from_name(*argv); | |
879 | if (link_netnsid < 0) | |
880 | invarg("Invalid \"link-netns\" value\n", | |
881 | *argv); | |
882 | addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID, | |
883 | link_netnsid); | |
ccdcbf35 ND |
884 | } else if (matches(*argv, "link-netnsid") == 0) { |
885 | NEXT_ARG(); | |
886 | if (link_netnsid != -1) | |
974ef93b | 887 | duparg("link-netns/link-netnsid", *argv); |
ccdcbf35 | 888 | if (get_integer(&link_netnsid, *argv, 0)) |
bb6ab47b SH |
889 | invarg("Invalid \"link-netnsid\" value\n", |
890 | *argv); | |
ccdcbf35 ND |
891 | addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID, |
892 | link_netnsid); | |
18864827 AK |
893 | } else if (strcmp(*argv, "protodown") == 0) { |
894 | unsigned int proto_down; | |
895 | ||
896 | NEXT_ARG(); | |
897 | if (strcmp(*argv, "on") == 0) | |
898 | proto_down = 1; | |
899 | else if (strcmp(*argv, "off") == 0) | |
900 | proto_down = 0; | |
901 | else | |
902 | return on_off("protodown", *argv); | |
903 | addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN, | |
904 | proto_down); | |
c2db423f SH |
905 | } else if (strcmp(*argv, "gso_max_size") == 0) { |
906 | unsigned int max_size; | |
907 | ||
908 | NEXT_ARG(); | |
051274b4 | 909 | if (get_unsigned(&max_size, *argv, 0) || |
08f9d166 | 910 | max_size > GSO_MAX_SIZE) |
c2db423f SH |
911 | invarg("Invalid \"gso_max_size\" value\n", |
912 | *argv); | |
08f9d166 SH |
913 | addattr32(&req->n, sizeof(*req), |
914 | IFLA_GSO_MAX_SIZE, max_size); | |
c2db423f SH |
915 | } else if (strcmp(*argv, "gso_max_segs") == 0) { |
916 | unsigned int max_segs; | |
917 | ||
918 | NEXT_ARG(); | |
08f9d166 SH |
919 | if (get_unsigned(&max_segs, *argv, 0) || |
920 | max_segs > GSO_MAX_SEGS) | |
c2db423f SH |
921 | invarg("Invalid \"gso_max_segs\" value\n", |
922 | *argv); | |
08f9d166 SH |
923 | addattr32(&req->n, sizeof(*req), |
924 | IFLA_GSO_MAX_SEGS, max_segs); | |
1d934839 | 925 | } else { |
05325552 PM |
926 | if (matches(*argv, "help") == 0) |
927 | usage(); | |
940a96e6 PS |
928 | |
929 | if (strcmp(*argv, "dev") == 0) | |
930 | NEXT_ARG(); | |
c58213f6 | 931 | if (dev != name) |
1d934839 | 932 | duparg2("dev", *argv); |
625df645 PS |
933 | if (check_ifname(*argv)) |
934 | invarg("\"dev\" not a valid ifname", *argv); | |
c58213f6 | 935 | dev = *argv; |
1d934839 PM |
936 | } |
937 | argc--; argv++; | |
938 | } | |
939 | ||
c58213f6 SP |
940 | ret -= argc; |
941 | ||
a24315ba | 942 | /* Allow "ip link add dev" and "ip link add name" */ |
c58213f6 SP |
943 | if (!name) |
944 | name = dev; | |
945 | else if (!dev) | |
946 | dev = name; | |
947 | else if (!strcmp(name, dev)) | |
948 | name = dev; | |
a24315ba | 949 | |
65083b5f SH |
950 | if (dev && addr_len) { |
951 | int halen = nl_get_ll_addr_len(dev); | |
ef0a738c | 952 | |
8fe58d58 PS |
953 | if (halen >= 0 && halen != addr_len) { |
954 | fprintf(stderr, | |
ef0a738c SH |
955 | "Invalid address length %d - must be %d bytes\n", |
956 | addr_len, halen); | |
8fe58d58 PS |
957 | return -1; |
958 | } | |
959 | } | |
960 | ||
c58213f6 | 961 | if (!(req->n.nlmsg_flags & NLM_F_CREATE) && index) { |
b06a2960 SP |
962 | fprintf(stderr, |
963 | "index can be used only when creating devices.\n"); | |
964 | exit(-1); | |
965 | } | |
966 | ||
db02608b | 967 | if (group != -1) { |
c58213f6 | 968 | if (!dev) { |
db02608b | 969 | if (argc) { |
6773bcc2 SH |
970 | fprintf(stderr, |
971 | "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n", | |
972 | *argv); | |
c58213f6 | 973 | exit(-1); |
db02608b | 974 | } |
c58213f6 | 975 | if (req->n.nlmsg_flags & NLM_F_CREATE) { |
bb6ab47b SH |
976 | fprintf(stderr, |
977 | "group cannot be used when creating devices.\n"); | |
c58213f6 | 978 | exit(-1); |
db02608b VD |
979 | } |
980 | ||
c58213f6 SP |
981 | *type = NULL; |
982 | return ret; | |
db02608b VD |
983 | } |
984 | } | |
985 | ||
c58213f6 | 986 | if (!(req->n.nlmsg_flags & NLM_F_CREATE)) { |
1d934839 | 987 | if (!dev) { |
bb6ab47b SH |
988 | fprintf(stderr, |
989 | "Not enough information: \"dev\" argument is required.\n"); | |
1d934839 PM |
990 | exit(-1); |
991 | } | |
992 | ||
c58213f6 SP |
993 | req->i.ifi_index = ll_name_to_index(dev); |
994 | if (!req->i.ifi_index) | |
fe99adbc | 995 | return nodev(dev); |
a24315ba SP |
996 | |
997 | /* Not renaming to the same name */ | |
998 | if (name == dev) | |
999 | name = NULL; | |
1d934839 | 1000 | } else { |
a24315ba SP |
1001 | if (name != dev) { |
1002 | fprintf(stderr, | |
1003 | "both \"name\" and \"dev\" cannot be used when creating devices.\n"); | |
1004 | exit(-1); | |
1005 | } | |
1d934839 PM |
1006 | |
1007 | if (link) { | |
1008 | int ifindex; | |
1009 | ||
1010 | ifindex = ll_name_to_index(link); | |
fe99adbc SP |
1011 | if (!ifindex) |
1012 | return nodev(link); | |
c58213f6 | 1013 | addattr32(&req->n, sizeof(*req), IFLA_LINK, ifindex); |
1d934839 | 1014 | } |
5e25cf77 | 1015 | |
c58213f6 | 1016 | req->i.ifi_index = index; |
1d934839 PM |
1017 | } |
1018 | ||
1019 | if (name) { | |
c58213f6 | 1020 | addattr_l(&req->n, sizeof(*req), |
625df645 | 1021 | IFLA_IFNAME, name, strlen(name) + 1); |
1d934839 PM |
1022 | } |
1023 | ||
c58213f6 SP |
1024 | return ret; |
1025 | } | |
1026 | ||
1027 | static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) | |
1028 | { | |
1029 | char *type = NULL; | |
1030 | struct iplink_req req = { | |
1031 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), | |
1032 | .n.nlmsg_flags = NLM_F_REQUEST | flags, | |
1033 | .n.nlmsg_type = cmd, | |
1034 | .i.ifi_family = preferred_family, | |
1035 | }; | |
1036 | int ret; | |
1037 | ||
1038 | ret = iplink_parse(argc, argv, &req, &type); | |
1039 | if (ret < 0) | |
1040 | return ret; | |
1041 | ||
09fa3279 | 1042 | if (type) { |
c58213f6 | 1043 | struct link_util *lu; |
a1e2e5fc | 1044 | struct rtattr *linkinfo; |
22a84711 | 1045 | char *ulinep = strchr(type, '_'); |
620ddeda NA |
1046 | int iflatype; |
1047 | ||
a1e2e5fc | 1048 | linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); |
09fa3279 SH |
1049 | addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type, |
1050 | strlen(type)); | |
1051 | ||
22a84711 HL |
1052 | lu = get_link_kind(type); |
1053 | if (ulinep && !strcmp(ulinep, "_slave")) | |
620ddeda | 1054 | iflatype = IFLA_INFO_SLAVE_DATA; |
22a84711 | 1055 | else |
620ddeda | 1056 | iflatype = IFLA_INFO_DATA; |
c58213f6 SP |
1057 | |
1058 | argc -= ret; | |
1059 | argv += ret; | |
1060 | ||
09fa3279 | 1061 | if (lu && argc) { |
c14f9d92 SP |
1062 | struct rtattr *data; |
1063 | ||
1064 | data = addattr_nest(&req.n, sizeof(req), iflatype); | |
09fa3279 SH |
1065 | |
1066 | if (lu->parse_opt && | |
1067 | lu->parse_opt(lu, argc, argv, &req.n)) | |
1068 | return -1; | |
1069 | ||
a1e2e5fc | 1070 | addattr_nest_end(&req.n, data); |
09fa3279 SH |
1071 | } else if (argc) { |
1072 | if (matches(*argv, "help") == 0) | |
1073 | usage(); | |
6773bcc2 SH |
1074 | fprintf(stderr, |
1075 | "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n", | |
1076 | *argv); | |
09fa3279 SH |
1077 | return -1; |
1078 | } | |
a1e2e5fc | 1079 | addattr_nest_end(&req.n, linkinfo); |
09fa3279 | 1080 | } else if (flags & NLM_F_CREATE) { |
bb6ab47b SH |
1081 | fprintf(stderr, |
1082 | "Not enough information: \"type\" argument is required\n"); | |
09fa3279 SH |
1083 | return -1; |
1084 | } | |
1085 | ||
86bf43c7 | 1086 | if (rtnl_talk(&rth, &req.n, NULL) < 0) |
f921f567 | 1087 | return -2; |
1d934839 PM |
1088 | |
1089 | return 0; | |
1090 | } | |
1091 | ||
260137e2 | 1092 | int iplink_get(char *name, __u32 filt_mask) |
50b9950d | 1093 | { |
d17b136f PS |
1094 | struct iplink_req req = { |
1095 | .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), | |
260137e2 | 1096 | .n.nlmsg_flags = NLM_F_REQUEST, |
d17b136f PS |
1097 | .n.nlmsg_type = RTM_GETLINK, |
1098 | .i.ifi_family = preferred_family, | |
1099 | }; | |
86bf43c7 | 1100 | struct nlmsghdr *answer; |
50b9950d | 1101 | |
50b9950d | 1102 | if (name) { |
625df645 PS |
1103 | addattr_l(&req.n, sizeof(req), |
1104 | IFLA_IFNAME, name, strlen(name) + 1); | |
50b9950d RP |
1105 | } |
1106 | addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask); | |
1107 | ||
86bf43c7 | 1108 | if (rtnl_talk(&rth, &req.n, &answer) < 0) |
50b9950d RP |
1109 | return -2; |
1110 | ||
e4a1216a | 1111 | open_json_object(NULL); |
cd554f2c | 1112 | print_linkinfo(answer, stdout); |
e4a1216a | 1113 | close_json_object(); |
50b9950d | 1114 | |
86bf43c7 | 1115 | free(answer); |
50b9950d RP |
1116 | return 0; |
1117 | } | |
1118 | ||
1d934839 | 1119 | #if IPLINK_IOCTL_COMPAT |
aba5acdf SH |
1120 | static int get_ctl_fd(void) |
1121 | { | |
1122 | int s_errno; | |
1123 | int fd; | |
1124 | ||
1125 | fd = socket(PF_INET, SOCK_DGRAM, 0); | |
1126 | if (fd >= 0) | |
1127 | return fd; | |
1128 | s_errno = errno; | |
1129 | fd = socket(PF_PACKET, SOCK_DGRAM, 0); | |
1130 | if (fd >= 0) | |
1131 | return fd; | |
1132 | fd = socket(PF_INET6, SOCK_DGRAM, 0); | |
1133 | if (fd >= 0) | |
1134 | return fd; | |
1135 | errno = s_errno; | |
1136 | perror("Cannot create control socket"); | |
1137 | return -1; | |
1138 | } | |
1139 | ||
71058eb8 | 1140 | static int do_chflags(const char *dev, __u32 flags, __u32 mask) |
aba5acdf SH |
1141 | { |
1142 | struct ifreq ifr; | |
1143 | int fd; | |
1144 | int err; | |
1145 | ||
fc9d755a | 1146 | strlcpy(ifr.ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
1147 | fd = get_ctl_fd(); |
1148 | if (fd < 0) | |
1149 | return -1; | |
1150 | err = ioctl(fd, SIOCGIFFLAGS, &ifr); | |
1151 | if (err) { | |
1152 | perror("SIOCGIFFLAGS"); | |
1153 | close(fd); | |
1154 | return -1; | |
1155 | } | |
1156 | if ((ifr.ifr_flags^flags)&mask) { | |
1157 | ifr.ifr_flags &= ~mask; | |
1158 | ifr.ifr_flags |= mask&flags; | |
1159 | err = ioctl(fd, SIOCSIFFLAGS, &ifr); | |
1160 | if (err) | |
1161 | perror("SIOCSIFFLAGS"); | |
1162 | } | |
1163 | close(fd); | |
1164 | return err; | |
1165 | } | |
1166 | ||
71058eb8 | 1167 | static int do_changename(const char *dev, const char *newdev) |
aba5acdf SH |
1168 | { |
1169 | struct ifreq ifr; | |
1170 | int fd; | |
1171 | int err; | |
1172 | ||
fc9d755a SH |
1173 | strlcpy(ifr.ifr_name, dev, IFNAMSIZ); |
1174 | strlcpy(ifr.ifr_newname, newdev, IFNAMSIZ); | |
aba5acdf SH |
1175 | fd = get_ctl_fd(); |
1176 | if (fd < 0) | |
1177 | return -1; | |
1178 | err = ioctl(fd, SIOCSIFNAME, &ifr); | |
1179 | if (err) { | |
1180 | perror("SIOCSIFNAME"); | |
1181 | close(fd); | |
1182 | return -1; | |
1183 | } | |
1184 | close(fd); | |
1185 | return err; | |
1186 | } | |
1187 | ||
71058eb8 | 1188 | static int set_qlen(const char *dev, int qlen) |
aba5acdf | 1189 | { |
d17b136f | 1190 | struct ifreq ifr = { .ifr_qlen = qlen }; |
aba5acdf SH |
1191 | int s; |
1192 | ||
1193 | s = get_ctl_fd(); | |
1194 | if (s < 0) | |
1195 | return -1; | |
1196 | ||
fc9d755a | 1197 | strlcpy(ifr.ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
1198 | if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { |
1199 | perror("SIOCSIFXQLEN"); | |
1200 | close(s); | |
1201 | return -1; | |
1202 | } | |
1203 | close(s); | |
1204 | ||
ae665a52 | 1205 | return 0; |
aba5acdf SH |
1206 | } |
1207 | ||
71058eb8 | 1208 | static int set_mtu(const char *dev, int mtu) |
aba5acdf | 1209 | { |
d17b136f | 1210 | struct ifreq ifr = { .ifr_mtu = mtu }; |
aba5acdf SH |
1211 | int s; |
1212 | ||
1213 | s = get_ctl_fd(); | |
1214 | if (s < 0) | |
1215 | return -1; | |
1216 | ||
fc9d755a | 1217 | strlcpy(ifr.ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
1218 | if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { |
1219 | perror("SIOCSIFMTU"); | |
1220 | close(s); | |
1221 | return -1; | |
1222 | } | |
1223 | close(s); | |
1224 | ||
ae665a52 | 1225 | return 0; |
aba5acdf SH |
1226 | } |
1227 | ||
71058eb8 | 1228 | static int get_address(const char *dev, int *htype) |
aba5acdf | 1229 | { |
d17b136f PS |
1230 | struct ifreq ifr = {}; |
1231 | struct sockaddr_ll me = { | |
1232 | .sll_family = AF_PACKET, | |
1233 | .sll_protocol = htons(ETH_P_LOOP), | |
1234 | }; | |
f332d169 | 1235 | socklen_t alen; |
aba5acdf SH |
1236 | int s; |
1237 | ||
1238 | s = socket(PF_PACKET, SOCK_DGRAM, 0); | |
ae665a52 | 1239 | if (s < 0) { |
aba5acdf SH |
1240 | perror("socket(PF_PACKET)"); |
1241 | return -1; | |
1242 | } | |
1243 | ||
fc9d755a | 1244 | strlcpy(ifr.ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
1245 | if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { |
1246 | perror("SIOCGIFINDEX"); | |
1247 | close(s); | |
1248 | return -1; | |
1249 | } | |
1250 | ||
aba5acdf | 1251 | me.sll_ifindex = ifr.ifr_ifindex; |
6c5ffb9a | 1252 | if (bind(s, (struct sockaddr *)&me, sizeof(me)) == -1) { |
aba5acdf SH |
1253 | perror("bind"); |
1254 | close(s); | |
1255 | return -1; | |
1256 | } | |
1257 | ||
1258 | alen = sizeof(me); | |
6c5ffb9a | 1259 | if (getsockname(s, (struct sockaddr *)&me, &alen) == -1) { |
aba5acdf SH |
1260 | perror("getsockname"); |
1261 | close(s); | |
1262 | return -1; | |
1263 | } | |
1264 | close(s); | |
1265 | *htype = me.sll_hatype; | |
1266 | return me.sll_halen; | |
1267 | } | |
1268 | ||
ae665a52 | 1269 | static int parse_address(const char *dev, int hatype, int halen, |
7b565754 | 1270 | char *lla, struct ifreq *ifr) |
aba5acdf SH |
1271 | { |
1272 | int alen; | |
1273 | ||
1274 | memset(ifr, 0, sizeof(*ifr)); | |
fc9d755a | 1275 | strlcpy(ifr->ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
1276 | ifr->ifr_hwaddr.sa_family = hatype; |
1277 | alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); | |
1278 | if (alen < 0) | |
1279 | return -1; | |
1280 | if (alen != halen) { | |
bb6ab47b SH |
1281 | fprintf(stderr, |
1282 | "Wrong address (%s) length: expected %d bytes\n", | |
ef0a738c | 1283 | lla, halen); |
aba5acdf SH |
1284 | return -1; |
1285 | } | |
ae665a52 | 1286 | return 0; |
aba5acdf SH |
1287 | } |
1288 | ||
1289 | static int set_address(struct ifreq *ifr, int brd) | |
1290 | { | |
1291 | int s; | |
1292 | ||
1293 | s = get_ctl_fd(); | |
1294 | if (s < 0) | |
1295 | return -1; | |
1296 | if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { | |
1297 | perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); | |
1298 | close(s); | |
1299 | return -1; | |
1300 | } | |
1301 | close(s); | |
ae665a52 | 1302 | return 0; |
aba5acdf SH |
1303 | } |
1304 | ||
aba5acdf SH |
1305 | static int do_set(int argc, char **argv) |
1306 | { | |
1307 | char *dev = NULL; | |
1308 | __u32 mask = 0; | |
1309 | __u32 flags = 0; | |
1310 | int qlen = -1; | |
1311 | int mtu = -1; | |
1312 | char *newaddr = NULL; | |
1313 | char *newbrd = NULL; | |
1314 | struct ifreq ifr0, ifr1; | |
1315 | char *newname = NULL; | |
1316 | int htype, halen; | |
1317 | ||
1318 | while (argc > 0) { | |
1319 | if (strcmp(*argv, "up") == 0) { | |
1320 | mask |= IFF_UP; | |
1321 | flags |= IFF_UP; | |
1322 | } else if (strcmp(*argv, "down") == 0) { | |
1323 | mask |= IFF_UP; | |
1324 | flags &= ~IFF_UP; | |
1325 | } else if (strcmp(*argv, "name") == 0) { | |
1326 | NEXT_ARG(); | |
625df645 PS |
1327 | if (check_ifname(*argv)) |
1328 | invarg("\"name\" not a valid ifname", *argv); | |
aba5acdf SH |
1329 | newname = *argv; |
1330 | } else if (matches(*argv, "address") == 0) { | |
1331 | NEXT_ARG(); | |
1332 | newaddr = *argv; | |
1333 | } else if (matches(*argv, "broadcast") == 0 || | |
1334 | strcmp(*argv, "brd") == 0) { | |
1335 | NEXT_ARG(); | |
1336 | newbrd = *argv; | |
1337 | } else if (matches(*argv, "txqueuelen") == 0 || | |
1338 | strcmp(*argv, "qlen") == 0 || | |
1339 | matches(*argv, "txqlen") == 0) { | |
1340 | NEXT_ARG(); | |
1341 | if (qlen != -1) | |
1342 | duparg("txqueuelen", *argv); | |
1343 | if (get_integer(&qlen, *argv, 0)) | |
1344 | invarg("Invalid \"txqueuelen\" value\n", *argv); | |
1345 | } else if (strcmp(*argv, "mtu") == 0) { | |
1346 | NEXT_ARG(); | |
1347 | if (mtu != -1) | |
1348 | duparg("mtu", *argv); | |
1349 | if (get_integer(&mtu, *argv, 0)) | |
1350 | invarg("Invalid \"mtu\" value\n", *argv); | |
1351 | } else if (strcmp(*argv, "multicast") == 0) { | |
1352 | NEXT_ARG(); | |
1353 | mask |= IFF_MULTICAST; | |
6c5ffb9a SH |
1354 | |
1355 | if (strcmp(*argv, "on") == 0) | |
aba5acdf | 1356 | flags |= IFF_MULTICAST; |
6c5ffb9a | 1357 | else if (strcmp(*argv, "off") == 0) |
aba5acdf | 1358 | flags &= ~IFF_MULTICAST; |
6c5ffb9a | 1359 | else |
14645ec2 | 1360 | return on_off("multicast", *argv); |
d27b1b5b | 1361 | } else if (strcmp(*argv, "allmulticast") == 0) { |
1362 | NEXT_ARG(); | |
1363 | mask |= IFF_ALLMULTI; | |
6c5ffb9a SH |
1364 | |
1365 | if (strcmp(*argv, "on") == 0) | |
d27b1b5b | 1366 | flags |= IFF_ALLMULTI; |
6c5ffb9a | 1367 | else if (strcmp(*argv, "off") == 0) |
d27b1b5b | 1368 | flags &= ~IFF_ALLMULTI; |
6c5ffb9a | 1369 | else |
14645ec2 | 1370 | return on_off("allmulticast", *argv); |
d27b1b5b | 1371 | } else if (strcmp(*argv, "promisc") == 0) { |
1372 | NEXT_ARG(); | |
1373 | mask |= IFF_PROMISC; | |
6c5ffb9a SH |
1374 | |
1375 | if (strcmp(*argv, "on") == 0) | |
d27b1b5b | 1376 | flags |= IFF_PROMISC; |
6c5ffb9a | 1377 | else if (strcmp(*argv, "off") == 0) |
d27b1b5b | 1378 | flags &= ~IFF_PROMISC; |
6c5ffb9a | 1379 | else |
14645ec2 | 1380 | return on_off("promisc", *argv); |
d27b1b5b | 1381 | } else if (strcmp(*argv, "trailers") == 0) { |
1382 | NEXT_ARG(); | |
1383 | mask |= IFF_NOTRAILERS; | |
6c5ffb9a SH |
1384 | |
1385 | if (strcmp(*argv, "off") == 0) | |
d27b1b5b | 1386 | flags |= IFF_NOTRAILERS; |
6c5ffb9a | 1387 | else if (strcmp(*argv, "on") == 0) |
d27b1b5b | 1388 | flags &= ~IFF_NOTRAILERS; |
6c5ffb9a | 1389 | else |
14645ec2 | 1390 | return on_off("trailers", *argv); |
aba5acdf SH |
1391 | } else if (strcmp(*argv, "arp") == 0) { |
1392 | NEXT_ARG(); | |
1393 | mask |= IFF_NOARP; | |
6c5ffb9a SH |
1394 | |
1395 | if (strcmp(*argv, "on") == 0) | |
aba5acdf | 1396 | flags &= ~IFF_NOARP; |
6c5ffb9a | 1397 | else if (strcmp(*argv, "off") == 0) |
aba5acdf | 1398 | flags |= IFF_NOARP; |
6c5ffb9a | 1399 | else |
ff1e35ed | 1400 | return on_off("arp", *argv); |
aba5acdf SH |
1401 | } else if (matches(*argv, "dynamic") == 0) { |
1402 | NEXT_ARG(); | |
1403 | mask |= IFF_DYNAMIC; | |
6c5ffb9a SH |
1404 | |
1405 | if (strcmp(*argv, "on") == 0) | |
aba5acdf | 1406 | flags |= IFF_DYNAMIC; |
6c5ffb9a | 1407 | else if (strcmp(*argv, "off") == 0) |
aba5acdf | 1408 | flags &= ~IFF_DYNAMIC; |
6c5ffb9a | 1409 | else |
14645ec2 | 1410 | return on_off("dynamic", *argv); |
aba5acdf | 1411 | } else { |
6c5ffb9a | 1412 | if (strcmp(*argv, "dev") == 0) |
aba5acdf | 1413 | NEXT_ARG(); |
8aacb9bb | 1414 | else if (matches(*argv, "help") == 0) |
aba5acdf | 1415 | usage(); |
6c5ffb9a | 1416 | |
aba5acdf SH |
1417 | if (dev) |
1418 | duparg2("dev", *argv); | |
625df645 PS |
1419 | if (check_ifname(*argv)) |
1420 | invarg("\"dev\" not a valid ifname", *argv); | |
aba5acdf SH |
1421 | dev = *argv; |
1422 | } | |
1423 | argc--; argv++; | |
1424 | } | |
1425 | ||
1426 | if (!dev) { | |
ef0a738c SH |
1427 | fprintf(stderr, |
1428 | "Not enough of information: \"dev\" argument is required.\n"); | |
aba5acdf SH |
1429 | exit(-1); |
1430 | } | |
1431 | ||
1432 | if (newaddr || newbrd) { | |
1433 | halen = get_address(dev, &htype); | |
1434 | if (halen < 0) | |
1435 | return -1; | |
1436 | if (newaddr) { | |
bb6ab47b SH |
1437 | if (parse_address(dev, htype, halen, |
1438 | newaddr, &ifr0) < 0) | |
aba5acdf SH |
1439 | return -1; |
1440 | } | |
1441 | if (newbrd) { | |
bb6ab47b SH |
1442 | if (parse_address(dev, htype, halen, |
1443 | newbrd, &ifr1) < 0) | |
ae665a52 | 1444 | return -1; |
aba5acdf SH |
1445 | } |
1446 | } | |
1447 | ||
1448 | if (newname && strcmp(dev, newname)) { | |
1449 | if (do_changename(dev, newname) < 0) | |
1450 | return -1; | |
1451 | dev = newname; | |
1452 | } | |
ae665a52 | 1453 | if (qlen != -1) { |
aba5acdf | 1454 | if (set_qlen(dev, qlen) < 0) |
ae665a52 | 1455 | return -1; |
aba5acdf | 1456 | } |
ae665a52 | 1457 | if (mtu != -1) { |
aba5acdf | 1458 | if (set_mtu(dev, mtu) < 0) |
ae665a52 | 1459 | return -1; |
aba5acdf SH |
1460 | } |
1461 | if (newaddr || newbrd) { | |
1462 | if (newbrd) { | |
1463 | if (set_address(&ifr1, 1) < 0) | |
ae665a52 | 1464 | return -1; |
aba5acdf SH |
1465 | } |
1466 | if (newaddr) { | |
1467 | if (set_address(&ifr0, 0) < 0) | |
1468 | return -1; | |
1469 | } | |
1470 | } | |
1471 | if (mask) | |
1472 | return do_chflags(dev, flags, mask); | |
1473 | return 0; | |
1474 | } | |
1d934839 | 1475 | #endif /* IPLINK_IOCTL_COMPAT */ |
aba5acdf | 1476 | |
837552b4 RS |
1477 | static void print_mpls_stats(FILE *fp, struct rtattr *attr) |
1478 | { | |
1479 | struct rtattr *mrtb[MPLS_STATS_MAX+1]; | |
1480 | struct mpls_link_stats *stats; | |
1481 | ||
1482 | parse_rtattr(mrtb, MPLS_STATS_MAX, RTA_DATA(attr), | |
1483 | RTA_PAYLOAD(attr)); | |
1484 | if (!mrtb[MPLS_STATS_LINK]) | |
1485 | return; | |
1486 | ||
1487 | stats = RTA_DATA(mrtb[MPLS_STATS_LINK]); | |
1488 | ||
1489 | fprintf(fp, " mpls:\n"); | |
1490 | fprintf(fp, " RX: bytes packets errors dropped noroute\n"); | |
1491 | fprintf(fp, " "); | |
1492 | print_num(fp, 10, stats->rx_bytes); | |
1493 | print_num(fp, 8, stats->rx_packets); | |
1494 | print_num(fp, 7, stats->rx_errors); | |
1495 | print_num(fp, 8, stats->rx_dropped); | |
1496 | print_num(fp, 7, stats->rx_noroute); | |
1497 | fprintf(fp, "\n"); | |
1498 | fprintf(fp, " TX: bytes packets errors dropped\n"); | |
1499 | fprintf(fp, " "); | |
1500 | print_num(fp, 10, stats->tx_bytes); | |
1501 | print_num(fp, 8, stats->tx_packets); | |
1502 | print_num(fp, 7, stats->tx_errors); | |
1503 | print_num(fp, 7, stats->tx_dropped); | |
1504 | fprintf(fp, "\n"); | |
1505 | } | |
1506 | ||
1507 | static void print_af_stats_attr(FILE *fp, int ifindex, struct rtattr *attr) | |
1508 | { | |
1509 | bool if_printed = false; | |
1510 | struct rtattr *i; | |
1511 | int rem; | |
1512 | ||
1513 | rem = RTA_PAYLOAD(attr); | |
1514 | for (i = RTA_DATA(attr); RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { | |
1515 | if (preferred_family != AF_UNSPEC && | |
1516 | i->rta_type != preferred_family) | |
1517 | continue; | |
1518 | ||
1519 | if (!if_printed) { | |
1520 | fprintf(fp, "%u: %s\n", ifindex, | |
1521 | ll_index_to_name(ifindex)); | |
1522 | if_printed = true; | |
1523 | } | |
1524 | ||
1525 | switch (i->rta_type) { | |
1526 | case AF_MPLS: | |
1527 | print_mpls_stats(fp, i); | |
1528 | break; | |
1529 | default: | |
1530 | fprintf(fp, " unknown af(%d)\n", i->rta_type); | |
1531 | break; | |
1532 | } | |
1533 | } | |
1534 | } | |
1535 | ||
1536 | struct af_stats_ctx { | |
1537 | FILE *fp; | |
1538 | int ifindex; | |
1539 | }; | |
1540 | ||
cd554f2c | 1541 | static int print_af_stats(struct nlmsghdr *n, void *arg) |
837552b4 RS |
1542 | { |
1543 | struct if_stats_msg *ifsm = NLMSG_DATA(n); | |
1544 | struct rtattr *tb[IFLA_STATS_MAX+1]; | |
1545 | int len = n->nlmsg_len; | |
1546 | struct af_stats_ctx *ctx = arg; | |
1547 | FILE *fp = ctx->fp; | |
1548 | ||
1549 | len -= NLMSG_LENGTH(sizeof(*ifsm)); | |
1550 | if (len < 0) { | |
1551 | fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); | |
1552 | return -1; | |
1553 | } | |
1554 | ||
1555 | if (ctx->ifindex && ifsm->ifindex != ctx->ifindex) | |
1556 | return 0; | |
1557 | ||
1558 | parse_rtattr(tb, IFLA_STATS_MAX, IFLA_STATS_RTA(ifsm), len); | |
1559 | ||
1560 | if (tb[IFLA_STATS_AF_SPEC]) | |
1561 | print_af_stats_attr(fp, ifsm->ifindex, tb[IFLA_STATS_AF_SPEC]); | |
1562 | ||
1563 | fflush(fp); | |
1564 | return 0; | |
1565 | } | |
1566 | ||
1567 | static int iplink_afstats(int argc, char **argv) | |
1568 | { | |
1569 | __u32 filt_mask = IFLA_STATS_FILTER_BIT(IFLA_STATS_AF_SPEC); | |
1570 | const char *filter_dev = NULL; | |
1571 | struct af_stats_ctx ctx = { | |
1572 | .fp = stdout, | |
1573 | .ifindex = 0, | |
1574 | }; | |
1575 | ||
1576 | while (argc > 0) { | |
1577 | if (strcmp(*argv, "dev") == 0) { | |
1578 | NEXT_ARG(); | |
1579 | if (filter_dev) | |
1580 | duparg2("dev", *argv); | |
1581 | filter_dev = *argv; | |
1582 | } else if (matches(*argv, "help") == 0) { | |
1583 | usage(); | |
1584 | } else { | |
1585 | fprintf(stderr, | |
1586 | "Command \"%s\" is unknown, try \"ip link help\".\n", | |
1587 | *argv); | |
1588 | exit(-1); | |
1589 | } | |
1590 | ||
1591 | argv++; argc--; | |
1592 | } | |
1593 | ||
1594 | if (filter_dev) { | |
1595 | ctx.ifindex = ll_name_to_index(filter_dev); | |
1596 | if (ctx.ifindex <= 0) { | |
1597 | fprintf(stderr, | |
1598 | "Device \"%s\" does not exist.\n", | |
1599 | filter_dev); | |
1600 | return -1; | |
1601 | } | |
1602 | } | |
1603 | ||
56eeeda9 | 1604 | if (rtnl_statsdump_req_filter(&rth, AF_UNSPEC, filt_mask) < 0) { |
837552b4 RS |
1605 | perror("Cannont send dump request"); |
1606 | return 1; | |
1607 | } | |
1608 | ||
1609 | if (rtnl_dump_filter(&rth, print_af_stats, &ctx) < 0) { | |
1610 | fprintf(stderr, "Dump terminated\n"); | |
1611 | return 1; | |
1612 | } | |
1613 | ||
1614 | return 0; | |
1615 | } | |
1616 | ||
561e650e | 1617 | static void do_help(int argc, char **argv) |
1618 | { | |
1619 | struct link_util *lu = NULL; | |
1620 | ||
1621 | if (argc <= 0) { | |
1622 | usage(); | |
6c5ffb9a | 1623 | return; |
561e650e | 1624 | } |
1625 | ||
1626 | lu = get_link_kind(*argv); | |
561e650e | 1627 | if (lu && lu->print_help) |
1628 | lu->print_help(lu, argc-1, argv+1, stdout); | |
1629 | else | |
1630 | usage(); | |
1631 | } | |
1632 | ||
aba5acdf SH |
1633 | int do_iplink(int argc, char **argv) |
1634 | { | |
6843d36e ZS |
1635 | if (argc < 1) |
1636 | return ipaddr_list_link(0, NULL); | |
1637 | ||
1638 | if (iplink_have_newlink()) { | |
1639 | if (matches(*argv, "add") == 0) | |
1640 | return iplink_modify(RTM_NEWLINK, | |
6c5ffb9a SH |
1641 | NLM_F_CREATE|NLM_F_EXCL, |
1642 | argc-1, argv+1); | |
6843d36e | 1643 | if (matches(*argv, "set") == 0 || |
6c5ffb9a | 1644 | matches(*argv, "change") == 0) |
6843d36e | 1645 | return iplink_modify(RTM_NEWLINK, 0, |
6c5ffb9a | 1646 | argc-1, argv+1); |
6843d36e ZS |
1647 | if (matches(*argv, "replace") == 0) |
1648 | return iplink_modify(RTM_NEWLINK, | |
6c5ffb9a SH |
1649 | NLM_F_CREATE|NLM_F_REPLACE, |
1650 | argc-1, argv+1); | |
6843d36e ZS |
1651 | if (matches(*argv, "delete") == 0) |
1652 | return iplink_modify(RTM_DELLINK, 0, | |
6c5ffb9a | 1653 | argc-1, argv+1); |
6843d36e | 1654 | } else { |
1d934839 | 1655 | #if IPLINK_IOCTL_COMPAT |
6843d36e ZS |
1656 | if (matches(*argv, "set") == 0) |
1657 | return do_set(argc-1, argv+1); | |
1d934839 | 1658 | #endif |
6843d36e | 1659 | } |
6c5ffb9a | 1660 | |
6843d36e | 1661 | if (matches(*argv, "show") == 0 || |
6c5ffb9a SH |
1662 | matches(*argv, "lst") == 0 || |
1663 | matches(*argv, "list") == 0) | |
6843d36e | 1664 | return ipaddr_list_link(argc-1, argv+1); |
6c5ffb9a | 1665 | |
94f1a22a NA |
1666 | if (matches(*argv, "xstats") == 0) |
1667 | return iplink_ifla_xstats(argc-1, argv+1); | |
1668 | ||
837552b4 RS |
1669 | if (matches(*argv, "afstats") == 0) { |
1670 | iplink_afstats(argc-1, argv+1); | |
1671 | return 0; | |
1672 | } | |
1673 | ||
6843d36e ZS |
1674 | if (matches(*argv, "help") == 0) { |
1675 | do_help(argc-1, argv+1); | |
1676 | return 0; | |
1677 | } | |
aba5acdf | 1678 | |
6843d36e | 1679 | fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", |
6c5ffb9a | 1680 | *argv); |
aba5acdf SH |
1681 | exit(-1); |
1682 | } |