vx32

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

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 }