ipv6.c 13 KB

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