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