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