1 /* vi: set sw=4 ts=4: */
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
9 * Rani Assaf <rani@magic.metawire.com> 980929: resolve addresses
10 * Rani Assaf <rani@magic.metawire.com> 980930: do not allow key for ipip/sit
11 * Phil Karn <karn@ka9q.ampr.org> 990408: "pmtudisc" flag
14 #include <netinet/ip.h>
16 #include <net/if_arp.h>
17 #include <asm/types.h>
19 #ifndef __constant_htons
20 #define __constant_htons htons
23 // FYI: #define SIOCDEVPRIVATE 0x89F0
25 /* From linux/if_tunnel.h. #including it proved troublesome
26 * (redefiniton errors due to name collisions in linux/ and net[inet]/) */
27 #define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
28 #define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
29 #define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
30 #define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3)
31 //#define SIOCGETPRL (SIOCDEVPRIVATE + 4)
32 //#define SIOCADDPRL (SIOCDEVPRIVATE + 5)
33 //#define SIOCDELPRL (SIOCDEVPRIVATE + 6)
34 //#define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
35 #define GRE_CSUM __constant_htons(0x8000)
36 //#define GRE_ROUTING __constant_htons(0x4000)
37 #define GRE_KEY __constant_htons(0x2000)
38 #define GRE_SEQ __constant_htons(0x1000)
39 //#define GRE_STRICT __constant_htons(0x0800)
40 //#define GRE_REC __constant_htons(0x0700)
41 //#define GRE_FLAGS __constant_htons(0x00F8)
42 //#define GRE_VERSION __constant_htons(0x0007)
43 struct ip_tunnel_parm
{
52 /* SIT-mode i_flags */
53 //#define SIT_ISATAP 0x0001
54 //struct ip_tunnel_prl {
57 // uint16_t __reserved;
59 // uint32_t __reserved2;
63 //#define PRL_DEFAULT 0x0001
65 #include "ip_common.h" /* #include "libbb.h" is inside */
71 static int do_ioctl_get_ifindex(char *dev
)
76 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
77 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
78 xioctl(fd
, SIOCGIFINDEX
, &ifr
);
80 return ifr
.ifr_ifindex
;
83 static int do_ioctl_get_iftype(char *dev
)
89 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
90 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
91 err
= ioctl_or_warn(fd
, SIOCGIFHWADDR
, &ifr
);
93 return err
? -1 : ifr
.ifr_addr
.sa_family
;
96 static char *do_ioctl_get_ifname(int idx
)
102 ifr
.ifr_ifindex
= idx
;
103 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
104 err
= ioctl_or_warn(fd
, SIOCGIFNAME
, &ifr
);
106 return err
? NULL
: xstrndup(ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
109 static int do_get_ioctl(const char *basedev
, struct ip_tunnel_parm
*p
)
115 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
116 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
117 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
118 err
= ioctl_or_warn(fd
, SIOCGETTUNNEL
, &ifr
);
123 /* Dies on error, otherwise returns 0 */
124 static int do_add_ioctl(int cmd
, const char *basedev
, struct ip_tunnel_parm
*p
)
129 if (cmd
== SIOCCHGTUNNEL
&& p
->name
[0]) {
130 strncpy_IFNAMSIZ(ifr
.ifr_name
, p
->name
);
132 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
134 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
135 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
136 #if ENABLE_IOCTL_HEX2STR_ERROR
137 /* #define magic will turn ioctl# into string */
138 if (cmd
== SIOCCHGTUNNEL
)
139 xioctl(fd
, SIOCCHGTUNNEL
, &ifr
);
141 xioctl(fd
, SIOCADDTUNNEL
, &ifr
);
143 xioctl(fd
, cmd
, &ifr
);
149 /* Dies on error, otherwise returns 0 */
150 static int do_del_ioctl(const char *basedev
, struct ip_tunnel_parm
*p
)
156 strncpy_IFNAMSIZ(ifr
.ifr_name
, p
->name
);
158 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
160 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
161 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
162 xioctl(fd
, SIOCDELTUNNEL
, &ifr
);
168 static void parse_args(char **argv
, int cmd
, struct ip_tunnel_parm
*p
)
170 static const char keywords
[] ALIGN1
=
171 "mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0"
172 "key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0"
173 "csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0"
174 "remote\0""any\0""local\0""dev\0"
175 "ttl\0""inherit\0""tos\0""dsfield\0"
178 ARG_mode
, ARG_ipip
, ARG_ip_ip
, ARG_gre
, ARG_gre_ip
, ARG_sit
, ARG_ip6_ip
,
179 ARG_key
, ARG_ikey
, ARG_okey
, ARG_seq
, ARG_iseq
, ARG_oseq
,
180 ARG_csum
, ARG_icsum
, ARG_ocsum
, ARG_nopmtudisc
, ARG_pmtudisc
,
181 ARG_remote
, ARG_any
, ARG_local
, ARG_dev
,
182 ARG_ttl
, ARG_inherit
, ARG_tos
, ARG_dsfield
,
186 char medium
[IFNAMSIZ
];
189 memset(p
, 0, sizeof(*p
));
195 #define IP_DF 0x4000 /* Flag: "Don't Fragment" */
197 p
->iph
.frag_off
= htons(IP_DF
);
200 key
= index_in_strings(keywords
, *argv
);
201 if (key
== ARG_mode
) {
203 key
= index_in_strings(keywords
, *argv
);
204 if (key
== ARG_ipip
||
207 if (p
->iph
.protocol
&& p
->iph
.protocol
!= IPPROTO_IPIP
) {
208 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
210 p
->iph
.protocol
= IPPROTO_IPIP
;
211 } 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
||
221 if (p
->iph
.protocol
&& p
->iph
.protocol
!= IPPROTO_IPV6
) {
222 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
224 p
->iph
.protocol
= IPPROTO_IPV6
;
226 bb_error_msg_and_die("%s tunnel mode", "can't guess");
228 } else if (key
== ARG_key
) {
231 p
->i_flags
|= GRE_KEY
;
232 p
->o_flags
|= GRE_KEY
;
233 if (strchr(*argv
, '.'))
234 p
->i_key
= p
->o_key
= get_addr32(*argv
);
236 uval
= get_unsigned(*argv
, "key");
237 p
->i_key
= p
->o_key
= htonl(uval
);
239 } else if (key
== ARG_ikey
) {
242 p
->i_flags
|= GRE_KEY
;
243 if (strchr(*argv
, '.'))
244 p
->o_key
= get_addr32(*argv
);
246 uval
= get_unsigned(*argv
, "ikey");
247 p
->i_key
= htonl(uval
);
249 } else if (key
== ARG_okey
) {
252 p
->o_flags
|= GRE_KEY
;
253 if (strchr(*argv
, '.'))
254 p
->o_key
= get_addr32(*argv
);
256 uval
= get_unsigned(*argv
, "okey");
257 p
->o_key
= htonl(uval
);
259 } else if (key
== ARG_seq
) {
260 p
->i_flags
|= GRE_SEQ
;
261 p
->o_flags
|= GRE_SEQ
;
262 } else if (key
== ARG_iseq
) {
263 p
->i_flags
|= GRE_SEQ
;
264 } else if (key
== ARG_oseq
) {
265 p
->o_flags
|= GRE_SEQ
;
266 } else if (key
== ARG_csum
) {
267 p
->i_flags
|= GRE_CSUM
;
268 p
->o_flags
|= GRE_CSUM
;
269 } else if (key
== ARG_icsum
) {
270 p
->i_flags
|= GRE_CSUM
;
271 } else if (key
== ARG_ocsum
) {
272 p
->o_flags
|= GRE_CSUM
;
273 } else if (key
== ARG_nopmtudisc
) {
275 } else if (key
== ARG_pmtudisc
) {
276 p
->iph
.frag_off
= htons(IP_DF
);
277 } else if (key
== ARG_remote
) {
279 key
= index_in_strings(keywords
, *argv
);
281 p
->iph
.daddr
= get_addr32(*argv
);
282 } else if (key
== ARG_local
) {
284 key
= index_in_strings(keywords
, *argv
);
286 p
->iph
.saddr
= get_addr32(*argv
);
287 } else if (key
== ARG_dev
) {
289 strncpy_IFNAMSIZ(medium
, *argv
);
290 } else if (key
== ARG_ttl
) {
293 key
= index_in_strings(keywords
, *argv
);
294 if (key
!= ARG_inherit
) {
295 uval
= get_unsigned(*argv
, "TTL");
297 invarg(*argv
, "TTL must be <=255");
300 } else if (key
== ARG_tos
||
305 key
= index_in_strings(keywords
, *argv
);
306 if (key
!= ARG_inherit
) {
307 if (rtnl_dsfield_a2n(&uval
, *argv
))
308 invarg(*argv
, "TOS");
313 if (key
== ARG_name
) {
317 duparg2("name", *argv
);
318 strncpy_IFNAMSIZ(p
->name
, *argv
);
319 if (cmd
== SIOCCHGTUNNEL
&& count
== 0) {
320 struct ip_tunnel_parm old_p
;
321 memset(&old_p
, 0, sizeof(old_p
));
322 if (do_get_ioctl(*argv
, &old_p
))
331 if (p
->iph
.protocol
== 0) {
332 if (memcmp(p
->name
, "gre", 3) == 0)
333 p
->iph
.protocol
= IPPROTO_GRE
;
334 else if (memcmp(p
->name
, "ipip", 4) == 0)
335 p
->iph
.protocol
= IPPROTO_IPIP
;
336 else if (memcmp(p
->name
, "sit", 3) == 0)
337 p
->iph
.protocol
= IPPROTO_IPV6
;
340 if (p
->iph
.protocol
== IPPROTO_IPIP
|| p
->iph
.protocol
== IPPROTO_IPV6
) {
341 if ((p
->i_flags
& GRE_KEY
) || (p
->o_flags
& GRE_KEY
)) {
342 bb_error_msg_and_die("keys are not allowed with ipip and sit");
347 p
->link
= do_ioctl_get_ifindex(medium
);
350 if (p
->i_key
== 0 && IN_MULTICAST(ntohl(p
->iph
.daddr
))) {
351 p
->i_key
= p
->iph
.daddr
;
352 p
->i_flags
|= GRE_KEY
;
354 if (p
->o_key
== 0 && IN_MULTICAST(ntohl(p
->iph
.daddr
))) {
355 p
->o_key
= p
->iph
.daddr
;
356 p
->o_flags
|= GRE_KEY
;
358 if (IN_MULTICAST(ntohl(p
->iph
.daddr
)) && !p
->iph
.saddr
) {
359 bb_error_msg_and_die("broadcast tunnel requires a source address");
363 /* Return value becomes exitcode. It's okay to not return at all */
364 static int do_add(int cmd
, char **argv
)
366 struct ip_tunnel_parm p
;
368 parse_args(argv
, cmd
, &p
);
370 if (p
.iph
.ttl
&& p
.iph
.frag_off
== 0) {
371 bb_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
374 switch (p
.iph
.protocol
) {
376 return do_add_ioctl(cmd
, "tunl0", &p
);
378 return do_add_ioctl(cmd
, "gre0", &p
);
380 return do_add_ioctl(cmd
, "sit0", &p
);
382 bb_error_msg_and_die("can't determine tunnel mode (ipip, gre or sit)");
386 /* Return value becomes exitcode. It's okay to not return at all */
387 static int do_del(char **argv
)
389 struct ip_tunnel_parm p
;
391 parse_args(argv
, SIOCDELTUNNEL
, &p
);
393 switch (p
.iph
.protocol
) {
395 return do_del_ioctl("tunl0", &p
);
397 return do_del_ioctl("gre0", &p
);
399 return do_del_ioctl("sit0", &p
);
401 return do_del_ioctl(p
.name
, &p
);
405 static void print_tunnel(struct ip_tunnel_parm
*p
)
412 format_host(AF_INET
, 4, &p
->iph
.daddr
, s1
, sizeof(s1
));
413 format_host(AF_INET
, 4, &p
->iph
.saddr
, s2
, sizeof(s2
));
414 inet_ntop(AF_INET
, &p
->i_key
, s3
, sizeof(s3
));
415 inet_ntop(AF_INET
, &p
->o_key
, s4
, sizeof(s4
));
417 printf("%s: %s/ip remote %s local %s ",
419 p
->iph
.protocol
== IPPROTO_IPIP
? "ip" :
420 (p
->iph
.protocol
== IPPROTO_GRE
? "gre" :
421 (p
->iph
.protocol
== IPPROTO_IPV6
? "ipv6" : "unknown")),
422 p
->iph
.daddr
? s1
: "any", p
->iph
.saddr
? s2
: "any");
424 char *n
= do_ioctl_get_ifname(p
->link
);
426 printf(" dev %s ", n
);
431 printf(" ttl %d ", p
->iph
.ttl
);
433 printf(" ttl inherit ");
440 printf("%c%s ", p
->iph
.tos
& 1 ? '/' : ' ',
441 rtnl_dsfield_n2a(p
->iph
.tos
& ~1, b1
));
443 if (!(p
->iph
.frag_off
& htons(IP_DF
)))
444 printf(" nopmtudisc");
446 if ((p
->i_flags
& GRE_KEY
) && (p
->o_flags
& GRE_KEY
) && p
->o_key
== p
->i_key
)
447 printf(" key %s", s3
);
448 else if ((p
->i_flags
| p
->o_flags
) & GRE_KEY
) {
449 if (p
->i_flags
& GRE_KEY
)
450 printf(" ikey %s ", s3
);
451 if (p
->o_flags
& GRE_KEY
)
452 printf(" okey %s ", s4
);
455 if (p
->i_flags
& GRE_SEQ
)
456 printf("%c Drop packets out of sequence.\n", _SL_
);
457 if (p
->i_flags
& GRE_CSUM
)
458 printf("%c Checksum in received packet is required.", _SL_
);
459 if (p
->o_flags
& GRE_SEQ
)
460 printf("%c Sequence packets on output.", _SL_
);
461 if (p
->o_flags
& GRE_CSUM
)
462 printf("%c Checksum output packets.", _SL_
);
465 static void do_tunnels_list(struct ip_tunnel_parm
*p
)
468 unsigned long rx_bytes
, rx_packets
, rx_errs
, rx_drops
,
470 tx_bytes
, tx_packets
, tx_errs
, tx_drops
,
471 tx_fifo
, tx_colls
, tx_carrier
, rx_multi
;
473 struct ip_tunnel_parm p1
;
475 FILE *fp
= fopen_or_warn("/proc/net/dev", "r");
481 fgets(buf
, sizeof(buf
), fp
);
482 fgets(buf
, sizeof(buf
), fp
);
484 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
487 /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
488 ptr
= strchr(buf
, ':');
490 (*ptr
++ = 0, sscanf(buf
, "%s", name
) != 1)
492 bb_error_msg("wrong format of /proc/net/dev");
495 if (sscanf(ptr
, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
496 &rx_bytes
, &rx_packets
, &rx_errs
, &rx_drops
,
497 &rx_fifo
, &rx_frame
, &rx_multi
,
498 &tx_bytes
, &tx_packets
, &tx_errs
, &tx_drops
,
499 &tx_fifo
, &tx_colls
, &tx_carrier
) != 14)
501 if (p
->name
[0] && strcmp(p
->name
, name
))
503 type
= do_ioctl_get_iftype(name
);
505 bb_error_msg("can't get type of [%s]", name
);
508 if (type
!= ARPHRD_TUNNEL
&& type
!= ARPHRD_IPGRE
&& type
!= ARPHRD_SIT
)
510 memset(&p1
, 0, sizeof(p1
));
511 if (do_get_ioctl(name
, &p1
))
513 if ((p
->link
&& p1
.link
!= p
->link
) ||
514 (p
->name
[0] && strcmp(p1
.name
, p
->name
)) ||
515 (p
->iph
.daddr
&& p1
.iph
.daddr
!= p
->iph
.daddr
) ||
516 (p
->iph
.saddr
&& p1
.iph
.saddr
!= p
->iph
.saddr
) ||
517 (p
->i_key
&& p1
.i_key
!= p
->i_key
)
526 /* Return value becomes exitcode. It's okay to not return at all */
527 static int do_show(char **argv
)
530 struct ip_tunnel_parm p
;
532 parse_args(argv
, SIOCGETTUNNEL
, &p
);
534 switch (p
.iph
.protocol
) {
536 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "tunl0", &p
);
539 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "gre0", &p
);
542 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "sit0", &p
);
556 /* Return value becomes exitcode. It's okay to not return at all */
557 int FAST_FUNC
do_iptunnel(char **argv
)
559 static const char keywords
[] ALIGN1
=
560 "add\0""change\0""delete\0""show\0""list\0""lst\0";
561 enum { ARG_add
= 0, ARG_change
, ARG_del
, ARG_show
, ARG_list
, ARG_lst
};
564 smalluint key
= index_in_substrings(keywords
, *argv
);
566 bb_error_msg_and_die(bb_msg_invalid_arg
, *argv
, applet_name
);
569 return do_add(SIOCADDTUNNEL
, argv
);
570 if (key
== ARG_change
)
571 return do_add(SIOCCHGTUNNEL
, argv
);
575 return do_show(argv
);