]>
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> | |
16 | #include <syslog.h> | |
17 | #include <fcntl.h> | |
1d934839 | 18 | #include <dlfcn.h> |
aba5acdf SH |
19 | #include <errno.h> |
20 | #include <sys/socket.h> | |
21 | #include <linux/if.h> | |
22 | #include <linux/if_packet.h> | |
23 | #include <linux/if_ether.h> | |
24 | #include <linux/sockios.h> | |
25 | #include <netinet/in.h> | |
26 | #include <arpa/inet.h> | |
27 | #include <string.h> | |
28 | #include <sys/ioctl.h> | |
29 | #include <linux/sockios.h> | |
fbea6115 | 30 | #include <stdbool.h> |
aba5acdf SH |
31 | |
32 | #include "rt_names.h" | |
33 | #include "utils.h" | |
34 | #include "ip_common.h" | |
c3087c10 | 35 | #include "namespace.h" |
aba5acdf | 36 | |
1d934839 | 37 | #define IPLINK_IOCTL_COMPAT 1 |
5e3bb534 | 38 | #ifndef LIBDIR |
5c434a9e | 39 | #define LIBDIR "/usr/lib" |
b514b358 | 40 | #endif |
aba5acdf SH |
41 | |
42 | static void usage(void) __attribute__((noreturn)); | |
750a405a | 43 | static int iplink_have_newlink(void); |
aba5acdf SH |
44 | |
45 | void iplink_usage(void) | |
46 | { | |
750a405a | 47 | if (iplink_have_newlink()) { |
a22e9295 | 48 | fprintf(stderr, "Usage: ip link add [link DEV] [ name ] NAME\n"); |
750a405a SH |
49 | fprintf(stderr, " [ txqueuelen PACKETS ]\n"); |
50 | fprintf(stderr, " [ address LLADDR ]\n"); | |
51 | fprintf(stderr, " [ broadcast LLADDR ]\n"); | |
5e25cf77 | 52 | fprintf(stderr, " [ mtu MTU ] [index IDX ]\n"); |
d992f3e6 JP |
53 | fprintf(stderr, " [ numtxqueues QUEUE_COUNT ]\n"); |
54 | fprintf(stderr, " [ numrxqueues QUEUE_COUNT ]\n"); | |
750a405a | 55 | fprintf(stderr, " type TYPE [ ARGS ]\n"); |
8916ccf6 | 56 | fprintf(stderr, " ip link delete { DEVICE | dev DEVICE | group DEVGROUP } type TYPE [ ARGS ]\n"); |
750a405a | 57 | fprintf(stderr, "\n"); |
8916ccf6 | 58 | fprintf(stderr, " ip link set { DEVICE | dev DEVICE | group DEVGROUP } [ { up | down } ]\n"); |
750a405a SH |
59 | } else |
60 | fprintf(stderr, "Usage: ip link set DEVICE [ { up | down } ]\n"); | |
61 | ||
62 | fprintf(stderr, " [ arp { on | off } ]\n"); | |
63 | fprintf(stderr, " [ dynamic { on | off } ]\n"); | |
64 | fprintf(stderr, " [ multicast { on | off } ]\n"); | |
65 | fprintf(stderr, " [ allmulticast { on | off } ]\n"); | |
66 | fprintf(stderr, " [ promisc { on | off } ]\n"); | |
67 | fprintf(stderr, " [ trailers { on | off } ]\n"); | |
68 | fprintf(stderr, " [ txqueuelen PACKETS ]\n"); | |
69 | fprintf(stderr, " [ name NEWNAME ]\n"); | |
70 | fprintf(stderr, " [ address LLADDR ]\n"); | |
71 | fprintf(stderr, " [ broadcast LLADDR ]\n"); | |
72 | fprintf(stderr, " [ mtu MTU ]\n"); | |
5c2ea5b8 | 73 | fprintf(stderr, " [ netns { PID | NAME } ]\n"); |
21107f52 | 74 | fprintf(stderr, " [ link-netnsid ID ]\n"); |
ace9c961 | 75 | fprintf(stderr, " [ alias NAME ]\n"); |
ae7229d5 WM |
76 | fprintf(stderr, " [ vf NUM [ mac LLADDR ]\n"); |
77 | fprintf(stderr, " [ vlan VLANID [ qos VLAN-QOS ] ]\n"); | |
7b8179c7 | 78 | |
5c2ea5b8 | 79 | fprintf(stderr, " [ rate TXRATE ]\n"); |
7b8179c7 | 80 | |
5c2ea5b8 PS |
81 | fprintf(stderr, " [ spoofchk { on | off} ]\n"); |
82 | fprintf(stderr, " [ query_rss { on | off} ]\n"); | |
07fa9c15 | 83 | fprintf(stderr, " [ state { auto | enable | disable} ] ]\n"); |
b6d77d9e | 84 | fprintf(stderr, " [ trust { on | off} ] ]\n"); |
104444c2 | 85 | fprintf(stderr, " [ master DEVICE ][ vrf NAME ]\n"); |
a1e191b9 | 86 | fprintf(stderr, " [ nomaster ]\n"); |
8e12bc0a | 87 | fprintf(stderr, " [ addrgenmode { eui64 | none | stable_secret | random } ]\n"); |
18864827 | 88 | fprintf(stderr, " [ protodown { on | off } ]\n"); |
104444c2 | 89 | fprintf(stderr, " ip link show [ DEVICE | group GROUP ] [up] [master DEV] [vrf NAME] [type TYPE]\n"); |
750a405a SH |
90 | |
91 | if (iplink_have_newlink()) { | |
561e650e | 92 | fprintf(stderr, " ip link help [ TYPE ]\n"); |
750a405a | 93 | fprintf(stderr, "\n"); |
1253a10a | 94 | fprintf(stderr, "TYPE := { vlan | veth | vcan | dummy | ifb | macvlan | macvtap |\n"); |
2b70fe15 | 95 | fprintf(stderr, " bridge | bond | ipoib | ip6tnl | ipip | sit | vxlan |\n"); |
620ddeda | 96 | fprintf(stderr, " gre | gretap | ip6gre | ip6gretap | vti | nlmon |\n"); |
15faa0a3 | 97 | fprintf(stderr, " bond_slave | ipvlan | geneve | bridge_slave | vrf }\n"); |
750a405a | 98 | } |
aba5acdf SH |
99 | exit(-1); |
100 | } | |
101 | ||
102 | static void usage(void) | |
103 | { | |
104 | iplink_usage(); | |
105 | } | |
106 | ||
14645ec2 | 107 | static int on_off(const char *msg, const char *realval) |
aba5acdf | 108 | { |
6c5ffb9a SH |
109 | fprintf(stderr, |
110 | "Error: argument of \"%s\" must be \"on\" or \"off\", not \"%s\"\n", | |
111 | msg, realval); | |
aba5acdf SH |
112 | return -1; |
113 | } | |
114 | ||
1d934839 PM |
115 | static void *BODY; /* cached dlopen(NULL) handle */ |
116 | static struct link_util *linkutil_list; | |
117 | ||
fbea6115 | 118 | static struct link_util *__get_link_kind(const char *id, bool slave) |
1d934839 PM |
119 | { |
120 | void *dlh; | |
121 | char buf[256]; | |
122 | struct link_util *l; | |
123 | ||
124 | for (l = linkutil_list; l; l = l->next) | |
fbea6115 JP |
125 | if (strcmp(l->id, id) == 0 && |
126 | l->slave == slave) | |
1d934839 PM |
127 | return l; |
128 | ||
5e3bb534 | 129 | snprintf(buf, sizeof(buf), LIBDIR "/ip/link_%s.so", id); |
1d934839 PM |
130 | dlh = dlopen(buf, RTLD_LAZY); |
131 | if (dlh == NULL) { | |
132 | /* look in current binary, only open once */ | |
133 | dlh = BODY; | |
134 | if (dlh == NULL) { | |
135 | dlh = BODY = dlopen(NULL, RTLD_LAZY); | |
136 | if (dlh == NULL) | |
137 | return NULL; | |
138 | } | |
139 | } | |
140 | ||
fbea6115 JP |
141 | if (slave) |
142 | snprintf(buf, sizeof(buf), "%s_slave_link_util", id); | |
143 | else | |
144 | snprintf(buf, sizeof(buf), "%s_link_util", id); | |
1d934839 PM |
145 | l = dlsym(dlh, buf); |
146 | if (l == NULL) | |
147 | return NULL; | |
148 | ||
149 | l->next = linkutil_list; | |
150 | linkutil_list = l; | |
151 | return l; | |
152 | } | |
153 | ||
fbea6115 JP |
154 | struct link_util *get_link_kind(const char *id) |
155 | { | |
156 | return __get_link_kind(id, false); | |
157 | } | |
158 | ||
159 | struct link_util *get_link_slave_kind(const char *id) | |
160 | { | |
161 | return __get_link_kind(id, true); | |
162 | } | |
163 | ||
d1f28cf1 | 164 | static int get_link_mode(const char *mode) |
82499282 | 165 | { |
4ccfb44d | 166 | if (strcasecmp(mode, "default") == 0) |
82499282 | 167 | return IF_LINK_MODE_DEFAULT; |
4ccfb44d | 168 | if (strcasecmp(mode, "dormant") == 0) |
82499282 SH |
169 | return IF_LINK_MODE_DORMANT; |
170 | return -1; | |
171 | } | |
172 | ||
ff7c2084 JP |
173 | static int get_addr_gen_mode(const char *mode) |
174 | { | |
175 | if (strcasecmp(mode, "eui64") == 0) | |
176 | return IN6_ADDR_GEN_MODE_EUI64; | |
177 | if (strcasecmp(mode, "none") == 0) | |
178 | return IN6_ADDR_GEN_MODE_NONE; | |
8e098dd8 BM |
179 | if (strcasecmp(mode, "stable_secret") == 0) |
180 | return IN6_ADDR_GEN_MODE_STABLE_PRIVACY; | |
8e12bc0a BM |
181 | if (strcasecmp(mode, "random") == 0) |
182 | return IN6_ADDR_GEN_MODE_RANDOM; | |
ff7c2084 JP |
183 | return -1; |
184 | } | |
185 | ||
1d934839 PM |
186 | #if IPLINK_IOCTL_COMPAT |
187 | static int have_rtnl_newlink = -1; | |
188 | ||
189 | static int accept_msg(const struct sockaddr_nl *who, | |
0628cddd | 190 | struct rtnl_ctrl_data *ctrl, |
1d934839 PM |
191 | struct nlmsghdr *n, void *arg) |
192 | { | |
193 | struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n); | |
194 | ||
66e529f5 PM |
195 | if (n->nlmsg_type == NLMSG_ERROR && |
196 | (err->error == -EOPNOTSUPP || err->error == -EINVAL)) | |
1d934839 PM |
197 | have_rtnl_newlink = 0; |
198 | else | |
199 | have_rtnl_newlink = 1; | |
200 | return -1; | |
201 | } | |
202 | ||
203 | static int iplink_have_newlink(void) | |
204 | { | |
205 | struct { | |
206 | struct nlmsghdr n; | |
207 | struct ifinfomsg i; | |
208 | char buf[1024]; | |
209 | } req; | |
210 | ||
211 | if (have_rtnl_newlink < 0) { | |
212 | memset(&req, 0, sizeof(req)); | |
213 | ||
214 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
215 | req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; | |
216 | req.n.nlmsg_type = RTM_NEWLINK; | |
217 | req.i.ifi_family = AF_UNSPEC; | |
218 | ||
d2468da0 SH |
219 | if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) { |
220 | perror("request send failed"); | |
221 | exit(1); | |
222 | } | |
1d934839 PM |
223 | rtnl_listen(&rth, accept_msg, NULL); |
224 | } | |
225 | return have_rtnl_newlink; | |
226 | } | |
227 | #else /* IPLINK_IOCTL_COMPAT */ | |
228 | static int iplink_have_newlink(void) | |
229 | { | |
230 | return 1; | |
231 | } | |
232 | #endif /* ! IPLINK_IOCTL_COMPAT */ | |
233 | ||
909dfe2c PE |
234 | struct iplink_req { |
235 | struct nlmsghdr n; | |
236 | struct ifinfomsg i; | |
237 | char buf[1024]; | |
238 | }; | |
239 | ||
8fe58d58 PS |
240 | static int nl_get_ll_addr_len(unsigned int dev_index) |
241 | { | |
242 | int len; | |
243 | struct iplink_req req = { | |
244 | .n = { | |
245 | .nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), | |
246 | .nlmsg_type = RTM_GETLINK, | |
247 | .nlmsg_flags = NLM_F_REQUEST | |
248 | }, | |
249 | .i = { | |
250 | .ifi_family = preferred_family, | |
251 | .ifi_index = dev_index, | |
252 | } | |
253 | }; | |
254 | struct rtattr *tb[IFLA_MAX+1]; | |
255 | ||
256 | if (rtnl_talk(&rth, &req.n, &req.n, sizeof(req)) < 0) | |
257 | return -1; | |
258 | ||
259 | len = req.n.nlmsg_len - NLMSG_LENGTH(sizeof(req.i)); | |
260 | if (len < 0) | |
261 | return -1; | |
262 | ||
263 | parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(&req.i), len, NLA_F_NESTED); | |
264 | if (!tb[IFLA_ADDRESS]) | |
265 | return -1; | |
266 | ||
267 | return RTA_PAYLOAD(tb[IFLA_ADDRESS]); | |
268 | } | |
269 | ||
d1f28cf1 | 270 | static int iplink_parse_vf(int vf, int *argcp, char ***argvp, |
f89a2a05 | 271 | struct iplink_req *req, int dev_index) |
3fd86630 | 272 | { |
f89a2a05 SC |
273 | char new_rate_api = 0, count = 0, override_legacy_rate = 0; |
274 | struct ifla_vf_rate tivt; | |
3fd86630 CW |
275 | int len, argc = *argcp; |
276 | char **argv = *argvp; | |
277 | struct rtattr *vfinfo; | |
1598b9ef | 278 | |
f89a2a05 SC |
279 | tivt.min_tx_rate = -1; |
280 | tivt.max_tx_rate = -1; | |
281 | ||
1598b9ef | 282 | vfinfo = addattr_nest(&req->n, sizeof(*req), IFLA_VF_INFO); |
3fd86630 | 283 | |
f89a2a05 SC |
284 | while (NEXT_ARG_OK()) { |
285 | NEXT_ARG(); | |
286 | count++; | |
287 | if (!matches(*argv, "max_tx_rate")) { | |
288 | /* new API in use */ | |
289 | new_rate_api = 1; | |
290 | /* override legacy rate */ | |
291 | override_legacy_rate = 1; | |
292 | } else if (!matches(*argv, "min_tx_rate")) { | |
293 | /* new API in use */ | |
294 | new_rate_api = 1; | |
295 | } | |
296 | } | |
297 | ||
298 | while (count--) { | |
299 | /* rewind arg */ | |
300 | PREV_ARG(); | |
301 | } | |
302 | ||
3fd86630 CW |
303 | while (NEXT_ARG_OK()) { |
304 | NEXT_ARG(); | |
1598b9ef | 305 | if (matches(*argv, "mac") == 0) { |
a89193a7 | 306 | struct ifla_vf_mac ivm = { 0 }; |
8fe58d58 | 307 | int halen = nl_get_ll_addr_len(dev_index); |
6c5ffb9a | 308 | |
3fd86630 | 309 | NEXT_ARG(); |
1598b9ef SH |
310 | ivm.vf = vf; |
311 | len = ll_addr_a2n((char *)ivm.mac, 32, *argv); | |
312 | if (len < 0) | |
313 | return -1; | |
8fe58d58 PS |
314 | if (halen > 0 && len != halen) { |
315 | fprintf(stderr, | |
316 | "Invalid address length %d - must be %d bytes\n", | |
317 | len, halen); | |
318 | return -1; | |
319 | } | |
1598b9ef | 320 | addattr_l(&req->n, sizeof(*req), IFLA_VF_MAC, &ivm, sizeof(ivm)); |
3fd86630 | 321 | } else if (matches(*argv, "vlan") == 0) { |
1598b9ef | 322 | struct ifla_vf_vlan ivv; |
6c5ffb9a | 323 | |
3fd86630 | 324 | NEXT_ARG(); |
6c5ffb9a | 325 | if (get_unsigned(&ivv.vlan, *argv, 0)) |
1598b9ef | 326 | invarg("Invalid \"vlan\" value\n", *argv); |
6c5ffb9a | 327 | |
1598b9ef SH |
328 | ivv.vf = vf; |
329 | ivv.qos = 0; | |
3fd86630 CW |
330 | if (NEXT_ARG_OK()) { |
331 | NEXT_ARG(); | |
332 | if (matches(*argv, "qos") == 0) { | |
333 | NEXT_ARG(); | |
6c5ffb9a | 334 | if (get_unsigned(&ivv.qos, *argv, 0)) |
1598b9ef | 335 | invarg("Invalid \"qos\" value\n", *argv); |
3fd86630 CW |
336 | } else { |
337 | /* rewind arg */ | |
338 | PREV_ARG(); | |
339 | } | |
340 | } | |
1598b9ef | 341 | addattr_l(&req->n, sizeof(*req), IFLA_VF_VLAN, &ivv, sizeof(ivv)); |
3fd86630 | 342 | } else if (matches(*argv, "rate") == 0) { |
1598b9ef | 343 | struct ifla_vf_tx_rate ivt; |
6c5ffb9a | 344 | |
3fd86630 | 345 | NEXT_ARG(); |
6c5ffb9a | 346 | if (get_unsigned(&ivt.rate, *argv, 0)) |
1598b9ef | 347 | invarg("Invalid \"rate\" value\n", *argv); |
6c5ffb9a | 348 | |
1598b9ef | 349 | ivt.vf = vf; |
f89a2a05 SC |
350 | if (!new_rate_api) |
351 | addattr_l(&req->n, sizeof(*req), | |
352 | IFLA_VF_TX_RATE, &ivt, sizeof(ivt)); | |
353 | else if (!override_legacy_rate) | |
354 | tivt.max_tx_rate = ivt.rate; | |
355 | ||
356 | } else if (matches(*argv, "max_tx_rate") == 0) { | |
357 | NEXT_ARG(); | |
358 | if (get_unsigned(&tivt.max_tx_rate, *argv, 0)) | |
359 | invarg("Invalid \"max tx rate\" value\n", | |
360 | *argv); | |
361 | tivt.vf = vf; | |
362 | ||
363 | } else if (matches(*argv, "min_tx_rate") == 0) { | |
364 | NEXT_ARG(); | |
365 | if (get_unsigned(&tivt.min_tx_rate, *argv, 0)) | |
366 | invarg("Invalid \"min tx rate\" value\n", | |
367 | *argv); | |
368 | tivt.vf = vf; | |
af89576d | 369 | |
7b8179c7 GR |
370 | } else if (matches(*argv, "spoofchk") == 0) { |
371 | struct ifla_vf_spoofchk ivs; | |
6c5ffb9a | 372 | |
7b8179c7 GR |
373 | NEXT_ARG(); |
374 | if (matches(*argv, "on") == 0) | |
375 | ivs.setting = 1; | |
376 | else if (matches(*argv, "off") == 0) | |
377 | ivs.setting = 0; | |
378 | else | |
ff1e35ed | 379 | return on_off("spoofchk", *argv); |
7b8179c7 GR |
380 | ivs.vf = vf; |
381 | addattr_l(&req->n, sizeof(*req), IFLA_VF_SPOOFCHK, &ivs, sizeof(ivs)); | |
382 | ||
6c55c8c4 VZ |
383 | } else if (matches(*argv, "query_rss") == 0) { |
384 | struct ifla_vf_rss_query_en ivs; | |
6c5ffb9a | 385 | |
6c55c8c4 VZ |
386 | NEXT_ARG(); |
387 | if (matches(*argv, "on") == 0) | |
388 | ivs.setting = 1; | |
389 | else if (matches(*argv, "off") == 0) | |
390 | ivs.setting = 0; | |
391 | else | |
ff1e35ed | 392 | return on_off("query_rss", *argv); |
6c55c8c4 VZ |
393 | ivs.vf = vf; |
394 | addattr_l(&req->n, sizeof(*req), IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(ivs)); | |
395 | ||
b6d77d9e HS |
396 | } else if (matches(*argv, "trust") == 0) { |
397 | struct ifla_vf_trust ivt; | |
56f5daac | 398 | |
b6d77d9e HS |
399 | NEXT_ARG(); |
400 | if (matches(*argv, "on") == 0) | |
401 | ivt.setting = 1; | |
402 | else if (matches(*argv, "off") == 0) | |
403 | ivt.setting = 0; | |
404 | else | |
405 | invarg("Invalid \"trust\" value\n", *argv); | |
406 | ivt.vf = vf; | |
407 | addattr_l(&req->n, sizeof(*req), IFLA_VF_TRUST, &ivt, sizeof(ivt)); | |
408 | ||
07fa9c15 RE |
409 | } else if (matches(*argv, "state") == 0) { |
410 | struct ifla_vf_link_state ivl; | |
6c5ffb9a | 411 | |
07fa9c15 RE |
412 | NEXT_ARG(); |
413 | if (matches(*argv, "auto") == 0) | |
414 | ivl.link_state = IFLA_VF_LINK_STATE_AUTO; | |
415 | else if (matches(*argv, "enable") == 0) | |
416 | ivl.link_state = IFLA_VF_LINK_STATE_ENABLE; | |
417 | else if (matches(*argv, "disable") == 0) | |
418 | ivl.link_state = IFLA_VF_LINK_STATE_DISABLE; | |
419 | else | |
420 | invarg("Invalid \"state\" value\n", *argv); | |
421 | ivl.vf = vf; | |
422 | addattr_l(&req->n, sizeof(*req), IFLA_VF_LINK_STATE, &ivl, sizeof(ivl)); | |
3fd86630 CW |
423 | } else { |
424 | /* rewind arg */ | |
425 | PREV_ARG(); | |
426 | break; | |
427 | } | |
428 | } | |
429 | ||
f89a2a05 SC |
430 | if (new_rate_api) { |
431 | int tmin, tmax; | |
9a02651a | 432 | |
f89a2a05 SC |
433 | if (tivt.min_tx_rate == -1 || tivt.max_tx_rate == -1) { |
434 | ipaddr_get_vf_rate(tivt.vf, &tmin, &tmax, dev_index); | |
435 | if (tivt.min_tx_rate == -1) | |
436 | tivt.min_tx_rate = tmin; | |
437 | if (tivt.max_tx_rate == -1) | |
438 | tivt.max_tx_rate = tmax; | |
439 | } | |
440 | addattr_l(&req->n, sizeof(*req), IFLA_VF_RATE, &tivt, | |
441 | sizeof(tivt)); | |
442 | } | |
443 | ||
3fd86630 CW |
444 | if (argc == *argcp) |
445 | incomplete_command(); | |
446 | ||
1598b9ef | 447 | addattr_nest_end(&req->n, vfinfo); |
3fd86630 CW |
448 | |
449 | *argcp = argc; | |
450 | *argvp = argv; | |
1598b9ef | 451 | return 0; |
3fd86630 CW |
452 | } |
453 | ||
909dfe2c | 454 | int iplink_parse(int argc, char **argv, struct iplink_req *req, |
6c5ffb9a SH |
455 | char **name, char **type, char **link, char **dev, |
456 | int *group, int *index) | |
1d934839 | 457 | { |
909dfe2c PE |
458 | int ret, len; |
459 | char abuf[32]; | |
1d934839 PM |
460 | int qlen = -1; |
461 | int mtu = -1; | |
e2613dc8 | 462 | int netns = -1; |
ae7229d5 | 463 | int vf = -1; |
d992f3e6 JP |
464 | int numtxqueues = -1; |
465 | int numrxqueues = -1; | |
9a02651a | 466 | int dev_index = 0; |
ccdcbf35 | 467 | int link_netnsid = -1; |
8fe58d58 | 468 | int addr_len = 0; |
1d934839 | 469 | |
db02608b | 470 | *group = -1; |
909dfe2c | 471 | ret = argc; |
1d934839 PM |
472 | |
473 | while (argc > 0) { | |
474 | if (strcmp(*argv, "up") == 0) { | |
909dfe2c PE |
475 | req->i.ifi_change |= IFF_UP; |
476 | req->i.ifi_flags |= IFF_UP; | |
1d934839 | 477 | } else if (strcmp(*argv, "down") == 0) { |
909dfe2c PE |
478 | req->i.ifi_change |= IFF_UP; |
479 | req->i.ifi_flags &= ~IFF_UP; | |
1d934839 PM |
480 | } else if (strcmp(*argv, "name") == 0) { |
481 | NEXT_ARG(); | |
909dfe2c | 482 | *name = *argv; |
5e25cf77 PE |
483 | } else if (strcmp(*argv, "index") == 0) { |
484 | NEXT_ARG(); | |
485 | *index = atoi(*argv); | |
3c682146 WC |
486 | if (*index < 0) |
487 | invarg("Invalid \"index\" value", *argv); | |
1d934839 PM |
488 | } else if (matches(*argv, "link") == 0) { |
489 | NEXT_ARG(); | |
909dfe2c | 490 | *link = *argv; |
1d934839 PM |
491 | } else if (matches(*argv, "address") == 0) { |
492 | NEXT_ARG(); | |
8fe58d58 | 493 | addr_len = ll_addr_a2n(abuf, sizeof(abuf), *argv); |
0aae2346 | 494 | if (addr_len < 0) |
cb2eb999 | 495 | return -1; |
8fe58d58 | 496 | addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, addr_len); |
1d934839 | 497 | } else if (matches(*argv, "broadcast") == 0 || |
6c5ffb9a | 498 | strcmp(*argv, "brd") == 0) { |
1d934839 PM |
499 | NEXT_ARG(); |
500 | len = ll_addr_a2n(abuf, sizeof(abuf), *argv); | |
cb2eb999 AH |
501 | if (len < 0) |
502 | return -1; | |
909dfe2c | 503 | addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len); |
1d934839 | 504 | } else if (matches(*argv, "txqueuelen") == 0 || |
6c5ffb9a SH |
505 | strcmp(*argv, "qlen") == 0 || |
506 | matches(*argv, "txqlen") == 0) { | |
1d934839 PM |
507 | NEXT_ARG(); |
508 | if (qlen != -1) | |
509 | duparg("txqueuelen", *argv); | |
510 | if (get_integer(&qlen, *argv, 0)) | |
511 | invarg("Invalid \"txqueuelen\" value\n", *argv); | |
909dfe2c | 512 | addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4); |
1d934839 PM |
513 | } else if (strcmp(*argv, "mtu") == 0) { |
514 | NEXT_ARG(); | |
515 | if (mtu != -1) | |
516 | duparg("mtu", *argv); | |
517 | if (get_integer(&mtu, *argv, 0)) | |
518 | invarg("Invalid \"mtu\" value\n", *argv); | |
909dfe2c | 519 | addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4); |
4b726cb1 SH |
520 | } else if (strcmp(*argv, "netns") == 0) { |
521 | NEXT_ARG(); | |
522 | if (netns != -1) | |
523 | duparg("netns", *argv); | |
6c5ffb9a SH |
524 | netns = netns_get_fd(*argv); |
525 | if (netns >= 0) | |
0dc34c77 EB |
526 | addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_FD, &netns, 4); |
527 | else if (get_integer(&netns, *argv, 0) == 0) | |
528 | addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4); | |
529 | else | |
4b726cb1 | 530 | invarg("Invalid \"netns\" value\n", *argv); |
1d934839 PM |
531 | } else if (strcmp(*argv, "multicast") == 0) { |
532 | NEXT_ARG(); | |
909dfe2c | 533 | req->i.ifi_change |= IFF_MULTICAST; |
6c5ffb9a SH |
534 | |
535 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 536 | req->i.ifi_flags |= IFF_MULTICAST; |
6c5ffb9a | 537 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 538 | req->i.ifi_flags &= ~IFF_MULTICAST; |
6c5ffb9a | 539 | else |
14645ec2 | 540 | return on_off("multicast", *argv); |
1d934839 PM |
541 | } else if (strcmp(*argv, "allmulticast") == 0) { |
542 | NEXT_ARG(); | |
909dfe2c | 543 | req->i.ifi_change |= IFF_ALLMULTI; |
6c5ffb9a SH |
544 | |
545 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 546 | req->i.ifi_flags |= IFF_ALLMULTI; |
6c5ffb9a | 547 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 548 | req->i.ifi_flags &= ~IFF_ALLMULTI; |
6c5ffb9a | 549 | else |
14645ec2 | 550 | return on_off("allmulticast", *argv); |
1d934839 PM |
551 | } else if (strcmp(*argv, "promisc") == 0) { |
552 | NEXT_ARG(); | |
909dfe2c | 553 | req->i.ifi_change |= IFF_PROMISC; |
6c5ffb9a SH |
554 | |
555 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 556 | req->i.ifi_flags |= IFF_PROMISC; |
6c5ffb9a | 557 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 558 | req->i.ifi_flags &= ~IFF_PROMISC; |
6c5ffb9a | 559 | else |
14645ec2 | 560 | return on_off("promisc", *argv); |
1d934839 PM |
561 | } else if (strcmp(*argv, "trailers") == 0) { |
562 | NEXT_ARG(); | |
909dfe2c | 563 | req->i.ifi_change |= IFF_NOTRAILERS; |
6c5ffb9a SH |
564 | |
565 | if (strcmp(*argv, "off") == 0) | |
909dfe2c | 566 | req->i.ifi_flags |= IFF_NOTRAILERS; |
6c5ffb9a | 567 | else if (strcmp(*argv, "on") == 0) |
909dfe2c | 568 | req->i.ifi_flags &= ~IFF_NOTRAILERS; |
6c5ffb9a | 569 | else |
14645ec2 | 570 | return on_off("trailers", *argv); |
1d934839 PM |
571 | } else if (strcmp(*argv, "arp") == 0) { |
572 | NEXT_ARG(); | |
909dfe2c | 573 | req->i.ifi_change |= IFF_NOARP; |
6c5ffb9a SH |
574 | |
575 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 576 | req->i.ifi_flags &= ~IFF_NOARP; |
6c5ffb9a | 577 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 578 | req->i.ifi_flags |= IFF_NOARP; |
6c5ffb9a | 579 | else |
e543a6a8 | 580 | return on_off("arp", *argv); |
ae7229d5 | 581 | } else if (strcmp(*argv, "vf") == 0) { |
1598b9ef | 582 | struct rtattr *vflist; |
6c5ffb9a | 583 | |
ae7229d5 | 584 | NEXT_ARG(); |
6c5ffb9a | 585 | if (get_integer(&vf, *argv, 0)) |
ae7229d5 | 586 | invarg("Invalid \"vf\" value\n", *argv); |
6c5ffb9a | 587 | |
1598b9ef SH |
588 | vflist = addattr_nest(&req->n, sizeof(*req), |
589 | IFLA_VFINFO_LIST); | |
9a02651a SH |
590 | if (dev_index == 0) |
591 | missarg("dev"); | |
592 | ||
f89a2a05 | 593 | len = iplink_parse_vf(vf, &argc, &argv, req, dev_index); |
1598b9ef SH |
594 | if (len < 0) |
595 | return -1; | |
596 | addattr_nest_end(&req->n, vflist); | |
a1e191b9 JP |
597 | } else if (matches(*argv, "master") == 0) { |
598 | int ifindex; | |
6c5ffb9a | 599 | |
a1e191b9 JP |
600 | NEXT_ARG(); |
601 | ifindex = ll_name_to_index(*argv); | |
602 | if (!ifindex) | |
603 | invarg("Device does not exist\n", *argv); | |
604 | addattr_l(&req->n, sizeof(*req), IFLA_MASTER, | |
605 | &ifindex, 4); | |
104444c2 DA |
606 | } else if (strcmp(*argv, "vrf") == 0) { |
607 | int ifindex; | |
608 | ||
609 | NEXT_ARG(); | |
610 | ifindex = ll_name_to_index(*argv); | |
611 | if (!ifindex) | |
612 | invarg("Not a valid VRF name\n", *argv); | |
613 | if (!name_is_vrf(*argv)) | |
614 | invarg("Not a valid VRF name\n", *argv); | |
615 | addattr_l(&req->n, sizeof(*req), IFLA_MASTER, | |
616 | &ifindex, sizeof(ifindex)); | |
a1e191b9 JP |
617 | } else if (matches(*argv, "nomaster") == 0) { |
618 | int ifindex = 0; | |
6c5ffb9a | 619 | |
a1e191b9 JP |
620 | addattr_l(&req->n, sizeof(*req), IFLA_MASTER, |
621 | &ifindex, 4); | |
1d934839 PM |
622 | } else if (matches(*argv, "dynamic") == 0) { |
623 | NEXT_ARG(); | |
909dfe2c | 624 | req->i.ifi_change |= IFF_DYNAMIC; |
6c5ffb9a SH |
625 | |
626 | if (strcmp(*argv, "on") == 0) | |
909dfe2c | 627 | req->i.ifi_flags |= IFF_DYNAMIC; |
6c5ffb9a | 628 | else if (strcmp(*argv, "off") == 0) |
909dfe2c | 629 | req->i.ifi_flags &= ~IFF_DYNAMIC; |
6c5ffb9a | 630 | else |
14645ec2 | 631 | return on_off("dynamic", *argv); |
1d934839 PM |
632 | } else if (matches(*argv, "type") == 0) { |
633 | NEXT_ARG(); | |
909dfe2c | 634 | *type = *argv; |
1d934839 PM |
635 | argc--; argv++; |
636 | break; | |
ace9c961 SH |
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; | |
db02608b VD |
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("Invalid \"group\" value\n", *argv); | |
82499282 SH |
649 | } else if (strcmp(*argv, "mode") == 0) { |
650 | int mode; | |
6c5ffb9a | 651 | |
82499282 | 652 | NEXT_ARG(); |
4ccfb44d | 653 | mode = get_link_mode(*argv); |
82499282 SH |
654 | if (mode < 0) |
655 | invarg("Invalid link mode\n", *argv); | |
656 | addattr8(&req->n, sizeof(*req), IFLA_LINKMODE, mode); | |
4f2fdd44 SH |
657 | } else if (strcmp(*argv, "state") == 0) { |
658 | int state; | |
6c5ffb9a | 659 | |
4f2fdd44 SH |
660 | NEXT_ARG(); |
661 | state = get_operstate(*argv); | |
662 | if (state < 0) | |
663 | invarg("Invalid operstate\n", *argv); | |
664 | ||
665 | addattr8(&req->n, sizeof(*req), IFLA_OPERSTATE, state); | |
d992f3e6 JP |
666 | } else if (matches(*argv, "numtxqueues") == 0) { |
667 | NEXT_ARG(); | |
668 | if (numtxqueues != -1) | |
669 | duparg("numtxqueues", *argv); | |
670 | if (get_integer(&numtxqueues, *argv, 0)) | |
671 | invarg("Invalid \"numtxqueues\" value\n", *argv); | |
672 | addattr_l(&req->n, sizeof(*req), IFLA_NUM_TX_QUEUES, | |
673 | &numtxqueues, 4); | |
674 | } else if (matches(*argv, "numrxqueues") == 0) { | |
675 | NEXT_ARG(); | |
676 | if (numrxqueues != -1) | |
677 | duparg("numrxqueues", *argv); | |
678 | if (get_integer(&numrxqueues, *argv, 0)) | |
679 | invarg("Invalid \"numrxqueues\" value\n", *argv); | |
680 | addattr_l(&req->n, sizeof(*req), IFLA_NUM_RX_QUEUES, | |
681 | &numrxqueues, 4); | |
ff7c2084 JP |
682 | } else if (matches(*argv, "addrgenmode") == 0) { |
683 | struct rtattr *afs, *afs6; | |
684 | int mode; | |
6c5ffb9a | 685 | |
ff7c2084 JP |
686 | NEXT_ARG(); |
687 | mode = get_addr_gen_mode(*argv); | |
688 | if (mode < 0) | |
689 | invarg("Invalid address generation mode\n", *argv); | |
690 | afs = addattr_nest(&req->n, sizeof(*req), IFLA_AF_SPEC); | |
691 | afs6 = addattr_nest(&req->n, sizeof(*req), AF_INET6); | |
692 | addattr8(&req->n, sizeof(*req), IFLA_INET6_ADDR_GEN_MODE, mode); | |
693 | addattr_nest_end(&req->n, afs6); | |
694 | addattr_nest_end(&req->n, afs); | |
ccdcbf35 ND |
695 | } else if (matches(*argv, "link-netnsid") == 0) { |
696 | NEXT_ARG(); | |
697 | if (link_netnsid != -1) | |
698 | duparg("link-netnsid", *argv); | |
699 | if (get_integer(&link_netnsid, *argv, 0)) | |
700 | invarg("Invalid \"link-netnsid\" value\n", *argv); | |
701 | addattr32(&req->n, sizeof(*req), IFLA_LINK_NETNSID, | |
702 | link_netnsid); | |
18864827 AK |
703 | } else if (strcmp(*argv, "protodown") == 0) { |
704 | unsigned int proto_down; | |
705 | ||
706 | NEXT_ARG(); | |
707 | if (strcmp(*argv, "on") == 0) | |
708 | proto_down = 1; | |
709 | else if (strcmp(*argv, "off") == 0) | |
710 | proto_down = 0; | |
711 | else | |
712 | return on_off("protodown", *argv); | |
713 | addattr8(&req->n, sizeof(*req), IFLA_PROTO_DOWN, | |
714 | proto_down); | |
1d934839 | 715 | } else { |
05325552 PM |
716 | if (matches(*argv, "help") == 0) |
717 | usage(); | |
940a96e6 PS |
718 | |
719 | if (strcmp(*argv, "dev") == 0) | |
720 | NEXT_ARG(); | |
909dfe2c | 721 | if (*dev) |
1d934839 | 722 | duparg2("dev", *argv); |
909dfe2c | 723 | *dev = *argv; |
f89a2a05 | 724 | dev_index = ll_name_to_index(*dev); |
1d934839 PM |
725 | } |
726 | argc--; argv++; | |
727 | } | |
728 | ||
8fe58d58 PS |
729 | if (dev_index && addr_len) { |
730 | int halen = nl_get_ll_addr_len(dev_index); | |
731 | if (halen >= 0 && halen != addr_len) { | |
732 | fprintf(stderr, | |
733 | "Invalid address length %d - must be %d bytes\n", | |
734 | addr_len, halen); | |
735 | return -1; | |
736 | } | |
737 | } | |
738 | ||
909dfe2c PE |
739 | return ret - argc; |
740 | } | |
741 | ||
742 | static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) | |
743 | { | |
744 | int len; | |
745 | char *dev = NULL; | |
746 | char *name = NULL; | |
747 | char *link = NULL; | |
748 | char *type = NULL; | |
3c682146 | 749 | int index = -1; |
db02608b | 750 | int group; |
909dfe2c PE |
751 | struct link_util *lu = NULL; |
752 | struct iplink_req req; | |
753 | int ret; | |
754 | ||
755 | memset(&req, 0, sizeof(req)); | |
756 | ||
757 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
758 | req.n.nlmsg_flags = NLM_F_REQUEST|flags; | |
759 | req.n.nlmsg_type = cmd; | |
760 | req.i.ifi_family = preferred_family; | |
761 | ||
5e25cf77 | 762 | ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev, &group, &index); |
909dfe2c PE |
763 | if (ret < 0) |
764 | return ret; | |
765 | ||
766 | argc -= ret; | |
767 | argv += ret; | |
db02608b VD |
768 | |
769 | if (group != -1) { | |
770 | if (dev) | |
771 | addattr_l(&req.n, sizeof(req), IFLA_GROUP, | |
772 | &group, sizeof(group)); | |
773 | else { | |
774 | if (argc) { | |
56f5daac | 775 | fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"ip link " |
db02608b VD |
776 | "help\".\n", *argv); |
777 | return -1; | |
778 | } | |
779 | if (flags & NLM_F_CREATE) { | |
56f5daac | 780 | fprintf(stderr, "group cannot be used when creating devices.\n"); |
db02608b VD |
781 | return -1; |
782 | } | |
783 | ||
784 | req.i.ifi_index = 0; | |
785 | addattr32(&req.n, sizeof(req), IFLA_GROUP, group); | |
c079e121 | 786 | if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) |
f921f567 | 787 | return -2; |
db02608b VD |
788 | return 0; |
789 | } | |
790 | } | |
791 | ||
1d934839 PM |
792 | if (!(flags & NLM_F_CREATE)) { |
793 | if (!dev) { | |
56f5daac | 794 | fprintf(stderr, "Not enough information: \"dev\" argument is required.\n"); |
1d934839 PM |
795 | exit(-1); |
796 | } | |
3c682146 | 797 | if (cmd == RTM_NEWLINK && index != -1) { |
56f5daac | 798 | fprintf(stderr, "index can be used only when creating devices.\n"); |
3c682146 WC |
799 | exit(-1); |
800 | } | |
1d934839 PM |
801 | |
802 | req.i.ifi_index = ll_name_to_index(dev); | |
803 | if (req.i.ifi_index == 0) { | |
804 | fprintf(stderr, "Cannot find device \"%s\"\n", dev); | |
805 | return -1; | |
806 | } | |
807 | } else { | |
808 | /* Allow "ip link add dev" and "ip link add name" */ | |
809 | if (!name) | |
810 | name = dev; | |
811 | ||
812 | if (link) { | |
813 | int ifindex; | |
814 | ||
815 | ifindex = ll_name_to_index(link); | |
816 | if (ifindex == 0) { | |
817 | fprintf(stderr, "Cannot find device \"%s\"\n", | |
818 | link); | |
819 | return -1; | |
820 | } | |
821 | addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4); | |
822 | } | |
5e25cf77 | 823 | |
68ac9ab3 AW |
824 | if (index == -1) |
825 | req.i.ifi_index = 0; | |
826 | else | |
827 | req.i.ifi_index = index; | |
1d934839 PM |
828 | } |
829 | ||
830 | if (name) { | |
831 | len = strlen(name) + 1; | |
ca78b0e7 PM |
832 | if (len == 1) |
833 | invarg("\"\" is not a valid device identifier\n", "name"); | |
1d934839 | 834 | if (len > IFNAMSIZ) |
ca78b0e7 | 835 | invarg("\"name\" too long\n", name); |
1d934839 PM |
836 | addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len); |
837 | } | |
838 | ||
09fa3279 | 839 | if (type) { |
a1e2e5fc | 840 | struct rtattr *linkinfo; |
620ddeda NA |
841 | char slavebuf[128], *ulinep = strchr(type, '_'); |
842 | int iflatype; | |
843 | ||
a1e2e5fc | 844 | linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); |
09fa3279 SH |
845 | addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type, |
846 | strlen(type)); | |
847 | ||
620ddeda NA |
848 | if (ulinep && !strcmp(ulinep, "_slave")) { |
849 | strncpy(slavebuf, type, sizeof(slavebuf)); | |
850 | slavebuf[sizeof(slavebuf) - 1] = '\0'; | |
851 | ulinep = strchr(slavebuf, '_'); | |
852 | /* check in case it was after sizeof(slavebuf) - 1*/ | |
853 | if (ulinep) | |
854 | *ulinep = '\0'; | |
855 | lu = get_link_slave_kind(slavebuf); | |
856 | iflatype = IFLA_INFO_SLAVE_DATA; | |
857 | } else { | |
858 | lu = get_link_kind(type); | |
859 | iflatype = IFLA_INFO_DATA; | |
860 | } | |
09fa3279 | 861 | if (lu && argc) { |
a1e2e5fc | 862 | struct rtattr *data = addattr_nest(&req.n, sizeof(req), iflatype); |
09fa3279 SH |
863 | |
864 | if (lu->parse_opt && | |
865 | lu->parse_opt(lu, argc, argv, &req.n)) | |
866 | return -1; | |
867 | ||
a1e2e5fc | 868 | addattr_nest_end(&req.n, data); |
09fa3279 SH |
869 | } else if (argc) { |
870 | if (matches(*argv, "help") == 0) | |
871 | usage(); | |
56f5daac SH |
872 | fprintf(stderr, "Garbage instead of arguments \"%s ...\". Try \"ip link help\".\n", |
873 | *argv); | |
09fa3279 SH |
874 | return -1; |
875 | } | |
a1e2e5fc | 876 | addattr_nest_end(&req.n, linkinfo); |
09fa3279 | 877 | } else if (flags & NLM_F_CREATE) { |
56f5daac | 878 | fprintf(stderr, "Not enough information: \"type\" argument is required\n"); |
09fa3279 SH |
879 | return -1; |
880 | } | |
881 | ||
c079e121 | 882 | if (rtnl_talk(&rth, &req.n, NULL, 0) < 0) |
f921f567 | 883 | return -2; |
1d934839 PM |
884 | |
885 | return 0; | |
886 | } | |
887 | ||
50b9950d RP |
888 | int iplink_get(unsigned int flags, char *name, __u32 filt_mask) |
889 | { | |
890 | int len; | |
891 | struct iplink_req req; | |
c079e121 SH |
892 | struct { |
893 | struct nlmsghdr n; | |
894 | char buf[16384]; | |
895 | } answer; | |
50b9950d RP |
896 | |
897 | memset(&req, 0, sizeof(req)); | |
898 | ||
899 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
900 | req.n.nlmsg_flags = NLM_F_REQUEST|flags; | |
901 | req.n.nlmsg_type = RTM_GETLINK; | |
902 | req.i.ifi_family = preferred_family; | |
903 | ||
904 | if (name) { | |
905 | len = strlen(name) + 1; | |
906 | if (len == 1) | |
907 | invarg("\"\" is not a valid device identifier\n", | |
908 | "name"); | |
909 | if (len > IFNAMSIZ) | |
910 | invarg("\"name\" too long\n", name); | |
911 | addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len); | |
912 | } | |
913 | addattr32(&req.n, sizeof(req), IFLA_EXT_MASK, filt_mask); | |
914 | ||
c079e121 | 915 | if (rtnl_talk(&rth, &req.n, &answer.n, sizeof(answer)) < 0) |
50b9950d RP |
916 | return -2; |
917 | ||
5d295bb8 AG |
918 | if (brief) |
919 | print_linkinfo_brief(NULL, &answer.n, stdout); | |
920 | else | |
921 | print_linkinfo(NULL, &answer.n, stdout); | |
50b9950d RP |
922 | |
923 | return 0; | |
924 | } | |
925 | ||
1d934839 | 926 | #if IPLINK_IOCTL_COMPAT |
aba5acdf SH |
927 | static int get_ctl_fd(void) |
928 | { | |
929 | int s_errno; | |
930 | int fd; | |
931 | ||
932 | fd = socket(PF_INET, SOCK_DGRAM, 0); | |
933 | if (fd >= 0) | |
934 | return fd; | |
935 | s_errno = errno; | |
936 | fd = socket(PF_PACKET, SOCK_DGRAM, 0); | |
937 | if (fd >= 0) | |
938 | return fd; | |
939 | fd = socket(PF_INET6, SOCK_DGRAM, 0); | |
940 | if (fd >= 0) | |
941 | return fd; | |
942 | errno = s_errno; | |
943 | perror("Cannot create control socket"); | |
944 | return -1; | |
945 | } | |
946 | ||
71058eb8 | 947 | static int do_chflags(const char *dev, __u32 flags, __u32 mask) |
aba5acdf SH |
948 | { |
949 | struct ifreq ifr; | |
950 | int fd; | |
951 | int err; | |
952 | ||
71058eb8 | 953 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
954 | fd = get_ctl_fd(); |
955 | if (fd < 0) | |
956 | return -1; | |
957 | err = ioctl(fd, SIOCGIFFLAGS, &ifr); | |
958 | if (err) { | |
959 | perror("SIOCGIFFLAGS"); | |
960 | close(fd); | |
961 | return -1; | |
962 | } | |
963 | if ((ifr.ifr_flags^flags)&mask) { | |
964 | ifr.ifr_flags &= ~mask; | |
965 | ifr.ifr_flags |= mask&flags; | |
966 | err = ioctl(fd, SIOCSIFFLAGS, &ifr); | |
967 | if (err) | |
968 | perror("SIOCSIFFLAGS"); | |
969 | } | |
970 | close(fd); | |
971 | return err; | |
972 | } | |
973 | ||
71058eb8 | 974 | static int do_changename(const char *dev, const char *newdev) |
aba5acdf SH |
975 | { |
976 | struct ifreq ifr; | |
977 | int fd; | |
978 | int err; | |
979 | ||
71058eb8 SH |
980 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
981 | strncpy(ifr.ifr_newname, newdev, IFNAMSIZ); | |
aba5acdf SH |
982 | fd = get_ctl_fd(); |
983 | if (fd < 0) | |
984 | return -1; | |
985 | err = ioctl(fd, SIOCSIFNAME, &ifr); | |
986 | if (err) { | |
987 | perror("SIOCSIFNAME"); | |
988 | close(fd); | |
989 | return -1; | |
990 | } | |
991 | close(fd); | |
992 | return err; | |
993 | } | |
994 | ||
71058eb8 | 995 | static int set_qlen(const char *dev, int qlen) |
aba5acdf SH |
996 | { |
997 | struct ifreq ifr; | |
998 | int s; | |
999 | ||
1000 | s = get_ctl_fd(); | |
1001 | if (s < 0) | |
1002 | return -1; | |
1003 | ||
1004 | memset(&ifr, 0, sizeof(ifr)); | |
ae665a52 SH |
1005 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
1006 | ifr.ifr_qlen = qlen; | |
aba5acdf SH |
1007 | if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { |
1008 | perror("SIOCSIFXQLEN"); | |
1009 | close(s); | |
1010 | return -1; | |
1011 | } | |
1012 | close(s); | |
1013 | ||
ae665a52 | 1014 | return 0; |
aba5acdf SH |
1015 | } |
1016 | ||
71058eb8 | 1017 | static int set_mtu(const char *dev, int mtu) |
aba5acdf SH |
1018 | { |
1019 | struct ifreq ifr; | |
1020 | int s; | |
1021 | ||
1022 | s = get_ctl_fd(); | |
1023 | if (s < 0) | |
1024 | return -1; | |
1025 | ||
1026 | memset(&ifr, 0, sizeof(ifr)); | |
ae665a52 SH |
1027 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
1028 | ifr.ifr_mtu = mtu; | |
aba5acdf SH |
1029 | if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { |
1030 | perror("SIOCSIFMTU"); | |
1031 | close(s); | |
1032 | return -1; | |
1033 | } | |
1034 | close(s); | |
1035 | ||
ae665a52 | 1036 | return 0; |
aba5acdf SH |
1037 | } |
1038 | ||
71058eb8 | 1039 | static int get_address(const char *dev, int *htype) |
aba5acdf SH |
1040 | { |
1041 | struct ifreq ifr; | |
1042 | struct sockaddr_ll me; | |
f332d169 | 1043 | socklen_t alen; |
aba5acdf SH |
1044 | int s; |
1045 | ||
1046 | s = socket(PF_PACKET, SOCK_DGRAM, 0); | |
ae665a52 | 1047 | if (s < 0) { |
aba5acdf SH |
1048 | perror("socket(PF_PACKET)"); |
1049 | return -1; | |
1050 | } | |
1051 | ||
1052 | memset(&ifr, 0, sizeof(ifr)); | |
71058eb8 | 1053 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
1054 | if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { |
1055 | perror("SIOCGIFINDEX"); | |
1056 | close(s); | |
1057 | return -1; | |
1058 | } | |
1059 | ||
1060 | memset(&me, 0, sizeof(me)); | |
1061 | me.sll_family = AF_PACKET; | |
1062 | me.sll_ifindex = ifr.ifr_ifindex; | |
1063 | me.sll_protocol = htons(ETH_P_LOOP); | |
6c5ffb9a | 1064 | if (bind(s, (struct sockaddr *)&me, sizeof(me)) == -1) { |
aba5acdf SH |
1065 | perror("bind"); |
1066 | close(s); | |
1067 | return -1; | |
1068 | } | |
1069 | ||
1070 | alen = sizeof(me); | |
6c5ffb9a | 1071 | if (getsockname(s, (struct sockaddr *)&me, &alen) == -1) { |
aba5acdf SH |
1072 | perror("getsockname"); |
1073 | close(s); | |
1074 | return -1; | |
1075 | } | |
1076 | close(s); | |
1077 | *htype = me.sll_hatype; | |
1078 | return me.sll_halen; | |
1079 | } | |
1080 | ||
ae665a52 | 1081 | static int parse_address(const char *dev, int hatype, int halen, |
7b565754 | 1082 | char *lla, struct ifreq *ifr) |
aba5acdf SH |
1083 | { |
1084 | int alen; | |
1085 | ||
1086 | memset(ifr, 0, sizeof(*ifr)); | |
71058eb8 | 1087 | strncpy(ifr->ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
1088 | ifr->ifr_hwaddr.sa_family = hatype; |
1089 | alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); | |
1090 | if (alen < 0) | |
1091 | return -1; | |
1092 | if (alen != halen) { | |
1093 | fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); | |
1094 | return -1; | |
1095 | } | |
ae665a52 | 1096 | return 0; |
aba5acdf SH |
1097 | } |
1098 | ||
1099 | static int set_address(struct ifreq *ifr, int brd) | |
1100 | { | |
1101 | int s; | |
1102 | ||
1103 | s = get_ctl_fd(); | |
1104 | if (s < 0) | |
1105 | return -1; | |
1106 | if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { | |
1107 | perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); | |
1108 | close(s); | |
1109 | return -1; | |
1110 | } | |
1111 | close(s); | |
ae665a52 | 1112 | return 0; |
aba5acdf SH |
1113 | } |
1114 | ||
aba5acdf SH |
1115 | static int do_set(int argc, char **argv) |
1116 | { | |
1117 | char *dev = NULL; | |
1118 | __u32 mask = 0; | |
1119 | __u32 flags = 0; | |
1120 | int qlen = -1; | |
1121 | int mtu = -1; | |
1122 | char *newaddr = NULL; | |
1123 | char *newbrd = NULL; | |
1124 | struct ifreq ifr0, ifr1; | |
1125 | char *newname = NULL; | |
1126 | int htype, halen; | |
1127 | ||
1128 | while (argc > 0) { | |
1129 | if (strcmp(*argv, "up") == 0) { | |
1130 | mask |= IFF_UP; | |
1131 | flags |= IFF_UP; | |
1132 | } else if (strcmp(*argv, "down") == 0) { | |
1133 | mask |= IFF_UP; | |
1134 | flags &= ~IFF_UP; | |
1135 | } else if (strcmp(*argv, "name") == 0) { | |
1136 | NEXT_ARG(); | |
1137 | newname = *argv; | |
1138 | } else if (matches(*argv, "address") == 0) { | |
1139 | NEXT_ARG(); | |
1140 | newaddr = *argv; | |
1141 | } else if (matches(*argv, "broadcast") == 0 || | |
1142 | strcmp(*argv, "brd") == 0) { | |
1143 | NEXT_ARG(); | |
1144 | newbrd = *argv; | |
1145 | } else if (matches(*argv, "txqueuelen") == 0 || | |
1146 | strcmp(*argv, "qlen") == 0 || | |
1147 | matches(*argv, "txqlen") == 0) { | |
1148 | NEXT_ARG(); | |
1149 | if (qlen != -1) | |
1150 | duparg("txqueuelen", *argv); | |
1151 | if (get_integer(&qlen, *argv, 0)) | |
1152 | invarg("Invalid \"txqueuelen\" value\n", *argv); | |
1153 | } else if (strcmp(*argv, "mtu") == 0) { | |
1154 | NEXT_ARG(); | |
1155 | if (mtu != -1) | |
1156 | duparg("mtu", *argv); | |
1157 | if (get_integer(&mtu, *argv, 0)) | |
1158 | invarg("Invalid \"mtu\" value\n", *argv); | |
1159 | } else if (strcmp(*argv, "multicast") == 0) { | |
1160 | NEXT_ARG(); | |
1161 | mask |= IFF_MULTICAST; | |
6c5ffb9a SH |
1162 | |
1163 | if (strcmp(*argv, "on") == 0) | |
aba5acdf | 1164 | flags |= IFF_MULTICAST; |
6c5ffb9a | 1165 | else if (strcmp(*argv, "off") == 0) |
aba5acdf | 1166 | flags &= ~IFF_MULTICAST; |
6c5ffb9a | 1167 | else |
14645ec2 | 1168 | return on_off("multicast", *argv); |
d27b1b5b | 1169 | } else if (strcmp(*argv, "allmulticast") == 0) { |
1170 | NEXT_ARG(); | |
1171 | mask |= IFF_ALLMULTI; | |
6c5ffb9a SH |
1172 | |
1173 | if (strcmp(*argv, "on") == 0) | |
d27b1b5b | 1174 | flags |= IFF_ALLMULTI; |
6c5ffb9a | 1175 | else if (strcmp(*argv, "off") == 0) |
d27b1b5b | 1176 | flags &= ~IFF_ALLMULTI; |
6c5ffb9a | 1177 | else |
14645ec2 | 1178 | return on_off("allmulticast", *argv); |
d27b1b5b | 1179 | } else if (strcmp(*argv, "promisc") == 0) { |
1180 | NEXT_ARG(); | |
1181 | mask |= IFF_PROMISC; | |
6c5ffb9a SH |
1182 | |
1183 | if (strcmp(*argv, "on") == 0) | |
d27b1b5b | 1184 | flags |= IFF_PROMISC; |
6c5ffb9a | 1185 | else if (strcmp(*argv, "off") == 0) |
d27b1b5b | 1186 | flags &= ~IFF_PROMISC; |
6c5ffb9a | 1187 | else |
14645ec2 | 1188 | return on_off("promisc", *argv); |
d27b1b5b | 1189 | } else if (strcmp(*argv, "trailers") == 0) { |
1190 | NEXT_ARG(); | |
1191 | mask |= IFF_NOTRAILERS; | |
6c5ffb9a SH |
1192 | |
1193 | if (strcmp(*argv, "off") == 0) | |
d27b1b5b | 1194 | flags |= IFF_NOTRAILERS; |
6c5ffb9a | 1195 | else if (strcmp(*argv, "on") == 0) |
d27b1b5b | 1196 | flags &= ~IFF_NOTRAILERS; |
6c5ffb9a | 1197 | else |
14645ec2 | 1198 | return on_off("trailers", *argv); |
aba5acdf SH |
1199 | } else if (strcmp(*argv, "arp") == 0) { |
1200 | NEXT_ARG(); | |
1201 | mask |= IFF_NOARP; | |
6c5ffb9a SH |
1202 | |
1203 | if (strcmp(*argv, "on") == 0) | |
aba5acdf | 1204 | flags &= ~IFF_NOARP; |
6c5ffb9a | 1205 | else if (strcmp(*argv, "off") == 0) |
aba5acdf | 1206 | flags |= IFF_NOARP; |
6c5ffb9a | 1207 | else |
ff1e35ed | 1208 | return on_off("arp", *argv); |
aba5acdf SH |
1209 | } else if (matches(*argv, "dynamic") == 0) { |
1210 | NEXT_ARG(); | |
1211 | mask |= IFF_DYNAMIC; | |
6c5ffb9a SH |
1212 | |
1213 | if (strcmp(*argv, "on") == 0) | |
aba5acdf | 1214 | flags |= IFF_DYNAMIC; |
6c5ffb9a | 1215 | else if (strcmp(*argv, "off") == 0) |
aba5acdf | 1216 | flags &= ~IFF_DYNAMIC; |
6c5ffb9a | 1217 | else |
14645ec2 | 1218 | return on_off("dynamic", *argv); |
aba5acdf | 1219 | } else { |
6c5ffb9a | 1220 | if (strcmp(*argv, "dev") == 0) |
aba5acdf | 1221 | NEXT_ARG(); |
8aacb9bb | 1222 | else if (matches(*argv, "help") == 0) |
aba5acdf | 1223 | usage(); |
6c5ffb9a | 1224 | |
aba5acdf SH |
1225 | if (dev) |
1226 | duparg2("dev", *argv); | |
1227 | dev = *argv; | |
1228 | } | |
1229 | argc--; argv++; | |
1230 | } | |
1231 | ||
1232 | if (!dev) { | |
1233 | fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); | |
1234 | exit(-1); | |
1235 | } | |
1236 | ||
1237 | if (newaddr || newbrd) { | |
1238 | halen = get_address(dev, &htype); | |
1239 | if (halen < 0) | |
1240 | return -1; | |
1241 | if (newaddr) { | |
1242 | if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) | |
1243 | return -1; | |
1244 | } | |
1245 | if (newbrd) { | |
1246 | if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) | |
ae665a52 | 1247 | return -1; |
aba5acdf SH |
1248 | } |
1249 | } | |
1250 | ||
1251 | if (newname && strcmp(dev, newname)) { | |
ca78b0e7 PM |
1252 | if (strlen(newname) == 0) |
1253 | invarg("\"\" is not a valid device identifier\n", "name"); | |
aba5acdf SH |
1254 | if (do_changename(dev, newname) < 0) |
1255 | return -1; | |
1256 | dev = newname; | |
1257 | } | |
ae665a52 | 1258 | if (qlen != -1) { |
aba5acdf | 1259 | if (set_qlen(dev, qlen) < 0) |
ae665a52 | 1260 | return -1; |
aba5acdf | 1261 | } |
ae665a52 | 1262 | if (mtu != -1) { |
aba5acdf | 1263 | if (set_mtu(dev, mtu) < 0) |
ae665a52 | 1264 | return -1; |
aba5acdf SH |
1265 | } |
1266 | if (newaddr || newbrd) { | |
1267 | if (newbrd) { | |
1268 | if (set_address(&ifr1, 1) < 0) | |
ae665a52 | 1269 | return -1; |
aba5acdf SH |
1270 | } |
1271 | if (newaddr) { | |
1272 | if (set_address(&ifr0, 0) < 0) | |
1273 | return -1; | |
1274 | } | |
1275 | } | |
1276 | if (mask) | |
1277 | return do_chflags(dev, flags, mask); | |
1278 | return 0; | |
1279 | } | |
1d934839 | 1280 | #endif /* IPLINK_IOCTL_COMPAT */ |
aba5acdf | 1281 | |
561e650e | 1282 | static void do_help(int argc, char **argv) |
1283 | { | |
1284 | struct link_util *lu = NULL; | |
1285 | ||
1286 | if (argc <= 0) { | |
1287 | usage(); | |
6c5ffb9a | 1288 | return; |
561e650e | 1289 | } |
1290 | ||
1291 | lu = get_link_kind(*argv); | |
561e650e | 1292 | if (lu && lu->print_help) |
1293 | lu->print_help(lu, argc-1, argv+1, stdout); | |
1294 | else | |
1295 | usage(); | |
1296 | } | |
1297 | ||
aba5acdf SH |
1298 | int do_iplink(int argc, char **argv) |
1299 | { | |
6843d36e ZS |
1300 | if (argc < 1) |
1301 | return ipaddr_list_link(0, NULL); | |
1302 | ||
1303 | if (iplink_have_newlink()) { | |
1304 | if (matches(*argv, "add") == 0) | |
1305 | return iplink_modify(RTM_NEWLINK, | |
6c5ffb9a SH |
1306 | NLM_F_CREATE|NLM_F_EXCL, |
1307 | argc-1, argv+1); | |
6843d36e | 1308 | if (matches(*argv, "set") == 0 || |
6c5ffb9a | 1309 | matches(*argv, "change") == 0) |
6843d36e | 1310 | return iplink_modify(RTM_NEWLINK, 0, |
6c5ffb9a | 1311 | argc-1, argv+1); |
6843d36e ZS |
1312 | if (matches(*argv, "replace") == 0) |
1313 | return iplink_modify(RTM_NEWLINK, | |
6c5ffb9a SH |
1314 | NLM_F_CREATE|NLM_F_REPLACE, |
1315 | argc-1, argv+1); | |
6843d36e ZS |
1316 | if (matches(*argv, "delete") == 0) |
1317 | return iplink_modify(RTM_DELLINK, 0, | |
6c5ffb9a | 1318 | argc-1, argv+1); |
6843d36e | 1319 | } else { |
1d934839 | 1320 | #if IPLINK_IOCTL_COMPAT |
6843d36e ZS |
1321 | if (matches(*argv, "set") == 0) |
1322 | return do_set(argc-1, argv+1); | |
1d934839 | 1323 | #endif |
6843d36e | 1324 | } |
6c5ffb9a | 1325 | |
6843d36e | 1326 | if (matches(*argv, "show") == 0 || |
6c5ffb9a SH |
1327 | matches(*argv, "lst") == 0 || |
1328 | matches(*argv, "list") == 0) | |
6843d36e | 1329 | return ipaddr_list_link(argc-1, argv+1); |
6c5ffb9a | 1330 | |
6843d36e ZS |
1331 | if (matches(*argv, "help") == 0) { |
1332 | do_help(argc-1, argv+1); | |
1333 | return 0; | |
1334 | } | |
aba5acdf | 1335 | |
6843d36e | 1336 | fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", |
6c5ffb9a | 1337 | *argv); |
aba5acdf SH |
1338 | exit(-1); |
1339 | } |