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