vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

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 }