summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ip/6in4.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ip/6in4.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ip/6in4.c')
-rwxr-xr-xsys/src/cmd/ip/6in4.c456
1 files changed, 456 insertions, 0 deletions
diff --git a/sys/src/cmd/ip/6in4.c b/sys/src/cmd/ip/6in4.c
new file mode 100755
index 000000000..44e1636e8
--- /dev/null
+++ b/sys/src/cmd/ip/6in4.c
@@ -0,0 +1,456 @@
+/*
+ * 6in4 - tunnel client for automatic 6to4 or configured v6-in-v4 tunnels.
+ * see rfc3056.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+
+/*
+ * IPv6 and related IP protocols & their numbers:
+ *
+ * ipv6 41 IPv6 # Internet Protocol, version 6
+ * ipv6-route 43 IPv6-Route # Routing Header for IPv6
+ * ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6
+ * esp 50 ESP # Encapsulating Security Payload
+ * ah 51 AH # Authentication Header
+ * ipv6-icmp 58 IPv6-ICMP icmp6 # ICMP version 6
+ * ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6
+ * ipv6-opts 60 IPv6-Opts # Destination Options for IPv6
+ */
+
+enum {
+ IP_IPV6PROTO = 41, /* IPv4 protocol number for IPv6 */
+ IP_ESPPROTO = 50, /* IP v4 and v6 protocol number */
+ IP_AHPROTO = 51, /* IP v4 and v6 protocol number */
+ IP_ICMPV6PROTO = 58,
+ V6to4pfx = 0x2002,
+};
+
+typedef struct Iphdr Iphdr;
+struct Iphdr
+{
+ uchar vihl; /* Version and header length */
+ uchar tos; /* Type of service */
+ uchar length[2]; /* packet length */
+ uchar id[2]; /* Identification */
+ uchar frag[2]; /* Fragment information */
+ uchar ttl; /* Time to live */
+ uchar proto; /* Protocol */
+ uchar cksum[2]; /* Header checksum */
+ uchar src[4]; /* Ip source (uchar ordering unimportant) */
+ uchar dst[4]; /* Ip destination (uchar ordering unimportant) */
+ uchar payload[];
+};
+
+#define STFHDR offsetof(Iphdr, payload[0])
+
+int anysender;
+int gateway;
+int debug;
+
+uchar local6[IPaddrlen];
+uchar remote6[IPaddrlen];
+uchar remote4[IPaddrlen];
+uchar localmask[IPaddrlen];
+uchar localnet[IPaddrlen];
+uchar myip[IPaddrlen];
+
+/* magic anycast address from rfc3068 */
+uchar anycast6to4[IPv4addrlen] = { 192, 88, 99, 1 };
+
+static char *net = "/net";
+
+static int badipv4(uchar*);
+static int badipv6(uchar*);
+static void ip2tunnel(int, int);
+static void tunnel2ip(int, int);
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s [-ag] [-x mtpt] [local6[/mask]] [remote4 [remote6]]\n",
+ argv0);
+ exits("Usage");
+}
+
+static char *
+defv6addr(void)
+{
+ uchar *ipv4 = &myip[IPaddrlen - IPv4addrlen];
+
+ return smprint("%ux:%2.2x%2.2x:%2.2x%2.2x::1/48", V6to4pfx,
+ ipv4[0], ipv4[1], ipv4[2], ipv4[3]);
+}
+
+/* process non-option arguments */
+static void
+procargs(int argc, char **argv)
+{
+ char *p, *loc6;
+
+ if (argc < 1)
+ loc6 = defv6addr();
+ else if (strcmp(argv[0], "-") == 0) {
+ loc6 = defv6addr();
+ argv++;
+ argc--;
+ } else {
+ loc6 = *argv++;
+ argc--;
+ }
+
+ /* local v6 address (mask defaults to /128) */
+ memcpy(localmask, IPallbits, sizeof localmask);
+ p = strchr(loc6, '/');
+ if (p != nil) {
+ parseipmask(localmask, p);
+ *p = 0;
+ }
+ if (parseip(local6, loc6) == -1)
+ sysfatal("bad local v6 address %s", loc6);
+ if (isv4(local6))
+ usage();
+ if (argc >= 1 && argv[0][0] == '/') {
+ parseipmask(localmask, *argv++);
+ argc--;
+ }
+ if (debug)
+ fprint(2, "local6 %I %M\n", local6, localmask);
+
+ /* remote v4 address (defaults to anycast 6to4) */
+ if (argc >= 1) {
+ if (parseip(remote4, *argv++) == -1)
+ sysfatal("bad remote v4 address %s", argv[-1]);
+ argc--;
+ if (!isv4(remote4))
+ usage();
+ } else {
+ v4tov6(remote4, anycast6to4);
+ anysender++;
+ }
+ if (debug)
+ fprint(2, "remote4 %I\n", remote4);
+
+ /* remote v6 address (defaults to link-local w/ v4 as interface part) */
+ if (argc >= 1) {
+ if (parseip(remote6, *argv++) == -1)
+ sysfatal("bad remote v6 address %s", argv[-1]);
+ argc--;
+ } else {
+ remote6[0] = 0xFE; /* link local */
+ remote6[1] = 0x80;
+ memcpy(remote6 + IPv4off, remote4 + IPv4off, IPv4addrlen);
+ }
+ USED(argv);
+ if (argc != 0)
+ usage();
+
+ maskip(local6, localmask, localnet);
+ if (debug)
+ fprint(2, "localnet %I remote6 %I\n", localnet, remote6);
+}
+
+static void
+setup(int *v6net, int *tunp)
+{
+ int n, cfd;
+ char *p, *cl, *ir;
+ char buf[128], path[64];
+
+ /*
+ * gain access to IPv6-in-IPv4 packets
+ */
+ p = seprint(buf, buf + sizeof buf, "%s/ipmux!proto=%2.2x|%2.2x;dst=%V",
+ net, IP_IPV6PROTO, IP_ICMPV6PROTO, myip + IPv4off);
+ if (!anysender)
+ seprint(p, buf + sizeof buf, ";src=%V", remote4 + IPv4off);
+ *tunp = dial(buf, 0, 0, 0);
+ if (*tunp < 0)
+ sysfatal("can't access ipv6-in-ipv4 with dial str %s: %r", buf);
+ if (debug)
+ fprint(2, "dialed %s for v6-in-v4 access\n", buf);
+
+ /*
+ * open local IPv6 interface (as a packet interface)
+ */
+
+ cl = smprint("%s/ipifc/clone", net);
+ cfd = open(cl, ORDWR); /* allocate a conversation */
+ n = 0;
+ if (cfd < 0 || (n = read(cfd, buf, sizeof buf - 1)) <= 0)
+ sysfatal("can't make packet interface %s: %r", cl);
+ if (debug)
+ fprint(2, "cloned %s as local v6 interface\n", cl);
+ free(cl);
+ buf[n] = 0;
+
+ snprint(path, sizeof path, "%s/ipifc/%s/data", net, buf);
+ *v6net = open(path, ORDWR);
+ if (*v6net < 0 || fprint(cfd, "bind pkt") < 0)
+ sysfatal("can't bind packet interface: %r");
+ /* 1280 is MTU, apparently from rfc2460 */
+ if (fprint(cfd, "add %I /128 %I 1280", local6, remote6) <= 0)
+ sysfatal("can't set local ipv6 address: %r");
+ close(cfd);
+ if (debug)
+ fprint(2, "opened & bound %s as local v6 interface\n", path);
+
+ if (gateway) {
+ /* route global addresses through the tunnel to remote6 */
+ ir = smprint("%s/iproute", net);
+ cfd = open(ir, OWRITE);
+ if (cfd >= 0 && debug)
+ fprint(2, "injected 2000::/3 %I into %s\n", remote6, ir);
+ free(ir);
+ if (cfd < 0 || fprint(cfd, "add 2000:: /3 %I", remote6) <= 0)
+ sysfatal("can't set default global route: %r");
+ }
+}
+
+static void
+runtunnel(int v6net, int tunnel)
+{
+ /* run the tunnel copying in the background */
+ switch (rfork(RFPROC|RFNOWAIT|RFMEM|RFNOTEG)) {
+ case -1:
+ sysfatal("rfork");
+ default:
+ exits(nil);
+ case 0:
+ break;
+ }
+
+ switch (rfork(RFPROC|RFNOWAIT|RFMEM)) {
+ case -1:
+ sysfatal("rfork");
+ default:
+ tunnel2ip(tunnel, v6net);
+ break;
+ case 0:
+ ip2tunnel(v6net, tunnel);
+ break;
+ }
+ exits("tunnel gone");
+}
+
+void
+main(int argc, char **argv)
+{
+ int tunnel, v6net;
+
+ fmtinstall('I', eipfmt);
+ fmtinstall('V', eipfmt);
+ fmtinstall('M', eipfmt);
+
+ ARGBEGIN {
+ case 'a':
+ anysender++;
+ break;
+ case 'd':
+ debug++;
+ break;
+ case 'g':
+ gateway++;
+ break;
+ case 'x':
+ net = EARGF(usage());
+ break;
+ default:
+ usage();
+ } ARGEND
+
+ if (myipaddr(myip, net) < 0)
+ sysfatal("can't find my ipv4 address on %s", net);
+ if (!isv4(myip))
+ sysfatal("my ip, %I, is not a v4 address", myip);
+
+ procargs(argc, argv);
+ setup(&v6net, &tunnel);
+ runtunnel(v6net, tunnel);
+}
+
+/*
+ * based on libthread's threadsetname, but drags in less library code.
+ * actually just sets the arguments displayed.
+ */
+void
+procsetname(char *fmt, ...)
+{
+ int fd;
+ char *cmdname;
+ char buf[128];
+ va_list arg;
+
+ va_start(arg, fmt);
+ cmdname = vsmprint(fmt, arg);
+ va_end(arg);
+ if (cmdname == nil)
+ return;
+ snprint(buf, sizeof buf, "#p/%d/args", getpid());
+ if((fd = open(buf, OWRITE)) >= 0){
+ write(fd, cmdname, strlen(cmdname)+1);
+ close(fd);
+ }
+ free(cmdname);
+}
+
+/*
+ * encapsulate v6 packets from the packet interface in v4 ones
+ * and send them into the tunnel.
+ */
+static void
+ip2tunnel(int in, int out)
+{
+ int n, m;
+ char buf[64*1024];
+ Iphdr *op;
+ Ip6hdr *ip;
+
+ if (anysender)
+ procsetname("v6 %I -> tunnel", local6);
+ else
+ procsetname("v6 %I -> tunnel %I %I", local6, remote4, remote6);
+
+ /* populate v4 header */
+ op = (Iphdr*)buf;
+ op->vihl = IP_VER4 | 5; /* hdr is 5 longs? */
+ memcpy(op->src, myip + IPv4off, sizeof op->src);
+ op->ttl = 100;
+
+ /* get a V6 packet destined for the tunnel */
+ ip = (Ip6hdr*)(buf + STFHDR);
+ while ((n = read(in, ip, sizeof buf - STFHDR)) > 0) {
+ /* if not IPV6, drop it */
+ if ((ip->vcf[0] & 0xF0) != IP_VER6)
+ continue;
+
+ /* check length: drop if too short, trim if too long */
+ m = nhgets(ip->ploadlen) + IPV6HDR_LEN;
+ if (m > n)
+ continue;
+ if (m < n)
+ n = m;
+
+ /* drop if v6 source or destination address is naughty */
+ if (badipv6(ip->src) ||
+ (!equivip6(ip->dst, remote6) && badipv6(ip->dst))) {
+ syslog(0, "6in4", "egress filtered %I -> %I",
+ ip->src, ip->dst);
+ continue;
+ }
+
+ op->proto = ip->proto;
+ if (debug > 1)
+ fprint(2, "v6 to tunnel %I -> %I\n", ip->src, ip->dst);
+ /* send 6to4 packets directly to ipv4 target */
+ if ((ip->dst[0]<<8 | ip->dst[1]) == V6to4pfx)
+ memcpy(op->dst, ip->dst+2, sizeof op->dst);
+ else
+ memcpy(op->dst, remote4+IPv4off, sizeof op->dst);
+
+ n += STFHDR;
+ /* pass packet to the other end of the tunnel */
+ if (write(out, op, n) != n) {
+ syslog(0, "6in4", "error writing to tunnel (%r), giving up");
+ break;
+ }
+ }
+}
+
+/*
+ * decapsulate v6 packets from v4 ones from the tunnel
+ * and forward them to the packet interface
+ */
+static void
+tunnel2ip(int in, int out)
+{
+ int n, m;
+ char buf[64*1024];
+ uchar a[IPaddrlen];
+ Ip6hdr *op;
+ Iphdr *ip;
+
+ if (anysender)
+ procsetname("tunnel -> v6 %I", local6);
+ else
+ procsetname("tunnel %I %I -> v6 %I", remote4, remote6, local6);
+
+ for (;;) {
+ /* get a packet from the tunnel */
+ n = read(in, buf, sizeof buf);
+ ip = (Iphdr*)(buf + IPaddrlen);
+ n -= IPaddrlen;
+ if (n <= 0) {
+ syslog(0, "6in4", "error reading from tunnel (%r), giving up");
+ break;
+ }
+
+ /* if not IPv4 nor IPv4 protocol IPv6 nor ICMPv6, drop it */
+ if ((ip->vihl & 0xF0) != IP_VER4 ||
+ ip->proto != IP_IPV6PROTO && ip->proto != IP_ICMPV6PROTO)
+ continue;
+
+ /* check length: drop if too short, trim if too long */
+ m = nhgets(ip->length);
+ if (m > n)
+ continue;
+ if (m < n)
+ n = m;
+
+ op = (Ip6hdr*)(buf + IPaddrlen + STFHDR);
+ n -= STFHDR;
+
+ /*
+ * don't relay: just accept packets for local host/subnet
+ * (this blocks link-local and multicast addresses as well)
+ */
+ maskip(op->dst, localmask, a);
+ if (!equivip6(a, localnet)) {
+ syslog(0, "6in4", "ingress filtered %I -> %I",
+ op->src, op->dst);
+ continue;
+ }
+ if (debug > 1)
+ fprint(2, "tunnel to v6 %I -> %I\n", op->src, op->dst);
+
+ /* pass V6 packet to the interface */
+ if (write(out, op, n) != n) {
+ syslog(0, "6in4", "error writing to packet interface (%r), giving up");
+ break;
+ }
+ }
+}
+
+static int
+badipv4(uchar *a)
+{
+ switch (a[0]) {
+ case 0: /* unassigned */
+ case 10: /* private */
+ case 127: /* loopback */
+ return 1;
+ case 172:
+ return a[1] >= 16; /* 172.16.0.0/12 private */
+ case 192:
+ return a[1] == 168; /* 192.168.0.0/16 private */
+ case 169:
+ return a[1] == 254; /* 169.254.0.0/16 DHCP link-local */
+ }
+ /* 224.0.0.0/4 multicast, 240.0.0.0/4 reserved, broadcast */
+ return a[0] >= 240;
+}
+
+/*
+ * 0x0000/16 prefix = v4 compatible, v4 mapped, loopback, unspecified...
+ * site-local is now deprecated, rfc3879
+ */
+static int
+badipv6(uchar *a)
+{
+ int h = a[0]<<8 | a[1];
+
+ return h == 0 || ISIPV6MCAST(a) || ISIPV6LINKLOCAL(a) ||
+ h == V6to4pfx && badipv4(a+2);
+}