1 /*
2 * User-level PPP over Ethernet (PPPoE) client.
3 * See RFC 2516
4 */
5
6 #include <u.h>
7 #include <libc.h>
8 #include <ip.h>
9
10 void dumppkt(uchar*);
11 uchar *findtag(uchar*, int, int*, int);
12 void hexdump(uchar*, int);
13 int malformed(uchar*, int, int);
14 int pppoe(char*);
15 void execppp(int);
16
17 int alarmed;
18 int debug;
19 int sessid;
20 char *keyspec;
21 int primary;
22 char *pppnetmtpt;
23 char *acname;
24 char *pppname = "/bin/ip/ppp";
25 char *srvname = "";
26 char *wantac;
27 uchar *cookie;
28 int cookielen;
29 uchar etherdst[6];
30 int mtu = 1492;
31
32 void
usage(void)33 usage(void)
34 {
35 fprint(2, "usage: pppoe [-Pd] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-x pppnet] [ether0]\n");
36 exits("usage");
37 }
38
39 int
catchalarm(void * a,char * msg)40 catchalarm(void *a, char *msg)
41 {
42 USED(a);
43
44 if(strstr(msg, "alarm")){
45 alarmed = 1;
46 return 1;
47 }
48 if(debug)
49 fprint(2, "note rcved: %s\n", msg);
50 return 0;
51 }
52
53 void
main(int argc,char ** argv)54 main(int argc, char **argv)
55 {
56 int fd;
57 char *dev;
58
59 ARGBEGIN{
60 case 'A':
61 wantac = EARGF(usage());
62 break;
63 case 'P':
64 primary = 1;
65 break;
66 case 'S':
67 srvname = EARGF(usage());
68 break;
69 case 'd':
70 debug++;
71 break;
72 case 'm':
73 mtu = atoi(EARGF(usage()));
74 break;
75 case 'k':
76 keyspec = EARGF(usage());
77 break;
78 case 'x':
79 pppnetmtpt = EARGF(usage());
80 break;
81 default:
82 usage();
83 }ARGEND
84
85 switch(argc){
86 default:
87 usage();
88 case 0:
89 dev = "ether0";
90 break;
91 case 1:
92 dev = argv[0];
93 break;
94 }
95
96 fmtinstall('E', eipfmt);
97
98 atnotify(catchalarm, 1);
99 fd = pppoe(dev);
100 execppp(fd);
101 }
102
103 typedef struct Etherhdr Etherhdr;
104 struct Etherhdr {
105 uchar dst[6];
106 uchar src[6];
107 uchar type[2];
108 };
109
110 enum {
111 EtherHdrSz = 6+6+2,
112 EtherMintu = 60,
113
114 EtherPppoeDiscovery = 0x8863,
115 EtherPppoeSession = 0x8864,
116 };
117
118 uchar etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
119
120 int
etherhdr(uchar * pkt,uchar * dst,int type)121 etherhdr(uchar *pkt, uchar *dst, int type)
122 {
123 Etherhdr *eh;
124
125 eh = (Etherhdr*)pkt;
126 memmove(eh->dst, dst, sizeof(eh->dst));
127 hnputs(eh->type, type);
128 return EtherHdrSz;
129 }
130
131 typedef struct Pppoehdr Pppoehdr;
132 struct Pppoehdr {
133 uchar verstype;
134 uchar code;
135 uchar sessid[2];
136 uchar length[2]; /* of payload */
137 };
138
139 enum {
140 PppoeHdrSz = 1+1+2+2,
141 Hdr = EtherHdrSz+PppoeHdrSz,
142 };
143
144 enum {
145 VersType = 0x11,
146
147 /* Discovery codes */
148 CodeDiscInit = 0x09, /* discovery init */
149 CodeDiscOffer = 0x07, /* discovery offer */
150 CodeDiscReq = 0x19, /* discovery request */
151 CodeDiscSess = 0x65, /* session confirmation */
152
153 /* Session codes */
154 CodeSession = 0x00,
155 };
156
157 int
pppoehdr(uchar * pkt,int code,int sessid)158 pppoehdr(uchar *pkt, int code, int sessid)
159 {
160 Pppoehdr *ph;
161
162 ph = (Pppoehdr*)pkt;
163 ph->verstype = VersType;
164 ph->code = code;
165 hnputs(ph->sessid, sessid);
166 return PppoeHdrSz;
167 }
168
169 typedef struct Taghdr Taghdr;
170 struct Taghdr {
171 uchar type[2];
172 uchar length[2]; /* of value */
173 };
174
175 enum {
176 TagEnd = 0x0000, /* end of tag list */
177 TagSrvName = 0x0101, /* service name */
178 TagAcName = 0x0102, /* access concentrator name */
179 TagHostUniq = 0x0103, /* nonce */
180 TagAcCookie = 0x0104, /* a.c. cookie */
181 TagVendSpec = 0x0105, /* vendor specific */
182 TagRelaySessId = 0x0110, /* relay session id */
183 TagSrvNameErr = 0x0201, /* service name error (ascii) */
184 TagAcSysErr = 0x0202, /* a.c. system error */
185 };
186
187 int
tag(uchar * pkt,int type,void * value,int nvalue)188 tag(uchar *pkt, int type, void *value, int nvalue)
189 {
190 Taghdr *h;
191
192 h = (Taghdr*)pkt;
193 hnputs(h->type, type);
194 hnputs(h->length, nvalue);
195 memmove(pkt+4, value, nvalue);
196 return 4+nvalue;
197 }
198
199 /* PPPoE Active Discovery Initiation */
200 int
padi(uchar * pkt)201 padi(uchar *pkt)
202 {
203 int sz, tagoff;
204 uchar *length;
205
206 sz = 0;
207 sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery);
208 sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000);
209 length = pkt+sz-2;
210 tagoff = sz;
211 sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
212 hnputs(length, sz-tagoff);
213 return sz;
214 }
215
216 /* PPPoE Active Discovery Request */
217 int
padr(uchar * pkt)218 padr(uchar *pkt)
219 {
220 int sz, tagoff;
221 uchar *length;
222
223 sz = 0;
224 sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery);
225 sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000);
226 length = pkt+sz-2;
227 tagoff = sz;
228 sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
229 sz += tag(pkt+sz, TagAcName, acname, strlen(acname));
230 if(cookie)
231 sz += tag(pkt+sz, TagAcCookie, cookie, cookielen);
232 hnputs(length, sz-tagoff);
233 return sz;
234 }
235
236 void
ewrite(int fd,void * buf,int nbuf)237 ewrite(int fd, void *buf, int nbuf)
238 {
239 char e[ERRMAX], path[64];
240
241 if(write(fd, buf, nbuf) != nbuf){
242 rerrstr(e, sizeof e);
243 strcpy(path, "unknown");
244 fd2path(fd, path, sizeof path);
245 sysfatal("write %d to %s: %s", nbuf, path, e);
246 }
247 }
248
249 void*
emalloc(long n)250 emalloc(long n)
251 {
252 void *v;
253
254 v = malloc(n);
255 if(v == nil)
256 sysfatal("out of memory");
257 return v;
258 }
259
260 int
aread(int timeout,int fd,void * buf,int nbuf)261 aread(int timeout, int fd, void *buf, int nbuf)
262 {
263 int n;
264
265 alarmed = 0;
266 alarm(timeout);
267 n = read(fd, buf, nbuf);
268 alarm(0);
269 if(alarmed)
270 return -1;
271 if(n < 0)
272 sysfatal("read: %r");
273 if(n == 0)
274 sysfatal("short read");
275 return n;
276 }
277
278 int
pktread(int timeout,int fd,void * buf,int nbuf,int (* want)(uchar *))279 pktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uchar*))
280 {
281 int n, t2;
282 n = -1;
283 for(t2=timeout; t2<16000; t2*=2){
284 while((n = aread(t2, fd, buf, nbuf)) > 0){
285 if(malformed(buf, n, EtherPppoeDiscovery)){
286 if(debug)
287 fprint(2, "dropping pkt: %r\n");
288 continue;
289 }
290 if(debug)
291 dumppkt(buf);
292 if(!want(buf)){
293 if(debug)
294 fprint(2, "dropping unwanted pkt: %r\n");
295 continue;
296 }
297 break;
298 }
299 if(n > 0)
300 break;
301 }
302 return n;
303 }
304
305 int
bad(char * reason)306 bad(char *reason)
307 {
308 werrstr(reason);
309 return 0;
310 }
311
312 void*
copy(uchar * s,int len)313 copy(uchar *s, int len)
314 {
315 uchar *v;
316
317 v = emalloc(len+1);
318 memmove(v, s, len);
319 v[len] = '\0';
320 return v;
321 }
322
323 void
clearstate(void)324 clearstate(void)
325 {
326 sessid = -1;
327 free(acname);
328 acname = nil;
329 free(cookie);
330 cookie = nil;
331 }
332
333 int
wantoffer(uchar * pkt)334 wantoffer(uchar *pkt)
335 {
336 int i, len;
337 uchar *s;
338 Etherhdr *eh;
339 Pppoehdr *ph;
340
341 eh = (Etherhdr*)pkt;
342 ph = (Pppoehdr*)(pkt+EtherHdrSz);
343
344 if(ph->code != CodeDiscOffer)
345 return bad("not an offer");
346 if(nhgets(ph->sessid) != 0x0000)
347 return bad("bad session id");
348
349 for(i=0;; i++){
350 if((s = findtag(pkt, TagSrvName, &len, i)) == nil)
351 return bad("no matching service name");
352 if(len == strlen(srvname) && memcmp(s, srvname, len) == 0)
353 break;
354 }
355
356 if((s = findtag(pkt, TagAcName, &len, 0)) == nil)
357 return bad("no ac name");
358 acname = copy(s, len);
359 if(wantac && strcmp(acname, wantac) != 0){
360 free(acname);
361 return bad("wrong ac name");
362 }
363
364 if(s = findtag(pkt, TagAcCookie, &len, 0)){
365 cookie = copy(s, len);
366 cookielen = len;
367 }
368 memmove(etherdst, eh->src, sizeof etherdst);
369 return 1;
370 }
371
372 int
wantsession(uchar * pkt)373 wantsession(uchar *pkt)
374 {
375 int len;
376 uchar *s;
377 Pppoehdr *ph;
378
379 ph = (Pppoehdr*)(pkt+EtherHdrSz);
380
381 if(ph->code != CodeDiscSess)
382 return bad("not a session confirmation");
383 if(nhgets(ph->sessid) == 0x0000)
384 return bad("bad session id");
385 if(findtag(pkt, TagSrvName, &len, 0) == nil)
386 return bad("no service name");
387 if(findtag(pkt, TagSrvNameErr, &len, 0))
388 return bad("service name error");
389 if(findtag(pkt, TagAcSysErr, &len, 0))
390 return bad("ac system error");
391
392 /*
393 * rsc said: ``if there is no -S option given, the current code
394 * waits for an offer with service name == "".
395 * that's silly. it should take the first one it gets.''
396 */
397 if(srvname[0] != '\0') {
398 if((s = findtag(pkt, TagSrvName, &len, 0)) == nil)
399 return bad("no matching service name");
400 if(len != strlen(srvname) || memcmp(s, srvname, len) != 0)
401 return bad("no matching service name");
402 }
403 sessid = nhgets(ph->sessid);
404 return 1;
405 }
406
407 int
pppoe(char * ether)408 pppoe(char *ether)
409 {
410 char buf[64];
411 uchar pkt[1520];
412 int dfd, p[2], n, sfd, sz, timeout;
413 Pppoehdr *ph;
414
415 ph = (Pppoehdr*)(pkt+EtherHdrSz);
416 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery);
417 if((dfd = dial(buf, nil, nil, nil)) < 0)
418 sysfatal("dial %s: %r", buf);
419
420 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession);
421 if((sfd = dial(buf, nil, nil, nil)) < 0)
422 sysfatal("dial %s: %r", buf);
423
424 for(timeout=250; timeout<16000; timeout*=2){
425 clearstate();
426 memset(pkt, 0, sizeof pkt);
427 sz = padi(pkt);
428 if(debug)
429 dumppkt(pkt);
430 if(sz < EtherMintu)
431 sz = EtherMintu;
432 ewrite(dfd, pkt, sz);
433
434 if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0)
435 continue;
436
437 memset(pkt, 0, sizeof pkt);
438 sz = padr(pkt);
439 if(debug)
440 dumppkt(pkt);
441 if(sz < EtherMintu)
442 sz = EtherMintu;
443 ewrite(dfd, pkt, sz);
444
445 if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0)
446 continue;
447
448 break;
449 }
450 if(sessid < 0)
451 sysfatal("could not establish session");
452
453 rfork(RFNOTEG);
454 if(pipe(p) < 0)
455 sysfatal("pipe: %r");
456
457 switch(fork()){
458 case -1:
459 sysfatal("fork: %r");
460 default:
461 break;
462 case 0:
463 close(p[1]);
464 while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){
465 etherhdr(pkt, etherdst, EtherPppoeSession);
466 pppoehdr(pkt+EtherHdrSz, 0x00, sessid);
467 hnputs(pkt+Hdr-2, n);
468 sz = Hdr+n;
469 if(debug > 1){
470 dumppkt(pkt);
471 hexdump(pkt, sz);
472 }
473 if(sz < EtherMintu)
474 sz = EtherMintu;
475 if(write(sfd, pkt, sz) < 0){
476 if(debug)
477 fprint(2, "write to ether failed: %r");
478 _exits(nil);
479 }
480 }
481 _exits(nil);
482 }
483
484 switch(fork()){
485 case -1:
486 sysfatal("fork: %r");
487 default:
488 break;
489 case 0:
490 close(p[1]);
491 while((n = read(sfd, pkt, sizeof pkt)) > 0){
492 if(malformed(pkt, n, EtherPppoeSession)
493 || ph->code != 0x00 || nhgets(ph->sessid) != sessid){
494 if(debug)
495 fprint(2, "malformed session pkt: %r\n");
496 if(debug)
497 dumppkt(pkt);
498 continue;
499 }
500 if(write(p[0], pkt+Hdr, nhgets(ph->length)) < 0){
501 if(debug)
502 fprint(2, "write to ppp failed: %r\n");
503 _exits(nil);
504 }
505 }
506 _exits(nil);
507 }
508 close(p[0]);
509 return p[1];
510 }
511
512 void
execppp(int fd)513 execppp(int fd)
514 {
515 char *argv[16];
516 int argc;
517 char smtu[10];
518
519 argc = 0;
520 argv[argc++] = pppname;
521 snprint(smtu, sizeof(smtu), "-m%d", mtu);
522 argv[argc++] = smtu;
523 argv[argc++] = "-F";
524 if(debug)
525 argv[argc++] = "-d";
526 if(primary)
527 argv[argc++] = "-P";
528 if(pppnetmtpt){
529 argv[argc++] = "-x";
530 argv[argc++] = pppnetmtpt;
531 }
532 if(keyspec){
533 argv[argc++] = "-k";
534 argv[argc++] = keyspec;
535 }
536 argv[argc] = nil;
537
538 dup(fd, 0);
539 dup(fd, 1);
540 exec(pppname, argv);
541 sysfatal("exec: %r");
542 }
543
544 uchar*
findtag(uchar * pkt,int tagtype,int * plen,int skip)545 findtag(uchar *pkt, int tagtype, int *plen, int skip)
546 {
547 int len, sz, totlen;
548 uchar *tagdat, *v;
549 Etherhdr *eh;
550 Pppoehdr *ph;
551 Taghdr *t;
552
553 eh = (Etherhdr*)pkt;
554 ph = (Pppoehdr*)(pkt+EtherHdrSz);
555 tagdat = pkt+Hdr;
556
557 if(nhgets(eh->type) != EtherPppoeDiscovery)
558 return nil;
559 totlen = nhgets(ph->length);
560
561 sz = 0;
562 while(sz+4 <= totlen){
563 t = (Taghdr*)(tagdat+sz);
564 v = tagdat+sz+4;
565 len = nhgets(t->length);
566 if(sz+4+len > totlen)
567 break;
568 if(nhgets(t->type) == tagtype && skip-- == 0){
569 *plen = len;
570 return v;
571 }
572 sz += 2+2+len;
573 }
574 return nil;
575 }
576
577 void
dumptags(uchar * tagdat,int ntagdat)578 dumptags(uchar *tagdat, int ntagdat)
579 {
580 int i,len, sz;
581 uchar *v;
582 Taghdr *t;
583
584 sz = 0;
585 while(sz+4 <= ntagdat){
586 t = (Taghdr*)(tagdat+sz);
587 v = tagdat+sz+2+2;
588 len = nhgets(t->length);
589 if(sz+4+len > ntagdat)
590 break;
591 fprint(2, "\t0x%x %d: ", nhgets(t->type), len);
592 switch(nhgets(t->type)){
593 case TagEnd:
594 fprint(2, "end of tag list\n");
595 break;
596 case TagSrvName:
597 fprint(2, "service '%.*s'\n", len, (char*)v);
598 break;
599 case TagAcName:
600 fprint(2, "ac '%.*s'\n", len, (char*)v);
601 break;
602 case TagHostUniq:
603 fprint(2, "nonce ");
604 Hex:
605 for(i=0; i<len; i++)
606 fprint(2, "%.2ux", v[i]);
607 fprint(2, "\n");
608 break;
609 case TagAcCookie:
610 fprint(2, "ac cookie ");
611 goto Hex;
612 case TagVendSpec:
613 fprint(2, "vend spec ");
614 goto Hex;
615 case TagRelaySessId:
616 fprint(2, "relay ");
617 goto Hex;
618 case TagSrvNameErr:
619 fprint(2, "srverr '%.*s'\n", len, (char*)v);
620 break;
621 case TagAcSysErr:
622 fprint(2, "syserr '%.*s'\n", len, (char*)v);
623 break;
624 }
625 sz += 2+2+len;
626 }
627 if(sz != ntagdat)
628 fprint(2, "warning: only dumped %d of %d bytes\n", sz, ntagdat);
629 }
630
631 void
dumppkt(uchar * pkt)632 dumppkt(uchar *pkt)
633 {
634 int et;
635 Etherhdr *eh;
636 Pppoehdr *ph;
637
638 eh = (Etherhdr*)pkt;
639 ph = (Pppoehdr*)(pkt+EtherHdrSz);
640 et = nhgets(eh->type);
641
642 fprint(2, "%E -> %E type 0x%x\n",
643 eh->src, eh->dst, et);
644 switch(et){
645 case EtherPppoeDiscovery:
646 case EtherPppoeSession:
647 fprint(2, "\tvers %d type %d code 0x%x sessid 0x%x length %d\n",
648 ph->verstype>>4, ph->verstype&15,
649 ph->code, nhgets(ph->sessid), nhgets(ph->length));
650 if(et == EtherPppoeDiscovery)
651 dumptags(pkt+Hdr, nhgets(ph->length));
652 }
653 }
654
655 int
malformed(uchar * pkt,int n,int wantet)656 malformed(uchar *pkt, int n, int wantet)
657 {
658 int et;
659 Etherhdr *eh;
660 Pppoehdr *ph;
661
662 eh = (Etherhdr*)pkt;
663 ph = (Pppoehdr*)(pkt+EtherHdrSz);
664
665 if(n < Hdr || n < Hdr+nhgets(ph->length)){
666 werrstr("packet too short %d != %d", n, Hdr+nhgets(ph->length));
667 return 1;
668 }
669
670 et = nhgets(eh->type);
671 if(et != wantet){
672 werrstr("wrong ethernet packet type 0x%x != 0x%x", et, wantet);
673 return 1;
674 }
675
676 return 0;
677 }
678
679 void
hexdump(uchar * a,int na)680 hexdump(uchar *a, int na)
681 {
682 int i;
683 char buf[80];
684
685 buf[0] = '\0';
686 for(i=0; i<na; i++){
687 sprint(buf+strlen(buf), " %.2ux", a[i]);
688 if(i%16 == 7)
689 sprint(buf+strlen(buf), " --");
690 if(i%16==15){
691 sprint(buf+strlen(buf), "\n");
692 write(2, buf, strlen(buf));
693 buf[0] = 0;
694 }
695 }
696 if(i%16){
697 sprint(buf+strlen(buf), "\n");
698 write(2, buf, strlen(buf));
699 }
700 }
701