1 /* vi: set sw=4 ts=4: */
3 * iptunnel.c "ip tunnel"
5 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
7 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
11 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
12 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
13 * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
16 #include <netinet/ip.h>
18 #include <net/if_arp.h>
19 #include <asm/types.h>
21 #ifndef __constant_htons
22 #define __constant_htons htons
25 // FYI: #define SIOCDEVPRIVATE 0x89F0
27 /* From linux/if_tunnel.h. #including it proved troublesome
28 * (redefiniton errors due to name collisions in linux/ and net[inet]/) */
29 #define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
30 #define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
31 #define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
32 #define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3)
33 //#define SIOCGETPRL (SIOCDEVPRIVATE + 4)
34 //#define SIOCADDPRL (SIOCDEVPRIVATE + 5)
35 //#define SIOCDELPRL (SIOCDEVPRIVATE + 6)
36 //#define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
37 #define GRE_CSUM __constant_htons(0x8000)
38 //#define GRE_ROUTING __constant_htons(0x4000)
39 #define GRE_KEY __constant_htons(0x2000)
40 #define GRE_SEQ __constant_htons(0x1000)
41 //#define GRE_STRICT __constant_htons(0x0800)
42 //#define GRE_REC __constant_htons(0x0700)
43 //#define GRE_FLAGS __constant_htons(0x00F8)
44 //#define GRE_VERSION __constant_htons(0x0007)
45 struct ip_tunnel_parm
{
54 /* SIT-mode i_flags */
55 //#define SIT_ISATAP 0x0001
56 //struct ip_tunnel_prl {
59 // uint16_t __reserved;
61 // uint32_t __reserved2;
65 //#define PRL_DEFAULT 0x0001
67 #include "ip_common.h" /* #include "libbb.h" is inside */
73 static int do_ioctl_get_ifindex(char *dev
)
78 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
79 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
80 xioctl(fd
, SIOCGIFINDEX
, &ifr
);
82 return ifr
.ifr_ifindex
;
85 static int do_ioctl_get_iftype(char *dev
)
91 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
92 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
93 err
= ioctl_or_warn(fd
, SIOCGIFHWADDR
, &ifr
);
95 return err
? -1 : ifr
.ifr_addr
.sa_family
;
98 static char *do_ioctl_get_ifname(int idx
)
104 ifr
.ifr_ifindex
= idx
;
105 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
106 err
= ioctl_or_warn(fd
, SIOCGIFNAME
, &ifr
);
108 return err
? NULL
: xstrndup(ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
111 static int do_get_ioctl(const char *basedev
, struct ip_tunnel_parm
*p
)
117 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
118 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
119 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
120 err
= ioctl_or_warn(fd
, SIOCGETTUNNEL
, &ifr
);
125 /* Dies on error, otherwise returns 0 */
126 static int do_add_ioctl(int cmd
, const char *basedev
, struct ip_tunnel_parm
*p
)
131 if (cmd
== SIOCCHGTUNNEL
&& p
->name
[0]) {
132 strncpy_IFNAMSIZ(ifr
.ifr_name
, p
->name
);
134 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
136 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
137 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
138 #if ENABLE_IOCTL_HEX2STR_ERROR
139 /* #define magic will turn ioctl# into string */
140 if (cmd
== SIOCCHGTUNNEL
)
141 xioctl(fd
, SIOCCHGTUNNEL
, &ifr
);
143 xioctl(fd
, SIOCADDTUNNEL
, &ifr
);
145 xioctl(fd
, cmd
, &ifr
);
151 /* Dies on error, otherwise returns 0 */
152 static int do_del_ioctl(const char *basedev
, struct ip_tunnel_parm
*p
)
158 strncpy_IFNAMSIZ(ifr
.ifr_name
, p
->name
);
160 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
162 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
163 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
164 xioctl(fd
, SIOCDELTUNNEL
, &ifr
);
170 static void parse_args(char **argv
, int cmd
, struct ip_tunnel_parm
*p
)
172 static const char keywords
[] ALIGN1
=
173 "mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0"
174 "key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0"
175 "csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0"
176 "remote\0""any\0""local\0""dev\0"
177 "ttl\0""inherit\0""tos\0""dsfield\0"
180 ARG_mode
, ARG_ipip
, ARG_ip_ip
, ARG_gre
, ARG_gre_ip
, ARG_sit
, ARG_ip6_ip
,
181 ARG_key
, ARG_ikey
, ARG_okey
, ARG_seq
, ARG_iseq
, ARG_oseq
,
182 ARG_csum
, ARG_icsum
, ARG_ocsum
, ARG_nopmtudisc
, ARG_pmtudisc
,
183 ARG_remote
, ARG_any
, ARG_local
, ARG_dev
,
184 ARG_ttl
, ARG_inherit
, ARG_tos
, ARG_dsfield
,
188 char medium
[IFNAMSIZ
];
191 memset(p
, 0, sizeof(*p
));
197 #define IP_DF 0x4000 /* Flag: "Don't Fragment" */
199 p
->iph
.frag_off
= htons(IP_DF
);
202 key
= index_in_strings(keywords
, *argv
);
203 if (key
== ARG_mode
) {
205 key
= index_in_strings(keywords
, *argv
);
206 if (key
== ARG_ipip
||
208 if (p
->iph
.protocol
&& p
->iph
.protocol
!= IPPROTO_IPIP
) {
209 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
211 p
->iph
.protocol
= IPPROTO_IPIP
;
212 } else if (key
== ARG_gre
||
214 if (p
->iph
.protocol
&& p
->iph
.protocol
!= IPPROTO_GRE
) {
215 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
217 p
->iph
.protocol
= IPPROTO_GRE
;
218 } else if (key
== ARG_sit
||
220 if (p
->iph
.protocol
&& p
->iph
.protocol
!= IPPROTO_IPV6
) {
221 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
223 p
->iph
.protocol
= IPPROTO_IPV6
;
225 bb_error_msg_and_die("%s tunnel mode", "cannot guess");
227 } else if (key
== ARG_key
) {
230 p
->i_flags
|= GRE_KEY
;
231 p
->o_flags
|= GRE_KEY
;
232 if (strchr(*argv
, '.'))
233 p
->i_key
= p
->o_key
= get_addr32(*argv
);
235 uval
= get_unsigned(*argv
, "key");
236 p
->i_key
= p
->o_key
= htonl(uval
);
238 } else if (key
== ARG_ikey
) {
241 p
->i_flags
|= GRE_KEY
;
242 if (strchr(*argv
, '.'))
243 p
->o_key
= get_addr32(*argv
);
245 uval
= get_unsigned(*argv
, "ikey");
246 p
->i_key
= htonl(uval
);
248 } else if (key
== ARG_okey
) {
251 p
->o_flags
|= GRE_KEY
;
252 if (strchr(*argv
, '.'))
253 p
->o_key
= get_addr32(*argv
);
255 uval
= get_unsigned(*argv
, "okey");
256 p
->o_key
= htonl(uval
);
258 } else if (key
== ARG_seq
) {
259 p
->i_flags
|= GRE_SEQ
;
260 p
->o_flags
|= GRE_SEQ
;
261 } else if (key
== ARG_iseq
) {
262 p
->i_flags
|= GRE_SEQ
;
263 } else if (key
== ARG_oseq
) {
264 p
->o_flags
|= GRE_SEQ
;
265 } else if (key
== ARG_csum
) {
266 p
->i_flags
|= GRE_CSUM
;
267 p
->o_flags
|= GRE_CSUM
;
268 } else if (key
== ARG_icsum
) {
269 p
->i_flags
|= GRE_CSUM
;
270 } else if (key
== ARG_ocsum
) {
271 p
->o_flags
|= GRE_CSUM
;
272 } else if (key
== ARG_nopmtudisc
) {
274 } else if (key
== ARG_pmtudisc
) {
275 p
->iph
.frag_off
= htons(IP_DF
);
276 } else if (key
== ARG_remote
) {
278 key
= index_in_strings(keywords
, *argv
);
280 p
->iph
.daddr
= get_addr32(*argv
);
281 } else if (key
== ARG_local
) {
283 key
= index_in_strings(keywords
, *argv
);
285 p
->iph
.saddr
= get_addr32(*argv
);
286 } else if (key
== ARG_dev
) {
288 strncpy_IFNAMSIZ(medium
, *argv
);
289 } else if (key
== ARG_ttl
) {
292 key
= index_in_strings(keywords
, *argv
);
293 if (key
!= ARG_inherit
) {
294 uval
= get_unsigned(*argv
, "TTL");
296 invarg(*argv
, "TTL must be <=255");
299 } else if (key
== ARG_tos
||
300 key
== ARG_dsfield
) {
303 key
= index_in_strings(keywords
, *argv
);
304 if (key
!= ARG_inherit
) {
305 if (rtnl_dsfield_a2n(&uval
, *argv
))
306 invarg(*argv
, "TOS");
311 if (key
== ARG_name
) {
315 duparg2("name", *argv
);
316 strncpy_IFNAMSIZ(p
->name
, *argv
);
317 if (cmd
== SIOCCHGTUNNEL
&& count
== 0) {
318 struct ip_tunnel_parm old_p
;
319 memset(&old_p
, 0, sizeof(old_p
));
320 if (do_get_ioctl(*argv
, &old_p
))
329 if (p
->iph
.protocol
== 0) {
330 if (memcmp(p
->name
, "gre", 3) == 0)
331 p
->iph
.protocol
= IPPROTO_GRE
;
332 else if (memcmp(p
->name
, "ipip", 4) == 0)
333 p
->iph
.protocol
= IPPROTO_IPIP
;
334 else if (memcmp(p
->name
, "sit", 3) == 0)
335 p
->iph
.protocol
= IPPROTO_IPV6
;
338 if (p
->iph
.protocol
== IPPROTO_IPIP
|| p
->iph
.protocol
== IPPROTO_IPV6
) {
339 if ((p
->i_flags
& GRE_KEY
) || (p
->o_flags
& GRE_KEY
)) {
340 bb_error_msg_and_die("keys are not allowed with ipip and sit");
345 p
->link
= do_ioctl_get_ifindex(medium
);
348 if (p
->i_key
== 0 && IN_MULTICAST(ntohl(p
->iph
.daddr
))) {
349 p
->i_key
= p
->iph
.daddr
;
350 p
->i_flags
|= GRE_KEY
;
352 if (p
->o_key
== 0 && IN_MULTICAST(ntohl(p
->iph
.daddr
))) {
353 p
->o_key
= p
->iph
.daddr
;
354 p
->o_flags
|= GRE_KEY
;
356 if (IN_MULTICAST(ntohl(p
->iph
.daddr
)) && !p
->iph
.saddr
) {
357 bb_error_msg_and_die("broadcast tunnel requires a source address");
361 /* Return value becomes exitcode. It's okay to not return at all */
362 static int do_add(int cmd
, char **argv
)
364 struct ip_tunnel_parm p
;
366 parse_args(argv
, cmd
, &p
);
368 if (p
.iph
.ttl
&& p
.iph
.frag_off
== 0) {
369 bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
372 switch (p
.iph
.protocol
) {
374 return do_add_ioctl(cmd
, "tunl0", &p
);
376 return do_add_ioctl(cmd
, "gre0", &p
);
378 return do_add_ioctl(cmd
, "sit0", &p
);
380 bb_error_msg_and_die("cannot determine tunnel mode (ipip, gre or sit)");
384 /* Return value becomes exitcode. It's okay to not return at all */
385 static int do_del(char **argv
)
387 struct ip_tunnel_parm p
;
389 parse_args(argv
, SIOCDELTUNNEL
, &p
);
391 switch (p
.iph
.protocol
) {
393 return do_del_ioctl("tunl0", &p
);
395 return do_del_ioctl("gre0", &p
);
397 return do_del_ioctl("sit0", &p
);
399 return do_del_ioctl(p
.name
, &p
);
403 static void print_tunnel(struct ip_tunnel_parm
*p
)
410 format_host(AF_INET
, 4, &p
->iph
.daddr
, s1
, sizeof(s1
));
411 format_host(AF_INET
, 4, &p
->iph
.saddr
, s2
, sizeof(s2
));
412 inet_ntop(AF_INET
, &p
->i_key
, s3
, sizeof(s3
));
413 inet_ntop(AF_INET
, &p
->o_key
, s4
, sizeof(s4
));
415 printf("%s: %s/ip remote %s local %s ",
417 p
->iph
.protocol
== IPPROTO_IPIP
? "ip" :
418 (p
->iph
.protocol
== IPPROTO_GRE
? "gre" :
419 (p
->iph
.protocol
== IPPROTO_IPV6
? "ipv6" : "unknown")),
420 p
->iph
.daddr
? s1
: "any", p
->iph
.saddr
? s2
: "any");
422 char *n
= do_ioctl_get_ifname(p
->link
);
424 printf(" dev %s ", n
);
429 printf(" ttl %d ", p
->iph
.ttl
);
431 printf(" ttl inherit ");
438 printf("%c%s ", p
->iph
.tos
& 1 ? '/' : ' ',
439 rtnl_dsfield_n2a(p
->iph
.tos
& ~1, b1
, sizeof(b1
)));
441 if (!(p
->iph
.frag_off
& htons(IP_DF
)))
442 printf(" nopmtudisc");
444 if ((p
->i_flags
& GRE_KEY
) && (p
->o_flags
& GRE_KEY
) && p
->o_key
== p
->i_key
)
445 printf(" key %s", s3
);
446 else if ((p
->i_flags
| p
->o_flags
) & GRE_KEY
) {
447 if (p
->i_flags
& GRE_KEY
)
448 printf(" ikey %s ", s3
);
449 if (p
->o_flags
& GRE_KEY
)
450 printf(" okey %s ", s4
);
453 if (p
->i_flags
& GRE_SEQ
)
454 printf("%c Drop packets out of sequence.\n", _SL_
);
455 if (p
->i_flags
& GRE_CSUM
)
456 printf("%c Checksum in received packet is required.", _SL_
);
457 if (p
->o_flags
& GRE_SEQ
)
458 printf("%c Sequence packets on output.", _SL_
);
459 if (p
->o_flags
& GRE_CSUM
)
460 printf("%c Checksum output packets.", _SL_
);
463 static void do_tunnels_list(struct ip_tunnel_parm
*p
)
466 unsigned long rx_bytes
, rx_packets
, rx_errs
, rx_drops
,
468 tx_bytes
, tx_packets
, tx_errs
, tx_drops
,
469 tx_fifo
, tx_colls
, tx_carrier
, rx_multi
;
471 struct ip_tunnel_parm p1
;
473 FILE *fp
= fopen_or_warn("/proc/net/dev", "r");
479 fgets(buf
, sizeof(buf
), fp
);
480 fgets(buf
, sizeof(buf
), fp
);
482 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
485 /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
486 ptr
= strchr(buf
, ':');
488 (*ptr
++ = 0, sscanf(buf
, "%s", name
) != 1)) {
489 bb_error_msg("wrong format of /proc/net/dev");
492 if (sscanf(ptr
, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
493 &rx_bytes
, &rx_packets
, &rx_errs
, &rx_drops
,
494 &rx_fifo
, &rx_frame
, &rx_multi
,
495 &tx_bytes
, &tx_packets
, &tx_errs
, &tx_drops
,
496 &tx_fifo
, &tx_colls
, &tx_carrier
) != 14)
498 if (p
->name
[0] && strcmp(p
->name
, name
))
500 type
= do_ioctl_get_iftype(name
);
502 bb_error_msg("cannot get type of [%s]", name
);
505 if (type
!= ARPHRD_TUNNEL
&& type
!= ARPHRD_IPGRE
&& type
!= ARPHRD_SIT
)
507 memset(&p1
, 0, sizeof(p1
));
508 if (do_get_ioctl(name
, &p1
))
510 if ((p
->link
&& p1
.link
!= p
->link
) ||
511 (p
->name
[0] && strcmp(p1
.name
, p
->name
)) ||
512 (p
->iph
.daddr
&& p1
.iph
.daddr
!= p
->iph
.daddr
) ||
513 (p
->iph
.saddr
&& p1
.iph
.saddr
!= p
->iph
.saddr
) ||
514 (p
->i_key
&& p1
.i_key
!= p
->i_key
))
521 /* Return value becomes exitcode. It's okay to not return at all */
522 static int do_show(char **argv
)
525 struct ip_tunnel_parm p
;
527 parse_args(argv
, SIOCGETTUNNEL
, &p
);
529 switch (p
.iph
.protocol
) {
531 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "tunl0", &p
);
534 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "gre0", &p
);
537 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "sit0", &p
);
551 /* Return value becomes exitcode. It's okay to not return at all */
552 int do_iptunnel(char **argv
)
554 static const char keywords
[] ALIGN1
=
555 "add\0""change\0""delete\0""show\0""list\0""lst\0";
556 enum { ARG_add
= 0, ARG_change
, ARG_del
, ARG_show
, ARG_list
, ARG_lst
};
560 key
= index_in_substrings(keywords
, *argv
);
562 bb_error_msg_and_die(bb_msg_invalid_arg
, *argv
, applet_name
);
565 return do_add(SIOCADDTUNNEL
, argv
);
566 if (key
== ARG_change
)
567 return do_add(SIOCCHGTUNNEL
, argv
);
571 return do_show(argv
);