plan 9 kernel history: overview | file list | diff list

1998/0307/ip/ip.c (diff list | history)

ip/ip.c on 1997/0327
1997/0327    
#include	"u.h" 
#include	"../port/lib.h" 
#include	"mem.h" 
#include	"dat.h" 
#include	"fns.h" 
#include	"../port/error.h" 
 
#include	"ip.h" 
 
typedef struct Iphdr	Iphdr; 
typedef struct Fragment	Fragment; 
typedef struct Ipfrag	Ipfrag; 
 
enum 
{ 
	IPHDR		= 20,		/* sizeof(Iphdr) */ 
	IP_VER		= 0x40,		/* Using IP version 4 */ 
	IP_HLEN		= 0x05,		/* Header length in characters */ 
	IP_DF		= 0x4000,	/* Don't fragment */ 
	IP_MF		= 0x2000,	/* More fragments */ 
1998/0306    
	IP_MAX		= (32*1024),	/* Maximum Internet packet size */ 
1997/0327    
}; 
 
struct Iphdr 
{ 
1998/0306    
	uchar	vihl;		/* Version and header length */ 
	uchar	tos;		/* Type of service */ 
	uchar	length[2];	/* packet length */ 
	uchar	id[2];		/* Identification */ 
	uchar	frag[2];	/* Fragment information */ 
	uchar	ttl;		/* Time to live */ 
	uchar	proto;		/* Protocol */ 
	uchar	cksum[2];	/* Header checksum */ 
	uchar	src[4];		/* Ip source */ 
	uchar	dst[4];		/* Ip destination */ 
1997/0327    
}; 
 
struct Fragment 
{ 
	Block*	blist; 
	Fragment* next; 
1998/0306    
	ulong 	src; 
	ulong 	dst; 
1997/0327    
	ushort	id; 
	ulong 	age; 
}; 
 
struct Ipfrag 
{ 
	ushort	foff; 
	ushort	flen; 
}; 
 
1997/0529    
QLock		fraglock; 
1997/0327    
Fragment*	flisthead; 
Fragment*	fragfree; 
ulong		Id; 
int		iprouting;	/* true if we route like a gateway */ 
ulong		ipcsumerr; 
1998/0306    
ulong		ipin, ippin;		/* uchars, packets in */ 
ulong		ipout, ippout;		/* uchars, packets out */ 
1997/0327    
 
#define BLKIP(xp)	((Iphdr*)((xp)->rp)) 
/* 
 * This sleazy macro relies on the media header size being 
 * larger than sizeof(Ipfrag). ipreassemble checks this is true 
 */ 
#define BKFG(xp)	((Ipfrag*)((xp)->base)) 
 
1997/0916    
static struct Stats 
{ 
	ulong	noroute; 
	ulong	droppedfrag; 
} stats; 
 
1998/0306    
ushort		ipcsum(uchar*); 
1997/0327    
Block*		ipreassemble(int, Block*, Iphdr*); 
1997/0529    
void		ipfragfree(Fragment*); 
1997/0327    
Fragment*	ipfragallo(void); 
 
