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