Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * 6in4 - tunnel client for automatic 6to4 or configured v6-in-v4 tunnels.
3
 *	see rfc3056.
4
 */
5
 
6
#include <u.h>
7
#include <libc.h>
8
#include <ip.h>
9
 
10
/*
11
 * IPv6 and related IP protocols & their numbers:
12
 *
13
 * ipv6		41      IPv6            # Internet Protocol, version 6
14
 * ipv6-route	43      IPv6-Route      # Routing Header for IPv6
15
 * ipv6-frag	44      IPv6-Frag       # Fragment Header for IPv6
16
 * esp		50      ESP             # Encapsulating Security Payload
17
 * ah		51      AH              # Authentication Header
18
 * ipv6-icmp	58      IPv6-ICMP icmp6 # ICMP version 6
19
 * ipv6-nonxt	59      IPv6-NoNxt      # No Next Header for IPv6
20
 * ipv6-opts	60      IPv6-Opts       # Destination Options for IPv6
21
 */
22
 
23
enum {
24
	IP_IPV6PROTO	= 41,		/* IPv4 protocol number for IPv6 */
25
 	IP_ESPPROTO	= 50,		/* IP v4 and v6 protocol number */
26
 	IP_AHPROTO	= 51,		/* IP v4 and v6 protocol number */
27
	IP_ICMPV6PROTO	= 58,
28
	V6to4pfx	= 0x2002,
29
};
30
 
31
typedef struct Iphdr Iphdr;
32
struct Iphdr
33
{
34
	uchar	vihl;		/* Version and header length */
35
	uchar	tos;		/* Type of service */
36
	uchar	length[2];	/* packet length */
37
	uchar	id[2];		/* Identification */
38
	uchar	frag[2];	/* Fragment information */
39
	uchar	ttl;		/* Time to live */
40
	uchar	proto;		/* Protocol */
41
	uchar	cksum[2];	/* Header checksum */
42
	uchar	src[4];		/* Ip source (uchar ordering unimportant) */
43
	uchar	dst[4];		/* Ip destination (uchar ordering unimportant) */
44
	uchar	payload[];
45
};
46
 
47
#define STFHDR offsetof(Iphdr, payload[0])
48
 
49
int anysender;
50
int gateway;
51
int debug;
52
 
53
uchar local6[IPaddrlen];
54
uchar remote6[IPaddrlen];
55
uchar remote4[IPaddrlen];
56
uchar localmask[IPaddrlen];
57
uchar localnet[IPaddrlen];
58
uchar myip[IPaddrlen];
59
 
60
/* magic anycast address from rfc3068 */
61
uchar anycast6to4[IPv4addrlen] = { 192, 88, 99, 1 };
62
 
63
static char *net = "/net";
64
 
65
static int	badipv4(uchar*);
66
static int	badipv6(uchar*);
67
static void	ip2tunnel(int, int);
68
static void	tunnel2ip(int, int);
69
 
70
static void
71
usage(void)
72
{
73
	fprint(2, "usage: %s [-ag] [-x mtpt] [local6[/mask]] [remote4 [remote6]]\n",
74
		argv0);
75
	exits("Usage");
76
}
77
 
78
static char *
79
defv6addr(void)
80
{
81
	uchar *ipv4 = &myip[IPaddrlen - IPv4addrlen];
82
 
83
	return smprint("%ux:%2.2x%2.2x:%2.2x%2.2x::1/48", V6to4pfx,
84
		ipv4[0], ipv4[1], ipv4[2], ipv4[3]);
85
}
86
 
