xref: /plan9-contrib/sys/src/cmd/ip/6in4.c (revision f80c7c99152dd8fa8c89f00378a2f8d344303faf)
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