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