void 
ipoput(Block *bp, int gating, int ttl) 
{ 
1998/0306    
	Ipifc *ifc; 
	uchar *gate; 
1997/0327    
	ushort fragoff; 
	Block *xp, *nb; 
	Iphdr *eh, *feh; 
	int lid, len, seglen, chunk, dlen, blklen, offset, medialen; 
1998/0306    
	Route *r; 
1997/0327    
 
	/* Fill out the ip header */ 
	eh = (Iphdr *)(bp->rp); 
 
1998/0306    
	/* Number of uchars in data and ip header to write */ 
1997/0327    
	len = blocklen(bp); 
	ipout += len; 
	ippout++; 
	if(gating){ 
		chunk = nhgets(eh->length); 
		if(chunk > len){ 
			netlog(Logip, "short gated packet\n"); 
			goto raise; 
		} 
		if(chunk < len) 
			len = chunk; 
	} 
1998/0306    
	if(len >= IP_MAX){ 
		netlog(Logip, "exceeded ip max size %V\n", eh->dst); 
1997/0327    
		goto raise; 
	} 
 
1998/0306    
	r = v4lookup(eh->dst); 
	if(r == nil){ 
1997/0916    
		stats.noroute++; 
1998/0306    
		netlog(Logip, "no interface %V\n", eh->dst); 
1997/0327    
		goto raise; 
	} 
1998/0306    
	if(r->type & (Rifc|Runi|Rbcast|Rmulti)) 
		gate = eh->dst; 
	else 
		gate = r->v4.gate; 
1997/0327    
	if(!gating){ 
		eh->vihl = IP_VER|IP_HLEN; 
		eh->tos = 0; 
		eh->ttl = ttl; 
	} 
1998/0306    
	ifc = r->ifc; 
1997/0327    
 
1998/0307    
	if(waserror()){ 
		runlock(ifc); 
		nexterror(); 
	} 
	rlock(ifc); 
	if(ifc->m == nil) 
		goto raise; 
 
1997/0327    
	/* If we dont need to fragment just send it */ 
1998/0306    
	medialen = ifc->m->maxmtu - ifc->m->hsize; 
1997/0327    
	if(len <= medialen) { 
		if(!gating) 
			hnputs(eh->id, Id++); 
		hnputs(eh->length, len); 
		eh->frag[0] = 0; 
		eh->frag[1] = 0; 
		eh->cksum[0] = 0; 
		eh->cksum[1] = 0; 
		hnputs(eh->cksum, ipcsum(&eh->vihl)); 
 
1998/0306    
/*print("ipoput %V->%V via %V\n", eh->src, eh->dst, gate);*/ 
		ifc->m->bwrite(ifc, bp, V4, gate); 
1998/0307    
		runlock(ifc); 
		poperror(); 
1997/0327    
		return; 
	} 
 
	if(eh->frag[0] & (IP_DF>>8)){ 
1998/0306    
		netlog(Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst); 
1997/0327    
		goto raise; 
	} 
 
	seglen = (medialen - IPHDR) & ~7; 
	if(seglen < 8){ 
1998/0306    
		netlog(Logip, "%V seglen < 8\n", eh->dst); 
1997/0327    
		goto raise; 
	} 
 
	dlen = len - IPHDR; 
	xp = bp; 
	if(gating) 
		lid = nhgets(eh->id); 
	else 
		lid = Id++; 
 
	offset = IPHDR; 
	while(xp != nil && offset && offset >= BLEN(xp)) { 
		offset -= BLEN(xp); 
		xp = xp->next; 
	} 
	xp->rp += offset; 
 
	for(fragoff = 0; fragoff < dlen; fragoff += seglen) { 
		nb = allocb(IPHDR+seglen); 
		feh = (Iphdr*)(nb->rp); 
 
		memmove(nb->wp, eh, IPHDR); 
		nb->wp += IPHDR; 
 
		if((fragoff + seglen) >= dlen) { 
			seglen = dlen - fragoff; 
			hnputs(feh->frag, fragoff>>3); 
		} 
		else	 
			hnputs(feh->frag, (fragoff>>3)|IP_MF); 
 
		hnputs(feh->length, seglen + IPHDR); 
		hnputs(feh->id, lid); 
 
		/* Copy up the data area */ 
		chunk = seglen; 
		while(chunk) { 
			if(!xp) { 
				freeblist(nb); 
				netlog(Logip, "!xp: 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; 
1998/0307    
		} 
1997/0327    
 
		feh->cksum[0] = 0; 
		feh->cksum[1] = 0; 
		hnputs(feh->cksum, ipcsum(&feh->vihl)); 
1998/0306    
		ifc->m->bwrite(ifc, nb, V4, gate); 
1997/0327    
	} 
 
raise: 
1998/0307    
	runlock(ifc); 
	poperror(); 
1997/0327    
	freeblist(bp);	 
} 
 
void 
initfrag(int size) 
{ 
	Fragment *fq, *eq; 
 
1998/0306    
	fragfree = (Fragment*)malloc(sizeof(Fragment) * size); 
1997/0327    
	if(fragfree == nil) 
		panic("initfrag"); 
 
	eq = &fragfree[size]; 
	for(fq = fragfree; fq < eq; fq++) 
		fq->next = fq+1; 
 
	fragfree[size-1].next = nil; 
} 
 
void (*ipextprotoiput)(Block*); 
 
1997/0806    
//#define DBG(x)	if((logmask & Logipmsg) && (iponly == 0 || x == iponly))netlog 
 
1997/0327    
void 
1998/0306    
ipiput(uchar *ia, Block *bp) 
1997/0327    
{ 
	Iphdr *h; 
	Proto *p; 
	ushort frag; 
	int notforme; 
1998/0306    
	uchar v6dst[IPaddrlen]; 
1997/0327    
 
1997/0806    
//	h = (Iphdr *)(bp->rp); 
//	DBG(nhgetl(h->src))(Logipmsg, "ipiput %I %I len %d proto %d\n", h->src, h->dst, BLEN(bp), h->proto); 
1997/0327    
 
	/* Ensure we have enough data to process */ 
	if(BLEN(bp) < IPHDR) { 
		bp = pullupblock(bp, IPHDR); 
		if(bp == nil) 
			return; 
	} 
	h = (Iphdr *)(bp->rp); 
 
1998/0306    
	/* dump anything that whose header doesn't checksum */ 
1997/0327    
	if(ipcsum(&h->vihl)) { 
		ipcsumerr++; 
		netlog(Logip, "ip: checksum error %I\n", h->src); 
		freeblist(bp); 
		return; 
	} 
 
1998/0306    
	/* route */ 
	v4tov6(v6dst, h->dst); 
	notforme = ipforme(v6dst) == 0; 
	if(notforme) { 
		if(iprouting) { 
			/* gate */ 
			if(h->ttl <= 1) 
				freeblist(bp); 
			else 
				ipoput(bp, 1, h->ttl - 1); 
		} else 
			useriprouter(ia, bp); 
		return; 
	} 
 
1997/0327    
	/* Check header length and version */ 
	if(h->vihl != (IP_VER|IP_HLEN)) { 
		netlog(Logip, "ip: %I bad hivl %ux\n", h->src, h->vihl); 
		freeblist(bp); 
		return; 
	} 
	frag = nhgets(h->frag); 
	if(frag) { 
		h->tos = 0; 
		if(frag & IP_MF) 
			h->tos = 1; 
		bp = ipreassemble(frag, bp, h); 
		if(bp == nil) 
			return; 
		h = (Iphdr *)(bp->rp); 
	} 
 
	ipin += blocklen(bp); 
	ippin++; 
 
	p = Fsrcvpcol(&fs, h->proto); 
	if(p != nil && p->rcv != nil) 
1998/0306    
		(*p->rcv)(ia, bp); 
1997/0327    
	else if(ipextprotoiput != nil) 
		ipextprotoiput(bp); 
	else 
		freeblist(bp); 
} 
 
int 
ipstats(char *buf, int len) 
{ 
	int n; 
 
1997/0808    
	n = snprint(buf, len, "ip: csum %lud inb %lud outb %lud inp %lud outp %lud\n", 
1997/0327    
		ipcsumerr, ipin, ipout, ippin, ippout); 
1997/0916    
	n += snprint(buf+n, len - n, "\tnoroute %lud droppedfrag %lud\n", 
		stats.noroute, stats.droppedfrag); 
1997/0327    
	return n; 
} 
 
Block* 
ipreassemble(int offset, Block *bp, Iphdr *ip) 
{ 
	int fend; 
	ushort id; 
	Fragment *f, *fnext; 
1998/0306    
	ulong src, dst; 
1997/0327    
	Block *bl, **l, *last, *prev; 
	int ovlap, len, fragsize, pktposn; 
 
	src = nhgetl(ip->src); 
	dst = nhgetl(ip->dst); 
	id = nhgets(ip->id); 
 
	/* 
	 *  block lists are too hard, pullupblock into a single block 
	 */ 
	if(bp->next){ 
		bp = pullupblock(bp, blocklen(bp)); 
		ip = (Iphdr *)(bp->rp); 
	} 
 
1997/0529    
	qlock(&fraglock); 
1997/0327    
 
	/* 
	 *  find a reassembly queue for this fragment 
	 */ 
	for(f = flisthead; f; f = fnext){ 
		fnext = f->next;	/* because ipfragfree changes the list */ 
		if(f->src == src && f->dst == dst && f->id == id) 
			break; 
1997/0916    
		if(f->age < msec){ 
			stats.droppedfrag++; 
1997/0529    
			ipfragfree(f); 
1997/0916    
		} 
1997/0327    
	} 
 
	/* 
	 *  if this isn't a fragmented packet, accept it 
	 *  and get rid of any fragments that might go 
	 *  with it. 
	 */ 
	if(!ip->tos && (offset & ~(IP_MF|IP_DF)) == 0) { 
1997/0529    
		if(f != nil) 
			ipfragfree(f); 
		qunlock(&fraglock); 
1997/0327    
		return bp; 
	} 
 
1997/0504    
	if(bp->base+sizeof(Ipfrag) >= bp->rp){ 
		bp = padblock(bp, sizeof(Ipfrag)); 
		bp->rp += sizeof(Ipfrag); 
	} 
1997/0327    
 
	BKFG(bp)->foff = offset<<3; 
	BKFG(bp)->flen = nhgets(ip->length)-IPHDR; 
 
	/* First fragment allocates a reassembly queue */ 
	if(f == nil) { 
		f = ipfragallo(); 
		f->id = id; 
		f->src = src; 
		f->dst = dst; 
 
		f->blist = bp; 
 
1997/0529    
		qunlock(&fraglock); 
1997/0327    
		return nil; 
	} 
 
	/* 
	 *  find the new fragment's position in the queue 
	 */ 
	prev = nil; 
	l = &f->blist; 
	bl = f->blist; 
	while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) { 
		prev = bl; 
		l = &bl->next; 
		bl = bl->next; 
	} 
 
	/* Check overlap of a previous fragment - trim away as necessary */ 
	if(prev) { 
		ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff; 
		if(ovlap > 0) { 
			if(ovlap >= BKFG(bp)->flen) { 
				freeblist(bp); 
1997/0529    
				qunlock(&fraglock); 
1997/0327    
				return nil; 
			} 
			BKFG(prev)->flen -= ovlap; 
		} 
	} 
 
	/* Link onto assembly queue */ 
	bp->next = *l; 
	*l = bp; 
 
	/* Check to see if succeeding segments overlap */ 
	if(bp->next) { 
		l = &bp->next; 
		fend = BKFG(bp)->foff + BKFG(bp)->flen; 
		/* Take completely covered segments out */ 
		while(*l) { 
			ovlap = fend - BKFG(*l)->foff; 
			if(ovlap <= 0) 
				break; 
			if(ovlap < BKFG(*l)->flen) { 
				BKFG(*l)->flen -= ovlap; 
				BKFG(*l)->foff += ovlap; 
				/* move up ip hdrs */ 
				memmove((*l)->rp + ovlap, (*l)->rp, IPHDR); 
				(*l)->rp += ovlap; 
				break; 
			} 
			last = (*l)->next; 
			(*l)->next = nil; 
			freeblist(*l); 
			*l = last; 
		} 
	} 
 
	/* 
	 *  look for a complete packet.  if we get to a fragment 
	 *  without IP_MF set, we're done. 
	 */ 
	pktposn = 0; 
	for(bl = f->blist; bl; bl = bl->next) { 
		if(BKFG(bl)->foff != pktposn) 
			break; 
		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) { 
			bl = f->blist; 
			len = nhgets(BLKIP(bl)->length); 
			bl->wp = bl->rp + len; 
 
			/* Pullup all the fragment headers and 
			 * return a complete packet 
			 */ 
			for(bl = bl->next; bl; bl = bl->next) { 
				fragsize = BKFG(bl)->flen; 
				len += fragsize; 
				bl->rp += IPHDR; 
				bl->wp = bl->rp + fragsize; 
			} 
 
			bl = f->blist; 
			f->blist = nil; 
1997/0529    
			ipfragfree(f); 
1997/0327    
			ip = BLKIP(bl); 
			hnputs(ip->length, len); 
1997/0529    
			qunlock(&fraglock); 
1997/0327    
			return bl;		 
		} 
		pktposn += BKFG(bl)->flen; 
	} 
1997/0529    
	qunlock(&fraglock); 
1997/0327    
	return nil; 
} 
 
