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