87
/* process non-option arguments */
88
static void
89
procargs(int argc, char **argv)
90
{
91
	char *p, *loc6;
92
 
93
	if (argc < 1)
94
		loc6 = defv6addr();
95
	else if (strcmp(argv[0], "-") == 0) {
96
		loc6 = defv6addr();
97
		argv++;
98
		argc--;
99
	} else {
100
		loc6 = *argv++;
101
		argc--;
102
	}
103
 
104
	/* local v6 address (mask defaults to /128) */
105
	memcpy(localmask, IPallbits, sizeof localmask);
106
	p = strchr(loc6, '/');
107
	if (p != nil) {
108
		parseipmask(localmask, p);
109
		*p = 0;
110
	}
111
	if (parseip(local6, loc6) == -1)
112
		sysfatal("bad local v6 address %s", loc6);
113
	if (isv4(local6))
114
		usage();
115
	if (argc >= 1 && argv[0][0] == '/') {
116
		parseipmask(localmask, *argv++);
117
		argc--;
118
	}
119
	if (debug)
120
		fprint(2, "local6 %I %M\n", local6, localmask);
121
 
122
	/* remote v4 address (defaults to anycast 6to4) */
123
	if (argc >= 1) {
124
		if (parseip(remote4, *argv++) == -1)
125
			sysfatal("bad remote v4 address %s", argv[-1]);
126
		argc--;
127
		if (!isv4(remote4))
128
			usage();
129
	} else {
130
		v4tov6(remote4, anycast6to4);
131
		anysender++;
132
	}
133
	if (debug)
134
		fprint(2, "remote4 %I\n", remote4);
135
 
136
	/* remote v6 address (defaults to link-local w/ v4 as interface part) */
137
	if (argc >= 1) {
138
		if (parseip(remote6, *argv++) == -1)
139
			sysfatal("bad remote v6 address %s", argv[-1]);
140
		argc--;
141
	} else {
142
		remote6[0] = 0xFE;		/* link local */
143
		remote6[1] = 0x80;
144
		memcpy(remote6 + IPv4off, remote4 + IPv4off, IPv4addrlen);
145
	}
146
	USED(argv);
147
	if (argc != 0)
148
		usage();
149
 
150
	maskip(local6, localmask, localnet);
151
	if (debug)
152
		fprint(2, "localnet %I remote6 %I\n", localnet, remote6);
153
}
154
 
155
static void
156
setup(int *v6net, int *tunp)
157
{
158
	int n, cfd;
159
	char *p, *cl, *ir;
160
	char buf[128], path[64];
161
 
162
	/*
163
	 * gain access to IPv6-in-IPv4 packets via ipmux
164
	 */
165
	p = seprint(buf, buf + sizeof buf, "%s/ipmux!proto=%2.2x|%2.2x;dst=%V",
166
		net, IP_IPV6PROTO, IP_ICMPV6PROTO, myip + IPv4off);
167
	if (!anysender)
168
		seprint(p, buf + sizeof buf, ";src=%V", remote4 + IPv4off);
169
	*tunp = dial(buf, 0, 0, 0);
170
	if (*tunp < 0)
171
		sysfatal("can't access ipv6-in-ipv4 with dial str %s: %r", buf);
172
	if (debug)
173
		fprint(2, "dialed %s for v6-in-v4 access\n", buf);
174
 
175
	/*
176
	 * open local IPv6 interface (as a packet interface)
177
	 */
178
 
179
	cl = smprint("%s/ipifc/clone", net);
180
	cfd = open(cl, ORDWR);			/* allocate a conversation */
181
	n = 0;
182
	if (cfd < 0 || (n = read(cfd, buf, sizeof buf - 1)) <= 0)
183
		sysfatal("can't make packet interface %s: %r", cl);
184
	if (debug)
185
		fprint(2, "cloned %s as local v6 interface\n", cl);
186
	free(cl);
187
	buf[n] = 0;
188
 
189
	snprint(path, sizeof path, "%s/ipifc/%s/data", net, buf);
190
	*v6net = open(path, ORDWR);
191
	if (*v6net < 0 || fprint(cfd, "bind pkt") < 0)
192
		sysfatal("can't bind packet interface: %r");
193
	/* 1280 is MTU, apparently from rfc2460 */
194
	if (fprint(cfd, "add %I /128 %I 1280", local6, remote6) <= 0)
195
		sysfatal("can't set local ipv6 address: %r");
196
	close(cfd);
197
	if (debug)
198
		fprint(2, "opened & bound %s as local v6 interface\n", path);
199
 
200
	if (gateway) {
201
		/* route global addresses through the tunnel to remote6 */
202
		ir = smprint("%s/iproute", net);
203
		cfd = open(ir, OWRITE);
204
		if (cfd >= 0 && debug)
205
			fprint(2, "injected 2000::/3 %I into %s\n", remote6, ir);
206
		free(ir);
207
		if (cfd < 0 || fprint(cfd, "add 2000:: /3 %I", remote6) <= 0)
208
			sysfatal("can't set default global route: %r");
209
	}
210
}
211
 
