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