]>
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 |
aba5acdf SH |
36 | |
37 | static void usage(void) __attribute__((noreturn)); | |
38 | ||
39 | void iplink_usage(void) | |
40 | { | |
d27b1b5b | 41 | fprintf(stderr, "Usage: ip link set DEVICE { up | down |\n"); |
42 | fprintf(stderr, " arp { on | off } |\n"); | |
aba5acdf | 43 | fprintf(stderr, " dynamic { on | off } |\n"); |
d27b1b5b | 44 | fprintf(stderr, " multicast { on | off } |\n"); |
45 | fprintf(stderr, " allmulticast { on | off } |\n"); | |
46 | fprintf(stderr, " promisc { on | off } |\n"); | |
47 | fprintf(stderr, " trailers { on | off } |\n"); | |
48 | fprintf(stderr, " txqueuelen PACKETS |\n"); | |
aba5acdf SH |
49 | fprintf(stderr, " name NEWNAME |\n"); |
50 | fprintf(stderr, " address LLADDR | broadcast LLADDR |\n"); | |
51 | fprintf(stderr, " mtu MTU }\n"); | |
e2613dc8 | 52 | fprintf(stderr, " netns PID }\n"); |
aba5acdf SH |
53 | fprintf(stderr, " ip link show [ DEVICE ]\n"); |
54 | exit(-1); | |
55 | } | |
56 | ||
57 | static void usage(void) | |
58 | { | |
59 | iplink_usage(); | |
60 | } | |
61 | ||
62 | static int on_off(char *msg) | |
63 | { | |
64 | fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg); | |
65 | return -1; | |
66 | } | |
67 | ||
1d934839 PM |
68 | static void *BODY; /* cached dlopen(NULL) handle */ |
69 | static struct link_util *linkutil_list; | |
70 | ||
71 | struct link_util *get_link_kind(const char *id) | |
72 | { | |
73 | void *dlh; | |
74 | char buf[256]; | |
75 | struct link_util *l; | |
76 | ||
77 | for (l = linkutil_list; l; l = l->next) | |
78 | if (strcmp(l->id, id) == 0) | |
79 | return l; | |
80 | ||
81 | snprintf(buf, sizeof(buf), "/usr/lib/ip/link_%s.so", id); | |
82 | dlh = dlopen(buf, RTLD_LAZY); | |
83 | if (dlh == NULL) { | |
84 | /* look in current binary, only open once */ | |
85 | dlh = BODY; | |
86 | if (dlh == NULL) { | |
87 | dlh = BODY = dlopen(NULL, RTLD_LAZY); | |
88 | if (dlh == NULL) | |
89 | return NULL; | |
90 | } | |
91 | } | |
92 | ||
93 | snprintf(buf, sizeof(buf), "%s_link_util", id); | |
94 | l = dlsym(dlh, buf); | |
95 | if (l == NULL) | |
96 | return NULL; | |
97 | ||
98 | l->next = linkutil_list; | |
99 | linkutil_list = l; | |
100 | return l; | |
101 | } | |
102 | ||
103 | #if IPLINK_IOCTL_COMPAT | |
104 | static int have_rtnl_newlink = -1; | |
105 | ||
106 | static int accept_msg(const struct sockaddr_nl *who, | |
107 | struct nlmsghdr *n, void *arg) | |
108 | { | |
109 | struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n); | |
110 | ||
66e529f5 PM |
111 | if (n->nlmsg_type == NLMSG_ERROR && |
112 | (err->error == -EOPNOTSUPP || err->error == -EINVAL)) | |
1d934839 PM |
113 | have_rtnl_newlink = 0; |
114 | else | |
115 | have_rtnl_newlink = 1; | |
116 | return -1; | |
117 | } | |
118 | ||
119 | static int iplink_have_newlink(void) | |
120 | { | |
121 | struct { | |
122 | struct nlmsghdr n; | |
123 | struct ifinfomsg i; | |
124 | char buf[1024]; | |
125 | } req; | |
126 | ||
127 | if (have_rtnl_newlink < 0) { | |
128 | memset(&req, 0, sizeof(req)); | |
129 | ||
130 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
131 | req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK; | |
132 | req.n.nlmsg_type = RTM_NEWLINK; | |
133 | req.i.ifi_family = AF_UNSPEC; | |
134 | ||
135 | rtnl_send(&rth, (char *)&req.n, req.n.nlmsg_len); | |
136 | rtnl_listen(&rth, accept_msg, NULL); | |
137 | } | |
138 | return have_rtnl_newlink; | |
139 | } | |
140 | #else /* IPLINK_IOCTL_COMPAT */ | |
141 | static int iplink_have_newlink(void) | |
142 | { | |
143 | return 1; | |
144 | } | |
145 | #endif /* ! IPLINK_IOCTL_COMPAT */ | |
146 | ||
909dfe2c PE |
147 | struct iplink_req { |
148 | struct nlmsghdr n; | |
149 | struct ifinfomsg i; | |
150 | char buf[1024]; | |
151 | }; | |
152 | ||
153 | int iplink_parse(int argc, char **argv, struct iplink_req *req, | |
154 | char **name, char **type, char **link, char **dev) | |
1d934839 | 155 | { |
909dfe2c PE |
156 | int ret, len; |
157 | char abuf[32]; | |
1d934839 PM |
158 | int qlen = -1; |
159 | int mtu = -1; | |
e2613dc8 | 160 | int netns = -1; |
1d934839 | 161 | |
909dfe2c | 162 | ret = argc; |
1d934839 PM |
163 | |
164 | while (argc > 0) { | |
165 | if (strcmp(*argv, "up") == 0) { | |
909dfe2c PE |
166 | req->i.ifi_change |= IFF_UP; |
167 | req->i.ifi_flags |= IFF_UP; | |
1d934839 | 168 | } else if (strcmp(*argv, "down") == 0) { |
909dfe2c PE |
169 | req->i.ifi_change |= IFF_UP; |
170 | req->i.ifi_flags &= ~IFF_UP; | |
1d934839 PM |
171 | } else if (strcmp(*argv, "name") == 0) { |
172 | NEXT_ARG(); | |
909dfe2c | 173 | *name = *argv; |
1d934839 PM |
174 | } else if (matches(*argv, "link") == 0) { |
175 | NEXT_ARG(); | |
909dfe2c | 176 | *link = *argv; |
1d934839 PM |
177 | } else if (matches(*argv, "address") == 0) { |
178 | NEXT_ARG(); | |
179 | len = ll_addr_a2n(abuf, sizeof(abuf), *argv); | |
909dfe2c | 180 | addattr_l(&req->n, sizeof(*req), IFLA_ADDRESS, abuf, len); |
1d934839 | 181 | } else if (matches(*argv, "broadcast") == 0 || |
909dfe2c | 182 | strcmp(*argv, "brd") == 0) { |
1d934839 PM |
183 | NEXT_ARG(); |
184 | len = ll_addr_a2n(abuf, sizeof(abuf), *argv); | |
909dfe2c | 185 | addattr_l(&req->n, sizeof(*req), IFLA_BROADCAST, abuf, len); |
1d934839 | 186 | } else if (matches(*argv, "txqueuelen") == 0 || |
909dfe2c PE |
187 | strcmp(*argv, "qlen") == 0 || |
188 | matches(*argv, "txqlen") == 0) { | |
1d934839 PM |
189 | NEXT_ARG(); |
190 | if (qlen != -1) | |
191 | duparg("txqueuelen", *argv); | |
192 | if (get_integer(&qlen, *argv, 0)) | |
193 | invarg("Invalid \"txqueuelen\" value\n", *argv); | |
909dfe2c | 194 | addattr_l(&req->n, sizeof(*req), IFLA_TXQLEN, &qlen, 4); |
1d934839 PM |
195 | } else if (strcmp(*argv, "mtu") == 0) { |
196 | NEXT_ARG(); | |
197 | if (mtu != -1) | |
198 | duparg("mtu", *argv); | |
199 | if (get_integer(&mtu, *argv, 0)) | |
200 | invarg("Invalid \"mtu\" value\n", *argv); | |
909dfe2c | 201 | addattr_l(&req->n, sizeof(*req), IFLA_MTU, &mtu, 4); |
e2613dc8 BT |
202 | } else if (strcmp(*argv, "netns") == 0) { |
203 | NEXT_ARG(); | |
204 | if (netns != -1) | |
205 | duparg("netns", *argv); | |
206 | if (get_integer(&netns, *argv, 0)) | |
207 | invarg("Invalid \"netns\" value\n", *argv); | |
208 | addattr_l(&req->n, sizeof(*req), IFLA_NET_NS_PID, &netns, 4); | |
1d934839 PM |
209 | } else if (strcmp(*argv, "multicast") == 0) { |
210 | NEXT_ARG(); | |
909dfe2c | 211 | req->i.ifi_change |= IFF_MULTICAST; |
1d934839 | 212 | if (strcmp(*argv, "on") == 0) { |
909dfe2c | 213 | req->i.ifi_flags |= IFF_MULTICAST; |
1d934839 | 214 | } else if (strcmp(*argv, "off") == 0) { |
909dfe2c | 215 | req->i.ifi_flags &= ~IFF_MULTICAST; |
1d934839 PM |
216 | } else |
217 | return on_off("multicast"); | |
218 | } else if (strcmp(*argv, "allmulticast") == 0) { | |
219 | NEXT_ARG(); | |
909dfe2c | 220 | req->i.ifi_change |= IFF_ALLMULTI; |
1d934839 | 221 | if (strcmp(*argv, "on") == 0) { |
909dfe2c | 222 | req->i.ifi_flags |= IFF_ALLMULTI; |
1d934839 | 223 | } else if (strcmp(*argv, "off") == 0) { |
909dfe2c | 224 | req->i.ifi_flags &= ~IFF_ALLMULTI; |
1d934839 PM |
225 | } else |
226 | return on_off("allmulticast"); | |
227 | } else if (strcmp(*argv, "promisc") == 0) { | |
228 | NEXT_ARG(); | |
909dfe2c | 229 | req->i.ifi_change |= IFF_PROMISC; |
1d934839 | 230 | if (strcmp(*argv, "on") == 0) { |
909dfe2c | 231 | req->i.ifi_flags |= IFF_PROMISC; |
1d934839 | 232 | } else if (strcmp(*argv, "off") == 0) { |
909dfe2c | 233 | req->i.ifi_flags &= ~IFF_PROMISC; |
1d934839 PM |
234 | } else |
235 | return on_off("promisc"); | |
236 | } else if (strcmp(*argv, "trailers") == 0) { | |
237 | NEXT_ARG(); | |
909dfe2c | 238 | req->i.ifi_change |= IFF_NOTRAILERS; |
1d934839 | 239 | if (strcmp(*argv, "off") == 0) { |
909dfe2c | 240 | req->i.ifi_flags |= IFF_NOTRAILERS; |
1d934839 | 241 | } else if (strcmp(*argv, "on") == 0) { |
909dfe2c | 242 | req->i.ifi_flags &= ~IFF_NOTRAILERS; |
1d934839 PM |
243 | } else |
244 | return on_off("trailers"); | |
245 | } else if (strcmp(*argv, "arp") == 0) { | |
246 | NEXT_ARG(); | |
909dfe2c | 247 | req->i.ifi_change |= IFF_NOARP; |
1d934839 | 248 | if (strcmp(*argv, "on") == 0) { |
909dfe2c | 249 | req->i.ifi_flags &= ~IFF_NOARP; |
1d934839 | 250 | } else if (strcmp(*argv, "off") == 0) { |
909dfe2c | 251 | req->i.ifi_flags |= IFF_NOARP; |
1d934839 PM |
252 | } else |
253 | return on_off("noarp"); | |
254 | #ifdef IFF_DYNAMIC | |
255 | } else if (matches(*argv, "dynamic") == 0) { | |
256 | NEXT_ARG(); | |
909dfe2c | 257 | req->i.ifi_change |= IFF_DYNAMIC; |
1d934839 | 258 | if (strcmp(*argv, "on") == 0) { |
909dfe2c | 259 | req->i.ifi_flags |= IFF_DYNAMIC; |
1d934839 | 260 | } else if (strcmp(*argv, "off") == 0) { |
909dfe2c | 261 | req->i.ifi_flags &= ~IFF_DYNAMIC; |
1d934839 PM |
262 | } else |
263 | return on_off("dynamic"); | |
264 | #endif | |
265 | } else if (matches(*argv, "type") == 0) { | |
266 | NEXT_ARG(); | |
909dfe2c | 267 | *type = *argv; |
1d934839 PM |
268 | argc--; argv++; |
269 | break; | |
270 | } else { | |
909dfe2c | 271 | if (strcmp(*argv, "dev") == 0) { |
1d934839 PM |
272 | NEXT_ARG(); |
273 | } | |
909dfe2c | 274 | if (*dev) |
1d934839 | 275 | duparg2("dev", *argv); |
909dfe2c | 276 | *dev = *argv; |
1d934839 PM |
277 | } |
278 | argc--; argv++; | |
279 | } | |
280 | ||
909dfe2c PE |
281 | return ret - argc; |
282 | } | |
283 | ||
284 | static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv) | |
285 | { | |
286 | int len; | |
287 | char *dev = NULL; | |
288 | char *name = NULL; | |
289 | char *link = NULL; | |
290 | char *type = NULL; | |
291 | struct link_util *lu = NULL; | |
292 | struct iplink_req req; | |
293 | int ret; | |
294 | ||
295 | memset(&req, 0, sizeof(req)); | |
296 | ||
297 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); | |
298 | req.n.nlmsg_flags = NLM_F_REQUEST|flags; | |
299 | req.n.nlmsg_type = cmd; | |
300 | req.i.ifi_family = preferred_family; | |
301 | ||
302 | ret = iplink_parse(argc, argv, &req, &name, &type, &link, &dev); | |
303 | if (ret < 0) | |
304 | return ret; | |
305 | ||
306 | argc -= ret; | |
307 | argv += ret; | |
1d934839 PM |
308 | ll_init_map(&rth); |
309 | ||
310 | if (type) { | |
311 | struct rtattr *linkinfo = NLMSG_TAIL(&req.n); | |
312 | addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0); | |
313 | addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type, | |
314 | strlen(type)); | |
315 | ||
316 | lu = get_link_kind(type); | |
317 | if (lu && argc) { | |
318 | struct rtattr * data = NLMSG_TAIL(&req.n); | |
319 | addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0); | |
320 | ||
321 | if (lu->parse_opt && | |
322 | lu->parse_opt(lu, argc, argv, &req.n)) | |
323 | return -1; | |
324 | ||
325 | data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data; | |
326 | } else if (argc) { | |
327 | if (matches(*argv, "help") == 0) | |
328 | usage(); | |
329 | fprintf(stderr, "Garbage instead of arguments \"%s ...\". " | |
330 | "Try \"ip link help\".\n", *argv); | |
331 | return -1; | |
332 | } | |
333 | linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo; | |
334 | } | |
335 | ||
336 | if (!(flags & NLM_F_CREATE)) { | |
337 | if (!dev) { | |
338 | fprintf(stderr, "Not enough information: \"dev\" " | |
339 | "argument is required.\n"); | |
340 | exit(-1); | |
341 | } | |
342 | ||
343 | req.i.ifi_index = ll_name_to_index(dev); | |
344 | if (req.i.ifi_index == 0) { | |
345 | fprintf(stderr, "Cannot find device \"%s\"\n", dev); | |
346 | return -1; | |
347 | } | |
348 | } else { | |
349 | /* Allow "ip link add dev" and "ip link add name" */ | |
350 | if (!name) | |
351 | name = dev; | |
352 | ||
353 | if (link) { | |
354 | int ifindex; | |
355 | ||
356 | ifindex = ll_name_to_index(link); | |
357 | if (ifindex == 0) { | |
358 | fprintf(stderr, "Cannot find device \"%s\"\n", | |
359 | link); | |
360 | return -1; | |
361 | } | |
362 | addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4); | |
363 | } | |
364 | } | |
365 | ||
366 | if (name) { | |
367 | len = strlen(name) + 1; | |
ca78b0e7 PM |
368 | if (len == 1) |
369 | invarg("\"\" is not a valid device identifier\n", "name"); | |
1d934839 | 370 | if (len > IFNAMSIZ) |
ca78b0e7 | 371 | invarg("\"name\" too long\n", name); |
1d934839 PM |
372 | addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len); |
373 | } | |
374 | ||
375 | if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) | |
376 | exit(2); | |
377 | ||
378 | return 0; | |
379 | } | |
380 | ||
381 | #if IPLINK_IOCTL_COMPAT | |
aba5acdf SH |
382 | static int get_ctl_fd(void) |
383 | { | |
384 | int s_errno; | |
385 | int fd; | |
386 | ||
387 | fd = socket(PF_INET, SOCK_DGRAM, 0); | |
388 | if (fd >= 0) | |
389 | return fd; | |
390 | s_errno = errno; | |
391 | fd = socket(PF_PACKET, SOCK_DGRAM, 0); | |
392 | if (fd >= 0) | |
393 | return fd; | |
394 | fd = socket(PF_INET6, SOCK_DGRAM, 0); | |
395 | if (fd >= 0) | |
396 | return fd; | |
397 | errno = s_errno; | |
398 | perror("Cannot create control socket"); | |
399 | return -1; | |
400 | } | |
401 | ||
71058eb8 | 402 | static int do_chflags(const char *dev, __u32 flags, __u32 mask) |
aba5acdf SH |
403 | { |
404 | struct ifreq ifr; | |
405 | int fd; | |
406 | int err; | |
407 | ||
71058eb8 | 408 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
409 | fd = get_ctl_fd(); |
410 | if (fd < 0) | |
411 | return -1; | |
412 | err = ioctl(fd, SIOCGIFFLAGS, &ifr); | |
413 | if (err) { | |
414 | perror("SIOCGIFFLAGS"); | |
415 | close(fd); | |
416 | return -1; | |
417 | } | |
418 | if ((ifr.ifr_flags^flags)&mask) { | |
419 | ifr.ifr_flags &= ~mask; | |
420 | ifr.ifr_flags |= mask&flags; | |
421 | err = ioctl(fd, SIOCSIFFLAGS, &ifr); | |
422 | if (err) | |
423 | perror("SIOCSIFFLAGS"); | |
424 | } | |
425 | close(fd); | |
426 | return err; | |
427 | } | |
428 | ||
71058eb8 | 429 | static int do_changename(const char *dev, const char *newdev) |
aba5acdf SH |
430 | { |
431 | struct ifreq ifr; | |
432 | int fd; | |
433 | int err; | |
434 | ||
71058eb8 SH |
435 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
436 | strncpy(ifr.ifr_newname, newdev, IFNAMSIZ); | |
aba5acdf SH |
437 | fd = get_ctl_fd(); |
438 | if (fd < 0) | |
439 | return -1; | |
440 | err = ioctl(fd, SIOCSIFNAME, &ifr); | |
441 | if (err) { | |
442 | perror("SIOCSIFNAME"); | |
443 | close(fd); | |
444 | return -1; | |
445 | } | |
446 | close(fd); | |
447 | return err; | |
448 | } | |
449 | ||
71058eb8 | 450 | static int set_qlen(const char *dev, int qlen) |
aba5acdf SH |
451 | { |
452 | struct ifreq ifr; | |
453 | int s; | |
454 | ||
455 | s = get_ctl_fd(); | |
456 | if (s < 0) | |
457 | return -1; | |
458 | ||
459 | memset(&ifr, 0, sizeof(ifr)); | |
ae665a52 SH |
460 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
461 | ifr.ifr_qlen = qlen; | |
aba5acdf SH |
462 | if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) { |
463 | perror("SIOCSIFXQLEN"); | |
464 | close(s); | |
465 | return -1; | |
466 | } | |
467 | close(s); | |
468 | ||
ae665a52 | 469 | return 0; |
aba5acdf SH |
470 | } |
471 | ||
71058eb8 | 472 | static int set_mtu(const char *dev, int mtu) |
aba5acdf SH |
473 | { |
474 | struct ifreq ifr; | |
475 | int s; | |
476 | ||
477 | s = get_ctl_fd(); | |
478 | if (s < 0) | |
479 | return -1; | |
480 | ||
481 | memset(&ifr, 0, sizeof(ifr)); | |
ae665a52 SH |
482 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
483 | ifr.ifr_mtu = mtu; | |
aba5acdf SH |
484 | if (ioctl(s, SIOCSIFMTU, &ifr) < 0) { |
485 | perror("SIOCSIFMTU"); | |
486 | close(s); | |
487 | return -1; | |
488 | } | |
489 | close(s); | |
490 | ||
ae665a52 | 491 | return 0; |
aba5acdf SH |
492 | } |
493 | ||
71058eb8 | 494 | static int get_address(const char *dev, int *htype) |
aba5acdf SH |
495 | { |
496 | struct ifreq ifr; | |
497 | struct sockaddr_ll me; | |
f332d169 | 498 | socklen_t alen; |
aba5acdf SH |
499 | int s; |
500 | ||
501 | s = socket(PF_PACKET, SOCK_DGRAM, 0); | |
ae665a52 | 502 | if (s < 0) { |
aba5acdf SH |
503 | perror("socket(PF_PACKET)"); |
504 | return -1; | |
505 | } | |
506 | ||
507 | memset(&ifr, 0, sizeof(ifr)); | |
71058eb8 | 508 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
509 | if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { |
510 | perror("SIOCGIFINDEX"); | |
511 | close(s); | |
512 | return -1; | |
513 | } | |
514 | ||
515 | memset(&me, 0, sizeof(me)); | |
516 | me.sll_family = AF_PACKET; | |
517 | me.sll_ifindex = ifr.ifr_ifindex; | |
518 | me.sll_protocol = htons(ETH_P_LOOP); | |
519 | if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) { | |
520 | perror("bind"); | |
521 | close(s); | |
522 | return -1; | |
523 | } | |
524 | ||
525 | alen = sizeof(me); | |
526 | if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) { | |
527 | perror("getsockname"); | |
528 | close(s); | |
529 | return -1; | |
530 | } | |
531 | close(s); | |
532 | *htype = me.sll_hatype; | |
533 | return me.sll_halen; | |
534 | } | |
535 | ||
ae665a52 | 536 | static int parse_address(const char *dev, int hatype, int halen, |
7b565754 | 537 | char *lla, struct ifreq *ifr) |
aba5acdf SH |
538 | { |
539 | int alen; | |
540 | ||
541 | memset(ifr, 0, sizeof(*ifr)); | |
71058eb8 | 542 | strncpy(ifr->ifr_name, dev, IFNAMSIZ); |
aba5acdf SH |
543 | ifr->ifr_hwaddr.sa_family = hatype; |
544 | alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla); | |
545 | if (alen < 0) | |
546 | return -1; | |
547 | if (alen != halen) { | |
548 | fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen); | |
549 | return -1; | |
550 | } | |
ae665a52 | 551 | return 0; |
aba5acdf SH |
552 | } |
553 | ||
554 | static int set_address(struct ifreq *ifr, int brd) | |
555 | { | |
556 | int s; | |
557 | ||
558 | s = get_ctl_fd(); | |
559 | if (s < 0) | |
560 | return -1; | |
561 | if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) { | |
562 | perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR"); | |
563 | close(s); | |
564 | return -1; | |
565 | } | |
566 | close(s); | |
ae665a52 | 567 | return 0; |
aba5acdf SH |
568 | } |
569 | ||
570 | ||
571 | static int do_set(int argc, char **argv) | |
572 | { | |
573 | char *dev = NULL; | |
574 | __u32 mask = 0; | |
575 | __u32 flags = 0; | |
576 | int qlen = -1; | |
577 | int mtu = -1; | |
578 | char *newaddr = NULL; | |
579 | char *newbrd = NULL; | |
580 | struct ifreq ifr0, ifr1; | |
581 | char *newname = NULL; | |
582 | int htype, halen; | |
583 | ||
584 | while (argc > 0) { | |
585 | if (strcmp(*argv, "up") == 0) { | |
586 | mask |= IFF_UP; | |
587 | flags |= IFF_UP; | |
588 | } else if (strcmp(*argv, "down") == 0) { | |
589 | mask |= IFF_UP; | |
590 | flags &= ~IFF_UP; | |
591 | } else if (strcmp(*argv, "name") == 0) { | |
592 | NEXT_ARG(); | |
593 | newname = *argv; | |
594 | } else if (matches(*argv, "address") == 0) { | |
595 | NEXT_ARG(); | |
596 | newaddr = *argv; | |
597 | } else if (matches(*argv, "broadcast") == 0 || | |
598 | strcmp(*argv, "brd") == 0) { | |
599 | NEXT_ARG(); | |
600 | newbrd = *argv; | |
601 | } else if (matches(*argv, "txqueuelen") == 0 || | |
602 | strcmp(*argv, "qlen") == 0 || | |
603 | matches(*argv, "txqlen") == 0) { | |
604 | NEXT_ARG(); | |
605 | if (qlen != -1) | |
606 | duparg("txqueuelen", *argv); | |
607 | if (get_integer(&qlen, *argv, 0)) | |
608 | invarg("Invalid \"txqueuelen\" value\n", *argv); | |
609 | } else if (strcmp(*argv, "mtu") == 0) { | |
610 | NEXT_ARG(); | |
611 | if (mtu != -1) | |
612 | duparg("mtu", *argv); | |
613 | if (get_integer(&mtu, *argv, 0)) | |
614 | invarg("Invalid \"mtu\" value\n", *argv); | |
615 | } else if (strcmp(*argv, "multicast") == 0) { | |
616 | NEXT_ARG(); | |
617 | mask |= IFF_MULTICAST; | |
618 | if (strcmp(*argv, "on") == 0) { | |
619 | flags |= IFF_MULTICAST; | |
620 | } else if (strcmp(*argv, "off") == 0) { | |
621 | flags &= ~IFF_MULTICAST; | |
622 | } else | |
623 | return on_off("multicast"); | |
d27b1b5b | 624 | } else if (strcmp(*argv, "allmulticast") == 0) { |
625 | NEXT_ARG(); | |
626 | mask |= IFF_ALLMULTI; | |
627 | if (strcmp(*argv, "on") == 0) { | |
628 | flags |= IFF_ALLMULTI; | |
629 | } else if (strcmp(*argv, "off") == 0) { | |
630 | flags &= ~IFF_ALLMULTI; | |
631 | } else | |
632 | return on_off("allmulticast"); | |
633 | } else if (strcmp(*argv, "promisc") == 0) { | |
634 | NEXT_ARG(); | |
635 | mask |= IFF_PROMISC; | |
636 | if (strcmp(*argv, "on") == 0) { | |
637 | flags |= IFF_PROMISC; | |
638 | } else if (strcmp(*argv, "off") == 0) { | |
639 | flags &= ~IFF_PROMISC; | |
640 | } else | |
641 | return on_off("promisc"); | |
642 | } else if (strcmp(*argv, "trailers") == 0) { | |
643 | NEXT_ARG(); | |
644 | mask |= IFF_NOTRAILERS; | |
645 | if (strcmp(*argv, "off") == 0) { | |
646 | flags |= IFF_NOTRAILERS; | |
647 | } else if (strcmp(*argv, "on") == 0) { | |
648 | flags &= ~IFF_NOTRAILERS; | |
649 | } else | |
650 | return on_off("trailers"); | |
aba5acdf SH |
651 | } else if (strcmp(*argv, "arp") == 0) { |
652 | NEXT_ARG(); | |
653 | mask |= IFF_NOARP; | |
654 | if (strcmp(*argv, "on") == 0) { | |
655 | flags &= ~IFF_NOARP; | |
656 | } else if (strcmp(*argv, "off") == 0) { | |
657 | flags |= IFF_NOARP; | |
658 | } else | |
659 | return on_off("noarp"); | |
660 | #ifdef IFF_DYNAMIC | |
661 | } else if (matches(*argv, "dynamic") == 0) { | |
662 | NEXT_ARG(); | |
663 | mask |= IFF_DYNAMIC; | |
664 | if (strcmp(*argv, "on") == 0) { | |
665 | flags |= IFF_DYNAMIC; | |
666 | } else if (strcmp(*argv, "off") == 0) { | |
667 | flags &= ~IFF_DYNAMIC; | |
668 | } else | |
669 | return on_off("dynamic"); | |
670 | #endif | |
671 | } else { | |
672 | if (strcmp(*argv, "dev") == 0) { | |
673 | NEXT_ARG(); | |
674 | } | |
675 | if (matches(*argv, "help") == 0) | |
676 | usage(); | |
677 | if (dev) | |
678 | duparg2("dev", *argv); | |
679 | dev = *argv; | |
680 | } | |
681 | argc--; argv++; | |
682 | } | |
683 | ||
684 | if (!dev) { | |
685 | fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n"); | |
686 | exit(-1); | |
687 | } | |
688 | ||
689 | if (newaddr || newbrd) { | |
690 | halen = get_address(dev, &htype); | |
691 | if (halen < 0) | |
692 | return -1; | |
693 | if (newaddr) { | |
694 | if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0) | |
695 | return -1; | |
696 | } | |
697 | if (newbrd) { | |
698 | if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0) | |
ae665a52 | 699 | return -1; |
aba5acdf SH |
700 | } |
701 | } | |
702 | ||
703 | if (newname && strcmp(dev, newname)) { | |
ca78b0e7 PM |
704 | if (strlen(newname) == 0) |
705 | invarg("\"\" is not a valid device identifier\n", "name"); | |
aba5acdf SH |
706 | if (do_changename(dev, newname) < 0) |
707 | return -1; | |
708 | dev = newname; | |
709 | } | |
ae665a52 | 710 | if (qlen != -1) { |
aba5acdf | 711 | if (set_qlen(dev, qlen) < 0) |
ae665a52 | 712 | return -1; |
aba5acdf | 713 | } |
ae665a52 | 714 | if (mtu != -1) { |
aba5acdf | 715 | if (set_mtu(dev, mtu) < 0) |
ae665a52 | 716 | return -1; |
aba5acdf SH |
717 | } |
718 | if (newaddr || newbrd) { | |
719 | if (newbrd) { | |
720 | if (set_address(&ifr1, 1) < 0) | |
ae665a52 | 721 | return -1; |
aba5acdf SH |
722 | } |
723 | if (newaddr) { | |
724 | if (set_address(&ifr0, 0) < 0) | |
725 | return -1; | |
726 | } | |
727 | } | |
728 | if (mask) | |
729 | return do_chflags(dev, flags, mask); | |
730 | return 0; | |
731 | } | |
1d934839 | 732 | #endif /* IPLINK_IOCTL_COMPAT */ |
aba5acdf SH |
733 | |
734 | int do_iplink(int argc, char **argv) | |
735 | { | |
736 | if (argc > 0) { | |
1d934839 PM |
737 | if (iplink_have_newlink()) { |
738 | if (matches(*argv, "add") == 0) | |
739 | return iplink_modify(RTM_NEWLINK, | |
740 | NLM_F_CREATE|NLM_F_EXCL, | |
741 | argc-1, argv+1); | |
742 | if (matches(*argv, "set") == 0 || | |
743 | matches(*argv, "change") == 0) | |
744 | return iplink_modify(RTM_NEWLINK, 0, | |
745 | argc-1, argv+1); | |
746 | if (matches(*argv, "replace") == 0) | |
747 | return iplink_modify(RTM_NEWLINK, | |
748 | NLM_F_CREATE|NLM_F_REPLACE, | |
749 | argc-1, argv+1); | |
750 | if (matches(*argv, "delete") == 0) | |
751 | return iplink_modify(RTM_DELLINK, 0, | |
752 | argc-1, argv+1); | |
753 | } else { | |
754 | #if IPLINK_IOCTL_COMPAT | |
755 | if (matches(*argv, "set") == 0) | |
756 | return do_set(argc-1, argv+1); | |
757 | #endif | |
758 | } | |
aba5acdf SH |
759 | if (matches(*argv, "show") == 0 || |
760 | matches(*argv, "lst") == 0 || | |
761 | matches(*argv, "list") == 0) | |
762 | return ipaddr_list_link(argc-1, argv+1); | |
763 | if (matches(*argv, "help") == 0) | |
764 | usage(); | |
765 | } else | |
766 | return ipaddr_list_link(0, NULL); | |
767 | ||
768 | fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv); | |
769 | exit(-1); | |
770 | } |