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;
84 uchar local6[IPaddrlen];
85 uchar remote6[IPaddrlen];
86 uchar localmask[IPaddrlen];
87 uchar localnet[IPaddrlen];
89 uchar nullsig[AYIYAMAXSIG];
91 static char *secret = nil;
93 static char *outside = nil; /* dial string of tunnel server */
94 static char *inside = "/net";
96 static int badipv4(uchar*);
97 static int badipv6(uchar*);
98 static void ip2tunnel(int, int);
99 static void tunnel2ip(int, int);
106 fprint(2, "idlen=%ud idtype=%ux siglen=%ud hashmeth=%ud authmeth=%ud opcode=%ux nexthdr=%ux epochtime=%ux\n",
107 a->idlen, a->idtype, a->siglen, a->hashmeth, a->authmeth, a->opcode, a->nexthdr, a->epochtime);
108 fprint(2, "identity=[ ");
109 for(i=0; i<a->idlen; i++)
110 fprint(2, "%.2ux ", a->identity[i]);
112 fprint(2, "signature=[ ");
113 for(i=0; i<a->siglen; i++)
114 fprint(2, "%.2ux ", a->signature[i]);
124 for(n = 0; (a >>= 1) != 0; n++)
130 ayiyapack(AYIYA *a, uchar *pay, int paylen)
137 memmove(pkt, a->signature, a->siglen);
141 memmove(pkt, a->identity, a->idlen);
145 pkt[0] = a->epochtime>>24;
146 pkt[1] = a->epochtime>>16;
147 pkt[2] = a->epochtime>>8;
148 pkt[3] = a->epochtime;
151 pkt[0] = (lg2(a->idlen)<<4) | a->idtype;
152 pkt[1] = ((a->siglen/4)<<4) | a->hashmeth;
153 pkt[2] = (a->authmeth<<4) | a->opcode;
162 ayiyaunpack(AYIYA *a, uchar *pkt, int pktlen)
169 a->idlen = 1<<(pkt[0] >> 4);
170 a->idtype = pkt[0] & 15;
171 a->siglen = (pkt[1] >> 4) * 4;
172 a->hashmeth = pkt[1] & 15;
173 a->authmeth = pkt[2] >> 4;
174 a->opcode = pkt[2] & 15;
176 a->epochtime = pkt[7] | pkt[6]<<8 | pkt[5]<<16 | pkt[4]<<24;
178 hdrlen = 8 + a->idlen + a->siglen;
184 a->identity = pkt + 8;
188 a->signature = pkt + 8 + a->idlen;
194 ayiyahash(uint meth, uchar *pkt, int pktlen, uchar *dig)
199 md5(pkt, pktlen, dig, nil);
203 sha1(pkt, pktlen, dig, nil);
210 ayiyasign(AYIYA *a, uchar *pkt, int pktlen)
212 uchar dig[AYIYAMAXSIG], *pktsig;
214 if(a->hashmeth == HashNone && a->siglen == 0)
217 assert(a->siglen <= sizeof(dig));
218 assert(a->siglen <= pktlen - a->idlen - 8);
219 pktsig = pkt + 8 + a->idlen;
221 if(ayiyahash(a->hashmeth, pkt, pktlen, dig) != a->siglen){
222 memset(pktsig, 0, a->siglen);
226 memmove(pktsig, dig, a->siglen);
230 ayiyaverify(AYIYA *a, uchar *pkt, int pktlen)
232 uchar dig[AYIYAMAXSIG], sig[AYIYAMAXSIG];
234 if(conf.hashmeth == HashNone && a->siglen == 0)
236 if(a->hashmeth != conf.hashmeth || a->authmeth != conf.authmeth || a->siglen != conf.siglen)
238 memmove(sig, a->signature, a->siglen);
239 memmove(a->signature, conf.signature, a->siglen);
240 if(ayiyahash(a->hashmeth, pkt, pktlen, dig) != a->siglen)
242 return memcmp(sig, dig, a->siglen) != 0;
246 ayiyaout(int fd, AYIYA *a, uchar *p, int n)
250 a->idlen = conf.idlen;
251 a->siglen = conf.siglen;
252 a->idtype = conf.idtype;
253 a->hashmeth = conf.hashmeth;
254 a->authmeth = conf.authmeth;
255 a->identity = conf.identity;
256 a->signature = conf.signature;
258 a->epochtime = time(nil);
265 m = ayiyapack(a, p, n);
270 if (write(fd, p, n) != n) {
271 syslog(0, "ayiya", "error writing to tunnel (%r), giving up");
280 fprint(2, "ayiyarquery: %s\n", q);
288 fprint(2, "usage: %s [-g] [-m mtu] [-x mtpt] [-k secret] local6[/mask] remote4 remote6\n",
293 /* process non-option arguments */
295 procargs(int argc, char **argv)
305 /* local v6 address (mask defaults to /128) */
306 memcpy(localmask, IPallbits, sizeof localmask);
307 p = strchr(loc6, '/');
309 parseipmask(localmask, p);
312 if (parseip(local6, loc6) == -1)
313 sysfatal("bad local v6 address %s", loc6);
316 if (argc >= 1 && argv[0][0] == '/') {
317 parseipmask(localmask, *argv++);
321 fprint(2, "local6 %I %M\n", local6, localmask);
323 outside = netmkaddr(*argv++, "udp", "5072");
327 outside = strdup(outside);
329 fprint(2, "outside %s\n", outside);
331 /* remote v6 address */
332 if (parseip(remote6, *argv++) == -1)
333 sysfatal("bad remote v6 address %s", argv[-1]);
338 maskip(local6, localmask, localnet);
340 fprint(2, "localnet %I remote6 %I\n", localnet, remote6);
348 char buf[128], path[64];
351 * open local IPv6 interface (as a packet interface)
354 cl = smprint("%s/ipifc/clone", inside);
355 cfd = open(cl, ORDWR); /* allocate a conversation */
357 if (cfd < 0 || (n = read(cfd, buf, sizeof buf - 1)) <= 0)
358 sysfatal("can't make packet interface %s: %r", cl);
360 fprint(2, "cloned %s as local v6 interface\n", cl);
364 snprint(path, sizeof path, "%s/ipifc/%s/data", inside, buf);
365 *v6net = open(path, ORDWR);
366 if (*v6net < 0 || fprint(cfd, "bind pkt") < 0)
367 sysfatal("can't bind packet interface: %r");
368 if (fprint(cfd, "add %I %M %I %d", local6, localmask, remote6,
369 mtu - (IPV4HDR_LEN+8) - (8+conf.idlen+conf.siglen)) <= 0)
370 sysfatal("can't set local ipv6 address: %r");
373 fprint(2, "opened & bound %s as local v6 interface\n", path);
376 /* route global addresses through the tunnel to remote6 */
377 ir = smprint("%s/iproute", inside);
378 cfd = open(ir, OWRITE);
379 if (cfd >= 0 && debug)
380 fprint(2, "injected 2000::/3 %I into %s\n", remote6, ir);
382 if (cfd < 0 || fprint(cfd, "add 2000:: /3 %I", remote6) <= 0)
383 sysfatal("can't set default global route: %r");
388 runtunnel(int v6net, int tunnel)
390 /* run the tunnel copying in the background */
391 switch (rfork(RFPROC|RFNOWAIT|RFMEM|RFNOTEG)) {
400 switch (rfork(RFPROC|RFNOWAIT|RFMEM)) {
404 tunnel2ip(tunnel, v6net);
407 ip2tunnel(v6net, tunnel);
410 exits("tunnel gone");
414 main(int argc, char **argv)
418 fmtinstall('I', eipfmt);
419 fmtinstall('V', eipfmt);
420 fmtinstall('M', eipfmt);
430 mtu = atoi(EARGF(usage()));
433 inside = EARGF(usage());
436 secret = EARGF(usage());
442 procargs(argc, argv);
444 conf.idtype = IdInteger;
445 conf.idlen = sizeof(local6);
446 conf.identity = local6;
448 conf.authmeth = AuthNone;
449 conf.hashmeth = HashSHA1;
450 conf.siglen = ayiyahash(conf.hashmeth, nil, 0, nil);
451 conf.signature = nullsig;
454 conf.authmeth = AuthSharedKey;
455 conf.signature = malloc(conf.siglen);
456 ayiyahash(conf.hashmeth, (uchar*)secret, strlen(secret), conf.signature);
457 memset(secret, 0, strlen(secret)); /* prevent accidents */
460 tunnel = dial(outside, nil, nil, nil);
462 sysfatal("can't dial tunnel: %r");
465 runtunnel(v6net, tunnel);
470 * based on libthread's threadsetname, but drags in less library code.
471 * actually just sets the arguments displayed.
474 procsetname(char *fmt, ...)
482 cmdname = vsmprint(fmt, arg);
486 snprint(buf, sizeof buf, "#p/%d/args", getpid());
487 if((fd = open(buf, OWRITE)) >= 0){
488 write(fd, cmdname, strlen(cmdname)+1);
497 catcher(void*, char *msg)
499 if(strstr(msg, "alarm") != nil){
507 * encapsulate v6 packets from the packet interface
508 * and send them into the tunnel.
511 ip2tunnel(int in, int out)
513 uchar buf[AYIYAMAXHDR + IP_MAXPAY], *p;
518 procsetname("v6 %I -> tunnel %s %I", local6, outside, remote6);
522 /* get a V6 packet destined for the tunnel */
527 p = buf + AYIYAMAXHDR;
528 if ((n = read(in, p, IP_MAXPAY)) <= 0) {
535 if(ayiyaout(out, y, p, 0) < 0)
543 /* if not IPV6, drop it */
544 if ((ip->vcf[0] & 0xF0) != IP_VER6)
547 /* check length: drop if too short, trim if too long */
548 m = nhgets(ip->ploadlen) + IPV6HDR_LEN;
554 /* drop if v6 source or destination address is naughty */
555 if (badipv6(ip->src)) {
556 syslog(0, "ayiya", "egress filtered %I -> %I; bad src",
560 if ((!equivip6(ip->dst, remote6) && badipv6(ip->dst))) {
561 syslog(0, "ayiya", "egress filtered %I -> %I; "
562 "bad dst not remote", ip->src, ip->dst);
567 fprint(2, "v6 to tunnel %I -> %I\n", ip->src, ip->dst);
569 /* pass packet to the other end of the tunnel */
570 y->nexthdr = IP_IPV6PROTO;
571 y->opcode = OpForward;
572 if(ayiyaout(out, y, p, n) < 0 && !alarmed)
580 * decapsulate v6 packets from the tunnel
581 * and forward them to the packet interface
584 tunnel2ip(int in, int out)
586 uchar buf[2*AYIYAMAXHDR + IP_MAXPAY + 5], *p;
592 procsetname("tunnel %s %I -> v6 %I", outside, remote6, local6);
595 p = buf + AYIYAMAXHDR; /* space for reply header */
597 /* get a packet from the tunnel */
598 if ((n = read(in, p, AYIYAMAXHDR + IP_MAXPAY)) <= 0)
601 /* zero slackspace */
604 m = ayiyaunpack(y, p, n);
613 if (ayiyaverify(y, p, n) != 0) {
614 fprint(2, "ayiya bad packet signature\n");
622 case OpEchoRequestAndForward:
625 fprint(2, "ayiya motd: %s\n", (char*)p);
630 if (ayiyarquery((char*)p + 4) < 0)
632 n = 4 + strlen((char*)p + 4);
633 y->opcode = OpQueryResponse;
634 if (ayiyaout(in, y, p, n) < 0)
639 case OpQueryResponse:
642 fprint(2, "ayiya unknown opcode: %x\n", y->opcode);
648 case OpEchoRequestAndForward:
649 /* if not IPv6 nor ICMPv6, drop it */
650 if (y->nexthdr != IP_IPV6PROTO && y->nexthdr != IP_ICMPV6PROTO) {
652 "dropping pkt from tunnel with inner proto %d",
662 * don't relay: just accept packets for local host/subnet
663 * (this blocks link-local and multicast addresses as well)
665 maskip(op->dst, localmask, a);
666 if (!equivip6(a, localnet)) {
667 syslog(0, "ayiya", "ingress filtered %I -> %I; "
668 "dst not on local net", op->src, op->dst);
672 fprint(2, "tunnel to v6 %I -> %I\n", op->src, op->dst);
674 /* pass V6 packet to the interface */
675 if (write(out, p, n) != n) {
676 syslog(0, "ayiya", "error writing to packet interface (%r), giving up");
684 case OpEchoRequestAndForward:
685 y->opcode = OpEchoResponse;
686 if (ayiyaout(in, y, p, n) < 0)
696 case 0: /* unassigned */
697 case 10: /* private */
698 case 127: /* loopback */
701 return a[1] >= 16; /* 172.16.0.0/12 private */
703 return a[1] == 168; /* 192.168.0.0/16 private */
705 return a[1] == 254; /* 169.254.0.0/16 DHCP link-local */
707 /* 224.0.0.0/4 multicast, 240.0.0.0/4 reserved, broadcast */
712 * 0x0000/16 prefix = v4 compatible, v4 mapped, loopback, unspecified...
713 * site-local is now deprecated, rfc3879
718 int h = a[0]<<8 | a[1];
720 return h == 0 || ISIPV6MCAST(a) || ISIPV6LINKLOCAL(a) ||
721 h == V6to4pfx && badipv4(a+2);