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