212
static void
213
runtunnel(int v6net, int tunnel)
214
{
215
	/* run the tunnel copying in the background */
216
	switch (rfork(RFPROC|RFNOWAIT|RFMEM|RFNOTEG)) {
217
	case -1:
218
		sysfatal("rfork");
219
	default:
220
		exits(nil);
221
	case 0:
222
		break;
223
	}
224
 
225
	switch (rfork(RFPROC|RFNOWAIT|RFMEM)) {
226
	case -1:
227
		sysfatal("rfork");
228
	default:
229
		tunnel2ip(tunnel, v6net);
230
		break;
231
	case 0:
232
		ip2tunnel(v6net, tunnel);
233
		break;
234
	}
235
	exits("tunnel gone");
236
}
237
 
238
void
239
main(int argc, char **argv)
240
{
241
	int tunnel, v6net;
242
 
243
	fmtinstall('I', eipfmt);
244
	fmtinstall('V', eipfmt);
245
	fmtinstall('M', eipfmt);
246
 
247
	ARGBEGIN {
248
	case 'a':
249
		anysender++;
250
		break;
251
	case 'd':
252
		debug++;
253
		break;
254
	case 'g':
255
		gateway++;
256
		break;
257
	case 'x':
258
		net = EARGF(usage());
259
		break;
260
	default:
261
		usage();
262
	} ARGEND
263
 
264
	if (myipaddr(myip, net) < 0)
265
		sysfatal("can't find my ipv4 address on %s", net);
266
	if (!isv4(myip))
267
		sysfatal("my ip, %I, is not a v4 address", myip);
268
 
269
	procargs(argc, argv);
270
	setup(&v6net, &tunnel);
271
	runtunnel(v6net, tunnel);
272
	exits(0);
273
}
274
 
275
/*
276
 * based on libthread's threadsetname, but drags in less library code.
277
 * actually just sets the arguments displayed.
278
 */
279
void
280
procsetname(char *fmt, ...)
281
{
282
	int fd;
283
	char *cmdname;
284
	char buf[128];
285
	va_list arg;
286
 
287
	va_start(arg, fmt);
288
	cmdname = vsmprint(fmt, arg);
289
	va_end(arg);
290
	if (cmdname == nil)
291
		return;
292
	snprint(buf, sizeof buf, "#p/%d/args", getpid());
293
	if((fd = open(buf, OWRITE)) >= 0){
294
		write(fd, cmdname, strlen(cmdname)+1);
295
		close(fd);
296
	}
297
	free(cmdname);
298
}
299
 
300
/*
301
 * encapsulate v6 packets from the packet interface in v4 ones
302
 * and send them into the tunnel.
303
 */
304
static void
305
ip2tunnel(int in, int out)
306
{
307
	int n, m;
308
	char buf[64*1024];
309
	Iphdr *op;
310
	Ip6hdr *ip;
311
 
312
	if (anysender)
313
		procsetname("v6 %I -> tunnel", local6);
314
	else
315
		procsetname("v6 %I -> tunnel %I %I", local6, remote4, remote6);
316
 
317
	/* populate v4 header */
318
	op = (Iphdr*)buf;
319
	op->vihl = IP_VER4 | 5;		/* hdr is 5 longs? */
320
	memcpy(op->src, myip + IPv4off, sizeof op->src);
321
	op->proto = IP_IPV6PROTO;	/* inner protocol */
322
	op->ttl = 100;
323
 
324
	/* get a V6 packet destined for the tunnel */
325
	ip = (Ip6hdr*)(buf + STFHDR);
326
	while ((n = read(in, ip, sizeof buf - STFHDR)) > 0) {
327
		/* if not IPV6, drop it */
328
		if ((ip->vcf[0] & 0xF0) != IP_VER6)
329
			continue;
330
 
331
		/* check length: drop if too short, trim if too long */
332
		m = nhgets(ip->ploadlen) + IPV6HDR_LEN;
333
		if (m > n)
334
			continue;
335
		if (m < n)
336
			n = m;
337
 
338
		/* drop if v6 source or destination address is naughty */
339
		if (badipv6(ip->src)) {
340
			syslog(0, "6in4", "egress filtered %I -> %I; bad src",
341
				ip->src, ip->dst);
342
			continue;
343
		}
344
		if ((!equivip6(ip->dst, remote6) && badipv6(ip->dst))) {
345
			syslog(0, "6in4", "egress filtered %I -> %I; "
346
				"bad dst not remote", ip->src, ip->dst);
347
			continue;
348
		}
349
 
350
		if (debug > 1)
351
			fprint(2, "v6 to tunnel %I -> %I\n", ip->src, ip->dst);
352
 
353
		/* send 6to4 packets directly to ipv4 target */
354
		if ((ip->dst[0]<<8 | ip->dst[1]) == V6to4pfx)
355
			memcpy(op->dst, ip->dst+2, sizeof op->dst);
356
		else
357
			memcpy(op->dst, remote4+IPv4off, sizeof op->dst);
358
 
359
		n += STFHDR;
360
		/* pass packet to the other end of the tunnel */
361
		if (write(out, op, n) != n) {
362
			syslog(0, "6in4", "error writing to tunnel (%r), giving up");
363
			break;
364
		}
365
	}
366
}
367
 
