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.
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
20 #include <sys/socket.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>
28 #include <sys/ioctl.h>
29 #include <linux/sockios.h>
33 #include "ip_common.h"
35 #define IPLINK_IOCTL_COMPAT 1
37 static void usage(void) __attribute__((noreturn));
39 void iplink_usage(void)
41 fprintf(stderr, "Usage: ip link set DEVICE { up | down |\n");
42 fprintf(stderr, " arp { on | off } |\n");
43 fprintf(stderr, " dynamic { on | off } |\n");
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");
49 fprintf(stderr, " name NEWNAME |\n");
50 fprintf(stderr, " address LLADDR | broadcast LLADDR |\n");
51 fprintf(stderr, " mtu MTU }\n");
52 fprintf(stderr, " ip link show [ DEVICE ]\n");
56 static void usage(void)
61 static int on_off(char *msg)
63 fprintf(stderr, "Error: argument of \"%s\" must be \"on\" or \"off\"\n", msg);
67 static void *BODY; /* cached dlopen(NULL) handle */
68 static struct link_util *linkutil_list;
70 struct link_util *get_link_kind(const char *id)
76 for (l = linkutil_list; l; l = l->next)
77 if (strcmp(l->id, id) == 0)
80 snprintf(buf, sizeof(buf), "/usr/lib/ip/link_%s.so", id);
81 dlh = dlopen(buf, RTLD_LAZY);
83 /* look in current binary, only open once */
86 dlh = BODY = dlopen(NULL, RTLD_LAZY);
92 snprintf(buf, sizeof(buf), "%s_link_util", id);
97 l->next = linkutil_list;
102 #if IPLINK_IOCTL_COMPAT
103 static int have_rtnl_newlink = -1;
105 static int accept_msg(const struct sockaddr_nl *who,
106 struct nlmsghdr *n, void *arg)
108 struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
110 if (n->nlmsg_type == NLMSG_ERROR && err->error == -EOPNOTSUPP)
111 have_rtnl_newlink = 0;
113 have_rtnl_newlink = 1;
117 static int iplink_have_newlink(void)
125 if (have_rtnl_newlink < 0) {
126 memset(&req, 0, sizeof(req));
128 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
129 req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK;
130 req.n.nlmsg_type = RTM_NEWLINK;
131 req.i.ifi_family = AF_UNSPEC;
133 rtnl_send(&rth, (char *)&req.n, req.n.nlmsg_len);
134 rtnl_listen(&rth, accept_msg, NULL);
136 return have_rtnl_newlink;
138 #else /* IPLINK_IOCTL_COMPAT */
139 static int iplink_have_newlink(void)
143 #endif /* ! IPLINK_IOCTL_COMPAT */
145 static int iplink_modify(int cmd, unsigned int flags, int argc, char **argv)
155 struct link_util *lu = NULL;
162 memset(&req, 0, sizeof(req));
164 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
165 req.n.nlmsg_flags = NLM_F_REQUEST|flags;
166 req.n.nlmsg_type = cmd;
167 req.i.ifi_family = preferred_family;
170 if (strcmp(*argv, "up") == 0) {
171 req.i.ifi_change |= IFF_UP;
172 req.i.ifi_flags |= IFF_UP;
173 } else if (strcmp(*argv, "down") == 0) {
174 req.i.ifi_change |= IFF_UP;
175 req.i.ifi_flags &= ~IFF_UP;
176 } else if (strcmp(*argv, "name") == 0) {
179 } else if (matches(*argv, "link") == 0) {
182 } else if (matches(*argv, "address") == 0) {
184 len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
185 addattr_l(&req.n, sizeof(req), IFLA_ADDRESS, abuf, len);
186 } else if (matches(*argv, "broadcast") == 0 ||
187 strcmp(*argv, "brd") == 0) {
189 len = ll_addr_a2n(abuf, sizeof(abuf), *argv);
190 addattr_l(&req.n, sizeof(req), IFLA_BROADCAST, abuf, len);
191 } else if (matches(*argv, "txqueuelen") == 0 ||
192 strcmp(*argv, "qlen") == 0 ||
193 matches(*argv, "txqlen") == 0) {
196 duparg("txqueuelen", *argv);
197 if (get_integer(&qlen, *argv, 0))
198 invarg("Invalid \"txqueuelen\" value\n", *argv);
199 addattr_l(&req.n, sizeof(req), IFLA_TXQLEN, &qlen, 4);
200 } else if (strcmp(*argv, "mtu") == 0) {
203 duparg("mtu", *argv);
204 if (get_integer(&mtu, *argv, 0))
205 invarg("Invalid \"mtu\" value\n", *argv);
206 addattr_l(&req.n, sizeof(req), IFLA_MTU, &mtu, 4);
207 } else if (strcmp(*argv, "multicast") == 0) {
209 req.i.ifi_change |= IFF_MULTICAST;
210 if (strcmp(*argv, "on") == 0) {
211 req.i.ifi_flags |= IFF_MULTICAST;
212 } else if (strcmp(*argv, "off") == 0) {
213 req.i.ifi_flags &= ~IFF_MULTICAST;
215 return on_off("multicast");
216 } else if (strcmp(*argv, "allmulticast") == 0) {
218 req.i.ifi_change |= IFF_ALLMULTI;
219 if (strcmp(*argv, "on") == 0) {
220 req.i.ifi_flags |= IFF_ALLMULTI;
221 } else if (strcmp(*argv, "off") == 0) {
222 req.i.ifi_flags &= ~IFF_ALLMULTI;
224 return on_off("allmulticast");
225 } else if (strcmp(*argv, "promisc") == 0) {
227 req.i.ifi_change |= IFF_PROMISC;
228 if (strcmp(*argv, "on") == 0) {
229 req.i.ifi_flags |= IFF_PROMISC;
230 } else if (strcmp(*argv, "off") == 0) {
231 req.i.ifi_flags &= ~IFF_PROMISC;
233 return on_off("promisc");
234 } else if (strcmp(*argv, "trailers") == 0) {
236 req.i.ifi_change |= IFF_NOTRAILERS;
237 if (strcmp(*argv, "off") == 0) {
238 req.i.ifi_flags |= IFF_NOTRAILERS;
239 } else if (strcmp(*argv, "on") == 0) {
240 req.i.ifi_flags &= ~IFF_NOTRAILERS;
242 return on_off("trailers");
243 } else if (strcmp(*argv, "arp") == 0) {
245 req.i.ifi_change |= IFF_NOARP;
246 if (strcmp(*argv, "on") == 0) {
247 req.i.ifi_flags &= ~IFF_NOARP;
248 } else if (strcmp(*argv, "off") == 0) {
249 req.i.ifi_flags |= IFF_NOARP;
251 return on_off("noarp");
253 } else if (matches(*argv, "dynamic") == 0) {
255 req.i.ifi_change |= IFF_DYNAMIC;
256 if (strcmp(*argv, "on") == 0) {
257 req.i.ifi_flags |= IFF_DYNAMIC;
258 } else if (strcmp(*argv, "off") == 0) {
259 req.i.ifi_flags &= ~IFF_DYNAMIC;
261 return on_off("dynamic");
263 } else if (matches(*argv, "type") == 0) {
269 if (strcmp(*argv, "dev") == 0) {
273 duparg2("dev", *argv);
282 struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
283 addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
284 addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, type,
287 lu = get_link_kind(type);
289 struct rtattr * data = NLMSG_TAIL(&req.n);
290 addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
293 lu->parse_opt(lu, argc, argv, &req.n))
296 data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
298 if (matches(*argv, "help") == 0)
300 fprintf(stderr, "Garbage instead of arguments \"%s ...\". "
301 "Try \"ip link help\".\n", *argv);
304 linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
307 if (!(flags & NLM_F_CREATE)) {
309 fprintf(stderr, "Not enough information: \"dev\" "
310 "argument is required.\n");
314 req.i.ifi_index = ll_name_to_index(dev);
315 if (req.i.ifi_index == 0) {
316 fprintf(stderr, "Cannot find device \"%s\"\n", dev);
320 /* Allow "ip link add dev" and "ip link add name" */
327 ifindex = ll_name_to_index(link);
329 fprintf(stderr, "Cannot find device \"%s\"\n",
333 addattr_l(&req.n, sizeof(req), IFLA_LINK, &ifindex, 4);
338 len = strlen(name) + 1;
340 invarg("\"name\" too long\n", *argv);
341 addattr_l(&req.n, sizeof(req), IFLA_IFNAME, name, len);
344 if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0)
350 #if IPLINK_IOCTL_COMPAT
351 static int get_ctl_fd(void)
356 fd = socket(PF_INET, SOCK_DGRAM, 0);
360 fd = socket(PF_PACKET, SOCK_DGRAM, 0);
363 fd = socket(PF_INET6, SOCK_DGRAM, 0);
367 perror("Cannot create control socket");
371 static int do_chflags(const char *dev, __u32 flags, __u32 mask)
377 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
381 err = ioctl(fd, SIOCGIFFLAGS, &ifr);
383 perror("SIOCGIFFLAGS");
387 if ((ifr.ifr_flags^flags)&mask) {
388 ifr.ifr_flags &= ~mask;
389 ifr.ifr_flags |= mask&flags;
390 err = ioctl(fd, SIOCSIFFLAGS, &ifr);
392 perror("SIOCSIFFLAGS");
398 static int do_changename(const char *dev, const char *newdev)
404 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
405 strncpy(ifr.ifr_newname, newdev, IFNAMSIZ);
409 err = ioctl(fd, SIOCSIFNAME, &ifr);
411 perror("SIOCSIFNAME");
419 static int set_qlen(const char *dev, int qlen)
428 memset(&ifr, 0, sizeof(ifr));
429 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
431 if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
432 perror("SIOCSIFXQLEN");
441 static int set_mtu(const char *dev, int mtu)
450 memset(&ifr, 0, sizeof(ifr));
451 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
453 if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
454 perror("SIOCSIFMTU");
463 static int get_address(const char *dev, int *htype)
466 struct sockaddr_ll me;
470 s = socket(PF_PACKET, SOCK_DGRAM, 0);
472 perror("socket(PF_PACKET)");
476 memset(&ifr, 0, sizeof(ifr));
477 strncpy(ifr.ifr_name, dev, IFNAMSIZ);
478 if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
479 perror("SIOCGIFINDEX");
484 memset(&me, 0, sizeof(me));
485 me.sll_family = AF_PACKET;
486 me.sll_ifindex = ifr.ifr_ifindex;
487 me.sll_protocol = htons(ETH_P_LOOP);
488 if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
495 if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
496 perror("getsockname");
501 *htype = me.sll_hatype;
505 static int parse_address(const char *dev, int hatype, int halen,
506 char *lla, struct ifreq *ifr)
510 memset(ifr, 0, sizeof(*ifr));
511 strncpy(ifr->ifr_name, dev, IFNAMSIZ);
512 ifr->ifr_hwaddr.sa_family = hatype;
513 alen = ll_addr_a2n(ifr->ifr_hwaddr.sa_data, 14, lla);
517 fprintf(stderr, "Wrong address (%s) length: expected %d bytes\n", lla, halen);
523 static int set_address(struct ifreq *ifr, int brd)
530 if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
531 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
540 static int do_set(int argc, char **argv)
547 char *newaddr = NULL;
549 struct ifreq ifr0, ifr1;
550 char *newname = NULL;
554 if (strcmp(*argv, "up") == 0) {
557 } else if (strcmp(*argv, "down") == 0) {
560 } else if (strcmp(*argv, "name") == 0) {
563 } else if (matches(*argv, "address") == 0) {
566 } else if (matches(*argv, "broadcast") == 0 ||
567 strcmp(*argv, "brd") == 0) {
570 } else if (matches(*argv, "txqueuelen") == 0 ||
571 strcmp(*argv, "qlen") == 0 ||
572 matches(*argv, "txqlen") == 0) {
575 duparg("txqueuelen", *argv);
576 if (get_integer(&qlen, *argv, 0))
577 invarg("Invalid \"txqueuelen\" value\n", *argv);
578 } else if (strcmp(*argv, "mtu") == 0) {
581 duparg("mtu", *argv);
582 if (get_integer(&mtu, *argv, 0))
583 invarg("Invalid \"mtu\" value\n", *argv);
584 } else if (strcmp(*argv, "multicast") == 0) {
586 mask |= IFF_MULTICAST;
587 if (strcmp(*argv, "on") == 0) {
588 flags |= IFF_MULTICAST;
589 } else if (strcmp(*argv, "off") == 0) {
590 flags &= ~IFF_MULTICAST;
592 return on_off("multicast");
593 } else if (strcmp(*argv, "allmulticast") == 0) {
595 mask |= IFF_ALLMULTI;
596 if (strcmp(*argv, "on") == 0) {
597 flags |= IFF_ALLMULTI;
598 } else if (strcmp(*argv, "off") == 0) {
599 flags &= ~IFF_ALLMULTI;
601 return on_off("allmulticast");
602 } else if (strcmp(*argv, "promisc") == 0) {
605 if (strcmp(*argv, "on") == 0) {
606 flags |= IFF_PROMISC;
607 } else if (strcmp(*argv, "off") == 0) {
608 flags &= ~IFF_PROMISC;
610 return on_off("promisc");
611 } else if (strcmp(*argv, "trailers") == 0) {
613 mask |= IFF_NOTRAILERS;
614 if (strcmp(*argv, "off") == 0) {
615 flags |= IFF_NOTRAILERS;
616 } else if (strcmp(*argv, "on") == 0) {
617 flags &= ~IFF_NOTRAILERS;
619 return on_off("trailers");
620 } else if (strcmp(*argv, "arp") == 0) {
623 if (strcmp(*argv, "on") == 0) {
625 } else if (strcmp(*argv, "off") == 0) {
628 return on_off("noarp");
630 } else if (matches(*argv, "dynamic") == 0) {
633 if (strcmp(*argv, "on") == 0) {
634 flags |= IFF_DYNAMIC;
635 } else if (strcmp(*argv, "off") == 0) {
636 flags &= ~IFF_DYNAMIC;
638 return on_off("dynamic");
641 if (strcmp(*argv, "dev") == 0) {
644 if (matches(*argv, "help") == 0)
647 duparg2("dev", *argv);
654 fprintf(stderr, "Not enough of information: \"dev\" argument is required.\n");
658 if (newaddr || newbrd) {
659 halen = get_address(dev, &htype);
663 if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
667 if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
672 if (newname && strcmp(dev, newname)) {
673 if (do_changename(dev, newname) < 0)
678 if (set_qlen(dev, qlen) < 0)
682 if (set_mtu(dev, mtu) < 0)
685 if (newaddr || newbrd) {
687 if (set_address(&ifr1, 1) < 0)
691 if (set_address(&ifr0, 0) < 0)
696 return do_chflags(dev, flags, mask);
699 #endif /* IPLINK_IOCTL_COMPAT */
701 int do_iplink(int argc, char **argv)
704 if (iplink_have_newlink()) {
705 if (matches(*argv, "add") == 0)
706 return iplink_modify(RTM_NEWLINK,
707 NLM_F_CREATE|NLM_F_EXCL,
709 if (matches(*argv, "set") == 0 ||
710 matches(*argv, "change") == 0)
711 return iplink_modify(RTM_NEWLINK, 0,
713 if (matches(*argv, "replace") == 0)
714 return iplink_modify(RTM_NEWLINK,
715 NLM_F_CREATE|NLM_F_REPLACE,
717 if (matches(*argv, "delete") == 0)
718 return iplink_modify(RTM_DELLINK, 0,
721 #if IPLINK_IOCTL_COMPAT
722 if (matches(*argv, "set") == 0)
723 return do_set(argc-1, argv+1);
726 if (matches(*argv, "show") == 0 ||
727 matches(*argv, "lst") == 0 ||
728 matches(*argv, "list") == 0)
729 return ipaddr_list_link(argc-1, argv+1);
730 if (matches(*argv, "help") == 0)
733 return ipaddr_list_link(0, NULL);
735 fprintf(stderr, "Command \"%s\" is unknown, try \"ip link help\".\n", *argv);