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;
36 fprint(2, "usage: pppoe [-PdcC] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-x pppnet] [ether0]\n");
41 catchalarm(void *a, char *msg)
45 if(strstr(msg, "alarm")){
50 fprint(2, "note rcved: %s\n", msg);
55 main(int argc, char **argv)
62 wantac = EARGF(usage());
68 srvname = EARGF(usage());
74 mtu = atoi(EARGF(usage()));
77 keyspec = EARGF(usage());
86 pppnetmtpt = EARGF(usage());
103 fmtinstall('E', eipfmt);
105 atnotify(catchalarm, 1);
110 typedef struct Etherhdr Etherhdr;
121 EtherPppoeDiscovery = 0x8863,
122 EtherPppoeSession = 0x8864,
125 uchar etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
128 etherhdr(uchar *pkt, uchar *dst, int type)
133 memmove(eh->dst, dst, sizeof(eh->dst));
134 hnputs(eh->type, type);
138 typedef struct Pppoehdr Pppoehdr;
143 uchar length[2]; /* of payload */
147 PppoeHdrSz = 1+1+2+2,
148 Hdr = EtherHdrSz+PppoeHdrSz,
154 /* Discovery codes */
155 CodeDiscInit = 0x09, /* discovery init */
156 CodeDiscOffer = 0x07, /* discovery offer */
157 CodeDiscReq = 0x19, /* discovery request */
158 CodeDiscSess = 0x65, /* session confirmation */
165 pppoehdr(uchar *pkt, int code, int sessid)
170 ph->verstype = VersType;
172 hnputs(ph->sessid, sessid);
176 typedef struct Taghdr Taghdr;
179 uchar length[2]; /* of value */
183 TagEnd = 0x0000, /* end of tag list */
184 TagSrvName = 0x0101, /* service name */
185 TagAcName = 0x0102, /* access concentrator name */
186 TagHostUniq = 0x0103, /* nonce */
187 TagAcCookie = 0x0104, /* a.c. cookie */
188 TagVendSpec = 0x0105, /* vendor specific */
189 TagRelaySessId = 0x0110, /* relay session id */
190 TagSrvNameErr = 0x0201, /* service name error (ascii) */
191 TagAcSysErr = 0x0202, /* a.c. system error */
195 tag(uchar *pkt, int type, void *value, int nvalue)
200 hnputs(h->type, type);
201 hnputs(h->length, nvalue);
202 memmove(pkt+4, value, nvalue);
206 /* PPPoE Active Discovery Initiation */
214 sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery);
215 sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000);
218 sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
219 hnputs(length, sz-tagoff);
223 /* PPPoE Active Discovery Request */
231 sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery);
232 sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000);
235 sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
236 sz += tag(pkt+sz, TagAcName, acname, strlen(acname));
238 sz += tag(pkt+sz, TagAcCookie, cookie, cookielen);
239 hnputs(length, sz-tagoff);
244 ewrite(int fd, void *buf, int nbuf)
246 char e[ERRMAX], path[64];
248 if(write(fd, buf, nbuf) != nbuf){
249 rerrstr(e, sizeof e);
250 strcpy(path, "unknown");
251 fd2path(fd, path, sizeof path);
252 sysfatal("write %d to %s: %s", nbuf, path, e);
263 sysfatal("out of memory");
268 aread(int timeout, int fd, void *buf, int nbuf)
274 n = read(fd, buf, nbuf);
279 sysfatal("read: %r");
281 sysfatal("short read");
286 pktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uchar*))
290 for(t2=timeout; t2<16000; t2*=2){
291 while((n = aread(t2, fd, buf, nbuf)) > 0){
292 if(malformed(buf, n, EtherPppoeDiscovery)){
294 fprint(2, "dropping pkt: %r\n");
301 fprint(2, "dropping unwanted pkt: %r\n");
320 copy(uchar *s, int len)
341 wantoffer(uchar *pkt)
349 ph = (Pppoehdr*)(pkt+EtherHdrSz);
351 if(ph->code != CodeDiscOffer)
352 return bad("not an offer");
353 if(nhgets(ph->sessid) != 0x0000)
354 return bad("bad session id");
357 if((s = findtag(pkt, TagSrvName, &len, i)) == nil)
358 return bad("no matching service name");
359 if(len == strlen(srvname) && memcmp(s, srvname, len) == 0)
363 if((s = findtag(pkt, TagAcName, &len, 0)) == nil)
364 return bad("no ac name");
365 acname = copy(s, len);
366 if(wantac && strcmp(acname, wantac) != 0){
369 return bad("wrong ac name");
372 if(s = findtag(pkt, TagAcCookie, &len, 0)){
373 cookie = copy(s, len);
376 memmove(etherdst, eh->src, sizeof etherdst);
381 wantsession(uchar *pkt)
387 ph = (Pppoehdr*)(pkt+EtherHdrSz);
389 if(ph->code != CodeDiscSess)
390 return bad("not a session confirmation");
391 if(nhgets(ph->sessid) == 0x0000)
392 return bad("bad session id");
393 if(findtag(pkt, TagSrvName, &len, 0) == nil)
394 return bad("no service name");
395 if(findtag(pkt, TagSrvNameErr, &len, 0))
396 return bad("service name error");
397 if(findtag(pkt, TagAcSysErr, &len, 0))
398 return bad("ac system error");
401 * rsc said: ``if there is no -S option given, the current code
402 * waits for an offer with service name == "".
403 * that's silly. it should take the first one it gets.''
405 if(srvname[0] != '\0') {
406 if((s = findtag(pkt, TagSrvName, &len, 0)) == nil)
407 return bad("no matching service name");
408 if(len != strlen(srvname) || memcmp(s, srvname, len) != 0)
409 return bad("no matching service name");
411 sessid = nhgets(ph->sessid);
420 int dfd, p[2], n, sfd, sz, timeout;
423 ph = (Pppoehdr*)(pkt+EtherHdrSz);
424 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery);
425 if((dfd = dial(buf, nil, nil, nil)) < 0)
426 sysfatal("dial %s: %r", buf);
428 snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession);
429 if((sfd = dial(buf, nil, nil, nil)) < 0)
430 sysfatal("dial %s: %r", buf);
432 for(timeout=250; timeout<16000; timeout*=2){
434 memset(pkt, 0, sizeof pkt);
440 ewrite(dfd, pkt, sz);
442 if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0)
445 memset(pkt, 0, sizeof pkt);
451 ewrite(dfd, pkt, sz);
453 if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0)
459 sysfatal("could not establish session");
463 sysfatal("pipe: %r");
467 sysfatal("fork: %r");
472 while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){
473 etherhdr(pkt, etherdst, EtherPppoeSession);
474 pppoehdr(pkt+EtherHdrSz, 0x00, sessid);
475 hnputs(pkt+Hdr-2, n);
483 if(write(sfd, pkt, sz) < 0){
485 fprint(2, "write to ether failed: %r");
494 sysfatal("fork: %r");
499 while((n = read(sfd, pkt, sizeof pkt)) > 0){
500 if(malformed(pkt, n, EtherPppoeSession)
501 || ph->code != 0x00 || nhgets(ph->sessid) != sessid){
503 fprint(2, "malformed session pkt: %r\n");
508 if(write(p[0], pkt+Hdr, nhgets(ph->length)) < 0){
510 fprint(2, "write to ppp failed: %r\n");
528 argv[argc++] = pppname;
529 snprint(smtu, sizeof(smtu), "-m%d", mtu);
542 argv[argc++] = pppnetmtpt;
546 argv[argc++] = keyspec;
553 sysfatal("exec: %r");
557 findtag(uchar *pkt, int tagtype, int *plen, int skip)
566 ph = (Pppoehdr*)(pkt+EtherHdrSz);
569 if(nhgets(eh->type) != EtherPppoeDiscovery)
571 totlen = nhgets(ph->length);
574 while(sz+4 <= totlen){
575 t = (Taghdr*)(tagdat+sz);
577 len = nhgets(t->length);
578 if(sz+4+len > totlen)
580 if(nhgets(t->type) == tagtype && skip-- == 0){
590 dumptags(uchar *tagdat, int ntagdat)
597 while(sz+4 <= ntagdat){
598 t = (Taghdr*)(tagdat+sz);
600 len = nhgets(t->length);
601 if(sz+4+len > ntagdat)
603 fprint(2, "\t0x%x %d: ", nhgets(t->type), len);
604 switch(nhgets(t->type)){
606 fprint(2, "end of tag list\n");
609 fprint(2, "service '%.*s'\n", len, (char*)v);
612 fprint(2, "ac '%.*s'\n", len, (char*)v);
618 fprint(2, "%.2ux", v[i]);
622 fprint(2, "ac cookie ");
625 fprint(2, "vend spec ");
631 fprint(2, "srverr '%.*s'\n", len, (char*)v);
634 fprint(2, "syserr '%.*s'\n", len, (char*)v);
640 fprint(2, "warning: only dumped %d of %d bytes\n", sz, ntagdat);
651 ph = (Pppoehdr*)(pkt+EtherHdrSz);
652 et = nhgets(eh->type);
654 fprint(2, "%E -> %E type 0x%x\n",
655 eh->src, eh->dst, et);
657 case EtherPppoeDiscovery:
658 case EtherPppoeSession:
659 fprint(2, "\tvers %d type %d code 0x%x sessid 0x%x length %d\n",
660 ph->verstype>>4, ph->verstype&15,
661 ph->code, nhgets(ph->sessid), nhgets(ph->length));
662 if(et == EtherPppoeDiscovery)
663 dumptags(pkt+Hdr, nhgets(ph->length));
668 malformed(uchar *pkt, int n, int wantet)
675 ph = (Pppoehdr*)(pkt+EtherHdrSz);
677 if(n < Hdr || n < Hdr+nhgets(ph->length)){
678 werrstr("packet too short %d != %d", n, Hdr+nhgets(ph->length));
682 et = nhgets(eh->type);
684 werrstr("wrong ethernet packet type 0x%x != 0x%x", et, wantet);
692 hexdump(uchar *a, int na)
699 sprint(buf+strlen(buf), " %.2ux", a[i]);
701 sprint(buf+strlen(buf), " --");
703 sprint(buf+strlen(buf), "\n");
704 write(2, buf, strlen(buf));
709 sprint(buf+strlen(buf), "\n");
710 write(2, buf, strlen(buf));