pppoe.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. /*
  10. * User-level PPP over Ethernet (PPPoE) client.
  11. * See RFC 2516
  12. */
  13. #include <u.h>
  14. #include <libc.h>
  15. #include <ip.h>
  16. void dumppkt(uint8_t*);
  17. uint8_t *findtag(uint8_t*, int, int*, int);
  18. void hexdump(uint8_t*, int);
  19. int malformed(uint8_t*, int, int);
  20. int pppoe(char*);
  21. void execppp(int);
  22. int alarmed;
  23. int debug;
  24. int sessid;
  25. char *keyspec;
  26. int primary;
  27. char *pppnetmtpt;
  28. char *acname;
  29. char *pppname = "/bin/ip/ppp";
  30. char *srvname = "";
  31. char *wantac;
  32. uint8_t *cookie;
  33. int cookielen;
  34. uint8_t etherdst[6];
  35. int mtu = 1492;
  36. void
  37. usage(void)
  38. {
  39. fprint(2, "usage: pppoe [-Pd] [-A acname] [-S srvname] [-k keyspec] [-m mtu] [-x pppnet] [ether0]\n");
  40. exits("usage");
  41. }
  42. int
  43. catchalarm(void *a, char *msg)
  44. {
  45. USED(a);
  46. if(strstr(msg, "alarm")){
  47. alarmed = 1;
  48. return 1;
  49. }
  50. if(debug)
  51. fprint(2, "note rcved: %s\n", msg);
  52. return 0;
  53. }
  54. void
  55. main(int argc, char **argv)
  56. {
  57. int fd;
  58. char *dev;
  59. ARGBEGIN{
  60. case 'A':
  61. wantac = EARGF(usage());
  62. break;
  63. case 'P':
  64. primary = 1;
  65. break;
  66. case 'S':
  67. srvname = EARGF(usage());
  68. break;
  69. case 'd':
  70. debug++;
  71. break;
  72. case 'm':
  73. mtu = atoi(EARGF(usage()));
  74. break;
  75. case 'k':
  76. keyspec = EARGF(usage());
  77. break;
  78. case 'x':
  79. pppnetmtpt = EARGF(usage());
  80. break;
  81. default:
  82. usage();
  83. }ARGEND
  84. switch(argc){
  85. default:
  86. usage();
  87. case 0:
  88. dev = "ether0";
  89. break;
  90. case 1:
  91. dev = argv[0];
  92. break;
  93. }
  94. fmtinstall('E', eipfmt);
  95. atnotify(catchalarm, 1);
  96. fd = pppoe(dev);
  97. execppp(fd);
  98. }
  99. typedef struct Etherhdr Etherhdr;
  100. struct Etherhdr {
  101. uint8_t dst[6];
  102. uint8_t src[6];
  103. uint8_t type[2];
  104. };
  105. enum {
  106. EtherHdrSz = 6+6+2,
  107. EtherMintu = 60,
  108. EtherPppoeDiscovery = 0x8863,
  109. EtherPppoeSession = 0x8864,
  110. };
  111. uint8_t etherbcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
  112. int
  113. etherhdr(uint8_t *pkt, uint8_t *dst, int type)
  114. {
  115. Etherhdr *eh;
  116. eh = (Etherhdr*)pkt;
  117. memmove(eh->dst, dst, sizeof(eh->dst));
  118. hnputs(eh->type, type);
  119. return EtherHdrSz;
  120. }
  121. typedef struct Pppoehdr Pppoehdr;
  122. struct Pppoehdr {
  123. uint8_t verstype;
  124. uint8_t code;
  125. uint8_t sessid[2];
  126. uint8_t length[2]; /* of payload */
  127. };
  128. enum {
  129. PppoeHdrSz = 1+1+2+2,
  130. Hdr = EtherHdrSz+PppoeHdrSz,
  131. };
  132. enum {
  133. VersType = 0x11,
  134. /* Discovery codes */
  135. CodeDiscInit = 0x09, /* discovery init */
  136. CodeDiscOffer = 0x07, /* discovery offer */
  137. CodeDiscReq = 0x19, /* discovery request */
  138. CodeDiscSess = 0x65, /* session confirmation */
  139. /* Session codes */
  140. CodeSession = 0x00,
  141. };
  142. int
  143. pppoehdr(uint8_t *pkt, int code, int sessid)
  144. {
  145. Pppoehdr *ph;
  146. ph = (Pppoehdr*)pkt;
  147. ph->verstype = VersType;
  148. ph->code = code;
  149. hnputs(ph->sessid, sessid);
  150. return PppoeHdrSz;
  151. }
  152. typedef struct Taghdr Taghdr;
  153. struct Taghdr {
  154. uint8_t type[2];
  155. uint8_t length[2]; /* of value */
  156. };
  157. enum {
  158. TagEnd = 0x0000, /* end of tag list */
  159. TagSrvName = 0x0101, /* service name */
  160. TagAcName = 0x0102, /* access concentrator name */
  161. TagHostUniq = 0x0103, /* nonce */
  162. TagAcCookie = 0x0104, /* a.c. cookie */
  163. TagVendSpec = 0x0105, /* vendor specific */
  164. TagRelaySessId = 0x0110, /* relay session id */
  165. TagSrvNameErr = 0x0201, /* service name error (ascii) */
  166. TagAcSysErr = 0x0202, /* a.c. system error */
  167. };
  168. int
  169. tag(uint8_t *pkt, int type, void *value, int nvalue)
  170. {
  171. Taghdr *h;
  172. h = (Taghdr*)pkt;
  173. hnputs(h->type, type);
  174. hnputs(h->length, nvalue);
  175. memmove(pkt+4, value, nvalue);
  176. return 4+nvalue;
  177. }
  178. /* PPPoE Active Discovery Initiation */
  179. int
  180. padi(uint8_t *pkt)
  181. {
  182. int sz, tagoff;
  183. uint8_t *length;
  184. sz = 0;
  185. sz += etherhdr(pkt+sz, etherbcast, EtherPppoeDiscovery);
  186. sz += pppoehdr(pkt+sz, CodeDiscInit, 0x0000);
  187. length = pkt+sz-2;
  188. tagoff = sz;
  189. sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
  190. hnputs(length, sz-tagoff);
  191. return sz;
  192. }
  193. /* PPPoE Active Discovery Request */
  194. int
  195. padr(uint8_t *pkt)
  196. {
  197. int sz, tagoff;
  198. uint8_t *length;
  199. sz = 0;
  200. sz += etherhdr(pkt+sz, etherdst, EtherPppoeDiscovery);
  201. sz += pppoehdr(pkt+sz, CodeDiscReq, 0x0000);
  202. length = pkt+sz-2;
  203. tagoff = sz;
  204. sz += tag(pkt+sz, TagSrvName, srvname, strlen(srvname));
  205. sz += tag(pkt+sz, TagAcName, acname, strlen(acname));
  206. if(cookie)
  207. sz += tag(pkt+sz, TagAcCookie, cookie, cookielen);
  208. hnputs(length, sz-tagoff);
  209. return sz;
  210. }
  211. void
  212. ewrite(int fd, void *buf, int nbuf)
  213. {
  214. char e[ERRMAX], path[64];
  215. if(write(fd, buf, nbuf) != nbuf){
  216. rerrstr(e, sizeof e);
  217. strcpy(path, "unknown");
  218. fd2path(fd, path, sizeof path);
  219. sysfatal("write %d to %s: %s", nbuf, path, e);
  220. }
  221. }
  222. void*
  223. emalloc(int32_t n)
  224. {
  225. void *v;
  226. v = malloc(n);
  227. if(v == nil)
  228. sysfatal("out of memory");
  229. return v;
  230. }
  231. int
  232. aread(int timeout, int fd, void *buf, int nbuf)
  233. {
  234. int n;
  235. alarmed = 0;
  236. alarm(timeout);
  237. n = read(fd, buf, nbuf);
  238. alarm(0);
  239. if(alarmed)
  240. return -1;
  241. if(n < 0)
  242. sysfatal("read: %r");
  243. if(n == 0)
  244. sysfatal("short read");
  245. return n;
  246. }
  247. int
  248. pktread(int timeout, int fd, void *buf, int nbuf, int (*want)(uint8_t*))
  249. {
  250. int n, t2;
  251. n = -1;
  252. for(t2=timeout; t2<16000; t2*=2){
  253. while((n = aread(t2, fd, buf, nbuf)) > 0){
  254. if(malformed(buf, n, EtherPppoeDiscovery)){
  255. if(debug)
  256. fprint(2, "dropping pkt: %r\n");
  257. continue;
  258. }
  259. if(debug)
  260. dumppkt(buf);
  261. if(!want(buf)){
  262. if(debug)
  263. fprint(2, "dropping unwanted pkt: %r\n");
  264. continue;
  265. }
  266. break;
  267. }
  268. if(n > 0)
  269. break;
  270. }
  271. return n;
  272. }
  273. int
  274. bad(char *reason)
  275. {
  276. werrstr(reason);
  277. return 0;
  278. }
  279. void*
  280. copy(uint8_t *s, int len)
  281. {
  282. uint8_t *v;
  283. v = emalloc(len+1);
  284. memmove(v, s, len);
  285. v[len] = '\0';
  286. return v;
  287. }
  288. void
  289. clearstate(void)
  290. {
  291. sessid = -1;
  292. free(acname);
  293. acname = nil;
  294. free(cookie);
  295. cookie = nil;
  296. }
  297. int
  298. wantoffer(uint8_t *pkt)
  299. {
  300. int i, len;
  301. uint8_t *s;
  302. Etherhdr *eh;
  303. Pppoehdr *ph;
  304. eh = (Etherhdr*)pkt;
  305. ph = (Pppoehdr*)(pkt+EtherHdrSz);
  306. if(ph->code != CodeDiscOffer)
  307. return bad("not an offer");
  308. if(nhgets(ph->sessid) != 0x0000)
  309. return bad("bad session id");
  310. for(i=0;; i++){
  311. if((s = findtag(pkt, TagSrvName, &len, i)) == nil)
  312. return bad("no matching service name");
  313. if(len == strlen(srvname) && memcmp(s, srvname, len) == 0)
  314. break;
  315. }
  316. if((s = findtag(pkt, TagAcName, &len, 0)) == nil)
  317. return bad("no ac name");
  318. acname = copy(s, len);
  319. if(wantac && strcmp(acname, wantac) != 0){
  320. free(acname);
  321. return bad("wrong ac name");
  322. }
  323. if(s = findtag(pkt, TagAcCookie, &len, 0)){
  324. cookie = copy(s, len);
  325. cookielen = len;
  326. }
  327. memmove(etherdst, eh->src, sizeof etherdst);
  328. return 1;
  329. }
  330. int
  331. wantsession(uint8_t *pkt)
  332. {
  333. int len;
  334. uint8_t *s;
  335. Pppoehdr *ph;
  336. ph = (Pppoehdr*)(pkt+EtherHdrSz);
  337. if(ph->code != CodeDiscSess)
  338. return bad("not a session confirmation");
  339. if(nhgets(ph->sessid) == 0x0000)
  340. return bad("bad session id");
  341. if(findtag(pkt, TagSrvName, &len, 0) == nil)
  342. return bad("no service name");
  343. if(findtag(pkt, TagSrvNameErr, &len, 0))
  344. return bad("service name error");
  345. if(findtag(pkt, TagAcSysErr, &len, 0))
  346. return bad("ac system error");
  347. /*
  348. * rsc said: ``if there is no -S option given, the current code
  349. * waits for an offer with service name == "".
  350. * that's silly. it should take the first one it gets.''
  351. */
  352. if(srvname[0] != '\0') {
  353. if((s = findtag(pkt, TagSrvName, &len, 0)) == nil)
  354. return bad("no matching service name");
  355. if(len != strlen(srvname) || memcmp(s, srvname, len) != 0)
  356. return bad("no matching service name");
  357. }
  358. sessid = nhgets(ph->sessid);
  359. return 1;
  360. }
  361. int
  362. pppoe(char *ether)
  363. {
  364. char buf[64];
  365. uint8_t pkt[1520];
  366. int dfd, p[2], n, sfd, sz, timeout;
  367. Pppoehdr *ph;
  368. ph = (Pppoehdr*)(pkt+EtherHdrSz);
  369. snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeDiscovery);
  370. if((dfd = dial(buf, nil, nil, nil)) < 0)
  371. sysfatal("dial %s: %r", buf);
  372. snprint(buf, sizeof buf, "%s!%d", ether, EtherPppoeSession);
  373. if((sfd = dial(buf, nil, nil, nil)) < 0)
  374. sysfatal("dial %s: %r", buf);
  375. for(timeout=250; timeout<16000; timeout*=2){
  376. clearstate();
  377. memset(pkt, 0, sizeof pkt);
  378. sz = padi(pkt);
  379. if(debug)
  380. dumppkt(pkt);
  381. if(sz < EtherMintu)
  382. sz = EtherMintu;
  383. ewrite(dfd, pkt, sz);
  384. if(pktread(timeout, dfd, pkt, sizeof pkt, wantoffer) < 0)
  385. continue;
  386. memset(pkt, 0, sizeof pkt);
  387. sz = padr(pkt);
  388. if(debug)
  389. dumppkt(pkt);
  390. if(sz < EtherMintu)
  391. sz = EtherMintu;
  392. ewrite(dfd, pkt, sz);
  393. if(pktread(timeout, dfd, pkt, sizeof pkt, wantsession) < 0)
  394. continue;
  395. break;
  396. }
  397. if(sessid < 0)
  398. sysfatal("could not establish session");
  399. rfork(RFNOTEG);
  400. if(pipe(p) < 0)
  401. sysfatal("pipe: %r");
  402. switch(fork()){
  403. case -1:
  404. sysfatal("fork: %r");
  405. default:
  406. break;
  407. case 0:
  408. close(p[1]);
  409. while((n = read(p[0], pkt+Hdr, sizeof pkt-Hdr)) > 0){
  410. etherhdr(pkt, etherdst, EtherPppoeSession);
  411. pppoehdr(pkt+EtherHdrSz, 0x00, sessid);
  412. hnputs(pkt+Hdr-2, n);
  413. sz = Hdr+n;
  414. if(debug > 1){
  415. dumppkt(pkt);
  416. hexdump(pkt, sz);
  417. }
  418. if(sz < EtherMintu)
  419. sz = EtherMintu;
  420. if(write(sfd, pkt, sz) < 0){
  421. if(debug)
  422. fprint(2, "write to ether failed: %r");
  423. _exits(nil);
  424. }
  425. }
  426. _exits(nil);
  427. }
  428. switch(fork()){
  429. case -1:
  430. sysfatal("fork: %r");
  431. default:
  432. break;
  433. case 0:
  434. close(p[1]);
  435. while((n = read(sfd, pkt, sizeof pkt)) > 0){
  436. if(malformed(pkt, n, EtherPppoeSession)
  437. || ph->code != 0x00 || nhgets(ph->sessid) != sessid){
  438. if(debug)
  439. fprint(2, "malformed session pkt: %r\n");
  440. if(debug)
  441. dumppkt(pkt);
  442. continue;
  443. }
  444. if(write(p[0], pkt+Hdr, nhgets(ph->length)) < 0){
  445. if(debug)
  446. fprint(2, "write to ppp failed: %r\n");
  447. _exits(nil);
  448. }
  449. }
  450. _exits(nil);
  451. }
  452. close(p[0]);
  453. return p[1];
  454. }
  455. void
  456. execppp(int fd)
  457. {
  458. char *argv[16];
  459. int argc;
  460. char smtu[10];
  461. argc = 0;
  462. argv[argc++] = pppname;
  463. snprint(smtu, sizeof(smtu), "-m%d", mtu);
  464. argv[argc++] = smtu;
  465. argv[argc++] = "-F";
  466. if(debug)
  467. argv[argc++] = "-d";
  468. if(primary)
  469. argv[argc++] = "-P";
  470. if(pppnetmtpt){
  471. argv[argc++] = "-x";
  472. argv[argc++] = pppnetmtpt;
  473. }
  474. if(keyspec){
  475. argv[argc++] = "-k";
  476. argv[argc++] = keyspec;
  477. }
  478. argv[argc] = nil;
  479. dup(fd, 0);
  480. dup(fd, 1);
  481. exec(pppname, argv);
  482. sysfatal("exec: %r");
  483. }
  484. uint8_t*
  485. findtag(uint8_t *pkt, int tagtype, int *plen, int skip)
  486. {
  487. int len, sz, totlen;
  488. uint8_t *tagdat, *v;
  489. Etherhdr *eh;
  490. Pppoehdr *ph;
  491. Taghdr *t;
  492. eh = (Etherhdr*)pkt;
  493. ph = (Pppoehdr*)(pkt+EtherHdrSz);
  494. tagdat = pkt+Hdr;
  495. if(nhgets(eh->type) != EtherPppoeDiscovery)
  496. return nil;
  497. totlen = nhgets(ph->length);
  498. sz = 0;
  499. while(sz+4 <= totlen){
  500. t = (Taghdr*)(tagdat+sz);
  501. v = tagdat+sz+4;
  502. len = nhgets(t->length);
  503. if(sz+4+len > totlen)
  504. break;
  505. if(nhgets(t->type) == tagtype && skip-- == 0){
  506. *plen = len;
  507. return v;
  508. }
  509. sz += 2+2+len;
  510. }
  511. return nil;
  512. }
  513. void
  514. dumptags(uint8_t *tagdat, int ntagdat)
  515. {
  516. int i,len, sz;
  517. uint8_t *v;
  518. Taghdr *t;
  519. sz = 0;
  520. while(sz+4 <= ntagdat){
  521. t = (Taghdr*)(tagdat+sz);
  522. v = tagdat+sz+2+2;
  523. len = nhgets(t->length);
  524. if(sz+4+len > ntagdat)
  525. break;
  526. fprint(2, "\t0x%x %d: ", nhgets(t->type), len);
  527. switch(nhgets(t->type)){
  528. case TagEnd:
  529. fprint(2, "end of tag list\n");
  530. break;
  531. case TagSrvName:
  532. fprint(2, "service '%.*s'\n", len, (char*)v);
  533. break;
  534. case TagAcName:
  535. fprint(2, "ac '%.*s'\n", len, (char*)v);
  536. break;
  537. case TagHostUniq:
  538. fprint(2, "nonce ");
  539. Hex:
  540. for(i=0; i<len; i++)
  541. fprint(2, "%.2ux", v[i]);
  542. fprint(2, "\n");
  543. break;
  544. case TagAcCookie:
  545. fprint(2, "ac cookie ");
  546. goto Hex;
  547. case TagVendSpec:
  548. fprint(2, "vend spec ");
  549. goto Hex;
  550. case TagRelaySessId:
  551. fprint(2, "relay ");
  552. goto Hex;
  553. case TagSrvNameErr:
  554. fprint(2, "srverr '%.*s'\n", len, (char*)v);
  555. break;
  556. case TagAcSysErr:
  557. fprint(2, "syserr '%.*s'\n", len, (char*)v);
  558. break;
  559. }
  560. sz += 2+2+len;
  561. }
  562. if(sz != ntagdat)
  563. fprint(2, "warning: only dumped %d of %d bytes\n", sz, ntagdat);
  564. }
  565. void
  566. dumppkt(uint8_t *pkt)
  567. {
  568. int et;
  569. Etherhdr *eh;
  570. Pppoehdr *ph;
  571. eh = (Etherhdr*)pkt;
  572. ph = (Pppoehdr*)(pkt+EtherHdrSz);
  573. et = nhgets(eh->type);
  574. fprint(2, "%E -> %E type 0x%x\n",
  575. eh->src, eh->dst, et);
  576. switch(et){
  577. case EtherPppoeDiscovery:
  578. case EtherPppoeSession:
  579. fprint(2, "\tvers %d type %d code 0x%x sessid 0x%x length %d\n",
  580. ph->verstype>>4, ph->verstype&15,
  581. ph->code, nhgets(ph->sessid), nhgets(ph->length));
  582. if(et == EtherPppoeDiscovery)
  583. dumptags(pkt+Hdr, nhgets(ph->length));
  584. }
  585. }
  586. int
  587. malformed(uint8_t *pkt, int n, int wantet)
  588. {
  589. int et;
  590. Etherhdr *eh;
  591. Pppoehdr *ph;
  592. eh = (Etherhdr*)pkt;
  593. ph = (Pppoehdr*)(pkt+EtherHdrSz);
  594. if(n < Hdr || n < Hdr+nhgets(ph->length)){
  595. werrstr("packet too short %d != %d", n, Hdr+nhgets(ph->length));
  596. return 1;
  597. }
  598. et = nhgets(eh->type);
  599. if(et != wantet){
  600. werrstr("wrong ethernet packet type 0x%x != 0x%x", et, wantet);
  601. return 1;
  602. }
  603. return 0;
  604. }
  605. void
  606. hexdump(uint8_t *a, int na)
  607. {
  608. int i;
  609. char buf[80];
  610. buf[0] = '\0';
  611. for(i=0; i<na; i++){
  612. sprint(buf+strlen(buf), " %.2ux", a[i]);
  613. if(i%16 == 7)
  614. sprint(buf+strlen(buf), " --");
  615. if(i%16==15){
  616. sprint(buf+strlen(buf), "\n");
  617. write(2, buf, strlen(buf));
  618. buf[0] = 0;
  619. }
  620. }
  621. if(i%16){
  622. sprint(buf+strlen(buf), "\n");
  623. write(2, buf, strlen(buf));
  624. }
  625. }