/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * User-level PPP over Ethernet (PPPoE) client. * See RFC 2516 */ #include <u.h>#include <libc.h>#include <ip.h> void dumppkt(uchar*);uchar *findtag(uchar*, int, int*, int);void hexdump(uchar*, int);int malformed(uchar*, int, int);int pppoe(char*);void execppp(int); int alarmed;int debug;int sessid;char *keyspec;int primary;char *pppnetmtpt;char *acname;char *pppname = "/bin/ip/ppp";char *srvname = "";char *wantac;uchar *cookie;int cookielen;uchar etherdst[6];int mtu = 1492; voidusage(void){ fprint(2, "usage: pppoe [-Pd] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-x pppnet] [ether0]\n"); exits("usage");} intcatchalarm(void *a, char *msg){ USED(a); if(strstr(msg, "alarm")){ alarmed = 1; return 1; } if(debug) fprint(2, "note rcved: %s\n", msg); return 0;} voidmain(int argc, char **argv){ int fd; char *dev; ARGBEGIN{ case 'A': wantac = EARGF(usage()); break; case 'P': primary = 1; break; case 'S': srvname = EARGF(usage()); break; case 'd': debug++; break; case 'm': mtu = atoi(EARGF(usage())); break; case 'k': keyspec = EARGF(usage()); break; case 'x': pppnetmtpt = EARGF(usage()); break; default: usage(); }ARGEND switch(argc){ default: usage(); case 0: dev = "ether0"; break; case 1: dev = argv[0]; break; } fmtinstall('E', eipfmt); atnotify(catchalarm, 1); fd = pppoe(dev); execppp(fd);} typedef struct Etherhdr Etherhdr;struct Etherhdr { uchar dst[6]; uchar src[6]; uchar type[2];}; enum { EtherHdrSz = 6+6+2, EtherMintu = 60, EtherPppoeDiscovery = 0x8863, EtherPppoeSession = 0x8864,}; uchar etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; intetherhdr(uchar *pkt, uchar *dst, int type){ Etherhdr *eh; eh = (Etherhdr*)pkt; memmove(eh->dst, dst, sizeof(eh->dst)); hnputs(eh->type, type); return EtherHdrSz;} typedef struct Pppoehdr Pppoehdr;struct Pppoehdr { uchar verstype; uchar code; uchar sessid[2]; uchar length[2]; /* of payload */}; enum { PppoeHdrSz = 1+1+2+2, Hdr = EtherHdrSz+PppoeHdrSz,}; enum { VersType = 0x11, /* Discovery codes */ CodeDiscInit = 0x09, /* discovery init */ CodeDiscOffer = 0x07, /* discovery offer */ CodeDiscReq = 0x19, /* discovery request */ CodeDiscSess = 0x65, /* session confirmation */ /* Session codes */ CodeSession = 0x00,}; intpppoehdr(uchar *pkt, int code, int sessid){ Pppoehdr *ph; ph = (Pppoehdr*)pkt; ph->verstype = VersType; ph->code = code; hnputs(ph->sessid, sessid); return PppoeHdrSz;} typedef struct Taghdr Taghdr;struct Taghdr { uchar type[2]; uchar length[2]; /* of value */}; enum { TagEnd = 0x0000, /* end of tag list */ TagSrvName = 0x0101, /* service name */ TagAcName = 0x0102, /* access concentrator name */ TagHostUniq = 0x0103, /* nonce */ TagAcCookie = 0x0104, /* a.c. cookie */ TagVendSpec = 0x0105, /* vendor specific */ TagRelaySessId = 0x0110, /* relay session id */ TagSrvNameErr = 0x0201, /* service name error (ascii) */ TagAcSysErr = 0x0202, /* a.c. system error */}; inttag(uchar *pkt, int type, void *value, int nvalue){ Taghdr *h; h = (Taghdr*)pkt; hnputs(h->type, type); hnputs(h->length, nvalue); memmove(pkt+4, value, nvalue); return 4+nvalue;} /* PPPoE Active Discovery Initiation */intpadi(uchar *pkt){ int sz, tagoff; uchar *length; sz = 0; sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery); sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000); length = pkt+sz-2; tagoff = sz; sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname)); hnputs(length, sz-tagoff); return sz;} /* PPPoE Active Discovery Request */intpadr(uchar *pkt){ int sz, tagoff; uchar *length; sz = 0; sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery); sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000); length = pkt+sz-2; tagoff = sz; sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname)); sz += tag(pkt+sz, TagAcName, acname, strlen(acname)); if(cookie) sz += tag(pkt+sz, TagAcCookie, cookie, cookielen); hnputs(length, sz-tagoff); return sz;} voidewrite(int fd, void *buf, int nbuf){ char e[ERRMAX], path[64]; if(write(fd, buf, nbuf) != nbuf){ rerrstr(e, sizeof e); strcpy(path, "unknown"); fd2path(fd, path, sizeof path); sysfatal("write %d to %s: %s", nbuf, path, e); }} void*emalloc(long n){ void *v; v = malloc(n); if(v == nil) sysfatal("out of memory"); return v;} intaread(int timeout, int fd, void *buf, int nbuf){ int n; alarmed = 0; alarm(timeout); n = read(fd, buf, nbuf); alarm(0); if(alarmed) return -1; if(n < 0) sysfatal("read: %r"); if(n == 0) sysfatal("short read"); return n;} intpktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uchar*)){ int n, t2; n = -1; for(t2=timeout; t2<16000; t2*=2){ while((n = aread(t2, fd, buf, nbuf)) > 0){ if(malformed(buf, n, EtherPppoeDiscovery)){ if(debug) fprint(2, "dropping pkt: %r\n"); continue; } if(debug) dumppkt(buf); if(!want(buf)){ if(debug) fprint(2, "dropping unwanted pkt: %r\n"); continue; } break; } if(n > 0) break; } return n;} intbad(char *reason){ werrstr(reason); return 0;} void*copy(uchar *s, int len){ uchar *v; v = emalloc(len+1); memmove(v, s, len); v[len] = '\0'; return v;} voidclearstate(void){ sessid = -1; free(acname); acname = nil; free(cookie); cookie = nil;} intwantoffer(uchar *pkt){ int i, len; uchar *s; Etherhdr *eh; Pppoehdr *ph; eh = (Etherhdr*)pkt; ph = (Pppoehdr*)(pkt+EtherHdrSz); if(ph->code != CodeDiscOffer) return bad("not an offer"); if(nhgets(ph->sessid) != 0x0000) return bad("bad session id"); for(i=0;; i++){ if((s = findtag(pkt, TagSrvName, &len, i)) == nil) return bad("no matching service name"); if(len == strlen(srvname) && memcmp(s, srvname, len) == 0) break; } if((s = findtag(pkt, TagAcName, &len, 0)) == nil) return bad("no ac name"); acname = copy(s, len); if(wantac && strcmp(acname, wantac) != 0){ free(acname); return bad("wrong ac name"); } if(s = findtag(pkt, TagAcCookie, &len, 0)){ cookie = copy(s, len); cookielen = len; } memmove(etherdst, eh->src, sizeof etherdst); return 1;} intwantsession(uchar *pkt){ int len; uchar *s; Pppoehdr *ph; ph = (Pppoehdr*)(pkt+EtherHdrSz); if(ph->code != CodeDiscSess) return bad("not a session confirmation"); if(nhgets(ph->sessid) == 0x0000) return bad("bad session id"); if(findtag(pkt, TagSrvName, &len, 0) == nil) return bad("no service name"); if(findtag(pkt, TagSrvNameErr, &len, 0)) return bad("service name error"); if(findtag(pkt, TagAcSysErr, &len, 0)) return bad("ac system error"); /* * rsc said: ``if there is no -S option given, the current code * waits for an offer with service name == "". * that's silly. it should take the first one it gets.'' */ if(srvname[0] != '\0') { if((s = findtag(pkt, TagSrvName, &len, 0)) == nil) return bad("no matching service name"); if(len != strlen(srvname) || memcmp(s, srvname, len) != 0) return bad("no matching service name"); } sessid = nhgets(ph->sessid); return 1;} intpppoe(char *ether){ char buf[64]; uchar pkt[1520]; int dfd, p[2], n, sfd, sz, timeout; Pppoehdr *ph; ph = (Pppoehdr*)(pkt+EtherHdrSz); snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery); if((dfd = dial(buf, nil, nil, nil)) < 0) sysfatal("dial %s: %r", buf); snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession); if((sfd = dial(buf, nil, nil, nil)) < 0) sysfatal("dial %s: %r", buf); for(timeout=250; timeout<16000; timeout*=2){ clearstate(); memset(pkt, 0, sizeof pkt); sz = padi(pkt); if(debug) dumppkt(pkt); if(sz < EtherMintu) sz = EtherMintu; ewrite(dfd, pkt, sz); if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0) continue; memset(pkt, 0, sizeof pkt); sz = padr(pkt); if(debug) dumppkt(pkt); if(sz < EtherMintu) sz = EtherMintu; ewrite(dfd, pkt, sz); if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0) continue; break; } if(sessid < 0) sysfatal("could not establish session"); rfork(RFNOTEG); if(pipe(p) < 0) sysfatal("pipe: %r"); switch(fork()){ case -1: sysfatal("fork: %r"); default: break; case 0: close(p[1]); while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){ etherhdr(pkt, etherdst, EtherPppoeSession); pppoehdr(pkt+EtherHdrSz, 0x00, sessid); hnputs(pkt+Hdr-2, n); sz = Hdr+n; if(debug > 1){ dumppkt(pkt); hexdump(pkt, sz); } if(sz < EtherMintu) sz = EtherMintu; if(write(sfd, pkt, sz) < 0){ if(debug) fprint(2, "write to ether failed: %r"); _exits(nil); } } _exits(nil); } switch(fork()){ case -1: sysfatal("fork: %r"); default: break; case 0: close(p[1]); while((n = read(sfd, pkt, sizeof pkt)) > 0){Lines 1–500 of 709. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * User-level PPP over Ethernet (PPPoE) client. * See RFC 2516 */ #include <u.h> #include <libc.h> #include <ip.h> void dumppkt(uchar*); uchar *findtag(uchar*, int, int*, int); void hexdump(uchar*, int); int malformed(uchar*, int, int); int pppoe(char*); void execppp(int); int alarmed; int debug; int sessid; char *keyspec; int primary; char *pppnetmtpt; char *acname; char *pppname = "/bin/ip/ppp"; char *srvname = ""; char *wantac; uchar *cookie; int cookielen; uchar etherdst[6]; int mtu = 1492; void usage(void) { fprint(2, "usage: pppoe [-Pd] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-x pppnet] [ether0]\n"); exits("usage"); } int catchalarm(void *a, char *msg) { USED(a); if(strstr(msg, "alarm")){ alarmed = 1; return 1; } if(debug) fprint(2, "note rcved: %s\n", msg); return 0; } void main(int argc, char **argv) { int fd; char *dev; ARGBEGIN{ case 'A': wantac = EARGF(usage()); break; case 'P': primary = 1; break; case 'S': srvname = EARGF(usage()); break; case 'd': debug++; break; case 'm': mtu = atoi(EARGF(usage())); break; case 'k': keyspec = EARGF(usage()); break; case 'x': pppnetmtpt = EARGF(usage()); break; default: usage(); }ARGEND switch(argc){ default: usage(); case 0: dev = "ether0"; break; case 1: dev = argv[0]; break; } fmtinstall('E', eipfmt); atnotify(catchalarm, 1); fd = pppoe(dev); execppp(fd); } typedef struct Etherhdr Etherhdr; struct Etherhdr { uchar dst[6]; uchar src[6]; uchar type[2]; }; enum { EtherHdrSz = 6+6+2, EtherMintu = 60, EtherPppoeDiscovery = 0x8863, EtherPppoeSession = 0x8864, }; uchar etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; int etherhdr(uchar *pkt, uchar *dst, int type) { Etherhdr *eh; eh = (Etherhdr*)pkt; memmove(eh->dst, dst, sizeof(eh->dst)); hnputs(eh->type, type); return EtherHdrSz; } typedef struct Pppoehdr Pppoehdr; struct Pppoehdr { uchar verstype; uchar code; uchar sessid[2]; uchar length[2]; /* of payload */ }; enum { PppoeHdrSz = 1+1+2+2, Hdr = EtherHdrSz+PppoeHdrSz, }; enum { VersType = 0x11, /* Discovery codes */ CodeDiscInit = 0x09, /* discovery init */ CodeDiscOffer = 0x07, /* discovery offer */ CodeDiscReq = 0x19, /* discovery request */ CodeDiscSess = 0x65, /* session confirmation */ /* Session codes */ CodeSession = 0x00, }; int pppoehdr(uchar *pkt, int code, int sessid) { Pppoehdr *ph; ph = (Pppoehdr*)pkt; ph->verstype = VersType; ph->code = code; hnputs(ph->sessid, sessid); return PppoeHdrSz; } typedef struct Taghdr Taghdr; struct Taghdr { uchar type[2]; uchar length[2]; /* of value */ }; enum { TagEnd = 0x0000, /* end of tag list */ TagSrvName = 0x0101, /* service name */ TagAcName = 0x0102, /* access concentrator name */ TagHostUniq = 0x0103, /* nonce */ TagAcCookie = 0x0104, /* a.c. cookie */ TagVendSpec = 0x0105, /* vendor specific */ TagRelaySessId = 0x0110, /* relay session id */ TagSrvNameErr = 0x0201, /* service name error (ascii) */ TagAcSysErr = 0x0202, /* a.c. system error */ }; int tag(uchar *pkt, int type, void *value, int nvalue) { Taghdr *h; h = (Taghdr*)pkt; hnputs(h->type, type); hnputs(h->length, nvalue); memmove(pkt+4, value, nvalue); return 4+nvalue; } /* PPPoE Active Discovery Initiation */ int padi(uchar *pkt) { int sz, tagoff; uchar *length; sz = 0; sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery); sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000); length = pkt+sz-2; tagoff = sz; sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname)); hnputs(length, sz-tagoff); return sz; } /* PPPoE Active Discovery Request */ int padr(uchar *pkt) { int sz, tagoff; uchar *length; sz = 0; sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery); sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000); length = pkt+sz-2; tagoff = sz; sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname)); sz += tag(pkt+sz, TagAcName, acname, strlen(acname)); if(cookie) sz += tag(pkt+sz, TagAcCookie, cookie, cookielen); hnputs(length, sz-tagoff); return sz; } void ewrite(int fd, void *buf, int nbuf) { char e[ERRMAX], path[64]; if(write(fd, buf, nbuf) != nbuf){ rerrstr(e, sizeof e); strcpy(path, "unknown"); fd2path(fd, path, sizeof path); sysfatal("write %d to %s: %s", nbuf, path, e); } } void* emalloc(long n) { void *v; v = malloc(n); if(v == nil) sysfatal("out of memory"); return v; } int aread(int timeout, int fd, void *buf, int nbuf) { int n; alarmed = 0; alarm(timeout); n = read(fd, buf, nbuf); alarm(0); if(alarmed) return -1; if(n < 0) sysfatal("read: %r"); if(n == 0) sysfatal("short read"); return n; } int pktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uchar*)) { int n, t2; n = -1; for(t2=timeout; t2<16000; t2*=2){ while((n = aread(t2, fd, buf, nbuf)) > 0){ if(malformed(buf, n, EtherPppoeDiscovery)){ if(debug) fprint(2, "dropping pkt: %r\n"); continue; } if(debug) dumppkt(buf); if(!want(buf)){ if(debug) fprint(2, "dropping unwanted pkt: %r\n"); continue; } break; } if(n > 0) break; } return n; } int bad(char *reason) { werrstr(reason); return 0; } void* copy(uchar *s, int len) { uchar *v; v = emalloc(len+1); memmove(v, s, len); v[len] = '\0'; return v; } void clearstate(void) { sessid = -1; free(acname); acname = nil; free(cookie); cookie = nil; } int wantoffer(uchar *pkt) { int i, len; uchar *s; Etherhdr *eh; Pppoehdr *ph; eh = (Etherhdr*)pkt; ph = (Pppoehdr*)(pkt+EtherHdrSz); if(ph->code != CodeDiscOffer) return bad("not an offer"); if(nhgets(ph->sessid) != 0x0000) return bad("bad session id"); for(i=0;; i++){ if((s = findtag(pkt, TagSrvName, &len, i)) == nil) return bad("no matching service name"); if(len == strlen(srvname) && memcmp(s, srvname, len) == 0) break; } if((s = findtag(pkt, TagAcName, &len, 0)) == nil) return bad("no ac name"); acname = copy(s, len); if(wantac && strcmp(acname, wantac) != 0){ free(acname); return bad("wrong ac name"); } if(s = findtag(pkt, TagAcCookie, &len, 0)){ cookie = copy(s, len); cookielen = len; } memmove(etherdst, eh->src, sizeof etherdst); return 1; } int wantsession(uchar *pkt) { int len; uchar *s; Pppoehdr *ph; ph = (Pppoehdr*)(pkt+EtherHdrSz); if(ph->code != CodeDiscSess) return bad("not a session confirmation"); if(nhgets(ph->sessid) == 0x0000) return bad("bad session id"); if(findtag(pkt, TagSrvName, &len, 0) == nil) return bad("no service name"); if(findtag(pkt, TagSrvNameErr, &len, 0)) return bad("service name error"); if(findtag(pkt, TagAcSysErr, &len, 0)) return bad("ac system error"); /* * rsc said: ``if there is no -S option given, the current code * waits for an offer with service name == "". * that's silly. it should take the first one it gets.'' */ if(srvname[0] != '\0') { if((s = findtag(pkt, TagSrvName, &len, 0)) == nil) return bad("no matching service name"); if(len != strlen(srvname) || memcmp(s, srvname, len) != 0) return bad("no matching service name"); } sessid = nhgets(ph->sessid); return 1; } int pppoe(char *ether) { char buf[64]; uchar pkt[1520]; int dfd, p[2], n, sfd, sz, timeout; Pppoehdr *ph; ph = (Pppoehdr*)(pkt+EtherHdrSz); snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery); if((dfd = dial(buf, nil, nil, nil)) < 0) sysfatal("dial %s: %r", buf); snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession); if((sfd = dial(buf, nil, nil, nil)) < 0) sysfatal("dial %s: %r", buf); for(timeout=250; timeout<16000; timeout*=2){ clearstate(); memset(pkt, 0, sizeof pkt); sz = padi(pkt); if(debug) dumppkt(pkt); if(sz < EtherMintu) sz = EtherMintu; ewrite(dfd, pkt, sz); if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0) continue; memset(pkt, 0, sizeof pkt); sz = padr(pkt); if(debug) dumppkt(pkt); if(sz < EtherMintu) sz = EtherMintu; ewrite(dfd, pkt, sz); if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0) continue; break; } if(sessid < 0) sysfatal("could not establish session"); rfork(RFNOTEG); if(pipe(p) < 0) sysfatal("pipe: %r"); switch(fork()){ case -1: sysfatal("fork: %r"); default: break; case 0: close(p[1]); while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){ etherhdr(pkt, etherdst, EtherPppoeSession); pppoehdr(pkt+EtherHdrSz, 0x00, sessid); hnputs(pkt+Hdr-2, n); sz = Hdr+n; if(debug > 1){ dumppkt(pkt); hexdump(pkt, sz); } if(sz < EtherMintu) sz = EtherMintu; if(write(sfd, pkt, sz) < 0){ if(debug) fprint(2, "write to ether failed: %r"); _exits(nil); } } _exits(nil); } switch(fork()){ case -1: sysfatal("fork: %r"); default: break; case 0: close(p[1]); while((n = read(sfd, pkt, sizeof pkt)) > 0){