]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/pptp.c
ip/dhcpd: handle multiple networks on the same interface, dont get confused by v6...
[plan9front.git] / sys / src / cmd / ip / pptp.c
1 /*
2  * Point-to-point Tunneling Protocol (PPTP)
3  * See RFC 2637, pptpd.c
4  */
5
6 #include <u.h>
7 #include <libc.h>
8 #include <bio.h>
9 #include <ip.h>
10 #include <thread.h>
11
12 int     ack;
13 int     alarmed;
14 int     ctlechotime;
15 int     ctlfd;
16 int     ctlrcvtime;
17 int     debug;
18 int     grefd;
19 uchar localip[IPaddrlen];
20 int     localwin;
21 char    *keyspec;
22 int     now;
23 char    *pppnetmntpt;
24 int     pid;
25 Channel *pidchan;
26 int     pppfd;
27 int     primary;
28 int     rack;
29 Channel *rdchan;
30 int     rdexpect;
31 int     remid;
32 uchar remoteip[IPaddrlen];
33 int     remwin;
34 int     rseq;
35 int     seq;
36 char    tcpdir[40];
37 Channel *tickchan;
38 int     topppfd;
39
40 int     aread(int, int, void*, int);
41 int     catchalarm(void*, char*);
42 void    dumpctlpkt(uchar*);
43 void    getaddrs(void);
44 void    *emalloc(long);
45 void    ewrite(int, void*, int);
46 void    myfatal(char*, ...);
47 #pragma varargck argpos myfatal 1
48 int     pptp(char*);
49 void    pushppp(int);
50 void    recordack(int);
51 int     schedack(int, uchar*, int);
52 void    waitacks(void);
53
54 void
55 usage(void)
56 {
57         fprint(2, "usage: ip/pptp [-Pd] [-k keyspec] [-x pppnetmntpt] [-w window] server\n");
58         exits("usage");
59 }
60
61 void
62 threadmain(int argc, char **argv)
63 {
64         int fd;
65
66         ARGBEGIN{
67         case 'P':
68                 primary = 1;
69                 break;
70         case 'd':
71                 debug++;
72                 break;
73         case 'k':
74                 keyspec = EARGF(usage());
75                 break;
76         case 'w':
77                 localwin = atoi(EARGF(usage()));
78                 break;
79         case 'x':
80                 pppnetmntpt = EARGF(usage());
81                 break;
82         default:
83                 usage();
84         }ARGEND
85
86         if(argc != 1)
87                 usage();
88
89         fmtinstall('E', eipfmt);
90         fmtinstall('I', eipfmt);
91
92         rfork(RFNOTEG);
93         atnotify(catchalarm, 1);
94         fd = pptp(argv[0]);
95         pushppp(fd);
96         exits(nil);
97 }
98
99 int
100 catchalarm(void *a, char *msg)
101 {
102         USED(a);
103
104         if(strstr(msg, "alarm")){
105                 alarmed = 1;
106                 return 1;
107         }
108         if(debug)
109                 fprint(2, "note rcved: %s\n", msg);
110         return 0;
111 }
112
113 enum {
114         Stack   = 8192,
115
116         PptpProto       = 0x0100,
117
118         Magic   = 0x1a2b3c4d,
119         Window  = 16,           /* default window size */
120         Timeout = 60,           /* timeout in seconds for control channel */
121         Pktsize = 2000,         /* maximum packet size */
122         Tick    = 500,          /* tick length in milliseconds */
123         Sendtimeout = 4,        /* in ticks */
124
125         Servertimeout = 5*60*1000/Tick,
126         Echointerval = 60*1000/Tick,
127 };
128
129 enum {
130         Syncframe       = 0x1,
131         Asyncframe      = 0x2,
132         Analog          = 0x1,
133         Digital         = 0x2,
134         Version         = 0x100,
135 };
136
137 enum {
138         Tstart          = 1,
139         Rstart          = 2,
140         Tstop           = 3,
141         Rstop           = 4,
142         Techo           = 5,
143         Recho           = 6,
144         Tcallout        = 7,
145         Rcallout        = 8,
146         Tcallreq        = 9,
147         Rcallreq        = 10,
148         Acallcon        = 11,
149         Tcallclear      = 12,
150         Acalldis        = 13,
151         Awaninfo        = 14,
152         Alinkinfo       = 15,
153 };
154
155 void
156 recho(uchar *in)
157 {
158         uchar out[20];
159
160         if(nhgets(in) < 16)
161                 return;
162
163         memset(out, 0, sizeof out);
164         hnputs(out, sizeof out);
165         hnputs(out+2, 1);
166         hnputl(out+4, Magic);
167         hnputs(out+8, Recho);
168         memmove(out+12, in+12, 4);
169         out[16] = 1;
170
171         ewrite(ctlfd, out, sizeof out);
172 }
173
174 void
175 sendecho(void)
176 {
177         uchar out[16];
178
179         ctlechotime = now;      
180         memset(out, 0, sizeof out);
181         hnputs(out, sizeof out);
182         hnputs(out+2, 1);
183         hnputl(out+4, Magic);
184         hnputs(out+8, Techo);
185
186         ewrite(ctlfd, out, sizeof out);
187 }
188
189 void
190 pptpctlproc(void*)
191 {
192         uchar pkt[1600], *p;
193         int len;
194
195         for(;;){
196                 if(readn(ctlfd, pkt, 2) != 2)
197                         myfatal("pptpread: %r");
198                 len = nhgets(pkt);
199                 if(len < 12 || len+2 >= sizeof pkt)
200                         myfatal("pptpread: bad length %d", len);
201                 if(readn(ctlfd, pkt+2, len-2) != len-2)
202                         myfatal("pptpread: %r");
203                 if(nhgetl(pkt+4) != Magic)
204                         myfatal("pptpread bad magic");
205                 if(nhgets(pkt+2) != 1)
206                         myfatal("pptpread bad message type");
207                 if(debug)
208                         dumpctlpkt(pkt);
209                 ctlrcvtime = now;
210
211                 switch(nhgets(pkt+8)){
212                 case Tstart:
213                 case Tstop:
214                 case Tcallout:
215                 case Tcallreq:
216                 case Tcallclear:
217                 case Acallcon:
218                 case Acalldis:
219                 case Awaninfo:
220                         myfatal("unexpected msg type %d", nhgets(pkt+8));
221                 case Techo:
222                         recho(pkt);
223                         break;
224                 case Recho:
225                         break;
226                 case Rstart:
227                 case Rstop:
228                 case Rcallout:
229                 case Rcallreq:
230                         if(rdexpect != nhgets(pkt+8))
231                                 continue;
232                         p = emalloc(len);
233                         memmove(p, pkt, len);
234                         sendp(rdchan, p);
235                         break;
236                 case Alinkinfo:
237                         myfatal("cannot change ppp params on the fly");
238                 }
239         }
240 }
241
242 enum {
243         Seqnum = 0x1000,
244         Acknum = 0x0080,
245
246         GrePPP = 0x880B,
247 };
248
249 void
250 grereadproc(void*)
251 {
252         int datoff, flags, len, n, pass;
253         uchar pkt[1600];
254         uchar src[IPaddrlen], dst[IPaddrlen];
255
256         rfork(RFFDG);
257         close(pppfd);
258         sendul(pidchan, getpid());
259
260         while((n = read(grefd, pkt, sizeof pkt)) > 0){
261                 if(n == sizeof pkt)
262                         myfatal("gre pkt buffer too small");
263                 if(n < 16){
264                         if(debug)
265                                 fprint(2, "small pkt len %d ignored\n", n);
266                         continue;
267                 }
268                 v4tov6(src, pkt);
269                 v4tov6(dst, pkt+4);
270                 if(ipcmp(src, remoteip) != 0 || ipcmp(dst, localip) != 0)
271                         myfatal("%I: gre read bad address src=%I dst=%I",
272                                 remoteip, src, dst);
273                 if(nhgets(pkt+10) != GrePPP)
274                         myfatal("%I: gre read bad protocol 0x%x",
275                                 remoteip, nhgets(pkt+10));
276
277                 flags = nhgets(pkt+8);
278                 if((flags&0xEF7F) != 0x2001){
279                         if(debug)
280                                 fprint(2, "bad flags in gre hdr 0x%x\n", flags);
281                         continue;
282                 }
283                 datoff = 8+8;
284                 pass = 0;
285                 len = nhgets(pkt+8+4);
286                 if(len > n-datoff){
287                         fprint(2, "bad payload length %d > %d\n",
288                                 len, n-datoff);
289                         continue;
290                 }
291                 if(flags&Seqnum)
292                         datoff += 4;
293                 if(flags&Acknum){
294                         recordack(nhgetl(pkt+datoff));
295                         datoff += 4;
296                 }
297                 if(flags&Seqnum)
298                         pass = schedack(nhgetl(pkt+8+8), pkt+datoff, len);
299                 if(debug)
300                         fprint(2, "got gre callid %d len %d flag 0x%x pass %d seq %d rseq %d\n", nhgets(pkt+8+6),
301                                 len, flags, pass, nhgetl(pkt+8+8), rseq);
302         }
303         threadexits(nil);
304 }
305
306 void
307 pppreadproc(void*)
308 {
309         int n, myrseq;
310         uchar pkt[1600];
311         enum {
312                 Hdr = 8+16,
313         };
314
315         rfork(RFFDG);
316         close(pppfd);
317         sendul(pidchan, getpid());
318
319         while((n = read(topppfd, pkt+Hdr, sizeof pkt-Hdr)) > 0){
320                 if(n == sizeof pkt-Hdr)
321                         myfatal("ppp pkt buffer too small");
322                 v6tov4(pkt+0, localip);
323                 v6tov4(pkt+4, remoteip);
324                 hnputs(pkt+8, 0x2001 | Seqnum | Acknum);
325                 hnputs(pkt+10, GrePPP);
326                 hnputs(pkt+12, n);
327                 hnputs(pkt+14, remid);
328                 hnputl(pkt+16, ++seq);
329                 myrseq = rseq;
330                 hnputl(pkt+20, myrseq);
331                 rack = myrseq;
332                 if(debug)
333                         fprint(2, "wrote gre callid %d len %d flag 0x%x seq %d rseq %d\n", nhgets(pkt+8+6),
334                                 n, nhgets(pkt+8), nhgetl(pkt+16), nhgetl(pkt+20));
335                 if(write(grefd, pkt, n+Hdr) != n+Hdr)
336                         myfatal("gre write: %r");
337                 waitacks();
338         }
339         threadexits(nil);
340 }
341
342 void
343 sendack(void)
344 {
345         int myrseq;
346         uchar pkt[20];
347
348         v6tov4(pkt+0, localip);
349         v6tov4(pkt+4, remoteip);
350         hnputs(pkt+8, 0x2001 | Acknum);
351         hnputs(pkt+10, GrePPP);
352         hnputs(pkt+12, 0);
353         hnputs(pkt+14, remid);
354         myrseq = rseq;
355         rack = myrseq;
356         hnputs(pkt+16, myrseq);
357
358         if(write(grefd, pkt, sizeof pkt) != sizeof pkt)
359                 myfatal("gre write: %r");
360 }
361
362 int
363 schedack(int n, uchar *dat, int len)
364 {
365         static uchar sdat[1600];
366         static int srseq, slen;
367
368         if(n-rseq <= 0){
369                 fprint(2, "skipping pkt %d len %d, have %d\n", n, len, rseq);
370                 return 0;
371         }
372
373         /* missed one pkt, maybe a swap happened, save pkt */
374         if(n==rseq+2){
375                 memmove(sdat, dat, len);
376                 slen = len;
377                 srseq = n;
378                 return 0;
379         }
380
381         if(n-rseq > 1){
382                 if(slen && srseq == n-1){       
383                         fprint(2, "reswapped pkts %d and %d\n", srseq, n);
384                         write(topppfd, sdat, slen);
385                         slen = 0;
386                 }else
387                         fprint(2, "missed pkts %d-%d, got %d len %d\n", rseq+1, n-1, n, len);
388         }
389         write(topppfd, dat, len);
390         rseq = n;
391
392         /* send ack if we haven't recently */
393         if((int)(rseq-rack) > (localwin>>1))
394                 sendack();
395
396         return 1;
397 }
398
399 void
400 gretimeoutproc(void*)
401 {
402         for(;;){
403                 sleep(Tick);
404                 now++;
405                 nbsendul(tickchan, now);
406                 if(now - ctlrcvtime > Servertimeout)
407                         myfatal("server timeout");
408                 if(now - ctlechotime > Echointerval)
409                         sendecho();
410         }
411 }
412
413 void
414 recordack(int n)
415 {
416         ack = n;
417 }
418
419 void
420 waitacks(void)
421 {
422 /*
423         int start;
424
425         start = now;
426         while(seq-ack > remwin && now-start < Sendtimeout){
427                 print("seq %d ack %d remwin %d now %d start %d\n",
428                         seq, ack, remwin, now, start);
429                 recvul(tickchan);
430         }
431 */
432 }
433
434 void
435 tstart(void)
436 {
437         char *name;
438         uchar pkt[200], *rpkt;
439
440         memset(pkt, 0, sizeof pkt);
441
442         hnputs(pkt+0, 156);
443         hnputs(pkt+2, 1);
444         hnputl(pkt+4, Magic);
445         hnputs(pkt+8, Tstart);
446         hnputs(pkt+12, PptpProto);
447         hnputl(pkt+16, 1);
448         hnputl(pkt+20, 1);
449         hnputs(pkt+24, 1);
450         name = sysname();
451         if(name == nil)
452                 name = "gnot";
453         strcpy((char*)pkt+28, name);
454         strcpy((char*)pkt+92, "plan 9");
455
456         if(debug)
457                 dumpctlpkt(pkt);
458
459         rdexpect = Rstart;
460         ewrite(ctlfd, pkt, 156);
461
462         rpkt = recvp(rdchan);
463         if(rpkt == nil)
464                 myfatal("recvp: %r");
465         if(nhgets(rpkt) != 156)
466                 myfatal("Rstart wrong length %d != 156", nhgets(rpkt));
467         if(rpkt[14] != 1)
468                 myfatal("Rstart error %d", rpkt[15]);
469         free(rpkt);
470 }
471
472 void
473 tcallout(void)
474 {
475         uchar pkt[200], *rpkt;
476
477         pid = getpid();
478
479         memset(pkt, 0, sizeof pkt);
480         hnputs(pkt+0, 168);
481         hnputs(pkt+2, 1);
482         hnputl(pkt+4, Magic);
483         hnputs(pkt+8, Tcallout);
484
485         hnputl(pkt+16, 56000);
486         hnputl(pkt+20, 768000);
487         hnputl(pkt+24, 3);
488         hnputl(pkt+28, 3);
489         if(localwin == 0)
490                 localwin = Window;
491         hnputs(pkt+32, localwin);
492
493         if(debug)
494                 dumpctlpkt(pkt);
495
496         rdexpect = Rcallout;
497         ewrite(ctlfd, pkt, 168);
498
499         rpkt = recvp(rdchan);
500         if(rpkt == nil)
501                 myfatal("recvp: %r");
502         if(nhgets(rpkt) != 32)
503                 myfatal("Rcallreq wrong length %d != 32", nhgets(rpkt));
504         if(rpkt[16] != 1)
505                 myfatal("Rcallreq error %d", rpkt[17]);
506         remid = nhgets(pkt+12);
507         remwin = nhgets(pkt+24);
508         free(rpkt);
509 }
510
511 /*
512 void
513 tcallreq(void)
514 {
515         uchar pkt[200], *rpkt;
516
517         pid = getpid();
518
519         memset(pkt, 0, sizeof pkt);
520         hnputs(pkt+0, 220);
521         hnputs(pkt+2, 1);
522         hnputl(pkt+4, Magic);
523         hnputs(pkt+8, Tcallreq);
524
525         if(debug)
526                 dumpctlpkt(pkt);
527
528         rdexpect = Rcallreq;
529         ewrite(ctlfd, pkt, 220);
530
531         rpkt = recvp(rdchan);
532         if(rpkt == nil)
533                 myfatal("recvp: %r");
534         if(nhgets(rpkt) != 24)
535                 myfatal("Rcallreq wrong length %d != 24", nhgets(rpkt));
536         if(rpkt[16] != 1)
537                 myfatal("Rcallreq error %d", rpkt[17]);
538         remid = nhgets(pkt+12);
539         remwin = nhgets(pkt+18);
540         free(rpkt);
541 }
542
543 void
544 acallcon(void)
545 {
546         uchar pkt[200];
547
548         memset(pkt, 0, sizeof pkt);
549         hnputs(pkt+0, 28);
550         hnputs(pkt+2, 1);
551         hnputl(pkt+4, Magic);
552         hnputs(pkt+8, Acallcon);
553         hnputs(pkt+12, remid);
554         if(localwin == 0)
555                 localwin = Window;
556         hnputs(pkt+20, localwin);
557         hnputl(pkt+24, 1);
558
559         if(debug)
560                 dumpctlpkt(pkt);
561
562         ewrite(ctlfd, pkt, 28);
563 }
564 */
565
566 int
567 pptp(char *addr)
568 {
569         int p[2];
570         char greaddr[128];
571
572         addr = netmkaddr(addr, "net", "pptp");
573         ctlfd = dial(addr, nil, tcpdir, nil);
574         if(ctlfd < 0)
575                 myfatal("dial %s: %r", addr);
576         getaddrs();
577
578         rdchan = chancreate(sizeof(void*), 0);
579         proccreate(pptpctlproc, nil, Stack);
580
581         tstart();
582         tcallout();
583
584         if(pipe(p) < 0)
585                 myfatal("pipe: %r");
586
587         pppfd = p[0];
588         topppfd = p[1];
589
590         strcpy(greaddr, tcpdir);
591         *strrchr(greaddr, '/') = '\0';
592         sprint(strrchr(greaddr, '/')+1, "gre!%I!%d", remoteip, GrePPP);
593
594         print("local %I remote %I gre %s remid %d remwin %d\n",
595                 localip, remoteip, greaddr, remid, remwin);
596
597         grefd = dial(greaddr, nil, nil, nil);
598         if(grefd < 0)
599                 myfatal("dial gre: %r");
600
601         tickchan = chancreate(sizeof(int), 0);
602         proccreate(gretimeoutproc, nil, Stack);
603
604         pidchan = chancreate(sizeof(int), 0);
605         proccreate(grereadproc, nil, Stack);
606         recvul(pidchan);
607         proccreate(pppreadproc, nil, Stack);
608         recvul(pidchan);
609
610         close(topppfd);
611         return pppfd;
612 }
613         
614 void
615 pushppp(int fd)
616 {
617         char *argv[16];
618         int argc;
619
620         argc = 0;
621         argv[argc++] = "/bin/ip/ppp";
622         argv[argc++] = "-C";
623         argv[argc++] = "-m1450";
624         if(debug)
625                 argv[argc++] = "-d";
626         if(primary)
627                 argv[argc++] = "-P";
628         if(pppnetmntpt){
629                 argv[argc++] = "-x";
630                 argv[argc++] = pppnetmntpt;
631         }
632         if(keyspec){
633                 argv[argc++] = "-k";
634                 argv[argc++] = keyspec;
635         }
636         argv[argc] = nil;
637
638         switch(fork()){
639         case -1:
640                 myfatal("fork: %r");
641         default:
642                 return;
643         case 0:
644                 dup(fd, 0);
645                 dup(fd, 1);
646                 exec(argv[0], argv);
647                 myfatal("exec: %r");
648         }
649 }
650
651 int
652 aread(int timeout, int fd, void *buf, int nbuf)
653 {
654         int n;
655
656         alarmed = 0;
657         alarm(timeout);
658         n = read(fd, buf, nbuf);
659         alarm(0);
660         if(alarmed)
661                 return -1;
662         if(n < 0)
663                 myfatal("read: %r");
664         if(n == 0)
665                 myfatal("short read");
666         return n;
667 }
668
669 void
670 ewrite(int fd, void *buf, int nbuf)
671 {
672         char e[ERRMAX], path[64];
673
674         if(write(fd, buf, nbuf) != nbuf){
675                 rerrstr(e, sizeof e);
676                 strcpy(path, "unknown");
677                 fd2path(fd, path, sizeof path);
678                 myfatal("write %d to %s: %s", nbuf, path, e);
679         }
680 }
681
682 void*
683 emalloc(long n)
684 {
685         void *v;
686
687         v = malloc(n);
688         if(v == nil)
689                 myfatal("out of memory");
690         return v;
691 }
692
693 int
694 thread(void(*f)(void*), void *a)
695 {
696         int pid;
697         pid=rfork(RFNOWAIT|RFMEM|RFPROC);
698         if(pid < 0)
699                 myfatal("rfork: %r");
700         if(pid != 0)
701                 return pid;
702         (*f)(a);
703         _exits(nil);
704         return 0; // never reaches here
705 }
706
707 void
708 dumpctlpkt(uchar *pkt)
709 {
710         fprint(2, "pkt len %d mtype %d cookie 0x%.8ux type %d\n",
711                 nhgets(pkt), nhgets(pkt+2),
712                 nhgetl(pkt+4), nhgets(pkt+8));
713
714         switch(nhgets(pkt+8)){
715         default:
716                 fprint(2, "\tunknown type\n");
717                 break;
718         case Tstart:
719                 fprint(2, "\tTstart proto %d framing %d bearer %d maxchan %d firmware %d\n",
720                         nhgets(pkt+12), nhgetl(pkt+16),
721                         nhgetl(pkt+20), nhgets(pkt+24),
722                         nhgets(pkt+26));
723                 fprint(2, "\thost %.64s\n", (char*)pkt+28);
724                 fprint(2, "\tvendor %.64s\n", (char*)pkt+92);
725                 break;
726         case Rstart:
727                 fprint(2, "\tRstart proto %d res %d err %d framing %d bearer %d maxchan %d firmware %d\n",
728                         nhgets(pkt+12), pkt[14], pkt[15],
729                         nhgetl(pkt+16),
730                         nhgetl(pkt+20), nhgets(pkt+24),
731                         nhgets(pkt+26));
732                 fprint(2, "\thost %.64s\n", (char*)pkt+28);
733                 fprint(2, "\tvendor %.64s\n", (char*)pkt+92);
734                 break;
735
736         case Tstop:
737                 fprint(2, "\tTstop reason %d\n", pkt[12]);
738                 break;
739
740         case Rstop:
741                 fprint(2, "\tRstop res %d err %d\n", pkt[12], pkt[13]);
742                 break;
743
744         case Techo:
745                 fprint(2, "\tTecho id %.8ux\n", nhgetl(pkt+12));
746                 break;
747
748         case Recho:
749                 fprint(2, "\tRecho id %.8ux res %d err %d\n", nhgetl(pkt+12), pkt[16], pkt[17]);
750                 break;
751
752         case Tcallout:
753                 fprint(2, "\tTcallout id %d serno %d bps %d-%d\n",
754                         nhgets(pkt+12), nhgets(pkt+14),
755                         nhgetl(pkt+16), nhgetl(pkt+20));
756                 fprint(2, "\tbearer 0x%x framing 0x%x recvwin %d delay %d\n",
757                         nhgetl(pkt+24), nhgetl(pkt+28),
758                         nhgets(pkt+32), nhgets(pkt+34));
759                 fprint(2, "\tphone len %d num %.64s\n", 
760                         nhgets(pkt+36), (char*)pkt+40);
761                 fprint(2, "\tsubaddr %.64s\n", (char*)pkt+104);
762                 break;
763
764         case Rcallout:
765                 fprint(2, "\tRcallout id %d peerid %d res %d err %d cause %d\n",
766                         nhgets(pkt+12), nhgets(pkt+14),
767                         pkt[16], pkt[17], nhgets(pkt+18));
768                 fprint(2, "\tconnect %d recvwin %d delay %d chan 0x%.8ux\n",
769                         nhgetl(pkt+20), nhgets(pkt+24),
770                         nhgets(pkt+26), nhgetl(pkt+28));
771                 break;
772
773         case Tcallreq:
774                 fprint(2, "\tTcallreq id %d serno %d bearer 0x%x id 0x%x\n",
775                         nhgets(pkt+12), nhgets(pkt+14),
776                         nhgetl(pkt+16), nhgetl(pkt+20));
777                 fprint(2, "\tdialed len %d num %.64s\n",
778                         nhgets(pkt+24), (char*)pkt+28);
779                 fprint(2, "\tdialing len %d num %.64s\n",
780                         nhgets(pkt+26), (char*)pkt+92);
781                 fprint(2, "\tsubaddr %.64s\n", (char*)pkt+156);
782                 break;
783
784         case Rcallreq:
785                 fprint(2, "\tRcallout id %d peerid %d res %d err %d recvwin %d delay %d\n",
786                         nhgets(pkt+12), nhgets(pkt+14),
787                         pkt[16], pkt[17], nhgets(pkt+18),
788                         nhgets(pkt+20));
789                 break;
790
791         case Acallcon:
792                 fprint(2, "\tAcallcon peerid %d connect %d recvwin %d delay %d framing 0x%x\n",
793                         nhgets(pkt+12), nhgetl(pkt+16),
794                         nhgets(pkt+20), nhgets(pkt+22),
795                         nhgetl(pkt+24));
796                 break;
797
798         case Tcallclear:
799                 fprint(2, "\tTcallclear callid %d\n",
800                         nhgets(pkt+12));
801                 break;
802
803         case Acalldis:
804                 fprint(2, "\tAcalldis callid %d res %d err %d cause %d\n",
805                         nhgets(pkt+12), pkt[14], pkt[15],
806                         nhgets(pkt+16));
807                 fprint(2, "\tstats %.128s\n", (char*)pkt+20);
808                 break;
809
810         case Awaninfo:
811                 fprint(2, "\tAwaninfo peerid %d\n", nhgets(pkt+12));
812                 fprint(2, "\tcrc errors %d\n", nhgetl(pkt+16));
813                 fprint(2, "\tframe errors %d\n", nhgetl(pkt+20));
814                 fprint(2, "\thardware overruns %d\n", nhgetl(pkt+24));
815                 fprint(2, "\tbuffer overruns %d\n", nhgetl(pkt+28));
816                 fprint(2, "\ttime-out errors %d\n", nhgetl(pkt+32));
817                 fprint(2, "\talignment errors %d\n", nhgetl(pkt+36));
818                 break;
819
820         case Alinkinfo:
821                 fprint(2, "\tAlinkinfo peerid %d sendaccm 0x%ux recvaccm 0x%ux\n",
822                         nhgets(pkt+12), nhgetl(pkt+16),
823                         nhgetl(pkt+20));
824                 break;
825         }
826 }
827
828 void
829 getaddrs(void)
830 {
831         char buf[128];
832         int fd, n;
833
834         sprint(buf, "%s/local", tcpdir);
835         if((fd = open(buf, OREAD)) < 0)
836                 myfatal("could not open %s: %r", buf);
837         if((n = read(fd, buf, sizeof(buf))) < 0)
838                 myfatal("could not read %s: %r", buf);
839         buf[n] = 0;
840         parseip(localip, buf);
841         close(fd);
842
843         sprint(buf, "%s/remote", tcpdir);
844         if((fd = open(buf, OREAD)) < 0)
845                 myfatal("could not open %s: %r", buf);
846         if((n = read(fd, buf, sizeof(buf))) < 0)
847                 myfatal("could not read %s: %r", buf);
848         buf[n] = 0;
849         parseip(remoteip, buf);
850         close(fd);
851 }
852
853 void
854 myfatal(char *fmt, ...)
855 {
856         char sbuf[512];
857         va_list arg;
858         uchar buf[16];
859
860         memset(buf, 0, sizeof(buf));
861         hnputs(buf+0, sizeof(buf));     /* length */
862         hnputs(buf+2, 1);               /* message type */
863         hnputl(buf+4, Magic);           /* magic */
864         hnputs(buf+8, Tstop);           /* op */
865         buf[12] = 3;                    /* local shutdown */
866         write(ctlfd, buf, sizeof(buf));
867
868         va_start(arg, fmt);
869         vseprint(sbuf, sbuf+sizeof(sbuf), fmt, arg);
870         va_end(arg);
871
872         fprint(2, "fatal: %s\n", sbuf);
873         threadexitsall(nil);
874 }