16c54378cSDavid du Colombier /*
212d7cf04SDavid du Colombier * 6in4 - tunnel client for automatic 6to4 or configured v6-in-v4 tunnels.
312d7cf04SDavid du Colombier * see rfc3056.
46c54378cSDavid du Colombier */
56c54378cSDavid du Colombier
66c54378cSDavid du Colombier #include <u.h>
76c54378cSDavid du Colombier #include <libc.h>
86c54378cSDavid du Colombier #include <ip.h>
96c54378cSDavid du Colombier
1095fd729bSDavid du Colombier /*
1195fd729bSDavid du Colombier * IPv6 and related IP protocols & their numbers:
1295fd729bSDavid du Colombier *
1395fd729bSDavid du Colombier * ipv6 41 IPv6 # Internet Protocol, version 6
1495fd729bSDavid du Colombier * ipv6-route 43 IPv6-Route # Routing Header for IPv6
1595fd729bSDavid du Colombier * ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6
1695fd729bSDavid du Colombier * esp 50 ESP # Encapsulating Security Payload
1795fd729bSDavid du Colombier * ah 51 AH # Authentication Header
1895fd729bSDavid du Colombier * ipv6-icmp 58 IPv6-ICMP icmp6 # ICMP version 6
1995fd729bSDavid du Colombier * ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6
2095fd729bSDavid du Colombier * ipv6-opts 60 IPv6-Opts # Destination Options for IPv6
2195fd729bSDavid du Colombier */
2295fd729bSDavid du Colombier
236c54378cSDavid du Colombier enum {
2412d7cf04SDavid du Colombier IP_IPV6PROTO = 41, /* IPv4 protocol number for IPv6 */
2595fd729bSDavid du Colombier IP_ESPPROTO = 50, /* IP v4 and v6 protocol number */
2695fd729bSDavid du Colombier IP_AHPROTO = 51, /* IP v4 and v6 protocol number */
2795fd729bSDavid du Colombier IP_ICMPV6PROTO = 58,
28ea813757SDavid du Colombier V6to4pfx = 0x2002,
296c54378cSDavid du Colombier };
306c54378cSDavid du Colombier
3112d7cf04SDavid du Colombier typedef struct Iphdr Iphdr;
3212d7cf04SDavid du Colombier struct Iphdr
3312d7cf04SDavid du Colombier {
3412d7cf04SDavid du Colombier uchar vihl; /* Version and header length */
3512d7cf04SDavid du Colombier uchar tos; /* Type of service */
3612d7cf04SDavid du Colombier uchar length[2]; /* packet length */
3712d7cf04SDavid du Colombier uchar id[2]; /* Identification */
3812d7cf04SDavid du Colombier uchar frag[2]; /* Fragment information */
3912d7cf04SDavid du Colombier uchar ttl; /* Time to live */
4012d7cf04SDavid du Colombier uchar proto; /* Protocol */
4112d7cf04SDavid du Colombier uchar cksum[2]; /* Header checksum */
4212d7cf04SDavid du Colombier uchar src[4]; /* Ip source (uchar ordering unimportant) */
4312d7cf04SDavid du Colombier uchar dst[4]; /* Ip destination (uchar ordering unimportant) */
4494aa1c4cSDavid du Colombier uchar payload[];
4512d7cf04SDavid du Colombier };
4612d7cf04SDavid du Colombier
4794aa1c4cSDavid du Colombier #define STFHDR offsetof(Iphdr, payload[0])
4812d7cf04SDavid du Colombier
496c54378cSDavid du Colombier int anysender;
506c54378cSDavid du Colombier int gateway;
5112d7cf04SDavid du Colombier int debug;
526c54378cSDavid du Colombier
536c54378cSDavid du Colombier uchar local6[IPaddrlen];
546c54378cSDavid du Colombier uchar remote6[IPaddrlen];
556c54378cSDavid du Colombier uchar remote4[IPaddrlen];
566c54378cSDavid du Colombier uchar localmask[IPaddrlen];
576c54378cSDavid du Colombier uchar localnet[IPaddrlen];
586c54378cSDavid du Colombier uchar myip[IPaddrlen];
596c54378cSDavid du Colombier
606c54378cSDavid du Colombier /* magic anycast address from rfc3068 */
616c54378cSDavid du Colombier uchar anycast6to4[IPv4addrlen] = { 192, 88, 99, 1 };
626c54378cSDavid du Colombier
636c54378cSDavid du Colombier static char *net = "/net";
646c54378cSDavid du Colombier
656c54378cSDavid du Colombier static int badipv4(uchar*);
666c54378cSDavid du Colombier static int badipv6(uchar*);
676c54378cSDavid du Colombier static void ip2tunnel(int, int);
686c54378cSDavid du Colombier static void tunnel2ip(int, int);
696c54378cSDavid du Colombier
706c54378cSDavid du Colombier static void
usage(void)716c54378cSDavid du Colombier usage(void)
726c54378cSDavid du Colombier {
73059f8267SDavid du Colombier fprint(2, "usage: %s [-ag] [-x mtpt] [local6[/mask]] [remote4 [remote6]]\n",
746c54378cSDavid du Colombier argv0);
756c54378cSDavid du Colombier exits("Usage");
766c54378cSDavid du Colombier }
776c54378cSDavid du Colombier
7812d7cf04SDavid du Colombier static char *
defv6addr(void)7912d7cf04SDavid du Colombier defv6addr(void)
806c54378cSDavid du Colombier {
8112d7cf04SDavid du Colombier uchar *ipv4 = &myip[IPaddrlen - IPv4addrlen];
826c54378cSDavid du Colombier
8312d7cf04SDavid du Colombier return smprint("%ux:%2.2x%2.2x:%2.2x%2.2x::1/48", V6to4pfx,
8412d7cf04SDavid du Colombier ipv4[0], ipv4[1], ipv4[2], ipv4[3]);
8512d7cf04SDavid du Colombier }
866c54378cSDavid du Colombier
8712d7cf04SDavid du Colombier /* process non-option arguments */
8812d7cf04SDavid du Colombier static void
procargs(int argc,char ** argv)8912d7cf04SDavid du Colombier procargs(int argc, char **argv)
9012d7cf04SDavid du Colombier {
9112d7cf04SDavid du Colombier char *p, *loc6;
92059f8267SDavid du Colombier
936c54378cSDavid du Colombier if (argc < 1)
9412d7cf04SDavid du Colombier loc6 = defv6addr();
9512d7cf04SDavid du Colombier else if (strcmp(argv[0], "-") == 0) {
9612d7cf04SDavid du Colombier loc6 = defv6addr();
97059f8267SDavid du Colombier argv++;
98059f8267SDavid du Colombier argc--;
9912d7cf04SDavid du Colombier } else {
10012d7cf04SDavid du Colombier loc6 = *argv++;
10112d7cf04SDavid du Colombier argc--;
102059f8267SDavid du Colombier }
1036c54378cSDavid du Colombier
1046c54378cSDavid du Colombier /* local v6 address (mask defaults to /128) */
1056c54378cSDavid du Colombier memcpy(localmask, IPallbits, sizeof localmask);
106059f8267SDavid du Colombier p = strchr(loc6, '/');
107059f8267SDavid du Colombier if (p != nil) {
1086c54378cSDavid du Colombier parseipmask(localmask, p);
1096c54378cSDavid du Colombier *p = 0;
1106c54378cSDavid du Colombier }
111ea58ad6fSDavid du Colombier if (parseip(local6, loc6) == -1)
112ea58ad6fSDavid du Colombier sysfatal("bad local v6 address %s", loc6);
1136c54378cSDavid du Colombier if (isv4(local6))
1146c54378cSDavid du Colombier usage();
1156c54378cSDavid du Colombier if (argc >= 1 && argv[0][0] == '/') {
11612d7cf04SDavid du Colombier parseipmask(localmask, *argv++);
1176c54378cSDavid du Colombier argc--;
1186c54378cSDavid du Colombier }
11912d7cf04SDavid du Colombier if (debug)
12012d7cf04SDavid du Colombier fprint(2, "local6 %I %M\n", local6, localmask);
1216c54378cSDavid du Colombier
1226c54378cSDavid du Colombier /* remote v4 address (defaults to anycast 6to4) */
1236c54378cSDavid du Colombier if (argc >= 1) {
124ea58ad6fSDavid du Colombier if (parseip(remote4, *argv++) == -1)
125ea58ad6fSDavid du Colombier sysfatal("bad remote v4 address %s", argv[-1]);
12612d7cf04SDavid du Colombier argc--;
1276c54378cSDavid du Colombier if (!isv4(remote4))
1286c54378cSDavid du Colombier usage();
1296c54378cSDavid du Colombier } else {
1306c54378cSDavid du Colombier v4tov6(remote4, anycast6to4);
1316c54378cSDavid du Colombier anysender++;
1326c54378cSDavid du Colombier }
13312d7cf04SDavid du Colombier if (debug)
13412d7cf04SDavid du Colombier fprint(2, "remote4 %I\n", remote4);
1356c54378cSDavid du Colombier
1366c54378cSDavid du Colombier /* remote v6 address (defaults to link-local w/ v4 as interface part) */
1376c54378cSDavid du Colombier if (argc >= 1) {
138ea58ad6fSDavid du Colombier if (parseip(remote6, *argv++) == -1)
139ea58ad6fSDavid du Colombier sysfatal("bad remote v6 address %s", argv[-1]);
14012d7cf04SDavid du Colombier argc--;
1416c54378cSDavid du Colombier } else {
1426c54378cSDavid du Colombier remote6[0] = 0xFE; /* link local */
1436c54378cSDavid du Colombier remote6[1] = 0x80;
1446c54378cSDavid du Colombier memcpy(remote6 + IPv4off, remote4 + IPv4off, IPv4addrlen);
1456c54378cSDavid du Colombier }
1466c54378cSDavid du Colombier USED(argv);
1476c54378cSDavid du Colombier if (argc != 0)
1486c54378cSDavid du Colombier usage();
1496c54378cSDavid du Colombier
1506c54378cSDavid du Colombier maskip(local6, localmask, localnet);
15112d7cf04SDavid du Colombier if (debug)
15212d7cf04SDavid du Colombier fprint(2, "localnet %I remote6 %I\n", localnet, remote6);
15312d7cf04SDavid du Colombier }
15412d7cf04SDavid du Colombier
15512d7cf04SDavid du Colombier static void
setup(int * v6net,int * tunp)15612d7cf04SDavid du Colombier setup(int *v6net, int *tunp)
15712d7cf04SDavid du Colombier {
15812d7cf04SDavid du Colombier int n, cfd;
15912d7cf04SDavid du Colombier char *p, *cl, *ir;
16012d7cf04SDavid du Colombier char buf[128], path[64];
1616c54378cSDavid du Colombier
1626c54378cSDavid du Colombier /*
163*f80c7c99SDavid du Colombier * gain access to IPv6-in-IPv4 packets via ipmux
1646c54378cSDavid du Colombier */
16595fd729bSDavid du Colombier p = seprint(buf, buf + sizeof buf, "%s/ipmux!proto=%2.2x|%2.2x;dst=%V",
16695fd729bSDavid du Colombier net, IP_IPV6PROTO, IP_ICMPV6PROTO, myip + IPv4off);
1676c54378cSDavid du Colombier if (!anysender)
1686c54378cSDavid du Colombier seprint(p, buf + sizeof buf, ";src=%V", remote4 + IPv4off);
16912d7cf04SDavid du Colombier *tunp = dial(buf, 0, 0, 0);
17012d7cf04SDavid du Colombier if (*tunp < 0)
17112d7cf04SDavid du Colombier sysfatal("can't access ipv6-in-ipv4 with dial str %s: %r", buf);
17212d7cf04SDavid du Colombier if (debug)
17312d7cf04SDavid du Colombier fprint(2, "dialed %s for v6-in-v4 access\n", buf);
1746c54378cSDavid du Colombier
1756c54378cSDavid du Colombier /*
1766c54378cSDavid du Colombier * open local IPv6 interface (as a packet interface)
1776c54378cSDavid du Colombier */
17812d7cf04SDavid du Colombier
1796c54378cSDavid du Colombier cl = smprint("%s/ipifc/clone", net);
1806c54378cSDavid du Colombier cfd = open(cl, ORDWR); /* allocate a conversation */
1816c54378cSDavid du Colombier n = 0;
1826c54378cSDavid du Colombier if (cfd < 0 || (n = read(cfd, buf, sizeof buf - 1)) <= 0)
18312d7cf04SDavid du Colombier sysfatal("can't make packet interface %s: %r", cl);
18412d7cf04SDavid du Colombier if (debug)
18512d7cf04SDavid du Colombier fprint(2, "cloned %s as local v6 interface\n", cl);
18612d7cf04SDavid du Colombier free(cl);
1876c54378cSDavid du Colombier buf[n] = 0;
1886c54378cSDavid du Colombier
1896c54378cSDavid du Colombier snprint(path, sizeof path, "%s/ipifc/%s/data", net, buf);
19012d7cf04SDavid du Colombier *v6net = open(path, ORDWR);
19112d7cf04SDavid du Colombier if (*v6net < 0 || fprint(cfd, "bind pkt") < 0)
1926c54378cSDavid du Colombier sysfatal("can't bind packet interface: %r");
1936c54378cSDavid du Colombier /* 1280 is MTU, apparently from rfc2460 */
1946c54378cSDavid du Colombier if (fprint(cfd, "add %I /128 %I 1280", local6, remote6) <= 0)
1956c54378cSDavid du Colombier sysfatal("can't set local ipv6 address: %r");
1966c54378cSDavid du Colombier close(cfd);
19712d7cf04SDavid du Colombier if (debug)
19812d7cf04SDavid du Colombier fprint(2, "opened & bound %s as local v6 interface\n", path);
1996c54378cSDavid du Colombier
2006c54378cSDavid du Colombier if (gateway) {
2016c54378cSDavid du Colombier /* route global addresses through the tunnel to remote6 */
2026c54378cSDavid du Colombier ir = smprint("%s/iproute", net);
2036c54378cSDavid du Colombier cfd = open(ir, OWRITE);
20412d7cf04SDavid du Colombier if (cfd >= 0 && debug)
20512d7cf04SDavid du Colombier fprint(2, "injected 2000::/3 %I into %s\n", remote6, ir);
2066c54378cSDavid du Colombier free(ir);
2076c54378cSDavid du Colombier if (cfd < 0 || fprint(cfd, "add 2000:: /3 %I", remote6) <= 0)
2086c54378cSDavid du Colombier sysfatal("can't set default global route: %r");
2096c54378cSDavid du Colombier }
21012d7cf04SDavid du Colombier }
2116c54378cSDavid du Colombier
21212d7cf04SDavid du Colombier static void
runtunnel(int v6net,int tunnel)21312d7cf04SDavid du Colombier runtunnel(int v6net, int tunnel)
21412d7cf04SDavid du Colombier {
215868c5196SDavid du Colombier /* run the tunnel copying in the background */
216868c5196SDavid du Colombier switch (rfork(RFPROC|RFNOWAIT|RFMEM|RFNOTEG)) {
21712d7cf04SDavid du Colombier case -1:
21812d7cf04SDavid du Colombier sysfatal("rfork");
219868c5196SDavid du Colombier default:
220868c5196SDavid du Colombier exits(nil);
221868c5196SDavid du Colombier case 0:
222868c5196SDavid du Colombier break;
223868c5196SDavid du Colombier }
224868c5196SDavid du Colombier
225868c5196SDavid du Colombier switch (rfork(RFPROC|RFNOWAIT|RFMEM)) {
226868c5196SDavid du Colombier case -1:
227868c5196SDavid du Colombier sysfatal("rfork");
22812d7cf04SDavid du Colombier default:
22912d7cf04SDavid du Colombier tunnel2ip(tunnel, v6net);
23012d7cf04SDavid du Colombier break;
23112d7cf04SDavid du Colombier case 0:
23212d7cf04SDavid du Colombier ip2tunnel(v6net, tunnel);
23312d7cf04SDavid du Colombier break;
2346c54378cSDavid du Colombier }
2356c54378cSDavid du Colombier exits("tunnel gone");
2366c54378cSDavid du Colombier }
2376c54378cSDavid du Colombier
23812d7cf04SDavid du Colombier void
main(int argc,char ** argv)23912d7cf04SDavid du Colombier main(int argc, char **argv)
2406c54378cSDavid du Colombier {
24112d7cf04SDavid du Colombier int tunnel, v6net;
2426c54378cSDavid du Colombier
24312d7cf04SDavid du Colombier fmtinstall('I', eipfmt);
24412d7cf04SDavid du Colombier fmtinstall('V', eipfmt);
24512d7cf04SDavid du Colombier fmtinstall('M', eipfmt);
2466c54378cSDavid du Colombier
24712d7cf04SDavid du Colombier ARGBEGIN {
24812d7cf04SDavid du Colombier case 'a':
24912d7cf04SDavid du Colombier anysender++;
25012d7cf04SDavid du Colombier break;
25112d7cf04SDavid du Colombier case 'd':
25212d7cf04SDavid du Colombier debug++;
25312d7cf04SDavid du Colombier break;
25412d7cf04SDavid du Colombier case 'g':
25512d7cf04SDavid du Colombier gateway++;
25612d7cf04SDavid du Colombier break;
25712d7cf04SDavid du Colombier case 'x':
25812d7cf04SDavid du Colombier net = EARGF(usage());
25912d7cf04SDavid du Colombier break;
26012d7cf04SDavid du Colombier default:
26112d7cf04SDavid du Colombier usage();
26212d7cf04SDavid du Colombier } ARGEND
26312d7cf04SDavid du Colombier
26412d7cf04SDavid du Colombier if (myipaddr(myip, net) < 0)
26512d7cf04SDavid du Colombier sysfatal("can't find my ipv4 address on %s", net);
26612d7cf04SDavid du Colombier if (!isv4(myip))
26712d7cf04SDavid du Colombier sysfatal("my ip, %I, is not a v4 address", myip);
26812d7cf04SDavid du Colombier
26912d7cf04SDavid du Colombier procargs(argc, argv);
27012d7cf04SDavid du Colombier setup(&v6net, &tunnel);
27112d7cf04SDavid du Colombier runtunnel(v6net, tunnel);
272*f80c7c99SDavid du Colombier exits(0);
27312d7cf04SDavid du Colombier }
27412d7cf04SDavid du Colombier
27512d7cf04SDavid du Colombier /*
2760774058cSDavid du Colombier * based on libthread's threadsetname, but drags in less library code.
2770774058cSDavid du Colombier * actually just sets the arguments displayed.
2780774058cSDavid du Colombier */
2790774058cSDavid du Colombier void
procsetname(char * fmt,...)2800774058cSDavid du Colombier procsetname(char *fmt, ...)
2810774058cSDavid du Colombier {
2820774058cSDavid du Colombier int fd;
2830774058cSDavid du Colombier char *cmdname;
2840774058cSDavid du Colombier char buf[128];
2850774058cSDavid du Colombier va_list arg;
2860774058cSDavid du Colombier
2870774058cSDavid du Colombier va_start(arg, fmt);
2880774058cSDavid du Colombier cmdname = vsmprint(fmt, arg);
2890774058cSDavid du Colombier va_end(arg);
2900774058cSDavid du Colombier if (cmdname == nil)
2910774058cSDavid du Colombier return;
2920774058cSDavid du Colombier snprint(buf, sizeof buf, "#p/%d/args", getpid());
2930774058cSDavid du Colombier if((fd = open(buf, OWRITE)) >= 0){
2940774058cSDavid du Colombier write(fd, cmdname, strlen(cmdname)+1);
2950774058cSDavid du Colombier close(fd);
2960774058cSDavid du Colombier }
2970774058cSDavid du Colombier free(cmdname);
2980774058cSDavid du Colombier }
2990774058cSDavid du Colombier
3000774058cSDavid du Colombier /*
30112d7cf04SDavid du Colombier * encapsulate v6 packets from the packet interface in v4 ones
30212d7cf04SDavid du Colombier * and send them into the tunnel.
30312d7cf04SDavid du Colombier */
3046c54378cSDavid du Colombier static void
ip2tunnel(int in,int out)3056c54378cSDavid du Colombier ip2tunnel(int in, int out)
3066c54378cSDavid du Colombier {
3076c54378cSDavid du Colombier int n, m;
3086c54378cSDavid du Colombier char buf[64*1024];
3096c54378cSDavid du Colombier Iphdr *op;
3106c54378cSDavid du Colombier Ip6hdr *ip;
3116c54378cSDavid du Colombier
3120774058cSDavid du Colombier if (anysender)
3130774058cSDavid du Colombier procsetname("v6 %I -> tunnel", local6);
3140774058cSDavid du Colombier else
3150774058cSDavid du Colombier procsetname("v6 %I -> tunnel %I %I", local6, remote4, remote6);
3160774058cSDavid du Colombier
31712d7cf04SDavid du Colombier /* populate v4 header */
3186c54378cSDavid du Colombier op = (Iphdr*)buf;
3190774058cSDavid du Colombier op->vihl = IP_VER4 | 5; /* hdr is 5 longs? */
3206c54378cSDavid du Colombier memcpy(op->src, myip + IPv4off, sizeof op->src);
321*f80c7c99SDavid du Colombier op->proto = IP_IPV6PROTO; /* inner protocol */
3226c54378cSDavid du Colombier op->ttl = 100;
3236c54378cSDavid du Colombier
3246c54378cSDavid du Colombier /* get a V6 packet destined for the tunnel */
3256c54378cSDavid du Colombier ip = (Ip6hdr*)(buf + STFHDR);
32695fd729bSDavid du Colombier while ((n = read(in, ip, sizeof buf - STFHDR)) > 0) {
32795fd729bSDavid du Colombier /* if not IPV6, drop it */
3280774058cSDavid du Colombier if ((ip->vcf[0] & 0xF0) != IP_VER6)
3296c54378cSDavid du Colombier continue;
3306c54378cSDavid du Colombier
3316c54378cSDavid du Colombier /* check length: drop if too short, trim if too long */
33294aa1c4cSDavid du Colombier m = nhgets(ip->ploadlen) + IPV6HDR_LEN;
3336c54378cSDavid du Colombier if (m > n)
3346c54378cSDavid du Colombier continue;
3356c54378cSDavid du Colombier if (m < n)
3366c54378cSDavid du Colombier n = m;
3376c54378cSDavid du Colombier
3386c54378cSDavid du Colombier /* drop if v6 source or destination address is naughty */
339*f80c7c99SDavid du Colombier if (badipv6(ip->src)) {
340*f80c7c99SDavid du Colombier syslog(0, "6in4", "egress filtered %I -> %I; bad src",
3416c54378cSDavid du Colombier ip->src, ip->dst);
3426c54378cSDavid du Colombier continue;
3436c54378cSDavid du Colombier }
344*f80c7c99SDavid du Colombier if ((!equivip6(ip->dst, remote6) && badipv6(ip->dst))) {
345*f80c7c99SDavid du Colombier syslog(0, "6in4", "egress filtered %I -> %I; "
346*f80c7c99SDavid du Colombier "bad dst not remote", ip->src, ip->dst);
347*f80c7c99SDavid du Colombier continue;
348*f80c7c99SDavid du Colombier }
3496c54378cSDavid du Colombier
3500774058cSDavid du Colombier if (debug > 1)
3510774058cSDavid du Colombier fprint(2, "v6 to tunnel %I -> %I\n", ip->src, ip->dst);
352*f80c7c99SDavid du Colombier
353ea813757SDavid du Colombier /* send 6to4 packets directly to ipv4 target */
354ea813757SDavid du Colombier if ((ip->dst[0]<<8 | ip->dst[1]) == V6to4pfx)
3556c54378cSDavid du Colombier memcpy(op->dst, ip->dst+2, sizeof op->dst);
3566c54378cSDavid du Colombier else
3576c54378cSDavid du Colombier memcpy(op->dst, remote4+IPv4off, sizeof op->dst);
3586c54378cSDavid du Colombier
3596c54378cSDavid du Colombier n += STFHDR;
3606c54378cSDavid du Colombier /* pass packet to the other end of the tunnel */
3616c54378cSDavid du Colombier if (write(out, op, n) != n) {
3626c54378cSDavid du Colombier syslog(0, "6in4", "error writing to tunnel (%r), giving up");
3636c54378cSDavid du Colombier break;
3646c54378cSDavid du Colombier }
3656c54378cSDavid du Colombier }
3666c54378cSDavid du Colombier }
3676c54378cSDavid du Colombier
36812d7cf04SDavid du Colombier /*
36912d7cf04SDavid du Colombier * decapsulate v6 packets from v4 ones from the tunnel
37012d7cf04SDavid du Colombier * and forward them to the packet interface
37112d7cf04SDavid du Colombier */
3726c54378cSDavid du Colombier static void
tunnel2ip(int in,int out)3736c54378cSDavid du Colombier tunnel2ip(int in, int out)
3746c54378cSDavid du Colombier {
3756c54378cSDavid du Colombier int n, m;
3766c54378cSDavid du Colombier char buf[64*1024];
3776c54378cSDavid du Colombier uchar a[IPaddrlen];
3786c54378cSDavid du Colombier Ip6hdr *op;
3796c54378cSDavid du Colombier Iphdr *ip;
3806c54378cSDavid du Colombier
3810774058cSDavid du Colombier if (anysender)
3820774058cSDavid du Colombier procsetname("tunnel -> v6 %I", local6);
3830774058cSDavid du Colombier else
3840774058cSDavid du Colombier procsetname("tunnel %I %I -> v6 %I", remote4, remote6, local6);
3850774058cSDavid du Colombier
3866c54378cSDavid du Colombier for (;;) {
3876c54378cSDavid du Colombier /* get a packet from the tunnel */
3886c54378cSDavid du Colombier n = read(in, buf, sizeof buf);
3896c54378cSDavid du Colombier ip = (Iphdr*)(buf + IPaddrlen);
3906c54378cSDavid du Colombier n -= IPaddrlen;
3916c54378cSDavid du Colombier if (n <= 0) {
3926c54378cSDavid du Colombier syslog(0, "6in4", "error reading from tunnel (%r), giving up");
3936c54378cSDavid du Colombier break;
3946c54378cSDavid du Colombier }
3956c54378cSDavid du Colombier
39695fd729bSDavid du Colombier /* if not IPv4 nor IPv4 protocol IPv6 nor ICMPv6, drop it */
39795fd729bSDavid du Colombier if ((ip->vihl & 0xF0) != IP_VER4 ||
398*f80c7c99SDavid du Colombier ip->proto != IP_IPV6PROTO && ip->proto != IP_ICMPV6PROTO) {
399*f80c7c99SDavid du Colombier syslog(0, "6in4",
400*f80c7c99SDavid du Colombier "dropping pkt from tunnel with inner proto %d",
401*f80c7c99SDavid du Colombier ip->proto);
4026c54378cSDavid du Colombier continue;
403*f80c7c99SDavid du Colombier }
4046c54378cSDavid du Colombier
4056c54378cSDavid du Colombier /* check length: drop if too short, trim if too long */
4066c54378cSDavid du Colombier m = nhgets(ip->length);
4076c54378cSDavid du Colombier if (m > n)
4086c54378cSDavid du Colombier continue;
4096c54378cSDavid du Colombier if (m < n)
4106c54378cSDavid du Colombier n = m;
4116c54378cSDavid du Colombier
4126c54378cSDavid du Colombier op = (Ip6hdr*)(buf + IPaddrlen + STFHDR);
4136c54378cSDavid du Colombier n -= STFHDR;
4146c54378cSDavid du Colombier
41512d7cf04SDavid du Colombier /*
41612d7cf04SDavid du Colombier * don't relay: just accept packets for local host/subnet
41712d7cf04SDavid du Colombier * (this blocks link-local and multicast addresses as well)
41812d7cf04SDavid du Colombier */
4196c54378cSDavid du Colombier maskip(op->dst, localmask, a);
4206c54378cSDavid du Colombier if (!equivip6(a, localnet)) {
421*f80c7c99SDavid du Colombier syslog(0, "6in4", "ingress filtered %I -> %I; "
422*f80c7c99SDavid du Colombier "dst not on local net", op->src, op->dst);
4236c54378cSDavid du Colombier continue;
4246c54378cSDavid du Colombier }
4250774058cSDavid du Colombier if (debug > 1)
4260774058cSDavid du Colombier fprint(2, "tunnel to v6 %I -> %I\n", op->src, op->dst);
4276c54378cSDavid du Colombier
4286c54378cSDavid du Colombier /* pass V6 packet to the interface */
42912d7cf04SDavid du Colombier if (write(out, op, n) != n) {
43012d7cf04SDavid du Colombier syslog(0, "6in4", "error writing to packet interface (%r), giving up");
43112d7cf04SDavid du Colombier break;
43212d7cf04SDavid du Colombier }
4336c54378cSDavid du Colombier }
4346c54378cSDavid du Colombier }
4356c54378cSDavid du Colombier
4366c54378cSDavid du Colombier static int
badipv4(uchar * a)4376c54378cSDavid du Colombier badipv4(uchar *a)
4386c54378cSDavid du Colombier {
4396c54378cSDavid du Colombier switch (a[0]) {
4406c54378cSDavid du Colombier case 0: /* unassigned */
4416c54378cSDavid du Colombier case 10: /* private */
4426c54378cSDavid du Colombier case 127: /* loopback */
4436c54378cSDavid du Colombier return 1;
4446c54378cSDavid du Colombier case 172:
4456c54378cSDavid du Colombier return a[1] >= 16; /* 172.16.0.0/12 private */
4466c54378cSDavid du Colombier case 192:
4476c54378cSDavid du Colombier return a[1] == 168; /* 192.168.0.0/16 private */
4486c54378cSDavid du Colombier case 169:
4496c54378cSDavid du Colombier return a[1] == 254; /* 169.254.0.0/16 DHCP link-local */
4506c54378cSDavid du Colombier }
4516c54378cSDavid du Colombier /* 224.0.0.0/4 multicast, 240.0.0.0/4 reserved, broadcast */
4526c54378cSDavid du Colombier return a[0] >= 240;
4536c54378cSDavid du Colombier }
4546c54378cSDavid du Colombier
455ea813757SDavid du Colombier /*
456ea813757SDavid du Colombier * 0x0000/16 prefix = v4 compatible, v4 mapped, loopback, unspecified...
457ea813757SDavid du Colombier * site-local is now deprecated, rfc3879
458ea813757SDavid du Colombier */
4596c54378cSDavid du Colombier static int
badipv6(uchar * a)4606c54378cSDavid du Colombier badipv6(uchar *a)
4616c54378cSDavid du Colombier {
4626c54378cSDavid du Colombier int h = a[0]<<8 | a[1];
4636c54378cSDavid du Colombier
464ea813757SDavid du Colombier return h == 0 || ISIPV6MCAST(a) || ISIPV6LINKLOCAL(a) ||
465ea813757SDavid du Colombier h == V6to4pfx && badipv4(a+2);
4666c54378cSDavid du Colombier }
467