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