]> git.proxmox.com Git - mirror_iproute2.git/blame - ip/iplink.c
Fix array out of bounds problem
[mirror_iproute2.git] / ip / iplink.c
CommitLineData
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
37static void usage(void) __attribute__((noreturn));
38
39void 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
57static void usage(void)
58{
59 iplink_usage();
60}
61
62static 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
68static void *BODY; /* cached dlopen(NULL) handle */
69static struct link_util *linkutil_list;
70
71struct 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
104static int have_rtnl_newlink = -1;
105
106static 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
119static 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 */
141static int iplink_have_newlink(void)
142{
143 return 1;
144}
145#endif /* ! IPLINK_IOCTL_COMPAT */
146
909dfe2c
PE
147struct iplink_req {
148 struct nlmsghdr n;
149 struct ifinfomsg i;
150 char buf[1024];
151};
152
153int 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
284static 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
382static 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 402static 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 429static 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 450static 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 472static 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 494static 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 536static 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
554static 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
571static 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
734int 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}