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