19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier * User-level PPP over Ethernet (PPPoE) client.
39a747e4fSDavid du Colombier * See RFC 2516
49a747e4fSDavid du Colombier */
59a747e4fSDavid du Colombier
69a747e4fSDavid du Colombier #include <u.h>
79a747e4fSDavid du Colombier #include <libc.h>
89a747e4fSDavid du Colombier #include <ip.h>
99a747e4fSDavid du Colombier
109a747e4fSDavid du Colombier void dumppkt(uchar*);
119a747e4fSDavid du Colombier uchar *findtag(uchar*, int, int*, int);
129a747e4fSDavid du Colombier void hexdump(uchar*, int);
139a747e4fSDavid du Colombier int malformed(uchar*, int, int);
149a747e4fSDavid du Colombier int pppoe(char*);
15d9306527SDavid du Colombier void execppp(int);
169a747e4fSDavid du Colombier
179a747e4fSDavid du Colombier int alarmed;
189a747e4fSDavid du Colombier int debug;
199a747e4fSDavid du Colombier int sessid;
20312a1df1SDavid du Colombier char *keyspec;
219a747e4fSDavid du Colombier int primary;
229a747e4fSDavid du Colombier char *pppnetmtpt;
239a747e4fSDavid du Colombier char *acname;
249a747e4fSDavid du Colombier char *pppname = "/bin/ip/ppp";
259a747e4fSDavid du Colombier char *srvname = "";
269a747e4fSDavid du Colombier char *wantac;
279a747e4fSDavid du Colombier uchar *cookie;
289a747e4fSDavid du Colombier int cookielen;
299a747e4fSDavid du Colombier uchar etherdst[6];
309a747e4fSDavid du Colombier int mtu = 1492;
319a747e4fSDavid du Colombier
329a747e4fSDavid du Colombier void
usage(void)339a747e4fSDavid du Colombier usage(void)
349a747e4fSDavid du Colombier {
35312a1df1SDavid du Colombier fprint(2, "usage: pppoe [-Pd] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-x pppnet] [ether0]\n");
369a747e4fSDavid du Colombier exits("usage");
379a747e4fSDavid du Colombier }
389a747e4fSDavid du Colombier
399a747e4fSDavid du Colombier int
catchalarm(void * a,char * msg)409a747e4fSDavid du Colombier catchalarm(void *a, char *msg)
419a747e4fSDavid du Colombier {
429a747e4fSDavid du Colombier USED(a);
439a747e4fSDavid du Colombier
449a747e4fSDavid du Colombier if(strstr(msg, "alarm")){
459a747e4fSDavid du Colombier alarmed = 1;
469a747e4fSDavid du Colombier return 1;
479a747e4fSDavid du Colombier }
489a747e4fSDavid du Colombier if(debug)
499a747e4fSDavid du Colombier fprint(2, "note rcved: %s\n", msg);
509a747e4fSDavid du Colombier return 0;
519a747e4fSDavid du Colombier }
529a747e4fSDavid du Colombier
539a747e4fSDavid du Colombier void
main(int argc,char ** argv)549a747e4fSDavid du Colombier main(int argc, char **argv)
559a747e4fSDavid du Colombier {
569a747e4fSDavid du Colombier int fd;
579a747e4fSDavid du Colombier char *dev;
589a747e4fSDavid du Colombier
599a747e4fSDavid du Colombier ARGBEGIN{
609a747e4fSDavid du Colombier case 'A':
619a747e4fSDavid du Colombier wantac = EARGF(usage());
629a747e4fSDavid du Colombier break;
639a747e4fSDavid du Colombier case 'P':
649a747e4fSDavid du Colombier primary = 1;
659a747e4fSDavid du Colombier break;
669a747e4fSDavid du Colombier case 'S':
679a747e4fSDavid du Colombier srvname = EARGF(usage());
689a747e4fSDavid du Colombier break;
699a747e4fSDavid du Colombier case 'd':
709a747e4fSDavid du Colombier debug++;
719a747e4fSDavid du Colombier break;
729a747e4fSDavid du Colombier case 'm':
739a747e4fSDavid du Colombier mtu = atoi(EARGF(usage()));
749a747e4fSDavid du Colombier break;
75bedadc12SDavid du Colombier case 'k':
76312a1df1SDavid du Colombier keyspec = EARGF(usage());
779a747e4fSDavid du Colombier break;
789a747e4fSDavid du Colombier case 'x':
799a747e4fSDavid du Colombier pppnetmtpt = EARGF(usage());
809a747e4fSDavid du Colombier break;
819a747e4fSDavid du Colombier default:
829a747e4fSDavid du Colombier usage();
839a747e4fSDavid du Colombier }ARGEND
849a747e4fSDavid du Colombier
859a747e4fSDavid du Colombier switch(argc){
869a747e4fSDavid du Colombier default:
879a747e4fSDavid du Colombier usage();
889a747e4fSDavid du Colombier case 0:
899a747e4fSDavid du Colombier dev = "ether0";
909a747e4fSDavid du Colombier break;
919a747e4fSDavid du Colombier case 1:
929a747e4fSDavid du Colombier dev = argv[0];
939a747e4fSDavid du Colombier break;
949a747e4fSDavid du Colombier }
959a747e4fSDavid du Colombier
969a747e4fSDavid du Colombier fmtinstall('E', eipfmt);
979a747e4fSDavid du Colombier
989a747e4fSDavid du Colombier atnotify(catchalarm, 1);
999a747e4fSDavid du Colombier fd = pppoe(dev);
100d9306527SDavid du Colombier execppp(fd);
1019a747e4fSDavid du Colombier }
1029a747e4fSDavid du Colombier
1039a747e4fSDavid du Colombier typedef struct Etherhdr Etherhdr;
1049a747e4fSDavid du Colombier struct Etherhdr {
1059a747e4fSDavid du Colombier uchar dst[6];
1069a747e4fSDavid du Colombier uchar src[6];
1079a747e4fSDavid du Colombier uchar type[2];
1089a747e4fSDavid du Colombier };
1099a747e4fSDavid du Colombier
1109a747e4fSDavid du Colombier enum {
1119a747e4fSDavid du Colombier EtherHdrSz = 6+6+2,
1129a747e4fSDavid du Colombier EtherMintu = 60,
1139a747e4fSDavid du Colombier
1149a747e4fSDavid du Colombier EtherPppoeDiscovery = 0x8863,
1159a747e4fSDavid du Colombier EtherPppoeSession = 0x8864,
1169a747e4fSDavid du Colombier };
1179a747e4fSDavid du Colombier
1189a747e4fSDavid du Colombier uchar etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
1199a747e4fSDavid du Colombier
1209a747e4fSDavid du Colombier int
etherhdr(uchar * pkt,uchar * dst,int type)1219a747e4fSDavid du Colombier etherhdr(uchar *pkt, uchar *dst, int type)
1229a747e4fSDavid du Colombier {
1239a747e4fSDavid du Colombier Etherhdr *eh;
1249a747e4fSDavid du Colombier
1259a747e4fSDavid du Colombier eh = (Etherhdr*)pkt;
1269a747e4fSDavid du Colombier memmove(eh->dst, dst, sizeof(eh->dst));
1279a747e4fSDavid du Colombier hnputs(eh->type, type);
1289a747e4fSDavid du Colombier return EtherHdrSz;
1299a747e4fSDavid du Colombier }
1309a747e4fSDavid du Colombier
1319a747e4fSDavid du Colombier typedef struct Pppoehdr Pppoehdr;
1329a747e4fSDavid du Colombier struct Pppoehdr {
1339a747e4fSDavid du Colombier uchar verstype;
1349a747e4fSDavid du Colombier uchar code;
1359a747e4fSDavid du Colombier uchar sessid[2];
1369a747e4fSDavid du Colombier uchar length[2]; /* of payload */
1379a747e4fSDavid du Colombier };
1389a747e4fSDavid du Colombier
1399a747e4fSDavid du Colombier enum {
1409a747e4fSDavid du Colombier PppoeHdrSz = 1+1+2+2,
1419a747e4fSDavid du Colombier Hdr = EtherHdrSz+PppoeHdrSz,
1429a747e4fSDavid du Colombier };
1439a747e4fSDavid du Colombier
1449a747e4fSDavid du Colombier enum {
1459a747e4fSDavid du Colombier VersType = 0x11,
1469a747e4fSDavid du Colombier
1479a747e4fSDavid du Colombier /* Discovery codes */
1489a747e4fSDavid du Colombier CodeDiscInit = 0x09, /* discovery init */
1499a747e4fSDavid du Colombier CodeDiscOffer = 0x07, /* discovery offer */
1509a747e4fSDavid du Colombier CodeDiscReq = 0x19, /* discovery request */
1519a747e4fSDavid du Colombier CodeDiscSess = 0x65, /* session confirmation */
1529a747e4fSDavid du Colombier
1539a747e4fSDavid du Colombier /* Session codes */
1549a747e4fSDavid du Colombier CodeSession = 0x00,
1559a747e4fSDavid du Colombier };
1569a747e4fSDavid du Colombier
1579a747e4fSDavid du Colombier int
pppoehdr(uchar * pkt,int code,int sessid)1589a747e4fSDavid du Colombier pppoehdr(uchar *pkt, int code, int sessid)
1599a747e4fSDavid du Colombier {
1609a747e4fSDavid du Colombier Pppoehdr *ph;
1619a747e4fSDavid du Colombier
1629a747e4fSDavid du Colombier ph = (Pppoehdr*)pkt;
1639a747e4fSDavid du Colombier ph->verstype = VersType;
1649a747e4fSDavid du Colombier ph->code = code;
1659a747e4fSDavid du Colombier hnputs(ph->sessid, sessid);
1669a747e4fSDavid du Colombier return PppoeHdrSz;
1679a747e4fSDavid du Colombier }
1689a747e4fSDavid du Colombier
1699a747e4fSDavid du Colombier typedef struct Taghdr Taghdr;
1709a747e4fSDavid du Colombier struct Taghdr {
1719a747e4fSDavid du Colombier uchar type[2];
1729a747e4fSDavid du Colombier uchar length[2]; /* of value */
1739a747e4fSDavid du Colombier };
1749a747e4fSDavid du Colombier
1759a747e4fSDavid du Colombier enum {
1769a747e4fSDavid du Colombier TagEnd = 0x0000, /* end of tag list */
1779a747e4fSDavid du Colombier TagSrvName = 0x0101, /* service name */
1789a747e4fSDavid du Colombier TagAcName = 0x0102, /* access concentrator name */
1799a747e4fSDavid du Colombier TagHostUniq = 0x0103, /* nonce */
1809a747e4fSDavid du Colombier TagAcCookie = 0x0104, /* a.c. cookie */
1819a747e4fSDavid du Colombier TagVendSpec = 0x0105, /* vendor specific */
1829a747e4fSDavid du Colombier TagRelaySessId = 0x0110, /* relay session id */
1839a747e4fSDavid du Colombier TagSrvNameErr = 0x0201, /* service name error (ascii) */
1849a747e4fSDavid du Colombier TagAcSysErr = 0x0202, /* a.c. system error */
1859a747e4fSDavid du Colombier };
1869a747e4fSDavid du Colombier
1879a747e4fSDavid du Colombier int
tag(uchar * pkt,int type,void * value,int nvalue)1889a747e4fSDavid du Colombier tag(uchar *pkt, int type, void *value, int nvalue)
1899a747e4fSDavid du Colombier {
1909a747e4fSDavid du Colombier Taghdr *h;
1919a747e4fSDavid du Colombier
1929a747e4fSDavid du Colombier h = (Taghdr*)pkt;
1939a747e4fSDavid du Colombier hnputs(h->type, type);
1949a747e4fSDavid du Colombier hnputs(h->length, nvalue);
1959a747e4fSDavid du Colombier memmove(pkt+4, value, nvalue);
1969a747e4fSDavid du Colombier return 4+nvalue;
1979a747e4fSDavid du Colombier }
1989a747e4fSDavid du Colombier
1999a747e4fSDavid du Colombier /* PPPoE Active Discovery Initiation */
2009a747e4fSDavid du Colombier int
padi(uchar * pkt)2019a747e4fSDavid du Colombier padi(uchar *pkt)
2029a747e4fSDavid du Colombier {
2039a747e4fSDavid du Colombier int sz, tagoff;
2049a747e4fSDavid du Colombier uchar *length;
2059a747e4fSDavid du Colombier
2069a747e4fSDavid du Colombier sz = 0;
2079a747e4fSDavid du Colombier sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery);
2089a747e4fSDavid du Colombier sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000);
2099a747e4fSDavid du Colombier length = pkt+sz-2;
2109a747e4fSDavid du Colombier tagoff = sz;
2119a747e4fSDavid du Colombier sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
2129a747e4fSDavid du Colombier hnputs(length, sz-tagoff);
2139a747e4fSDavid du Colombier return sz;
2149a747e4fSDavid du Colombier }
2159a747e4fSDavid du Colombier
2169a747e4fSDavid du Colombier /* PPPoE Active Discovery Request */
2179a747e4fSDavid du Colombier int
padr(uchar * pkt)2189a747e4fSDavid du Colombier padr(uchar *pkt)
2199a747e4fSDavid du Colombier {
2209a747e4fSDavid du Colombier int sz, tagoff;
2219a747e4fSDavid du Colombier uchar *length;
2229a747e4fSDavid du Colombier
2239a747e4fSDavid du Colombier sz = 0;
2249a747e4fSDavid du Colombier sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery);
2259a747e4fSDavid du Colombier sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000);
2269a747e4fSDavid du Colombier length = pkt+sz-2;
2279a747e4fSDavid du Colombier tagoff = sz;
2289a747e4fSDavid du Colombier sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
2299a747e4fSDavid du Colombier sz += tag(pkt+sz, TagAcName, acname, strlen(acname));
2309a747e4fSDavid du Colombier if(cookie)
2319a747e4fSDavid du Colombier sz += tag(pkt+sz, TagAcCookie, cookie, cookielen);
2329a747e4fSDavid du Colombier hnputs(length, sz-tagoff);
2339a747e4fSDavid du Colombier return sz;
2349a747e4fSDavid du Colombier }
2359a747e4fSDavid du Colombier
2369a747e4fSDavid du Colombier void
ewrite(int fd,void * buf,int nbuf)2379a747e4fSDavid du Colombier ewrite(int fd, void *buf, int nbuf)
2389a747e4fSDavid du Colombier {
2399a747e4fSDavid du Colombier char e[ERRMAX], path[64];
2409a747e4fSDavid du Colombier
2419a747e4fSDavid du Colombier if(write(fd, buf, nbuf) != nbuf){
2429a747e4fSDavid du Colombier rerrstr(e, sizeof e);
2439a747e4fSDavid du Colombier strcpy(path, "unknown");
2449a747e4fSDavid du Colombier fd2path(fd, path, sizeof path);
2459a747e4fSDavid du Colombier sysfatal("write %d to %s: %s", nbuf, path, e);
2469a747e4fSDavid du Colombier }
2479a747e4fSDavid du Colombier }
2489a747e4fSDavid du Colombier
2499a747e4fSDavid du Colombier void*
emalloc(long n)2509a747e4fSDavid du Colombier emalloc(long n)
2519a747e4fSDavid du Colombier {
2529a747e4fSDavid du Colombier void *v;
2539a747e4fSDavid du Colombier
2549a747e4fSDavid du Colombier v = malloc(n);
2559a747e4fSDavid du Colombier if(v == nil)
2569a747e4fSDavid du Colombier sysfatal("out of memory");
2579a747e4fSDavid du Colombier return v;
2589a747e4fSDavid du Colombier }
2599a747e4fSDavid du Colombier
2609a747e4fSDavid du Colombier int
aread(int timeout,int fd,void * buf,int nbuf)2619a747e4fSDavid du Colombier aread(int timeout, int fd, void *buf, int nbuf)
2629a747e4fSDavid du Colombier {
2639a747e4fSDavid du Colombier int n;
2649a747e4fSDavid du Colombier
2659a747e4fSDavid du Colombier alarmed = 0;
2669a747e4fSDavid du Colombier alarm(timeout);
2679a747e4fSDavid du Colombier n = read(fd, buf, nbuf);
2689a747e4fSDavid du Colombier alarm(0);
2699a747e4fSDavid du Colombier if(alarmed)
2709a747e4fSDavid du Colombier return -1;
2719a747e4fSDavid du Colombier if(n < 0)
2729a747e4fSDavid du Colombier sysfatal("read: %r");
2739a747e4fSDavid du Colombier if(n == 0)
2749a747e4fSDavid du Colombier sysfatal("short read");
2759a747e4fSDavid du Colombier return n;
2769a747e4fSDavid du Colombier }
2779a747e4fSDavid du Colombier
2789a747e4fSDavid du Colombier int
pktread(int timeout,int fd,void * buf,int nbuf,int (* want)(uchar *))2799a747e4fSDavid du Colombier pktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uchar*))
2809a747e4fSDavid du Colombier {
2819a747e4fSDavid du Colombier int n, t2;
2829a747e4fSDavid du Colombier n = -1;
2839a747e4fSDavid du Colombier for(t2=timeout; t2<16000; t2*=2){
2849a747e4fSDavid du Colombier while((n = aread(t2, fd, buf, nbuf)) > 0){
2859a747e4fSDavid du Colombier if(malformed(buf, n, EtherPppoeDiscovery)){
2869a747e4fSDavid du Colombier if(debug)
2879a747e4fSDavid du Colombier fprint(2, "dropping pkt: %r\n");
2889a747e4fSDavid du Colombier continue;
2899a747e4fSDavid du Colombier }
2909a747e4fSDavid du Colombier if(debug)
2919a747e4fSDavid du Colombier dumppkt(buf);
2929a747e4fSDavid du Colombier if(!want(buf)){
2939a747e4fSDavid du Colombier if(debug)
2949a747e4fSDavid du Colombier fprint(2, "dropping unwanted pkt: %r\n");
2959a747e4fSDavid du Colombier continue;
2969a747e4fSDavid du Colombier }
2979a747e4fSDavid du Colombier break;
2989a747e4fSDavid du Colombier }
2999a747e4fSDavid du Colombier if(n > 0)
3009a747e4fSDavid du Colombier break;
3019a747e4fSDavid du Colombier }
3029a747e4fSDavid du Colombier return n;
3039a747e4fSDavid du Colombier }
3049a747e4fSDavid du Colombier
3059a747e4fSDavid du Colombier int
bad(char * reason)3069a747e4fSDavid du Colombier bad(char *reason)
3079a747e4fSDavid du Colombier {
3089a747e4fSDavid du Colombier werrstr(reason);
3099a747e4fSDavid du Colombier return 0;
3109a747e4fSDavid du Colombier }
3119a747e4fSDavid du Colombier
3129a747e4fSDavid du Colombier void*
copy(uchar * s,int len)3139a747e4fSDavid du Colombier copy(uchar *s, int len)
3149a747e4fSDavid du Colombier {
3159a747e4fSDavid du Colombier uchar *v;
3169a747e4fSDavid du Colombier
3179a747e4fSDavid du Colombier v = emalloc(len+1);
3189a747e4fSDavid du Colombier memmove(v, s, len);
3199a747e4fSDavid du Colombier v[len] = '\0';
3209a747e4fSDavid du Colombier return v;
3219a747e4fSDavid du Colombier }
3229a747e4fSDavid du Colombier
3239a747e4fSDavid du Colombier void
clearstate(void)3249a747e4fSDavid du Colombier clearstate(void)
3259a747e4fSDavid du Colombier {
3269a747e4fSDavid du Colombier sessid = -1;
3279a747e4fSDavid du Colombier free(acname);
3289a747e4fSDavid du Colombier acname = nil;
3299a747e4fSDavid du Colombier free(cookie);
3309a747e4fSDavid du Colombier cookie = nil;
3319a747e4fSDavid du Colombier }
3329a747e4fSDavid du Colombier
3339a747e4fSDavid du Colombier int
wantoffer(uchar * pkt)3349a747e4fSDavid du Colombier wantoffer(uchar *pkt)
3359a747e4fSDavid du Colombier {
3369a747e4fSDavid du Colombier int i, len;
3379a747e4fSDavid du Colombier uchar *s;
3389a747e4fSDavid du Colombier Etherhdr *eh;
3399a747e4fSDavid du Colombier Pppoehdr *ph;
3409a747e4fSDavid du Colombier
3419a747e4fSDavid du Colombier eh = (Etherhdr*)pkt;
3429a747e4fSDavid du Colombier ph = (Pppoehdr*)(pkt+EtherHdrSz);
3439a747e4fSDavid du Colombier
3449a747e4fSDavid du Colombier if(ph->code != CodeDiscOffer)
3459a747e4fSDavid du Colombier return bad("not an offer");
3469a747e4fSDavid du Colombier if(nhgets(ph->sessid) != 0x0000)
3479a747e4fSDavid du Colombier return bad("bad session id");
3489a747e4fSDavid du Colombier
3499a747e4fSDavid du Colombier for(i=0;; i++){
3509a747e4fSDavid du Colombier if((s = findtag(pkt, TagSrvName, &len, i)) == nil)
3519a747e4fSDavid du Colombier return bad("no matching service name");
3529a747e4fSDavid du Colombier if(len == strlen(srvname) && memcmp(s, srvname, len) == 0)
3539a747e4fSDavid du Colombier break;
3549a747e4fSDavid du Colombier }
3559a747e4fSDavid du Colombier
3569a747e4fSDavid du Colombier if((s = findtag(pkt, TagAcName, &len, 0)) == nil)
3579a747e4fSDavid du Colombier return bad("no ac name");
3589a747e4fSDavid du Colombier acname = copy(s, len);
3599a747e4fSDavid du Colombier if(wantac && strcmp(acname, wantac) != 0){
3609a747e4fSDavid du Colombier free(acname);
3619a747e4fSDavid du Colombier return bad("wrong ac name");
3629a747e4fSDavid du Colombier }
3639a747e4fSDavid du Colombier
3649a747e4fSDavid du Colombier if(s = findtag(pkt, TagAcCookie, &len, 0)){
3659a747e4fSDavid du Colombier cookie = copy(s, len);
3669a747e4fSDavid du Colombier cookielen = len;
3679a747e4fSDavid du Colombier }
3689a747e4fSDavid du Colombier memmove(etherdst, eh->src, sizeof etherdst);
3699a747e4fSDavid du Colombier return 1;
3709a747e4fSDavid du Colombier }
3719a747e4fSDavid du Colombier
3729a747e4fSDavid du Colombier int
wantsession(uchar * pkt)3739a747e4fSDavid du Colombier wantsession(uchar *pkt)
3749a747e4fSDavid du Colombier {
3759a747e4fSDavid du Colombier int len;
3769a747e4fSDavid du Colombier uchar *s;
3779a747e4fSDavid du Colombier Pppoehdr *ph;
3789a747e4fSDavid du Colombier
3799a747e4fSDavid du Colombier ph = (Pppoehdr*)(pkt+EtherHdrSz);
3809a747e4fSDavid du Colombier
3819a747e4fSDavid du Colombier if(ph->code != CodeDiscSess)
3829a747e4fSDavid du Colombier return bad("not a session confirmation");
3839a747e4fSDavid du Colombier if(nhgets(ph->sessid) == 0x0000)
3849a747e4fSDavid du Colombier return bad("bad session id");
3859a747e4fSDavid du Colombier if(findtag(pkt, TagSrvName, &len, 0) == nil)
3869a747e4fSDavid du Colombier return bad("no service name");
3879a747e4fSDavid du Colombier if(findtag(pkt, TagSrvNameErr, &len, 0))
3889a747e4fSDavid du Colombier return bad("service name error");
3899a747e4fSDavid du Colombier if(findtag(pkt, TagAcSysErr, &len, 0))
3909a747e4fSDavid du Colombier return bad("ac system error");
3919a747e4fSDavid du Colombier
392*e9b54818SDavid du Colombier /*
393*e9b54818SDavid du Colombier * rsc said: ``if there is no -S option given, the current code
394*e9b54818SDavid du Colombier * waits for an offer with service name == "".
395*e9b54818SDavid du Colombier * that's silly. it should take the first one it gets.''
396*e9b54818SDavid du Colombier */
397*e9b54818SDavid du Colombier if(srvname[0] != '\0') {
3989a747e4fSDavid du Colombier if((s = findtag(pkt, TagSrvName, &len, 0)) == nil)
3999a747e4fSDavid du Colombier return bad("no matching service name");
4009a747e4fSDavid du Colombier if(len != strlen(srvname) || memcmp(s, srvname, len) != 0)
4019a747e4fSDavid du Colombier return bad("no matching service name");
402*e9b54818SDavid du Colombier }
4039a747e4fSDavid du Colombier sessid = nhgets(ph->sessid);
4049a747e4fSDavid du Colombier return 1;
4059a747e4fSDavid du Colombier }
4069a747e4fSDavid du Colombier
4079a747e4fSDavid du Colombier int
pppoe(char * ether)4089a747e4fSDavid du Colombier pppoe(char *ether)
4099a747e4fSDavid du Colombier {
4109a747e4fSDavid du Colombier char buf[64];
4119a747e4fSDavid du Colombier uchar pkt[1520];
4129a747e4fSDavid du Colombier int dfd, p[2], n, sfd, sz, timeout;
4139a747e4fSDavid du Colombier Pppoehdr *ph;
4149a747e4fSDavid du Colombier
4159a747e4fSDavid du Colombier ph = (Pppoehdr*)(pkt+EtherHdrSz);
4169a747e4fSDavid du Colombier snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery);
4179a747e4fSDavid du Colombier if((dfd = dial(buf, nil, nil, nil)) < 0)
4189a747e4fSDavid du Colombier sysfatal("dial %s: %r", buf);
4199a747e4fSDavid du Colombier
4209a747e4fSDavid du Colombier snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession);
4219a747e4fSDavid du Colombier if((sfd = dial(buf, nil, nil, nil)) < 0)
4229a747e4fSDavid du Colombier sysfatal("dial %s: %r", buf);
4239a747e4fSDavid du Colombier
4249a747e4fSDavid du Colombier for(timeout=250; timeout<16000; timeout*=2){
4259a747e4fSDavid du Colombier clearstate();
4269a747e4fSDavid du Colombier memset(pkt, 0, sizeof pkt);
4279a747e4fSDavid du Colombier sz = padi(pkt);
4289a747e4fSDavid du Colombier if(debug)
4299a747e4fSDavid du Colombier dumppkt(pkt);
4309a747e4fSDavid du Colombier if(sz < EtherMintu)
4319a747e4fSDavid du Colombier sz = EtherMintu;
4329a747e4fSDavid du Colombier ewrite(dfd, pkt, sz);
4339a747e4fSDavid du Colombier
4349a747e4fSDavid du Colombier if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0)
4359a747e4fSDavid du Colombier continue;
4369a747e4fSDavid du Colombier
4379a747e4fSDavid du Colombier memset(pkt, 0, sizeof pkt);
4389a747e4fSDavid du Colombier sz = padr(pkt);
4399a747e4fSDavid du Colombier if(debug)
4409a747e4fSDavid du Colombier dumppkt(pkt);
4419a747e4fSDavid du Colombier if(sz < EtherMintu)
4429a747e4fSDavid du Colombier sz = EtherMintu;
4439a747e4fSDavid du Colombier ewrite(dfd, pkt, sz);
4449a747e4fSDavid du Colombier
4459a747e4fSDavid du Colombier if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0)
4469a747e4fSDavid du Colombier continue;
4479a747e4fSDavid du Colombier
4489a747e4fSDavid du Colombier break;
4499a747e4fSDavid du Colombier }
4509a747e4fSDavid du Colombier if(sessid < 0)
4519a747e4fSDavid du Colombier sysfatal("could not establish session");
4529a747e4fSDavid du Colombier
4539a747e4fSDavid du Colombier rfork(RFNOTEG);
4549a747e4fSDavid du Colombier if(pipe(p) < 0)
4559a747e4fSDavid du Colombier sysfatal("pipe: %r");
4569a747e4fSDavid du Colombier
4579a747e4fSDavid du Colombier switch(fork()){
4589a747e4fSDavid du Colombier case -1:
4599a747e4fSDavid du Colombier sysfatal("fork: %r");
4609a747e4fSDavid du Colombier default:
4619a747e4fSDavid du Colombier break;
4629a747e4fSDavid du Colombier case 0:
4639a747e4fSDavid du Colombier close(p[1]);
4649a747e4fSDavid du Colombier while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){
4659a747e4fSDavid du Colombier etherhdr(pkt, etherdst, EtherPppoeSession);
4669a747e4fSDavid du Colombier pppoehdr(pkt+EtherHdrSz, 0x00, sessid);
4679a747e4fSDavid du Colombier hnputs(pkt+Hdr-2, n);
4689a747e4fSDavid du Colombier sz = Hdr+n;
4699a747e4fSDavid du Colombier if(debug > 1){
4709a747e4fSDavid du Colombier dumppkt(pkt);
4719a747e4fSDavid du Colombier hexdump(pkt, sz);
4729a747e4fSDavid du Colombier }
4739a747e4fSDavid du Colombier if(sz < EtherMintu)
4749a747e4fSDavid du Colombier sz = EtherMintu;
4759a747e4fSDavid du Colombier if(write(sfd, pkt, sz) < 0){
4769a747e4fSDavid du Colombier if(debug)
4779a747e4fSDavid du Colombier fprint(2, "write to ether failed: %r");
4789a747e4fSDavid du Colombier _exits(nil);
4799a747e4fSDavid du Colombier }
4809a747e4fSDavid du Colombier }
4819a747e4fSDavid du Colombier _exits(nil);
4829a747e4fSDavid du Colombier }
4839a747e4fSDavid du Colombier
4849a747e4fSDavid du Colombier switch(fork()){
4859a747e4fSDavid du Colombier case -1:
4869a747e4fSDavid du Colombier sysfatal("fork: %r");
4879a747e4fSDavid du Colombier default:
4889a747e4fSDavid du Colombier break;
4899a747e4fSDavid du Colombier case 0:
4909a747e4fSDavid du Colombier close(p[1]);
4919a747e4fSDavid du Colombier while((n = read(sfd, pkt, sizeof pkt)) > 0){
4929a747e4fSDavid du Colombier if(malformed(pkt, n, EtherPppoeSession)
4939a747e4fSDavid du Colombier || ph->code != 0x00 || nhgets(ph->sessid) != sessid){
4949a747e4fSDavid du Colombier if(debug)
4959a747e4fSDavid du Colombier fprint(2, "malformed session pkt: %r\n");
4969a747e4fSDavid du Colombier if(debug)
4979a747e4fSDavid du Colombier dumppkt(pkt);
4989a747e4fSDavid du Colombier continue;
4999a747e4fSDavid du Colombier }
5009a747e4fSDavid du Colombier if(write(p[0], pkt+Hdr, nhgets(ph->length)) < 0){
5019a747e4fSDavid du Colombier if(debug)
5029a747e4fSDavid du Colombier fprint(2, "write to ppp failed: %r\n");
5039a747e4fSDavid du Colombier _exits(nil);
5049a747e4fSDavid du Colombier }
5059a747e4fSDavid du Colombier }
5069a747e4fSDavid du Colombier _exits(nil);
5079a747e4fSDavid du Colombier }
5089a747e4fSDavid du Colombier close(p[0]);
5099a747e4fSDavid du Colombier return p[1];
5109a747e4fSDavid du Colombier }
5119a747e4fSDavid du Colombier
5129a747e4fSDavid du Colombier void
execppp(int fd)513d9306527SDavid du Colombier execppp(int fd)
5149a747e4fSDavid du Colombier {
5159a747e4fSDavid du Colombier char *argv[16];
5169a747e4fSDavid du Colombier int argc;
5179a747e4fSDavid du Colombier char smtu[10];
5189a747e4fSDavid du Colombier
5199a747e4fSDavid du Colombier argc = 0;
5209a747e4fSDavid du Colombier argv[argc++] = pppname;
5219a747e4fSDavid du Colombier snprint(smtu, sizeof(smtu), "-m%d", mtu);
5229a747e4fSDavid du Colombier argv[argc++] = smtu;
5239a747e4fSDavid du Colombier argv[argc++] = "-F";
5249a747e4fSDavid du Colombier if(debug)
5259a747e4fSDavid du Colombier argv[argc++] = "-d";
5269a747e4fSDavid du Colombier if(primary)
5279a747e4fSDavid du Colombier argv[argc++] = "-P";
5289a747e4fSDavid du Colombier if(pppnetmtpt){
5299a747e4fSDavid du Colombier argv[argc++] = "-x";
5309a747e4fSDavid du Colombier argv[argc++] = pppnetmtpt;
5319a747e4fSDavid du Colombier }
532312a1df1SDavid du Colombier if(keyspec){
533bedadc12SDavid du Colombier argv[argc++] = "-k";
534312a1df1SDavid du Colombier argv[argc++] = keyspec;
5359a747e4fSDavid du Colombier }
5369a747e4fSDavid du Colombier argv[argc] = nil;
5379a747e4fSDavid du Colombier
5389a747e4fSDavid du Colombier dup(fd, 0);
5399a747e4fSDavid du Colombier dup(fd, 1);
5409a747e4fSDavid du Colombier exec(pppname, argv);
5419a747e4fSDavid du Colombier sysfatal("exec: %r");
5429a747e4fSDavid du Colombier }
5439a747e4fSDavid du Colombier
5449a747e4fSDavid du Colombier uchar*
findtag(uchar * pkt,int tagtype,int * plen,int skip)5459a747e4fSDavid du Colombier findtag(uchar *pkt, int tagtype, int *plen, int skip)
5469a747e4fSDavid du Colombier {
5479a747e4fSDavid du Colombier int len, sz, totlen;
5489a747e4fSDavid du Colombier uchar *tagdat, *v;
5499a747e4fSDavid du Colombier Etherhdr *eh;
5509a747e4fSDavid du Colombier Pppoehdr *ph;
5519a747e4fSDavid du Colombier Taghdr *t;
5529a747e4fSDavid du Colombier
5539a747e4fSDavid du Colombier eh = (Etherhdr*)pkt;
5549a747e4fSDavid du Colombier ph = (Pppoehdr*)(pkt+EtherHdrSz);
5559a747e4fSDavid du Colombier tagdat = pkt+Hdr;
5569a747e4fSDavid du Colombier
5579a747e4fSDavid du Colombier if(nhgets(eh->type) != EtherPppoeDiscovery)
5589a747e4fSDavid du Colombier return nil;
5599a747e4fSDavid du Colombier totlen = nhgets(ph->length);
5609a747e4fSDavid du Colombier
5619a747e4fSDavid du Colombier sz = 0;
5629a747e4fSDavid du Colombier while(sz+4 <= totlen){
5639a747e4fSDavid du Colombier t = (Taghdr*)(tagdat+sz);
5649a747e4fSDavid du Colombier v = tagdat+sz+4;
5659a747e4fSDavid du Colombier len = nhgets(t->length);
5669a747e4fSDavid du Colombier if(sz+4+len > totlen)
5679a747e4fSDavid du Colombier break;
5689a747e4fSDavid du Colombier if(nhgets(t->type) == tagtype && skip-- == 0){
5699a747e4fSDavid du Colombier *plen = len;
5709a747e4fSDavid du Colombier return v;
5719a747e4fSDavid du Colombier }
5729a747e4fSDavid du Colombier sz += 2+2+len;
5739a747e4fSDavid du Colombier }
5749a747e4fSDavid du Colombier return nil;
5759a747e4fSDavid du Colombier }
5769a747e4fSDavid du Colombier
5779a747e4fSDavid du Colombier void
dumptags(uchar * tagdat,int ntagdat)5789a747e4fSDavid du Colombier dumptags(uchar *tagdat, int ntagdat)
5799a747e4fSDavid du Colombier {
5809a747e4fSDavid du Colombier int i,len, sz;
5819a747e4fSDavid du Colombier uchar *v;
5829a747e4fSDavid du Colombier Taghdr *t;
5839a747e4fSDavid du Colombier
5849a747e4fSDavid du Colombier sz = 0;
5859a747e4fSDavid du Colombier while(sz+4 <= ntagdat){
5869a747e4fSDavid du Colombier t = (Taghdr*)(tagdat+sz);
5879a747e4fSDavid du Colombier v = tagdat+sz+2+2;
5889a747e4fSDavid du Colombier len = nhgets(t->length);
5899a747e4fSDavid du Colombier if(sz+4+len > ntagdat)
5909a747e4fSDavid du Colombier break;
5919a747e4fSDavid du Colombier fprint(2, "\t0x%x %d: ", nhgets(t->type), len);
5929a747e4fSDavid du Colombier switch(nhgets(t->type)){
5939a747e4fSDavid du Colombier case TagEnd:
5949a747e4fSDavid du Colombier fprint(2, "end of tag list\n");
5959a747e4fSDavid du Colombier break;
5969a747e4fSDavid du Colombier case TagSrvName:
5979a747e4fSDavid du Colombier fprint(2, "service '%.*s'\n", len, (char*)v);
5989a747e4fSDavid du Colombier break;
5999a747e4fSDavid du Colombier case TagAcName:
6009a747e4fSDavid du Colombier fprint(2, "ac '%.*s'\n", len, (char*)v);
6019a747e4fSDavid du Colombier break;
6029a747e4fSDavid du Colombier case TagHostUniq:
6039a747e4fSDavid du Colombier fprint(2, "nonce ");
6049a747e4fSDavid du Colombier Hex:
6059a747e4fSDavid du Colombier for(i=0; i<len; i++)
6069a747e4fSDavid du Colombier fprint(2, "%.2ux", v[i]);
6079a747e4fSDavid du Colombier fprint(2, "\n");
6089a747e4fSDavid du Colombier break;
6099a747e4fSDavid du Colombier case TagAcCookie:
6109a747e4fSDavid du Colombier fprint(2, "ac cookie ");
6119a747e4fSDavid du Colombier goto Hex;
6129a747e4fSDavid du Colombier case TagVendSpec:
6139a747e4fSDavid du Colombier fprint(2, "vend spec ");
6149a747e4fSDavid du Colombier goto Hex;
6159a747e4fSDavid du Colombier case TagRelaySessId:
6169a747e4fSDavid du Colombier fprint(2, "relay ");
6179a747e4fSDavid du Colombier goto Hex;
6189a747e4fSDavid du Colombier case TagSrvNameErr:
6199a747e4fSDavid du Colombier fprint(2, "srverr '%.*s'\n", len, (char*)v);
6209a747e4fSDavid du Colombier break;
6219a747e4fSDavid du Colombier case TagAcSysErr:
6229a747e4fSDavid du Colombier fprint(2, "syserr '%.*s'\n", len, (char*)v);
6239a747e4fSDavid du Colombier break;
6249a747e4fSDavid du Colombier }
6259a747e4fSDavid du Colombier sz += 2+2+len;
6269a747e4fSDavid du Colombier }
6279a747e4fSDavid du Colombier if(sz != ntagdat)
6289a747e4fSDavid du Colombier fprint(2, "warning: only dumped %d of %d bytes\n", sz, ntagdat);
6299a747e4fSDavid du Colombier }
6309a747e4fSDavid du Colombier
6319a747e4fSDavid du Colombier void
dumppkt(uchar * pkt)6329a747e4fSDavid du Colombier dumppkt(uchar *pkt)
6339a747e4fSDavid du Colombier {
6349a747e4fSDavid du Colombier int et;
6359a747e4fSDavid du Colombier Etherhdr *eh;
6369a747e4fSDavid du Colombier Pppoehdr *ph;
6379a747e4fSDavid du Colombier
6389a747e4fSDavid du Colombier eh = (Etherhdr*)pkt;
6399a747e4fSDavid du Colombier ph = (Pppoehdr*)(pkt+EtherHdrSz);
6409a747e4fSDavid du Colombier et = nhgets(eh->type);
6419a747e4fSDavid du Colombier
6429a747e4fSDavid du Colombier fprint(2, "%E -> %E type 0x%x\n",
6439a747e4fSDavid du Colombier eh->src, eh->dst, et);
6449a747e4fSDavid du Colombier switch(et){
6459a747e4fSDavid du Colombier case EtherPppoeDiscovery:
6469a747e4fSDavid du Colombier case EtherPppoeSession:
6479a747e4fSDavid du Colombier fprint(2, "\tvers %d type %d code 0x%x sessid 0x%x length %d\n",
6489a747e4fSDavid du Colombier ph->verstype>>4, ph->verstype&15,
6499a747e4fSDavid du Colombier ph->code, nhgets(ph->sessid), nhgets(ph->length));
6509a747e4fSDavid du Colombier if(et == EtherPppoeDiscovery)
6519a747e4fSDavid du Colombier dumptags(pkt+Hdr, nhgets(ph->length));
6529a747e4fSDavid du Colombier }
6539a747e4fSDavid du Colombier }
6549a747e4fSDavid du Colombier
6559a747e4fSDavid du Colombier int
malformed(uchar * pkt,int n,int wantet)6569a747e4fSDavid du Colombier malformed(uchar *pkt, int n, int wantet)
6579a747e4fSDavid du Colombier {
6589a747e4fSDavid du Colombier int et;
6599a747e4fSDavid du Colombier Etherhdr *eh;
6609a747e4fSDavid du Colombier Pppoehdr *ph;
6619a747e4fSDavid du Colombier
6629a747e4fSDavid du Colombier eh = (Etherhdr*)pkt;
6639a747e4fSDavid du Colombier ph = (Pppoehdr*)(pkt+EtherHdrSz);
6649a747e4fSDavid du Colombier
6659a747e4fSDavid du Colombier if(n < Hdr || n < Hdr+nhgets(ph->length)){
6669a747e4fSDavid du Colombier werrstr("packet too short %d != %d", n, Hdr+nhgets(ph->length));
6679a747e4fSDavid du Colombier return 1;
6689a747e4fSDavid du Colombier }
6699a747e4fSDavid du Colombier
6709a747e4fSDavid du Colombier et = nhgets(eh->type);
6719a747e4fSDavid du Colombier if(et != wantet){
6729a747e4fSDavid du Colombier werrstr("wrong ethernet packet type 0x%x != 0x%x", et, wantet);
6739a747e4fSDavid du Colombier return 1;
6749a747e4fSDavid du Colombier }
6759a747e4fSDavid du Colombier
6769a747e4fSDavid du Colombier return 0;
6779a747e4fSDavid du Colombier }
6789a747e4fSDavid du Colombier
6799a747e4fSDavid du Colombier void
hexdump(uchar * a,int na)6809a747e4fSDavid du Colombier hexdump(uchar *a, int na)
6819a747e4fSDavid du Colombier {
6829a747e4fSDavid du Colombier int i;
6839a747e4fSDavid du Colombier char buf[80];
6849a747e4fSDavid du Colombier
6859a747e4fSDavid du Colombier buf[0] = '\0';
6869a747e4fSDavid du Colombier for(i=0; i<na; i++){
6879a747e4fSDavid du Colombier sprint(buf+strlen(buf), " %.2ux", a[i]);
6889a747e4fSDavid du Colombier if(i%16 == 7)
6899a747e4fSDavid du Colombier sprint(buf+strlen(buf), " --");
6909a747e4fSDavid du Colombier if(i%16==15){
6919a747e4fSDavid du Colombier sprint(buf+strlen(buf), "\n");
6929a747e4fSDavid du Colombier write(2, buf, strlen(buf));
6939a747e4fSDavid du Colombier buf[0] = 0;
6949a747e4fSDavid du Colombier }
6959a747e4fSDavid du Colombier }
6969a747e4fSDavid du Colombier if(i%16){
6979a747e4fSDavid du Colombier sprint(buf+strlen(buf), "\n");
6989a747e4fSDavid du Colombier write(2, buf, strlen(buf));
6999a747e4fSDavid du Colombier }
7009a747e4fSDavid du Colombier }
701