2 * User-level PPP over Ethernet (PPPoE) client.
11 uchar *findtag(uchar*, int, int*, int);
12 void hexdump(uchar*, int);
13 int malformed(uchar*, int, int);
24 char *pppname = "/bin/ip/ppp";
35 fprint(2, "usage: pppoe [-Pd] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-x pppnet] [ether0]\n");
40 catchalarm(void *a, char *msg)
44 if(strstr(msg, "alarm")){
49 fprint(2, "note rcved: %s\n", msg);
54 main(int argc, char **argv)
61 wantac = EARGF(usage());
67 srvname = EARGF(usage());
73 mtu = atoi(EARGF(usage()));
76 keyspec = EARGF(usage());
79 pppnetmtpt = EARGF(usage());
96 fmtinstall('E', eipfmt);
98 atnotify(catchalarm, 1);
103 typedef struct Etherhdr Etherhdr;
114 EtherPppoeDiscovery = 0x8863,
115 EtherPppoeSession = 0x8864,
118 uchar etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
121 etherhdr(uchar *pkt, uchar *dst, int type)
126 memmove(eh->dst, dst, sizeof(eh->dst));
127 hnputs(eh->type, type);
131 typedef struct Pppoehdr Pppoehdr;
136 uchar length[2]; /* of payload */
140 PppoeHdrSz = 1+1+2+2,
141 Hdr = EtherHdrSz+PppoeHdrSz,
147 /* Discovery codes */
148 CodeDiscInit = 0x09, /* discovery init */
149 CodeDiscOffer = 0x07, /* discovery offer */
150 CodeDiscReq = 0x19, /* discovery request */
151 CodeDiscSess = 0x65, /* session confirmation */
158 pppoehdr(uchar *pkt, int code, int sessid)
163 ph->verstype = VersType;
165 hnputs(ph->sessid, sessid);
169 typedef struct Taghdr Taghdr;
172 uchar length[2]; /* of value */
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 */
188 tag(uchar *pkt, int type, void *value, int nvalue)
193 hnputs(h->type, type);
194 hnputs(h->length, nvalue);
195 memmove(pkt+4, value, nvalue);
199 /* PPPoE Active Discovery Initiation */
207 sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery);
208 sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000);
211 sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
212 hnputs(length, sz-tagoff);
216 /* PPPoE Active Discovery Request */
224 sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery);
225 sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000);
228 sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
229 sz += tag(pkt+sz, TagAcName, acname, strlen(acname));
231 sz += tag(pkt+sz, TagAcCookie, cookie, cookielen);
232 hnputs(length, sz-tagoff);
237 ewrite(int fd, void *buf, int nbuf)
239 char e[ERRMAX], path[64];
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);
256 sysfatal("out of memory");
261 aread(int timeout, int fd, void *buf, int nbuf)
267 n = read(fd, buf, nbuf);
272 sysfatal("read: %r");
274 sysfatal("short read");
279 pktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uchar*))
283 for(t2=timeout; t2<16000; t2*=2){
284 while((n = aread(t2, fd, buf, nbuf)) > 0){
285 if(malformed(buf, n, EtherPppoeDiscovery)){
287 fprint(2, "dropping pkt: %r\n");
294 fprint(2, "dropping unwanted pkt: %r\n");
313 copy(uchar *s, int len)
334 wantoffer(uchar *pkt)
342 ph = (Pppoehdr*)(pkt+EtherHdrSz);
344 if(ph->code != CodeDiscOffer)
345 return bad("not an offer");
346 if(nhgets(ph->sessid) != 0x0000)
347 return bad("bad session id");
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)
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){
362 return bad("wrong ac name");
365 if(s = findtag(pkt, TagAcCookie, &len, 0)){
366 cookie = copy(s, len);
369 memmove(etherdst, eh->src, sizeof etherdst);
374 wantsession(uchar *pkt)
380 ph = (Pppoehdr*)(pkt+EtherHdrSz);
382 if(ph->code != CodeDiscSess)
383 return bad("not a session confirmation");
384 if(nhgets(ph->sessid) == 0x0000)
385 return bad("bad session id");
386 if(findtag(pkt, TagSrvName, &len, 0) == nil)
387 return bad("no service name");
388 if(findtag(pkt, TagSrvNameErr, &len, 0))
389 return bad("service name error");
390 if(findtag(pkt, TagAcSysErr, &len, 0))
391 return bad("ac system error");
394 * rsc said: ``if there is no -S option given, the current code
395 * waits for an offer with service name == "".
396 * that's silly. it should take the first one it gets.''
398 if(srvname[0] != '\0') {
399 if((s = findtag(pkt, TagSrvName, &len, 0)) == nil)
400 return bad("no matching service name");
401 if(len != strlen(srvname) || memcmp(s, srvname, len) != 0)
402 return bad("no matching service name");
404 sessid = nhgets(ph->sessid);
413 int dfd, p[2], n, sfd, sz, timeout;
416 ph = (Pppoehdr*)(pkt+EtherHdrSz);
417 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery);
418 if((dfd = dial(buf, nil, nil, nil)) < 0)
419 sysfatal("dial %s: %r", buf);
421 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession);
422 if((sfd = dial(buf, nil, nil, nil)) < 0)
423 sysfatal("dial %s: %r", buf);
425 for(timeout=250; timeout<16000; timeout*=2){
427 memset(pkt, 0, sizeof pkt);
433 ewrite(dfd, pkt, sz);
435 if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0)
438 memset(pkt, 0, sizeof pkt);
444 ewrite(dfd, pkt, sz);
446 if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0)
452 sysfatal("could not establish session");
456 sysfatal("pipe: %r");
460 sysfatal("fork: %r");
465 while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){
466 etherhdr(pkt, etherdst, EtherPppoeSession);
467 pppoehdr(pkt+EtherHdrSz, 0x00, sessid);
468 hnputs(pkt+Hdr-2, n);
476 if(write(sfd, pkt, sz) < 0){
478 fprint(2, "write to ether failed: %r");
487 sysfatal("fork: %r");
492 while((n = read(sfd, pkt, sizeof pkt)) > 0){
493 if(malformed(pkt, n, EtherPppoeSession)
494 || ph->code != 0x00 || nhgets(ph->sessid) != sessid){
496 fprint(2, "malformed session pkt: %r\n");
501 if(write(p[0], pkt+Hdr, nhgets(ph->length)) < 0){
503 fprint(2, "write to ppp failed: %r\n");
521 argv[argc++] = pppname;
522 snprint(smtu, sizeof(smtu), "-m%d", mtu);
531 argv[argc++] = pppnetmtpt;
535 argv[argc++] = keyspec;
542 sysfatal("exec: %r");
546 findtag(uchar *pkt, int tagtype, int *plen, int skip)
555 ph = (Pppoehdr*)(pkt+EtherHdrSz);
558 if(nhgets(eh->type) != EtherPppoeDiscovery)
560 totlen = nhgets(ph->length);
563 while(sz+4 <= totlen){
564 t = (Taghdr*)(tagdat+sz);
566 len = nhgets(t->length);
567 if(sz+4+len > totlen)
569 if(nhgets(t->type) == tagtype && skip-- == 0){
579 dumptags(uchar *tagdat, int ntagdat)
586 while(sz+4 <= ntagdat){
587 t = (Taghdr*)(tagdat+sz);
589 len = nhgets(t->length);
590 if(sz+4+len > ntagdat)
592 fprint(2, "\t0x%x %d: ", nhgets(t->type), len);
593 switch(nhgets(t->type)){
595 fprint(2, "end of tag list\n");
598 fprint(2, "service '%.*s'\n", len, (char*)v);
601 fprint(2, "ac '%.*s'\n", len, (char*)v);
607 fprint(2, "%.2ux", v[i]);
611 fprint(2, "ac cookie ");
614 fprint(2, "vend spec ");
620 fprint(2, "srverr '%.*s'\n", len, (char*)v);
623 fprint(2, "syserr '%.*s'\n", len, (char*)v);
629 fprint(2, "warning: only dumped %d of %d bytes\n", sz, ntagdat);
640 ph = (Pppoehdr*)(pkt+EtherHdrSz);
641 et = nhgets(eh->type);
643 fprint(2, "%E -> %E type 0x%x\n",
644 eh->src, eh->dst, et);
646 case EtherPppoeDiscovery:
647 case EtherPppoeSession:
648 fprint(2, "\tvers %d type %d code 0x%x sessid 0x%x length %d\n",
649 ph->verstype>>4, ph->verstype&15,
650 ph->code, nhgets(ph->sessid), nhgets(ph->length));
651 if(et == EtherPppoeDiscovery)
652 dumptags(pkt+Hdr, nhgets(ph->length));
657 malformed(uchar *pkt, int n, int wantet)
664 ph = (Pppoehdr*)(pkt+EtherHdrSz);
666 if(n < Hdr || n < Hdr+nhgets(ph->length)){
667 werrstr("packet too short %d != %d", n, Hdr+nhgets(ph->length));
671 et = nhgets(eh->type);
673 werrstr("wrong ethernet packet type 0x%x != 0x%x", et, wantet);
681 hexdump(uchar *a, int na)
688 sprint(buf+strlen(buf), " %.2ux", a[i]);
690 sprint(buf+strlen(buf), " --");
692 sprint(buf+strlen(buf), "\n");
693 write(2, buf, strlen(buf));
698 sprint(buf+strlen(buf), "\n");
699 write(2, buf, strlen(buf));