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