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