use xbind, xconnect where appropriate.
[oweals/busybox.git] / networking / libiproute / iplink.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * iplink.c "ip link".
4  *
5  * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  */
9
10 #include "libbb.h"
11
12 #include <sys/ioctl.h>
13 #include <sys/socket.h>
14
15 #include <net/if.h>
16 #include <net/if_packet.h>
17 #include <netpacket/packet.h>
18
19 #include <net/ethernet.h>
20
21 #include "rt_names.h"
22 #include "utils.h"
23 #include "ip_common.h"
24
25 /* take from linux/sockios.h */
26 #define SIOCSIFNAME     0x8923          /* set interface name */
27
28 static int on_off(char *msg)
29 {
30         bb_error_msg("error: argument of \"%s\" must be \"on\" or \"off\"", msg);
31         return -1;
32 }
33
34 static int get_ctl_fd(void)
35 {
36         int s_errno;
37         int fd;
38
39         fd = socket(PF_INET, SOCK_DGRAM, 0);
40         if (fd >= 0)
41                 return fd;
42         s_errno = errno;
43         fd = socket(PF_PACKET, SOCK_DGRAM, 0);
44         if (fd >= 0)
45                 return fd;
46         fd = socket(PF_INET6, SOCK_DGRAM, 0);
47         if (fd >= 0)
48                 return fd;
49         errno = s_errno;
50         perror("Cannot create control socket");
51         return -1;
52 }
53
54 static int do_chflags(char *dev, __u32 flags, __u32 mask)
55 {
56         struct ifreq ifr;
57         int fd;
58         int err;
59
60         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
61         fd = get_ctl_fd();
62         if (fd < 0)
63                 return -1;
64         err = ioctl(fd, SIOCGIFFLAGS, &ifr);
65         if (err) {
66                 perror("SIOCGIFFLAGS");
67                 close(fd);
68                 return -1;
69         }
70         if ((ifr.ifr_flags^flags)&mask) {
71                 ifr.ifr_flags &= ~mask;
72                 ifr.ifr_flags |= mask&flags;
73                 err = ioctl(fd, SIOCSIFFLAGS, &ifr);
74                 if (err)
75                         perror("SIOCSIFFLAGS");
76         }
77         close(fd);
78         return err;
79 }
80
81 static int do_changename(char *dev, char *newdev)
82 {
83         struct ifreq ifr;
84         int fd;
85         int err;
86
87         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
88         strncpy(ifr.ifr_newname, newdev, sizeof(ifr.ifr_newname));
89         fd = get_ctl_fd();
90         if (fd < 0)
91                 return -1;
92         err = ioctl(fd, SIOCSIFNAME, &ifr);
93         if (err) {
94                 perror("SIOCSIFNAME");
95                 close(fd);
96                 return -1;
97         }
98         close(fd);
99         return err;
100 }
101
102 static int set_qlen(char *dev, int qlen)
103 {
104         struct ifreq ifr;
105         int s;
106
107         s = get_ctl_fd();
108         if (s < 0)
109                 return -1;
110
111         memset(&ifr, 0, sizeof(ifr));
112         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
113         ifr.ifr_qlen = qlen;
114         if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
115                 perror("SIOCSIFXQLEN");
116                 close(s);
117                 return -1;
118         }
119         close(s);
120
121         return 0;
122 }
123
124 static int set_mtu(char *dev, int mtu)
125 {
126         struct ifreq ifr;
127         int s;
128
129         s = get_ctl_fd();
130         if (s < 0)
131                 return -1;
132
133         memset(&ifr, 0, sizeof(ifr));
134         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
135         ifr.ifr_mtu = mtu;
136         if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
137                 perror("SIOCSIFMTU");
138                 close(s);
139                 return -1;
140         }
141         close(s);
142
143         return 0;
144 }
145
146 static int get_address(char *dev, int *htype)
147 {
148         struct ifreq ifr;
149         struct sockaddr_ll me;
150         socklen_t alen;
151         int s;
152
153         s = socket(PF_PACKET, SOCK_DGRAM, 0);
154         if (s < 0) {
155                 perror("socket(PF_PACKET)");
156                 return -1;
157         }
158
159         memset(&ifr, 0, sizeof(ifr));
160         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
161         if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
162                 perror("SIOCGIFINDEX");
163                 close(s);
164                 return -1;
165         }
166
167         memset(&me, 0, sizeof(me));
168         me.sll_family = AF_PACKET;
169         me.sll_ifindex = ifr.ifr_ifindex;
170         me.sll_protocol = htons(ETH_P_LOOP);
171         if (bind(s, (struct sockaddr*)&me, sizeof(me)) == -1) {
172                 perror("bind");
173                 close(s);
174                 return -1;
175         }
176
177         alen = sizeof(me);
178         if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
179                 perror("getsockname");
180                 close(s);
181                 return -1;
182         }
183         close(s);
184         *htype = me.sll_hatype;
185         return me.sll_halen;
186 }
187
188 static int parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
189 {
190         int alen;
191
192         memset(ifr, 0, sizeof(*ifr));
193         strncpy(ifr->ifr_name, dev, sizeof(ifr->ifr_name));
194         ifr->ifr_hwaddr.sa_family = hatype;
195         alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), 14, lla);
196         if (alen < 0)
197                 return -1;
198         if (alen != halen) {
199                 bb_error_msg("wrong address (%s) length: expected %d bytes", lla, halen);
200                 return -1;
201         }
202         return 0;
203 }
204
205 static int set_address(struct ifreq *ifr, int brd)
206 {
207         int s;
208
209         s = get_ctl_fd();
210         if (s < 0)
211                 return -1;
212         if (ioctl(s, brd?SIOCSIFHWBROADCAST:SIOCSIFHWADDR, ifr) < 0) {
213                 perror(brd?"SIOCSIFHWBROADCAST":"SIOCSIFHWADDR");
214                 close(s);
215                 return -1;
216         }
217         close(s);
218         return 0;
219 }
220
221
222 static int do_set(int argc, char **argv)
223 {
224         char *dev = NULL;
225         __u32 mask = 0;
226         __u32 flags = 0;
227         int qlen = -1;
228         int mtu = -1;
229         char *newaddr = NULL;
230         char *newbrd = NULL;
231         struct ifreq ifr0, ifr1;
232         char *newname = NULL;
233         int htype, halen;
234
235         while (argc > 0) {
236                 if (strcmp(*argv, "up") == 0) {
237                         mask |= IFF_UP;
238                         flags |= IFF_UP;
239                 } else if (strcmp(*argv, "down") == 0) {
240                         mask |= IFF_UP;
241                         flags &= ~IFF_UP;
242                 } else if (strcmp(*argv, "name") == 0) {
243                         NEXT_ARG();
244                         newname = *argv;
245                 } else if (strcmp(*argv, "mtu") == 0) {
246                         NEXT_ARG();
247                         if (mtu != -1)
248                                 duparg("mtu", *argv);
249                         if (get_integer(&mtu, *argv, 0))
250                                 invarg(*argv, "mtu");
251                 } else if (strcmp(*argv, "multicast") == 0) {
252                         NEXT_ARG();
253                         mask |= IFF_MULTICAST;
254                         if (strcmp(*argv, "on") == 0) {
255                                 flags |= IFF_MULTICAST;
256                         } else if (strcmp(*argv, "off") == 0) {
257                                 flags &= ~IFF_MULTICAST;
258                         } else
259                                 return on_off("multicast");
260                 } else if (strcmp(*argv, "arp") == 0) {
261                         NEXT_ARG();
262                         mask |= IFF_NOARP;
263                         if (strcmp(*argv, "on") == 0) {
264                                 flags &= ~IFF_NOARP;
265                         } else if (strcmp(*argv, "off") == 0) {
266                                 flags |= IFF_NOARP;
267                         } else
268                                 return on_off("noarp");
269                 } else if (strcmp(*argv, "addr") == 0) {
270                         NEXT_ARG();
271                         newaddr = *argv;
272                 } else {
273                         if (strcmp(*argv, "dev") == 0) {
274                                 NEXT_ARG();
275                         }
276                         if (dev)
277                                 duparg2("dev", *argv);
278                         dev = *argv;
279                 }
280                 argc--; argv++;
281         }
282
283         if (!dev) {
284                 bb_error_msg(bb_msg_requires_arg, "\"dev\"");
285                 exit(-1);
286         }
287
288         if (newaddr || newbrd) {
289                 halen = get_address(dev, &htype);
290                 if (halen < 0)
291                         return -1;
292                 if (newaddr) {
293                         if (parse_address(dev, htype, halen, newaddr, &ifr0) < 0)
294                                 return -1;
295                 }
296                 if (newbrd) {
297                         if (parse_address(dev, htype, halen, newbrd, &ifr1) < 0)
298                                 return -1;
299                 }
300         }
301
302         if (newname && strcmp(dev, newname)) {
303                 if (do_changename(dev, newname) < 0)
304                         return -1;
305                 dev = newname;
306         }
307         if (qlen != -1) {
308                 if (set_qlen(dev, qlen) < 0)
309                         return -1;
310         }
311         if (mtu != -1) {
312                 if (set_mtu(dev, mtu) < 0)
313                         return -1;
314         }
315         if (newaddr || newbrd) {
316                 if (newbrd) {
317                         if (set_address(&ifr1, 1) < 0)
318                                 return -1;
319                 }
320                 if (newaddr) {
321                         if (set_address(&ifr0, 0) < 0)
322                                 return -1;
323                 }
324         }
325         if (mask)
326                 return do_chflags(dev, flags, mask);
327         return 0;
328 }
329
330 static int ipaddr_list_link(int argc, char **argv)
331 {
332         preferred_family = AF_PACKET;
333         return ipaddr_list_or_flush(argc, argv, 0);
334 }
335
336 int do_iplink(int argc, char **argv)
337 {
338         if (argc > 0) {
339                 if (matches(*argv, "set") == 0)
340                         return do_set(argc-1, argv+1);
341                 if (matches(*argv, "show") == 0 ||
342                     matches(*argv, "lst") == 0 ||
343                     matches(*argv, "list") == 0)
344                         return ipaddr_list_link(argc-1, argv+1);
345         } else
346                 return ipaddr_list_link(0, NULL);
347
348         bb_error_msg("command \"%s\" is unknown", *argv);
349         exit(-1);
350 }