blob: 52a50993d8c6dd6f29b51e051c3b744a726375e7 [file] [log] [blame]
"Robert P. J. Day"63fc1a92006-07-02 19:47:05 +00001/* vi: set sw=4 ts=4: */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +00002/*
3 * iptunnel.c "ip tunnel"
4 *
Bernhard Reutner-Fischereedd1be2006-01-12 13:15:49 +00005 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
Glenn L McGrath9a2d2722002-11-10 01:33:55 +00006 *
7 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
8 *
9 *
10 * Changes:
11 *
12 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
13 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
14 * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
15 */
16
Rob Landleyecae66a2006-06-02 20:53:38 +000017#include "libbb.h"
Glenn L McGrath275be872002-12-16 07:37:21 +000018#include <sys/socket.h>
19#include <sys/ioctl.h>
20
Glenn L McGrath275be872002-12-16 07:37:21 +000021#include <netinet/ip.h>
22
Eric Andersenab4e19a2003-01-14 08:54:08 +000023#include <net/if.h>
24#include <net/if_arp.h>
25
26#include <asm/types.h>
Bernhard Reutner-Fischereedd1be2006-01-12 13:15:49 +000027#ifndef __constant_htons
Eric Andersenab4e19a2003-01-14 08:54:08 +000028#define __constant_htons htons
Bernhard Reutner-Fischereedd1be2006-01-12 13:15:49 +000029#endif
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000030#include <linux/if_tunnel.h>
31
32#include "rt_names.h"
33#include "utils.h"
Bernhard Reutner-Fischer1d62d3b2005-10-08 20:47:15 +000034#include "ip_common.h"
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000035
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000036
Denis Vlasenko540a2a12007-04-07 01:14:45 +000037/* Dies on error */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000038static int do_ioctl_get_ifindex(char *dev)
39{
40 struct ifreq ifr;
41 int fd;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000042
Denis Vlasenko229b3d22006-11-27 23:44:57 +000043 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
Denis Vlasenko27af5a02006-09-03 12:21:59 +000044 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
Glenn L McGrath8b602442002-11-28 12:19:51 +000045 if (ioctl(fd, SIOCGIFINDEX, &ifr)) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +000046 bb_perror_msg_and_die("SIOCGIFINDEX");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000047 }
48 close(fd);
49 return ifr.ifr_ifindex;
50}
51
52static int do_ioctl_get_iftype(char *dev)
53{
54 struct ifreq ifr;
55 int fd;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000056
Denis Vlasenko229b3d22006-11-27 23:44:57 +000057 strncpy(ifr.ifr_name, dev, sizeof(ifr.ifr_name));
Denis Vlasenko27af5a02006-09-03 12:21:59 +000058 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
Glenn L McGrath8b602442002-11-28 12:19:51 +000059 if (ioctl(fd, SIOCGIFHWADDR, &ifr)) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +000060 bb_perror_msg("SIOCGIFHWADDR");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000061 return -1;
62 }
63 close(fd);
64 return ifr.ifr_addr.sa_family;
65}
66
Glenn L McGrath8b602442002-11-28 12:19:51 +000067static char *do_ioctl_get_ifname(int idx)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000068{
Denis Vlasenko540a2a12007-04-07 01:14:45 +000069 struct ifreq ifr;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000070 int fd;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000071
72 ifr.ifr_ifindex = idx;
Denis Vlasenko27af5a02006-09-03 12:21:59 +000073 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
Glenn L McGrath8b602442002-11-28 12:19:51 +000074 if (ioctl(fd, SIOCGIFNAME, &ifr)) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +000075 bb_perror_msg("SIOCGIFNAME");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000076 return NULL;
77 }
78 close(fd);
Denis Vlasenko540a2a12007-04-07 01:14:45 +000079 return xstrndup(ifr.ifr_name, sizeof(ifr.ifr_name));
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000080}
81
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000082static int do_get_ioctl(const char *basedev, struct ip_tunnel_parm *p)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000083{
84 struct ifreq ifr;
85 int fd;
86 int err;
87
Denis Vlasenko229b3d22006-11-27 23:44:57 +000088 strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000089 ifr.ifr_ifru.ifru_data = (void*)p;
Denis Vlasenko27af5a02006-09-03 12:21:59 +000090 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000091 err = ioctl(fd, SIOCGETTUNNEL, &ifr);
Glenn L McGrath8b602442002-11-28 12:19:51 +000092 if (err) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +000093 bb_perror_msg("SIOCGETTUNNEL");
Glenn L McGrath8b602442002-11-28 12:19:51 +000094 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +000095 close(fd);
96 return err;
97}
98
Denis Vlasenko540a2a12007-04-07 01:14:45 +000099/* Dies on error, otherwise returns 0 */
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000100static int do_add_ioctl(int cmd, const char *basedev, struct ip_tunnel_parm *p)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000101{
102 struct ifreq ifr;
103 int fd;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000104
Glenn L McGrath8b602442002-11-28 12:19:51 +0000105 if (cmd == SIOCCHGTUNNEL && p->name[0]) {
Denis Vlasenko229b3d22006-11-27 23:44:57 +0000106 strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name));
Glenn L McGrath8b602442002-11-28 12:19:51 +0000107 } else {
Denis Vlasenko229b3d22006-11-27 23:44:57 +0000108 strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
Glenn L McGrath8b602442002-11-28 12:19:51 +0000109 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000110 ifr.ifr_ifru.ifru_data = (void*)p;
Denis Vlasenko27af5a02006-09-03 12:21:59 +0000111 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000112 if (ioctl(fd, cmd, &ifr)) {
113 bb_perror_msg_and_die("ioctl");
Glenn L McGrath8b602442002-11-28 12:19:51 +0000114 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000115 close(fd);
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000116 return 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000117}
118
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000119/* Dies on error, otherwise returns 0 */
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000120static int do_del_ioctl(const char *basedev, struct ip_tunnel_parm *p)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000121{
122 struct ifreq ifr;
123 int fd;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000124
Glenn L McGrath8b602442002-11-28 12:19:51 +0000125 if (p->name[0]) {
Denis Vlasenko229b3d22006-11-27 23:44:57 +0000126 strncpy(ifr.ifr_name, p->name, sizeof(ifr.ifr_name));
Glenn L McGrath8b602442002-11-28 12:19:51 +0000127 } else {
Denis Vlasenko229b3d22006-11-27 23:44:57 +0000128 strncpy(ifr.ifr_name, basedev, sizeof(ifr.ifr_name));
Glenn L McGrath8b602442002-11-28 12:19:51 +0000129 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000130 ifr.ifr_ifru.ifru_data = (void*)p;
Denis Vlasenko27af5a02006-09-03 12:21:59 +0000131 fd = xsocket(AF_INET, SOCK_DGRAM, 0);
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000132 if (ioctl(fd, SIOCDELTUNNEL, &ifr)) {
133 bb_perror_msg_and_die("SIOCDELTUNNEL");
Glenn L McGrath8b602442002-11-28 12:19:51 +0000134 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000135 close(fd);
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000136 return 0;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000137}
138
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000139/* Dies on error */
140static void parse_args(int argc, char **argv, int cmd, struct ip_tunnel_parm *p)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000141{
142 int count = 0;
143 char medium[IFNAMSIZ];
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000144 memset(p, 0, sizeof(*p));
145 memset(&medium, 0, sizeof(medium));
146
147 p->iph.version = 4;
148 p->iph.ihl = 5;
149#ifndef IP_DF
150#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
151#endif
152 p->iph.frag_off = htons(IP_DF);
153
154 while (argc > 0) {
155 if (strcmp(*argv, "mode") == 0) {
156 NEXT_ARG();
157 if (strcmp(*argv, "ipip") == 0 ||
158 strcmp(*argv, "ip/ip") == 0) {
159 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPIP) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000160 bb_error_msg_and_die("you managed to ask for more than one tunnel mode");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000161 }
162 p->iph.protocol = IPPROTO_IPIP;
163 } else if (strcmp(*argv, "gre") == 0 ||
164 strcmp(*argv, "gre/ip") == 0) {
165 if (p->iph.protocol && p->iph.protocol != IPPROTO_GRE) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000166 bb_error_msg_and_die("you managed to ask for more than one tunnel mode");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000167 }
168 p->iph.protocol = IPPROTO_GRE;
169 } else if (strcmp(*argv, "sit") == 0 ||
170 strcmp(*argv, "ipv6/ip") == 0) {
171 if (p->iph.protocol && p->iph.protocol != IPPROTO_IPV6) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000172 bb_error_msg_and_die("you managed to ask for more than one tunnel mode");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000173 }
174 p->iph.protocol = IPPROTO_IPV6;
175 } else {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000176 bb_error_msg_and_die("cannot guess tunnel mode");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000177 }
178 } else if (strcmp(*argv, "key") == 0) {
179 unsigned uval;
180 NEXT_ARG();
181 p->i_flags |= GRE_KEY;
182 p->o_flags |= GRE_KEY;
183 if (strchr(*argv, '.'))
184 p->i_key = p->o_key = get_addr32(*argv);
185 else {
186 if (get_unsigned(&uval, *argv, 0)<0) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000187 bb_error_msg_and_die("invalid value of \"key\"");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000188 }
189 p->i_key = p->o_key = htonl(uval);
190 }
191 } else if (strcmp(*argv, "ikey") == 0) {
192 unsigned uval;
193 NEXT_ARG();
194 p->i_flags |= GRE_KEY;
195 if (strchr(*argv, '.'))
196 p->o_key = get_addr32(*argv);
197 else {
198 if (get_unsigned(&uval, *argv, 0)<0) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000199 bb_error_msg_and_die("invalid value of \"ikey\"");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000200 }
201 p->i_key = htonl(uval);
202 }
203 } else if (strcmp(*argv, "okey") == 0) {
204 unsigned uval;
205 NEXT_ARG();
206 p->o_flags |= GRE_KEY;
207 if (strchr(*argv, '.'))
208 p->o_key = get_addr32(*argv);
209 else {
210 if (get_unsigned(&uval, *argv, 0)<0) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000211 bb_error_msg_and_die("invalid value of \"okey\"");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000212 }
213 p->o_key = htonl(uval);
214 }
215 } else if (strcmp(*argv, "seq") == 0) {
216 p->i_flags |= GRE_SEQ;
217 p->o_flags |= GRE_SEQ;
218 } else if (strcmp(*argv, "iseq") == 0) {
219 p->i_flags |= GRE_SEQ;
220 } else if (strcmp(*argv, "oseq") == 0) {
221 p->o_flags |= GRE_SEQ;
222 } else if (strcmp(*argv, "csum") == 0) {
223 p->i_flags |= GRE_CSUM;
224 p->o_flags |= GRE_CSUM;
225 } else if (strcmp(*argv, "icsum") == 0) {
226 p->i_flags |= GRE_CSUM;
227 } else if (strcmp(*argv, "ocsum") == 0) {
228 p->o_flags |= GRE_CSUM;
229 } else if (strcmp(*argv, "nopmtudisc") == 0) {
230 p->iph.frag_off = 0;
231 } else if (strcmp(*argv, "pmtudisc") == 0) {
232 p->iph.frag_off = htons(IP_DF);
233 } else if (strcmp(*argv, "remote") == 0) {
234 NEXT_ARG();
235 if (strcmp(*argv, "any"))
236 p->iph.daddr = get_addr32(*argv);
237 } else if (strcmp(*argv, "local") == 0) {
238 NEXT_ARG();
239 if (strcmp(*argv, "any"))
240 p->iph.saddr = get_addr32(*argv);
241 } else if (strcmp(*argv, "dev") == 0) {
242 NEXT_ARG();
243 strncpy(medium, *argv, IFNAMSIZ-1);
244 } else if (strcmp(*argv, "ttl") == 0) {
245 unsigned uval;
246 NEXT_ARG();
247 if (strcmp(*argv, "inherit") != 0) {
248 if (get_unsigned(&uval, *argv, 0))
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000249 invarg(*argv, "TTL");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000250 if (uval > 255)
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000251 invarg(*argv, "TTL must be <=255");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000252 p->iph.ttl = uval;
253 }
254 } else if (strcmp(*argv, "tos") == 0 ||
255 matches(*argv, "dsfield") == 0) {
Denis Vlasenko98ee06d2006-12-31 18:57:37 +0000256 uint32_t uval;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000257 NEXT_ARG();
258 if (strcmp(*argv, "inherit") != 0) {
259 if (rtnl_dsfield_a2n(&uval, *argv))
Bernhard Reutner-Fischer19008b82006-06-07 20:17:41 +0000260 invarg(*argv, "TOS");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000261 p->iph.tos = uval;
262 } else
263 p->iph.tos = 1;
264 } else {
265 if (strcmp(*argv, "name") == 0) {
266 NEXT_ARG();
267 }
268 if (p->name[0])
269 duparg2("name", *argv);
270 strncpy(p->name, *argv, IFNAMSIZ);
271 if (cmd == SIOCCHGTUNNEL && count == 0) {
272 struct ip_tunnel_parm old_p;
273 memset(&old_p, 0, sizeof(old_p));
274 if (do_get_ioctl(*argv, &old_p))
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000275 exit(1);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000276 *p = old_p;
277 }
278 }
279 count++;
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000280 argc--;
281 argv++;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000282 }
283
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000284 if (p->iph.protocol == 0) {
285 if (memcmp(p->name, "gre", 3) == 0)
286 p->iph.protocol = IPPROTO_GRE;
287 else if (memcmp(p->name, "ipip", 4) == 0)
288 p->iph.protocol = IPPROTO_IPIP;
289 else if (memcmp(p->name, "sit", 3) == 0)
290 p->iph.protocol = IPPROTO_IPV6;
291 }
292
293 if (p->iph.protocol == IPPROTO_IPIP || p->iph.protocol == IPPROTO_IPV6) {
294 if ((p->i_flags & GRE_KEY) || (p->o_flags & GRE_KEY)) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000295 bb_error_msg_and_die("keys are not allowed with ipip and sit");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000296 }
297 }
298
299 if (medium[0]) {
300 p->link = do_ioctl_get_ifindex(medium);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000301 }
302
303 if (p->i_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
304 p->i_key = p->iph.daddr;
305 p->i_flags |= GRE_KEY;
306 }
307 if (p->o_key == 0 && IN_MULTICAST(ntohl(p->iph.daddr))) {
308 p->o_key = p->iph.daddr;
309 p->o_flags |= GRE_KEY;
310 }
311 if (IN_MULTICAST(ntohl(p->iph.daddr)) && !p->iph.saddr) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000312 bb_error_msg_and_die("broadcast tunnel requires a source address");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000313 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000314}
315
316
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000317/* Return value becomes exitcode. It's okay to not return at all */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000318static int do_add(int cmd, int argc, char **argv)
319{
320 struct ip_tunnel_parm p;
321
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000322 parse_args(argc, argv, cmd, &p);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000323
324 if (p.iph.ttl && p.iph.frag_off == 0) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000325 bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000326 }
327
328 switch (p.iph.protocol) {
329 case IPPROTO_IPIP:
330 return do_add_ioctl(cmd, "tunl0", &p);
331 case IPPROTO_GRE:
332 return do_add_ioctl(cmd, "gre0", &p);
333 case IPPROTO_IPV6:
334 return do_add_ioctl(cmd, "sit0", &p);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000335 default:
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000336 bb_error_msg_and_die("cannot determine tunnel mode (ipip, gre or sit)");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000337 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000338}
339
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000340/* Return value becomes exitcode. It's okay to not return at all */
Eric Andersen14f5c8d2005-04-16 19:39:00 +0000341static int do_del(int argc, char **argv)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000342{
343 struct ip_tunnel_parm p;
344
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000345 parse_args(argc, argv, SIOCDELTUNNEL, &p);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000346
347 switch (p.iph.protocol) {
348 case IPPROTO_IPIP:
349 return do_del_ioctl("tunl0", &p);
350 case IPPROTO_GRE:
351 return do_del_ioctl("gre0", &p);
352 case IPPROTO_IPV6:
353 return do_del_ioctl("sit0", &p);
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000354 default:
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000355 return do_del_ioctl(p.name, &p);
356 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000357}
358
Eric Andersen14f5c8d2005-04-16 19:39:00 +0000359static void print_tunnel(struct ip_tunnel_parm *p)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000360{
361 char s1[256];
362 char s2[256];
363 char s3[64];
364 char s4[64];
365
366 format_host(AF_INET, 4, &p->iph.daddr, s1, sizeof(s1));
367 format_host(AF_INET, 4, &p->iph.saddr, s2, sizeof(s2));
368 inet_ntop(AF_INET, &p->i_key, s3, sizeof(s3));
369 inet_ntop(AF_INET, &p->o_key, s4, sizeof(s4));
370
371 printf("%s: %s/ip remote %s local %s ",
372 p->name,
373 p->iph.protocol == IPPROTO_IPIP ? "ip" :
374 (p->iph.protocol == IPPROTO_GRE ? "gre" :
375 (p->iph.protocol == IPPROTO_IPV6 ? "ipv6" : "unknown")),
376 p->iph.daddr ? s1 : "any", p->iph.saddr ? s2 : "any");
377 if (p->link) {
378 char *n = do_ioctl_get_ifname(p->link);
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000379 if (n) {
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000380 printf(" dev %s ", n);
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000381 free(n);
382 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000383 }
384 if (p->iph.ttl)
385 printf(" ttl %d ", p->iph.ttl);
386 else
387 printf(" ttl inherit ");
388 if (p->iph.tos) {
389 SPRINT_BUF(b1);
390 printf(" tos");
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000391 if (p->iph.tos & 1)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000392 printf(" inherit");
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000393 if (p->iph.tos & ~1)
394 printf("%c%s ", p->iph.tos & 1 ? '/' : ' ',
395 rtnl_dsfield_n2a(p->iph.tos & ~1, b1, sizeof(b1)));
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000396 }
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000397 if (!(p->iph.frag_off & htons(IP_DF)))
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000398 printf(" nopmtudisc");
399
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000400 if ((p->i_flags & GRE_KEY) && (p->o_flags & GRE_KEY) && p->o_key == p->i_key)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000401 printf(" key %s", s3);
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000402 else if ((p->i_flags | p->o_flags) & GRE_KEY) {
403 if (p->i_flags & GRE_KEY)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000404 printf(" ikey %s ", s3);
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000405 if (p->o_flags & GRE_KEY)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000406 printf(" okey %s ", s4);
407 }
408
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000409 if (p->i_flags & GRE_SEQ)
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000410 printf("%c Drop packets out of sequence.\n", _SL_);
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000411 if (p->i_flags & GRE_CSUM)
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000412 printf("%c Checksum in received packet is required.", _SL_);
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000413 if (p->o_flags & GRE_SEQ)
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000414 printf("%c Sequence packets on output.", _SL_);
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000415 if (p->o_flags & GRE_CSUM)
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000416 printf("%c Checksum output packets.", _SL_);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000417}
418
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000419static void do_tunnels_list(struct ip_tunnel_parm *p)
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000420{
421 char name[IFNAMSIZ];
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000422 unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
423 rx_fifo, rx_frame,
424 tx_bytes, tx_packets, tx_errs, tx_drops,
425 tx_fifo, tx_colls, tx_carrier, rx_multi;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000426 int type;
427 struct ip_tunnel_parm p1;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000428 char buf[512];
429 FILE *fp = fopen("/proc/net/dev", "r");
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000430
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000431 if (fp == NULL) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000432 bb_perror_msg("fopen");
433 return;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000434 }
435
436 fgets(buf, sizeof(buf), fp);
437 fgets(buf, sizeof(buf), fp);
438
439 while (fgets(buf, sizeof(buf), fp) != NULL) {
440 char *ptr;
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000441
442 /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
443 ptr = strchr(buf, ':');
444 if (ptr == NULL ||
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000445 (*ptr++ = 0, sscanf(buf, "%s", name) != 1)) {
Denis Vlasenkoc6f188d2006-10-26 00:37:00 +0000446 bb_error_msg("wrong format of /proc/net/dev. Sorry");
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000447 return;
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000448 }
Bernhard Reutner-Fischereedd1be2006-01-12 13:15:49 +0000449 if (sscanf(ptr, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000450 &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
451 &rx_fifo, &rx_frame, &rx_multi,
452 &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
453 &tx_fifo, &tx_colls, &tx_carrier) != 14)
454 continue;
455 if (p->name[0] && strcmp(p->name, name))
456 continue;
457 type = do_ioctl_get_iftype(name);
458 if (type == -1) {
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000459 bb_error_msg("cannot get type of [%s]", name);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000460 continue;
461 }
462 if (type != ARPHRD_TUNNEL && type != ARPHRD_IPGRE && type != ARPHRD_SIT)
463 continue;
464 memset(&p1, 0, sizeof(p1));
465 if (do_get_ioctl(name, &p1))
466 continue;
467 if ((p->link && p1.link != p->link) ||
468 (p->name[0] && strcmp(p1.name, p->name)) ||
469 (p->iph.daddr && p1.iph.daddr != p->iph.daddr) ||
470 (p->iph.saddr && p1.iph.saddr != p->iph.saddr) ||
471 (p->i_key && p1.i_key != p->i_key))
472 continue;
473 print_tunnel(&p1);
Denis Vlasenkoc6f188d2006-10-26 00:37:00 +0000474 puts("");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000475 }
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000476}
477
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000478/* Return value becomes exitcode. It's okay to not return at all */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000479static int do_show(int argc, char **argv)
480{
481 int err;
482 struct ip_tunnel_parm p;
483
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000484 parse_args(argc, argv, SIOCGETTUNNEL, &p);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000485
486 switch (p.iph.protocol) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000487 case IPPROTO_IPIP:
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000488 err = do_get_ioctl(p.name[0] ? p.name : "tunl0", &p);
489 break;
490 case IPPROTO_GRE:
491 err = do_get_ioctl(p.name[0] ? p.name : "gre0", &p);
492 break;
493 case IPPROTO_IPV6:
494 err = do_get_ioctl(p.name[0] ? p.name : "sit0", &p);
495 break;
496 default:
497 do_tunnels_list(&p);
498 return 0;
499 }
500 if (err)
501 return -1;
502
503 print_tunnel(&p);
Denis Vlasenkoc6f188d2006-10-26 00:37:00 +0000504 puts("");
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000505 return 0;
506}
507
Denis Vlasenko540a2a12007-04-07 01:14:45 +0000508/* Return value becomes exitcode. It's okay to not return at all */
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000509int do_iptunnel(int argc, char **argv)
510{
511 if (argc > 0) {
512 if (matches(*argv, "add") == 0)
513 return do_add(SIOCADDTUNNEL, argc-1, argv+1);
514 if (matches(*argv, "change") == 0)
515 return do_add(SIOCCHGTUNNEL, argc-1, argv+1);
516 if (matches(*argv, "del") == 0)
517 return do_del(argc-1, argv+1);
518 if (matches(*argv, "show") == 0 ||
519 matches(*argv, "lst") == 0 ||
520 matches(*argv, "list") == 0)
521 return do_show(argc-1, argv+1);
522 } else
523 return do_show(0, NULL);
524
Denis Vlasenkoace35ee2007-01-02 16:32:16 +0000525 bb_error_msg_and_die("command \"%s\" is unknown", *argv);
Glenn L McGrath9a2d2722002-11-10 01:33:55 +0000526}