xref: /plan9/sys/src/9/ip/ip.c (revision 6b7c5dce8459a5aafcfa1a501da6f475d0d3f97c)
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;
1327dd7cddfSDavid du Colombier 
1337dd7cddfSDavid du Colombier 	ip = f->ip;
1347dd7cddfSDavid du Colombier 
1357dd7cddfSDavid du Colombier 	/* Fill out the ip header */
1363ff48bf5SDavid du Colombier 	eh = (Ip4hdr*)(bp->rp);
1377dd7cddfSDavid du Colombier 
13859cc4ca5SDavid du Colombier 	ip->stats[OutRequests]++;
1397dd7cddfSDavid du Colombier 
1407dd7cddfSDavid du Colombier 	/* Number of uchars in data and ip header to write */
1417dd7cddfSDavid du Colombier 	len = blocklen(bp);
1427dd7cddfSDavid du Colombier 
1437dd7cddfSDavid du Colombier 	if(gating){
1447dd7cddfSDavid du Colombier 		chunk = nhgets(eh->length);
1457dd7cddfSDavid du Colombier 		if(chunk > len){
14659cc4ca5SDavid du Colombier 			ip->stats[OutDiscards]++;
1477dd7cddfSDavid du Colombier 			netlog(f, Logip, "short gated packet\n");
1487dd7cddfSDavid du Colombier 			goto free;
1497dd7cddfSDavid du Colombier 		}
1507dd7cddfSDavid du Colombier 		if(chunk < len)
1517dd7cddfSDavid du Colombier 			len = chunk;
1527dd7cddfSDavid du Colombier 	}
1537dd7cddfSDavid du Colombier 	if(len >= IP_MAX){
15459cc4ca5SDavid du Colombier 		ip->stats[OutDiscards]++;
1557dd7cddfSDavid du Colombier 		netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
1567dd7cddfSDavid du Colombier 		goto free;
1577dd7cddfSDavid du Colombier 	}
1587dd7cddfSDavid du Colombier 
159a6a9e072SDavid du Colombier 	r = v4lookup(f, eh->dst, c);
1607dd7cddfSDavid du Colombier 	if(r == nil){
16159cc4ca5SDavid du Colombier 		ip->stats[OutNoRoutes]++;
1627dd7cddfSDavid du Colombier 		netlog(f, Logip, "no interface %V\n", eh->dst);
163e6c6b7f8SDavid du Colombier 		rv = -1;
1647dd7cddfSDavid du Colombier 		goto free;
1657dd7cddfSDavid du Colombier 	}
1667dd7cddfSDavid du Colombier 
1677dd7cddfSDavid du Colombier 	ifc = r->ifc;
1687dd7cddfSDavid du Colombier 	if(r->type & (Rifc|Runi))
1697dd7cddfSDavid du Colombier 		gate = eh->dst;
1707dd7cddfSDavid du Colombier 	else
1717dd7cddfSDavid du Colombier 	if(r->type & (Rbcast|Rmulti)) {
1727dd7cddfSDavid du Colombier 		gate = eh->dst;
173a6a9e072SDavid du Colombier 		sr = v4lookup(f, eh->src, nil);
1747dd7cddfSDavid du Colombier 		if(sr != nil && (sr->type & Runi))
1757dd7cddfSDavid du Colombier 			ifc = sr->ifc;
1767dd7cddfSDavid du Colombier 	}
1777dd7cddfSDavid du Colombier 	else
1787dd7cddfSDavid du Colombier 		gate = r->v4.gate;
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier 	if(!gating)
1813ff48bf5SDavid du Colombier 		eh->vihl = IP_VER4|IP_HLEN4;
1827dd7cddfSDavid du Colombier 	eh->ttl = ttl;
1839a747e4fSDavid du Colombier 	if(!gating)
1847dd7cddfSDavid du Colombier 		eh->tos = tos;
1857dd7cddfSDavid du Colombier 
1867dd7cddfSDavid du Colombier 	if(!canrlock(ifc))
1877dd7cddfSDavid du Colombier 		goto free;
1887dd7cddfSDavid du Colombier 	if(waserror()){
1897dd7cddfSDavid du Colombier 		runlock(ifc);
1907dd7cddfSDavid du Colombier 		nexterror();
1917dd7cddfSDavid du Colombier 	}
1927dd7cddfSDavid du Colombier 	if(ifc->m == nil)
1937dd7cddfSDavid du Colombier 		goto raise;
1947dd7cddfSDavid du Colombier 
1957dd7cddfSDavid du Colombier 	/* If we dont need to fragment just send it */
1967ec5746aSDavid du Colombier 	if(c && c->maxfragsize && c->maxfragsize < ifc->maxtu)
1977ec5746aSDavid du Colombier 		medialen = c->maxfragsize - ifc->m->hsize;
1987ec5746aSDavid du Colombier 	else
1993f695129SDavid du Colombier 		medialen = ifc->maxtu - ifc->m->hsize;
2007dd7cddfSDavid du Colombier 	if(len <= medialen) {
2017dd7cddfSDavid du Colombier 		if(!gating)
2023ff48bf5SDavid du Colombier 			hnputs(eh->id, incref(&ip->id4));
2037dd7cddfSDavid du Colombier 		hnputs(eh->length, len);
2049a747e4fSDavid du Colombier 		if(!gating){
2057dd7cddfSDavid du Colombier 			eh->frag[0] = 0;
2067dd7cddfSDavid du Colombier 			eh->frag[1] = 0;
2079a747e4fSDavid du Colombier 		}
2087dd7cddfSDavid du Colombier 		eh->cksum[0] = 0;
2097dd7cddfSDavid du Colombier 		eh->cksum[1] = 0;
2107dd7cddfSDavid du Colombier 		hnputs(eh->cksum, ipcsum(&eh->vihl));
2117ec5746aSDavid du Colombier 		assert(bp->next == nil);
2127dd7cddfSDavid du Colombier 		ifc->m->bwrite(ifc, bp, V4, gate);
2137dd7cddfSDavid du Colombier 		runlock(ifc);
2147dd7cddfSDavid du Colombier 		poperror();
215e6c6b7f8SDavid du Colombier 		return 0;
2167dd7cddfSDavid du Colombier 	}
2177dd7cddfSDavid du Colombier 
218*6b7c5dceSDavid du Colombier 	if((eh->frag[0] & (IP_DF>>8)) && !gating)
219*6b7c5dceSDavid du Colombier 		print("%V: DF set\n", eh->dst);
220d9306527SDavid du Colombier 
2217dd7cddfSDavid du Colombier 	if(eh->frag[0] & (IP_DF>>8)){
22259cc4ca5SDavid du Colombier 		ip->stats[FragFails]++;
22359cc4ca5SDavid du Colombier 		ip->stats[OutDiscards]++;
224d9306527SDavid du Colombier 		icmpcantfrag(f, bp, medialen);
2257dd7cddfSDavid du Colombier 		netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
2267dd7cddfSDavid du Colombier 		goto raise;
2277dd7cddfSDavid du Colombier 	}
2287dd7cddfSDavid du Colombier 
2293ff48bf5SDavid du Colombier 	seglen = (medialen - IP4HDR) & ~7;
2307dd7cddfSDavid du Colombier 	if(seglen < 8){
23159cc4ca5SDavid du Colombier 		ip->stats[FragFails]++;
23259cc4ca5SDavid du Colombier 		ip->stats[OutDiscards]++;
2337dd7cddfSDavid du Colombier 		netlog(f, Logip, "%V seglen < 8\n", eh->dst);
2347dd7cddfSDavid du Colombier 		goto raise;
2357dd7cddfSDavid du Colombier 	}
2367dd7cddfSDavid du Colombier 
2373ff48bf5SDavid du Colombier 	dlen = len - IP4HDR;
2387dd7cddfSDavid du Colombier 	xp = bp;
2397dd7cddfSDavid du Colombier 	if(gating)
2407dd7cddfSDavid du Colombier 		lid = nhgets(eh->id);
2417dd7cddfSDavid du Colombier 	else
2423ff48bf5SDavid du Colombier 		lid = incref(&ip->id4);
2437dd7cddfSDavid du Colombier 
2443ff48bf5SDavid du Colombier 	offset = IP4HDR;
2457dd7cddfSDavid du Colombier 	while(xp != nil && offset && offset >= BLEN(xp)) {
2467dd7cddfSDavid du Colombier 		offset -= BLEN(xp);
2477dd7cddfSDavid du Colombier 		xp = xp->next;
2487dd7cddfSDavid du Colombier 	}
2497dd7cddfSDavid du Colombier 	xp->rp += offset;
2507dd7cddfSDavid du Colombier 
2519a747e4fSDavid du Colombier 	if(gating)
2529a747e4fSDavid du Colombier 		fragoff = nhgets(eh->frag)<<3;
2539a747e4fSDavid du Colombier 	else
2549a747e4fSDavid du Colombier 		fragoff = 0;
2559a747e4fSDavid du Colombier 	dlen += fragoff;
2569a747e4fSDavid du Colombier 	for(; fragoff < dlen; fragoff += seglen) {
2573ff48bf5SDavid du Colombier 		nb = allocb(IP4HDR+seglen);
2583ff48bf5SDavid du Colombier 		feh = (Ip4hdr*)(nb->rp);
2597dd7cddfSDavid du Colombier 
2603ff48bf5SDavid du Colombier 		memmove(nb->wp, eh, IP4HDR);
2613ff48bf5SDavid du Colombier 		nb->wp += IP4HDR;
2627dd7cddfSDavid du Colombier 
2637dd7cddfSDavid du Colombier 		if((fragoff + seglen) >= dlen) {
2647dd7cddfSDavid du Colombier 			seglen = dlen - fragoff;
2657dd7cddfSDavid du Colombier 			hnputs(feh->frag, fragoff>>3);
2667dd7cddfSDavid du Colombier 		}
2677dd7cddfSDavid du Colombier 		else
2687dd7cddfSDavid du Colombier 			hnputs(feh->frag, (fragoff>>3)|IP_MF);
2697dd7cddfSDavid du Colombier 
2703ff48bf5SDavid du Colombier 		hnputs(feh->length, seglen + IP4HDR);
2717dd7cddfSDavid du Colombier 		hnputs(feh->id, lid);
2727dd7cddfSDavid du Colombier 
2737dd7cddfSDavid du Colombier 		/* Copy up the data area */
2747dd7cddfSDavid du Colombier 		chunk = seglen;
2757dd7cddfSDavid du Colombier 		while(chunk) {
2767dd7cddfSDavid du Colombier 			if(!xp) {
27759cc4ca5SDavid du Colombier 				ip->stats[OutDiscards]++;
27859cc4ca5SDavid du Colombier 				ip->stats[FragFails]++;
2797dd7cddfSDavid du Colombier 				freeblist(nb);
2807dd7cddfSDavid du Colombier 				netlog(f, Logip, "!xp: chunk %d\n", chunk);
2817dd7cddfSDavid du Colombier 				goto raise;
2827dd7cddfSDavid du Colombier 			}
2837dd7cddfSDavid du Colombier 			blklen = chunk;
2847dd7cddfSDavid du Colombier 			if(BLEN(xp) < chunk)
2857dd7cddfSDavid du Colombier 				blklen = BLEN(xp);
2867dd7cddfSDavid du Colombier 			memmove(nb->wp, xp->rp, blklen);
2877dd7cddfSDavid du Colombier 			nb->wp += blklen;
2887dd7cddfSDavid du Colombier 			xp->rp += blklen;
2897dd7cddfSDavid du Colombier 			chunk -= blklen;
2907dd7cddfSDavid du Colombier 			if(xp->rp == xp->wp)
2917dd7cddfSDavid du Colombier 				xp = xp->next;
2927dd7cddfSDavid du Colombier 		}
2937dd7cddfSDavid du Colombier 
2947dd7cddfSDavid du Colombier 		feh->cksum[0] = 0;
2957dd7cddfSDavid du Colombier 		feh->cksum[1] = 0;
2967dd7cddfSDavid du Colombier 		hnputs(feh->cksum, ipcsum(&feh->vihl));
2977dd7cddfSDavid du Colombier 		ifc->m->bwrite(ifc, nb, V4, gate);
29859cc4ca5SDavid du Colombier 		ip->stats[FragCreates]++;
2997dd7cddfSDavid du Colombier 	}
30059cc4ca5SDavid du Colombier 	ip->stats[FragOKs]++;
3017dd7cddfSDavid du Colombier raise:
3027dd7cddfSDavid du Colombier 	runlock(ifc);
3037dd7cddfSDavid du Colombier 	poperror();
3047dd7cddfSDavid du Colombier free:
3057dd7cddfSDavid du Colombier 	freeblist(bp);
306e6c6b7f8SDavid du Colombier 	return rv;
3077dd7cddfSDavid du Colombier }
3087dd7cddfSDavid du Colombier 
3097dd7cddfSDavid du Colombier void
ipiput4(Fs * f,Ipifc * ifc,Block * bp)3103ff48bf5SDavid du Colombier ipiput4(Fs *f, Ipifc *ifc, Block *bp)
3117dd7cddfSDavid du Colombier {
3127dd7cddfSDavid du Colombier 	int hl;
3133ff48bf5SDavid du Colombier 	int hop, tos, proto, olen;
3143ff48bf5SDavid du Colombier 	Ip4hdr *h;
3157dd7cddfSDavid du Colombier 	Proto *p;
3167dd7cddfSDavid du Colombier 	ushort frag;
3177dd7cddfSDavid du Colombier 	int notforme;
3187dd7cddfSDavid du Colombier 	uchar *dp, v6dst[IPaddrlen];
3197dd7cddfSDavid du Colombier 	IP *ip;
320a6a9e072SDavid du Colombier 	Route *r;
3217ec5746aSDavid du Colombier 	Conv conv;
3227ec5746aSDavid du Colombier 
3233ff48bf5SDavid du Colombier 	if(BLKIPVER(bp) != IP_VER4) {
3243ff48bf5SDavid du Colombier 		ipiput6(f, ifc, bp);
3253ff48bf5SDavid du Colombier 		return;
3263ff48bf5SDavid du Colombier 	}
3277dd7cddfSDavid du Colombier 
3287dd7cddfSDavid du Colombier 	ip = f->ip;
32959cc4ca5SDavid du Colombier 	ip->stats[InReceives]++;
3307dd7cddfSDavid du Colombier 
3317dd7cddfSDavid du Colombier 	/*
3327dd7cddfSDavid du Colombier 	 *  Ensure we have all the header info in the first
3337dd7cddfSDavid du Colombier 	 *  block.  Make life easier for other protocols by
3347dd7cddfSDavid du Colombier 	 *  collecting up to the first 64 bytes in the first block.
3357dd7cddfSDavid du Colombier 	 */
3367dd7cddfSDavid du Colombier 	if(BLEN(bp) < 64) {
3377dd7cddfSDavid du Colombier 		hl = blocklen(bp);
3383ff48bf5SDavid du Colombier 		if(hl < IP4HDR)
3393ff48bf5SDavid du Colombier 			hl = IP4HDR;
3407dd7cddfSDavid du Colombier 		if(hl > 64)
3417dd7cddfSDavid du Colombier 			hl = 64;
3427dd7cddfSDavid du Colombier 		bp = pullupblock(bp, hl);
3437dd7cddfSDavid du Colombier 		if(bp == nil)
3447dd7cddfSDavid du Colombier 			return;
3457dd7cddfSDavid du Colombier 	}
3463ff48bf5SDavid du Colombier 
3473ff48bf5SDavid du Colombier 	h = (Ip4hdr*)(bp->rp);
3487dd7cddfSDavid du Colombier 
3497dd7cddfSDavid du Colombier 	/* dump anything that whose header doesn't checksum */
3505fab9909SDavid du Colombier 	if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
35159cc4ca5SDavid du Colombier 		ip->stats[InHdrErrors]++;
3527dd7cddfSDavid du Colombier 		netlog(f, Logip, "ip: checksum error %V\n", h->src);
3537dd7cddfSDavid du Colombier 		freeblist(bp);
3547dd7cddfSDavid du Colombier 		return;
3557dd7cddfSDavid du Colombier 	}
3567dd7cddfSDavid du Colombier 	v4tov6(v6dst, h->dst);
3577dd7cddfSDavid du Colombier 	notforme = ipforme(f, v6dst) == 0;
3587dd7cddfSDavid du Colombier 
3597dd7cddfSDavid du Colombier 	/* Check header length and version */
3603ff48bf5SDavid du Colombier 	if((h->vihl&0x0F) != IP_HLEN4) {
3617dd7cddfSDavid du Colombier 		hl = (h->vihl&0xF)<<2;
3623ff48bf5SDavid du Colombier 		if(hl < (IP_HLEN4<<2)) {
36359cc4ca5SDavid du Colombier 			ip->stats[InHdrErrors]++;
3647dd7cddfSDavid du Colombier 			netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
3657dd7cddfSDavid du Colombier 			freeblist(bp);
3667dd7cddfSDavid du Colombier 			return;
3677dd7cddfSDavid du Colombier 		}
3687dd7cddfSDavid du Colombier 		/* If this is not routed strip off the options */
3697dd7cddfSDavid du Colombier 		if(notforme == 0) {
370b7b24591SDavid du Colombier 			olen = nhgets(h->length);
3713ff48bf5SDavid du Colombier 			dp = bp->rp + (hl - (IP_HLEN4<<2));
3723ff48bf5SDavid du Colombier 			memmove(dp, h, IP_HLEN4<<2);
3737dd7cddfSDavid du Colombier 			bp->rp = dp;
3743ff48bf5SDavid du Colombier 			h = (Ip4hdr*)(bp->rp);
3753ff48bf5SDavid du Colombier 			h->vihl = (IP_VER4|IP_HLEN4);
3763ff48bf5SDavid du Colombier 			hnputs(h->length, olen-hl+(IP_HLEN4<<2));
3777dd7cddfSDavid du Colombier 		}
3787dd7cddfSDavid du Colombier 	}
3797dd7cddfSDavid du Colombier 
3807dd7cddfSDavid du Colombier 	/* route */
3817dd7cddfSDavid du Colombier 	if(notforme) {
3827dd7cddfSDavid du Colombier 		if(!ip->iprouting){
3837ec5746aSDavid du Colombier 			freeblist(bp);
3847dd7cddfSDavid du Colombier 			return;
3857dd7cddfSDavid du Colombier 		}
3863ff48bf5SDavid du Colombier 
387a6a9e072SDavid du Colombier 		/* don't forward to source's network */
38884ba54caSDavid du Colombier 		memset(&conv, 0, sizeof conv);
389a6a9e072SDavid du Colombier 		conv.r = nil;
390a6a9e072SDavid du Colombier 		r = v4lookup(f, h->dst, &conv);
39104b73bddSDavid du Colombier 		if(r == nil || r->ifc == ifc){
39259cc4ca5SDavid du Colombier 			ip->stats[OutDiscards]++;
3937dd7cddfSDavid du Colombier 			freeblist(bp);
3947dd7cddfSDavid du Colombier 			return;
3957dd7cddfSDavid du Colombier 		}
3967dd7cddfSDavid du Colombier 
3977dd7cddfSDavid du Colombier 		/* don't forward if packet has timed out */
3983ff48bf5SDavid du Colombier 		hop = h->ttl;
3993ff48bf5SDavid du Colombier 		if(hop < 1) {
40059cc4ca5SDavid du Colombier 			ip->stats[InHdrErrors]++;
4013ff48bf5SDavid du Colombier 			icmpttlexceeded(f, ifc->lifc->local, bp);
4027dd7cddfSDavid du Colombier 			freeblist(bp);
4037dd7cddfSDavid du Colombier 			return;
4047dd7cddfSDavid du Colombier 		}
4057dd7cddfSDavid du Colombier 
4069a747e4fSDavid du Colombier 		/* reassemble if the interface expects it */
40704b73bddSDavid du Colombier if(r->ifc == nil) panic("nil route rfc");
4089a747e4fSDavid du Colombier 		if(r->ifc->reassemble){
4099a747e4fSDavid du Colombier 			frag = nhgets(h->frag);
4109a747e4fSDavid du Colombier 			if(frag) {
4119a747e4fSDavid du Colombier 				h->tos = 0;
4129a747e4fSDavid du Colombier 				if(frag & IP_MF)
4139a747e4fSDavid du Colombier 					h->tos = 1;
4143ff48bf5SDavid du Colombier 				bp = ip4reassemble(ip, frag, bp, h);
4159a747e4fSDavid du Colombier 				if(bp == nil)
4169a747e4fSDavid du Colombier 					return;
4173ff48bf5SDavid du Colombier 				h = (Ip4hdr*)(bp->rp);
4189a747e4fSDavid du Colombier 			}
4199a747e4fSDavid du Colombier 		}
4209a747e4fSDavid du Colombier 
42159cc4ca5SDavid du Colombier 		ip->stats[ForwDatagrams]++;
4223ff48bf5SDavid du Colombier 		tos = h->tos;
4233ff48bf5SDavid du Colombier 		hop = h->ttl;
424a6a9e072SDavid du Colombier 		ipoput4(f, bp, 1, hop - 1, tos, &conv);
4257dd7cddfSDavid du Colombier 		return;
4267dd7cddfSDavid du Colombier 	}
4277dd7cddfSDavid du Colombier 
4287dd7cddfSDavid du Colombier 	frag = nhgets(h->frag);
4297dd7cddfSDavid du Colombier 	if(frag) {
4307dd7cddfSDavid du Colombier 		h->tos = 0;
4317dd7cddfSDavid du Colombier 		if(frag & IP_MF)
4327dd7cddfSDavid du Colombier 			h->tos = 1;
4333ff48bf5SDavid du Colombier 		bp = ip4reassemble(ip, frag, bp, h);
4347dd7cddfSDavid du Colombier 		if(bp == nil)
4357dd7cddfSDavid du Colombier 			return;
4363ff48bf5SDavid du Colombier 		h = (Ip4hdr*)(bp->rp);
4377dd7cddfSDavid du Colombier 	}
4387dd7cddfSDavid du Colombier 
439d9306527SDavid du Colombier 	/* don't let any frag info go up the stack */
440d9306527SDavid du Colombier 	h->frag[0] = 0;
441d9306527SDavid du Colombier 	h->frag[1] = 0;
442d9306527SDavid du Colombier 
4433ff48bf5SDavid du Colombier 	proto = h->proto;
4443ff48bf5SDavid du Colombier 	p = Fsrcvpcol(f, proto);
4457dd7cddfSDavid du Colombier 	if(p != nil && p->rcv != nil) {
44659cc4ca5SDavid du Colombier 		ip->stats[InDelivers]++;
4479a747e4fSDavid du Colombier 		(*p->rcv)(p, ifc, bp);
4487dd7cddfSDavid du Colombier 		return;
4497dd7cddfSDavid du Colombier 	}
45059cc4ca5SDavid du Colombier 	ip->stats[InDiscards]++;
45159cc4ca5SDavid du Colombier 	ip->stats[InUnknownProtos]++;
4527dd7cddfSDavid du Colombier 	freeblist(bp);
4537dd7cddfSDavid du Colombier }
4547dd7cddfSDavid du Colombier 
4557dd7cddfSDavid du Colombier int
ipstats(Fs * f,char * buf,int len)4567dd7cddfSDavid du Colombier ipstats(Fs *f, char *buf, int len)
4577dd7cddfSDavid du Colombier {
4587dd7cddfSDavid du Colombier 	IP *ip;
45959cc4ca5SDavid du Colombier 	char *p, *e;
46059cc4ca5SDavid du Colombier 	int i;
4617dd7cddfSDavid du Colombier 
4627dd7cddfSDavid du Colombier 	ip = f->ip;
46359cc4ca5SDavid du Colombier 	ip->stats[DefaultTTL] = MAXTTL;
46459cc4ca5SDavid du Colombier 
46559cc4ca5SDavid du Colombier 	p = buf;
46659cc4ca5SDavid du Colombier 	e = p+len;
4675e27dea9SDavid du Colombier 	for(i = 0; i < Nipstats; i++)
4685e27dea9SDavid du Colombier 		p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);
46959cc4ca5SDavid du Colombier 	return p - buf;
4707dd7cddfSDavid du Colombier }
4717dd7cddfSDavid du Colombier 
4727dd7cddfSDavid du Colombier Block*
ip4reassemble(IP * ip,int offset,Block * bp,Ip4hdr * ih)4733ff48bf5SDavid du Colombier ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
4747dd7cddfSDavid du Colombier {
4757dd7cddfSDavid du Colombier 	int fend;
4767dd7cddfSDavid du Colombier 	ushort id;
4773ff48bf5SDavid du Colombier 	Fragment4 *f, *fnext;
4787dd7cddfSDavid du Colombier 	ulong src, dst;
4797dd7cddfSDavid du Colombier 	Block *bl, **l, *last, *prev;
4807dd7cddfSDavid du Colombier 	int ovlap, len, fragsize, pktposn;
4817dd7cddfSDavid du Colombier 
4827dd7cddfSDavid du Colombier 	src = nhgetl(ih->src);
4837dd7cddfSDavid du Colombier 	dst = nhgetl(ih->dst);
4847dd7cddfSDavid du Colombier 	id = nhgets(ih->id);
4857dd7cddfSDavid du Colombier 
4867dd7cddfSDavid du Colombier 	/*
4877dd7cddfSDavid du Colombier 	 *  block lists are too hard, pullupblock into a single block
4887dd7cddfSDavid du Colombier 	 */
4897dd7cddfSDavid du Colombier 	if(bp->next){
4907dd7cddfSDavid du Colombier 		bp = pullupblock(bp, blocklen(bp));
4913ff48bf5SDavid du Colombier 		ih = (Ip4hdr*)(bp->rp);
4927dd7cddfSDavid du Colombier 	}
4937dd7cddfSDavid du Colombier 
4943ff48bf5SDavid du Colombier 	qlock(&ip->fraglock4);
4957dd7cddfSDavid du Colombier 
4967dd7cddfSDavid du Colombier 	/*
4977dd7cddfSDavid du Colombier 	 *  find a reassembly queue for this fragment
4987dd7cddfSDavid du Colombier 	 */
4993ff48bf5SDavid du Colombier 	for(f = ip->flisthead4; f; f = fnext){
5003ff48bf5SDavid du Colombier 		fnext = f->next;	/* because ipfragfree4 changes the list */
5017dd7cddfSDavid du Colombier 		if(f->src == src && f->dst == dst && f->id == id)
5027dd7cddfSDavid du Colombier 			break;
5033ff48bf5SDavid du Colombier 		if(f->age < NOW){
50459cc4ca5SDavid du Colombier 			ip->stats[ReasmTimeout]++;
5053ff48bf5SDavid du Colombier 			ipfragfree4(ip, f);
5067dd7cddfSDavid du Colombier 		}
5077dd7cddfSDavid du Colombier 	}
5087dd7cddfSDavid du Colombier 
5097dd7cddfSDavid du Colombier 	/*
5107dd7cddfSDavid du Colombier 	 *  if this isn't a fragmented packet, accept it
5117dd7cddfSDavid du Colombier 	 *  and get rid of any fragments that might go
5127dd7cddfSDavid du Colombier 	 *  with it.
5137dd7cddfSDavid du Colombier 	 */
5147dd7cddfSDavid du Colombier 	if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
5157dd7cddfSDavid du Colombier 		if(f != nil) {
5163ff48bf5SDavid du Colombier 			ipfragfree4(ip, f);
51759cc4ca5SDavid du Colombier 			ip->stats[ReasmFails]++;
5187dd7cddfSDavid du Colombier 		}
5193ff48bf5SDavid du Colombier 		qunlock(&ip->fraglock4);
5207dd7cddfSDavid du Colombier 		return bp;
5217dd7cddfSDavid du Colombier 	}
5227dd7cddfSDavid du Colombier 
5237ec5746aSDavid du Colombier 	if(bp->base+IPFRAGSZ >= bp->rp){
5247ec5746aSDavid du Colombier 		bp = padblock(bp, IPFRAGSZ);
5257ec5746aSDavid du Colombier 		bp->rp += IPFRAGSZ;
5267dd7cddfSDavid du Colombier 	}
5277dd7cddfSDavid du Colombier 
5287dd7cddfSDavid du Colombier 	BKFG(bp)->foff = offset<<3;
5293ff48bf5SDavid du Colombier 	BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
5307dd7cddfSDavid du Colombier 
5317dd7cddfSDavid du Colombier 	/* First fragment allocates a reassembly queue */
5327dd7cddfSDavid du Colombier 	if(f == nil) {
5333ff48bf5SDavid du Colombier 		f = ipfragallo4(ip);
5347dd7cddfSDavid du Colombier 		f->id = id;
5357dd7cddfSDavid du Colombier 		f->src = src;
5367dd7cddfSDavid du Colombier 		f->dst = dst;
5377dd7cddfSDavid du Colombier 
5387dd7cddfSDavid du Colombier 		f->blist = bp;
5397dd7cddfSDavid du Colombier 
5403ff48bf5SDavid du Colombier 		qunlock(&ip->fraglock4);
54159cc4ca5SDavid du Colombier 		ip->stats[ReasmReqds]++;
5427dd7cddfSDavid du Colombier 		return nil;
5437dd7cddfSDavid du Colombier 	}
5447dd7cddfSDavid du Colombier 
5457dd7cddfSDavid du Colombier 	/*
5467dd7cddfSDavid du Colombier 	 *  find the new fragment's position in the queue
5477dd7cddfSDavid du Colombier 	 */
5487dd7cddfSDavid du Colombier 	prev = nil;
5497dd7cddfSDavid du Colombier 	l = &f->blist;
5507dd7cddfSDavid du Colombier 	bl = f->blist;
5517dd7cddfSDavid du Colombier 	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
5527dd7cddfSDavid du Colombier 		prev = bl;
5537dd7cddfSDavid du Colombier 		l = &bl->next;
5547dd7cddfSDavid du Colombier 		bl = bl->next;
5557dd7cddfSDavid du Colombier 	}
5567dd7cddfSDavid du Colombier 
5577dd7cddfSDavid du Colombier 	/* Check overlap of a previous fragment - trim away as necessary */
5587dd7cddfSDavid du Colombier 	if(prev) {
5597dd7cddfSDavid du Colombier 		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
5607dd7cddfSDavid du Colombier 		if(ovlap > 0) {
5617dd7cddfSDavid du Colombier 			if(ovlap >= BKFG(bp)->flen) {
5627dd7cddfSDavid du Colombier 				freeblist(bp);
5633ff48bf5SDavid du Colombier 				qunlock(&ip->fraglock4);
5647dd7cddfSDavid du Colombier 				return nil;
5657dd7cddfSDavid du Colombier 			}
5667dd7cddfSDavid du Colombier 			BKFG(prev)->flen -= ovlap;
5677dd7cddfSDavid du Colombier 		}
5687dd7cddfSDavid du Colombier 	}
5697dd7cddfSDavid du Colombier 
5707dd7cddfSDavid du Colombier 	/* Link onto assembly queue */
5717dd7cddfSDavid du Colombier 	bp->next = *l;
5727dd7cddfSDavid du Colombier 	*l = bp;
5737dd7cddfSDavid du Colombier 
5747dd7cddfSDavid du Colombier 	/* Check to see if succeeding segments overlap */
5757dd7cddfSDavid du Colombier 	if(bp->next) {
5767dd7cddfSDavid du Colombier 		l = &bp->next;
5777dd7cddfSDavid du Colombier 		fend = BKFG(bp)->foff + BKFG(bp)->flen;
5787dd7cddfSDavid du Colombier 		/* Take completely covered segments out */
5797dd7cddfSDavid du Colombier 		while(*l) {
5807dd7cddfSDavid du Colombier 			ovlap = fend - BKFG(*l)->foff;
5817dd7cddfSDavid du Colombier 			if(ovlap <= 0)
5827dd7cddfSDavid du Colombier 				break;
5837dd7cddfSDavid du Colombier 			if(ovlap < BKFG(*l)->flen) {
5847dd7cddfSDavid du Colombier 				BKFG(*l)->flen -= ovlap;
5857dd7cddfSDavid du Colombier 				BKFG(*l)->foff += ovlap;
5867dd7cddfSDavid du Colombier 				/* move up ih hdrs */
5873ff48bf5SDavid du Colombier 				memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
5887dd7cddfSDavid du Colombier 				(*l)->rp += ovlap;
5897dd7cddfSDavid du Colombier 				break;
5907dd7cddfSDavid du Colombier 			}
5917dd7cddfSDavid du Colombier 			last = (*l)->next;
5927dd7cddfSDavid du Colombier 			(*l)->next = nil;
5937dd7cddfSDavid du Colombier 			freeblist(*l);
5947dd7cddfSDavid du Colombier 			*l = last;
5957dd7cddfSDavid du Colombier 		}
5967dd7cddfSDavid du Colombier 	}
5977dd7cddfSDavid du Colombier 
5987dd7cddfSDavid du Colombier 	/*
5997dd7cddfSDavid du Colombier 	 *  look for a complete packet.  if we get to a fragment
6007dd7cddfSDavid du Colombier 	 *  without IP_MF set, we're done.
6017dd7cddfSDavid du Colombier 	 */
6027dd7cddfSDavid du Colombier 	pktposn = 0;
6037dd7cddfSDavid du Colombier 	for(bl = f->blist; bl; bl = bl->next) {
6047dd7cddfSDavid du Colombier 		if(BKFG(bl)->foff != pktposn)
6057dd7cddfSDavid du Colombier 			break;
6067dd7cddfSDavid du Colombier 		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
6077dd7cddfSDavid du Colombier 			bl = f->blist;
6087dd7cddfSDavid du Colombier 			len = nhgets(BLKIP(bl)->length);
6097dd7cddfSDavid du Colombier 			bl->wp = bl->rp + len;
6107dd7cddfSDavid du Colombier 
6117dd7cddfSDavid du Colombier 			/* Pullup all the fragment headers and
6127dd7cddfSDavid du Colombier 			 * return a complete packet
6137dd7cddfSDavid du Colombier 			 */
6147dd7cddfSDavid du Colombier 			for(bl = bl->next; bl; bl = bl->next) {
6157dd7cddfSDavid du Colombier 				fragsize = BKFG(bl)->flen;
6167dd7cddfSDavid du Colombier 				len += fragsize;
6173ff48bf5SDavid du Colombier 				bl->rp += IP4HDR;
6187dd7cddfSDavid du Colombier 				bl->wp = bl->rp + fragsize;
6197dd7cddfSDavid du Colombier 			}
6207dd7cddfSDavid du Colombier 
6217dd7cddfSDavid du Colombier 			bl = f->blist;
6227dd7cddfSDavid du Colombier 			f->blist = nil;
6233ff48bf5SDavid du Colombier 			ipfragfree4(ip, f);
6247dd7cddfSDavid du Colombier 			ih = BLKIP(bl);
6257dd7cddfSDavid du Colombier 			hnputs(ih->length, len);
6263ff48bf5SDavid du Colombier 			qunlock(&ip->fraglock4);
62759cc4ca5SDavid du Colombier 			ip->stats[ReasmOKs]++;
6287dd7cddfSDavid du Colombier 			return bl;
6297dd7cddfSDavid du Colombier 		}
6307dd7cddfSDavid du Colombier 		pktposn += BKFG(bl)->flen;
6317dd7cddfSDavid du Colombier 	}
6323ff48bf5SDavid du Colombier 	qunlock(&ip->fraglock4);
6337dd7cddfSDavid du Colombier 	return nil;
6347dd7cddfSDavid du Colombier }
6357dd7cddfSDavid du Colombier 
6367dd7cddfSDavid du Colombier /*
6373ff48bf5SDavid du Colombier  * ipfragfree4 - Free a list of fragments - assume hold fraglock4
6387dd7cddfSDavid du Colombier  */
6397dd7cddfSDavid du Colombier void
ipfragfree4(IP * ip,Fragment4 * frag)6403ff48bf5SDavid du Colombier ipfragfree4(IP *ip, Fragment4 *frag)
6417dd7cddfSDavid du Colombier {
6423ff48bf5SDavid du Colombier 	Fragment4 *fl, **l;
6437dd7cddfSDavid du Colombier 
6447dd7cddfSDavid du Colombier 	if(frag->blist)
6457dd7cddfSDavid du Colombier 		freeblist(frag->blist);
6467dd7cddfSDavid du Colombier 
6477dd7cddfSDavid du Colombier 	frag->src = 0;
6487dd7cddfSDavid du Colombier 	frag->id = 0;
6497dd7cddfSDavid du Colombier 	frag->blist = nil;
6507dd7cddfSDavid du Colombier 
6513ff48bf5SDavid du Colombier 	l = &ip->flisthead4;
6527dd7cddfSDavid du Colombier 	for(fl = *l; fl; fl = fl->next) {
6537dd7cddfSDavid du Colombier 		if(fl == frag) {
6547dd7cddfSDavid du Colombier 			*l = frag->next;
6557dd7cddfSDavid du Colombier 			break;
6567dd7cddfSDavid du Colombier 		}
6577dd7cddfSDavid du Colombier 		l = &fl->next;
6587dd7cddfSDavid du Colombier 	}
6597dd7cddfSDavid du Colombier 
6603ff48bf5SDavid du Colombier 	frag->next = ip->fragfree4;
6613ff48bf5SDavid du Colombier 	ip->fragfree4 = frag;
6627dd7cddfSDavid du Colombier 
6637dd7cddfSDavid du Colombier }
6647dd7cddfSDavid du Colombier 
6657dd7cddfSDavid du Colombier /*
6663ff48bf5SDavid du Colombier  * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
6677dd7cddfSDavid du Colombier  */
6683ff48bf5SDavid du Colombier Fragment4 *
ipfragallo4(IP * ip)6693ff48bf5SDavid du Colombier ipfragallo4(IP *ip)
6707dd7cddfSDavid du Colombier {
6713ff48bf5SDavid du Colombier 	Fragment4 *f;
6727dd7cddfSDavid du Colombier 
6733ff48bf5SDavid du Colombier 	while(ip->fragfree4 == nil) {
6747dd7cddfSDavid du Colombier 		/* free last entry on fraglist */
6753ff48bf5SDavid du Colombier 		for(f = ip->flisthead4; f->next; f = f->next)
6767dd7cddfSDavid du Colombier 			;
6773ff48bf5SDavid du Colombier 		ipfragfree4(ip, f);
6787dd7cddfSDavid du Colombier 	}
6793ff48bf5SDavid du Colombier 	f = ip->fragfree4;
6803ff48bf5SDavid du Colombier 	ip->fragfree4 = f->next;
6813ff48bf5SDavid du Colombier 	f->next = ip->flisthead4;
6823ff48bf5SDavid du Colombier 	ip->flisthead4 = f;
6833ff48bf5SDavid du Colombier 	f->age = NOW + 30000;
6847dd7cddfSDavid du Colombier 
6857dd7cddfSDavid du Colombier 	return f;
6867dd7cddfSDavid du Colombier }
6877dd7cddfSDavid du Colombier 
6887dd7cddfSDavid du Colombier ushort
ipcsum(uchar * addr)6897dd7cddfSDavid du Colombier ipcsum(uchar *addr)
6907dd7cddfSDavid du Colombier {
6917dd7cddfSDavid du Colombier 	int len;
6927dd7cddfSDavid du Colombier 	ulong sum;
6937dd7cddfSDavid du Colombier 
6947dd7cddfSDavid du Colombier 	sum = 0;
6957dd7cddfSDavid du Colombier 	len = (addr[0]&0xf)<<2;
6967dd7cddfSDavid du Colombier 
6977dd7cddfSDavid du Colombier 	while(len > 0) {
6987dd7cddfSDavid du Colombier 		sum += addr[0]<<8 | addr[1] ;
6997dd7cddfSDavid du Colombier 		len -= 2;
7007dd7cddfSDavid du Colombier 		addr += 2;
7017dd7cddfSDavid du Colombier 	}
7027dd7cddfSDavid du Colombier 
7037dd7cddfSDavid du Colombier 	sum = (sum & 0xffff) + (sum >> 16);
7047dd7cddfSDavid du Colombier 	sum = (sum & 0xffff) + (sum >> 16);
7057dd7cddfSDavid du Colombier 
7067dd7cddfSDavid du Colombier 	return (sum^0xffff);
7077dd7cddfSDavid du Colombier }
708