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
13 #include <netinet/ip.h>
15 #include <net/if_arp.h>
16 #include <asm/types.h>
18 #ifndef __constant_htons
19 #define __constant_htons htons
22 // FYI: #define SIOCDEVPRIVATE 0x89F0
24 /* From linux/if_tunnel.h. #including it proved troublesome
25 * (redefiniton errors due to name collisions in linux/ and net[inet]/) */
26 #define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
27 #define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
28 #define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
29 #define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3)
30 //#define SIOCGETPRL (SIOCDEVPRIVATE + 4)
31 //#define SIOCADDPRL (SIOCDEVPRIVATE + 5)
32 //#define SIOCDELPRL (SIOCDEVPRIVATE + 6)
33 //#define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
34 #define GRE_CSUM __constant_htons(0x8000)
35 //#define GRE_ROUTING __constant_htons(0x4000)
36 #define GRE_KEY __constant_htons(0x2000)
37 #define GRE_SEQ __constant_htons(0x1000)
38 //#define GRE_STRICT __constant_htons(0x0800)
39 //#define GRE_REC __constant_htons(0x0700)
40 //#define GRE_FLAGS __constant_htons(0x00F8)
41 //#define GRE_VERSION __constant_htons(0x0007)
42 struct ip_tunnel_parm
{
51 /* SIT-mode i_flags */
52 //#define SIT_ISATAP 0x0001
53 //struct ip_tunnel_prl {
56 // uint16_t __reserved;
58 // uint32_t __reserved2;
62 //#define PRL_DEFAULT 0x0001
64 #include "ip_common.h" /* #include "libbb.h" is inside */
70 static int do_ioctl_get_ifindex(char *dev
)
75 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
76 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
77 xioctl(fd
, SIOCGIFINDEX
, &ifr
);
79 return ifr
.ifr_ifindex
;
82 static int do_ioctl_get_iftype(char *dev
)
88 strncpy_IFNAMSIZ(ifr
.ifr_name
, dev
);
89 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
90 err
= ioctl_or_warn(fd
, SIOCGIFHWADDR
, &ifr
);
92 return err
? -1 : ifr
.ifr_addr
.sa_family
;
95 static char *do_ioctl_get_ifname(int idx
)
101 ifr
.ifr_ifindex
= idx
;
102 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
103 err
= ioctl_or_warn(fd
, SIOCGIFNAME
, &ifr
);
105 return err
? NULL
: xstrndup(ifr
.ifr_name
, sizeof(ifr
.ifr_name
));
108 static int do_get_ioctl(const char *basedev
, struct ip_tunnel_parm
*p
)
114 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
115 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
116 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
117 err
= ioctl_or_warn(fd
, SIOCGETTUNNEL
, &ifr
);
122 /* Dies on error, otherwise returns 0 */
123 static int do_add_ioctl(int cmd
, const char *basedev
, struct ip_tunnel_parm
*p
)
128 if (cmd
== SIOCCHGTUNNEL
&& p
->name
[0]) {
129 strncpy_IFNAMSIZ(ifr
.ifr_name
, p
->name
);
131 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
133 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
134 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
135 #if ENABLE_IOCTL_HEX2STR_ERROR
136 /* #define magic will turn ioctl# into string */
137 if (cmd
== SIOCCHGTUNNEL
)
138 xioctl(fd
, SIOCCHGTUNNEL
, &ifr
);
140 xioctl(fd
, SIOCADDTUNNEL
, &ifr
);
142 xioctl(fd
, cmd
, &ifr
);
148 /* Dies on error, otherwise returns 0 */
149 static int do_del_ioctl(const char *basedev
, struct ip_tunnel_parm
*p
)
155 strncpy_IFNAMSIZ(ifr
.ifr_name
, p
->name
);
157 strncpy_IFNAMSIZ(ifr
.ifr_name
, basedev
);
159 ifr
.ifr_ifru
.ifru_data
= (void*)p
;
160 fd
= xsocket(AF_INET
, SOCK_DGRAM
, 0);
161 xioctl(fd
, SIOCDELTUNNEL
, &ifr
);
167 static void parse_args(char **argv
, int cmd
, struct ip_tunnel_parm
*p
)
169 static const char keywords
[] ALIGN1
=
170 "mode\0""ipip\0""ip/ip\0""gre\0""gre/ip\0""sit\0""ipv6/ip\0"
171 "key\0""ikey\0""okey\0""seq\0""iseq\0""oseq\0"
172 "csum\0""icsum\0""ocsum\0""nopmtudisc\0""pmtudisc\0"
173 "remote\0""any\0""local\0""dev\0"
174 "ttl\0""inherit\0""tos\0""dsfield\0"
177 ARG_mode
, ARG_ipip
, ARG_ip_ip
, ARG_gre
, ARG_gre_ip
, ARG_sit
, ARG_ip6_ip
,
178 ARG_key
, ARG_ikey
, ARG_okey
, ARG_seq
, ARG_iseq
, ARG_oseq
,
179 ARG_csum
, ARG_icsum
, ARG_ocsum
, ARG_nopmtudisc
, ARG_pmtudisc
,
180 ARG_remote
, ARG_any
, ARG_local
, ARG_dev
,
181 ARG_ttl
, ARG_inherit
, ARG_tos
, ARG_dsfield
,
185 char medium
[IFNAMSIZ
];
188 memset(p
, 0, sizeof(*p
));
194 #define IP_DF 0x4000 /* Flag: "Don't Fragment" */
196 p
->iph
.frag_off
= htons(IP_DF
);
199 key
= index_in_strings(keywords
, *argv
);
200 if (key
== ARG_mode
) {
202 key
= index_in_strings(keywords
, *argv
);
203 if (key
== ARG_ipip
||
206 if (p
->iph
.protocol
&& p
->iph
.protocol
!= IPPROTO_IPIP
) {
207 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
209 p
->iph
.protocol
= IPPROTO_IPIP
;
210 } else if (key
== ARG_gre
||
213 if (p
->iph
.protocol
&& p
->iph
.protocol
!= IPPROTO_GRE
) {
214 bb_error_msg_and_die("%s tunnel mode", "you managed to ask for more than one");
216 p
->iph
.protocol
= IPPROTO_GRE
;
217 } 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", "can't 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_1_to_2(*argv
, "TTL");
299 } else if (key
== ARG_tos
||
304 key
= index_in_strings(keywords
, *argv
);
305 if (key
!= ARG_inherit
) {
306 if (rtnl_dsfield_a2n(&uval
, *argv
))
307 invarg_1_to_2(*argv
, "TOS");
312 if (key
== ARG_name
) {
316 duparg2("name", *argv
);
317 strncpy_IFNAMSIZ(p
->name
, *argv
);
318 if (cmd
== SIOCCHGTUNNEL
&& count
== 0) {
319 struct ip_tunnel_parm old_p
;
320 memset(&old_p
, 0, sizeof(old_p
));
321 if (do_get_ioctl(*argv
, &old_p
))
330 if (p
->iph
.protocol
== 0) {
331 if (memcmp(p
->name
, "gre", 3) == 0)
332 p
->iph
.protocol
= IPPROTO_GRE
;
333 else if (memcmp(p
->name
, "ipip", 4) == 0)
334 p
->iph
.protocol
= IPPROTO_IPIP
;
335 else if (memcmp(p
->name
, "sit", 3) == 0)
336 p
->iph
.protocol
= IPPROTO_IPV6
;
339 if (p
->iph
.protocol
== IPPROTO_IPIP
|| p
->iph
.protocol
== IPPROTO_IPV6
) {
340 if ((p
->i_flags
& GRE_KEY
) || (p
->o_flags
& GRE_KEY
)) {
341 bb_simple_error_msg_and_die("keys are not allowed with ipip and sit");
346 p
->link
= do_ioctl_get_ifindex(medium
);
349 if (p
->i_key
== 0 && IN_MULTICAST(ntohl(p
->iph
.daddr
))) {
350 p
->i_key
= p
->iph
.daddr
;
351 p
->i_flags
|= GRE_KEY
;
353 if (p
->o_key
== 0 && IN_MULTICAST(ntohl(p
->iph
.daddr
))) {
354 p
->o_key
= p
->iph
.daddr
;
355 p
->o_flags
|= GRE_KEY
;
357 if (IN_MULTICAST(ntohl(p
->iph
.daddr
)) && !p
->iph
.saddr
) {
358 bb_simple_error_msg_and_die("broadcast tunnel requires a source address");
362 /* Return value becomes exitcode. It's okay to not return at all */
363 static int do_add(int cmd
, char **argv
)
365 struct ip_tunnel_parm p
;
367 parse_args(argv
, cmd
, &p
);
369 if (p
.iph
.ttl
&& p
.iph
.frag_off
== 0) {
370 bb_simple_error_msg_and_die("ttl != 0 and noptmudisc are incompatible");
373 switch (p
.iph
.protocol
) {
375 return do_add_ioctl(cmd
, "tunl0", &p
);
377 return do_add_ioctl(cmd
, "gre0", &p
);
379 return do_add_ioctl(cmd
, "sit0", &p
);
381 bb_simple_error_msg_and_die("can't determine tunnel mode (ipip, gre or sit)");
385 /* Return value becomes exitcode. It's okay to not return at all */
386 static int do_del(char **argv
)
388 struct ip_tunnel_parm p
;
390 parse_args(argv
, SIOCDELTUNNEL
, &p
);
392 switch (p
.iph
.protocol
) {
394 return do_del_ioctl("tunl0", &p
);
396 return do_del_ioctl("gre0", &p
);
398 return do_del_ioctl("sit0", &p
);
400 return do_del_ioctl(p
.name
, &p
);
404 static void print_tunnel(struct ip_tunnel_parm
*p
)
406 char s3
[INET_ADDRSTRLEN
];
407 char s4
[INET_ADDRSTRLEN
];
409 printf("%s: %s/ip remote %s local %s ",
411 p
->iph
.protocol
== IPPROTO_IPIP
? "ip" :
412 p
->iph
.protocol
== IPPROTO_GRE
? "gre" :
413 p
->iph
.protocol
== IPPROTO_IPV6
? "ipv6" :
415 p
->iph
.daddr
? format_host(AF_INET
, 4, &p
->iph
.daddr
) : "any",
416 p
->iph
.saddr
? format_host(AF_INET
, 4, &p
->iph
.saddr
) : "any"
419 char *n
= do_ioctl_get_ifname(p
->link
);
421 printf(" dev %s ", n
);
426 printf(" ttl %d ", p
->iph
.ttl
);
428 printf(" ttl inherit ");
434 printf("%c%s ", p
->iph
.tos
& 1 ? '/' : ' ',
435 rtnl_dsfield_n2a(p
->iph
.tos
& ~1));
437 if (!(p
->iph
.frag_off
& htons(IP_DF
)))
438 printf(" nopmtudisc");
440 inet_ntop(AF_INET
, &p
->i_key
, s3
, sizeof(s3
));
441 inet_ntop(AF_INET
, &p
->o_key
, s4
, sizeof(s4
));
442 if ((p
->i_flags
& GRE_KEY
) && (p
->o_flags
& GRE_KEY
) && p
->o_key
== p
->i_key
)
443 printf(" key %s", s3
);
445 if (p
->i_flags
& GRE_KEY
)
446 printf(" ikey %s ", s3
);
447 if (p
->o_flags
& GRE_KEY
)
448 printf(" okey %s ", s4
);
451 if (p
->i_flags
& GRE_SEQ
)
452 printf("%c Drop packets out of sequence.\n", _SL_
);
453 if (p
->i_flags
& GRE_CSUM
)
454 printf("%c Checksum in received packet is required.", _SL_
);
455 if (p
->o_flags
& GRE_SEQ
)
456 printf("%c Sequence packets on output.", _SL_
);
457 if (p
->o_flags
& GRE_CSUM
)
458 printf("%c Checksum output packets.", _SL_
);
461 static void do_tunnels_list(struct ip_tunnel_parm
*p
)
464 unsigned long rx_bytes
, rx_packets
, rx_errs
, rx_drops
,
466 tx_bytes
, tx_packets
, tx_errs
, tx_drops
,
467 tx_fifo
, tx_colls
, tx_carrier
, rx_multi
;
469 struct ip_tunnel_parm p1
;
471 FILE *fp
= fopen_or_warn("/proc/net/dev", "r");
477 fgets(buf
, sizeof(buf
), fp
);
478 fgets(buf
, sizeof(buf
), fp
);
480 while (fgets(buf
, sizeof(buf
), fp
) != NULL
) {
483 /*buf[sizeof(buf) - 1] = 0; - fgets is safe anyway */
484 ptr
= strchr(buf
, ':');
486 (*ptr
++ = 0, sscanf(buf
, "%s", name
) != 1)
488 bb_simple_error_msg("wrong format of /proc/net/dev");
491 if (sscanf(ptr
, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
492 &rx_bytes
, &rx_packets
, &rx_errs
, &rx_drops
,
493 &rx_fifo
, &rx_frame
, &rx_multi
,
494 &tx_bytes
, &tx_packets
, &tx_errs
, &tx_drops
,
495 &tx_fifo
, &tx_colls
, &tx_carrier
) != 14)
497 if (p
->name
[0] && strcmp(p
->name
, name
))
499 type
= do_ioctl_get_iftype(name
);
501 bb_error_msg("can't get type of [%s]", name
);
504 if (type
!= ARPHRD_TUNNEL
&& type
!= ARPHRD_IPGRE
&& type
!= ARPHRD_SIT
)
506 memset(&p1
, 0, sizeof(p1
));
507 if (do_get_ioctl(name
, &p1
))
509 if ((p
->link
&& p1
.link
!= p
->link
) ||
510 (p
->name
[0] && strcmp(p1
.name
, p
->name
)) ||
511 (p
->iph
.daddr
&& p1
.iph
.daddr
!= p
->iph
.daddr
) ||
512 (p
->iph
.saddr
&& p1
.iph
.saddr
!= p
->iph
.saddr
) ||
513 (p
->i_key
&& p1
.i_key
!= p
->i_key
)
522 /* Return value becomes exitcode. It's okay to not return at all */
523 static int do_show(char **argv
)
526 struct ip_tunnel_parm p
;
528 parse_args(argv
, SIOCGETTUNNEL
, &p
);
530 switch (p
.iph
.protocol
) {
532 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "tunl0", &p
);
535 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "gre0", &p
);
538 err
= do_get_ioctl(p
.name
[0] ? p
.name
: "sit0", &p
);
552 /* Return value becomes exitcode. It's okay to not return at all */
553 int FAST_FUNC
do_iptunnel(char **argv
)
555 static const char keywords
[] ALIGN1
=
556 "add\0""change\0""delete\0""show\0""list\0""lst\0";
557 enum { ARG_add
= 0, ARG_change
, ARG_del
, ARG_show
, ARG_list
, ARG_lst
};
560 int key
= index_in_substrings(keywords
, *argv
);
562 invarg_1_to_2(*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
);