708 lines · 13.5 KB · C
RawDownload
#include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h" #include	"ip.h" static char *statnames[] ={[Forwarding]	"Forwarding",[DefaultTTL]	"DefaultTTL",[InReceives]	"InReceives",[InHdrErrors]	"InHdrErrors",[InAddrErrors]	"InAddrErrors",[ForwDatagrams]	"ForwDatagrams",[InUnknownProtos]	"InUnknownProtos",[InDiscards]	"InDiscards",[InDelivers]	"InDelivers",[OutRequests]	"OutRequests",[OutDiscards]	"OutDiscards",[OutNoRoutes]	"OutNoRoutes",[ReasmTimeout]	"ReasmTimeout",[ReasmReqds]	"ReasmReqds",[ReasmOKs]	"ReasmOKs",[ReasmFails]	"ReasmFails",[FragOKs]	"FragOKs",[FragFails]	"FragFails",[FragCreates]	"FragCreates",}; static Block*		ip4reassemble(IP*, int, Block*);static void		ipfragfree4(IP*, Fragment4*);static Fragment4*	ipfragallo4(IP*); static voidinitfrag(IP *ip, int size){	Fragment4 *fq4, *eq4;	Fragment6 *fq6, *eq6; 	ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);	if(ip->fragfree4 == nil)		panic("initfrag"); 	eq4 = &ip->fragfree4[size];	for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)		fq4->next = fq4+1; 	ip->fragfree4[size-1].next = nil; 	ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);	if(ip->fragfree6 == nil)		panic("initfrag"); 	eq6 = &ip->fragfree6[size];	for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)		fq6->next = fq6+1; 	ip->fragfree6[size-1].next = nil;} voidip_init(Fs *f){	IP *ip; 	ip = smalloc(sizeof(IP));	ip->stats[DefaultTTL] = MAXTTL;	initfrag(ip, 100);	f->ip = ip; 	ip_init_6(f);} voidiprouting(Fs *f, int on){	f->ip->iprouting = on;	if(f->ip->iprouting==0)		f->ip->stats[Forwarding] = 2;	else		f->ip->stats[Forwarding] = 1;} intipoput4(Fs *f, Block *bp, Ipifc *gating, int ttl, int tos, Routehint *rh){	Ipifc *ifc;	uchar *gate;	ulong fragoff;	Block *xp, *nb;	Ip4hdr *eh, *feh;	int lid, len, seglen, chunk, hlen, dlen, blklen, offset, medialen;	Route *r;	IP *ip;	int rv = 0; 	ip = f->ip;	ip->stats[OutRequests]++; 	/* Fill out the ip header */	eh = (Ip4hdr*)bp->rp;	assert(BLEN(bp) >= IP4HDR);	len = blocklen(bp);	if(len >= IP_MAX){		ip->stats[OutDiscards]++;		netlog(f, Logip, "%V -> %V: exceeded ip max size: %d\n", eh->src, eh->dst, len);		goto free;	} 	r = v4lookup(f, eh->dst, eh->src, rh);	if(r == nil || (ifc = r->ifc) == nil){		ip->stats[OutNoRoutes]++;		netlog(f, Logip, "%V -> %V: no interface\n", eh->src, eh->dst);		rv = -1;		goto free;	} 	if(r->type & (Rifc|Runi|Rbcast|Rmulti))		gate = eh->dst;	else		gate = r->v4.gate; 	if(!canrlock(ifc)){		ip->stats[OutDiscards]++;		goto free;	}	if(waserror()){		ifc->outerr++;		runlock(ifc);		/* bp is freed by m->bwrite() called from ipifcoput() */		return -1;	}	if(ifc->m == nil || ifc->ifcid != r->ifcid){		rv = -1;		goto raise;	} 	medialen = ifc->maxtu - ifc->m->hsize;	if(gating != nil) {		int mtu = gating->maxtu - gating->m->hsize;		if(medialen < mtu)			mtu = medialen;		tcpmssclamp(bp->rp, BLEN(bp), mtu);	} else {		eh->vihl = IP_VER4|IP_HLEN4;		eh->tos = tos; 		/* bypass for loopback */		if(r->type & Runi){			eh->ttl = ttl;			hnputs(eh->length, len);			hnputs(eh->id, incref(&ip->id4));			eh->frag[0] = 0;			eh->frag[1] = 0;			eh->cksum[0] = 0;			eh->cksum[1] = 0;			hnputs(eh->cksum, ipcsum(&eh->vihl));			bp->flag |= Bipck;			(*loopbackmedium.bwrite)(ifc, concatblock(bp), V4, gate, rh);			runlock(ifc);			poperror();			return 0;		}	}	eh->ttl = ttl; 	/* If we dont need to fragment just send it */	if(len <= medialen) {		hnputs(eh->length, len);		if(gating == nil){			hnputs(eh->id, incref(&ip->id4));			eh->frag[0] = 0;			eh->frag[1] = 0;		}		eh->cksum[0] = 0;		eh->cksum[1] = 0;		hnputs(eh->cksum, ipcsum(&eh->vihl));		ipifcoput(ifc, bp, V4, gate, rh);		runlock(ifc);		poperror();		return 0;	} 	if(eh->frag[0] & (IP_DF>>8)){		ip->stats[FragFails]++;		ip->stats[OutDiscards]++;		icmpcantfrag(f, gating!=nil? gating: ifc, bp, medialen);		netlog(f, Logip, "%V -> %V: can't fragment with DF flag set\n", eh->src, eh->dst);		goto raise;	} 	hlen = (eh->vihl & 0xF)<<2;	seglen = (medialen - hlen) & ~7;	if(seglen < 8){		ip->stats[FragFails]++;		ip->stats[OutDiscards]++;		netlog(f, Logip, "%V -> %V: can't fragment with seglen < 8\n", eh->src, eh->dst);		goto raise;	} 	dlen = len - hlen;	xp = bp;	if(gating)		lid = nhgets(eh->id);	else		lid = incref(&ip->id4); 	offset = hlen;	while(offset && offset >= BLEN(xp)) {		offset -= BLEN(xp);		xp = xp->next;	}	xp->rp += offset; 	if(gating)		fragoff = nhgets(eh->frag)<<3;	else		fragoff = 0;	dlen += fragoff;	for(; fragoff < dlen; fragoff += seglen) {		nb = allocb(hlen+seglen);		feh = (Ip4hdr*)nb->rp; 		memmove(nb->wp, eh, hlen);		nb->wp += hlen; 		if((fragoff + seglen) >= dlen) {			seglen = dlen - fragoff;			hnputs(feh->frag, fragoff>>3);		}		else			hnputs(feh->frag, (fragoff>>3)|IP_MF); 		hnputs(feh->length, seglen + hlen);		hnputs(feh->id, lid); 		/* Copy up the data area */		chunk = seglen;		while(chunk) {			if(xp == nil) {				ip->stats[OutDiscards]++;				ip->stats[FragFails]++;				freeblist(nb);				netlog(f, Logip, "xp == nil: chunk %d\n", chunk);				goto raise;			}			blklen = chunk;			if(BLEN(xp) < chunk)				blklen = BLEN(xp);			memmove(nb->wp, xp->rp, blklen);			nb->wp += blklen;			xp->rp += blklen;			chunk -= blklen;			if(xp->rp == xp->wp)				xp = xp->next;		} 		feh->cksum[0] = 0;		feh->cksum[1] = 0;		hnputs(feh->cksum, ipcsum(&feh->vihl));		ipifcoput(ifc, nb, V4, gate, rh);		ip->stats[FragCreates]++;	}	ip->stats[FragOKs]++;raise:	runlock(ifc);	poperror();free:	freeblist(bp);	return rv;} voidipiput4(Fs *f, Ipifc *ifc, Block *bp){	int hl, len, hop;	uchar v6dst[IPaddrlen];	ushort frag;	Ip4hdr *h;	Proto *p;	IP *ip; 	if((bp->rp[0]&0xF0) != IP_VER4) {		ipiput6(f, ifc, bp);		return;	} 	if(ifc->lifc == nil)		goto drop; 	ifc->in++; 	ip = f->ip;	ip->stats[InReceives]++; 	/*	 *  Ensure we have all the header info in the first	 *  block.  Make life easier for other protocols by	 *  collecting up to the first 64 bytes in the first block.	 */	if(BLEN(bp) < 64) {		hl = blocklen(bp);		if(hl < IP4HDR)			hl = IP4HDR;		if(hl > 64)			hl = 64;		bp = pullupblock(bp, hl);		if(bp == nil)			return;	} 	h = (Ip4hdr*)bp->rp;	hl = (h->vihl & 0xF)<<2;	if(hl < IP4HDR || hl > BLEN(bp)) {		ip->stats[InHdrErrors]++;		netlog(f, Logip, "%V -> %V: bad ip header length: %d\n", h->src, h->dst, hl);		goto drop;	}	if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {		ip->stats[InHdrErrors]++;		netlog(f, Logip, "%V -> %V: bad ip header checksum\n", h->src, h->dst);		goto drop;	}	len = nhgets(h->length);	if(len < hl || (bp = trimblock(bp, 0, len)) == nil){		ip->stats[InHdrErrors]++;		netlog(f, Logip, "%V -> %V: bogus packet length: %d\n", h->src, h->dst, len);		if(bp != nil)			goto drop;		return;	}	h = (Ip4hdr*)bp->rp; 	/* route */	v4tov6(v6dst, h->dst);	if(!ipforme(f, v6dst)) {		Routehint rh;		Route *r;		Ipifc *nifc; 		if(!ip->iprouting)			goto drop; 		/* don't forward to source's network */		rh.r = nil;		rh.a = nil;		r = v4lookup(f, h->dst, h->src, &rh);		if(r == nil || (nifc = r->ifc) == nil		|| (nifc == ifc && !ifc->reflect)){			ip->stats[OutDiscards]++;			goto drop;		} 		/* don't forward if packet has timed out */		hop = h->ttl;		if(hop <= 1) {			ip->stats[InHdrErrors]++;			icmpttlexceeded(f, ifc, bp);			goto drop;		} 		if(r->type & Rtrans) {			p = Fsrcvpcolx(f, h->proto);			if(p == nil || p->forward == nil){				ip->stats[OutDiscards]++;				goto drop;			} 			if(hl > IP4HDR) {				hl -= IP4HDR;				len -= hl;				bp->rp += hl;				memmove(bp->rp, h, IP4HDR);				h = (Ip4hdr*)bp->rp;				h->vihl = IP_VER4|IP_HLEN4;				hnputs(h->length, len);			} 			frag = nhgets(h->frag);			if(frag & (IP_MF|IP_FO)) {				bp = ip4reassemble(ip, frag, bp);				if(bp == nil)					return;			} 			bp = (*p->forward)(p, bp, r);			if(bp == nil)				return;			h = (Ip4hdr*)bp->rp;		} else if(nifc->reassemble) {			/* reassemble as the interface expects it */			frag = nhgets(h->frag);			if(frag & (IP_MF|IP_FO)) {				bp = ip4reassemble(ip, frag, bp);				if(bp == nil)					return;				h = (Ip4hdr*)bp->rp;			}		} 		ip->stats[ForwDatagrams]++;		ipoput4(f, bp, ifc, hop - 1, h->tos, &rh);		return;	} 	/* If this is not routed strip off the options */	if(hl > IP4HDR) {		hl -= IP4HDR;		len -= hl;		bp->rp += hl;		memmove(bp->rp, h, IP4HDR);		h = (Ip4hdr*)bp->rp;		h->vihl = IP_VER4|IP_HLEN4;		hnputs(h->length, len);	} 	frag = nhgets(h->frag);	if(frag & (IP_MF|IP_FO)) {		bp = ip4reassemble(ip, frag, bp);		if(bp == nil)			return;		h = (Ip4hdr*)bp->rp;	} 	p = Fsrcvpcol(f, h->proto);	if(p != nil && p->rcv != nil) {		ip->stats[InDelivers]++;		(*p->rcv)(p, ifc, bp);		return;	}	ip->stats[InDiscards]++;	ip->stats[InUnknownProtos]++;drop:	freeblist(bp);} intipstats(Fs *f, char *buf, int len){	IP *ip;	char *p, *e;	int i; 	ip = f->ip;	p = buf;	e = p+len;	for(i = 0; i < Nipstats; i++)		p = seprint(p, e, "%s: %llud\n", statnames[i], ip->stats[i]);	return p - buf;} static Block*ip4reassemble(IP *ip, int offset, Block *bp){	int ovlap, fragsize, len;	ulong src, dst;	ushort id;	Block *bl, **l, *prev;	Fragment4 *f, *fnext;	Ipfrag *fp, *fq;	Ip4hdr *ih; 	/*	 *  block lists are too hard, concatblock into a single block	 */	bp = concatblock(bp); 	ih = (Ip4hdr*)bp->rp;	src = nhgetl(ih->src);	dst = nhgetl(ih->dst);	id = nhgets(ih->id);	fragsize = BLEN(bp) - ((ih->vihl&0xF)<<2); 	qlock(&ip->fraglock4); 	/*	 *  find a reassembly queue for this fragment	 */	for(f = ip->flisthead4; f != nil; f = fnext){		fnext = f->next;	/* because ipfragfree4 changes the list */		if(f->id == id && f->src == src && f->dst == dst)			break;		if(f->age < NOW){			ip->stats[ReasmTimeout]++;			ipfragfree4(ip, f);		}	} 	/*	 *  if this isn't a fragmented packet, accept it	 *  and get rid of any fragments that might go	 *  with it.	 */	if((offset & (IP_MF|IP_FO)) == 0) {		if(f != nil) {			ip->stats[ReasmFails]++;			ipfragfree4(ip, f);

Lines 1–500 of 708.