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)
297 char *ipstr, *maskstr;
301 ipstr = *argv++, argc--;
302 maskstr = strchr(ipstr, '/');
303 if (maskstr == nil && **argv == '/')
304 maskstr = *argv++, argc--;
305 if (parseipandmask(local6, localmask, ipstr, maskstr) == -1 || isv4(local6))
306 sysfatal("bad local v6 address/mask: %s", ipstr);
308 fprint(2, "local6 %I %M\n", local6, localmask);
310 outside = netmkaddr(*argv++, "udp", "5072");
313 outside = strdup(outside);
315 fprint(2, "outside %s\n", outside);
317 /* remote v6 address */
318 if (parseip(remote6, *argv++) == -1)
319 sysfatal("bad remote v6 address %s", argv[-1]);
324 maskip(local6, localmask, localnet);
326 fprint(2, "localnet %I remote6 %I\n", localnet, remote6);
334 char buf[128], path[64];
337 * open local IPv6 interface (as a packet interface)
340 cl = smprint("%s/ipifc/clone", inside);
341 cfd = open(cl, ORDWR); /* allocate a conversation */
343 if (cfd < 0 || (n = read(cfd, buf, sizeof buf - 1)) <= 0)
344 sysfatal("can't make packet interface %s: %r", cl);
346 fprint(2, "cloned %s as local v6 interface\n", cl);
350 snprint(path, sizeof path, "%s/ipifc/%s/data", inside, buf);
351 *v6net = open(path, ORDWR);
352 if (*v6net < 0 || fprint(cfd, "bind pkt") < 0)
353 sysfatal("can't bind packet interface: %r");
354 if (fprint(cfd, "add %I %M %I %d", local6, localmask, remote6,
355 mtu - (IPV4HDR_LEN+8) - (8+conf.idlen+conf.siglen)) <= 0)
356 sysfatal("can't set local ipv6 address: %r");
359 fprint(2, "opened & bound %s as local v6 interface\n", path);
362 /* route global addresses through the tunnel to remote6 */
363 ir = smprint("%s/iproute", inside);
364 cfd = open(ir, OWRITE);
365 if (cfd >= 0 && debug)
366 fprint(2, "injected 2000::/3 %I into %s\n", remote6, ir);
368 if (cfd < 0 || fprint(cfd, "add 2000:: /3 %I", remote6) <= 0)
369 sysfatal("can't set default global route: %r");
374 runtunnel(int v6net, int tunnel)
376 /* run the tunnel copying in the background */
377 switch (rfork(RFPROC|RFNOWAIT|RFMEM|RFNOTEG)) {
386 switch (rfork(RFPROC|RFNOWAIT|RFMEM)) {
390 tunnel2ip(tunnel, v6net);
393 ip2tunnel(v6net, tunnel);
396 exits("tunnel gone");
400 main(int argc, char **argv)
404 fmtinstall('I', eipfmt);
405 fmtinstall('V', eipfmt);
406 fmtinstall('M', eipfmt);
416 mtu = atoi(EARGF(usage()));
419 inside = EARGF(usage());
422 secret = EARGF(usage());
428 procargs(argc, argv);
430 conf.idtype = IdInteger;
431 conf.idlen = sizeof(local6);
432 conf.identity = local6;
434 conf.authmeth = AuthNone;
435 conf.hashmeth = HashSHA1;
436 conf.siglen = ayiyahash(conf.hashmeth, nil, 0, nil);
437 conf.signature = nullsig;
440 conf.authmeth = AuthSharedKey;
441 conf.signature = malloc(conf.siglen);
442 ayiyahash(conf.hashmeth, (uchar*)secret, strlen(secret), conf.signature);
443 memset(secret, 0, strlen(secret)); /* prevent accidents */
446 tunnel = dial(outside, nil, nil, nil);
448 sysfatal("can't dial tunnel: %r");
451 runtunnel(v6net, tunnel);
458 catcher(void*, char *msg)
460 if(strstr(msg, "alarm") != nil){
468 * encapsulate v6 packets from the packet interface
469 * and send them into the tunnel.
472 ip2tunnel(int in, int out)
474 uchar buf[AYIYAMAXHDR + IP_MAXPAY], *p;
479 procsetname("v6 %I -> tunnel %s %I", local6, outside, remote6);
483 /* get a V6 packet destined for the tunnel */
488 p = buf + AYIYAMAXHDR;
489 if ((n = read(in, p, IP_MAXPAY)) <= 0) {
496 if(ayiyaout(out, y, p, 0) < 0)
504 /* if not IPV6, drop it */
505 if ((ip->vcf[0] & 0xF0) != IP_VER6)
508 /* check length: drop if too short, trim if too long */
509 m = nhgets(ip->ploadlen) + IPV6HDR_LEN;
515 /* drop if v6 source or destination address is naughty */
516 if (badipv6(ip->src)) {
517 syslog(0, "ayiya", "egress filtered %I -> %I; bad src",
521 if ((ipcmp(ip->dst, remote6) != 0 && badipv6(ip->dst))) {
522 syslog(0, "ayiya", "egress filtered %I -> %I; "
523 "bad dst not remote", ip->src, ip->dst);
528 fprint(2, "v6 to tunnel %I -> %I\n", ip->src, ip->dst);
530 /* pass packet to the other end of the tunnel */
531 y->nexthdr = IP_IPV6PROTO;
532 y->opcode = OpForward;
533 if(ayiyaout(out, y, p, n) < 0 && !alarmed)
541 * decapsulate v6 packets from the tunnel
542 * and forward them to the packet interface
545 tunnel2ip(int in, int out)
547 uchar buf[2*AYIYAMAXHDR + IP_MAXPAY + 5], *p;
553 procsetname("tunnel %s %I -> v6 %I", outside, remote6, local6);
556 p = buf + AYIYAMAXHDR; /* space for reply header */
558 /* get a packet from the tunnel */
559 if ((n = read(in, p, AYIYAMAXHDR + IP_MAXPAY)) <= 0)
562 /* zero slackspace */
565 m = ayiyaunpack(y, p, n);
574 if (ayiyaverify(y, p, n) != 0) {
575 fprint(2, "ayiya bad packet signature\n");
583 case OpEchoRequestAndForward:
586 fprint(2, "ayiya motd: %s\n", (char*)p);
591 if (ayiyarquery((char*)p + 4) < 0)
593 n = 4 + strlen((char*)p + 4);
594 y->opcode = OpQueryResponse;
595 if (ayiyaout(in, y, p, n) < 0)
600 case OpQueryResponse:
603 fprint(2, "ayiya unknown opcode: %x\n", y->opcode);
609 case OpEchoRequestAndForward:
610 /* if not IPv6 nor ICMPv6, drop it */
611 if (y->nexthdr != IP_IPV6PROTO && y->nexthdr != IP_ICMPV6PROTO) {
613 "dropping pkt from tunnel with inner proto %d",
623 * don't relay: just accept packets for local host/subnet
624 * (this blocks link-local and multicast addresses as well)
626 maskip(op->dst, localmask, a);
627 if (ipcmp(a, localnet) != 0) {
628 syslog(0, "ayiya", "ingress filtered %I -> %I; "
629 "dst not on local net", op->src, op->dst);
633 fprint(2, "tunnel to v6 %I -> %I\n", op->src, op->dst);
635 /* pass V6 packet to the interface */
636 if (write(out, p, n) != n) {
637 syslog(0, "ayiya", "error writing to packet interface (%r), giving up");
645 case OpEchoRequestAndForward:
646 y->opcode = OpEchoResponse;
647 if (ayiyaout(in, y, p, n) < 0)
657 case 0: /* unassigned */
658 case 10: /* private */
659 case 127: /* loopback */
662 return a[1] >= 16; /* 172.16.0.0/12 private */
664 return a[1] == 168; /* 192.168.0.0/16 private */
666 return a[1] == 254; /* 169.254.0.0/16 DHCP link-local */
668 /* 224.0.0.0/4 multicast, 240.0.0.0/4 reserved, broadcast */
673 * 0x0000/16 prefix = v4 compatible, v4 mapped, loopback, unspecified...
674 * site-local is now deprecated, rfc3879
679 int h = a[0]<<8 | a[1];
681 return h == 0 || ISIPV6MCAST(a) || ISIPV6LINKLOCAL(a) ||
682 h == V6to4pfx && badipv4(a+2);