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