/* * 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 1int pptp(char*);void pushppp(int);void recordack(int);int schedack(int, uchar*, int);void waitacks(void); voidusage(void){ fprint(2, "usage: ip/pptp [-Pd] [-k keyspec] [-x pppnetmntpt] [-w window] server\n"); exits("usage");} voidthreadmain(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);} intcatchalarm(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,}; voidrecho(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);} voidsendecho(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);} voidpptpctlproc(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,}; voidgrereadproc(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);} voidpppreadproc(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);} voidsendack(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");} intschedack(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;} voidgretimeoutproc(void*){ for(;;){ sleep(Tick); now++; nbsendul(tickchan, now); if(now - ctlrcvtime > Servertimeout) myfatal("server timeout"); if(now - ctlechotime > Echointerval) sendecho(); }} voidrecordack(int n){ ack = n;} voidwaitacks(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); }*/} voidtstart(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);} voidtcallout(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)Lines 1–500 of 874. Load more
/* * 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)