xref: /plan9-contrib/sys/src/9/ip/ip.c (revision 8bbc9479b88bcf3f19c475fa9e8e8ca555aedaa3)
17dd7cddfSDavid du Colombier #include	"u.h"
27dd7cddfSDavid du Colombier #include	"../port/lib.h"
37dd7cddfSDavid du Colombier #include	"mem.h"
47dd7cddfSDavid du Colombier #include	"dat.h"
57dd7cddfSDavid du Colombier #include	"fns.h"
67dd7cddfSDavid du Colombier #include	"../port/error.h"
77dd7cddfSDavid du Colombier 
83ff48bf5SDavid du Colombier #include	"ip.h"
97dd7cddfSDavid du Colombier 
103ff48bf5SDavid du Colombier #define BLKIPVER(xp)	(((Ip4hdr*)((xp)->rp))->vihl&0xF0)
113ff48bf5SDavid du Colombier 
1259cc4ca5SDavid du Colombier static char *statnames[] =
1359cc4ca5SDavid du Colombier {
1459cc4ca5SDavid du Colombier [Forwarding]	"Forwarding",
1559cc4ca5SDavid du Colombier [DefaultTTL]	"DefaultTTL",
1659cc4ca5SDavid du Colombier [InReceives]	"InReceives",
1759cc4ca5SDavid du Colombier [InHdrErrors]	"InHdrErrors",
1859cc4ca5SDavid du Colombier [InAddrErrors]	"InAddrErrors",
1959cc4ca5SDavid du Colombier [ForwDatagrams]	"ForwDatagrams",
2059cc4ca5SDavid du Colombier [InUnknownProtos]	"InUnknownProtos",
2159cc4ca5SDavid du Colombier [InDiscards]	"InDiscards",
2259cc4ca5SDavid du Colombier [InDelivers]	"InDelivers",
2359cc4ca5SDavid du Colombier [OutRequests]	"OutRequests",
2459cc4ca5SDavid du Colombier [OutDiscards]	"OutDiscards",
2559cc4ca5SDavid du Colombier [OutNoRoutes]	"OutNoRoutes",
2659cc4ca5SDavid du Colombier [ReasmTimeout]	"ReasmTimeout",
2759cc4ca5SDavid du Colombier [ReasmReqds]	"ReasmReqds",
2859cc4ca5SDavid du Colombier [ReasmOKs]	"ReasmOKs",
2959cc4ca5SDavid du Colombier [ReasmFails]	"ReasmFails",
3059cc4ca5SDavid du Colombier [FragOKs]	"FragOKs",
3159cc4ca5SDavid du Colombier [FragFails]	"FragFails",
3259cc4ca5SDavid du Colombier [FragCreates]	"FragCreates",
337dd7cddfSDavid du Colombier };
347dd7cddfSDavid du Colombier 
353ff48bf5SDavid du Colombier #define BLKIP(xp)	((Ip4hdr*)((xp)->rp))
367dd7cddfSDavid du Colombier /*
377dd7cddfSDavid du Colombier  * This sleazy macro relies on the media header size being
387dd7cddfSDavid du Colombier  * larger than sizeof(Ipfrag). ipreassemble checks this is true
397dd7cddfSDavid du Colombier  */
407dd7cddfSDavid du Colombier #define BKFG(xp)	((Ipfrag*)((xp)->base))
417dd7cddfSDavid du Colombier 
427dd7cddfSDavid du Colombier ushort		ipcsum(uchar*);
433ff48bf5SDavid du Colombier Block*		ip4reassemble(IP*, int, Block*, Ip4hdr*);
443ff48bf5SDavid du Colombier void		ipfragfree4(IP*, Fragment4*);
453ff48bf5SDavid du Colombier Fragment4*	ipfragallo4(IP*);
463ff48bf5SDavid du Colombier 
473ff48bf5SDavid du Colombier void
ip_init_6(Fs * f)483ff48bf5SDavid du Colombier ip_init_6(Fs *f)
493ff48bf5SDavid du Colombier {
503ff48bf5SDavid du Colombier 	v6params *v6p;
513ff48bf5SDavid du Colombier 
523ff48bf5SDavid du Colombier 	v6p = smalloc(sizeof(v6params));
533ff48bf5SDavid du Colombier 
543179bee6SDavid du Colombier 	v6p->rp.mflag		= 0;		/* default not managed */
553ff48bf5SDavid du Colombier 	v6p->rp.oflag		= 0;
563179bee6SDavid du Colombier 	v6p->rp.maxraint	= 600000;	/* millisecs */
573ff48bf5SDavid du Colombier 	v6p->rp.minraint	= 200000;
583179bee6SDavid du Colombier 	v6p->rp.linkmtu		= 0;		/* no mtu sent */
593ff48bf5SDavid du Colombier 	v6p->rp.reachtime	= 0;
603ff48bf5SDavid du Colombier 	v6p->rp.rxmitra		= 0;
613ff48bf5SDavid du Colombier 	v6p->rp.ttl		= MAXTTL;
623179bee6SDavid du Colombier 	v6p->rp.routerlt	= 3 * v6p->rp.maxraint;
633ff48bf5SDavid du Colombier 
643179bee6SDavid du Colombier 	v6p->hp.rxmithost	= 1000;		/* v6 RETRANS_TIMER */
653ff48bf5SDavid du Colombier 
663ff48bf5SDavid du Colombier 	v6p->cdrouter		= -1;
673ff48bf5SDavid du Colombier 
683ff48bf5SDavid du Colombier 	f->v6p			= v6p;
693ff48bf5SDavid du Colombier }
703ff48bf5SDavid du Colombier 
713ff48bf5SDavid du Colombier void
initfrag(IP * ip,int size)723ff48bf5SDavid du Colombier initfrag(IP *ip, int size)
733ff48bf5SDavid du Colombier {
743ff48bf5SDavid du Colombier 	Fragment4 *fq4, *eq4;
753ff48bf5SDavid du Colombier 	Fragment6 *fq6, *eq6;
763ff48bf5SDavid du Colombier 
773ff48bf5SDavid du Colombier 	ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
783ff48bf5SDavid du Colombier 	if(ip->fragfree4 == nil)
793ff48bf5SDavid du Colombier 		panic("initfrag");
803ff48bf5SDavid du Colombier 
813ff48bf5SDavid du Colombier 	eq4 = &ip->fragfree4[size];
823ff48bf5SDavid du Colombier 	for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
833ff48bf5SDavid du Colombier 		fq4->next = fq4+1;
843ff48bf5SDavid du Colombier 
853ff48bf5SDavid du Colombier 	ip->fragfree4[size-1].next = nil;
863ff48bf5SDavid du Colombier 
873ff48bf5SDavid du Colombier 	ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
883ff48bf5SDavid du Colombier 	if(ip->fragfree6 == nil)
893ff48bf5SDavid du Colombier 		panic("initfrag");
903ff48bf5SDavid du Colombier 
913ff48bf5SDavid du Colombier 	eq6 = &ip->fragfree6[size];
923ff48bf5SDavid du Colombier 	for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
933ff48bf5SDavid du Colombier 		fq6->next = fq6+1;
943ff48bf5SDavid du Colombier 
953ff48bf5SDavid du Colombier 	ip->fragfree6[size-1].next = nil;
963ff48bf5SDavid du Colombier }
977dd7cddfSDavid du Colombier 
987dd7cddfSDavid du Colombier void
ip_init(Fs * f)997dd7cddfSDavid du Colombier ip_init(Fs *f)
1007dd7cddfSDavid du Colombier {
1017dd7cddfSDavid du Colombier 	IP *ip;
1027dd7cddfSDavid du Colombier 
1037dd7cddfSDavid du Colombier 	ip = smalloc(sizeof(IP));
1047dd7cddfSDavid du Colombier 	initfrag(ip, 100);
1057dd7cddfSDavid du Colombier 	f->ip = ip;
1063ff48bf5SDavid du Colombier 
1073ff48bf5SDavid du Colombier 	ip_init_6(f);
1087dd7cddfSDavid du Colombier }
1097dd7cddfSDavid du Colombier 
1107dd7cddfSDavid du Colombier void
iprouting(Fs * f,int on)1117dd7cddfSDavid du Colombier iprouting(Fs *f, int on)
1127dd7cddfSDavid du Colombier {
1137dd7cddfSDavid du Colombier 	f->ip->iprouting = on;
1147dd7cddfSDavid du Colombier 	if(f->ip->iprouting==0)
11559cc4ca5SDavid du Colombier 		f->ip->stats[Forwarding] = 2;
1167dd7cddfSDavid du Colombier 	else
11759cc4ca5SDavid du Colombier 		f->ip->stats[Forwarding] = 1;
1187dd7cddfSDavid du Colombier }
1197dd7cddfSDavid du Colombier 
120e6c6b7f8SDavid du Colombier int
ipoput4(Fs * f,Block * bp,int gating,int ttl,int tos,Conv * c)121a6a9e072SDavid du Colombier ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
1227dd7cddfSDavid du Colombier {
1237dd7cddfSDavid du Colombier 	Ipifc *ifc;
1247dd7cddfSDavid du Colombier 	uchar *gate;
1259a747e4fSDavid du Colombier 	ulong fragoff;
1267dd7cddfSDavid du Colombier 	Block *xp, *nb;
1273ff48bf5SDavid du Colombier 	Ip4hdr *eh, *feh;
1287dd7cddfSDavid du Colombier 	int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
1297dd7cddfSDavid du Colombier 	Route *r, *sr;
1307dd7cddfSDavid du Colombier 	IP *ip;
131e6c6b7f8SDavid du Colombier 	int rv = 0;
132*8bbc9479SDavid du Colombier 	uchar v4dst[IPv4addrlen];
1337dd7cddfSDavid du Colombier 
1347dd7cddfSDavid du Colombier 	ip = f->ip;
1357dd7cddfSDavid du Colombier 
1367dd7cddfSDavid du Colombier 	/* Fill out the ip header */
1373ff48bf5SDavid du Colombier 	eh = (Ip4hdr*)(bp->rp);
1387dd7cddfSDavid du Colombier 
13959cc4ca5SDavid du Colombier 	ip->stats[OutRequests]++;
1407dd7cddfSDavid du Colombier 
1417dd7cddfSDavid du Colombier 	/* Number of uchars in data and ip header to write */
1427dd7cddfSDavid du Colombier 	len = blocklen(bp);
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier 	if(gating){
1457dd7cddfSDavid du Colombier 		chunk = nhgets(eh->length);
1467dd7cddfSDavid du Colombier 		if(chunk > len){
14759cc4ca5SDavid du Colombier 			ip->stats[OutDiscards]++;
1487dd7cddfSDavid du Colombier 			netlog(f, Logip, "short gated packet\n");
1497dd7cddfSDavid du Colombier 			goto free;
1507dd7cddfSDavid du Colombier 		}
1517dd7cddfSDavid du Colombier 		if(chunk < len)
1527dd7cddfSDavid du Colombier 			len = chunk;
1537dd7cddfSDavid du Colombier 	}
1547dd7cddfSDavid du Colombier 	if(len >= IP_MAX){
15559cc4ca5SDavid du Colombier 		ip->stats[OutDiscards]++;
1567dd7cddfSDavid du Colombier 		netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
1577dd7cddfSDavid du Colombier 		goto free;
1587dd7cddfSDavid du Colombier 	}
1597dd7cddfSDavid du Colombier 
160a6a9e072SDavid du Colombier 	r = v4lookup(f, eh->dst, c);
1617dd7cddfSDavid du Colombier 	if(r == nil){
16259cc4ca5SDavid du Colombier 		ip->stats[OutNoRoutes]++;
1637dd7cddfSDavid du Colombier 		netlog(f, Logip, "no interface %V\n", eh->dst);
164e6c6b7f8SDavid du Colombier 		rv = -1;
1657dd7cddfSDavid du Colombier 		goto free;
1667dd7cddfSDavid du Colombier 	}
1677dd7cddfSDavid du Colombier 
1687dd7cddfSDavid du Colombier 	ifc = r->ifc;
1697dd7cddfSDavid du Colombier 	if(r->type & (Rifc|Runi))
1707dd7cddfSDavid du Colombier 		gate = eh->dst;
1717dd7cddfSDavid du Colombier 	else
1727dd7cddfSDavid du Colombier 	if(r->type & (Rbcast|Rmulti)) {
173*8bbc9479SDavid du Colombier 		if(nhgetl(r->v4.gate) == 0){
174*8bbc9479SDavid du Colombier 			hnputl(v4dst, r->v4.address);
175*8bbc9479SDavid du Colombier 			gate = v4dst;
176*8bbc9479SDavid du Colombier 		}else
1777dd7cddfSDavid du Colombier 			gate = eh->dst;
178a6a9e072SDavid du Colombier 		sr = v4lookup(f, eh->src, nil);
1797dd7cddfSDavid du Colombier 		if(sr != nil && (sr->type & Runi))
1807dd7cddfSDavid du Colombier 			ifc = sr->ifc;
1817dd7cddfSDavid du Colombier 	}
1827dd7cddfSDavid du Colombier 	else
1837dd7cddfSDavid du Colombier 		gate = r->v4.gate;
1847dd7cddfSDavid du Colombier 
1857dd7cddfSDavid du Colombier 	if(!gating)
1863ff48bf5SDavid du Colombier 		eh->vihl = IP_VER4|IP_HLEN4;
1877dd7cddfSDavid du Colombier 	eh->ttl = ttl;
1889a747e4fSDavid du Colombier 	if(!gating)
1897dd7cddfSDavid du Colombier 		eh->tos = tos;
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier 	if(!canrlock(ifc))
1927dd7cddfSDavid du Colombier 		goto free;
1937dd7cddfSDavid du Colombier 	if(waserror()){
1947dd7cddfSDavid du Colombier 		runlock(ifc);
1957dd7cddfSDavid du Colombier 		nexterror();
1967dd7cddfSDavid du Colombier 	}
197cc057a51SDavid du Colombier 	if(ifc->medium == nil)
1987dd7cddfSDavid du Colombier 		goto raise;
1997dd7cddfSDavid du Colombier 
2007dd7cddfSDavid du Colombier 	/* If we dont need to fragment just send it */
2017ec5746aSDavid du Colombier 	if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
202cc057a51SDavid du Colombier 		medialen = c->maxfragsize - ifc->medium->hsize;
2037ec5746aSDavid du Colombier 	else
204cc057a51SDavid du Colombier 		medialen = ifc->maxtu - ifc->medium->hsize;
2057dd7cddfSDavid du Colombier 	if(len <= medialen) {
2067dd7cddfSDavid du Colombier 		if(!gating)
2073ff48bf5SDavid du Colombier 			hnputs(eh->id, incref(&ip->id4));
2087dd7cddfSDavid du Colombier 		hnputs(eh->length, len);
2099a747e4fSDavid du Colombier 		if(!gating){
2107dd7cddfSDavid du Colombier 			eh->frag[0] = 0;
2117dd7cddfSDavid du Colombier 			eh->frag[1] = 0;
2129a747e4fSDavid du Colombier 		}
2137dd7cddfSDavid du Colombier 		eh->cksum[0] = 0;
2147dd7cddfSDavid du Colombier 		eh->cksum[1] = 0;
2157dd7cddfSDavid du Colombier 		hnputs(eh->cksum, ipcsum(&eh->vihl));
2167ec5746aSDavid du Colombier 		assert(bp->next == nil);
217cc057a51SDavid du Colombier 		ifc->medium->bwrite(ifc, bp, V4, gate);
2187dd7cddfSDavid du Colombier 		runlock(ifc);
2197dd7cddfSDavid du Colombier 		poperror();
220e6c6b7f8SDavid du Colombier 		return 0;
2217dd7cddfSDavid du Colombier 	}
2227dd7cddfSDavid du Colombier 
2236b7c5dceSDavid du Colombier 	if((eh->frag[0] & (IP_DF>>8)) && !gating)
2246b7c5dceSDavid du Colombier 		print("%V: DF set\n", eh->dst);
225d9306527SDavid du Colombier 
2267dd7cddfSDavid du Colombier 	if(eh->frag[0] & (IP_DF>>8)){
22759cc4ca5SDavid du Colombier 		ip->stats[FragFails]++;
22859cc4ca5SDavid du Colombier 		ip->stats[OutDiscards]++;
229d9306527SDavid du Colombier 		icmpcantfrag(f, bp, medialen);
2307dd7cddfSDavid du Colombier 		netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
2317dd7cddfSDavid du Colombier 		goto raise;
2327dd7cddfSDavid du Colombier 	}
2337dd7cddfSDavid du Colombier 
2343ff48bf5SDavid du Colombier 	seglen = (medialen - IP4HDR) & ~7;
2357dd7cddfSDavid du Colombier 	if(seglen < 8){
23659cc4ca5SDavid du Colombier 		ip->stats[FragFails]++;
23759cc4ca5SDavid du Colombier 		ip->stats[OutDiscards]++;
2387dd7cddfSDavid du Colombier 		netlog(f, Logip, "%V seglen < 8\n", eh->dst);
2397dd7cddfSDavid du Colombier 		goto raise;
2407dd7cddfSDavid du Colombier 	}
2417dd7cddfSDavid du Colombier 
2423ff48bf5SDavid du Colombier 	dlen = len - IP4HDR;
2437dd7cddfSDavid du Colombier 	xp = bp;
2447dd7cddfSDavid du Colombier 	if(gating)
2457dd7cddfSDavid du Colombier 		lid = nhgets(eh->id);
2467dd7cddfSDavid du Colombier 	else
2473ff48bf5SDavid du Colombier 		lid = incref(&ip->id4);
2487dd7cddfSDavid du Colombier 
2493ff48bf5SDavid du Colombier 	offset = IP4HDR;
2507dd7cddfSDavid du Colombier 	while(xp != nil && offset && offset >= BLEN(xp)) {
2517dd7cddfSDavid du Colombier 		offset -= BLEN(xp);
2527dd7cddfSDavid du Colombier 		xp = xp->next;
2537dd7cddfSDavid du Colombier 	}
2547dd7cddfSDavid du Colombier 	xp->rp += offset;
2557dd7cddfSDavid du Colombier 
2569a747e4fSDavid du Colombier 	if(gating)
2579a747e4fSDavid du Colombier 		fragoff = nhgets(eh->frag)<<3;
2589a747e4fSDavid du Colombier 	else
2599a747e4fSDavid du Colombier 		fragoff = 0;
2609a747e4fSDavid du Colombier 	dlen += fragoff;
2619a747e4fSDavid du Colombier 	for(; fragoff < dlen; fragoff += seglen) {
2623ff48bf5SDavid du Colombier 		nb = allocb(IP4HDR+seglen);
2633ff48bf5SDavid du Colombier 		feh = (Ip4hdr*)(nb->rp);
2647dd7cddfSDavid du Colombier 
2653ff48bf5SDavid du Colombier 		memmove(nb->wp, eh, IP4HDR);
2663ff48bf5SDavid du Colombier 		nb->wp += IP4HDR;
2677dd7cddfSDavid du Colombier 
2687dd7cddfSDavid du Colombier 		if((fragoff + seglen) >= dlen) {
2697dd7cddfSDavid du Colombier 			seglen = dlen - fragoff;
2707dd7cddfSDavid du Colombier 			hnputs(feh->frag, fragoff>>3);
2717dd7cddfSDavid du Colombier 		}
2727dd7cddfSDavid du Colombier 		else
2737dd7cddfSDavid du Colombier 			hnputs(feh->frag, (fragoff>>3)|IP_MF);
2747dd7cddfSDavid du Colombier 
2753ff48bf5SDavid du Colombier 		hnputs(feh->length, seglen + IP4HDR);
2767dd7cddfSDavid du Colombier 		hnputs(feh->id, lid);
2777dd7cddfSDavid du Colombier 
2787dd7cddfSDavid du Colombier 		/* Copy up the data area */
2797dd7cddfSDavid du Colombier 		chunk = seglen;
2807dd7cddfSDavid du Colombier 		while(chunk) {
2817dd7cddfSDavid du Colombier 			if(!xp) {
28259cc4ca5SDavid du Colombier 				ip->stats[OutDiscards]++;
28359cc4ca5SDavid du Colombier 				ip->stats[FragFails]++;
2847dd7cddfSDavid du Colombier 				freeblist(nb);
2857dd7cddfSDavid du Colombier 				netlog(f, Logip, "!xp: chunk %d\n", chunk);
2867dd7cddfSDavid du Colombier 				goto raise;
2877dd7cddfSDavid du Colombier 			}
2887dd7cddfSDavid du Colombier 			blklen = chunk;
2897dd7cddfSDavid du Colombier 			if(BLEN(xp) < chunk)
2907dd7cddfSDavid du Colombier 				blklen = BLEN(xp);
2917dd7cddfSDavid du Colombier 			memmove(nb->wp, xp->rp, blklen);
2927dd7cddfSDavid du Colombier 			nb->wp += blklen;
2937dd7cddfSDavid du Colombier 			xp->rp += blklen;
2947dd7cddfSDavid du Colombier 			chunk -= blklen;
2957dd7cddfSDavid du Colombier 			if(xp->rp == xp->wp)
2967dd7cddfSDavid du Colombier 				xp = xp->next;
2977dd7cddfSDavid du Colombier 		}
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 		feh->cksum[0] = 0;
3007dd7cddfSDavid du Colombier 		feh->cksum[1] = 0;
3017dd7cddfSDavid du Colombier 		hnputs(feh->cksum, ipcsum(&feh->vihl));
302cc057a51SDavid du Colombier 		ifc->medium->bwrite(ifc, nb, V4, gate);
30359cc4ca5SDavid du Colombier 		ip->stats[FragCreates]++;
3047dd7cddfSDavid du Colombier 	}
30559cc4ca5SDavid du Colombier 	ip->stats[FragOKs]++;
3067dd7cddfSDavid du Colombier raise:
3077dd7cddfSDavid du Colombier 	runlock(ifc);
3087dd7cddfSDavid du Colombier 	poperror();
3097dd7cddfSDavid du Colombier free:
3107dd7cddfSDavid du Colombier 	freeblist(bp);
311e6c6b7f8SDavid du Colombier 	return rv;
3127dd7cddfSDavid du Colombier }
3137dd7cddfSDavid du Colombier 
3147dd7cddfSDavid du Colombier void
ipiput4(Fs * f,Ipifc * ifc,Block * bp)3153ff48bf5SDavid du Colombier ipiput4(Fs *f, Ipifc *ifc, Block *bp)
3167dd7cddfSDavid du Colombier {
3177dd7cddfSDavid du Colombier 	int hl;
3183ff48bf5SDavid du Colombier 	int hop, tos, proto, olen;
3193ff48bf5SDavid du Colombier 	Ip4hdr *h;
3207dd7cddfSDavid du Colombier 	Proto *p;
3217dd7cddfSDavid du Colombier 	ushort frag;
3227dd7cddfSDavid du Colombier 	int notforme;
3237dd7cddfSDavid du Colombier 	uchar *dp, v6dst[IPaddrlen];
3247dd7cddfSDavid du Colombier 	IP *ip;
325a6a9e072SDavid du Colombier 	Route *r;
3267ec5746aSDavid du Colombier 	Conv conv;
3277ec5746aSDavid du Colombier 
3283ff48bf5SDavid du Colombier 	if(BLKIPVER(bp) != IP_VER4) {
3293ff48bf5SDavid du Colombier 		ipiput6(f, ifc, bp);
3303ff48bf5SDavid du Colombier 		return;
3313ff48bf5SDavid du Colombier 	}
3327dd7cddfSDavid du Colombier 
3337dd7cddfSDavid du Colombier 	ip = f->ip;
33459cc4ca5SDavid du Colombier 	ip->stats[InReceives]++;
3357dd7cddfSDavid du Colombier 
3367dd7cddfSDavid du Colombier 	/*
3377dd7cddfSDavid du Colombier 	 *  Ensure we have all the header info in the first
3387dd7cddfSDavid du Colombier 	 *  block.  Make life easier for other protocols by
3397dd7cddfSDavid du Colombier 	 *  collecting up to the first 64 bytes in the first block.
3407dd7cddfSDavid du Colombier 	 */
3417dd7cddfSDavid du Colombier 	if(BLEN(bp) < 64) {
3427dd7cddfSDavid du Colombier 		hl = blocklen(bp);
3433ff48bf5SDavid du Colombier 		if(hl < IP4HDR)
3443ff48bf5SDavid du Colombier 			hl = IP4HDR;
3457dd7cddfSDavid du Colombier 		if(hl > 64)
3467dd7cddfSDavid du Colombier 			hl = 64;
3477dd7cddfSDavid du Colombier 		bp = pullupblock(bp, hl);
3487dd7cddfSDavid du Colombier 		if(bp == nil)
3497dd7cddfSDavid du Colombier 			return;
3507dd7cddfSDavid du Colombier 	}
3513ff48bf5SDavid du Colombier 
3523ff48bf5SDavid du Colombier 	h = (Ip4hdr*)(bp->rp);
3537dd7cddfSDavid du Colombier 
3547dd7cddfSDavid du Colombier 	/* dump anything that whose header doesn't checksum */
3555fab9909SDavid du Colombier 	if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
35659cc4ca5SDavid du Colombier 		ip->stats[InHdrErrors]++;
3577dd7cddfSDavid du Colombier 		netlog(f, Logip, "ip: checksum error %V\n", h->src);
3587dd7cddfSDavid du Colombier 		freeblist(bp);
3597dd7cddfSDavid du Colombier 		return;
3607dd7cddfSDavid du Colombier 	}
3617dd7cddfSDavid du Colombier 	v4tov6(v6dst, h->dst);
3627dd7cddfSDavid du Colombier 	notforme = ipforme(f, v6dst) == 0;
3637dd7cddfSDavid du Colombier 
3647dd7cddfSDavid du Colombier 	/* Check header length and version */
3653ff48bf5SDavid du Colombier 	if((h->vihl&0x0F) != IP_HLEN4) {
3667dd7cddfSDavid du Colombier 		hl = (h->vihl&0xF)<<2;
3673ff48bf5SDavid du Colombier 		if(hl < (IP_HLEN4<<2)) {
36859cc4ca5SDavid du Colombier 			ip->stats[InHdrErrors]++;
3697dd7cddfSDavid du Colombier 			netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
3707dd7cddfSDavid du Colombier 			freeblist(bp);
3717dd7cddfSDavid du Colombier 			return;
3727dd7cddfSDavid du Colombier 		}
3737dd7cddfSDavid du Colombier 		/* If this is not routed strip off the options */
3747dd7cddfSDavid du Colombier 		if(notforme == 0) {
375b7b24591SDavid du Colombier 			olen = nhgets(h->length);
3763ff48bf5SDavid du Colombier 			dp = bp->rp + (hl - (IP_HLEN4<<2));
3773ff48bf5SDavid du Colombier 			memmove(dp, h, IP_HLEN4<<2);
3787dd7cddfSDavid du Colombier 			bp->rp = dp;
3793ff48bf5SDavid du Colombier 			h = (Ip4hdr*)(bp->rp);
3803ff48bf5SDavid du Colombier 			h->vihl = (IP_VER4|IP_HLEN4);
3813ff48bf5SDavid du Colombier 			hnputs(h->length, olen-hl+(IP_HLEN4<<2));
3827dd7cddfSDavid du Colombier 		}
3837dd7cddfSDavid du Colombier 	}
3847dd7cddfSDavid du Colombier 
3857dd7cddfSDavid du Colombier 	/* route */
3867dd7cddfSDavid du Colombier 	if(notforme) {
3877dd7cddfSDavid du Colombier 		if(!ip->iprouting){
3887ec5746aSDavid du Colombier 			freeblist(bp);
3897dd7cddfSDavid du Colombier 			return;
3907dd7cddfSDavid du Colombier 		}
3913ff48bf5SDavid du Colombier 
392a6a9e072SDavid du Colombier 		/* don't forward to source's network */
39384ba54caSDavid du Colombier 		memset(&conv, 0, sizeof conv);
394a6a9e072SDavid du Colombier 		conv.r = nil;
395a6a9e072SDavid du Colombier 		r = v4lookup(f, h->dst, &conv);
39604b73bddSDavid du Colombier 		if(r == nil || r->ifc == ifc){
39759cc4ca5SDavid du Colombier 			ip->stats[OutDiscards]++;
3987dd7cddfSDavid du Colombier 			freeblist(bp);
3997dd7cddfSDavid du Colombier 			return;
4007dd7cddfSDavid du Colombier 		}
4017dd7cddfSDavid du Colombier 
4027dd7cddfSDavid du Colombier 		/* don't forward if packet has timed out */
4033ff48bf5SDavid du Colombier 		hop = h->ttl;
4043ff48bf5SDavid du Colombier 		if(hop < 1) {
40559cc4ca5SDavid du Colombier 			ip->stats[InHdrErrors]++;
4063ff48bf5SDavid du Colombier 			icmpttlexceeded(f, ifc->lifc->local, bp);
4077dd7cddfSDavid du Colombier 			freeblist(bp);
4087dd7cddfSDavid du Colombier 			return;
4097dd7cddfSDavid du Colombier 		}
4107dd7cddfSDavid du Colombier 
4119a747e4fSDavid du Colombier 		/* reassemble if the interface expects it */
41204b73bddSDavid du Colombier if(r->ifc == nil) panic("nil route rfc");
4139a747e4fSDavid du Colombier 		if(r->ifc->reassemble){
4149a747e4fSDavid du Colombier 			frag = nhgets(h->frag);
4159a747e4fSDavid du Colombier 			if(frag) {
4169a747e4fSDavid du Colombier 				h->tos = 0;
4179a747e4fSDavid du Colombier 				if(frag & IP_MF)
4189a747e4fSDavid du Colombier 					h->tos = 1;
4193ff48bf5SDavid du Colombier 				bp = ip4reassemble(ip, frag, bp, h);
4209a747e4fSDavid du Colombier 				if(bp == nil)
4219a747e4fSDavid du Colombier 					return;
4223ff48bf5SDavid du Colombier 				h = (Ip4hdr*)(bp->rp);
4239a747e4fSDavid du Colombier 			}
4249a747e4fSDavid du Colombier 		}
4259a747e4fSDavid du Colombier 
42659cc4ca5SDavid du Colombier 		ip->stats[ForwDatagrams]++;
4273ff48bf5SDavid du Colombier 		tos = h->tos;
4283ff48bf5SDavid du Colombier 		hop = h->ttl;
429a6a9e072SDavid du Colombier 		ipoput4(f, bp, 1, hop - 1, tos, &conv);
4307dd7cddfSDavid du Colombier 		return;
4317dd7cddfSDavid du Colombier 	}
4327dd7cddfSDavid du Colombier 
4337dd7cddfSDavid du Colombier 	frag = nhgets(h->frag);
4347dd7cddfSDavid du Colombier 	if(frag) {
4357dd7cddfSDavid du Colombier 		h->tos = 0;
4367dd7cddfSDavid du Colombier 		if(frag & IP_MF)
4377dd7cddfSDavid du Colombier 			h->tos = 1;
4383ff48bf5SDavid du Colombier 		bp = ip4reassemble(ip, frag, bp, h);
4397dd7cddfSDavid du Colombier 		if(bp == nil)
4407dd7cddfSDavid du Colombier 			return;
4413ff48bf5SDavid du Colombier 		h = (Ip4hdr*)(bp->rp);
4427dd7cddfSDavid du Colombier 	}
4437dd7cddfSDavid du Colombier 
444d9306527SDavid du Colombier 	/* don't let any frag info go up the stack */
445d9306527SDavid du Colombier 	h->frag[0] = 0;
446d9306527SDavid du Colombier 	h->frag[1] = 0;
447d9306527SDavid du Colombier 
4483ff48bf5SDavid du Colombier 	proto = h->proto;
4493ff48bf5SDavid du Colombier 	p = Fsrcvpcol(f, proto);
4507dd7cddfSDavid du Colombier 	if(p != nil && p->rcv != nil) {
45159cc4ca5SDavid du Colombier 		ip->stats[InDelivers]++;
4529a747e4fSDavid du Colombier 		(*p->rcv)(p, ifc, bp);
4537dd7cddfSDavid du Colombier 		return;
4547dd7cddfSDavid du Colombier 	}
45559cc4ca5SDavid du Colombier 	ip->stats[InDiscards]++;
45659cc4ca5SDavid du Colombier 	ip->stats[InUnknownProtos]++;
4577dd7cddfSDavid du Colombier 	freeblist(bp);
4587dd7cddfSDavid du Colombier }
4597dd7cddfSDavid du Colombier 
4607dd7cddfSDavid du Colombier int
ipstats(Fs * f,char * buf,int len)4617dd7cddfSDavid du Colombier ipstats(Fs *f, char *buf, int len)
4627dd7cddfSDavid du Colombier {
4637dd7cddfSDavid du Colombier 	IP *ip;
46459cc4ca5SDavid du Colombier 	char *p, *e;
46559cc4ca5SDavid du Colombier 	int i;
4667dd7cddfSDavid du Colombier 
4677dd7cddfSDavid du Colombier 	ip = f->ip;
46859cc4ca5SDavid du Colombier 	ip->stats[DefaultTTL] = MAXTTL;
46959cc4ca5SDavid du Colombier 
47059cc4ca5SDavid du Colombier 	p = buf;
47159cc4ca5SDavid du Colombier 	e = p+len;
4725e27dea9SDavid du Colombier 	for(i = 0; i < Nipstats; i++)
4735e27dea9SDavid du Colombier 		p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);
47459cc4ca5SDavid du Colombier 	return p - buf;
4757dd7cddfSDavid du Colombier }
4767dd7cddfSDavid du Colombier 
4777dd7cddfSDavid du Colombier Block*
ip4reassemble(IP * ip,int offset,Block * bp,Ip4hdr * ih)4783ff48bf5SDavid du Colombier ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
4797dd7cddfSDavid du Colombier {
4807dd7cddfSDavid du Colombier 	int fend;
4817dd7cddfSDavid du Colombier 	ushort id;
4823ff48bf5SDavid du Colombier 	Fragment4 *f, *fnext;
4837dd7cddfSDavid du Colombier 	ulong src, dst;
4847dd7cddfSDavid du Colombier 	Block *bl, **l, *last, *prev;
4857dd7cddfSDavid du Colombier 	int ovlap, len, fragsize, pktposn;
4867dd7cddfSDavid du Colombier 
4877dd7cddfSDavid du Colombier 	src = nhgetl(ih->src);
4887dd7cddfSDavid du Colombier 	dst = nhgetl(ih->dst);
4897dd7cddfSDavid du Colombier 	id = nhgets(ih->id);
4907dd7cddfSDavid du Colombier 
4917dd7cddfSDavid du Colombier 	/*
4927dd7cddfSDavid du Colombier 	 *  block lists are too hard, pullupblock into a single block
4937dd7cddfSDavid du Colombier 	 */
4947dd7cddfSDavid du Colombier 	if(bp->next){
4957dd7cddfSDavid du Colombier 		bp = pullupblock(bp, blocklen(bp));
4963ff48bf5SDavid du Colombier 		ih = (Ip4hdr*)(bp->rp);
4977dd7cddfSDavid du Colombier 	}
4987dd7cddfSDavid du Colombier 
4993ff48bf5SDavid du Colombier 	qlock(&ip->fraglock4);
5007dd7cddfSDavid du Colombier 
5017dd7cddfSDavid du Colombier 	/*
5027dd7cddfSDavid du Colombier 	 *  find a reassembly queue for this fragment
5037dd7cddfSDavid du Colombier 	 */
5043ff48bf5SDavid du Colombier 	for(f = ip->flisthead4; f; f = fnext){
5053ff48bf5SDavid du Colombier 		fnext = f->next;	/* because ipfragfree4 changes the list */
5067dd7cddfSDavid du Colombier 		if(f->src == src && f->dst == dst && f->id == id)
5077dd7cddfSDavid du Colombier 			break;
5083ff48bf5SDavid du Colombier 		if(f->age < NOW){
50959cc4ca5SDavid du Colombier 			ip->stats[ReasmTimeout]++;
5103ff48bf5SDavid du Colombier 			ipfragfree4(ip, f);
5117dd7cddfSDavid du Colombier 		}
5127dd7cddfSDavid du Colombier 	}
5137dd7cddfSDavid du Colombier 
5147dd7cddfSDavid du Colombier 	/*
5157dd7cddfSDavid du Colombier 	 *  if this isn't a fragmented packet, accept it
5167dd7cddfSDavid du Colombier 	 *  and get rid of any fragments that might go
5177dd7cddfSDavid du Colombier 	 *  with it.
5187dd7cddfSDavid du Colombier 	 */
5197dd7cddfSDavid du Colombier 	if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
5207dd7cddfSDavid du Colombier 		if(f != nil) {
5213ff48bf5SDavid du Colombier 			ipfragfree4(ip, f);
52259cc4ca5SDavid du Colombier 			ip->stats[ReasmFails]++;
5237dd7cddfSDavid du Colombier 		}
5243ff48bf5SDavid du Colombier 		qunlock(&ip->fraglock4);
5257dd7cddfSDavid du Colombier 		return bp;
5267dd7cddfSDavid du Colombier 	}
5277dd7cddfSDavid du Colombier 
5287ec5746aSDavid du Colombier 	if(bp->base+IPFRAGSZ >= bp->rp){
5297ec5746aSDavid du Colombier 		bp = padblock(bp, IPFRAGSZ);
5307ec5746aSDavid du Colombier 		bp->rp += IPFRAGSZ;
5317dd7cddfSDavid du Colombier 	}
5327dd7cddfSDavid du Colombier 
5337dd7cddfSDavid du Colombier 	BKFG(bp)->foff = offset<<3;
5343ff48bf5SDavid du Colombier 	BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
5357dd7cddfSDavid du Colombier 
5367dd7cddfSDavid du Colombier 	/* First fragment allocates a reassembly queue */
5377dd7cddfSDavid du Colombier 	if(f == nil) {
5383ff48bf5SDavid du Colombier 		f = ipfragallo4(ip);
5397dd7cddfSDavid du Colombier 		f->id = id;
5407dd7cddfSDavid du Colombier 		f->src = src;
5417dd7cddfSDavid du Colombier 		f->dst = dst;
5427dd7cddfSDavid du Colombier 
5437dd7cddfSDavid du Colombier 		f->blist = bp;
5447dd7cddfSDavid du Colombier 
5453ff48bf5SDavid du Colombier 		qunlock(&ip->fraglock4);
54659cc4ca5SDavid du Colombier 		ip->stats[ReasmReqds]++;
5477dd7cddfSDavid du Colombier 		return nil;
5487dd7cddfSDavid du Colombier 	}
5497dd7cddfSDavid du Colombier 
5507dd7cddfSDavid du Colombier 	/*
5517dd7cddfSDavid du Colombier 	 *  find the new fragment's position in the queue
5527dd7cddfSDavid du Colombier 	 */
5537dd7cddfSDavid du Colombier 	prev = nil;
5547dd7cddfSDavid du Colombier 	l = &f->blist;
5557dd7cddfSDavid du Colombier 	bl = f->blist;
5567dd7cddfSDavid du Colombier 	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
5577dd7cddfSDavid du Colombier 		prev = bl;
5587dd7cddfSDavid du Colombier 		l = &bl->next;
5597dd7cddfSDavid du Colombier 		bl = bl->next;
5607dd7cddfSDavid du Colombier 	}
5617dd7cddfSDavid du Colombier 
5627dd7cddfSDavid du Colombier 	/* Check overlap of a previous fragment - trim away as necessary */
5637dd7cddfSDavid du Colombier 	if(prev) {
5647dd7cddfSDavid du Colombier 		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
5657dd7cddfSDavid du Colombier 		if(ovlap > 0) {
5667dd7cddfSDavid du Colombier 			if(ovlap >= BKFG(bp)->flen) {
5677dd7cddfSDavid du Colombier 				freeblist(bp);
5683ff48bf5SDavid du Colombier 				qunlock(&ip->fraglock4);
5697dd7cddfSDavid du Colombier 				return nil;
5707dd7cddfSDavid du Colombier 			}
5717dd7cddfSDavid du Colombier 			BKFG(prev)->flen -= ovlap;
5727dd7cddfSDavid du Colombier 		}
5737dd7cddfSDavid du Colombier 	}
5747dd7cddfSDavid du Colombier 
5757dd7cddfSDavid du Colombier 	/* Link onto assembly queue */
5767dd7cddfSDavid du Colombier 	bp->next = *l;
5777dd7cddfSDavid du Colombier 	*l = bp;
5787dd7cddfSDavid du Colombier 
5797dd7cddfSDavid du Colombier 	/* Check to see if succeeding segments overlap */
5807dd7cddfSDavid du Colombier 	if(bp->next) {
5817dd7cddfSDavid du Colombier 		l = &bp->next;
5827dd7cddfSDavid du Colombier 		fend = BKFG(bp)->foff + BKFG(bp)->flen;
5837dd7cddfSDavid du Colombier 		/* Take completely covered segments out */
5847dd7cddfSDavid du Colombier 		while(*l) {
5857dd7cddfSDavid du Colombier 			ovlap = fend - BKFG(*l)->foff;
5867dd7cddfSDavid du Colombier 			if(ovlap <= 0)
5877dd7cddfSDavid du Colombier 				break;
5887dd7cddfSDavid du Colombier 			if(ovlap < BKFG(*l)->flen) {
5897dd7cddfSDavid du Colombier 				BKFG(*l)->flen -= ovlap;
5907dd7cddfSDavid du Colombier 				BKFG(*l)->foff += ovlap;
5917dd7cddfSDavid du Colombier 				/* move up ih hdrs */
5923ff48bf5SDavid du Colombier 				memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
5937dd7cddfSDavid du Colombier 				(*l)->rp += ovlap;
5947dd7cddfSDavid du Colombier 				break;
5957dd7cddfSDavid du Colombier 			}
5967dd7cddfSDavid du Colombier 			last = (*l)->next;
5977dd7cddfSDavid du Colombier 			(*l)->next = nil;
5987dd7cddfSDavid du Colombier 			freeblist(*l);
5997dd7cddfSDavid du Colombier 			*l = last;
6007dd7cddfSDavid du Colombier 		}
6017dd7cddfSDavid du Colombier 	}
6027dd7cddfSDavid du Colombier 
6037dd7cddfSDavid du Colombier 	/*
6047dd7cddfSDavid du Colombier 	 *  look for a complete packet.  if we get to a fragment
6057dd7cddfSDavid du Colombier 	 *  without IP_MF set, we're done.
6067dd7cddfSDavid du Colombier 	 */
6077dd7cddfSDavid du Colombier 	pktposn = 0;
6087dd7cddfSDavid du Colombier 	for(bl = f->blist; bl; bl = bl->next) {
6097dd7cddfSDavid du Colombier 		if(BKFG(bl)->foff != pktposn)
6107dd7cddfSDavid du Colombier 			break;
6117dd7cddfSDavid du Colombier 		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
6127dd7cddfSDavid du Colombier 			bl = f->blist;
6137dd7cddfSDavid du Colombier 			len = nhgets(BLKIP(bl)->length);
6147dd7cddfSDavid du Colombier 			bl->wp = bl->rp + len;
6157dd7cddfSDavid du Colombier 
6167dd7cddfSDavid du Colombier 			/* Pullup all the fragment headers and
6177dd7cddfSDavid du Colombier 			 * return a complete packet
6187dd7cddfSDavid du Colombier 			 */
6197dd7cddfSDavid du Colombier 			for(bl = bl->next; bl; bl = bl->next) {
6207dd7cddfSDavid du Colombier 				fragsize = BKFG(bl)->flen;
6217dd7cddfSDavid du Colombier 				len += fragsize;
6223ff48bf5SDavid du Colombier 				bl->rp += IP4HDR;
6237dd7cddfSDavid du Colombier 				bl->wp = bl->rp + fragsize;
6247dd7cddfSDavid du Colombier 			}
6257dd7cddfSDavid du Colombier 
6267dd7cddfSDavid du Colombier 			bl = f->blist;
6277dd7cddfSDavid du Colombier 			f->blist = nil;
6283ff48bf5SDavid du Colombier 			ipfragfree4(ip, f);
6297dd7cddfSDavid du Colombier 			ih = BLKIP(bl);
6307dd7cddfSDavid du Colombier 			hnputs(ih->length, len);
6313ff48bf5SDavid du Colombier 			qunlock(&ip->fraglock4);
63259cc4ca5SDavid du Colombier 			ip->stats[ReasmOKs]++;
6337dd7cddfSDavid du Colombier 			return bl;
6347dd7cddfSDavid du Colombier 		}
6357dd7cddfSDavid du Colombier 		pktposn += BKFG(bl)->flen;
6367dd7cddfSDavid du Colombier 	}
6373ff48bf5SDavid du Colombier 	qunlock(&ip->fraglock4);
6387dd7cddfSDavid du Colombier 	return nil;
6397dd7cddfSDavid du Colombier }
6407dd7cddfSDavid du Colombier 
6417dd7cddfSDavid du Colombier /*
6423ff48bf5SDavid du Colombier  * ipfragfree4 - Free a list of fragments - assume hold fraglock4
6437dd7cddfSDavid du Colombier  */
6447dd7cddfSDavid du Colombier void
ipfragfree4(IP * ip,Fragment4 * frag)6453ff48bf5SDavid du Colombier ipfragfree4(IP *ip, Fragment4 *frag)
6467dd7cddfSDavid du Colombier {
6473ff48bf5SDavid du Colombier 	Fragment4 *fl, **l;
6487dd7cddfSDavid du Colombier 
6497dd7cddfSDavid du Colombier 	if(frag->blist)
6507dd7cddfSDavid du Colombier 		freeblist(frag->blist);
6517dd7cddfSDavid du Colombier 
6527dd7cddfSDavid du Colombier 	frag->src = 0;
6537dd7cddfSDavid du Colombier 	frag->id = 0;
6547dd7cddfSDavid du Colombier 	frag->blist = nil;
6557dd7cddfSDavid du Colombier 
6563ff48bf5SDavid du Colombier 	l = &ip->flisthead4;
6577dd7cddfSDavid du Colombier 	for(fl = *l; fl; fl = fl->next) {
6587dd7cddfSDavid du Colombier 		if(fl == frag) {
6597dd7cddfSDavid du Colombier 			*l = frag->next;
6607dd7cddfSDavid du Colombier 			break;
6617dd7cddfSDavid du Colombier 		}
6627dd7cddfSDavid du Colombier 		l = &fl->next;
6637dd7cddfSDavid du Colombier 	}
6647dd7cddfSDavid du Colombier 
6653ff48bf5SDavid du Colombier 	frag->next = ip->fragfree4;
6663ff48bf5SDavid du Colombier 	ip->fragfree4 = frag;
6677dd7cddfSDavid du Colombier 
6687dd7cddfSDavid du Colombier }
6697dd7cddfSDavid du Colombier 
6707dd7cddfSDavid du Colombier /*
6713ff48bf5SDavid du Colombier  * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
6727dd7cddfSDavid du Colombier  */
6733ff48bf5SDavid du Colombier Fragment4 *
ipfragallo4(IP * ip)6743ff48bf5SDavid du Colombier ipfragallo4(IP *ip)
6757dd7cddfSDavid du Colombier {
6763ff48bf5SDavid du Colombier 	Fragment4 *f;
6777dd7cddfSDavid du Colombier 
6783ff48bf5SDavid du Colombier 	while(ip->fragfree4 == nil) {
6797dd7cddfSDavid du Colombier 		/* free last entry on fraglist */
6803ff48bf5SDavid du Colombier 		for(f = ip->flisthead4; f->next; f = f->next)
6817dd7cddfSDavid du Colombier 			;
6823ff48bf5SDavid du Colombier 		ipfragfree4(ip, f);
6837dd7cddfSDavid du Colombier 	}
6843ff48bf5SDavid du Colombier 	f = ip->fragfree4;
6853ff48bf5SDavid du Colombier 	ip->fragfree4 = f->next;
6863ff48bf5SDavid du Colombier 	f->next = ip->flisthead4;
6873ff48bf5SDavid du Colombier 	ip->flisthead4 = f;
6883ff48bf5SDavid du Colombier 	f->age = NOW + 30000;
6897dd7cddfSDavid du Colombier 
6907dd7cddfSDavid du Colombier 	return f;
6917dd7cddfSDavid du Colombier }
6927dd7cddfSDavid du Colombier 
6937dd7cddfSDavid du Colombier ushort
ipcsum(uchar * addr)6947dd7cddfSDavid du Colombier ipcsum(uchar *addr)
6957dd7cddfSDavid du Colombier {
6967dd7cddfSDavid du Colombier 	int len;
6977dd7cddfSDavid du Colombier 	ulong sum;
6987dd7cddfSDavid du Colombier 
6997dd7cddfSDavid du Colombier 	sum = 0;
7007dd7cddfSDavid du Colombier 	len = (addr[0]&0xf)<<2;
7017dd7cddfSDavid du Colombier 
7027dd7cddfSDavid du Colombier 	while(len > 0) {
7037dd7cddfSDavid du Colombier 		sum += addr[0]<<8 | addr[1] ;
7047dd7cddfSDavid du Colombier 		len -= 2;
7057dd7cddfSDavid du Colombier 		addr += 2;
7067dd7cddfSDavid du Colombier 	}
7077dd7cddfSDavid du Colombier 
7087dd7cddfSDavid du Colombier 	sum = (sum & 0xffff) + (sum >> 16);
7097dd7cddfSDavid du Colombier 	sum = (sum & 0xffff) + (sum >> 16);
7107dd7cddfSDavid du Colombier 
7117dd7cddfSDavid du Colombier 	return (sum^0xffff);
7127dd7cddfSDavid du Colombier }
713