diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/ip/pptp.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/ip/pptp.c')
-rwxr-xr-x | sys/src/cmd/ip/pptp.c | 874 |
1 files changed, 874 insertions, 0 deletions
diff --git a/sys/src/cmd/ip/pptp.c b/sys/src/cmd/ip/pptp.c new file mode 100755 index 000000000..eaf2064d1 --- /dev/null +++ b/sys/src/cmd/ip/pptp.c @@ -0,0 +1,874 @@ +/* + * Point-to-point Tunneling Protocol (PPTP) + * See RFC 2637, pptpd.c + */ + +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <ip.h> +#include <thread.h> + +int ack; +int alarmed; +int ctlechotime; +int ctlfd; +int ctlrcvtime; +int debug; +int grefd; +uchar localip[IPaddrlen]; +int localwin; +char *keyspec; +int now; +char *pppnetmntpt; +int pid; +Channel *pidchan; +int pppfd; +int primary; +int rack; +Channel *rdchan; +int rdexpect; +int remid; +uchar remoteip[IPaddrlen]; +int remwin; +int rseq; +int seq; +char tcpdir[40]; +Channel *tickchan; +int topppfd; + +int aread(int, int, void*, int); +int catchalarm(void*, char*); +void dumpctlpkt(uchar*); +void getaddrs(void); +void *emalloc(long); +void ewrite(int, void*, int); +void myfatal(char*, ...); +#pragma varargck argpos myfatal 1 +int pptp(char*); +void pushppp(int); +void recordack(int); +int schedack(int, uchar*, int); +void waitacks(void); + +void +usage(void) +{ + fprint(2, "usage: ip/pptp [-Pd] [-k keyspec] [-x pppnetmntpt] [-w window] server\n"); + exits("usage"); +} + +void +threadmain(int argc, char **argv) +{ + int fd; + + ARGBEGIN{ + case 'P': + primary = 1; + break; + case 'd': + debug++; + break; + case 'k': + keyspec = EARGF(usage()); + break; + case 'w': + localwin = atoi(EARGF(usage())); + break; + case 'x': + pppnetmntpt = EARGF(usage()); + break; + default: + usage(); + }ARGEND + + if(argc != 1) + usage(); + + fmtinstall('E', eipfmt); + fmtinstall('I', eipfmt); + + rfork(RFNOTEG); + atnotify(catchalarm, 1); + fd = pptp(argv[0]); + pushppp(fd); + exits(nil); +} + +int +catchalarm(void *a, char *msg) +{ + USED(a); + + if(strstr(msg, "alarm")){ + alarmed = 1; + return 1; + } + if(debug) + fprint(2, "note rcved: %s\n", msg); + return 0; +} + +enum { + Stack = 8192, + + PptpProto = 0x0100, + + Magic = 0x1a2b3c4d, + Window = 16, /* default window size */ + Timeout = 60, /* timeout in seconds for control channel */ + Pktsize = 2000, /* maximum packet size */ + Tick = 500, /* tick length in milliseconds */ + Sendtimeout = 4, /* in ticks */ + + Servertimeout = 5*60*1000/Tick, + Echointerval = 60*1000/Tick, +}; + +enum { + Syncframe = 0x1, + Asyncframe = 0x2, + Analog = 0x1, + Digital = 0x2, + Version = 0x100, +}; + +enum { + Tstart = 1, + Rstart = 2, + Tstop = 3, + Rstop = 4, + Techo = 5, + Recho = 6, + Tcallout = 7, + Rcallout = 8, + Tcallreq = 9, + Rcallreq = 10, + Acallcon = 11, + Tcallclear = 12, + Acalldis = 13, + Awaninfo = 14, + Alinkinfo = 15, +}; + +void +recho(uchar *in) +{ + uchar out[20]; + + if(nhgets(in) < 16) + return; + + memset(out, 0, sizeof out); + hnputs(out, sizeof out); + hnputs(out+2, 1); + hnputl(out+4, Magic); + hnputs(out+8, Recho); + memmove(out+12, in+12, 4); + out[16] = 1; + + ewrite(ctlfd, out, sizeof out); +} + +void +sendecho(void) +{ + uchar out[16]; + + ctlechotime = now; + memset(out, 0, sizeof out); + hnputs(out, sizeof out); + hnputs(out+2, 1); + hnputl(out+4, Magic); + hnputs(out+8, Techo); + + ewrite(ctlfd, out, sizeof out); +} + +void +pptpctlproc(void*) +{ + uchar pkt[1600], *p; + int len; + + for(;;){ + if(readn(ctlfd, pkt, 2) != 2) + myfatal("pptpread: %r"); + len = nhgets(pkt); + if(len < 12 || len+2 >= sizeof pkt) + myfatal("pptpread: bad length %d", len); + if(readn(ctlfd, pkt+2, len-2) != len-2) + myfatal("pptpread: %r"); + if(nhgetl(pkt+4) != Magic) + myfatal("pptpread bad magic"); + if(nhgets(pkt+2) != 1) + myfatal("pptpread bad message type"); + if(debug) + dumpctlpkt(pkt); + ctlrcvtime = now; + + switch(nhgets(pkt+8)){ + case Tstart: + case Tstop: + case Tcallout: + case Tcallreq: + case Tcallclear: + case Acallcon: + case Acalldis: + case Awaninfo: + myfatal("unexpected msg type %d", nhgets(pkt+8)); + case Techo: + recho(pkt); + break; + case Recho: + break; + case Rstart: + case Rstop: + case Rcallout: + case Rcallreq: + if(rdexpect != nhgets(pkt+8)) + continue; + p = emalloc(len); + memmove(p, pkt, len); + sendp(rdchan, p); + break; + case Alinkinfo: + myfatal("cannot change ppp params on the fly"); + } + } +} + +enum { + Seqnum = 0x1000, + Acknum = 0x0080, + + GrePPP = 0x880B, +}; + +void +grereadproc(void*) +{ + int datoff, flags, len, n, pass; + uchar pkt[1600]; + uchar src[IPaddrlen], dst[IPaddrlen]; + + rfork(RFFDG); + close(pppfd); + sendul(pidchan, getpid()); + + while((n = read(grefd, pkt, sizeof pkt)) > 0){ + if(n == sizeof pkt) + myfatal("gre pkt buffer too small"); + if(n < 16){ + if(debug) + fprint(2, "small pkt len %d ignored\n", n); + continue; + } + v4tov6(src, pkt); + v4tov6(dst, pkt+4); + if(ipcmp(src, remoteip) != 0 || ipcmp(dst, localip) != 0) + myfatal("%I: gre read bad address src=%I dst=%I", + remoteip, src, dst); + if(nhgets(pkt+10) != GrePPP) + myfatal("%I: gre read bad protocol 0x%x", + remoteip, nhgets(pkt+10)); + + flags = nhgets(pkt+8); + if((flags&0xEF7F) != 0x2001){ + if(debug) + fprint(2, "bad flags in gre hdr 0x%x\n", flags); + continue; + } + datoff = 8+8; + pass = 0; + len = nhgets(pkt+8+4); + if(len > n-datoff){ + fprint(2, "bad payload length %d > %d\n", + len, n-datoff); + continue; + } + if(flags&Seqnum) + datoff += 4; + if(flags&Acknum){ + recordack(nhgetl(pkt+datoff)); + datoff += 4; + } + if(flags&Seqnum) + pass = schedack(nhgetl(pkt+8+8), pkt+datoff, len); + if(debug) + fprint(2, "got gre callid %d len %d flag 0x%x pass %d seq %d rseq %d\n", nhgets(pkt+8+6), + len, flags, pass, nhgetl(pkt+8+8), rseq); + } + threadexits(nil); +} + +void +pppreadproc(void*) +{ + int n, myrseq; + uchar pkt[1600]; + enum { + Hdr = 8+16, + }; + + rfork(RFFDG); + close(pppfd); + sendul(pidchan, getpid()); + + while((n = read(topppfd, pkt+Hdr, sizeof pkt-Hdr)) > 0){ + if(n == sizeof pkt-Hdr) + myfatal("ppp pkt buffer too small"); + v6tov4(pkt+0, localip); + v6tov4(pkt+4, remoteip); + hnputs(pkt+8, 0x2001 | Seqnum | Acknum); + hnputs(pkt+10, GrePPP); + hnputs(pkt+12, n); + hnputs(pkt+14, remid); + hnputl(pkt+16, ++seq); + myrseq = rseq; + hnputl(pkt+20, myrseq); + rack = myrseq; + if(debug) + fprint(2, "wrote gre callid %d len %d flag 0x%x seq %d rseq %d\n", nhgets(pkt+8+6), + n, nhgets(pkt+8), nhgetl(pkt+16), nhgetl(pkt+20)); + if(write(grefd, pkt, n+Hdr) != n+Hdr) + myfatal("gre write: %r"); + waitacks(); + } + threadexits(nil); +} + +void +sendack(void) +{ + int myrseq; + uchar pkt[20]; + + v6tov4(pkt+0, localip); + v6tov4(pkt+4, remoteip); + hnputs(pkt+8, 0x2001 | Acknum); + hnputs(pkt+10, GrePPP); + hnputs(pkt+12, 0); + hnputs(pkt+14, remid); + myrseq = rseq; + rack = myrseq; + hnputs(pkt+16, myrseq); + + if(write(grefd, pkt, sizeof pkt) != sizeof pkt) + myfatal("gre write: %r"); +} + +int +schedack(int n, uchar *dat, int len) +{ + static uchar sdat[1600]; + static int srseq, slen; + + if(n-rseq <= 0){ + fprint(2, "skipping pkt %d len %d, have %d\n", n, len, rseq); + return 0; + } + + /* missed one pkt, maybe a swap happened, save pkt */ + if(n==rseq+2){ + memmove(sdat, dat, len); + slen = len; + srseq = n; + return 0; + } + + if(n-rseq > 1){ + if(slen && srseq == n-1){ + fprint(2, "reswapped pkts %d and %d\n", srseq, n); + write(topppfd, sdat, slen); + slen = 0; + }else + fprint(2, "missed pkts %d-%d, got %d len %d\n", rseq+1, n-1, n, len); + } + write(topppfd, dat, len); + rseq = n; + + /* send ack if we haven't recently */ + if((int)(rseq-rack) > (localwin>>1)) + sendack(); + + return 1; +} + +void +gretimeoutproc(void*) +{ + for(;;){ + sleep(Tick); + now++; + nbsendul(tickchan, now); + if(now - ctlrcvtime > Servertimeout) + myfatal("server timeout"); + if(now - ctlechotime > Echointerval) + sendecho(); + } +} + +void +recordack(int n) +{ + ack = n; +} + +void +waitacks(void) +{ +/* + int start; + + start = now; + while(seq-ack > remwin && now-start < Sendtimeout){ + print("seq %d ack %d remwin %d now %d start %d\n", + seq, ack, remwin, now, start); + recvul(tickchan); + } +*/ +} + +void +tstart(void) +{ + char *name; + uchar pkt[200], *rpkt; + + memset(pkt, 0, sizeof pkt); + + hnputs(pkt+0, 156); + hnputs(pkt+2, 1); + hnputl(pkt+4, Magic); + hnputs(pkt+8, Tstart); + hnputs(pkt+12, PptpProto); + hnputl(pkt+16, 1); + hnputl(pkt+20, 1); + hnputs(pkt+24, 1); + name = sysname(); + if(name == nil) + name = "gnot"; + strcpy((char*)pkt+28, name); + strcpy((char*)pkt+92, "plan 9"); + + if(debug) + dumpctlpkt(pkt); + + rdexpect = Rstart; + ewrite(ctlfd, pkt, 156); + + rpkt = recvp(rdchan); + if(rpkt == nil) + myfatal("recvp: %r"); + if(nhgets(rpkt) != 156) + myfatal("Rstart wrong length %d != 156", nhgets(rpkt)); + if(rpkt[14] != 1) + myfatal("Rstart error %d", rpkt[15]); + free(rpkt); +} + +void +tcallout(void) +{ + uchar pkt[200], *rpkt; + + pid = getpid(); + + memset(pkt, 0, sizeof pkt); + hnputs(pkt+0, 168); + hnputs(pkt+2, 1); + hnputl(pkt+4, Magic); + hnputs(pkt+8, Tcallout); + + hnputl(pkt+16, 56000); + hnputl(pkt+20, 768000); + hnputl(pkt+24, 3); + hnputl(pkt+28, 3); + if(localwin == 0) + localwin = Window; + hnputs(pkt+32, localwin); + + if(debug) + dumpctlpkt(pkt); + + rdexpect = Rcallout; + ewrite(ctlfd, pkt, 168); + + rpkt = recvp(rdchan); + if(rpkt == nil) + myfatal("recvp: %r"); + if(nhgets(rpkt) != 32) + myfatal("Rcallreq wrong length %d != 32", nhgets(rpkt)); + if(rpkt[16] != 1) + myfatal("Rcallreq error %d", rpkt[17]); + remid = nhgets(pkt+12); + remwin = nhgets(pkt+24); + free(rpkt); +} + +/* +void +tcallreq(void) +{ + uchar pkt[200], *rpkt; + + pid = getpid(); + + memset(pkt, 0, sizeof pkt); + hnputs(pkt+0, 220); + hnputs(pkt+2, 1); + hnputl(pkt+4, Magic); + hnputs(pkt+8, Tcallreq); + + if(debug) + dumpctlpkt(pkt); + + rdexpect = Rcallreq; + ewrite(ctlfd, pkt, 220); + + rpkt = recvp(rdchan); + if(rpkt == nil) + myfatal("recvp: %r"); + if(nhgets(rpkt) != 24) + myfatal("Rcallreq wrong length %d != 24", nhgets(rpkt)); + if(rpkt[16] != 1) + myfatal("Rcallreq error %d", rpkt[17]); + remid = nhgets(pkt+12); + remwin = nhgets(pkt+18); + free(rpkt); +} + +void +acallcon(void) +{ + uchar pkt[200]; + + memset(pkt, 0, sizeof pkt); + hnputs(pkt+0, 28); + hnputs(pkt+2, 1); + hnputl(pkt+4, Magic); + hnputs(pkt+8, Acallcon); + hnputs(pkt+12, remid); + if(localwin == 0) + localwin = Window; + hnputs(pkt+20, localwin); + hnputl(pkt+24, 1); + + if(debug) + dumpctlpkt(pkt); + + ewrite(ctlfd, pkt, 28); +} +*/ + +int +pptp(char *addr) +{ + int p[2]; + char greaddr[128]; + + addr = netmkaddr(addr, "net", "pptp"); + ctlfd = dial(addr, nil, tcpdir, nil); + if(ctlfd < 0) + myfatal("dial %s: %r", addr); + getaddrs(); + + rdchan = chancreate(sizeof(void*), 0); + proccreate(pptpctlproc, nil, Stack); + + tstart(); + tcallout(); + + if(pipe(p) < 0) + myfatal("pipe: %r"); + + pppfd = p[0]; + topppfd = p[1]; + + strcpy(greaddr, tcpdir); + *strrchr(greaddr, '/') = '\0'; + sprint(strrchr(greaddr, '/')+1, "gre!%I!%d", remoteip, GrePPP); + + print("local %I remote %I gre %s remid %d remwin %d\n", + localip, remoteip, greaddr, remid, remwin); + + grefd = dial(greaddr, nil, nil, nil); + if(grefd < 0) + myfatal("dial gre: %r"); + + tickchan = chancreate(sizeof(int), 0); + proccreate(gretimeoutproc, nil, Stack); + + pidchan = chancreate(sizeof(int), 0); + proccreate(grereadproc, nil, Stack); + recvul(pidchan); + proccreate(pppreadproc, nil, Stack); + recvul(pidchan); + + close(topppfd); + return pppfd; +} + +void +pushppp(int fd) +{ + char *argv[16]; + int argc; + + argc = 0; + argv[argc++] = "/bin/ip/ppp"; + argv[argc++] = "-C"; + argv[argc++] = "-m1450"; + if(debug) + argv[argc++] = "-d"; + if(primary) + argv[argc++] = "-P"; + if(pppnetmntpt){ + argv[argc++] = "-x"; + argv[argc++] = pppnetmntpt; + } + if(keyspec){ + argv[argc++] = "-k"; + argv[argc++] = keyspec; + } + argv[argc] = nil; + + switch(fork()){ + case -1: + myfatal("fork: %r"); + default: + return; + case 0: + dup(fd, 0); + dup(fd, 1); + exec(argv[0], argv); + myfatal("exec: %r"); + } +} + +int +aread(int timeout, int fd, void *buf, int nbuf) +{ + int n; + + alarmed = 0; + alarm(timeout); + n = read(fd, buf, nbuf); + alarm(0); + if(alarmed) + return -1; + if(n < 0) + myfatal("read: %r"); + if(n == 0) + myfatal("short read"); + return n; +} + +void +ewrite(int fd, void *buf, int nbuf) +{ + char e[ERRMAX], path[64]; + + if(write(fd, buf, nbuf) != nbuf){ + rerrstr(e, sizeof e); + strcpy(path, "unknown"); + fd2path(fd, path, sizeof path); + myfatal("write %d to %s: %s", nbuf, path, e); + } +} + +void* +emalloc(long n) +{ + void *v; + + v = malloc(n); + if(v == nil) + myfatal("out of memory"); + return v; +} + +int +thread(void(*f)(void*), void *a) +{ + int pid; + pid=rfork(RFNOWAIT|RFMEM|RFPROC); + if(pid < 0) + myfatal("rfork: %r"); + if(pid != 0) + return pid; + (*f)(a); + _exits(nil); + return 0; // never reaches here +} + +void +dumpctlpkt(uchar *pkt) +{ + fprint(2, "pkt len %d mtype %d cookie 0x%.8ux type %d\n", + nhgets(pkt), nhgets(pkt+2), + nhgetl(pkt+4), nhgets(pkt+8)); + + switch(nhgets(pkt+8)){ + default: + fprint(2, "\tunknown type\n"); + break; + case Tstart: + fprint(2, "\tTstart proto %d framing %d bearer %d maxchan %d firmware %d\n", + nhgets(pkt+12), nhgetl(pkt+16), + nhgetl(pkt+20), nhgets(pkt+24), + nhgets(pkt+26)); + fprint(2, "\thost %.64s\n", (char*)pkt+28); + fprint(2, "\tvendor %.64s\n", (char*)pkt+92); + break; + case Rstart: + fprint(2, "\tRstart proto %d res %d err %d framing %d bearer %d maxchan %d firmware %d\n", + nhgets(pkt+12), pkt[14], pkt[15], + nhgetl(pkt+16), + nhgetl(pkt+20), nhgets(pkt+24), + nhgets(pkt+26)); + fprint(2, "\thost %.64s\n", (char*)pkt+28); + fprint(2, "\tvendor %.64s\n", (char*)pkt+92); + break; + + case Tstop: + fprint(2, "\tTstop reason %d\n", pkt[12]); + break; + + case Rstop: + fprint(2, "\tRstop res %d err %d\n", pkt[12], pkt[13]); + break; + + case Techo: + fprint(2, "\tTecho id %.8ux\n", nhgetl(pkt+12)); + break; + + case Recho: + fprint(2, "\tRecho id %.8ux res %d err %d\n", nhgetl(pkt+12), pkt[16], pkt[17]); + break; + + case Tcallout: + fprint(2, "\tTcallout id %d serno %d bps %d-%d\n", + nhgets(pkt+12), nhgets(pkt+14), + nhgetl(pkt+16), nhgetl(pkt+20)); + fprint(2, "\tbearer 0x%x framing 0x%x recvwin %d delay %d\n", + nhgetl(pkt+24), nhgetl(pkt+28), + nhgets(pkt+32), nhgets(pkt+34)); + fprint(2, "\tphone len %d num %.64s\n", + nhgets(pkt+36), (char*)pkt+40); + fprint(2, "\tsubaddr %.64s\n", (char*)pkt+104); + break; + + case Rcallout: + fprint(2, "\tRcallout id %d peerid %d res %d err %d cause %d\n", + nhgets(pkt+12), nhgets(pkt+14), + pkt[16], pkt[17], nhgets(pkt+18)); + fprint(2, "\tconnect %d recvwin %d delay %d chan 0x%.8ux\n", + nhgetl(pkt+20), nhgets(pkt+24), + nhgets(pkt+26), nhgetl(pkt+28)); + break; + + case Tcallreq: + fprint(2, "\tTcallreq id %d serno %d bearer 0x%x id 0x%x\n", + nhgets(pkt+12), nhgets(pkt+14), + nhgetl(pkt+16), nhgetl(pkt+20)); + fprint(2, "\tdialed len %d num %.64s\n", + nhgets(pkt+24), (char*)pkt+28); + fprint(2, "\tdialing len %d num %.64s\n", + nhgets(pkt+26), (char*)pkt+92); + fprint(2, "\tsubaddr %.64s\n", (char*)pkt+156); + break; + + case Rcallreq: + fprint(2, "\tRcallout id %d peerid %d res %d err %d recvwin %d delay %d\n", + nhgets(pkt+12), nhgets(pkt+14), + pkt[16], pkt[17], nhgets(pkt+18), + nhgets(pkt+20)); + break; + + case Acallcon: + fprint(2, "\tAcallcon peerid %d connect %d recvwin %d delay %d framing 0x%x\n", + nhgets(pkt+12), nhgetl(pkt+16), + nhgets(pkt+20), nhgets(pkt+22), + nhgetl(pkt+24)); + break; + + case Tcallclear: + fprint(2, "\tTcallclear callid %d\n", + nhgets(pkt+12)); + break; + + case Acalldis: + fprint(2, "\tAcalldis callid %d res %d err %d cause %d\n", + nhgets(pkt+12), pkt[14], pkt[15], + nhgets(pkt+16)); + fprint(2, "\tstats %.128s\n", (char*)pkt+20); + break; + + case Awaninfo: + fprint(2, "\tAwaninfo peerid %d\n", nhgets(pkt+12)); + fprint(2, "\tcrc errors %d\n", nhgetl(pkt+16)); + fprint(2, "\tframe errors %d\n", nhgetl(pkt+20)); + fprint(2, "\thardware overruns %d\n", nhgetl(pkt+24)); + fprint(2, "\tbuffer overruns %d\n", nhgetl(pkt+28)); + fprint(2, "\ttime-out errors %d\n", nhgetl(pkt+32)); + fprint(2, "\talignment errors %d\n", nhgetl(pkt+36)); + break; + + case Alinkinfo: + fprint(2, "\tAlinkinfo peerid %d sendaccm 0x%ux recvaccm 0x%ux\n", + nhgets(pkt+12), nhgetl(pkt+16), + nhgetl(pkt+20)); + break; + } +} + +void +getaddrs(void) +{ + char buf[128]; + int fd, n; + + sprint(buf, "%s/local", tcpdir); + if((fd = open(buf, OREAD)) < 0) + myfatal("could not open %s: %r", buf); + if((n = read(fd, buf, sizeof(buf))) < 0) + myfatal("could not read %s: %r", buf); + buf[n] = 0; + parseip(localip, buf); + close(fd); + + sprint(buf, "%s/remote", tcpdir); + if((fd = open(buf, OREAD)) < 0) + myfatal("could not open %s: %r", buf); + if((n = read(fd, buf, sizeof(buf))) < 0) + myfatal("could not read %s: %r", buf); + buf[n] = 0; + parseip(remoteip, buf); + close(fd); +} + +void +myfatal(char *fmt, ...) +{ + char sbuf[512]; + va_list arg; + uchar buf[16]; + + memset(buf, 0, sizeof(buf)); + hnputs(buf+0, sizeof(buf)); /* length */ + hnputs(buf+2, 1); /* message type */ + hnputl(buf+4, Magic); /* magic */ + hnputs(buf+8, Tstop); /* op */ + buf[12] = 3; /* local shutdown */ + write(ctlfd, buf, sizeof(buf)); + + va_start(arg, fmt); + vseprint(sbuf, sbuf+sizeof(sbuf), fmt, arg); + va_end(arg); + + fprint(2, "fatal: %s\n", sbuf); + threadexitsall(nil); +} |