]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/6in4.c
add ip/tftpfs
[plan9front.git] / sys / src / cmd / ip / 6in4.c
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
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 }
273
274 /*
275  * based on libthread's threadsetname, but drags in less library code.
276  * actually just sets the arguments displayed.
277  */
278 void
279 procsetname(char *fmt, ...)
280 {
281         int fd;
282         char *cmdname;
283         char buf[128];
284         va_list arg;
285
286         va_start(arg, fmt);
287         cmdname = vsmprint(fmt, arg);
288         va_end(arg);
289         if (cmdname == nil)
290                 return;
291         snprint(buf, sizeof buf, "#p/%d/args", getpid());
292         if((fd = open(buf, OWRITE)) >= 0){
293                 write(fd, cmdname, strlen(cmdname)+1);
294                 close(fd);
295         }
296         free(cmdname);
297 }
298
299 /*
300  * encapsulate v6 packets from the packet interface in v4 ones
301  * and send them into the tunnel.
302  */
303 static void
304 ip2tunnel(int in, int out)
305 {
306         int n, m;
307         char buf[64*1024];
308         Iphdr *op;
309         Ip6hdr *ip;
310
311         if (anysender)
312                 procsetname("v6 %I -> tunnel", local6);
313         else
314                 procsetname("v6 %I -> tunnel %I %I", local6, remote4, remote6);
315
316         /* populate v4 header */
317         op = (Iphdr*)buf;
318         op->vihl = IP_VER4 | 5;         /* hdr is 5 longs? */
319         memcpy(op->src, myip + IPv4off, sizeof op->src);
320         op->ttl = 100;
321
322         /* get a V6 packet destined for the tunnel */
323         ip = (Ip6hdr*)(buf + STFHDR);
324         while ((n = read(in, ip, sizeof buf - STFHDR)) > 0) {
325                 /* if not IPV6, drop it */
326                 if ((ip->vcf[0] & 0xF0) != IP_VER6)
327                         continue;
328
329                 /* check length: drop if too short, trim if too long */
330                 m = nhgets(ip->ploadlen) + IPV6HDR_LEN;
331                 if (m > n)
332                         continue;
333                 if (m < n)
334                         n = m;
335
336                 /* drop if v6 source or destination address is naughty */
337                 if (badipv6(ip->src) ||
338                     (!equivip6(ip->dst, remote6) && badipv6(ip->dst))) {
339                         syslog(0, "6in4", "egress filtered %I -> %I",
340                                 ip->src, ip->dst);
341                         continue;
342                 }
343
344                 op->proto = ip->proto;
345                 if (debug > 1)
346                         fprint(2, "v6 to tunnel %I -> %I\n", ip->src, ip->dst);
347                 /* send 6to4 packets directly to ipv4 target */
348                 if ((ip->dst[0]<<8 | ip->dst[1]) == V6to4pfx)
349                         memcpy(op->dst, ip->dst+2, sizeof op->dst);
350                 else
351                         memcpy(op->dst, remote4+IPv4off, sizeof op->dst);
352
353                 n += STFHDR;
354                 /* pass packet to the other end of the tunnel */
355                 if (write(out, op, n) != n) {
356                         syslog(0, "6in4", "error writing to tunnel (%r), giving up");
357                         break;
358                 }
359         }
360 }
361
362 /*
363  * decapsulate v6 packets from v4 ones from the tunnel
364  * and forward them to the packet interface
365  */
366 static void
367 tunnel2ip(int in, int out)
368 {
369         int n, m;
370         char buf[64*1024];
371         uchar a[IPaddrlen];
372         Ip6hdr *op;
373         Iphdr *ip;
374
375         if (anysender)
376                 procsetname("tunnel -> v6 %I", local6);
377         else
378                 procsetname("tunnel %I %I -> v6 %I", remote4, remote6, local6);
379
380         for (;;) {
381                 /* get a packet from the tunnel */
382                 n = read(in, buf, sizeof buf);
383                 ip = (Iphdr*)(buf + IPaddrlen);
384                 n -= IPaddrlen;
385                 if (n <= 0) {
386                         syslog(0, "6in4", "error reading from tunnel (%r), giving up");
387                         break;
388                 }
389
390                 /* if not IPv4 nor IPv4 protocol IPv6 nor ICMPv6, drop it */
391                 if ((ip->vihl & 0xF0) != IP_VER4 ||
392                     ip->proto != IP_IPV6PROTO && ip->proto != IP_ICMPV6PROTO)
393                         continue;
394
395                 /* check length: drop if too short, trim if too long */
396                 m = nhgets(ip->length);
397                 if (m > n)
398                         continue;
399                 if (m < n)
400                         n = m;
401
402                 op = (Ip6hdr*)(buf + IPaddrlen + STFHDR);
403                 n -= STFHDR;
404
405                 /*
406                  * don't relay: just accept packets for local host/subnet
407                  * (this blocks link-local and multicast addresses as well)
408                  */
409                 maskip(op->dst, localmask, a);
410                 if (!equivip6(a, localnet)) {
411                         syslog(0, "6in4", "ingress filtered %I -> %I",
412                                 op->src, op->dst);
413                         continue;
414                 }
415                 if (debug > 1)
416                         fprint(2, "tunnel to v6 %I -> %I\n", op->src, op->dst);
417
418                 /* pass V6 packet to the interface */
419                 if (write(out, op, n) != n) {
420                         syslog(0, "6in4", "error writing to packet interface (%r), giving up");
421                         break;
422                 }
423         }
424 }
425
426 static int
427 badipv4(uchar *a)
428 {
429         switch (a[0]) {
430         case 0:                         /* unassigned */
431         case 10:                        /* private */
432         case 127:                       /* loopback */
433                 return 1;
434         case 172:
435                 return a[1] >= 16;      /* 172.16.0.0/12 private */
436         case 192:
437                 return a[1] == 168;     /* 192.168.0.0/16 private */
438         case 169:
439                 return a[1] == 254;     /* 169.254.0.0/16 DHCP link-local */
440         }
441         /* 224.0.0.0/4 multicast, 240.0.0.0/4 reserved, broadcast */
442         return a[0] >= 240;
443 }
444
445 /*
446  * 0x0000/16 prefix = v4 compatible, v4 mapped, loopback, unspecified...
447  * site-local is now deprecated, rfc3879
448  */
449 static int
450 badipv6(uchar *a)
451 {
452         int h = a[0]<<8 | a[1];
453
454         return h == 0 || ISIPV6MCAST(a) || ISIPV6LINKLOCAL(a) ||
455             h == V6to4pfx && badipv4(a+2);
456 }