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