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";
31 int pktcompress, hdrcompress;
37 fprint(2, "usage: pppoe [-PdcC] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-b baud] [-x pppnet] [ether0]\n");
42 catchalarm(void *a, char *msg)
46 if(strstr(msg, "alarm")){
51 fprint(2, "note rcved: %s\n", msg);
56 main(int argc, char **argv)
63 wantac = EARGF(usage());
69 srvname = EARGF(usage());
75 mtu = atoi(EARGF(usage()));
78 keyspec = EARGF(usage());
81 baud = EARGF(usage());
90 pppnetmtpt = EARGF(usage());
107 fmtinstall('E', eipfmt);
109 atnotify(catchalarm, 1);
114 typedef struct Etherhdr Etherhdr;
125 EtherPppoeDiscovery = 0x8863,
126 EtherPppoeSession = 0x8864,
129 uchar etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
132 etherhdr(uchar *pkt, uchar *dst, int type)
137 memmove(eh->dst, dst, sizeof(eh->dst));
138 hnputs(eh->type, type);
142 typedef struct Pppoehdr Pppoehdr;
147 uchar length[2]; /* of payload */
151 PppoeHdrSz = 1+1+2+2,
152 Hdr = EtherHdrSz+PppoeHdrSz,
158 /* Discovery codes */
159 CodeDiscInit = 0x09, /* discovery init */
160 CodeDiscOffer = 0x07, /* discovery offer */
161 CodeDiscReq = 0x19, /* discovery request */
162 CodeDiscSess = 0x65, /* session confirmation */
169 pppoehdr(uchar *pkt, int code, int sessid)
174 ph->verstype = VersType;
176 hnputs(ph->sessid, sessid);
180 typedef struct Taghdr Taghdr;
183 uchar length[2]; /* of value */
187 TagEnd = 0x0000, /* end of tag list */
188 TagSrvName = 0x0101, /* service name */
189 TagAcName = 0x0102, /* access concentrator name */
190 TagHostUniq = 0x0103, /* nonce */
191 TagAcCookie = 0x0104, /* a.c. cookie */
192 TagVendSpec = 0x0105, /* vendor specific */
193 TagRelaySessId = 0x0110, /* relay session id */
194 TagSrvNameErr = 0x0201, /* service name error (ascii) */
195 TagAcSysErr = 0x0202, /* a.c. system error */
199 tag(uchar *pkt, int type, void *value, int nvalue)
204 hnputs(h->type, type);
205 hnputs(h->length, nvalue);
206 memmove(pkt+4, value, nvalue);
210 /* PPPoE Active Discovery Initiation */
218 sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery);
219 sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000);
222 sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
223 hnputs(length, sz-tagoff);
227 /* PPPoE Active Discovery Request */
235 sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery);
236 sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000);
239 sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
240 sz += tag(pkt+sz, TagAcName, acname, strlen(acname));
242 sz += tag(pkt+sz, TagAcCookie, cookie, cookielen);
243 hnputs(length, sz-tagoff);
248 ewrite(int fd, void *buf, int nbuf)
250 char e[ERRMAX], path[64];
252 if(write(fd, buf, nbuf) != nbuf){
253 rerrstr(e, sizeof e);
254 strcpy(path, "unknown");
255 fd2path(fd, path, sizeof path);
256 sysfatal("write %d to %s: %s", nbuf, path, e);
267 sysfatal("out of memory");
272 aread(int timeout, int fd, void *buf, int nbuf)
278 n = read(fd, buf, nbuf);
283 sysfatal("read: %r");
285 sysfatal("short read");
290 pktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uchar*))
294 for(t2=timeout; t2<16000; t2*=2){
295 while((n = aread(t2, fd, buf, nbuf)) > 0){
296 if(malformed(buf, n, EtherPppoeDiscovery)){
298 fprint(2, "dropping pkt: %r\n");
305 fprint(2, "dropping unwanted pkt: %r\n");
324 copy(uchar *s, int len)
345 wantoffer(uchar *pkt)
353 ph = (Pppoehdr*)(pkt+EtherHdrSz);
355 if(ph->code != CodeDiscOffer)
356 return bad("not an offer");
357 if(nhgets(ph->sessid) != 0x0000)
358 return bad("bad session id");
361 if((s = findtag(pkt, TagSrvName, &len, i)) == nil)
362 return bad("no matching service name");
363 if(len == strlen(srvname) && memcmp(s, srvname, len) == 0)
367 if((s = findtag(pkt, TagAcName, &len, 0)) == nil)
368 return bad("no ac name");
369 acname = copy(s, len);
370 if(wantac && strcmp(acname, wantac) != 0){
373 return bad("wrong ac name");
376 if(s = findtag(pkt, TagAcCookie, &len, 0)){
377 cookie = copy(s, len);
380 memmove(etherdst, eh->src, sizeof etherdst);
385 wantsession(uchar *pkt)
391 ph = (Pppoehdr*)(pkt+EtherHdrSz);
393 if(ph->code != CodeDiscSess)
394 return bad("not a session confirmation");
395 if(nhgets(ph->sessid) == 0x0000)
396 return bad("bad session id");
397 if(findtag(pkt, TagSrvName, &len, 0) == nil)
398 return bad("no service name");
399 if(findtag(pkt, TagSrvNameErr, &len, 0))
400 return bad("service name error");
401 if(findtag(pkt, TagAcSysErr, &len, 0))
402 return bad("ac system error");
405 * rsc said: ``if there is no -S option given, the current code
406 * waits for an offer with service name == "".
407 * that's silly. it should take the first one it gets.''
409 if(srvname[0] != '\0') {
410 if((s = findtag(pkt, TagSrvName, &len, 0)) == nil)
411 return bad("no matching service name");
412 if(len != strlen(srvname) || memcmp(s, srvname, len) != 0)
413 return bad("no matching service name");
415 sessid = nhgets(ph->sessid);
424 int dfd, p[2], n, sfd, sz, timeout;
427 ph = (Pppoehdr*)(pkt+EtherHdrSz);
428 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery);
429 if((dfd = dial(buf, nil, nil, nil)) < 0)
430 sysfatal("dial %s: %r", buf);
432 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession);
433 if((sfd = dial(buf, nil, nil, nil)) < 0)
434 sysfatal("dial %s: %r", buf);
436 for(timeout=250; timeout<16000; timeout*=2){
438 memset(pkt, 0, sizeof pkt);
444 ewrite(dfd, pkt, sz);
446 if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0)
449 memset(pkt, 0, sizeof pkt);
455 ewrite(dfd, pkt, sz);
457 if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0)
463 sysfatal("could not establish session");
467 sysfatal("pipe: %r");
471 sysfatal("fork: %r");
476 while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){
477 etherhdr(pkt, etherdst, EtherPppoeSession);
478 pppoehdr(pkt+EtherHdrSz, 0x00, sessid);
479 hnputs(pkt+Hdr-2, n);
487 if(write(sfd, pkt, sz) < 0){
489 fprint(2, "write to ether failed: %r");
498 sysfatal("fork: %r");
503 while((n = read(sfd, pkt, sizeof pkt)) > 0){
504 if(malformed(pkt, n, EtherPppoeSession)
505 || ph->code != 0x00 || nhgets(ph->sessid) != sessid){
507 fprint(2, "malformed session pkt: %r\n");
512 if(write(p[0], pkt+Hdr, nhgets(ph->length)) < 0){
514 fprint(2, "write to ppp failed: %r\n");
532 argv[argc++] = pppname;
533 snprint(smtu, sizeof(smtu), "-m%d", mtu);
550 argv[argc++] = pppnetmtpt;
554 argv[argc++] = keyspec;
561 sysfatal("exec: %r");
565 findtag(uchar *pkt, int tagtype, int *plen, int skip)
574 ph = (Pppoehdr*)(pkt+EtherHdrSz);
577 if(nhgets(eh->type) != EtherPppoeDiscovery)
579 totlen = nhgets(ph->length);
582 while(sz+4 <= totlen){
583 t = (Taghdr*)(tagdat+sz);
585 len = nhgets(t->length);
586 if(sz+4+len > totlen)
588 if(nhgets(t->type) == tagtype && skip-- == 0){
598 dumptags(uchar *tagdat, int ntagdat)
605 while(sz+4 <= ntagdat){
606 t = (Taghdr*)(tagdat+sz);
608 len = nhgets(t->length);
609 if(sz+4+len > ntagdat)
611 fprint(2, "\t0x%x %d: ", nhgets(t->type), len);
612 switch(nhgets(t->type)){
614 fprint(2, "end of tag list\n");
617 fprint(2, "service '%.*s'\n", utfnlen((char*)v, len), (char*)v);
620 fprint(2, "ac '%.*s'\n", utfnlen((char*)v, len), (char*)v);
626 fprint(2, "%.2ux", v[i]);
630 fprint(2, "ac cookie ");
633 fprint(2, "vend spec ");
639 fprint(2, "srverr '%.*s'\n", utfnlen((char*)v, len), (char*)v);
642 fprint(2, "syserr '%.*s'\n", utfnlen((char*)v, len), (char*)v);
648 fprint(2, "warning: only dumped %d of %d bytes\n", sz, ntagdat);
659 ph = (Pppoehdr*)(pkt+EtherHdrSz);
660 et = nhgets(eh->type);
662 fprint(2, "%E -> %E type 0x%x\n",
663 eh->src, eh->dst, et);
665 case EtherPppoeDiscovery:
666 case EtherPppoeSession:
667 fprint(2, "\tvers %d type %d code 0x%x sessid 0x%x length %d\n",
668 ph->verstype>>4, ph->verstype&15,
669 ph->code, nhgets(ph->sessid), nhgets(ph->length));
670 if(et == EtherPppoeDiscovery)
671 dumptags(pkt+Hdr, nhgets(ph->length));
676 malformed(uchar *pkt, int n, int wantet)
683 ph = (Pppoehdr*)(pkt+EtherHdrSz);
685 if(n < Hdr || n < Hdr+nhgets(ph->length)){
686 werrstr("packet too short %d != %d", n, Hdr+nhgets(ph->length));
690 et = nhgets(eh->type);
692 werrstr("wrong ethernet packet type 0x%x != 0x%x", et, wantet);
700 hexdump(uchar *a, int na)
707 sprint(buf+strlen(buf), " %.2ux", a[i]);
709 sprint(buf+strlen(buf), " --");
711 sprint(buf+strlen(buf), "\n");
712 write(2, buf, strlen(buf));
717 sprint(buf+strlen(buf), "\n");
718 write(2, buf, strlen(buf));