ipv6.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  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. #include "u.h"
  10. #include "../port/lib.h"
  11. #include "mem.h"
  12. #include "dat.h"
  13. #include "fns.h"
  14. #include "../port/error.h"
  15. #include "ip.h"
  16. #include "ipv6.h"
  17. enum
  18. {
  19. IP6FHDR = 8, /* sizeof(Fraghdr6) */
  20. };
  21. #define IPV6CLASS(hdr) (((hdr)->vcf[0]&0x0F)<<2 | ((hdr)->vcf[1]&0xF0)>>2)
  22. #define BLKIPVER(xp) (((Ip6hdr*)((xp)->rp))->vcf[0] & 0xF0)
  23. /*
  24. * This sleazy macro is stolen shamelessly from ip.c, see comment there.
  25. */
  26. #define BKFG(xp) ((Ipfrag*)((xp)->base))
  27. Block* ip6reassemble(IP*, int, Block*, Ip6hdr*);
  28. Fragment6* ipfragallo6(IP*);
  29. void ipfragfree6(IP*, Fragment6*);
  30. Block* procopts(Block *bp);
  31. static Block* procxtns(IP *ip, Block *bp, int doreasm);
  32. int unfraglen(Block *bp, uint8_t *nexthdr, int setfh);
  33. int
  34. ipoput6(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
  35. {
  36. Proc *up = externup();
  37. int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff;
  38. int morefrags, blklen, rv = 0, tentative;
  39. uint8_t *gate, nexthdr;
  40. Block *xp, *nb;
  41. Fraghdr6 fraghdr;
  42. IP *ip;
  43. Ip6hdr *eh;
  44. Ipifc *ifc;
  45. Route *r, *sr;
  46. ip = f->ip;
  47. /* Fill out the ip header */
  48. eh = (Ip6hdr*)(bp->rp);
  49. ip->stats[OutRequests]++;
  50. /* Number of uint8_ts in data and ip header to write */
  51. len = blocklen(bp);
  52. tentative = iptentative(f, eh->src);
  53. if(tentative){
  54. netlog(f, Logip, "reject tx of packet with tentative src address %I\n",
  55. eh->src);
  56. goto free;
  57. }
  58. if(gating){
  59. chunk = nhgets(eh->ploadlen);
  60. if(chunk > len){
  61. ip->stats[OutDiscards]++;
  62. netlog(f, Logip, "short gated packet\n");
  63. goto free;
  64. }
  65. if(chunk + IP6HDR < len)
  66. len = chunk + IP6HDR;
  67. }
  68. if(len >= IP_MAX){
  69. ip->stats[OutDiscards]++;
  70. netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
  71. goto free;
  72. }
  73. r = v6lookup(f, eh->dst, c);
  74. if(r == nil){
  75. // print("no route for %I, src %I free\n", eh->dst, eh->src);
  76. ip->stats[OutNoRoutes]++;
  77. netlog(f, Logip, "no interface %I\n", eh->dst);
  78. rv = -1;
  79. goto free;
  80. }
  81. ifc = r->RouteTree.ifc;
  82. if(r->RouteTree.type & (Rifc|Runi))
  83. gate = eh->dst;
  84. else if(r->RouteTree.type & (Rbcast|Rmulti)) {
  85. gate = eh->dst;
  86. sr = v6lookup(f, eh->src, nil);
  87. if(sr && (sr->RouteTree.type & Runi))
  88. ifc = sr->RouteTree.ifc;
  89. }
  90. else
  91. gate = r->v6.gate;
  92. if(!gating)
  93. eh->vcf[0] = IP_VER6;
  94. eh->ttl = ttl;
  95. if(!gating) {
  96. eh->vcf[0] |= tos >> 4;
  97. eh->vcf[1] = tos << 4;
  98. }
  99. if(!canrlock(&ifc->rwl))
  100. goto free;
  101. if(waserror()){
  102. runlock(&ifc->rwl);
  103. nexterror();
  104. }
  105. if(ifc->medium == nil)
  106. goto raise;
  107. /* If we dont need to fragment just send it */
  108. medialen = ifc->maxtu - ifc->medium->hsize;
  109. if(len <= medialen) {
  110. hnputs(eh->ploadlen, len - IP6HDR);
  111. ifc->medium->bwrite(ifc, bp, V6, gate);
  112. runlock(&ifc->rwl);
  113. poperror();
  114. return 0;
  115. }
  116. if(gating && ifc->reassemble <= 0) {
  117. /*
  118. * v6 intermediate nodes are not supposed to fragment pkts;
  119. * we fragment if ifc->reassemble is turned on; an exception
  120. * needed for nat.
  121. */
  122. ip->stats[OutDiscards]++;
  123. icmppkttoobig6(f, ifc, bp);
  124. netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
  125. goto raise;
  126. }
  127. /* start v6 fragmentation */
  128. uflen = unfraglen(bp, &nexthdr, 1);
  129. if(uflen > medialen) {
  130. ip->stats[FragFails]++;
  131. ip->stats[OutDiscards]++;
  132. netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
  133. goto raise;
  134. }
  135. flen = len - uflen;
  136. seglen = (medialen - (uflen + IP6FHDR)) & ~7;
  137. if(seglen < 8) {
  138. ip->stats[FragFails]++;
  139. ip->stats[OutDiscards]++;
  140. netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
  141. goto raise;
  142. }
  143. lid = incref(&ip->id6);
  144. fraghdr.nexthdr = nexthdr;
  145. fraghdr.res = 0;
  146. hnputl(fraghdr.id, lid);
  147. xp = bp;
  148. offset = uflen;
  149. while (xp && offset && offset >= BLEN(xp)) {
  150. offset -= BLEN(xp);
  151. xp = xp->next;
  152. }
  153. xp->rp += offset;
  154. fragoff = 0;
  155. morefrags = 1;
  156. for(; fragoff < flen; fragoff += seglen) {
  157. nb = allocb(uflen + IP6FHDR + seglen);
  158. if(fragoff + seglen >= flen) {
  159. seglen = flen - fragoff;
  160. morefrags = 0;
  161. }
  162. hnputs(eh->ploadlen, seglen+IP6FHDR);
  163. memmove(nb->wp, eh, uflen);
  164. nb->wp += uflen;
  165. hnputs(fraghdr.offsetRM, fragoff); /* last 3 bits must be 0 */
  166. fraghdr.offsetRM[1] |= morefrags;
  167. memmove(nb->wp, &fraghdr, IP6FHDR);
  168. nb->wp += IP6FHDR;
  169. /* Copy data */
  170. chunk = seglen;
  171. while (chunk) {
  172. if(!xp) {
  173. ip->stats[OutDiscards]++;
  174. ip->stats[FragFails]++;
  175. freeblist(nb);
  176. netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
  177. goto raise;
  178. }
  179. blklen = chunk;
  180. if(BLEN(xp) < chunk)
  181. blklen = BLEN(xp);
  182. memmove(nb->wp, xp->rp, blklen);
  183. nb->wp += blklen;
  184. xp->rp += blklen;
  185. chunk -= blklen;
  186. if(xp->rp == xp->wp)
  187. xp = xp->next;
  188. }
  189. ifc->medium->bwrite(ifc, nb, V6, gate);
  190. ip->stats[FragCreates]++;
  191. }
  192. ip->stats[FragOKs]++;
  193. raise:
  194. runlock(&ifc->rwl);
  195. poperror();
  196. free:
  197. freeblist(bp);
  198. return rv;
  199. }
  200. void
  201. ipiput6(Fs *f, Ipifc *ifc, Block *bp)
  202. {
  203. int hl, hop, tos, notforme, tentative;
  204. uint8_t proto;
  205. uint8_t v6dst[IPaddrlen];
  206. IP *ip;
  207. Ip6hdr *h;
  208. Proto *p;
  209. Route *r, *sr;
  210. ip = f->ip;
  211. ip->stats[InReceives]++;
  212. /*
  213. * Ensure we have all the header info in the first
  214. * block. Make life easier for other protocols by
  215. * collecting up to the first 64 bytes in the first block.
  216. */
  217. if(BLEN(bp) < 64) {
  218. hl = blocklen(bp);
  219. if(hl < IP6HDR)
  220. hl = IP6HDR;
  221. if(hl > 64)
  222. hl = 64;
  223. bp = pullupblock(bp, hl);
  224. if(bp == nil)
  225. return;
  226. }
  227. h = (Ip6hdr *)bp->rp;
  228. memmove(&v6dst[0], &h->dst[0], IPaddrlen);
  229. notforme = ipforme(f, v6dst) == 0;
  230. tentative = iptentative(f, v6dst);
  231. if(tentative && h->proto != ICMPv6) {
  232. print("ipv6 non-icmp tentative addr %I, drop\n", v6dst);
  233. freeblist(bp);
  234. return;
  235. }
  236. /* Check header version */
  237. if(BLKIPVER(bp) != IP_VER6) {
  238. ip->stats[InHdrErrors]++;
  239. netlog(f, Logip, "ip: bad version %x\n", (h->vcf[0]&0xF0)>>2);
  240. freeblist(bp);
  241. return;
  242. }
  243. /* route */
  244. if(notforme) {
  245. if(!ip->iprouting){
  246. freeblist(bp);
  247. return;
  248. }
  249. /* don't forward to link-local destinations */
  250. if(islinklocal(h->dst) ||
  251. (isv6mcast(h->dst) && (h->dst[1]&0xF) <= Link_local_scop)){
  252. ip->stats[OutDiscards]++;
  253. freeblist(bp);
  254. return;
  255. }
  256. /* don't forward to source's network */
  257. sr = v6lookup(f, h->src, nil);
  258. r = v6lookup(f, h->dst, nil);
  259. if(r == nil || sr == r){
  260. ip->stats[OutDiscards]++;
  261. freeblist(bp);
  262. return;
  263. }
  264. /* don't forward if packet has timed out */
  265. hop = h->ttl;
  266. if(hop < 1) {
  267. ip->stats[InHdrErrors]++;
  268. icmpttlexceeded6(f, ifc, bp);
  269. freeblist(bp);
  270. return;
  271. }
  272. /* process headers & reassemble if the interface expects it */
  273. bp = procxtns(ip, bp, r->RouteTree.ifc->reassemble);
  274. if(bp == nil)
  275. return;
  276. ip->stats[ForwDatagrams]++;
  277. h = (Ip6hdr *)bp->rp;
  278. tos = IPV6CLASS(h);
  279. hop = h->ttl;
  280. ipoput6(f, bp, 1, hop-1, tos, nil);
  281. return;
  282. }
  283. /* reassemble & process headers if needed */
  284. bp = procxtns(ip, bp, 1);
  285. if(bp == nil)
  286. return;
  287. h = (Ip6hdr *) (bp->rp);
  288. proto = h->proto;
  289. p = Fsrcvpcol(f, proto);
  290. if(p && p->rcv) {
  291. ip->stats[InDelivers]++;
  292. (*p->rcv)(p, ifc, bp);
  293. return;
  294. }
  295. ip->stats[InDiscards]++;
  296. ip->stats[InUnknownProtos]++;
  297. freeblist(bp);
  298. }
  299. /*
  300. * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
  301. */
  302. void
  303. ipfragfree6(IP *ip, Fragment6 *frag)
  304. {
  305. Fragment6 *fl, **l;
  306. if(frag->blist)
  307. freeblist(frag->blist);
  308. memset(frag->src, 0, IPaddrlen);
  309. frag->id = 0;
  310. frag->blist = nil;
  311. l = &ip->flisthead6;
  312. for(fl = *l; fl; fl = fl->next) {
  313. if(fl == frag) {
  314. *l = frag->next;
  315. break;
  316. }
  317. l = &fl->next;
  318. }
  319. frag->next = ip->fragfree6;
  320. ip->fragfree6 = frag;
  321. }
  322. /*
  323. * ipfragallo6 - copied from ipfragalloc4
  324. */
  325. Fragment6*
  326. ipfragallo6(IP *ip)
  327. {
  328. Fragment6 *f;
  329. while(ip->fragfree6 == nil) {
  330. /* free last entry on fraglist */
  331. for(f = ip->flisthead6; f->next; f = f->next)
  332. ;
  333. ipfragfree6(ip, f);
  334. }
  335. f = ip->fragfree6;
  336. ip->fragfree6 = f->next;
  337. f->next = ip->flisthead6;
  338. ip->flisthead6 = f;
  339. f->age = NOW + 30000;
  340. return f;
  341. }
  342. static Block*
  343. procxtns(IP *ip, Block *bp, int doreasm)
  344. {
  345. int offset;
  346. uint8_t proto;
  347. Ip6hdr *h;
  348. h = (Ip6hdr *)bp->rp;
  349. offset = unfraglen(bp, &proto, 0);
  350. if(proto == FH && doreasm != 0) {
  351. bp = ip6reassemble(ip, offset, bp, h);
  352. if(bp == nil)
  353. return nil;
  354. offset = unfraglen(bp, &proto, 0);
  355. }
  356. if(proto == DOH || offset > IP6HDR)
  357. bp = procopts(bp);
  358. return bp;
  359. }
  360. /*
  361. * returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
  362. * hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
  363. * of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
  364. * field of the last header in the "Unfragmentable part" is set to FH.
  365. */
  366. int
  367. unfraglen(Block *bp, uint8_t *nexthdr, int setfh)
  368. {
  369. uint8_t *p, *q;
  370. int ufl, hs;
  371. p = bp->rp;
  372. q = p+6; /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
  373. *nexthdr = *q;
  374. ufl = IP6HDR;
  375. p += ufl;
  376. while (*nexthdr == HBH || *nexthdr == RH) {
  377. *nexthdr = *p;
  378. hs = ((int)*(p+1) + 1) * 8;
  379. ufl += hs;
  380. q = p;
  381. p += hs;
  382. }
  383. if(*nexthdr == FH)
  384. *q = *p;
  385. if(setfh)
  386. *q = FH;
  387. return ufl;
  388. }
  389. Block*
  390. procopts(Block *bp)
  391. {
  392. return bp;
  393. }
  394. Block*
  395. ip6reassemble(IP* ip, int uflen, Block* bp, Ip6hdr* ih)
  396. {
  397. int fend, offset, ovlap, len, fragsize, pktposn;
  398. uint id;
  399. uint8_t src[IPaddrlen], dst[IPaddrlen];
  400. Block *bl, **l, *last, *prev;
  401. Fraghdr6 *fraghdr;
  402. Fragment6 *f, *fnext;
  403. fraghdr = (Fraghdr6 *)(bp->rp + uflen);
  404. memmove(src, ih->src, IPaddrlen);
  405. memmove(dst, ih->dst, IPaddrlen);
  406. id = nhgetl(fraghdr->id);
  407. offset = nhgets(fraghdr->offsetRM) & ~7;
  408. /*
  409. * block lists are too hard, pullupblock into a single block
  410. */
  411. if(bp->next){
  412. bp = pullupblock(bp, blocklen(bp));
  413. ih = (Ip6hdr *)bp->rp;
  414. }
  415. qlock(&ip->fraglock6);
  416. /*
  417. * find a reassembly queue for this fragment
  418. */
  419. for(f = ip->flisthead6; f; f = fnext){
  420. fnext = f->next;
  421. if(ipcmp(f->src, src)==0 && ipcmp(f->dst, dst)==0 && f->id == id)
  422. break;
  423. if(f->age < NOW){
  424. ip->stats[ReasmTimeout]++;
  425. ipfragfree6(ip, f);
  426. }
  427. }
  428. /*
  429. * if this isn't a fragmented packet, accept it
  430. * and get rid of any fragments that might go
  431. * with it.
  432. */
  433. if(nhgets(fraghdr->offsetRM) == 0) { /* 1st frag is also last */
  434. if(f) {
  435. ipfragfree6(ip, f);
  436. ip->stats[ReasmFails]++;
  437. }
  438. qunlock(&ip->fraglock6);
  439. return bp;
  440. }
  441. if(bp->base+IPFRAGSZ >= bp->rp){
  442. bp = padblock(bp, IPFRAGSZ);
  443. bp->rp += IPFRAGSZ;
  444. }
  445. BKFG(bp)->foff = offset;
  446. BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
  447. /* First fragment allocates a reassembly queue */
  448. if(f == nil) {
  449. f = ipfragallo6(ip);
  450. f->id = id;
  451. memmove(f->src, src, IPaddrlen);
  452. memmove(f->dst, dst, IPaddrlen);
  453. f->blist = bp;
  454. qunlock(&ip->fraglock6);
  455. ip->stats[ReasmReqds]++;
  456. return nil;
  457. }
  458. /*
  459. * find the new fragment's position in the queue
  460. */
  461. prev = nil;
  462. l = &f->blist;
  463. bl = f->blist;
  464. while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
  465. prev = bl;
  466. l = &bl->next;
  467. bl = bl->next;
  468. }
  469. /* Check overlap of a previous fragment - trim away as necessary */
  470. if(prev) {
  471. ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
  472. if(ovlap > 0) {
  473. if(ovlap >= BKFG(bp)->flen) {
  474. freeblist(bp);
  475. qunlock(&ip->fraglock6);
  476. return nil;
  477. }
  478. BKFG(prev)->flen -= ovlap;
  479. }
  480. }
  481. /* Link onto assembly queue */
  482. bp->next = *l;
  483. *l = bp;
  484. /* Check to see if succeeding segments overlap */
  485. if(bp->next) {
  486. l = &bp->next;
  487. fend = BKFG(bp)->foff + BKFG(bp)->flen;
  488. /* Take completely covered segments out */
  489. while(*l) {
  490. ovlap = fend - BKFG(*l)->foff;
  491. if(ovlap <= 0)
  492. break;
  493. if(ovlap < BKFG(*l)->flen) {
  494. BKFG(*l)->flen -= ovlap;
  495. BKFG(*l)->foff += ovlap;
  496. /* move up ih hdrs */
  497. memmove((*l)->rp + ovlap, (*l)->rp, uflen);
  498. (*l)->rp += ovlap;
  499. break;
  500. }
  501. last = (*l)->next;
  502. (*l)->next = nil;
  503. freeblist(*l);
  504. *l = last;
  505. }
  506. }
  507. /*
  508. * look for a complete packet. if we get to a fragment
  509. * with the trailing bit of fraghdr->offsetRM[1] set, we're done.
  510. */
  511. pktposn = 0;
  512. for(bl = f->blist; bl && BKFG(bl)->foff == pktposn; bl = bl->next) {
  513. fraghdr = (Fraghdr6 *)(bl->rp + uflen);
  514. if((fraghdr->offsetRM[1] & 1) == 0) {
  515. bl = f->blist;
  516. /* get rid of frag header in first fragment */
  517. memmove(bl->rp + IP6FHDR, bl->rp, uflen);
  518. bl->rp += IP6FHDR;
  519. len = nhgets(((Ip6hdr*)bl->rp)->ploadlen) - IP6FHDR;
  520. bl->wp = bl->rp + len + IP6HDR;
  521. /*
  522. * Pullup all the fragment headers and
  523. * return a complete packet
  524. */
  525. for(bl = bl->next; bl; bl = bl->next) {
  526. fragsize = BKFG(bl)->flen;
  527. len += fragsize;
  528. bl->rp += uflen + IP6FHDR;
  529. bl->wp = bl->rp + fragsize;
  530. }
  531. bl = f->blist;
  532. f->blist = nil;
  533. ipfragfree6(ip, f);
  534. ih = (Ip6hdr*)bl->rp;
  535. hnputs(ih->ploadlen, len);
  536. qunlock(&ip->fraglock6);
  537. ip->stats[ReasmOKs]++;
  538. return bl;
  539. }
  540. pktposn += BKFG(bl)->flen;
  541. }
  542. qunlock(&ip->fraglock6);
  543. return nil;
  544. }