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