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