2 * ayiya - tunnel client.
12 * IPv6 and related IP protocols & their numbers:
14 * ipv6 41 IPv6 # Internet Protocol, version 6
15 * ipv6-route 43 IPv6-Route # Routing Header for IPv6
16 * ipv6-frag 44 IPv6-Frag # Fragment Header for IPv6
17 * esp 50 ESP # Encapsulating Security Payload
18 * ah 51 AH # Authentication Header
19 * ipv6-icmp 58 IPv6-ICMP icmp6 # ICMP version 6
20 * ipv6-nonxt 59 IPv6-NoNxt # No Next Header for IPv6
21 * ipv6-opts 60 IPv6-Opts # Destination Options for IPv6
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 */
37 AYIYAMAXHDR = 8+AYIYAMAXID+AYIYAMAXSIG,
54 OpEchoRequestAndForward,
61 typedef struct AYIYA AYIYA;
82 uchar local6[IPaddrlen];
83 uchar remote6[IPaddrlen];
84 uchar localmask[IPaddrlen];
85 uchar localnet[IPaddrlen];
87 uchar nullsig[AYIYAMAXSIG];
89 static char *secret = nil;
91 static char *outside = nil; /* dial string of tunnel server */
92 static char *inside = "/net";
94 static int badipv4(uchar*);
95 static int badipv6(uchar*);
96 static void ip2tunnel(int, int);
97 static void tunnel2ip(int, int);
104 fprint(2, "idlen=%ud idtype=%ux siglen=%ud hashmeth=%ud authmeth=%ud opcode=%ux nexthdr=%ux epochtime=%ux\n",
105 a->idlen, a->idtype, a->siglen, a->hashmeth, a->authmeth, a->opcode, a->nexthdr, a->epochtime);
106 fprint(2, "identity=[ ");
107 for(i=0; i<a->idlen; i++)
108 fprint(2, "%.2ux ", a->identity[i]);
110 fprint(2, "signature=[ ");
111 for(i=0; i<a->siglen; i++)
112 fprint(2, "%.2ux ", a->signature[i]);
122 for(n = 0; (a >>= 1) != 0; n++)
128 ayiyapack(AYIYA *a, uchar *pay, int paylen)
135 memmove(pkt, a->signature, a->siglen);
139 memmove(pkt, a->identity, a->idlen);
143 pkt[0] = a->epochtime>>24;
144 pkt[1] = a->epochtime>>16;
145 pkt[2] = a->epochtime>>8;
146 pkt[3] = a->epochtime;
149 pkt[0] = (lg2(a->idlen)<<4) | a->idtype;
150 pkt[1] = ((a->siglen/4)<<4) | a->hashmeth;
151 pkt[2] = (a->authmeth<<4) | a->opcode;
160 ayiyaunpack(AYIYA *a, uchar *pkt, int pktlen)
167 a->idlen = 1<<(pkt[0] >> 4);
168 a->idtype = pkt[0] & 15;
169 a->siglen = (pkt[1] >> 4) * 4;
170 a->hashmeth = pkt[1] & 15;
171 a->authmeth = pkt[2] >> 4;
172 a->opcode = pkt[2] & 15;
174 a->epochtime = pkt[7] | pkt[6]<<8 | pkt[5]<<16 | pkt[4]<<24;
176 hdrlen = 8 + a->idlen + a->siglen;
182 a->identity = pkt + 8;
186 a->signature = pkt + 8 + a->idlen;
192 ayiyahash(uint meth, uchar *pkt, int pktlen, uchar *dig)
197 md5(pkt, pktlen, dig, nil);
201 sha1(pkt, pktlen, dig, nil);
208 ayiyasign(AYIYA *a, uchar *pkt, int pktlen)
210 uchar dig[AYIYAMAXSIG], *pktsig;
212 if(a->hashmeth == HashNone && a->siglen == 0)
215 assert(a->siglen <= sizeof(dig));
216 assert(a->siglen <= pktlen - a->idlen - 8);
217 pktsig = pkt + 8 + a->idlen;
219 if(ayiyahash(a->hashmeth, pkt, pktlen, dig) != a->siglen){
220 memset(pktsig, 0, a->siglen);
224 memmove(pktsig, dig, a->siglen);
228 ayiyaverify(AYIYA *a, uchar *pkt, int pktlen)
230 uchar dig[AYIYAMAXSIG], sig[AYIYAMAXSIG];
232 if(conf.hashmeth == HashNone && a->siglen == 0)
234 if(a->hashmeth != conf.hashmeth || a->authmeth != conf.authmeth || a->siglen != conf.siglen)
236 memmove(sig, a->signature, a->siglen);
237 memmove(a->signature, conf.signature, a->siglen);
238 if(ayiyahash(a->hashmeth, pkt, pktlen, dig) != a->siglen)
240 return memcmp(sig, dig, a->siglen) != 0;
244 ayiyaout(int fd, AYIYA *a, uchar *p, int n)
248 a->idlen = conf.idlen;
249 a->siglen = conf.siglen;
250 a->idtype = conf.idtype;
251 a->hashmeth = conf.hashmeth;
252 a->authmeth = conf.authmeth;
253 a->identity = conf.identity;
254 a->signature = conf.signature;
256 a->epochtime = time(nil);
263 m = ayiyapack(a, p, n);
268 if (write(fd, p, n) != n) {
269 syslog(0, "ayiya", "error writing to tunnel (%r), giving up");
278 fprint(2, "ayiyarquery: %s\n", q);
286 fprint(2, "usage: %s [-g] [-x mtpt] [-k secret] local6[/mask] remote4 remote6\n",
291 /* process non-option arguments */
293 procargs(int argc, char **argv)
303 /* local v6 address (mask defaults to /128) */
304 memcpy(localmask, IPallbits, sizeof localmask);
305 p = strchr(loc6, '/');
307 parseipmask(localmask, p);
310 if (parseip(local6, loc6) == -1)
311 sysfatal("bad local v6 address %s", loc6);
314 if (argc >= 1 && argv[0][0] == '/') {
315 parseipmask(localmask, *argv++);
319 fprint(2, "local6 %I %M\n", local6, localmask);
321 outside = netmkaddr(*argv++, "udp", "5072");
325 outside = strdup(outside);
327 fprint(2, "outside %s\n", outside);
329 /* remote v6 address */
330 if (parseip(remote6, *argv++) == -1)
331 sysfatal("bad remote v6 address %s", argv[-1]);
336 maskip(local6, localmask, localnet);
338 fprint(2, "localnet %I remote6 %I\n", localnet, remote6);
346 char buf[128], path[64];
349 * open local IPv6 interface (as a packet interface)
352 cl = smprint("%s/ipifc/clone", inside);
353 cfd = open(cl, ORDWR); /* allocate a conversation */
355 if (cfd < 0 || (n = read(cfd, buf, sizeof buf - 1)) <= 0)
356 sysfatal("can't make packet interface %s: %r", cl);
358 fprint(2, "cloned %s as local v6 interface\n", cl);
362 snprint(path, sizeof path, "%s/ipifc/%s/data", inside, buf);
363 *v6net = open(path, ORDWR);
364 if (*v6net < 0 || fprint(cfd, "bind pkt") < 0)
365 sysfatal("can't bind packet interface: %r");
366 /* 1280 is MTU, apparently from rfc2460 */
367 if (fprint(cfd, "add %I %M %I 1280", local6, localmask, remote6) <= 0)
368 sysfatal("can't set local ipv6 address: %r");
371 fprint(2, "opened & bound %s as local v6 interface\n", path);
374 /* route global addresses through the tunnel to remote6 */
375 ir = smprint("%s/iproute", inside);
376 cfd = open(ir, OWRITE);
377 if (cfd >= 0 && debug)
378 fprint(2, "injected 2000::/3 %I into %s\n", remote6, ir);
380 if (cfd < 0 || fprint(cfd, "add 2000:: /3 %I", remote6) <= 0)
381 sysfatal("can't set default global route: %r");
386 runtunnel(int v6net, int tunnel)
388 /* run the tunnel copying in the background */
389 switch (rfork(RFPROC|RFNOWAIT|RFMEM|RFNOTEG)) {
398 switch (rfork(RFPROC|RFNOWAIT|RFMEM)) {
402 tunnel2ip(tunnel, v6net);
405 ip2tunnel(v6net, tunnel);
408 exits("tunnel gone");
412 main(int argc, char **argv)
416 fmtinstall('I', eipfmt);
417 fmtinstall('V', eipfmt);
418 fmtinstall('M', eipfmt);
428 inside = EARGF(usage());
431 secret = EARGF(usage());
437 procargs(argc, argv);
439 conf.idtype = IdInteger;
440 conf.idlen = sizeof(local6);
441 conf.identity = local6;
443 conf.authmeth = AuthNone;
444 conf.hashmeth = HashSHA1;
445 conf.siglen = ayiyahash(conf.hashmeth, nil, 0, nil);
446 conf.signature = nullsig;
449 conf.authmeth = AuthSharedKey;
450 conf.signature = malloc(conf.siglen);
451 ayiyahash(conf.hashmeth, (uchar*)secret, strlen(secret), conf.signature);
452 memset(secret, 0, strlen(secret)); /* prevent accidents */
455 tunnel = dial(outside, nil, nil, nil);
457 sysfatal("can't dial tunnel: %r");
460 runtunnel(v6net, tunnel);
465 * based on libthread's threadsetname, but drags in less library code.
466 * actually just sets the arguments displayed.
469 procsetname(char *fmt, ...)
477 cmdname = vsmprint(fmt, arg);
481 snprint(buf, sizeof buf, "#p/%d/args", getpid());
482 if((fd = open(buf, OWRITE)) >= 0){
483 write(fd, cmdname, strlen(cmdname)+1);
492 catcher(void*, char *msg)
494 if(strstr(msg, "alarm") != nil){
502 * encapsulate v6 packets from the packet interface
503 * and send them into the tunnel.
506 ip2tunnel(int in, int out)
508 uchar buf[AYIYAMAXHDR + IP_MAXPAY], *p;
513 procsetname("v6 %I -> tunnel %s %I", local6, outside, remote6);
517 /* get a V6 packet destined for the tunnel */
522 p = buf + AYIYAMAXHDR;
523 if ((n = read(in, p, IP_MAXPAY)) <= 0) {
530 if(ayiyaout(out, y, p, 0) < 0)
538 /* if not IPV6, drop it */
539 if ((ip->vcf[0] & 0xF0) != IP_VER6)
542 /* check length: drop if too short, trim if too long */
543 m = nhgets(ip->ploadlen) + IPV6HDR_LEN;
549 /* drop if v6 source or destination address is naughty */
550 if (badipv6(ip->src)) {
551 syslog(0, "ayiya", "egress filtered %I -> %I; bad src",
555 if ((!equivip6(ip->dst, remote6) && badipv6(ip->dst))) {
556 syslog(0, "ayiya", "egress filtered %I -> %I; "
557 "bad dst not remote", ip->src, ip->dst);
562 fprint(2, "v6 to tunnel %I -> %I\n", ip->src, ip->dst);
564 /* pass packet to the other end of the tunnel */
565 y->nexthdr = IP_IPV6PROTO;
566 y->opcode = OpForward;
567 if(ayiyaout(out, y, p, n) < 0 && !alarmed)
575 * decapsulate v6 packets from the tunnel
576 * and forward them to the packet interface
579 tunnel2ip(int in, int out)
581 uchar buf[2*AYIYAMAXHDR + IP_MAXPAY + 5], *p;
587 procsetname("tunnel %s %I -> v6 %I", outside, remote6, local6);
590 p = buf + AYIYAMAXHDR; /* space for reply header */
592 /* get a packet from the tunnel */
593 if ((n = read(in, p, AYIYAMAXHDR + IP_MAXPAY)) <= 0)
596 /* zero slackspace */
599 m = ayiyaunpack(y, p, n);
608 if (ayiyaverify(y, p, n) != 0) {
609 fprint(2, "ayiya bad packet signature\n");
617 case OpEchoRequestAndForward:
620 fprint(2, "ayiya motd: %s\n", (char*)p);
625 if (ayiyarquery((char*)p + 4) < 0)
627 n = 4 + strlen((char*)p + 4);
628 y->opcode = OpQueryResponse;
629 if (ayiyaout(in, y, p, n) < 0)
634 case OpQueryResponse:
637 fprint(2, "ayiya unknown opcode: %x\n", y->opcode);
643 case OpEchoRequestAndForward:
644 /* if not IPv6 nor ICMPv6, drop it */
645 if (y->nexthdr != IP_IPV6PROTO && y->nexthdr != IP_ICMPV6PROTO) {
647 "dropping pkt from tunnel with inner proto %d",
657 * don't relay: just accept packets for local host/subnet
658 * (this blocks link-local and multicast addresses as well)
660 maskip(op->dst, localmask, a);
661 if (!equivip6(a, localnet)) {
662 syslog(0, "ayiya", "ingress filtered %I -> %I; "
663 "dst not on local net", op->src, op->dst);
667 fprint(2, "tunnel to v6 %I -> %I\n", op->src, op->dst);
669 /* pass V6 packet to the interface */
670 if (write(out, p, n) != n) {
671 syslog(0, "ayiya", "error writing to packet interface (%r), giving up");
679 case OpEchoRequestAndForward:
680 y->opcode = OpEchoResponse;
681 if (ayiyaout(in, y, p, n) < 0)
691 case 0: /* unassigned */
692 case 10: /* private */
693 case 127: /* loopback */
696 return a[1] >= 16; /* 172.16.0.0/12 private */
698 return a[1] == 168; /* 192.168.0.0/16 private */
700 return a[1] == 254; /* 169.254.0.0/16 DHCP link-local */
702 /* 224.0.0.0/4 multicast, 240.0.0.0/4 reserved, broadcast */
707 * 0x0000/16 prefix = v4 compatible, v4 mapped, loopback, unspecified...
708 * site-local is now deprecated, rfc3879
713 int h = a[0]<<8 | a[1];
715 return h == 0 || ISIPV6MCAST(a) || ISIPV6LINKLOCAL(a) ||
716 h == V6to4pfx && badipv4(a+2);