/* 
1997/0529    
 * ipfragfree - Free a list of fragments - assume hold fraglock 
1997/0327    
 */ 
void 
1997/0529    
ipfragfree(Fragment *frag) 
1997/0327    
{ 
	Fragment *fl, **l; 
 
	if(frag->blist) 
		freeblist(frag->blist); 
 
	frag->src = 0; 
	frag->id = 0; 
	frag->blist = nil; 
 
	l = &flisthead; 
	for(fl = *l; fl; fl = fl->next) { 
		if(fl == frag) { 
			*l = frag->next; 
			break; 
		} 
		l = &fl->next; 
	} 
 
	frag->next = fragfree; 
	fragfree = frag; 
 
} 
 
/* 
1997/0529    
 * ipfragallo - allocate a reassembly queue - assume hold fraglock 
1997/0327    
 */ 
Fragment * 
ipfragallo(void) 
{ 
	Fragment *f; 
 
	while(fragfree == nil) { 
1997/0529    
		/* free last entry on fraglist */ 
		for(f = flisthead; f->next; f = f->next) 
			; 
		ipfragfree(f); 
1997/0327    
	} 
	f = fragfree; 
	fragfree = f->next; 
	f->next = flisthead; 
	flisthead = f; 
	f->age = msec + 30000; 
 
	return f; 
} 
 
ushort 
1998/0306    
ipcsum(uchar *addr) 
1997/0327    
{ 
	int len; 
	ulong sum; 
 
	sum = 0; 
	len = (addr[0]&0xf)<<2; 
 
	while(len > 0) { 
		sum += addr[0]<<8 | addr[1] ; 
		len -= 2; 
		addr += 2; 
	} 
 
	sum = (sum & 0xffff) + (sum >> 16); 
	sum = (sum & 0xffff) + (sum >> 16); 
 
	return (sum^0xffff); 
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)