delete tons of extra #includes
[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 <sys/ioctl.h>
11 //#include <sys/socket.h>
12 #include <net/if.h>
13 #include <net/if_packet.h>
14 #include <netpacket/packet.h>
15 #include <net/ethernet.h>
16
17 #include "ip_common.h"  /* #include "libbb.h" is inside */
18 #include "rt_names.h"
19 #include "utils.h"
20
21 /* taken from linux/sockios.h */
22 #define SIOCSIFNAME     0x8923          /* set interface name */
23
24 static void on_off(const char *msg) ATTRIBUTE_NORETURN;
25 static void on_off(const char *msg)
26 {
27         bb_error_msg_and_die("error: argument of \"%s\" must be \"on\" or \"off\"", msg);
28 }
29
30 /* Exits on error */
31 static int get_ctl_fd(void)
32 {
33         int fd;
34
35         fd = socket(PF_INET, SOCK_DGRAM, 0);
36         if (fd >= 0)
37                 return fd;
38         fd = socket(PF_PACKET, SOCK_DGRAM, 0);
39         if (fd >= 0)
40                 return fd;
41         return xsocket(PF_INET6, SOCK_DGRAM, 0);
42 }
43
44 /* Exits on error */
45 static void do_chflags(char *dev, uint32_t flags, uint32_t mask)
46 {
47         struct ifreq ifr;
48         int fd;
49
50         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
51         fd = get_ctl_fd();
52         if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
53                 bb_perror_msg_and_die("SIOCGIFFLAGS");
54         }
55         if ((ifr.ifr_flags ^ flags) & mask) {
56                 ifr.ifr_flags &= ~mask;
57                 ifr.ifr_flags |= mask & flags;
58                 if (ioctl(fd, SIOCSIFFLAGS, &ifr))
59                         bb_perror_msg_and_die("SIOCSIFFLAGS");
60         }
61         close(fd);
62 }
63
64 /* Exits on error */
65 static void do_changename(char *dev, char *newdev)
66 {
67         struct ifreq ifr;
68         int fd;
69         int err;
70
71         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
72         strncpy(ifr.ifr_newname, newdev, sizeof(ifr.ifr_newname));
73         fd = get_ctl_fd();
74         err = ioctl(fd, SIOCSIFNAME, &ifr);
75         if (err) {
76                 bb_perror_msg_and_die("SIOCSIFNAME");
77         }
78         close(fd);
79 }
80
81 /* Exits on error */
82 static void set_qlen(char *dev, int qlen)
83 {
84         struct ifreq ifr;
85         int s;
86
87         s = get_ctl_fd();
88         memset(&ifr, 0, sizeof(ifr));
89         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
90         ifr.ifr_qlen = qlen;
91         if (ioctl(s, SIOCSIFTXQLEN, &ifr) < 0) {
92                 bb_perror_msg_and_die("SIOCSIFXQLEN");
93         }
94         close(s);
95 }
96
97 /* Exits on error */
98 static void set_mtu(char *dev, int mtu)
99 {
100         struct ifreq ifr;
101         int s;
102
103         s = get_ctl_fd();
104         memset(&ifr, 0, sizeof(ifr));
105         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
106         ifr.ifr_mtu = mtu;
107         if (ioctl(s, SIOCSIFMTU, &ifr) < 0) {
108                 bb_perror_msg_and_die("SIOCSIFMTU");
109         }
110         close(s);
111 }
112
113 /* Exits on error */
114 static int get_address(char *dev, int *htype)
115 {
116         struct ifreq ifr;
117         struct sockaddr_ll me;
118         socklen_t alen;
119         int s;
120
121         s = xsocket(PF_PACKET, SOCK_DGRAM, 0);
122
123         memset(&ifr, 0, sizeof(ifr));
124         strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
125         if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
126                 bb_perror_msg_and_die("SIOCGIFINDEX");
127         }
128
129         memset(&me, 0, sizeof(me));
130         me.sll_family = AF_PACKET;
131         me.sll_ifindex = ifr.ifr_ifindex;
132         me.sll_protocol = htons(ETH_P_LOOP);
133         xbind(s, (struct sockaddr*)&me, sizeof(me));
134
135         alen = sizeof(me);
136         if (getsockname(s, (struct sockaddr*)&me, &alen) == -1) {
137                 bb_perror_msg_and_die("getsockname");
138         }
139         close(s);
140         *htype = me.sll_hatype;
141         return me.sll_halen;
142 }
143
144 /* Exits on error */
145 static void parse_address(char *dev, int hatype, int halen, char *lla, struct ifreq *ifr)
146 {
147         int alen;
148
149         memset(ifr, 0, sizeof(*ifr));
150         strncpy(ifr->ifr_name, dev, sizeof(ifr->ifr_name));
151         ifr->ifr_hwaddr.sa_family = hatype;
152         alen = ll_addr_a2n((unsigned char *)(ifr->ifr_hwaddr.sa_data), 14, lla);
153         if (alen < 0)
154                 exit(1);
155         if (alen != halen) {
156                 bb_error_msg_and_die("wrong address (%s) length: expected %d bytes", lla, halen);
157         }
158 }
159
160 /* Exits on error */
161 static void set_address(struct ifreq *ifr, int brd)
162 {
163         int s;
164
165         s = get_ctl_fd();
166         if (ioctl(s, brd ? SIOCSIFHWBROADCAST  :SIOCSIFHWADDR, ifr) < 0) {
167                 bb_perror_msg_and_die(brd ? "SIOCSIFHWBROADCAST" : "SIOCSIFHWADDR");
168         }
169         close(s);
170 }
171
172
173 /* Return value becomes exitcode. It's okay to not return at all */
174 static int do_set(int argc, char **argv)
175 {
176         char *dev = NULL;
177         uint32_t mask = 0;
178         uint32_t flags = 0;
179         int qlen = -1;
180         int mtu = -1;
181         char *newaddr = NULL;
182         char *newbrd = NULL;
183         struct ifreq ifr0, ifr1;
184         char *newname = NULL;
185         int htype, halen;
186         static const char * const keywords[] = {
187                 "up", "down", "name", "mtu", "multicast", "arp", "addr", "dev",
188                 "on", "off", NULL
189         };
190         enum { ARG_up = 1, ARG_down, ARG_name, ARG_mtu, ARG_multicast, ARG_arp,
191                 ARG_addr, ARG_dev, PARM_on, PARM_off };
192         smalluint key;
193
194         while (argc > 0) {
195                 key = index_in_str_array(keywords, *argv) + 1;
196                 if (key == ARG_up) {
197                         mask |= IFF_UP;
198                         flags |= IFF_UP;
199                 } else if (key == ARG_down) {
200                         mask |= IFF_UP;
201                         flags &= ~IFF_UP;
202                 } else if (key == ARG_name) {
203                         NEXT_ARG();
204                         newname = *argv;
205                 } else if (key == ARG_mtu) {
206                         NEXT_ARG();
207                         if (mtu != -1)
208                                 duparg("mtu", *argv);
209                         if (get_integer(&mtu, *argv, 0))
210                                 invarg(*argv, "mtu");
211                 } else if (key == ARG_multicast) {
212                         NEXT_ARG();
213                         mask |= IFF_MULTICAST;
214                         key = index_in_str_array(keywords, *argv) + 1;
215                         if (key == PARM_on) {
216                                 flags |= IFF_MULTICAST;
217                         } else if (key == PARM_off) {
218                                 flags &= ~IFF_MULTICAST;
219                         } else
220                                 on_off("multicast");
221                 } else if (key == ARG_arp) {
222                         NEXT_ARG();
223                         mask |= IFF_NOARP;
224                         key = index_in_str_array(keywords, *argv) + 1;
225                         if (key == PARM_on) {
226                                 flags &= ~IFF_NOARP;
227                         } else if (key == PARM_off) {
228                                 flags |= IFF_NOARP;
229                         } else
230                                 on_off("arp");
231                 } else if (key == ARG_addr) {
232                         NEXT_ARG();
233                         newaddr = *argv;
234                 } else {
235                         if (key == ARG_dev) {
236                                 NEXT_ARG();
237                         }
238                         if (dev)
239                                 duparg2("dev", *argv);
240                         dev = *argv;
241                 }
242                 argc--; argv++;
243         }
244
245         if (!dev) {
246                 bb_error_msg_and_die(bb_msg_requires_arg, "\"dev\"");
247         }
248
249         if (newaddr || newbrd) {
250                 halen = get_address(dev, &htype);
251                 if (newaddr) {
252                         parse_address(dev, htype, halen, newaddr, &ifr0);
253                 }
254                 if (newbrd) {
255                         parse_address(dev, htype, halen, newbrd, &ifr1);
256                 }
257         }
258
259         if (newname && strcmp(dev, newname)) {
260                 do_changename(dev, newname);
261                 dev = newname;
262         }
263         if (qlen != -1) {
264                 set_qlen(dev, qlen);
265         }
266         if (mtu != -1) {
267                 set_mtu(dev, mtu);
268         }
269         if (newaddr || newbrd) {
270                 if (newbrd) {
271                         set_address(&ifr1, 1);
272                 }
273                 if (newaddr) {
274                         set_address(&ifr0, 0);
275                 }
276         }
277         if (mask)
278                 do_chflags(dev, flags, mask);
279         return 0;
280 }
281
282 static int ipaddr_list_link(int argc, char **argv)
283 {
284         preferred_family = AF_PACKET;
285         return ipaddr_list_or_flush(argc, argv, 0);
286 }
287
288 /* Return value becomes exitcode. It's okay to not return at all */
289 int do_iplink(int argc, char **argv)
290 {
291         static const char * const keywords[] = {
292                 "set", "show", "lst", "list", NULL
293         };
294         smalluint key;
295         if (argc <= 0)
296                 return ipaddr_list_link(0, NULL);
297         key = index_in_substr_array(keywords, *argv) + 1;
298         if (key == 0)
299                 bb_error_msg_and_die(bb_msg_invalid_arg, *argv, applet_name);
300         argc--; argv++;
301         if (key == 1) /* set */
302                 return do_set(argc, argv);
303         else /* show, lst, list */
304                 return ipaddr_list_link(argc, argv);
305 }