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){
361 return bad("wrong ac name");
364 if(s = findtag(pkt, TagAcCookie, &len, 0)){
365 cookie = copy(s, len);
368 memmove(etherdst, eh->src, sizeof etherdst);
373 wantsession(uchar *pkt)
379 ph = (Pppoehdr*)(pkt+EtherHdrSz);
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");
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.''
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");
403 sessid = nhgets(ph->sessid);
412 int dfd, p[2], n, sfd, sz, timeout;
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);
420 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession);
421 if((sfd = dial(buf, nil, nil, nil)) < 0)
422 sysfatal("dial %s: %r", buf);
424 for(timeout=250; timeout<16000; timeout*=2){
426 memset(pkt, 0, sizeof pkt);
432 ewrite(dfd, pkt, sz);
434 if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0)
437 memset(pkt, 0, sizeof pkt);
443 ewrite(dfd, pkt, sz);
445 if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0)
451 sysfatal("could not establish session");
455 sysfatal("pipe: %r");
459 sysfatal("fork: %r");
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);
475 if(write(sfd, pkt, sz) < 0){
477 fprint(2, "write to ether failed: %r");
486 sysfatal("fork: %r");
491 while((n = read(sfd, pkt, sizeof pkt)) > 0){
492 if(malformed(pkt, n, EtherPppoeSession)
493 || ph->code != 0x00 || nhgets(ph->sessid) != sessid){
495 fprint(2, "malformed session pkt: %r\n");
500 if(write(p[0], pkt+Hdr, nhgets(ph->length)) < 0){
502 fprint(2, "write to ppp failed: %r\n");
520 argv[argc++] = pppname;
521 snprint(smtu, sizeof(smtu), "-m%d", mtu);
530 argv[argc++] = pppnetmtpt;
534 argv[argc++] = keyspec;
541 sysfatal("exec: %r");
545 findtag(uchar *pkt, int tagtype, int *plen, int skip)
554 ph = (Pppoehdr*)(pkt+EtherHdrSz);
557 if(nhgets(eh->type) != EtherPppoeDiscovery)
559 totlen = nhgets(ph->length);
562 while(sz+4 <= totlen){
563 t = (Taghdr*)(tagdat+sz);
565 len = nhgets(t->length);
566 if(sz+4+len > totlen)
568 if(nhgets(t->type) == tagtype && skip-- == 0){
578 dumptags(uchar *tagdat, int ntagdat)
585 while(sz+4 <= ntagdat){
586 t = (Taghdr*)(tagdat+sz);
588 len = nhgets(t->length);
589 if(sz+4+len > ntagdat)
591 fprint(2, "\t0x%x %d: ", nhgets(t->type), len);
592 switch(nhgets(t->type)){
594 fprint(2, "end of tag list\n");
597 fprint(2, "service '%.*s'\n", len, (char*)v);
600 fprint(2, "ac '%.*s'\n", len, (char*)v);
606 fprint(2, "%.2ux", v[i]);
610 fprint(2, "ac cookie ");
613 fprint(2, "vend spec ");
619 fprint(2, "srverr '%.*s'\n", len, (char*)v);
622 fprint(2, "syserr '%.*s'\n", len, (char*)v);
628 fprint(2, "warning: only dumped %d of %d bytes\n", sz, ntagdat);
639 ph = (Pppoehdr*)(pkt+EtherHdrSz);
640 et = nhgets(eh->type);
642 fprint(2, "%E -> %E type 0x%x\n",
643 eh->src, eh->dst, 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));
656 malformed(uchar *pkt, int n, int wantet)
663 ph = (Pppoehdr*)(pkt+EtherHdrSz);
665 if(n < Hdr || n < Hdr+nhgets(ph->length)){
666 werrstr("packet too short %d != %d", n, Hdr+nhgets(ph->length));
670 et = nhgets(eh->type);
672 werrstr("wrong ethernet packet type 0x%x != 0x%x", et, wantet);
680 hexdump(uchar *a, int na)
687 sprint(buf+strlen(buf), " %.2ux", a[i]);
689 sprint(buf+strlen(buf), " --");
691 sprint(buf+strlen(buf), "\n");
692 write(2, buf, strlen(buf));
697 sprint(buf+strlen(buf), "\n");
698 write(2, buf, strlen(buf));