summaryrefslogtreecommitdiff
path: root/sys/src/cmd/ip/pptp.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/cmd/ip/pptp.c874
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);
+}