368
/*
369
 * decapsulate v6 packets from v4 ones from the tunnel
370
 * and forward them to the packet interface
371
 */
372
static void
373
tunnel2ip(int in, int out)
374
{
375
	int n, m;
376
	char buf[64*1024];
377
	uchar a[IPaddrlen];
378
	Ip6hdr *op;
379
	Iphdr *ip;
380
 
381
	if (anysender)
382
		procsetname("tunnel -> v6 %I", local6);
383
	else
384
		procsetname("tunnel %I %I -> v6 %I", remote4, remote6, local6);
385
 
386
	for (;;) {
387
		/* get a packet from the tunnel */
388
		n = read(in, buf, sizeof buf);
389
		ip = (Iphdr*)(buf + IPaddrlen);
390
		n -= IPaddrlen;
391
		if (n <= 0) {
392
			syslog(0, "6in4", "error reading from tunnel (%r), giving up");
393
			break;
394
		}
395
 
396
		/* if not IPv4 nor IPv4 protocol IPv6 nor ICMPv6, drop it */
397
		if ((ip->vihl & 0xF0) != IP_VER4 ||
398
		    ip->proto != IP_IPV6PROTO && ip->proto != IP_ICMPV6PROTO) {
399
			syslog(0, "6in4",
400
				"dropping pkt from tunnel with inner proto %d",
401
				ip->proto);
402
			continue;
403
		}
404
 
405
		/* check length: drop if too short, trim if too long */
406
		m = nhgets(ip->length);
407
		if (m > n)
408
			continue;
409
		if (m < n)
410
			n = m;
411
 
412
		op = (Ip6hdr*)(buf + IPaddrlen + STFHDR);
413
		n -= STFHDR;
414
 
415
		/*
416
		 * don't relay: just accept packets for local host/subnet
417
		 * (this blocks link-local and multicast addresses as well)
418
		 */
419
		maskip(op->dst, localmask, a);
420
		if (!equivip6(a, localnet)) {
421
			syslog(0, "6in4", "ingress filtered %I -> %I; "
422
				"dst not on local net", op->src, op->dst);
423
			continue;
424
		}
425
		if (debug > 1)
426
			fprint(2, "tunnel to v6 %I -> %I\n", op->src, op->dst);
427
 
428
		/* pass V6 packet to the interface */
429
		if (write(out, op, n) != n) {
430
			syslog(0, "6in4", "error writing to packet interface (%r), giving up");
431
			break;
432
		}
433
	}
434
}
435
 
436
static int
437
badipv4(uchar *a)
438
{
439
	switch (a[0]) {
440
	case 0:				/* unassigned */
441
	case 10:			/* private */
442
	case 127:			/* loopback */
443
		return 1;
444
	case 172:
445
		return a[1] >= 16;	/* 172.16.0.0/12 private */
446
	case 192:
447
		return a[1] == 168;	/* 192.168.0.0/16 private */
448
	case 169:
449
		return a[1] == 254;	/* 169.254.0.0/16 DHCP link-local */
450
	}
451
	/* 224.0.0.0/4 multicast, 240.0.0.0/4 reserved, broadcast */
452
	return a[0] >= 240;
453
}
454
 
455
/*
456
 * 0x0000/16 prefix = v4 compatible, v4 mapped, loopback, unspecified...
457
 * site-local is now deprecated, rfc3879
458
 */
459
static int
460
badipv6(uchar *a)
461
{
462
	int h = a[0]<<8 | a[1];
463
 
464
	return h == 0 || ISIPV6MCAST(a) || ISIPV6LINKLOCAL(a) ||
465
	    h == V6to4pfx && badipv4(a+2);
466
}