xref: /plan9-contrib/sys/src/9k/ip/ip.c (revision 55ba833bbc29e52fa60df7522a0e5f7350ace33d)
19ef1f84bSDavid du Colombier #include	"u.h"
29ef1f84bSDavid du Colombier #include	"../port/lib.h"
39ef1f84bSDavid du Colombier #include	"mem.h"
49ef1f84bSDavid du Colombier #include	"dat.h"
59ef1f84bSDavid du Colombier #include	"fns.h"
69ef1f84bSDavid du Colombier #include	"../port/error.h"
79ef1f84bSDavid du Colombier 
89ef1f84bSDavid du Colombier #include	"ip.h"
99ef1f84bSDavid du Colombier 
109ef1f84bSDavid du Colombier #define BLKIPVER(xp)	(((Ip4hdr*)((xp)->rp))->vihl&0xF0)
119ef1f84bSDavid du Colombier 
129ef1f84bSDavid du Colombier static char *statnames[] =
139ef1f84bSDavid du Colombier {
149ef1f84bSDavid du Colombier [Forwarding]	"Forwarding",
159ef1f84bSDavid du Colombier [DefaultTTL]	"DefaultTTL",
169ef1f84bSDavid du Colombier [InReceives]	"InReceives",
179ef1f84bSDavid du Colombier [InHdrErrors]	"InHdrErrors",
189ef1f84bSDavid du Colombier [InAddrErrors]	"InAddrErrors",
199ef1f84bSDavid du Colombier [ForwDatagrams]	"ForwDatagrams",
209ef1f84bSDavid du Colombier [InUnknownProtos]	"InUnknownProtos",
219ef1f84bSDavid du Colombier [InDiscards]	"InDiscards",
229ef1f84bSDavid du Colombier [InDelivers]	"InDelivers",
239ef1f84bSDavid du Colombier [OutRequests]	"OutRequests",
249ef1f84bSDavid du Colombier [OutDiscards]	"OutDiscards",
259ef1f84bSDavid du Colombier [OutNoRoutes]	"OutNoRoutes",
269ef1f84bSDavid du Colombier [ReasmTimeout]	"ReasmTimeout",
279ef1f84bSDavid du Colombier [ReasmReqds]	"ReasmReqds",
289ef1f84bSDavid du Colombier [ReasmOKs]	"ReasmOKs",
299ef1f84bSDavid du Colombier [ReasmFails]	"ReasmFails",
309ef1f84bSDavid du Colombier [FragOKs]	"FragOKs",
319ef1f84bSDavid du Colombier [FragFails]	"FragFails",
329ef1f84bSDavid du Colombier [FragCreates]	"FragCreates",
339ef1f84bSDavid du Colombier };
349ef1f84bSDavid du Colombier 
359ef1f84bSDavid du Colombier #define BLKIP(xp)	((Ip4hdr*)((xp)->rp))
369ef1f84bSDavid du Colombier /*
379ef1f84bSDavid du Colombier  * This sleazy macro relies on the media header size being
389ef1f84bSDavid du Colombier  * larger than sizeof(Ipfrag). ipreassemble checks this is true
399ef1f84bSDavid du Colombier  */
409ef1f84bSDavid du Colombier #define BKFG(xp)	((Ipfrag*)((xp)->base))
419ef1f84bSDavid du Colombier 
429ef1f84bSDavid du Colombier ushort		ipcsum(uchar*);
439ef1f84bSDavid du Colombier Block*		ip4reassemble(IP*, int, Block*, Ip4hdr*);
449ef1f84bSDavid du Colombier void		ipfragfree4(IP*, Fragment4*);
459ef1f84bSDavid du Colombier Fragment4*	ipfragallo4(IP*);
469ef1f84bSDavid du Colombier 
479ef1f84bSDavid du Colombier void
ip_init_6(Fs * f)489ef1f84bSDavid du Colombier ip_init_6(Fs *f)
499ef1f84bSDavid du Colombier {
509ef1f84bSDavid du Colombier 	v6params *v6p;
519ef1f84bSDavid du Colombier 
529ef1f84bSDavid du Colombier 	v6p = smalloc(sizeof(v6params));
539ef1f84bSDavid du Colombier 
549ef1f84bSDavid du Colombier 	v6p->rp.mflag		= 0;		/* default not managed */
559ef1f84bSDavid du Colombier 	v6p->rp.oflag		= 0;
569ef1f84bSDavid du Colombier 	v6p->rp.maxraint	= 600000;	/* millisecs */
579ef1f84bSDavid du Colombier 	v6p->rp.minraint	= 200000;
589ef1f84bSDavid du Colombier 	v6p->rp.linkmtu		= 0;		/* no mtu sent */
599ef1f84bSDavid du Colombier 	v6p->rp.reachtime	= 0;
609ef1f84bSDavid du Colombier 	v6p->rp.rxmitra		= 0;
619ef1f84bSDavid du Colombier 	v6p->rp.ttl		= MAXTTL;
629ef1f84bSDavid du Colombier 	v6p->rp.routerlt	= 3 * v6p->rp.maxraint;
639ef1f84bSDavid du Colombier 
649ef1f84bSDavid du Colombier 	v6p->hp.rxmithost	= 1000;		/* v6 RETRANS_TIMER */
659ef1f84bSDavid du Colombier 
669ef1f84bSDavid du Colombier 	v6p->cdrouter		= -1;
679ef1f84bSDavid du Colombier 
689ef1f84bSDavid du Colombier 	f->v6p			= v6p;
699ef1f84bSDavid du Colombier }
709ef1f84bSDavid du Colombier 
719ef1f84bSDavid du Colombier void
initfrag(IP * ip,int size)729ef1f84bSDavid du Colombier initfrag(IP *ip, int size)
739ef1f84bSDavid du Colombier {
749ef1f84bSDavid du Colombier 	Fragment4 *fq4, *eq4;
759ef1f84bSDavid du Colombier 	Fragment6 *fq6, *eq6;
769ef1f84bSDavid du Colombier 
779ef1f84bSDavid du Colombier 	ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
789ef1f84bSDavid du Colombier 	if(ip->fragfree4 == nil)
799ef1f84bSDavid du Colombier 		panic("initfrag");
809ef1f84bSDavid du Colombier 
819ef1f84bSDavid du Colombier 	eq4 = &ip->fragfree4[size];
829ef1f84bSDavid du Colombier 	for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
839ef1f84bSDavid du Colombier 		fq4->next = fq4+1;
849ef1f84bSDavid du Colombier 
859ef1f84bSDavid du Colombier 	ip->fragfree4[size-1].next = nil;
869ef1f84bSDavid du Colombier 
879ef1f84bSDavid du Colombier 	ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
889ef1f84bSDavid du Colombier 	if(ip->fragfree6 == nil)
899ef1f84bSDavid du Colombier 		panic("initfrag");
909ef1f84bSDavid du Colombier 
919ef1f84bSDavid du Colombier 	eq6 = &ip->fragfree6[size];
929ef1f84bSDavid du Colombier 	for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
939ef1f84bSDavid du Colombier 		fq6->next = fq6+1;
949ef1f84bSDavid du Colombier 
959ef1f84bSDavid du Colombier 	ip->fragfree6[size-1].next = nil;
969ef1f84bSDavid du Colombier }
979ef1f84bSDavid du Colombier 
989ef1f84bSDavid du Colombier void
ip_init(Fs * f)999ef1f84bSDavid du Colombier ip_init(Fs *f)
1009ef1f84bSDavid du Colombier {
1019ef1f84bSDavid du Colombier 	IP *ip;
1029ef1f84bSDavid du Colombier 
1039ef1f84bSDavid du Colombier 	ip = smalloc(sizeof(IP));
1049ef1f84bSDavid du Colombier 	initfrag(ip, 100);
1059ef1f84bSDavid du Colombier 	f->ip = ip;
1069ef1f84bSDavid du Colombier 
1079ef1f84bSDavid du Colombier 	ip_init_6(f);
1089ef1f84bSDavid du Colombier }
1099ef1f84bSDavid du Colombier 
1109ef1f84bSDavid du Colombier void
iprouting(Fs * f,int on)1119ef1f84bSDavid du Colombier iprouting(Fs *f, int on)
1129ef1f84bSDavid du Colombier {
1139ef1f84bSDavid du Colombier 	f->ip->iprouting = on;
1149ef1f84bSDavid du Colombier 	if(f->ip->iprouting==0)
1159ef1f84bSDavid du Colombier 		f->ip->stats[Forwarding] = 2;
1169ef1f84bSDavid du Colombier 	else
1179ef1f84bSDavid du Colombier 		f->ip->stats[Forwarding] = 1;
1189ef1f84bSDavid du Colombier }
1199ef1f84bSDavid du Colombier 
1209ef1f84bSDavid du Colombier int
ipoput4(Fs * f,Block * bp,int gating,int ttl,int tos,Conv * c)1219ef1f84bSDavid du Colombier ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
1229ef1f84bSDavid du Colombier {
1239ef1f84bSDavid du Colombier 	Ipifc *ifc;
1249ef1f84bSDavid du Colombier 	uchar *gate;
1259ef1f84bSDavid du Colombier 	ulong fragoff;
1269ef1f84bSDavid du Colombier 	Block *xp, *nb;
1279ef1f84bSDavid du Colombier 	Ip4hdr *eh, *feh;
1289ef1f84bSDavid du Colombier 	int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
1299ef1f84bSDavid du Colombier 	Route *r, *sr;
1309ef1f84bSDavid du Colombier 	IP *ip;
1319ef1f84bSDavid du Colombier 	int rv = 0;
132*55ba833bSDavid du Colombier 	uchar v4dst[IPv4addrlen];
1339ef1f84bSDavid du Colombier 
1349ef1f84bSDavid du Colombier 	ip = f->ip;
1359ef1f84bSDavid du Colombier 
1369ef1f84bSDavid du Colombier 	/* Fill out the ip header */
1379ef1f84bSDavid du Colombier 	eh = (Ip4hdr*)(bp->rp);
1389ef1f84bSDavid du Colombier 
1399ef1f84bSDavid du Colombier 	ip->stats[OutRequests]++;
1409ef1f84bSDavid du Colombier 
1419ef1f84bSDavid du Colombier 	/* Number of uchars in data and ip header to write */
1429ef1f84bSDavid du Colombier 	len = blocklen(bp);
1439ef1f84bSDavid du Colombier 
1449ef1f84bSDavid du Colombier 	if(gating){
1459ef1f84bSDavid du Colombier 		chunk = nhgets(eh->length);
1469ef1f84bSDavid du Colombier 		if(chunk > len){
1479ef1f84bSDavid du Colombier 			ip->stats[OutDiscards]++;
1489ef1f84bSDavid du Colombier 			netlog(f, Logip, "short gated packet\n");
1499ef1f84bSDavid du Colombier 			goto free;
1509ef1f84bSDavid du Colombier 		}
1519ef1f84bSDavid du Colombier 		if(chunk < len)
1529ef1f84bSDavid du Colombier 			len = chunk;
1539ef1f84bSDavid du Colombier 	}
1549ef1f84bSDavid du Colombier 	if(len >= IP_MAX){
1559ef1f84bSDavid du Colombier 		ip->stats[OutDiscards]++;
1569ef1f84bSDavid du Colombier 		netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
1579ef1f84bSDavid du Colombier 		goto free;
1589ef1f84bSDavid du Colombier 	}
1599ef1f84bSDavid du Colombier 
1609ef1f84bSDavid du Colombier 	r = v4lookup(f, eh->dst, c);
1619ef1f84bSDavid du Colombier 	if(r == nil){
1629ef1f84bSDavid du Colombier 		ip->stats[OutNoRoutes]++;
1639ef1f84bSDavid du Colombier 		netlog(f, Logip, "no interface %V\n", eh->dst);
1649ef1f84bSDavid du Colombier 		rv = -1;
1659ef1f84bSDavid du Colombier 		goto free;
1669ef1f84bSDavid du Colombier 	}
1679ef1f84bSDavid du Colombier 
1689ef1f84bSDavid du Colombier 	ifc = r->ifc;
1699ef1f84bSDavid du Colombier 	if(r->type & (Rifc|Runi))
1709ef1f84bSDavid du Colombier 		gate = eh->dst;
1719ef1f84bSDavid du Colombier 	else
1729ef1f84bSDavid du Colombier 	if(r->type & (Rbcast|Rmulti)) {
173*55ba833bSDavid du Colombier 		if(nhgetl(r->v4.gate) == 0){
174*55ba833bSDavid du Colombier 			hnputl(v4dst, r->v4.address);
175*55ba833bSDavid du Colombier 			gate = v4dst;
176*55ba833bSDavid du Colombier 		}else
1779ef1f84bSDavid du Colombier 			gate = eh->dst;
1789ef1f84bSDavid du Colombier 		sr = v4lookup(f, eh->src, nil);
1799ef1f84bSDavid du Colombier 		if(sr != nil && (sr->type & Runi))
1809ef1f84bSDavid du Colombier 			ifc = sr->ifc;
1819ef1f84bSDavid du Colombier 	}
1829ef1f84bSDavid du Colombier 	else
1839ef1f84bSDavid du Colombier 		gate = r->v4.gate;
1849ef1f84bSDavid du Colombier 
1859ef1f84bSDavid du Colombier 	if(!gating)
1869ef1f84bSDavid du Colombier 		eh->vihl = IP_VER4|IP_HLEN4;
1879ef1f84bSDavid du Colombier 	eh->ttl = ttl;
1889ef1f84bSDavid du Colombier 	if(!gating)
1899ef1f84bSDavid du Colombier 		eh->tos = tos;
1909ef1f84bSDavid du Colombier 
1919ef1f84bSDavid du Colombier 	if(!canrlock(ifc))
1929ef1f84bSDavid du Colombier 		goto free;
1939ef1f84bSDavid du Colombier 	if(waserror()){
1949ef1f84bSDavid du Colombier 		runlock(ifc);
1959ef1f84bSDavid du Colombier 		nexterror();
1969ef1f84bSDavid du Colombier 	}
1979ef1f84bSDavid du Colombier 	if(ifc->medium == nil)
1989ef1f84bSDavid du Colombier 		goto raise;
1999ef1f84bSDavid du Colombier 
2009ef1f84bSDavid du Colombier 	/* If we dont need to fragment just send it */
2019ef1f84bSDavid du Colombier 	if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
2029ef1f84bSDavid du Colombier 		medialen = c->maxfragsize - ifc->medium->hsize;
2039ef1f84bSDavid du Colombier 	else
2049ef1f84bSDavid du Colombier 		medialen = ifc->maxtu - ifc->medium->hsize;
2059ef1f84bSDavid du Colombier 	if(len <= medialen) {
2069ef1f84bSDavid du Colombier 		if(!gating)
2079ef1f84bSDavid du Colombier 			hnputs(eh->id, incref(&ip->id4));
2089ef1f84bSDavid du Colombier 		hnputs(eh->length, len);
2099ef1f84bSDavid du Colombier 		if(!gating){
2109ef1f84bSDavid du Colombier 			eh->frag[0] = 0;
2119ef1f84bSDavid du Colombier 			eh->frag[1] = 0;
2129ef1f84bSDavid du Colombier 		}
2139ef1f84bSDavid du Colombier 		eh->cksum[0] = 0;
2149ef1f84bSDavid du Colombier 		eh->cksum[1] = 0;
2159ef1f84bSDavid du Colombier 		hnputs(eh->cksum, ipcsum(&eh->vihl));
2169ef1f84bSDavid du Colombier 		assert(bp->next == nil);
2179ef1f84bSDavid du Colombier 		ifc->medium->bwrite(ifc, bp, V4, gate);
2189ef1f84bSDavid du Colombier 		runlock(ifc);
2199ef1f84bSDavid du Colombier 		poperror();
2209ef1f84bSDavid du Colombier 		return 0;
2219ef1f84bSDavid du Colombier 	}
2229ef1f84bSDavid du Colombier 
223a3323688SDavid du Colombier 	if((eh->frag[0] & (IP_DF>>8)) && !gating)
224a3323688SDavid du Colombier 		print("%V: DF set\n", eh->dst);
2259ef1f84bSDavid du Colombier 
2269ef1f84bSDavid du Colombier 	if(eh->frag[0] & (IP_DF>>8)){
2279ef1f84bSDavid du Colombier 		ip->stats[FragFails]++;
2289ef1f84bSDavid du Colombier 		ip->stats[OutDiscards]++;
2299ef1f84bSDavid du Colombier 		icmpcantfrag(f, bp, medialen);
2309ef1f84bSDavid du Colombier 		netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
2319ef1f84bSDavid du Colombier 		goto raise;
2329ef1f84bSDavid du Colombier 	}
2339ef1f84bSDavid du Colombier 
2349ef1f84bSDavid du Colombier 	seglen = (medialen - IP4HDR) & ~7;
2359ef1f84bSDavid du Colombier 	if(seglen < 8){
2369ef1f84bSDavid du Colombier 		ip->stats[FragFails]++;
2379ef1f84bSDavid du Colombier 		ip->stats[OutDiscards]++;
2389ef1f84bSDavid du Colombier 		netlog(f, Logip, "%V seglen < 8\n", eh->dst);
2399ef1f84bSDavid du Colombier 		goto raise;
2409ef1f84bSDavid du Colombier 	}
2419ef1f84bSDavid du Colombier 
2429ef1f84bSDavid du Colombier 	dlen = len - IP4HDR;
2439ef1f84bSDavid du Colombier 	xp = bp;
2449ef1f84bSDavid du Colombier 	if(gating)
2459ef1f84bSDavid du Colombier 		lid = nhgets(eh->id);
2469ef1f84bSDavid du Colombier 	else
2479ef1f84bSDavid du Colombier 		lid = incref(&ip->id4);
2489ef1f84bSDavid du Colombier 
2499ef1f84bSDavid du Colombier 	offset = IP4HDR;
2509ef1f84bSDavid du Colombier 	while(xp != nil && offset && offset >= BLEN(xp)) {
2519ef1f84bSDavid du Colombier 		offset -= BLEN(xp);
2529ef1f84bSDavid du Colombier 		xp = xp->next;
2539ef1f84bSDavid du Colombier 	}
2549ef1f84bSDavid du Colombier 	xp->rp += offset;
2559ef1f84bSDavid du Colombier 
2569ef1f84bSDavid du Colombier 	if(gating)
2579ef1f84bSDavid du Colombier 		fragoff = nhgets(eh->frag)<<3;
2589ef1f84bSDavid du Colombier 	else
2599ef1f84bSDavid du Colombier 		fragoff = 0;
2609ef1f84bSDavid du Colombier 	dlen += fragoff;
2619ef1f84bSDavid du Colombier 	for(; fragoff < dlen; fragoff += seglen) {
2629ef1f84bSDavid du Colombier 		nb = allocb(IP4HDR+seglen);
2639ef1f84bSDavid du Colombier 		feh = (Ip4hdr*)(nb->rp);
2649ef1f84bSDavid du Colombier 
2659ef1f84bSDavid du Colombier 		memmove(nb->wp, eh, IP4HDR);
2669ef1f84bSDavid du Colombier 		nb->wp += IP4HDR;
2679ef1f84bSDavid du Colombier 
2689ef1f84bSDavid du Colombier 		if((fragoff + seglen) >= dlen) {
2699ef1f84bSDavid du Colombier 			seglen = dlen - fragoff;
2709ef1f84bSDavid du Colombier 			hnputs(feh->frag, fragoff>>3);
2719ef1f84bSDavid du Colombier 		}
2729ef1f84bSDavid du Colombier 		else
2739ef1f84bSDavid du Colombier 			hnputs(feh->frag, (fragoff>>3)|IP_MF);
2749ef1f84bSDavid du Colombier 
2759ef1f84bSDavid du Colombier 		hnputs(feh->length, seglen + IP4HDR);
2769ef1f84bSDavid du Colombier 		hnputs(feh->id, lid);
2779ef1f84bSDavid du Colombier 
2789ef1f84bSDavid du Colombier 		/* Copy up the data area */
2799ef1f84bSDavid du Colombier 		chunk = seglen;
2809ef1f84bSDavid du Colombier 		while(chunk) {
2819ef1f84bSDavid du Colombier 			if(!xp) {
2829ef1f84bSDavid du Colombier 				ip->stats[OutDiscards]++;
2839ef1f84bSDavid du Colombier 				ip->stats[FragFails]++;
2849ef1f84bSDavid du Colombier 				freeblist(nb);
2859ef1f84bSDavid du Colombier 				netlog(f, Logip, "!xp: chunk %d\n", chunk);
2869ef1f84bSDavid du Colombier 				goto raise;
2879ef1f84bSDavid du Colombier 			}
2889ef1f84bSDavid du Colombier 			blklen = chunk;
2899ef1f84bSDavid du Colombier 			if(BLEN(xp) < chunk)
2909ef1f84bSDavid du Colombier 				blklen = BLEN(xp);
2919ef1f84bSDavid du Colombier 			memmove(nb->wp, xp->rp, blklen);
2929ef1f84bSDavid du Colombier 			nb->wp += blklen;
2939ef1f84bSDavid du Colombier 			xp->rp += blklen;
2949ef1f84bSDavid du Colombier 			chunk -= blklen;
2959ef1f84bSDavid du Colombier 			if(xp->rp == xp->wp)
2969ef1f84bSDavid du Colombier 				xp = xp->next;
2979ef1f84bSDavid du Colombier 		}
2989ef1f84bSDavid du Colombier 
2999ef1f84bSDavid du Colombier 		feh->cksum[0] = 0;
3009ef1f84bSDavid du Colombier 		feh->cksum[1] = 0;
3019ef1f84bSDavid du Colombier 		hnputs(feh->cksum, ipcsum(&feh->vihl));
3029ef1f84bSDavid du Colombier 		ifc->medium->bwrite(ifc, nb, V4, gate);
3039ef1f84bSDavid du Colombier 		ip->stats[FragCreates]++;
3049ef1f84bSDavid du Colombier 	}
3059ef1f84bSDavid du Colombier 	ip->stats[FragOKs]++;
3069ef1f84bSDavid du Colombier raise:
3079ef1f84bSDavid du Colombier 	runlock(ifc);
3089ef1f84bSDavid du Colombier 	poperror();
3099ef1f84bSDavid du Colombier free:
3109ef1f84bSDavid du Colombier 	freeblist(bp);
3119ef1f84bSDavid du Colombier 	return rv;
3129ef1f84bSDavid du Colombier }
3139ef1f84bSDavid du Colombier 
3149ef1f84bSDavid du Colombier void
ipiput4(Fs * f,Ipifc * ifc,Block * bp)3159ef1f84bSDavid du Colombier ipiput4(Fs *f, Ipifc *ifc, Block *bp)
3169ef1f84bSDavid du Colombier {
3179ef1f84bSDavid du Colombier 	int hl;
3189ef1f84bSDavid du Colombier 	int hop, tos, proto, olen;
3199ef1f84bSDavid du Colombier 	Ip4hdr *h;
3209ef1f84bSDavid du Colombier 	Proto *p;
3219ef1f84bSDavid du Colombier 	ushort frag;
3229ef1f84bSDavid du Colombier 	int notforme;
3239ef1f84bSDavid du Colombier 	uchar *dp, v6dst[IPaddrlen];
3249ef1f84bSDavid du Colombier 	IP *ip;
3259ef1f84bSDavid du Colombier 	Route *r;
3269ef1f84bSDavid du Colombier 	Conv conv;
3279ef1f84bSDavid du Colombier 
3289ef1f84bSDavid du Colombier 	if(BLKIPVER(bp) != IP_VER4) {
3299ef1f84bSDavid du Colombier 		ipiput6(f, ifc, bp);
3309ef1f84bSDavid du Colombier 		return;
3319ef1f84bSDavid du Colombier 	}
3329ef1f84bSDavid du Colombier 
3339ef1f84bSDavid du Colombier 	ip = f->ip;
3349ef1f84bSDavid du Colombier 	ip->stats[InReceives]++;
3359ef1f84bSDavid du Colombier 
3369ef1f84bSDavid du Colombier 	/*
3379ef1f84bSDavid du Colombier 	 *  Ensure we have all the header info in the first
3389ef1f84bSDavid du Colombier 	 *  block.  Make life easier for other protocols by
3399ef1f84bSDavid du Colombier 	 *  collecting up to the first 64 bytes in the first block.
3409ef1f84bSDavid du Colombier 	 */
3419ef1f84bSDavid du Colombier 	if(BLEN(bp) < 64) {
3429ef1f84bSDavid du Colombier 		hl = blocklen(bp);
3439ef1f84bSDavid du Colombier 		if(hl < IP4HDR)
3449ef1f84bSDavid du Colombier 			hl = IP4HDR;
3459ef1f84bSDavid du Colombier 		if(hl > 64)
3469ef1f84bSDavid du Colombier 			hl = 64;
3479ef1f84bSDavid du Colombier 		bp = pullupblock(bp, hl);
3489ef1f84bSDavid du Colombier 		if(bp == nil)
3499ef1f84bSDavid du Colombier 			return;
3509ef1f84bSDavid du Colombier 	}
3519ef1f84bSDavid du Colombier 
3529ef1f84bSDavid du Colombier 	h = (Ip4hdr*)(bp->rp);
3539ef1f84bSDavid du Colombier 
3549ef1f84bSDavid du Colombier 	/* dump anything that whose header doesn't checksum */
3559ef1f84bSDavid du Colombier 	if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
3569ef1f84bSDavid du Colombier 		ip->stats[InHdrErrors]++;
3579ef1f84bSDavid du Colombier 		netlog(f, Logip, "ip: checksum error %V\n", h->src);
3589ef1f84bSDavid du Colombier 		freeblist(bp);
3599ef1f84bSDavid du Colombier 		return;
3609ef1f84bSDavid du Colombier 	}
3619ef1f84bSDavid du Colombier 	v4tov6(v6dst, h->dst);
3629ef1f84bSDavid du Colombier 	notforme = ipforme(f, v6dst) == 0;
3639ef1f84bSDavid du Colombier 
3649ef1f84bSDavid du Colombier 	/* Check header length and version */
3659ef1f84bSDavid du Colombier 	if((h->vihl&0x0F) != IP_HLEN4) {
3669ef1f84bSDavid du Colombier 		hl = (h->vihl&0xF)<<2;
3679ef1f84bSDavid du Colombier 		if(hl < (IP_HLEN4<<2)) {
3689ef1f84bSDavid du Colombier 			ip->stats[InHdrErrors]++;
3699ef1f84bSDavid du Colombier 			netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
3709ef1f84bSDavid du Colombier 			freeblist(bp);
3719ef1f84bSDavid du Colombier 			return;
3729ef1f84bSDavid du Colombier 		}
3739ef1f84bSDavid du Colombier 		/* If this is not routed strip off the options */
3749ef1f84bSDavid du Colombier 		if(notforme == 0) {
3759ef1f84bSDavid du Colombier 			olen = nhgets(h->length);
3769ef1f84bSDavid du Colombier 			dp = bp->rp + (hl - (IP_HLEN4<<2));
3779ef1f84bSDavid du Colombier 			memmove(dp, h, IP_HLEN4<<2);
3789ef1f84bSDavid du Colombier 			bp->rp = dp;
3799ef1f84bSDavid du Colombier 			h = (Ip4hdr*)(bp->rp);
3809ef1f84bSDavid du Colombier 			h->vihl = (IP_VER4|IP_HLEN4);
3819ef1f84bSDavid du Colombier 			hnputs(h->length, olen-hl+(IP_HLEN4<<2));
3829ef1f84bSDavid du Colombier 		}
3839ef1f84bSDavid du Colombier 	}
3849ef1f84bSDavid du Colombier 
3859ef1f84bSDavid du Colombier 	/* route */
3869ef1f84bSDavid du Colombier 	if(notforme) {
3879ef1f84bSDavid du Colombier 		if(!ip->iprouting){
3889ef1f84bSDavid du Colombier 			freeblist(bp);
3899ef1f84bSDavid du Colombier 			return;
3909ef1f84bSDavid du Colombier 		}
3919ef1f84bSDavid du Colombier 
3929ef1f84bSDavid du Colombier 		/* don't forward to source's network */
3939ef1f84bSDavid du Colombier 		memset(&conv, 0, sizeof conv);
3949ef1f84bSDavid du Colombier 		conv.r = nil;
3959ef1f84bSDavid du Colombier 		r = v4lookup(f, h->dst, &conv);
3969ef1f84bSDavid du Colombier 		if(r == nil || r->ifc == ifc){
3979ef1f84bSDavid du Colombier 			ip->stats[OutDiscards]++;
3989ef1f84bSDavid du Colombier 			freeblist(bp);
3999ef1f84bSDavid du Colombier 			return;
4009ef1f84bSDavid du Colombier 		}
4019ef1f84bSDavid du Colombier 
4029ef1f84bSDavid du Colombier 		/* don't forward if packet has timed out */
4039ef1f84bSDavid du Colombier 		hop = h->ttl;
4049ef1f84bSDavid du Colombier 		if(hop < 1) {
4059ef1f84bSDavid du Colombier 			ip->stats[InHdrErrors]++;
4069ef1f84bSDavid du Colombier 			icmpttlexceeded(f, ifc->lifc->local, bp);
4079ef1f84bSDavid du Colombier 			freeblist(bp);
4089ef1f84bSDavid du Colombier 			return;
4099ef1f84bSDavid du Colombier 		}
4109ef1f84bSDavid du Colombier 
4119ef1f84bSDavid du Colombier 		/* reassemble if the interface expects it */
4129ef1f84bSDavid du Colombier if(r->ifc == nil) panic("nil route rfc");
4139ef1f84bSDavid du Colombier 		if(r->ifc->reassemble){
4149ef1f84bSDavid du Colombier 			frag = nhgets(h->frag);
4159ef1f84bSDavid du Colombier 			if(frag) {
4169ef1f84bSDavid du Colombier 				h->tos = 0;
4179ef1f84bSDavid du Colombier 				if(frag & IP_MF)
4189ef1f84bSDavid du Colombier 					h->tos = 1;
4199ef1f84bSDavid du Colombier 				bp = ip4reassemble(ip, frag, bp, h);
4209ef1f84bSDavid du Colombier 				if(bp == nil)
4219ef1f84bSDavid du Colombier 					return;
4229ef1f84bSDavid du Colombier 				h = (Ip4hdr*)(bp->rp);
4239ef1f84bSDavid du Colombier 			}
4249ef1f84bSDavid du Colombier 		}
4259ef1f84bSDavid du Colombier 
4269ef1f84bSDavid du Colombier 		ip->stats[ForwDatagrams]++;
4279ef1f84bSDavid du Colombier 		tos = h->tos;
4289ef1f84bSDavid du Colombier 		hop = h->ttl;
4299ef1f84bSDavid du Colombier 		ipoput4(f, bp, 1, hop - 1, tos, &conv);
4309ef1f84bSDavid du Colombier 		return;
4319ef1f84bSDavid du Colombier 	}
4329ef1f84bSDavid du Colombier 
4339ef1f84bSDavid du Colombier 	frag = nhgets(h->frag);
4349ef1f84bSDavid du Colombier 	if(frag) {
4359ef1f84bSDavid du Colombier 		h->tos = 0;
4369ef1f84bSDavid du Colombier 		if(frag & IP_MF)
4379ef1f84bSDavid du Colombier 			h->tos = 1;
4389ef1f84bSDavid du Colombier 		bp = ip4reassemble(ip, frag, bp, h);
4399ef1f84bSDavid du Colombier 		if(bp == nil)
4409ef1f84bSDavid du Colombier 			return;
4419ef1f84bSDavid du Colombier 		h = (Ip4hdr*)(bp->rp);
4429ef1f84bSDavid du Colombier 	}
4439ef1f84bSDavid du Colombier 
4449ef1f84bSDavid du Colombier 	/* don't let any frag info go up the stack */
4459ef1f84bSDavid du Colombier 	h->frag[0] = 0;
4469ef1f84bSDavid du Colombier 	h->frag[1] = 0;
4479ef1f84bSDavid du Colombier 
4489ef1f84bSDavid du Colombier 	proto = h->proto;
4499ef1f84bSDavid du Colombier 	p = Fsrcvpcol(f, proto);
4509ef1f84bSDavid du Colombier 	if(p != nil && p->rcv != nil) {
4519ef1f84bSDavid du Colombier 		ip->stats[InDelivers]++;
4529ef1f84bSDavid du Colombier 		(*p->rcv)(p, ifc, bp);
4539ef1f84bSDavid du Colombier 		return;
4549ef1f84bSDavid du Colombier 	}
4559ef1f84bSDavid du Colombier 	ip->stats[InDiscards]++;
4569ef1f84bSDavid du Colombier 	ip->stats[InUnknownProtos]++;
4579ef1f84bSDavid du Colombier 	freeblist(bp);
4589ef1f84bSDavid du Colombier }
4599ef1f84bSDavid du Colombier 
4609ef1f84bSDavid du Colombier int
ipstats(Fs * f,char * buf,int len)4619ef1f84bSDavid du Colombier ipstats(Fs *f, char *buf, int len)
4629ef1f84bSDavid du Colombier {
4639ef1f84bSDavid du Colombier 	IP *ip;
4649ef1f84bSDavid du Colombier 	char *p, *e;
4659ef1f84bSDavid du Colombier 	int i;
4669ef1f84bSDavid du Colombier 
4679ef1f84bSDavid du Colombier 	ip = f->ip;
4689ef1f84bSDavid du Colombier 	ip->stats[DefaultTTL] = MAXTTL;
4699ef1f84bSDavid du Colombier 
4709ef1f84bSDavid du Colombier 	p = buf;
4719ef1f84bSDavid du Colombier 	e = p+len;
4729ef1f84bSDavid du Colombier 	for(i = 0; i < Nipstats; i++)
4739ef1f84bSDavid du Colombier 		p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);
4749ef1f84bSDavid du Colombier 	return p - buf;
4759ef1f84bSDavid du Colombier }
4769ef1f84bSDavid du Colombier 
4779ef1f84bSDavid du Colombier Block*
ip4reassemble(IP * ip,int offset,Block * bp,Ip4hdr * ih)4789ef1f84bSDavid du Colombier ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
4799ef1f84bSDavid du Colombier {
4809ef1f84bSDavid du Colombier 	int fend;
4819ef1f84bSDavid du Colombier 	ushort id;
4829ef1f84bSDavid du Colombier 	Fragment4 *f, *fnext;
4839ef1f84bSDavid du Colombier 	ulong src, dst;
4849ef1f84bSDavid du Colombier 	Block *bl, **l, *last, *prev;
4859ef1f84bSDavid du Colombier 	int ovlap, len, fragsize, pktposn;
4869ef1f84bSDavid du Colombier 
4879ef1f84bSDavid du Colombier 	src = nhgetl(ih->src);
4889ef1f84bSDavid du Colombier 	dst = nhgetl(ih->dst);
4899ef1f84bSDavid du Colombier 	id = nhgets(ih->id);
4909ef1f84bSDavid du Colombier 
4919ef1f84bSDavid du Colombier 	/*
4929ef1f84bSDavid du Colombier 	 *  block lists are too hard, pullupblock into a single block
4939ef1f84bSDavid du Colombier 	 */
4949ef1f84bSDavid du Colombier 	if(bp->next){
4959ef1f84bSDavid du Colombier 		bp = pullupblock(bp, blocklen(bp));
4969ef1f84bSDavid du Colombier 		ih = (Ip4hdr*)(bp->rp);
4979ef1f84bSDavid du Colombier 	}
4989ef1f84bSDavid du Colombier 
4999ef1f84bSDavid du Colombier 	qlock(&ip->fraglock4);
5009ef1f84bSDavid du Colombier 
5019ef1f84bSDavid du Colombier 	/*
5029ef1f84bSDavid du Colombier 	 *  find a reassembly queue for this fragment
5039ef1f84bSDavid du Colombier 	 */
5049ef1f84bSDavid du Colombier 	for(f = ip->flisthead4; f; f = fnext){
5059ef1f84bSDavid du Colombier 		fnext = f->next;	/* because ipfragfree4 changes the list */
5069ef1f84bSDavid du Colombier 		if(f->src == src && f->dst == dst && f->id == id)
5079ef1f84bSDavid du Colombier 			break;
5089ef1f84bSDavid du Colombier 		if(f->age < NOW){
5099ef1f84bSDavid du Colombier 			ip->stats[ReasmTimeout]++;
5109ef1f84bSDavid du Colombier 			ipfragfree4(ip, f);
5119ef1f84bSDavid du Colombier 		}
5129ef1f84bSDavid du Colombier 	}
5139ef1f84bSDavid du Colombier 
5149ef1f84bSDavid du Colombier 	/*
5159ef1f84bSDavid du Colombier 	 *  if this isn't a fragmented packet, accept it
5169ef1f84bSDavid du Colombier 	 *  and get rid of any fragments that might go
5179ef1f84bSDavid du Colombier 	 *  with it.
5189ef1f84bSDavid du Colombier 	 */
5199ef1f84bSDavid du Colombier 	if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
5209ef1f84bSDavid du Colombier 		if(f != nil) {
5219ef1f84bSDavid du Colombier 			ipfragfree4(ip, f);
5229ef1f84bSDavid du Colombier 			ip->stats[ReasmFails]++;
5239ef1f84bSDavid du Colombier 		}
5249ef1f84bSDavid du Colombier 		qunlock(&ip->fraglock4);
5259ef1f84bSDavid du Colombier 		return bp;
5269ef1f84bSDavid du Colombier 	}
5279ef1f84bSDavid du Colombier 
5289ef1f84bSDavid du Colombier 	if(bp->base+IPFRAGSZ >= bp->rp){
5299ef1f84bSDavid du Colombier 		bp = padblock(bp, IPFRAGSZ);
5309ef1f84bSDavid du Colombier 		bp->rp += IPFRAGSZ;
5319ef1f84bSDavid du Colombier 	}
5329ef1f84bSDavid du Colombier 
5339ef1f84bSDavid du Colombier 	BKFG(bp)->foff = offset<<3;
5349ef1f84bSDavid du Colombier 	BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
5359ef1f84bSDavid du Colombier 
5369ef1f84bSDavid du Colombier 	/* First fragment allocates a reassembly queue */
5379ef1f84bSDavid du Colombier 	if(f == nil) {
5389ef1f84bSDavid du Colombier 		f = ipfragallo4(ip);
5399ef1f84bSDavid du Colombier 		f->id = id;
5409ef1f84bSDavid du Colombier 		f->src = src;
5419ef1f84bSDavid du Colombier 		f->dst = dst;
5429ef1f84bSDavid du Colombier 
5439ef1f84bSDavid du Colombier 		f->blist = bp;
5449ef1f84bSDavid du Colombier 
5459ef1f84bSDavid du Colombier 		qunlock(&ip->fraglock4);
5469ef1f84bSDavid du Colombier 		ip->stats[ReasmReqds]++;
5479ef1f84bSDavid du Colombier 		return nil;
5489ef1f84bSDavid du Colombier 	}
5499ef1f84bSDavid du Colombier 
5509ef1f84bSDavid du Colombier 	/*
5519ef1f84bSDavid du Colombier 	 *  find the new fragment's position in the queue
5529ef1f84bSDavid du Colombier 	 */
5539ef1f84bSDavid du Colombier 	prev = nil;
5549ef1f84bSDavid du Colombier 	l = &f->blist;
5559ef1f84bSDavid du Colombier 	bl = f->blist;
5569ef1f84bSDavid du Colombier 	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
5579ef1f84bSDavid du Colombier 		prev = bl;
5589ef1f84bSDavid du Colombier 		l = &bl->next;
5599ef1f84bSDavid du Colombier 		bl = bl->next;
5609ef1f84bSDavid du Colombier 	}
5619ef1f84bSDavid du Colombier 
5629ef1f84bSDavid du Colombier 	/* Check overlap of a previous fragment - trim away as necessary */
5639ef1f84bSDavid du Colombier 	if(prev) {
5649ef1f84bSDavid du Colombier 		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
5659ef1f84bSDavid du Colombier 		if(ovlap > 0) {
5669ef1f84bSDavid du Colombier 			if(ovlap >= BKFG(bp)->flen) {
5679ef1f84bSDavid du Colombier 				freeblist(bp);
5689ef1f84bSDavid du Colombier 				qunlock(&ip->fraglock4);
5699ef1f84bSDavid du Colombier 				return nil;
5709ef1f84bSDavid du Colombier 			}
5719ef1f84bSDavid du Colombier 			BKFG(prev)->flen -= ovlap;
5729ef1f84bSDavid du Colombier 		}
5739ef1f84bSDavid du Colombier 	}
5749ef1f84bSDavid du Colombier 
5759ef1f84bSDavid du Colombier 	/* Link onto assembly queue */
5769ef1f84bSDavid du Colombier 	bp->next = *l;
5779ef1f84bSDavid du Colombier 	*l = bp;
5789ef1f84bSDavid du Colombier 
5799ef1f84bSDavid du Colombier 	/* Check to see if succeeding segments overlap */
5809ef1f84bSDavid du Colombier 	if(bp->next) {
5819ef1f84bSDavid du Colombier 		l = &bp->next;
5829ef1f84bSDavid du Colombier 		fend = BKFG(bp)->foff + BKFG(bp)->flen;
5839ef1f84bSDavid du Colombier 		/* Take completely covered segments out */
5849ef1f84bSDavid du Colombier 		while(*l) {
5859ef1f84bSDavid du Colombier 			ovlap = fend - BKFG(*l)->foff;
5869ef1f84bSDavid du Colombier 			if(ovlap <= 0)
5879ef1f84bSDavid du Colombier 				break;
5889ef1f84bSDavid du Colombier 			if(ovlap < BKFG(*l)->flen) {
5899ef1f84bSDavid du Colombier 				BKFG(*l)->flen -= ovlap;
5909ef1f84bSDavid du Colombier 				BKFG(*l)->foff += ovlap;
5919ef1f84bSDavid du Colombier 				/* move up ih hdrs */
5929ef1f84bSDavid du Colombier 				memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
5939ef1f84bSDavid du Colombier 				(*l)->rp += ovlap;
5949ef1f84bSDavid du Colombier 				break;
5959ef1f84bSDavid du Colombier 			}
5969ef1f84bSDavid du Colombier 			last = (*l)->next;
5979ef1f84bSDavid du Colombier 			(*l)->next = nil;
5989ef1f84bSDavid du Colombier 			freeblist(*l);
5999ef1f84bSDavid du Colombier 			*l = last;
6009ef1f84bSDavid du Colombier 		}
6019ef1f84bSDavid du Colombier 	}
6029ef1f84bSDavid du Colombier 
6039ef1f84bSDavid du Colombier 	/*
6049ef1f84bSDavid du Colombier 	 *  look for a complete packet.  if we get to a fragment
6059ef1f84bSDavid du Colombier 	 *  without IP_MF set, we're done.
6069ef1f84bSDavid du Colombier 	 */
6079ef1f84bSDavid du Colombier 	pktposn = 0;
6089ef1f84bSDavid du Colombier 	for(bl = f->blist; bl; bl = bl->next) {
6099ef1f84bSDavid du Colombier 		if(BKFG(bl)->foff != pktposn)
6109ef1f84bSDavid du Colombier 			break;
6119ef1f84bSDavid du Colombier 		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
6129ef1f84bSDavid du Colombier 			bl = f->blist;
6139ef1f84bSDavid du Colombier 			len = nhgets(BLKIP(bl)->length);
6149ef1f84bSDavid du Colombier 			bl->wp = bl->rp + len;
6159ef1f84bSDavid du Colombier 
6169ef1f84bSDavid du Colombier 			/* Pullup all the fragment headers and
6179ef1f84bSDavid du Colombier 			 * return a complete packet
6189ef1f84bSDavid du Colombier 			 */
6199ef1f84bSDavid du Colombier 			for(bl = bl->next; bl; bl = bl->next) {
6209ef1f84bSDavid du Colombier 				fragsize = BKFG(bl)->flen;
6219ef1f84bSDavid du Colombier 				len += fragsize;
6229ef1f84bSDavid du Colombier 				bl->rp += IP4HDR;
6239ef1f84bSDavid du Colombier 				bl->wp = bl->rp + fragsize;
6249ef1f84bSDavid du Colombier 			}
6259ef1f84bSDavid du Colombier 
6269ef1f84bSDavid du Colombier 			bl = f->blist;
6279ef1f84bSDavid du Colombier 			f->blist = nil;
6289ef1f84bSDavid du Colombier 			ipfragfree4(ip, f);
6299ef1f84bSDavid du Colombier 			ih = BLKIP(bl);
6309ef1f84bSDavid du Colombier 			hnputs(ih->length, len);
6319ef1f84bSDavid du Colombier 			qunlock(&ip->fraglock4);
6329ef1f84bSDavid du Colombier 			ip->stats[ReasmOKs]++;
6339ef1f84bSDavid du Colombier 			return bl;
6349ef1f84bSDavid du Colombier 		}
6359ef1f84bSDavid du Colombier 		pktposn += BKFG(bl)->flen;
6369ef1f84bSDavid du Colombier 	}
6379ef1f84bSDavid du Colombier 	qunlock(&ip->fraglock4);
6389ef1f84bSDavid du Colombier 	return nil;
6399ef1f84bSDavid du Colombier }
6409ef1f84bSDavid du Colombier 
6419ef1f84bSDavid du Colombier /*
6429ef1f84bSDavid du Colombier  * ipfragfree4 - Free a list of fragments - assume hold fraglock4
6439ef1f84bSDavid du Colombier  */
6449ef1f84bSDavid du Colombier void
ipfragfree4(IP * ip,Fragment4 * frag)6459ef1f84bSDavid du Colombier ipfragfree4(IP *ip, Fragment4 *frag)
6469ef1f84bSDavid du Colombier {
6479ef1f84bSDavid du Colombier 	Fragment4 *fl, **l;
6489ef1f84bSDavid du Colombier 
6499ef1f84bSDavid du Colombier 	if(frag->blist)
6509ef1f84bSDavid du Colombier 		freeblist(frag->blist);
6519ef1f84bSDavid du Colombier 
6529ef1f84bSDavid du Colombier 	frag->src = 0;
6539ef1f84bSDavid du Colombier 	frag->id = 0;
6549ef1f84bSDavid du Colombier 	frag->blist = nil;
6559ef1f84bSDavid du Colombier 
6569ef1f84bSDavid du Colombier 	l = &ip->flisthead4;
6579ef1f84bSDavid du Colombier 	for(fl = *l; fl; fl = fl->next) {
6589ef1f84bSDavid du Colombier 		if(fl == frag) {
6599ef1f84bSDavid du Colombier 			*l = frag->next;
6609ef1f84bSDavid du Colombier 			break;
6619ef1f84bSDavid du Colombier 		}
6629ef1f84bSDavid du Colombier 		l = &fl->next;
6639ef1f84bSDavid du Colombier 	}
6649ef1f84bSDavid du Colombier 
6659ef1f84bSDavid du Colombier 	frag->next = ip->fragfree4;
6669ef1f84bSDavid du Colombier 	ip->fragfree4 = frag;
6679ef1f84bSDavid du Colombier 
6689ef1f84bSDavid du Colombier }
6699ef1f84bSDavid du Colombier 
6709ef1f84bSDavid du Colombier /*
6719ef1f84bSDavid du Colombier  * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
6729ef1f84bSDavid du Colombier  */
6739ef1f84bSDavid du Colombier Fragment4 *
ipfragallo4(IP * ip)6749ef1f84bSDavid du Colombier ipfragallo4(IP *ip)
6759ef1f84bSDavid du Colombier {
6769ef1f84bSDavid du Colombier 	Fragment4 *f;
6779ef1f84bSDavid du Colombier 
6789ef1f84bSDavid du Colombier 	while(ip->fragfree4 == nil) {
6799ef1f84bSDavid du Colombier 		/* free last entry on fraglist */
6809ef1f84bSDavid du Colombier 		for(f = ip->flisthead4; f->next; f = f->next)
6819ef1f84bSDavid du Colombier 			;
6829ef1f84bSDavid du Colombier 		ipfragfree4(ip, f);
6839ef1f84bSDavid du Colombier 	}
6849ef1f84bSDavid du Colombier 	f = ip->fragfree4;
6859ef1f84bSDavid du Colombier 	ip->fragfree4 = f->next;
6869ef1f84bSDavid du Colombier 	f->next = ip->flisthead4;
6879ef1f84bSDavid du Colombier 	ip->flisthead4 = f;
6889ef1f84bSDavid du Colombier 	f->age = NOW + 30000;
6899ef1f84bSDavid du Colombier 
6909ef1f84bSDavid du Colombier 	return f;
6919ef1f84bSDavid du Colombier }
6929ef1f84bSDavid du Colombier 
6939ef1f84bSDavid du Colombier ushort
ipcsum(uchar * addr)6949ef1f84bSDavid du Colombier ipcsum(uchar *addr)
6959ef1f84bSDavid du Colombier {
6969ef1f84bSDavid du Colombier 	int len;
6979ef1f84bSDavid du Colombier 	ulong sum;
6989ef1f84bSDavid du Colombier 
6999ef1f84bSDavid du Colombier 	sum = 0;
7009ef1f84bSDavid du Colombier 	len = (addr[0]&0xf)<<2;
7019ef1f84bSDavid du Colombier 
7029ef1f84bSDavid du Colombier 	while(len > 0) {
7039ef1f84bSDavid du Colombier 		sum += addr[0]<<8 | addr[1] ;
7049ef1f84bSDavid du Colombier 		len -= 2;
7059ef1f84bSDavid du Colombier 		addr += 2;
7069ef1f84bSDavid du Colombier 	}
7079ef1f84bSDavid du Colombier 
7089ef1f84bSDavid du Colombier 	sum = (sum & 0xffff) + (sum >> 16);
7099ef1f84bSDavid du Colombier 	sum = (sum & 0xffff) + (sum >> 16);
7109ef1f84bSDavid du Colombier 
7119ef1f84bSDavid du Colombier 	return (sum^0xffff);
7129ef1f84bSDavid du Colombier }
713