~kris/9p

9hist

1216123949d23d5cd0921e2bfe162b52bea875df — David du Colombier 35 years ago 06450e3
Plan 9 from Bell Labs 1991-04-24
A port/arp.h => port/arp.h +42 -0
@@ 0,0 1,42 @@
typedef struct Arppkt	Arppkt;
typedef struct Arpentry	Arpentry;
typedef struct Arpstats	Arpstats;

/* Format of ethernet arp request */
struct Arppkt {
	uchar	d[6];
	uchar	s[6];
	uchar	type[2];
	uchar	hrd[2];
	uchar	pro[2];
	uchar	hln;
	uchar	pln;
	uchar	op[2];
	uchar	sha[6];
	uchar	spa[4];
	uchar	tha[6];
	uchar	tpa[4];
	};

#define ARPSIZE		42

/* Format of request from starp to user level arpd */
struct Arpentry {
	uchar	etaddr[6];
	uchar	ipaddr[4];
	};

/* Arp cache statistics */
struct Arpstats {
	int	hit;
	int	miss;
	int	failed;
	};

#define ET_ARP		0x0806
#define ET_RARP		0x8035

#define ARP_REQUEST	1
#define ARP_REPLY	2
#define RARP_REQUEST	3
#define RARP_REPLY	4

A port/devarp.c => port/devarp.c +233 -0
@@ 0,0 1,233 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include	"arp.h"
#include 	"ipdat.h"

#include	"devtab.h"

Arpcache 	*arp;
Arpcache	**arphash;
Arpstats	arpstats;
Queue		*Servq;

#define ARP_ENTRYLEN	50
char *padstr = "                                           ";


extern Arpcache *arplruhead;
extern Arpcache *arplrutail;

enum{
	arpdirqid,
	arpstatqid,
	arpctlqid,
	arpdataqid,
};

Dirtab arptab[]={
	"stats",	{arpstatqid},		0,	0600,
	"ctl",		{arpctlqid},		0,	0600,
	"data",		{arpdataqid},		0,	0600,
};
#define Narptab (sizeof(arptab)/sizeof(Dirtab))

void
arpreset(void)
{
	Arpcache *ap, *ep;

	arp = (Arpcache *)ialloc(sizeof(Arpcache) * conf.arp, 0);
	arphash = (Arpcache **)ialloc(sizeof(Arpcache *) * Arphashsize, 0);

	ep = &arp[conf.arp];
	for(ap = arp; ap < ep; ap++) {
		ap->frwd = ap+1;
		ap->prev = ap-1;
		ap->type = ARP_FREE;
		ap->status = ARP_TEMP;
	}

	arp[0].prev = 0;
	arplruhead = arp;
	ap = &arp[conf.arp-1];
	ap->frwd = 0;
	arplrutail = ap;
}

void
arpinit(void)
{
}

Chan *
arpattach(char *spec)
{
	return devattach('a', spec);
}

Chan *
arpclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int
arpwalk(Chan *c, char *name)
{
	return devwalk(c, name, arptab, (long)Narptab, devgen);
}

Chan*
arpclwalk(Chan *c, char *name)
{
	return devclwalk(c, name);
}

void
arpstat(Chan *c, char *db)
{
	devstat(c, db, arptab, (long)Narptab, devgen);
}

Chan *
arpopen(Chan *c, int omode)
{

	if(c->qid.path == CHDIR){
		if(omode != OREAD)
			error(Eperm);
	}

	switch(STREAMTYPE(c->qid.path)) {
	case arpdataqid:
		break;
	case arpstatqid:
		if(omode != OREAD)
			error(Ebadarg);
		break;
	case arpctlqid:
		break;
	}


	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void
arpcreate(Chan *c, char *name, int omode, ulong perm)
{
	error(Eperm);
}

void
arpremove(Chan *c)
{
	error(Eperm);
}

void
arpwstat(Chan *c, char *dp)
{
	error(Eperm);
}

void
arpclose(Chan *c)
{
	streamclose(c);
}

long
arpread(Chan *c, void *a, long n, ulong offset)
{
	char	 buf[100];
	Arpcache *ap, *ep;
	int	 part, bytes, size;
	char	 *ptr, *ststr;

	switch((int)(c->qid.path&~CHDIR)){
	case arpdirqid:
		return devdirread(c, a, n, arptab, Narptab, devgen);
	case arpdataqid:
		bytes = c->offset;
		while(bytes < conf.arp*ARP_ENTRYLEN && n) {
			ap = &arp[bytes/ARP_ENTRYLEN];
			part = bytes%ARP_ENTRYLEN;

			if(ap->status != ARP_OK)
				ststr = "invalid";
			else
				ststr = (ap->type == ARP_TEMP ? "temp" : "perm");

			sprint(buf,"%d.%d.%d.%d to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s%s",
				ap->eip[0], ap->eip[1], ap->eip[2], ap->eip[3],
				ap->et[0], ap->et[1], ap->et[2], ap->et[3],
				ap->et[4], ap->et[5],
				ststr, padstr); 
			
			buf[ARP_ENTRYLEN-1] = '\n';

			size = ARP_ENTRYLEN - part;
			size = MIN(n, size);
			memmove(a, buf+part, size);

			a = (void *)((int)a + size);
			n -= size;
			bytes += size;
		}
		return bytes - c->offset;
		break;
	case arpstatqid:
		sprint(buf, "hits: %d miss: %d failed: %d\n",
			arpstats.hit, arpstats.miss, arpstats.failed);

		return stringread(c, a, n, buf, offset);
	default:
		n=0;
		break;
	}
	return n;
}

long
arpwrite(Chan *c, char *a, long n, ulong offset)
{
	Arpentry entry;
	char	 buf[20], *field[5];
	int 	 m;

	switch(STREAMTYPE(c->qid.path)) {
	case arpctlqid:

		strncpy(buf, a, sizeof buf);
		m = getfields(buf, field, 5, ' ');

		if(strncmp(field[0], "flush", 5) == 0)
			arp_flush();
		else if(strcmp(field[0], "delete") == 0) {
			if(m != 2)
				error(Ebadarg);

			if(arp_delete(field[1]) < 0)
				error(Eaddrnotfound);
		}
	case arpdataqid:
		if(n != sizeof(Arpentry))
			error(Emsgsize);
		memmove(&entry, a, sizeof(Arpentry));
		arp_enter(&entry, ARP_TEMP);
		break;
	default:
		error(Ebadusefd);
	}

	return n;
}


A port/devip.c => port/devip.c +882 -0
@@ 0,0 1,882 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include 	"arp.h"
#include 	"ipdat.h"

#include	"devtab.h"

enum{
	Nrprotocol = 2, /* Number of protocols supported by this driver */
	Nipsubdir = 4,	/* Number of subdirectory entries per connection */
};

int udpsum = 1;

Queue	*Tcpoutput;		/* Tcp to lance output channel */
Ipifc	*ipifc;			/* IP protocol interfaces for stip */
Ipconv	*ipconv[Nrprotocol];	/* Connections for each protocol */
Dirtab  *ipdir[Nrprotocol];	/* Connection directory structures */
QLock	ipalloc;		/* Protocol port allocation lock */

/* ARPA User Datagram Protocol */
void	udpstiput(Queue *, Block *);
void	udpstoput(Queue *, Block *);
void	udpstopen(Queue *, Stream *);
void	udpstclose(Queue *);
/* ARPA Transmission Control Protocol */
void	tcpstiput(Queue *, Block *);
void	tcpstoput(Queue *, Block *);
void	tcpstopen(Queue *, Stream *);
void	tcpstclose(Queue *);

Qinfo tcpinfo = { tcpstiput, tcpstoput, tcpstopen, tcpstclose, "tcp" };
Qinfo udpinfo = { udpstiput, udpstoput, udpstopen, udpstclose, "udp" };

Qinfo *protocols[] = { &tcpinfo, &udpinfo, 0 };

enum{
	ipdirqid,
	iplistenqid,
	iplportqid,
	iprportqid,
	ipstatusqid,
	ipchanqid,

	ipcloneqid
};

Dirtab ipsubdir[]={
	"listen",	{iplistenqid},		0,	0600,
	"local",	{iplportqid},		0,	0600,
	"remote",	{iprportqid},		0,	0600,
	"status",	{ipstatusqid},		0,	0600,
};

void
ipreset(void)
{
	int i;

	ipifc = (Ipifc *)ialloc(sizeof(Ipifc) * conf.ip, 0);

	for(i = 0; i < Nrprotocol; i++) {
		ipconv[i] = (Ipconv *)ialloc(sizeof(Ipconv) * conf.ip, 0);
		ipdir[i] = (Dirtab *)ialloc(sizeof(Dirtab) * (conf.ip+1), 0);
		ipmkdir(protocols[i], ipdir[i], ipconv[i]);
		newqinfo(protocols[i]);
	}

	initfrag(conf.frag);
}

void
ipmkdir(Qinfo *stproto, Dirtab *dir, Ipconv *cp)
{
	Dirtab *etab;
	int i;

	etab = &dir[conf.ip];
	for(i = 0; dir < etab; i++, cp++, dir++) {
		cp->stproto = stproto;
		sprint(dir->name, "%d", i);
		dir->qid.path = CHDIR|STREAMQID(i, ipchanqid);
		dir->qid.vers = 0;
		dir->length = 0;
		dir->perm = 0600;
	}

	/* Make the clone */
	strcpy(dir->name, "clone");
	dir->qid.path = ipcloneqid;
	dir->qid.vers = 0;
	dir->length = 0;
	dir->perm = 0600;
}

void
ipinit(void)
{
}

Chan *
ipattach(char *spec)
{
	Chan *c;
	int i;

	for(i = 0; protocols[i]; i++) {
		if(strcmp(spec, protocols[i]->name) == 0) {
			c = devattach('I', spec);
			c->dev = i;

			return (c);
		}
	}

	error(Enoproto);
}

Chan *
ipclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int
ipwalk(Chan *c, char *name)
{
	if(c->qid.path == CHDIR)
		return devwalk(c, name, ipdir[c->dev], conf.ip+1, devgen);
	else
		return devwalk(c, name, ipsubdir, Nipsubdir, streamgen);
}

Chan*
ipclwalk(Chan *c, char *name)
{
	return devclwalk(c, name);
}

void
ipstat(Chan *c, char *db)
{
	if(c->qid.path == CHDIR)
		devstat(c, db, ipdir[c->dev], conf.ip+1, devgen);
	else if(c->qid.path == ipcloneqid)
		devstat(c, db, &ipdir[c->dev][conf.ip], 1, devgen);
	else
		devstat(c, db, ipsubdir, Nipsubdir, streamgen);
}

Chan *
ipopen(Chan *c, int omode)
{
	Ipconv *cp;

	cp = &ipconv[c->dev][STREAMID(c->qid.path)];
	if(c->qid.path & CHDIR) {
		if(omode != OREAD)
			error(Eperm);
	}
	else switch(STREAMTYPE(c->qid.path)) {
	case ipcloneqid:
		ipclonecon(c);
		break;
	case iplportqid:
	case iprportqid:
	case ipstatusqid:
		if(omode != OREAD)
			error(Ebadarg);
		break;
	case iplistenqid:
		if(cp->stproto != &tcpinfo)
			error(Eprotonosup);

		if(cp->backlog == 0)
			cp->backlog = 1;

		streamopen(c, &ipinfo);
		if(c->stream->devq->next->info != cp->stproto)
			pushq(c->stream, cp->stproto);

		if(cp->stproto == &tcpinfo)
			open_tcp(cp, TCP_PASSIVE, Streamhi, 0);
	
		iplisten(c, cp, ipconv[c->dev]);
		break;
	case Sdataqid:
		streamopen(c, &ipinfo);
		if(c->stream->devq->next->info != cp->stproto)
			pushq(c->stream, cp->stproto);

		if(cp->stproto == &tcpinfo)
			open_tcp(cp, TCP_ACTIVE, Streamhi, 0);
		break;
	case Sctlqid:
		streamopen(c, &ipinfo);
		if(c->stream->devq->next->info != cp->stproto)
			pushq(c->stream, cp->stproto);
		break;
	}

	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

Ipconv *
ipclonecon(Chan *c)
{
	Ipconv *base, *new, *etab;

	base = ipconv[c->dev];
	etab = &base[conf.ip];
	for(new = base; new < etab; new++) {
		if(new->ref == 0 && canqlock(new)) {
			if(new->ref ||
		          (new->stproto == &tcpinfo &&
			   new->tcpctl.state != CLOSED)) {
				qunlock(new);
				continue;
			}
			new->ref++;
			c->qid.path = CHDIR|STREAMQID(new-base, ipchanqid);
			devwalk(c, "ctl", 0, 0, streamgen);
			qunlock(new);

			streamopen(c, &ipinfo);
			pushq(c->stream, new->stproto);
			new->ref--;
			return new;
		}	
	}

	error(Enodev);
}

void
ipcreate(Chan *c, char *name, int omode, ulong perm)
{
	error(Eperm);
}

void
ipremove(Chan *c)
{
	error(Eperm);
}

void
ipwstat(Chan *c, char *dp)
{
	error(Eperm);
}

void
ipclose(Chan *c)
{
	if(c->qid.path != CHDIR)
		streamclose(c);
}

long
ipread(Chan *c, void *a, long n, ulong offset)
{
	int t, connection;
	Ipconv *cp;
	char buf[WORKBUF];

	t = STREAMTYPE(c->qid.path);
	if(t >= Slowqid)
		return streamread(c, a, n);

	if(c->qid.path == CHDIR)
		return devdirread(c, a, n, ipdir[c->dev], conf.ip+1, devgen);
	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, ipsubdir, Nipsubdir, streamgen);

	connection = STREAMID(c->qid.path);
	cp = &ipconv[c->dev][connection];

	switch(t) {
	case iprportqid:
		sprint(buf, "%d.%d.%d.%d %d\n", fmtaddr(cp->dst), cp->pdst);
		return stringread(c, a, n, buf, offset);
	case iplportqid:
		sprint(buf, "%d.%d.%d.%d %d\n", fmtaddr(Myip), cp->psrc);
		return stringread(c, a, n, buf, offset);
	case ipstatusqid:
		if(cp->stproto == &tcpinfo) {
			sprint(buf, "tcp/%d %d %s %s\n", connection,
				cp->ref, tcpstate[cp->tcpctl.state],
				cp->tcpctl.flags & CLONE ? "listen" : "connect");
		}
		else
			sprint(buf, "%s/%d %d\n", cp->stproto->name, 
				connection, cp->ref);

		return stringread(c, a, n, buf, offset);
	}

	return Eperm;
}

long
ipwrite(Chan *c, char *a, long n, ulong offset)
{
	int 	m, backlog, type;
	char 	*field[5], buf[256];
	Ipconv  *cp;
	Port	port, base;

	type = STREAMTYPE(c->qid.path);
	if (type == Sdataqid)
		return streamwrite(c, a, n, 0); 

	if (type == Sctlqid) {
		cp = &ipconv[c->dev][STREAMID(c->qid.path)];
		if(cp->stproto == &tcpinfo && cp->tcpctl.state != CLOSED)
			return Edevbusy;

		strncpy(buf, a, sizeof buf);
		m = getfields(buf, field, 5, ' ');

		if(strcmp(field[0], "connect") == 0) {
			if(m != 2)
				return Ebadarg;

			switch(getfields(field[1], field, 5, '!')) {
			default:
				return Ebadarg;
			case 2:
				base = PORTALLOC;
				break;
			case 3:
				if(strcmp(field[2], "r") != 0)
					return Eperm;
				base = PRIVPORTALLOC;
				break;
			}
			cp->dst = ipparse(field[0]);
			cp->pdst = atoi(field[1]);

			/* If we have no local port assign one */
			qlock(&ipalloc);
			if(cp->psrc == 0)
				cp->psrc = nextport(ipconv[c->dev], base);
			qunlock(&ipalloc);

		}
		else if(strcmp(field[0], "announce") == 0 ||
			strcmp(field[0], "reserve") == 0) {
			if(m != 2)
				return Ebadarg;
			port = atoi(field[1]);

			qlock(&ipalloc);
			if(portused(ipconv[c->dev], port)) {
				qunlock(&ipalloc);	
				return Einuse;
			}
			cp->psrc = port;
			cp->ptype = *field[0];
			qunlock(&ipalloc);
		}
		else if(strcmp(field[0], "backlog") == 0) {
			if(m != 2)
				return Ebadarg;
			backlog = atoi(field[1]);
			if(backlog == 0)
				return Ebadarg;
			if(backlog > 5)
				backlog = 5;
			cp->backlog = backlog;
		}
		else
			return streamwrite(c, a, n, 0);

		return n;
	}

	return Eperm;
}


void
udpstiput(Queue *q, Block *bp)
{
	if(bp->type == M_CTL)
		PUTNEXT(q, bp);
	else
		panic("udpstiput: Why am I here");
}

/*
 * udprcvmsg - called by stip to multiplex udp ports onto conversations
 */
void
udprcvmsg(Ipconv *muxed, Block *bp)
{
	Ipconv *ifc, *etab;
	Udphdr *uh;
	Port   dport;
	ushort sum, len;
	Ipaddr addr;

	uh = (Udphdr *)(bp->rptr);

	/* Put back pseudo header for checksum */
	uh->Unused = 0;
	len = nhgets(uh->udplen);
	hnputs(uh->udpplen, len);

	addr = nhgetl(uh->udpsrc);

	if(udpsum && nhgets(uh->udpcksum)) {
		if(sum = ptcl_csum(bp, UDP_EHSIZE, len+UDP_PHDRSIZE)) {
			print("udp: checksum error %x (%d.%d.%d.%d)\n",
			      sum, fmtaddr(addr));
			
			freeb(bp);
			return;
		}
	}

	dport = nhgets(uh->udpdport);

	/* Look for a conversation structure for this port */
	etab = &muxed[conf.ip];
	for(ifc = muxed; ifc < etab; ifc++) {
		if(ifc->psrc == dport && ifc->ref) {
			/* Trim the packet down to data size */
			len = len - (UDP_HDRSIZE-UDP_PHDRSIZE);
			bp = btrim(bp, UDP_EHSIZE+UDP_HDRSIZE, len);
			if(bp == 0)
				return;

			/* Stuff the src address into the remote file */
		 	ifc->dst = addr;
			ifc->pdst = nhgets(uh->udpsport);
			PUTNEXT(ifc->readq, bp);
			return;
		}
	}

	freeb(bp);
}

void
udpstoput(Queue *q, Block *bp)
{
	Ipconv *ipc;
	Udphdr *uh;
	int	dlen, ptcllen, newlen;

	/* Prepend udp header to packet and pass on to ip layer */
	ipc = (Ipconv *)(q->ptr);
	if(ipc->psrc == 0)
		error(Enoport);

	if(bp->type != M_DATA) {
		freeb(bp);
		error(Ebadctl);
	}

	/* Only allow atomic udp writes to form datagrams */
	if(!(bp->flags & S_DELIM)) {
		freeb(bp);
		error(Emsgsize);
	}

	/* Round packet up to even number of bytes and check we can
	 * send it
	 */
	dlen = blen(bp);
	if(dlen > UDP_DATMAX) {
		freeb(bp);
		error(Emsgsize);
	}
	newlen = bround(bp, 1);

	/* Make space to fit udp & ip & ethernet header */
	bp = padb(bp, UDP_EHSIZE + UDP_HDRSIZE);

	uh = (Udphdr *)(bp->rptr);

	ptcllen = dlen + (UDP_HDRSIZE-UDP_PHDRSIZE);
	uh->Unused = 0;
	uh->udpproto = IP_UDPPROTO;
	hnputs(uh->udpplen, ptcllen);
	hnputl(uh->udpdst, ipc->dst);
	hnputl(uh->udpsrc, Myip);
	hnputs(uh->udpsport, ipc->psrc);
	hnputs(uh->udpdport, ipc->pdst);
	hnputs(uh->udplen, ptcllen);
	uh->udpcksum[0] = 0;
	uh->udpcksum[1] = 0;

	hnputs(uh->udpcksum, ptcl_csum(bp, UDP_EHSIZE, newlen+UDP_HDRSIZE));
	PUTNEXT(q, bp);
}

void
udpstclose(Queue *q)
{
	Ipconv *ipc;

	ipc = (Ipconv *)(q->ptr);

	/* If the port was bound rather than reserved, clear the allocation */
	qlock(ipc);
	if(--ipc->ref == 0 && ipc->ptype == 'b')
		ipc->psrc = 0;
	qunlock(ipc);

	closeipifc(ipc->ipinterface);
}

void
udpstopen(Queue *q, Stream *s)
{
	Ipconv *ipc;

	ipc = &ipconv[s->dev][s->id];
	ipc->ipinterface = newipifc(IP_UDPPROTO, udprcvmsg, ipconv[s->dev],
			            1500, 512, ETHER_HDR, "UDP");

	qlock(ipc);
	ipc->ref++;
	qunlock(ipc);
	ipc->readq = RD(q);	
	RD(q)->ptr = (void *)ipc;
	WR(q)->next->ptr = (void *)ipc->ipinterface;
	WR(q)->ptr = (void *)ipc;
}

void
tcpstiput(Queue *q, Block *bp)
{
	if(bp->type == M_CTL)
		PUTNEXT(q, bp);
	else
		panic("tcpstiput: Why am I here");
}

void
tcpstoput(Queue *q, Block *bp)
{
	Ipconv *s;
	Tcpctl *tcb; 
	int len, errnum, oob = 0;

	s = (Ipconv *)(q->ptr);
	len = blen(bp);
	tcb = &s->tcpctl;

	if(s->psrc == 0)
		error(Enoport);

	/* Report asynchronous errors */
	if(s->err)
		error(s->err);

	switch(tcb->state) {
	case LISTEN:
		tcb->flags |= ACTIVE;
		send_syn(tcb);
		setstate(s, SYN_SENT);
		/* No break */
	case SYN_SENT:
	case SYN_RECEIVED:
	case ESTABLISHED:
	case CLOSE_WAIT:
		qlock(tcb);
		if(oob == 0) {
			appendb(&tcb->sndq, bp);
			tcb->sndcnt += len;
		}
		else {
			if(tcb->snd.up == tcb->snd.una)
				tcb->snd.up = tcb->snd.ptr;
			appendb(&tcb->sndoobq, bp);
			tcb->sndoobcnt += len;
		}

		tcprcvwin(s);
		tcp_output(s);
		qunlock(tcb);
		break;
	default:
		freeb(bp);
		error(Ehungup);
	}	
}

void
tcpstopen(Queue *q, Stream *s)
{
	Ipconv *ipc;

	/* Start tcp service processes */
	if(!Tcpoutput) {
		Tcpoutput = WR(q);
		kproc("tcpack", tcpackproc, 0);
		kproc("tcpflow", tcpflow, &ipconv[s->dev]);
	}

	ipc = &ipconv[s->dev][s->id];
	ipc->ipinterface = newipifc(IP_TCPPROTO, tcp_input, ipconv[s->dev], 
			            1500, 512, ETHER_HDR, "TCP");

	qlock(ipc);
	ipc->ref++;
	qunlock(ipc);

	ipc->readq = RD(q);
	ipc->readq->rp = &tcpflowr;

	RD(q)->ptr = (void *)ipc;
	WR(q)->next->ptr = (void *)ipc->ipinterface;
	WR(q)->ptr = (void *)ipc;
}

int
tcp_havecon(Ipconv *s)
{
	return s->curlog;
}

void
iplisten(Chan *c, Ipconv *s, Ipconv *base)
{
	Ipconv *etab, *new;

	qlock(&s->listenq);

	for(;;) {
		sleep(&s->listenr, tcp_havecon, s);

		/* Search for the new connection, clone the control channel and
		 * return an open channel to the listener
		 */
		for(new = base, etab = &base[conf.ip]; new < etab; new++) {
			if(new->psrc == s->psrc && new->pdst != 0 && 
			   new->dst && (new->tcpctl.flags & CLONE) == 0) {
				new->ref++;

				/* Remove the listen channel reference */
				streamclose(c);

				s->curlog--;
				/* Attach the control channel to the new connection */
				c->qid.path = CHDIR|STREAMQID(new-base, ipchanqid);
				devwalk(c, "ctl", 0, 0, streamgen);
				streamopen(c, &ipinfo);
				pushq(c->stream, new->stproto);
				new->ref--;

				qunlock(&s->listenq);
				return;
			}
		}
	}
}

void
tcpstclose(Queue *q)
{
	Ipconv *s;
	Tcpctl *tcb;

	s = (Ipconv *)(q->ptr);
	tcb = &s->tcpctl;

	qlock(s);
	s->ref--;
	qunlock(s);

	/* Not interested in data anymore */
	s->readq = 0;

	switch(tcb->state){
	case CLOSED:
	case LISTEN:
	case SYN_SENT:
		close_self(s, 0);
		break;
	case SYN_RECEIVED:
	case ESTABLISHED:
		tcb->sndcnt++;
		tcb->snd.nxt++;
		setstate(s, FINWAIT1);
		goto output;
	case CLOSE_WAIT:
		tcb->sndcnt++;
		tcb->snd.nxt++;
		setstate(s, LAST_ACK);
	output:
		qlock(tcb);
		tcp_output(s);
		qunlock(tcb);
		break;
	}
}

/*
 * Network byte order functions
 */

void
hnputs(uchar *ptr, ushort val)
{
	ptr[0] = val>>8;
	ptr[1] = val;
}

void
hnputl(uchar *ptr, ulong val)
{
	ptr[0] = val>>24;
	ptr[1] = val>>16;
	ptr[2] = val>>8;
	ptr[3] = val;
}

ulong
nhgetl(uchar *ptr)
{
	return ((ptr[0]<<24) | (ptr[1]<<16) | (ptr[2]<<8) | ptr[3]);
}

ushort
nhgets(uchar *ptr)
{
	return ((ptr[0]<<8) | ptr[1]);
}

/* 
 * ptcl_csum - protcol cecksum routine
 */
ushort
ptcl_csum(Block *bp, int offset, int len)
{
	uchar *addr;
	ulong losum = 0, hisum = 0;
	ushort csum;
	int odd, blen, x;

	/* Correct to front of data area */
	while(bp && offset && offset >= BLEN(bp)) {
		offset -= BLEN(bp);
		bp = bp->next;
	}
	if(bp == 0)
		return 0;

	addr = bp->rptr + offset;
	blen = BLEN(bp) - offset;
	odd = 0;
	while(len) {
		if(odd) {
			losum += *addr++;
			blen--;
			len--;
			odd = 0;
		}
		for(x = MIN(len, blen); x > 1; x -= 2) {
			hisum += addr[0];
			losum += addr[1];
			len -= 2;
			blen -= 2;
			addr += 2;
		}
		if(blen && x) {
			odd = 1;
			hisum += addr[0];
			len--;
		}
		bp = bp->next;
		if(bp == 0)
			break;
		blen = BLEN(bp);
		addr = bp->rptr;

	}

	losum += hisum>>8;
	losum += (hisum&0xff)<<8;
	while((csum = losum>>16) != 0)
		losum = csum + (losum & 0xffff);

	losum &= 0xffff;

	return ~losum & 0xffff;
}

Block *
btrim(Block *bp, int offset, int len)
{
	Block *nb, *startb;
	ulong l;

	if(blen(bp) < offset+len) {
		freeb(bp);
		return 0;
	}

	while((l = BLEN(bp)) < offset) {
		offset -= l;
		nb = bp->next;
		bp->next = 0;
		freeb(bp);
		bp = nb;
	}

	startb = bp;
	bp->rptr += offset;

	while((l = BLEN(bp)) < len) {
		len -= l;
		bp = bp->next;
	}

	bp->wptr -= (BLEN(bp) - len);
	bp->flags |= S_DELIM;

	if(bp->next) {
		freeb(bp->next);
		bp->next = 0;
	}

	return(startb);
}

Ipconv *
portused(Ipconv *ic, Port port)
{
	Ipconv *ifc, *etab;

	etab = &ic[conf.ip];
	for(ifc = ic; ifc < etab; ifc++) {
		if(ifc->psrc == port) 
			return ifc;
	}

	return 0;
}

Port
nextport(Ipconv *ic, Port base)
{
	Port i;

	for(i = base; i < PORTMAX; i++) {
		if(!portused(ic, i))
			return(i);
	}
	return(0);
}

/* NEEDS HASHING ! */

Ipconv *
ip_conn(Ipconv *ic, Port dst, Port src, Ipaddr dest, char proto)
{
	Ipconv *s, *etab;

	/* Look for a conversation structure for this port */
	etab = &ic[conf.ip];
	for(s = ic; s < etab; s++) {
		if(s->psrc == dst && s->pdst == src &&
		   (s->dst == dest || dest == 0))
			return s;
	}

	return 0;
}


A port/ipdat.h => port/ipdat.h +440 -0
@@ 0,0 1,440 @@
typedef struct Ipconv	Ipconv;
typedef struct Ipifc	Ipifc;
typedef struct Fragq	Fragq;
typedef struct Ipfrag	Ipfrag;
typedef ulong		Ipaddr;
typedef struct Arpcache	Arpcache;
typedef ushort		Port;
typedef struct Udphdr	Udphdr;
typedef struct Etherhdr	Etherhdr;
typedef struct Reseq	Reseq;
typedef struct Tcp	Tcp;
typedef struct Tcpctl	Tcpctl;
typedef struct Tcphdr	Tcphdr;
typedef struct Timer	Timer;

struct Etherhdr {
#define ETHER_HDR	14
	uchar	d[6];
	uchar	s[6];
	uchar	type[2];

	/* Now we have the ip fields */
#define ETHER_IPHDR	20
	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 */
};

/* Ethernet packet types */
#define ET_IP	0x0800

/* A userlevel data gram */
struct Udphdr {
#define UDP_EHSIZE	22
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */
	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 */

	/* Udp pseudo ip really starts here */
#define UDP_PHDRSIZE	12
#define UDP_HDRSIZE	20
	uchar	Unused;	
	uchar	udpproto;	/* Protocol */
	uchar	udpplen[2];	/* Header plus data length */
	uchar	udpsrc[4];	/* Ip source */
	uchar	udpdst[4];	/* Ip destination */
	uchar	udpsport[2];	/* Source port */
	uchar	udpdport[2];	/* Destination port */
	uchar	udplen[2];	/* data length */
	uchar	udpcksum[2];	/* Checksum */
};

#define TCP_PKT	(TCP_EHSIZE+TCP_IPLEN+TCP_PHDRSIZE)

struct Tcphdr {
#define TCP_EHSIZE	14
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */
#define TCP_IPLEN	8
	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 */

#define TCP_PHDRSIZE	12	
	uchar	Unused;
	uchar	proto;
	uchar	tcplen[2];
	uchar	tcpsrc[4];
	uchar	tcpdst[4];

#define TCP_HDRSIZE	20
	uchar	tcpsport[2];
	uchar	tcpdport[2];
	uchar	tcpseq[4];
	uchar	tcpack[4];
	uchar	tcpflag[2];
	uchar	tcpwin[2];
	uchar	tcpcksum[2];
	uchar	tcpurg[2];

	/* Options segment */
	uchar	tcpopt[2];
	uchar	tcpmss[2];
	};



struct Timer {
	Timer	*next;
	Timer	*prev;
	int	state;
	int	start;
	int	count;
	void	(*func)(void*);
	void	*arg;
	};

struct Tcpctl {
	QLock;
	uchar	state;		/* Connection state */
	uchar	type;		/* Listening or active connection */
	uchar	code;		/* Icmp code */		
	struct {
		int una;	/* Unacked data pointer */
		int nxt;	/* Next sequence expected */
		int ptr;	/* Data pointer */
		ushort wnd;	/* Tcp send window */
		int up;		/* Urgent data pointer */
		int wl1;
		int wl2;
	} snd;
	int	iss;
	ushort	cwind;
	ushort	ssthresh;
	int	resent;
	struct {
		int nxt;
		ushort wnd;
		int up;
	} rcv;
	int	irs;
	ushort	mss;
	int	rerecv;
	ushort	window;
	int	max_snd;
	int	last_ack;
	char	backoff;
	char	flags;
	char	tos;

	Block	*rcvq;
	ushort	rcvcnt;

	Block	*rcvoobq;
	ushort	rcvoobcnt;

	Block	*sndq;			/* List of data going out */
	ushort	sndcnt;			/* Amount of data in send queue */

	Block	*sndoobq;		/* List of blocks going oob */
	ushort	sndoobcnt;		/* Size of out of band queue */
	ushort	oobmark;		/* Out of band sequence mark */
	char	oobflags;		/* Out of band data flags */

	Reseq	*reseq;			/* Resequencing queue */
	Timer	timer;			 
	Timer	acktimer;		/* Acknoledge timer */
	Timer	rtt_timer;		/* Round trip timer */
	int	rttseq;			/* Round trip sequence */
	int	srtt;			/* Shortened round trip */
	int	mdev;			/* Mean deviation of round trip */
};

struct	Tcp {
	Port	source;
	Port	dest;
	int	seq;
	int	ack;
	char	flags;
	ushort	wnd;
	ushort	up;
	ushort	mss;
	};

struct Reseq {
	Reseq 	*next;
	Tcp	seg;
	Block	*bp;
	ushort	length;
	char	tos;
	};

/* An ip interface used for UDP/TCP/ARP/ICMP */
struct Ipconv {
	QLock;				/* Ref count lock */
	int 	ref;
	Qinfo	*stproto;		/* Stream protocol for this device */
	Ipaddr	dst;			/* Destination from connect */

	Port	psrc;			/* Source port */
	Port	pdst;			/* Destination port */

	uchar	ptype;			/* Source port type */
	Ipifc	*ipinterface;		/* Ip protocol interface */
	Queue	*readq;			/* Pointer to upstream read q */

	QLock	listenq;		/* List of people waiting incoming cons */
	Rendez	listenr;		/* Some where to sleep while waiting */
	Ipconv	*listen;
		
	char	err;			/* Async protocol error */
	int	backlog;		/* Maximum number of waiting connections */
	int	curlog;			/* Number of waiting connections */
	int 	contype;
	Tcpctl	tcpctl;			/* Tcp control block */
};

#define	MAX_TIME	100000000	/* Forever */
#define TCP_ACK		200		/* Timed ack sequence every 200ms */

#define URG	0x20
#define ACK	0x10
#define PSH	0x08
#define RST	0x04
#define SYN	0x02
#define FIN	0x01

#define EOL_KIND	0
#define NOOP_KIND	1
#define MSS_KIND	2

#define MSS_LENGTH	4
#define MSL2		10
#define MSPTICK		200
#define DEF_MSS		1024
#define DEF_RTT		1000
#define	TCPOOB_HADDATA	1
#define	TCPOOB_HAVEDATA 2

#define TCP_PASSIVE	0
#define TCP_ACTIVE	1

#define MAXBACKOFF	5
#define FORCE		1
#define	CLONE		2
#define RETRAN		4
#define ACTIVE		8
#define SYNACK		16
#define AGAIN		8
#define DGAIN		4

#define TIMER_STOP	0
#define TIMER_RUN	1
#define TIMER_EXPIRE	2

#define	set_timer(t,x)	(((t)->start) = (x)/MSPTICK)
#define	dur_timer(t)	((t)->start)
#define	read_timer(t)	((t)->count)
#define	run_timer(t)	((t)->state == TIMER_RUN)

enum {
	CLOSED = 0,
	LISTEN,
	SYN_SENT,
	SYN_RECEIVED,
	ESTABLISHED,
	FINWAIT1,
	FINWAIT2,
	CLOSE_WAIT,
	CLOSING,
	LAST_ACK,
	TIME_WAIT
	};

/*
 * Ip interface structure. We have one for each active protocol driver
 */
struct Ipifc {
	QLock;
	int 		ref;
	uchar		protocol;		/* Ip header protocol number */
	char		name[NAMELEN];		/* Protocol name */
	void (*iprcv)	(Ipconv *, Block *);	/* Receive demultiplexor */
	Ipconv		*connections;		/* Connection list */
	int		maxmtu;			/* Maximum transfer unit */
	int		minmtu;			/* Minumum tranfer unit */
	int		hsize;			/* Media header size */	
	Lock;	
};

struct Fragq {
	QLock;
	Block  *blist;
	Fragq  *next;
	Ipaddr src;
	Ipaddr dst;
	ushort id;
	};

struct Ipfrag {
	ushort	foff;
	ushort	flen;
	};

struct Arpcache {
	uchar	status;		/* Entry status */
	uchar	type;		/* Entry type */
	Ipaddr	ip;		/* Host byte order */
	uchar	eip[4];		/* Network byte order */
	uchar	et[6];		/* Ethernet address for this ip */
	int	age;		/* Entry timeout */
	Arpcache *hash;
	Arpcache **hashhd;
	Arpcache *frwd;
	Arpcache *prev;
};
#define ARP_FREE	0
#define ARP_OK		1
#define ARP_ASKED	2
#define ARP_TEMP	0
#define ARP_PERM	1
#define Arphashsize	32
#define ARPHASH(p)	arphash[((p[2]^p[3])%Arphashsize)]
#define ARP_WAITMS	2500		/* Wait for arp replys */

#define IP_VER	0x40			/* Using IP version 4 */
#define IP_HLEN 0x05			/* Header length in characters */
#define IP_DF	0x4000			/* Don't fragment */
#define IP_MF	0x2000			/* More fragments */

#define	ICMP_ECHOREPLY		0	/* Echo Reply */
#define	ICMP_UNREACH		3	/* Destination Unreachable */
#define	ICMP_SOURCEQUENCH	4	/* Source Quench */
#define	ICMP_REDIRECT		5	/* Redirect */
#define	ICMP_ECHO		8	/* Echo Request */
#define	ICMP_TIMXCEED		11	/* Time-to-live Exceeded */
#define	ICMP_PARAMPROB		12	/* Parameter Problem */
#define	ICMP_TSTAMP		13	/* Timestamp */
#define	ICMP_TSTAMPREPLY	14	/* Timestamp Reply */
#define	ICMP_IREQ		15	/* Information Request */
#define	ICMP_IREQREPLY		16	/* Information Reply */

/* Sizes */
#define IP_MAX		8192			/* Maximum Internet packet size */
#define UDP_MAX		(IP_MAX-ETHER_IPHDR)	/* Maximum UDP datagram size */
#define UDP_DATMAX	(UDP_MAX-UDP_HDRSIZE)	/* Maximum amount of udp data */

/* Protocol numbers */
#define IP_UDPPROTO	17
#define IP_TCPPROTO	6

/* Protocol port numbers */
#define PORTALLOC	5000		/* First automatic allocated port */
#define PRIVPORTALLOC	600		/* First priveleged port allocated */
#define PORTMAX		30000		/* Last port to allocte */

/* Stuff to go in funs.h someday */
Ipifc   *newipifc(uchar, void (*)(Ipconv *, Block*), Ipconv *, int, int, int, char*);
void	closeipifc(Ipifc*);
ushort	ip_csum(uchar*);
int	arp_lookup(uchar*, uchar*);
Ipaddr	ipparse(char*);
void	hnputs(uchar*, ushort);
void	hnputl(uchar*, ulong);
ulong	nhgetl(uchar*);
ushort	nhgets(uchar*);
ushort	ptcl_csum(Block*bp, int, int);
void	ppkt(Block*);
void	udprcvmsg(Ipconv *, Block*);
Block	*btrim(Block*, int, int);
Block	*ip_reassemble(int, Block*, Etherhdr*);
Ipconv	*portused(Ipconv *, Port);
Port	nextport(Ipconv *, Port);
void	arp_enter(Arpentry*, int);
void	arp_flush(void);
int	arp_delete(char*);
void	arplinkhead(Arpcache*);
Fragq   *ipfragallo(void);
void	ipfragfree(Fragq*);
void	iproute(uchar*, uchar*);
void	initfrag(int);
Block	*copyb(Block*, int);
int	ntohtcp(Tcp*, Block**);
void	reset(Ipaddr, Ipaddr, char, ushort, Tcp*);
void	proc_syn(Ipconv*, char, Tcp*);
void	send_syn(Tcpctl*);
void	tcp_output(Ipconv*);
int	seq_within(int, int, int);
void	update(Ipconv *, Tcp *);
int	trim(Tcpctl *, Tcp *, Block **, ushort *);
void	add_reseq(Tcpctl *, char, Tcp *, Block *, ushort);
void	close_self(Ipconv *, int);
int	seq_gt(int, int);
void	appendb(Block **, Block *);
Ipconv	*ip_conn(Ipconv *, Port, Port, Ipaddr dest, char proto);
void	ipmkdir(Qinfo *, Dirtab *, Ipconv *);
int	inb_window(Tcpctl *, int);
Block	*htontcp(Tcp *, Block *, Tcphdr *);
void	start_timer(Timer *);
void	stop_timer(Timer *);
int	copyupb(Block **, uchar *, int);
void	init_tcpctl(Ipconv *);
void	close_self(Ipconv *, int);
int	iss(void);
int	seq_within(int, int, int);
int	seq_lt(int, int);
int	seq_le(int, int);
int	seq_gt(int, int);
int	seq_ge(int, int);
void	setstate(Ipconv *, char);
void	tcpackproc(void*);
Block 	*htontcp(Tcp *, Block *, Tcphdr *);
int	ntohtcp(Tcp *, Block **);
void	extract_oob(Block **, Block **, Tcp *);
void	get_reseq(Tcpctl *, char *, Tcp *, Block **, ushort *);
void	state_upcall(Ipconv*, char oldstate, char newstate);
int	backoff(int);
int	dupb(Block **, Block *, int, int);
void	tcp_input(Ipconv *, Block *);
void 	tcprcvwin(Ipconv *);
void	open_tcp(Ipconv *, int, ushort, char);
void	tcpflow(void*);
void 	tcp_timeout(void *);
void	tcp_acktimer(void *);
Ipconv  *ipclonecon(Chan *);
void	iplisten(Chan *, Ipconv *, Ipconv *);

#define	fmtaddr(xx)	(xx>>24)&0xff,(xx>>16)&0xff,(xx>>8)&0xff,xx&0xff
#define	MIN(a, b)	((a) < (b) ? (a) : (b))
#define MAX(a, b)	((a) > (b) ? (a) : (b))
#define BLKIP(xp)	((Etherhdr *)((xp)->rptr))
#define BLKFRAG(xp)	((Ipfrag *)((xp)->rptr))
#define PREC(x)		((x)>>5 & 7)

#define WORKBUF		64

extern Ipaddr Myip;
extern Ipaddr Mymask;
extern Ipaddr classmask[4];
extern Ipconv *ipconv[];
extern char *tcpstate[];
extern Rendez tcpflowr;
extern Qinfo tcpinfo;
extern Qinfo ipinfo;
extern Qinfo udpinfo;

M port/lib.h => port/lib.h +1 -1
@@ 47,7 47,7 @@ extern	char	*doprint(char*, char*, char*, void*);
extern	int	fmtinstall(char, int (*)(Op*));
extern	int	sprint(char*, char*, ...);
extern	int	print(char*, ...);

extern  int	atoi(char *);
/*
 * one-of-a-kind
 */

A port/stip.c => port/stip.c +631 -0
@@ 0,0 1,631 @@
/*
 *  ethernet specific multiplexor for ip
 *
 *  this line discipline gets pushed onto an ethernet channel
 *  to demultiplex/multiplex ip conversations.
 */
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"errno.h"
#include	"arp.h"
#include 	"ipdat.h"

#define DPRINT if(pip)print
int pip = 0;
int ipcksum = 1;
extern Ipifc *ipifc;
int Id = 1;

Fragq		*flisthead;
Fragq		*fragfree;
QLock		fraglock;

Queue 		*Etherq;

Ipaddr		Myip;
Ipaddr		Mymask;

/* Predeclaration */
static void	ipetherclose(Queue*);
static void	ipetheriput(Queue*, Block*);
static void	ipetheropen(Queue*, Stream*);
static void	ipetheroput(Queue*, Block*);

/*
 *  the ethernet multiplexor stream module definition
 */
Qinfo ipinfo =
{
	ipetheriput,
	ipetheroput,
	ipetheropen,
	ipetherclose,
	"internet"
};

void
initfrag(int size)
{
	Fragq *fq, *eq;

	fragfree = (Fragq*)ialloc(sizeof(Fragq) * size, 0);

	eq = &fragfree[size];
	for(fq = fragfree; fq < eq; fq++)
		fq->next = fq+1;

	fragfree[size-1].next = 0;
}

/*
 *  set up an ether interface
 */
static void
ipetheropen(Queue *q, Stream *s)
{
	/* First open is by ipconfig and sets up channel
	 * to ethernet
	 */
	if(!Etherq)
		Etherq = WR(q);

	DPRINT("ipetheropen EQ %lux dev=%d id=%d RD %lux WR %lux\n",
		Etherq, s->dev, s->id, RD(q), WR(q));
}

/*
 * newipifc - Attach to or Create a new protocol interface
 */

Ipifc *
newipifc(uchar ptcl, void (*recvfun)(Ipconv *, Block *bp),
	 Ipconv *con, int max, int min, int hdrsize, char *name)
{
	Ipifc *ifc, *free;
 
	free = 0;
	for(ifc = ipifc; ifc < &ipifc[conf.ipif]; ifc++) {
		qlock(ifc);
		if(ifc->protocol == ptcl) {
			ifc->ref++;
			qunlock(ifc);
			return(ifc);
		}
		if(!free && ifc->ref == 0) {
			ifc->ref = 1;
			free = ifc;
		}
		else
			qunlock(ifc);
	}

	if(!free)
		error(Enoifc);

	free->iprcv = recvfun;

	/* If media supports large transfer units limit maxmtu
	 * to max ip size */
	if(max > IP_MAX)
		max = IP_MAX;
	free->maxmtu = max;
	free->minmtu = min;
	free->hsize = hdrsize;
	free->connections = con;

	free->protocol = ptcl;
	strncpy(free->name, name, NAMELEN);

	qunlock(free);
	return(free);
}

static void
ipetherclose(Queue *q)
{
	if(q == Etherq) {
		print("stip: Clearing ether channel\n");
		Etherq = 0;
	}

	DPRINT("ipetherclose RD %lux WR %lux\n", RD(q), WR(q));
}

void
closeipifc(Ipifc *ifc)
{
	/* If this is the last reference to the protocol multiplexor
	 * cancel upcalls from this stream
	 */
	qlock(ifc);
	if(--ifc->ref == 0) {
		ifc->protocol = 0;
		ifc->name[0] = 0;
	}
	qunlock(ifc);
}

static void
ipetheroput(Queue *q, Block *bp)
{
	Etherhdr *eh, *feh;
	int	 lid, len, seglen, chunk, dlen, blklen, offset;
	Ipifc	 *ifp;
	ushort	 fragoff;
	Block	 *xp, *nb;
	uchar 	 *ptr;

	if(bp->type != M_DATA){
		/* Allow one setting of the ip address */
		if(!Myip && streamparse("setip", bp)) {
			ptr = bp->rptr;
			Myip = ipparse((char *)ptr);
			Mymask = classmask[Myip>>30];
			while(*ptr != ' ' && *ptr)
				ptr++;
			if(*ptr)
				Mymask = ipparse((char *)ptr);
			freeb(bp);
		}
		else
			PUTNEXT(Etherq, bp);
		return;
	}

	ifp = (Ipifc *)(q->ptr);

	/* Number of bytes in ip and media header to write */
	len = blen(bp);

	/* Fill out the ip header */
	eh = (Etherhdr *)(bp->rptr);
	eh->vihl = IP_VER|IP_HLEN;
	eh->tos = 0;
	eh->ttl = 255;

	/* If we dont need to fragment just send it */
	if(len <= ifp->maxmtu) {
		hnputs(eh->length, len-ETHER_HDR);
		eh->frag[0] = 0;
		eh->frag[1] = 0;
		eh->cksum[0] = 0;
		eh->cksum[1] = 0;
		hnputs(eh->cksum, ip_csum(&eh->vihl));

		/* Finally put in the ethernet level information */
		hnputs(eh->type, ET_IP);
		if(!arp_lookup(eh->dst, eh->d)) {
			freeb(bp);
			return;
		}

		PUTNEXT(Etherq, bp);
		return;
	}

	if(eh->frag[0] & (IP_DF>>8))
		goto drop;

	seglen = (ifp->minmtu - (ETHER_HDR+ETHER_IPHDR)) & ~7;
	if(seglen < 8)
		goto drop;

	/* Make prototype output header */
	hnputs(eh->type, ET_IP);
	if(!arp_lookup(eh->dst, eh->d)) {
		freeb(bp);
		return;
	}
	
	dlen = len - (ETHER_HDR+ETHER_IPHDR);
	xp = bp;
	lid = Id++;

	offset = ETHER_HDR+ETHER_IPHDR;
	while(xp && offset && offset >= BLEN(xp)) {
		offset -= BLEN(xp);
		xp = xp->next;
	}
	xp->rptr += offset;

	for(fragoff = 0; fragoff < dlen; fragoff += seglen) {
		nb = allocb(ETHER_HDR+ETHER_IPHDR+seglen);
		feh = (Etherhdr *)(nb->rptr);

		memmove(nb->wptr, eh, ETHER_HDR+ETHER_IPHDR);
		nb->wptr += ETHER_HDR+ETHER_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 + ETHER_IPHDR);
		hnputs(feh->id, lid);

		/* Copy up the data area */
		chunk = seglen;
		while(chunk) {
			if(!xp) {
				freeb(nb);
				goto drop;
			}
			blklen = MIN(BLEN(xp), chunk);
			memmove(nb->wptr, xp->rptr, blklen);
			nb->wptr += blklen;
			xp->rptr += blklen;
			chunk -= blklen;
			if(xp->rptr == xp->wptr)
				xp = xp->next;
		} 
				
		feh->cksum[0] = 0;
		feh->cksum[1] = 0;
		hnputs(feh->cksum, ip_csum(&feh->vihl));

		nb->flags |= S_DELIM;
		PUTNEXT(Etherq, nb);
	}

drop:
	freeb(bp);	
}


/*
 *  Input a packet and use the ip protocol to select the correct
 *  device to pass it to.
 *
 */
static void
ipetheriput(Queue *q, Block *bp)
{
	Ipifc 	 *ep, *ifp;
	Etherhdr *h;
	ushort   frag;

	if(bp->type != M_DATA){
		PUTNEXT(q, bp);
		return;
	}

	h = (Etherhdr *)(bp->rptr);

	/* Ensure we have enough data to process */
	if(BLEN(bp) < (ETHER_HDR+ETHER_IPHDR)) {
		bp = pullup(bp, ETHER_HDR+ETHER_IPHDR);
		if(!bp)
			return;
	}

	if(ipcksum && ip_csum(&h->vihl)) {
		print("ip: checksum error (from %d.%d.%d.%d ?)\n",
		      h->src[0], h->src[1], h->src[2], h->src[3]);
		goto drop;
	}

	/* Check header length and version */
	if(h->vihl != (IP_VER|IP_HLEN))
		goto drop;

	frag = nhgets(h->frag);
	if(frag) {
		h->tos = frag & IP_MF ? 1 : 0;
		bp = ip_reassemble(frag, bp, h);
		if(!bp)
			return;
	}

	/*
 	 * Look for an ip interface attached to this protocol
	 */
	ep = &ipifc[conf.ipif];
	for(ifp = ipifc; ifp < ep; ifp++) {
		if(ifp->protocol == h->proto) {
			(*ifp->iprcv)(ifp->connections, bp);
			return;
		}
	}
			
drop:
	freeb(bp);
}

Block *
ip_reassemble(int offset, Block *bp, Etherhdr *ip)
{
	Fragq *f;
	Ipaddr src, dst;
	ushort id;
	Block *bl, **l, *last, *prev;
	int ovlap, len, fragsize;

	src = nhgetl(ip->src);
	dst = nhgetl(ip->dst);
	id = nhgets(ip->id);

	for(f = flisthead; f; f = f->next) {
		if(f->src == src && f->dst == dst && f->id == id)
			break;
	}

	if(!ip->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
		if(f) {
			qlock(f);
			ipfragfree(f);
		}
		return(bp);
	}

	BLKFRAG(bp)->foff = offset<<3;
	BLKFRAG(bp)->flen = nhgets(ip->length) - ETHER_IPHDR; /* Ip data length */
	bp->flags &= ~S_DELIM;

	/* First fragment allocates a reassembly queue */
	if(f == 0) {
		f = ipfragallo();
		if(f == 0) {
			freeb(bp);
			return 0;
		}
		qlock(f);
		f->id = id;
		f->src = src;
		f->dst = dst;

		f->blist = bp;
		/* Check lance has handed us a contiguous buffer */
		if(bp->next)
			panic("ip: reass ?");

		qunlock(f);
		return 0;
	}
	qlock(f);

	prev = 0;
	l = &f->blist;
	for(bl = f->blist; bl && BLKFRAG(bp)->foff > BLKFRAG(bl)->foff; bl = bl->next) {
		prev = bl;
		l = &bl->next;
	}

	/* Check overlap of a previous fragment - trim away as necessary */
	if(prev) {
		ovlap = BLKFRAG(prev)->foff + BLKFRAG(prev)->flen - BLKFRAG(bp)->foff;
		if(ovlap > 0) {
			if(ovlap > BLKFRAG(bp)->flen) {
				freeb(bp);
				qunlock(f);
				return 0;
			}
			BLKFRAG(bp)->flen -= ovlap;
		}
	}

	/* Link onto assembly queue */
	bp->next = *l;
	*l = bp;

	/* Check to see if suceeding segments overlap */
	if(bp->next) {
		l = &bp->next;
		end = BLKFRAG(bp)->foff + BLKFRAG(bp)->flen;
		/* Take completely covered segements out */
		while(*l && (ovlap = (end - BLKFRAG(*l)->foff)) > 0) {
			if(ovlap < BLKFRAG(*l)->flen) {
				BLKFRAG(*l)->flen -= ovlap;
				(*l)->rptr += ovlap;
				break;
			}	
			last = *l;
			freeb(*l);
			*l = last;
		}
	}

	/* Now look for a completed queue */
	for(bl = f->blist; bl; bl = bl->next) {
		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0)
			goto complete;
		if(bl->next == 0 ||
		   BLKFRAG(bl)->foff+BLKFRAG(bl)->flen != BLKFRAG(bl->next)->foff)
			break;
	}
	qunlock(f);
	return 0;
	
complete:
	bl = f->blist;
	last = bl;
	len = nhgets(BLKIP(bl)->length);
	bl->wptr = bl->rptr + len + ETHER_HDR;

	/* Pullup all the fragment headers and return a complete packet */
	for(bl = bl->next; bl; bl = bl->next) {
		fragsize = BLKFRAG(bl)->flen;
		len += fragsize;
		bl->rptr += (ETHER_HDR+ETHER_IPHDR);
		bl->wptr = bl->rptr + fragsize;
		last = bl;
	}

	last->flags |= S_DELIM;
	bl = f->blist;
	f->blist = 0;
	ipfragfree(f);

	ip = BLKIP(bl);
	hnputs(ip->length, len);

	return(bl);		
}

/*
 * ipfragfree - Free a list of fragments, fragment list must be locked
 */

void
ipfragfree(Fragq *frag)
{
	Block *f, *next;
	Fragq *fl, **l;

	for(f = frag->blist; f; f = next) {
		next = f->next;
		freeb(f);
	}

	frag->src = 0;
	frag->id = 0;
	qunlock(frag);

	qlock(&fraglock);

	l = &flisthead;
	for(fl = *l; fl; fl = fl->next) {
		if(fl == frag) {
			*l = frag->next;
			break;
		}
		l = &fl->next;
	}

	frag->next = fragfree;
	fragfree = frag;

	qunlock(&fraglock);


}

/*
 * ipfragallo - allocate a reassembly queue
 */
Fragq *
ipfragallo(void)
{
	Fragq *f;

	qlock(&fraglock);
	if(!fragfree) {
		qunlock(&fraglock);
		print("ipfragallo: no queue\n");
		return 0;
	}
	f = fragfree;
	fragfree = f->next;
	f->next = flisthead;
	flisthead = f;

	qunlock(&fraglock);
	return f;

}

/*
 * ip_csum - Compute internet header checksums
 */
ushort
ip_csum(uchar *addr)
{
	int len;
	ulong 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);
}

/*
 * ipparse - Parse an ip address out of a string
 */

Ipaddr classmask[4] = {
	0xff000000,
	0xff000000,
	0xffff0000,
	0xffffff00
};

Ipaddr
ipparse(char *ipa)
{
	Ipaddr address = 0;
	int shift;
	Ipaddr net;

	shift = 24;

	while(shift >= 0 && ipa != (char *)1) {
		address |= atoi(ipa) << shift;
		shift -= 8;
		ipa = strchr(ipa, '.')+1;
	}
	net = address & classmask[address>>30];

	shift += 8;
	return net | ((address & ~classmask[address>>30])>>shift);
}

/* 
 * XXX debugging code !
 */
void
ppkt(Block *bp)
{
	uchar *x;
	int len, cnt = 0;
	Block *xp;
	uchar ch;

	if(bp)
		print("ppkt: %lux len %d\n", bp, blen(bp));
	else
		print("ppkt: Null packet pointer\n");

	xp = bp;
	while(bp) {
		len = BLEN(bp);
		x = bp->rptr;
		while(len--) {
			if(cnt%64 == 0)
				print("\n%.04d: ", cnt);
			print("%.02x ", *x++);
			cnt++;
		}
		bp = bp->next;
	}
	bp = xp;
	cnt = 0;
	while(bp) {
		len = BLEN(bp);
		x = bp->rptr;
		while(len--) {
			if(cnt%64 == 0)
				print("\n%.04d: ", cnt);
			ch = *x++;
			print("%c  ", ch < ' ' || ch >= 0x7f ? '.' : ch);
			cnt++;
		}
		bp = bp->next;
	}
	print("\n");

}

A port/tcpif.c => port/tcpif.c +84 -0
@@ 0,0 1,84 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include 	"arp.h"
#include 	"ipdat.h"

extern int tcpdbg;
#define DPRINT	if(tcpdbg) print

void
state_upcall(Ipconv *s, char oldstate, char newstate)
{
	Block *bp;
	int len;
	char *ptr = 0;

	SET(len);

	DPRINT("state_upcall: %s -> %s err %d\n", 
	      tcpstate[oldstate], tcpstate[newstate], s->err);

	if(oldstate == newstate)
		return;

	switch(newstate) {
	case CLOSED:
		s->psrc = 0;
		s->pdst = 0;
		s->dst = 0;
	case CLOSE_WAIT:		/* Remote closes */
		if(s->readq == 0)
			break;
		if(s->err) {
			ptr = errstrtab[s->err];
			len = strlen(ptr)+1;
			bp = allocb(len);
		}
		else
			bp = allocb(0);

		if(bp) {
			if(ptr) {
				strcpy((char *)bp->wptr, ptr);
				bp->wptr += len;
			}
			bp->flags |= S_DELIM;
			bp->type = M_HANGUP;
			PUTNEXT(s->readq, bp);
		}
	}
}

void
open_tcp(Ipconv *s, int mode, ushort window, char tos)
{
	Tcpctl *tcb = &s->tcpctl;

	if(tcb->state != CLOSED)
		return;

	init_tcpctl(s);

	tcb->window = tcb->rcv.wnd = window;
	tcb->tos = tos;

	switch(mode){
	case TCP_PASSIVE:
		tcb->flags |= CLONE;
		setstate(s, LISTEN);
		break;
	case TCP_ACTIVE:
		/* Send SYN, go into SYN_SENT state */
		tcb->flags |= ACTIVE;
		qlock(tcb);
		send_syn(tcb);
		setstate(s, SYN_SENT);
		tcp_output(s);
		qunlock(tcb);
		break;
	}
}

A port/tcpinput.c => port/tcpinput.c +1126 -0
@@ 0,0 1,1126 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include 	"arp.h"
#include 	"ipdat.h"

int tcpdbg = 0;
#define DPRINT	if(tcpdbg) print
#define LPRINT  print

extern Queue *Tcpoutput;
QLock	reseqlock;
Reseq	*reseqfree;

char *tcpstate[] = {
	"Closed", 	"Listen", 	"Syn_sent", "Syn_received",
	"Established", 	"Finwait1",	"Finwait2", "Close_wait",
	"Closing", 	"Last_ack", 	"Time_wait" };

void
tcp_input(Ipconv *ipc, Block *bp)
{
	Ipconv *s, *new, *etab;
	Tcpctl *tcb;		
	Tcphdr *h;
	Tcp seg;
	int hdrlen;	
	Block *oobbp;
	Ipaddr source, dest;
	char tos;
	ushort length;

	DPRINT("tcp_input.\n");

	h = (Tcphdr *)(bp->rptr);
	dest = nhgetl(h->tcpdst);
	source = nhgetl(h->tcpsrc);

	tos = h->tos;
	length = nhgets(h->length);

	if (dest == source) {
		if (!(bp = copyb(bp, blen(bp)))) {
			print("tcpin: allocb failure.");
			return;
		}
		DPRINT("tcpin: Duplicate packet %lux\n", bp);
	}

	h->Unused = 0;
	hnputs(h->tcplen, length - (TCP_IPLEN+TCP_PHDRSIZE));

	if(ptcl_csum(bp, TCP_EHSIZE+TCP_IPLEN, length - TCP_IPLEN)) {
		DPRINT("tcpin: Bad checksum.\n");
		freeb(bp);
		return;
	}

	if((hdrlen = ntohtcp(&seg, &bp)) < 0)
		return;

	/* Adjust the data length */
	length -= (hdrlen+TCP_IPLEN+TCP_PHDRSIZE);
	
	DPRINT("tcpin: lport = %d, rport = %d hdrlen %d",
		seg.dest, seg.source, hdrlen);
	DPRINT(" flags = 0x%lux, seqo = %d, seqi = %d len %d\n", 
		seg.flags, seg.seq, seg.ack, length);

	/* Trim the packet down to just the data */
	bp = btrim(bp, hdrlen+TCP_PKT, length);
	if(bp == 0)
		return;

	if (!(s = ip_conn(ipc, seg.dest, seg.source, source, IP_TCPPROTO))) {
		LPRINT("tcpin: look for listen on %d\n", seg.dest);

		if(!(seg.flags & SYN)) {
			LPRINT("tcpin: No SYN\n");
		clear:
			LPRINT("tcpin: call cleared\n");
			freeb(bp);   
                        reset(source, dest, tos, length, &seg);
                        return;
		}		

		if(!(s = ip_conn(ipc, seg.dest, 0, 0, IP_TCPPROTO))) {
			LPRINT("tcpin: No socket dest on %d\n", seg.dest);
			goto clear;
		}

		if(s->curlog >= s->backlog) {
			LPRINT("too many pending\n");
			goto clear;
		}

		/* Find a conversation to clone onto */
		etab = &ipc[conf.ip];
		for(new = ipc; new < etab; new++) {
			if(new->ref == 0 && canqlock(new)) {
				if(new->ref || new->tcpctl.state != CLOSED) {
					qunlock(new);
					continue;
				}
				new->ref++;
				qunlock(new);
				break;
			}
		}

		if(new == etab)
			goto clear;

		s->curlog++;
		LPRINT("tcpin: cloning socket\n");
		new->psrc = s->psrc;
		new->pdst = seg.source;
		new->dst = source;
		memmove(&new->tcpctl, &s->tcpctl, sizeof(Tcpctl));
		new->tcpctl.flags &= ~CLONE;
		new->tcpctl.timer.arg = new;
		new->tcpctl.timer.state = TIMER_STOP;
		new->tcpctl.acktimer.arg = new;
		new->tcpctl.acktimer.state = TIMER_STOP;

		new->ipinterface = s->ipinterface;
		s->ipinterface->ref++;

		/* Wake the sleeping dodo */
		wakeup(&s->listenr);
		s = new;
	}
	
	tcb = &s->tcpctl;
	qlock(tcb);

	switch(tcb->state) {
	case CLOSED:
		freeb(bp);
		reset(source, dest, tos, length, &seg);
		goto done;
	case LISTEN:
		if(seg.flags & RST) {
			freeb(bp);
			goto done;
		} 
		if(seg.flags & ACK) {
			freeb(bp);
			reset(source, dest, tos, length, &seg);
			goto done;
		}
		if(seg.flags & SYN) {
			proc_syn(s, tos, &seg);
			send_syn(tcb);
			setstate(s, SYN_RECEIVED);		
			if(length != 0 || (seg.flags & FIN)) 
				break;
			freeb(bp);
			goto output;
		}
		freeb(bp);
		goto done;
	case SYN_SENT:
		if(seg.flags & ACK) {
			if(!seq_within(seg.ack, tcb->iss+1, tcb->snd.nxt)) {
				freeb(bp);
				reset(source, dest, tos, length, &seg);
				goto done;
			}
		}
		if(seg.flags & RST) {
			if(seg.flags & ACK)
				close_self(s, Econrefused);
			freeb(bp);
			goto done;
		}

		if((seg.flags & ACK) && PREC(tos) != PREC(tcb->tos)){
			freeb(bp);
			reset(source, dest, tos, length, &seg);
			goto done;
		}
		if(seg.flags & SYN) {
			proc_syn(s, tos, &seg);
			if(seg.flags & ACK){
				update(s, &seg);
				setstate(s, ESTABLISHED);
			}
			else 
				setstate(s, SYN_RECEIVED);

			if(length != 0 || (seg.flags & FIN))
				break;

			freeb(bp);
			goto output;
		}
		else 
			freeb(bp);
		goto done;
	}

	/* Trim segment to fit receive window. */
	if(trim(tcb, &seg, &bp, &length) == -1) {
		if(!(seg.flags & RST)) {
			tcb->flags |= FORCE;
			goto output;
		}
		goto done;
	}

	/* If we have no opens and the other end is sending data then
	 * reply with a reset
	 */
	if(s->readq == 0 && length) {
		freeb(bp);
		reset(source, dest, tos, length, &seg);
		goto done;
	}

	if(seg.seq != tcb->rcv.nxt
	 && (length != 0 || (seg.flags & (SYN|FIN)) )) {
		add_reseq(tcb, tos, &seg, bp, length);
		tcb->flags |= FORCE;
		goto output;
	}

	for(;;) {
		if(seg.flags & RST) {
			if(tcb->state == SYN_RECEIVED
			   && !(tcb->flags & (CLONE|ACTIVE))) 
				setstate(s, LISTEN);
			else
				close_self(s, Econrefused);

			freeb(bp);
			goto done;
		}

		if(PREC(tos) != PREC(tcb->tos) || (seg.flags & SYN)){
			freeb(bp);
			reset(source, dest, tos, length, &seg);
			goto done;
		}

		if(!(seg.flags & ACK)) {
			freeb(bp);	
			goto done;
		}

		switch(tcb->state) {
		case SYN_RECEIVED:
			if(seq_within(seg.ack, tcb->snd.una+1, tcb->snd.nxt)){
				update(s, &seg);
				setstate(s, ESTABLISHED);
			}
			else {
				freeb(bp);
				reset(source, dest, tos, length, &seg);
				goto done;
			}
			break;
		case ESTABLISHED:
		case CLOSE_WAIT:
			update(s, &seg);
			break;
		case FINWAIT1:
			update(s, &seg);
			if(tcb->sndcnt == 0)
				setstate(s, FINWAIT2);
			break;
		case FINWAIT2:
			update(s, &seg);
			break;
		case CLOSING:
			update(s, &seg);
			if(tcb->sndcnt == 0){
				setstate(s, TIME_WAIT);
				tcb->timer.start = MSL2 * (1000 / MSPTICK);
				start_timer(&tcb->timer);
			}
			break;
		case LAST_ACK:
			update(s, &seg);
			if(tcb->sndcnt == 0) {
				close_self(s, 0);
				goto done;
			}			
		case TIME_WAIT:
			tcb->flags |= FORCE;
			start_timer(&tcb->timer);
		}

		if ((seg.flags&URG) && seg.up) {
			DPRINT("tcpin: oob: up = %u seq = %u rcv.up = %u\n",
			       seg.up, seg.seq, tcb->rcv.up);
			if (seq_gt(seg.up + seg.seq, tcb->rcv.up)) {
				tcb->rcv.up = seg.up + seg.seq;
				tcb->oobflags &= ~(TCPOOB_HAVEDATA|TCPOOB_HADDATA);
				extract_oob(&bp, &oobbp, &seg);
				if (oobbp) {
					DPRINT("tcpin: oob delivered\n");
					appendb(&tcb->rcvoobq, oobbp);
					tcb->rcvoobcnt += blen(oobbp);
					tcb->oobmark = tcb->rcvcnt;
					tcb->oobflags |= TCPOOB_HAVEDATA;
#ifdef NOTIFY
					urg_signal(s);
#endif
				}
			}
		} 
		else if (seq_gt(tcb->rcv.nxt, tcb->rcv.up))
			tcb->rcv.up = tcb->rcv.nxt;

		DPRINT("tcpin: Append pkt len=%d state=%s\n", 
			length, tcpstate[tcb->state]);

		if(length != 0){
			switch(tcb->state){
			case SYN_RECEIVED:
			case ESTABLISHED:
			case FINWAIT1:
			case FINWAIT2:
				/* Place on receive queue */
				tcb->rcvcnt += blen(bp);
				if(s->readq && bp) {
					PUTNEXT(s->readq, bp);
					bp = 0;
				}
				tcb->rcv.nxt += length;

				tcprcvwin(s);
	
				start_timer(&tcb->acktimer);

				if (tcb->max_snd <= tcb->rcv.nxt-tcb->last_ack)
					tcb->flags |= FORCE;
				break;
			default:
				/* Ignore segment text */
				freeb(bp);
				break;
			}
		}

		if(seg.flags & FIN) {
			tcb->flags |= FORCE;

			switch(tcb->state) {
			case SYN_RECEIVED:
			case ESTABLISHED:
				tcb->rcv.nxt++;
				setstate(s, CLOSE_WAIT);
				break;
			case FINWAIT1:
				tcb->rcv.nxt++;
				if(tcb->sndcnt == 0) {
					setstate(s, TIME_WAIT);
					tcb->timer.start = MSL2 * (1000/MSPTICK);
					start_timer(&tcb->timer);
				}
				else 
					setstate(s, CLOSING);
				break;
			case FINWAIT2:
				tcb->rcv.nxt++;
				setstate(s, TIME_WAIT);
				tcb->timer.start = MSL2 * (1000/MSPTICK);
				start_timer(&tcb->timer);
				break;
			case CLOSE_WAIT:
			case CLOSING:
			case LAST_ACK:
				break;
			case TIME_WAIT:
				start_timer(&tcb->timer);
				break;
			}
		}
		while(tcb->reseq != 0 &&
		      seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq)){
			get_reseq(tcb, &tos, &seg, &bp, &length);
			if(trim(tcb, &seg, &bp, &length) == 0)
				goto gotone;
		}
		break;
gotone:;
	}
output:
	tcp_output(s);
done:
	qunlock(tcb);
}

void
tcp_icmp(Ipconv *ipc, Ipaddr source, Ipaddr dest, char type, char code, Block **bpp)
{
	Tcp seg;
	Tcpctl *tcb;
	Ipconv *s;

	ntohtcp(&seg, bpp);
	if(!(s = ip_conn(ipc, seg.source, seg.dest, dest, IP_TCPPROTO)))
		return;

	tcb = &s->tcpctl;

	if(!seq_within(seg.seq, tcb->snd.una, tcb->snd.nxt))
		return;

	switch((uchar)type) {
	case ICMP_UNREACH:
		tcb->type = type;
		tcb->code = code;
		if(tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED)
			close_self(s, Enetunreach);
		break;
	case ICMP_TIMXCEED:
		tcb->type = type;
		tcb->code = code;
		if(tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED)
			close_self(s, Etimedout);
		break;
	case ICMP_SOURCEQUENCH:
		tcb->cwind /= 2;
		tcb->cwind = MAX(tcb->mss,tcb->cwind);
		break;
	}
}

void
reset(Ipaddr source, Ipaddr dest, char tos, ushort length, Tcp *seg)
{
	Block *hbp;
	Port tmp;
	char rflags;
	Tcphdr ph;

	if(seg->flags & RST)
		return;

	hnputl(ph.tcpsrc, dest);
	hnputl(ph.tcpdst, source);
	ph.proto = IP_TCPPROTO;
	hnputs(ph.tcplen, TCP_HDRSIZE);

	/* Swap port numbers */
	tmp = seg->dest;
	seg->dest = seg->source;
	seg->source = tmp;

	rflags = RST;
	if(seg->flags & ACK) {
		/* This reset is being sent to clear a half-open connection.
		 * Set the sequence number of the RST to the incoming ACK
		 * so it will be acceptable.
		 */
		seg->seq = seg->ack;
		seg->ack = 0;
	}
	else {
		/* We're rejecting a connect request (SYN) from LISTEN state
		 * so we have to "acknowledge" their SYN.
		 */
		rflags |= ACK;
		seg->ack = seg->seq;
		seg->seq = 0;
		if(seg->flags & SYN)
			seg->ack++;
		seg->ack += length;
		if(seg->flags & FIN)
			seg->ack++;
	}
	seg->flags = rflags;
	seg->wnd = 0;
	seg->up = 0;
	seg->mss = 0;
	if((hbp = htontcp(seg, 0, &ph)) == 0)
		return;

	DPRINT("Reset: seq = %lux ack = %d flags = %lux\n",
	       seg->seq, seg->ack, seg->flags);

	PUTNEXT(Tcpoutput, hbp);
}

void
update(Ipconv *s, Tcp *seg)
{
	ushort acked;
	ushort oobacked;
	ushort expand;
	Tcpctl *tcb = &s->tcpctl;

	if(seq_gt(seg->ack, tcb->snd.nxt)) {
		tcb->flags |= FORCE;
		return;
	}

	if(seq_gt(seg->seq,tcb->snd.wl1) || ((seg->seq == tcb->snd.wl1) 
	 && seq_ge(seg->ack,tcb->snd.wl2))) {
		if(tcb->snd.wnd == 0 && seg->wnd != 0)
			tcb->snd.ptr = tcb->snd.una;
		tcb->snd.wnd = seg->wnd;
		tcb->snd.wl1 = seg->seq;
		tcb->snd.wl2 = seg->ack;
	}

	if(!seq_gt(seg->ack, tcb->snd.una))
		return;	

	acked = seg->ack - tcb->snd.una;

	if(tcb->cwind < tcb->snd.wnd) {
		if(tcb->cwind < tcb->ssthresh)
			expand = MIN(acked,tcb->mss);
		else
			expand = ((long)tcb->mss * tcb->mss) / tcb->cwind;

		if(tcb->cwind + expand < tcb->cwind)
			expand = 65535 - tcb->cwind;

		if(tcb->cwind + expand > tcb->snd.wnd)
			expand = tcb->snd.wnd - tcb->cwind;

		if(expand != 0)
			tcb->cwind += expand;

	}

	/* Round trip time estimation */
	if(run_timer(&tcb->rtt_timer) && seq_ge(seg->ack, tcb->rttseq)) {
		stop_timer(&tcb->rtt_timer);
		if(!(tcb->flags & RETRAN)) {
			int rtt;	/* measured round trip time */
			int abserr;	/* abs(rtt - srtt) */

			rtt = tcb->rtt_timer.start - tcb->rtt_timer.count;
			rtt *= MSPTICK;	

			if(rtt > tcb->srtt &&
			  (tcb->state == SYN_SENT || tcb->state == SYN_RECEIVED))
				tcb->srtt = rtt;
			else {
				abserr = (rtt > tcb->srtt) ? rtt - tcb->srtt : tcb->srtt - rtt;
				tcb->srtt = ((AGAIN-1)*tcb->srtt + rtt) / AGAIN;
				tcb->mdev = ((DGAIN-1)*tcb->mdev + abserr) / DGAIN;
				DPRINT("tcpout: rtt %d, srtt %d, mdev %d\n", 
					rtt, tcb->srtt, tcb->mdev);
			}

			tcb->backoff = 0;
		}
	}

	/* If we're waiting for an ack of our SYN, note it and adjust count */
	if(!(tcb->flags & SYNACK)){
		tcb->flags |= SYNACK;
		acked--;
		tcb->sndcnt--;
	}

	/* Acking some oob data if relevant */
	if(tcb->sndoobq && seq_ge(tcb->snd.up,tcb->snd.una) &&
	   seq_gt(seg->ack, tcb->snd.up)) {
		oobacked = seg->ack - tcb->snd.up;
		acked -= oobacked;
		copyupb(&tcb->sndoobq, 0, oobacked);
		tcb->sndoobcnt -= oobacked;
		DPRINT("update: oobacked = %d\n", oobacked);
	}

	copyupb(&tcb->sndq, 0, acked);

	/* This will include the FIN if there is one */
	tcb->sndcnt -= acked;
	tcb->snd.una = seg->ack;
	/* If ack includes some out-of-band data then update urgent pointer */
	if (seq_gt(seg->ack, tcb->snd.up))
		tcb->snd.up = seg->ack;

	/* Stop retransmission timer, but restart it if there is still
	 * unacknowledged data.
	 */	
	stop_timer(&tcb->timer);
	if(tcb->snd.una != tcb->snd.nxt)
		start_timer(&tcb->timer);

	/* If retransmissions have been occurring, make sure the
	 * send pointer doesn't repeat ancient history
	 */
	if(seq_lt(tcb->snd.ptr, tcb->snd.una))
		tcb->snd.ptr = tcb->snd.una;

	/* Clear the retransmission flag since the oldest
	 * unacknowledged segment (the only one that is ever retransmitted)
	 * has now been acked.
	 */
	tcb->flags &= ~RETRAN;
	tcb->backoff = 0;
}

int
in_window(Tcpctl *tcb, int seq)
{
	return seq_within(seq, tcb->rcv.nxt, 
			 (int)(tcb->rcv.nxt+tcb->rcv.wnd-1));
}

void
proc_syn(Ipconv *s, char tos, Tcp *seg)
{
	Tcpctl *tcb = &s->tcpctl;
	ushort mtu;


	tcb->flags |= FORCE;

	if(PREC(tos) > PREC(tcb->tos))
		tcb->tos = tos;

	tcb->rcv.up = tcb->rcv.nxt = seg->seq + 1;	/* p 68 */
	tcb->snd.wl1 = tcb->irs = seg->seq;
	tcb->snd.wnd = seg->wnd;

	if(seg->mss != 0)
		tcb->mss = seg->mss;

	tcb->max_snd = seg->wnd;

	if((mtu = s->ipinterface->maxmtu) != 0) {
		mtu -= TCP_HDRSIZE + TCP_EHSIZE + TCP_PHDRSIZE; 
		tcb->cwind = tcb->mss = MIN(mtu, tcb->mss);
	}
}

/* Generate an initial sequence number and put a SYN on the send queue */
void
send_syn(Tcpctl *tcb)
{
	tcb->iss = iss();
	tcb->rttseq = tcb->snd.wl2 = tcb->snd.una = tcb->iss;
	tcb->snd.ptr = tcb->snd.nxt = tcb->rttseq;
	tcb->sndcnt++;
	tcb->flags |= FORCE;
}

void
add_reseq(Tcpctl *tcb, char tos, Tcp *seg, Block *bp, ushort length)
{
	Reseq *rp, *rp1;

	qlock(&reseqlock);
	if(!reseqfree) {
		qunlock(&reseqlock);
		print("tcp: no resequence descriptors\n");
		freeb(bp);
		return;
	}

	rp = reseqfree;
	reseqfree = rp->next;
	qunlock(&reseqlock);

	rp->seg = *seg;
	rp->tos = tos;
	rp->bp = bp;
	rp->length = length;

	/* Place on reassembly list sorting by starting seq number */
	rp1 = tcb->reseq;
	if(rp1 == 0 || seq_lt(seg->seq, rp1->seg.seq)) {
		rp->next = rp1;
		tcb->reseq = rp;
	} 
	else {
		for(;;){
			if(rp1->next == 0 ||
			   seq_lt(seg->seq, rp1->next->seg.seq)) {
				rp->next = rp1->next;
				rp1->next = rp;
				break;
			}
			rp1 = rp1->next;
		}
	}
}


void
get_reseq(Tcpctl *tcb, char *tos, Tcp *seg, Block **bp, ushort *length)
{
	Reseq *rp;

	if((rp = tcb->reseq) == 0)
		return;

	tcb->reseq = rp->next;

	*tos = rp->tos;
	*seg = rp->seg;
	*bp = rp->bp;
	*length = rp->length;

	qlock(&reseqlock);
	rp->next = reseqfree;
	reseqfree = rp;
	qunlock(&reseqlock);
}

int
trim(Tcpctl *tcb, Tcp *seg, Block **bp, ushort *length)
{
	Block *nbp;
	long dupcnt,excess;
	ushort len;		/* Segment length including flags */
	char accept;

	accept = 0;
	len = *length;
	if(seg->flags & SYN)
		len++;
	if(seg->flags & FIN)
		len++;

	/* Acceptability tests */
	if(tcb->rcv.wnd == 0) {
		if(seg->seq == tcb->rcv.nxt && len == 0)
			return 0;
	} else {
		/* Some part of the segment must be in the window */
		if(in_window(tcb,seg->seq)) {
			accept++;
		}
		else if(len != 0) {
			if(in_window(tcb, (int)(seg->seq+len-1)) || 
			seq_within(tcb->rcv.nxt, seg->seq,(int)(seg->seq+len-1)))
				accept++;
		}
	}
	if(!accept) {
		freeb(*bp);
		return -1;
	}
	dupcnt = tcb->rcv.nxt - seg->seq;
	if(dupcnt > 0){
		tcb->rerecv += dupcnt;
		if(seg->flags & SYN){
			seg->flags &= ~SYN;
			seg->seq++;

			if (seg->up > 1)
				seg->up--;
			else
				seg->flags &= ~URG;
			dupcnt--;
		}
		if(dupcnt > 0){
			copyupb(bp, 0, (ushort)dupcnt);
			seg->seq += dupcnt;
			*length -= dupcnt;

			if (seg->up > dupcnt)
				seg->up -= dupcnt;
			else {
				seg->flags &= ~URG;
				seg->up = 0;
			}
		}
	}
	excess = seg->seq + *length - (tcb->rcv.nxt + tcb->rcv.wnd);
	if(excess > 0){
		tcb->rerecv += excess;
		*length -= excess;
		nbp = copyb(*bp, *length);
		freeb(*bp);
		*bp = nbp;
		seg->flags &= ~FIN;
	}
	return 0;
}

void
extract_oob(Block **bp, Block **oobbp, Tcp *seg)

{
	DPRINT("extract_oob: size = %u\n", seg->up);

	if (*oobbp = allocb(seg->up))
		(*oobbp)->wptr = (*oobbp)->wptr +
			         copyupb(bp, (*oobbp)->rptr, seg->up);
	else
		copyupb(bp, 0, seg->up);
}

int
copyupb(Block **bph, uchar *data, int count)
{
	int n, bytes;
	Block *bp;

	bytes = 0;
	if(bph == 0)
		return 0;

	while(*bph && count != 0) {
		bp = *bph;
		n = MIN(count, BLEN(bp));
		if(data && n) {
			memmove(data, bp->rptr, n);
			data += n;
		}
		bytes += n;
		count -= n;
		bp->rptr += n;
		if(BLEN(bp) == 0) {
			*bph = bp->next;
			bp->next = 0;
			freeb(bp);
		}
	}

	return bytes;
}

void
appendb(Block **list, Block *bp)
{
	Block *f;

	if(f = *list) {
		while(f->next)
			f = f->next;
		f->next = bp;
	}
	else
		*list = bp;

	bp->next = 0;
}

int
dupb(Block **hp, Block *bp, int offset, int count)
{
	int i, blen, bytes = 0;
	uchar *addr;
	
	*hp = allocb(count);
	if(*hp == 0)
		return 0;

	/* Correct to front of data area */
	while(bp && offset && offset >= BLEN(bp)) {
		offset -= BLEN(bp);
		bp = bp->next;
	}
	if(bp == 0)
		return 0;

	addr = bp->rptr + offset;
	blen = BLEN(bp) - offset;

	while(count) {
		i = MIN(count, blen);
		memmove((*hp)->wptr, addr, i);
		(*hp)->wptr += i;
		bytes += i;
		count -= i;
		bp = bp->next;
		if(!bp)
			break;
		blen = BLEN(bp);
		addr = bp->rptr;
	}

	return bytes;
}

Block *
copyb(Block *bp, int count)
{
	Block *nbp;
	int i;

	nbp = allocb(count);
	if(nbp == 0)
		return 0;

	while(bp && count) {
		i = MIN(count, BLEN(bp));
		memmove(nbp->wptr, bp->rptr, i);
		nbp->wptr += i;
		count -= i;
		bp = bp->next;
	}

	return nbp;	
}

ushort tcp_mss = DEF_MSS;	/* Maximum segment size to be sent with SYN */
int tcp_irtt = DEF_RTT;		/* Initial guess at round trip time */

void
init_tcpctl(Ipconv *s)
{

	Tcpctl *tcb = &s->tcpctl;

	memset(tcb, 0, sizeof(Tcpctl));

	tcb->cwind = tcb->mss = tcp_mss;
	tcb->ssthresh = 65535;
	tcb->srtt = tcp_irtt;

	/* Initialize timer intervals */
	tcb->timer.start = tcb->srtt / MSPTICK;
	tcb->timer.func = (void(*)(void*))tcp_timeout;
	tcb->timer.arg = (void *)s;
	tcb->rtt_timer.start = MAX_TIME; 

	/* Initialise ack timer */
	tcb->acktimer.start = TCP_ACK / MSPTICK;
	tcb->acktimer.func = (void(*)(void*))tcp_acktimer;
	tcb->acktimer.arg = (void *)s;
}

void
close_self(Ipconv *s, int reason)
{
	Reseq *rp,*rp1;
	Tcpctl *tcb = &s->tcpctl;

	stop_timer(&tcb->timer);
	stop_timer(&tcb->rtt_timer);
	s->err = reason;

	/* Flush reassembly queue; nothing more can arrive */
	for(rp = tcb->reseq;rp != 0;rp = rp1){
		rp1 = rp->next;
		freeb(rp->bp);

		qlock(&reseqlock);
		rp->next = reseqfree;
		reseqfree = rp;
		qunlock(&reseqlock);
	}

	tcb->reseq = 0;
	s->err = reason;

	setstate(s, CLOSED);
}

int
iss(void)
{
	static int seq;

	seq += 250000;
	return seq;
}

int
seq_within(int x, int low, int high)
{
	if(low <= high){
		if(low <= x && x <= high)
			return 1;
	} else {
		if(low >= x && x >= high)
			return 1;
	}
	return 0;
}

int
seq_lt(int x, int y)
{
	return (long)(x-y) < 0;
}

int
seq_le(int x, int y)
{
	return (long)(x-y) <= 0;
}

int
seq_gt(int x, int y)
{
	return (long)(x-y) > 0;
}

int
seq_ge(int x, int y)
{
	return (long)(x-y) >= 0;
}

void
setstate(Ipconv *s, char newstate)
{
	char oldstate;
	Tcpctl *tcb = &s->tcpctl;

	oldstate = tcb->state;
	tcb->state = newstate;

	state_upcall(s, oldstate, newstate);
}

Block *
htontcp(Tcp *tcph, Block *data, Tcphdr *ph)
{
	ushort hdrlen;
	int dlen;
	ushort csum;
	Tcphdr *h;
	Block *bp;

	hdrlen = TCP_HDRSIZE;
	if(tcph->mss)
		hdrlen += MSS_LENGTH;

	if(data) {
		dlen = blen(data);	
		if((data = padb(data, hdrlen + TCP_PKT)) == 0)
			return 0;
		/* If we collected blocks delimit the end of the chain */
		for(bp = data; bp->next; bp = bp->next)
			bp->flags &= ~S_DELIM;
		bp->flags |= S_DELIM;
	}
	else {
		dlen = 0;
		data = allocb(hdrlen + TCP_PKT);
		if(data == 0)
			return 0;
		data->wptr += hdrlen + TCP_PKT;
		data->flags |= S_DELIM;
	}


	memmove(data->rptr, ph, TCP_PKT);
	
	h = (Tcphdr *)(data->rptr);
	h->proto = IP_TCPPROTO;
	hnputs(h->tcplen, hdrlen + dlen);
	hnputs(h->tcpsport, tcph->source);
	hnputs(h->tcpdport, tcph->dest);
	hnputl(h->tcpseq, tcph->seq);
	hnputl(h->tcpack, tcph->ack);
	hnputs(h->tcpflag, (hdrlen<<10) | tcph->flags);
	hnputs(h->tcpwin, tcph->wnd);
	h->tcpcksum[0] = 0;
	h->tcpcksum[1] = 0;
	h->Unused = 0;
	hnputs(h->tcpurg, tcph->up);

	if(tcph->mss != 0){
		h->tcpopt[0] = MSS_KIND;
		h->tcpopt[1] = MSS_LENGTH;
		hnputs(h->tcpmss, tcph->mss);
	}
	csum = ptcl_csum(data, TCP_EHSIZE+TCP_IPLEN, hdrlen+dlen+TCP_PHDRSIZE);
	hnputs(h->tcpcksum, csum);

	return data;
}

int
ntohtcp(Tcp *tcph, Block **bpp)
{
	ushort hdrlen;
	ushort i, optlen;
	Block *nbp;
	Tcphdr *h;
	uchar *optr;

	*bpp = pullup(*bpp, TCP_PKT+TCP_HDRSIZE);
	if(*bpp == 0)
		return -1;

	h = (Tcphdr *)((*bpp)->rptr);
	tcph->source = nhgets(h->tcpsport);
	tcph->dest = nhgets(h->tcpdport);
	tcph->seq = nhgetl(h->tcpseq);
	tcph->ack = nhgetl(h->tcpack);

	hdrlen = (h->tcpflag[0] & 0xf0) >> 2;
	if(hdrlen < TCP_HDRSIZE) {
		freeb(*bpp);
		return -1;
	}

	tcph->flags = h->tcpflag[1];
	tcph->wnd = nhgets(h->tcpwin);
	tcph->up = nhgets(h->tcpurg);
	tcph->mss = 0;

	*bpp = pullup(*bpp, hdrlen+TCP_PKT);
	if(!*bpp)
		return -1;

	for(optr = h->tcpopt, i = TCP_HDRSIZE; i < hdrlen;) {
		switch(*optr++){
		case EOL_KIND:
			goto eol;
		case NOOP_KIND:
			i++;
			break;
		case MSS_KIND:
			optlen = *optr++;
			if(optlen == MSS_LENGTH)
				tcph->mss = nhgets(optr);
			i += optlen;
			break;
		}
	}
eol:
	return hdrlen;
}

A port/tcpoutput.c => port/tcpoutput.c +283 -0
@@ 0,0 1,283 @@
#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include 	"arp.h"
#include 	"ipdat.h"

extern int tcpdbg;
extern ushort tcp_mss;
extern Queue *Tcpoutput;

#define DPRINT if(tcpdbg) print

void
tcp_output(Ipconv *s)
{
	Block *hbp,*dbp, *sndq;
	ushort ssize, dsize, usable, sent, oobsent;
	int qlen;
	char doing_oob;	
	Tcphdr ph;
	Tcp seg;
	Tcpctl *tcb;

	tcb = &s->tcpctl;

	switch(tcb->state) {
	case LISTEN:
	case CLOSED:
		return;
	}
	for(;;){
		if (tcb->sndoobq) {
			/* We have pending out-of-band data - use it */
			qlen = tcb->sndoobcnt;
			oobsent = tcb->snd.ptr - tcb->snd.up;
			if (oobsent >= qlen) {
				oobsent = qlen;
				goto normal;
			}
			sndq = tcb->sndoobq;
			sent = oobsent;
			doing_oob = 1;
			DPRINT("tcp_out: oob: qlen = %lux sent = %lux\n",
						qlen, sent);
		} else {
			oobsent = 0;
			normal:
			qlen = tcb->sndcnt;
			sent = tcb->snd.ptr - tcb->snd.una - oobsent;
			sndq = tcb->sndq;
			doing_oob = 0;
			DPRINT("tcp_out: norm: qlen = %lux sent = %lux\n", qlen, sent);
		}

		/* Don't send anything else until our SYN has been acked */
		if(sent != 0 && !(tcb->flags & SYNACK))
			break;

		if(tcb->snd.wnd == 0){
			/* Allow only one closed-window probe at a time */
			if(sent != 0)
				break;
			/* Force a closed-window probe */
			usable = 1;
		} else {
			/* usable window = offered window - unacked bytes in transit
			 * limited by the congestion window
			 */
			usable = MIN(tcb->snd.wnd,tcb->cwind) - sent;
			if(sent != 0 && qlen - sent < tcb->mss) 
				usable = 0;
		}

		ssize = MIN(qlen - sent, usable);
		ssize = MIN(ssize, tcb->mss);
		dsize = ssize;

		if (!doing_oob)
			seg.up = 0;
		else {
			seg.up = ssize;
			DPRINT("tcp_out: oob seg.up = %d\n", seg.up);
		}

		DPRINT("tcp_out: ssize = %lux\n", ssize);
		if(ssize == 0 && !(tcb->flags & FORCE))
			break;

		/* Stop ack timer if one will be piggy backed on data */
		stop_timer(&tcb->acktimer);

		tcb->flags &= ~FORCE;

		seg.source = s->psrc;
		seg.dest = s->pdst;
		/* Every state except SYN_SENT */
		seg.flags = ACK; 	
		seg.mss = 0;

		switch(tcb->state){
		case SYN_SENT:
			seg.flags = 0;
			/* No break */
		case SYN_RECEIVED:
			if(tcb->snd.ptr == tcb->iss){
				seg.flags |= SYN;
				dsize--;
				/* Also send MSS */
				seg.mss = tcp_mss;
			}
			break;
		}
		seg.seq = tcb->snd.ptr;
		seg.ack = tcb->last_ack = tcb->rcv.nxt;
		seg.wnd = tcb->rcv.wnd;

		if (doing_oob) {
			DPRINT("tcp_out: Setting URG (up = %u)\n", seg.up);
			seg.flags |= URG;
		}

		/* Now try to extract some data from the send queue.
		 * Since SYN and FIN occupy sequence space and are reflected
		 * in sndcnt but don't actually sit in the send queue,
		 * dupb will return one less than dsize if a FIN needs to be sent.
		 */
		if(dsize != 0){
			if(dupb(&dbp, sndq, sent, dsize) != dsize) {
				seg.flags |= FIN;
				dsize--;
			}
			DPRINT("dupb: 1st char = %c\n", dbp->rptr[0]);
		} else
			dbp = 0;

		/* If the entire send queue will now be in the pipe, set the
		 * push flag
		 */
		if((dsize != 0) && 
		   ((sent + ssize) == (tcb->rcvcnt + tcb->rcvoobcnt)))
			seg.flags |= PSH;

		/* If this transmission includes previously transmitted data,
		 * snd.nxt will already be past snd.ptr. In this case,
		 * compute the amount of retransmitted data and keep score
		 */
		if(tcb->snd.ptr < tcb->snd.nxt)
			tcb->resent += MIN((int)tcb->snd.nxt - (int)tcb->snd.ptr,(int)ssize);

		tcb->snd.ptr += ssize;

		/* If this is the first transmission of a range of sequence
		 * numbers, record it so we'll accept acknowledgments
		 * for it later
		 */
		if(seq_gt(tcb->snd.ptr,tcb->snd.nxt))
			tcb->snd.nxt = tcb->snd.ptr;

		/* Fill in fields of pseudo IP header */
		hnputl(ph.tcpdst, s->dst);
		hnputl(ph.tcpsrc, Myip);
		hnputs(ph.tcpsport, s->psrc);
		hnputs(ph.tcpdport, s->pdst);

		/* Build header, link data and compute cksum */
		if((hbp = htontcp(&seg, dbp, &ph)) == 0) {
			freeb(dbp);
			return;
		}

		/* If we're sending some data or flags, start retransmission
		 * and round trip timers if they aren't already running.
		 */
		if(ssize != 0){
			tcb->timer.start = backoff(tcb->backoff) *
			 (2 * tcb->mdev + tcb->srtt + MSPTICK) / MSPTICK;
			if(!run_timer(&tcb->timer))
				start_timer(&tcb->timer);

			/* If round trip timer isn't running, start it */
			if(!run_timer(&tcb->rtt_timer)){
				start_timer(&tcb->rtt_timer);
				tcb->rttseq = tcb->snd.ptr;
			}
		}
		DPRINT("tcp_output: ip_send s%lux a%lux w%lux u%lux\n",
			seg.seq, seg.ack, seg.wnd, seg.up);

		PUTNEXT(Tcpoutput, hbp);
	}
}

int tcptimertype = 0;

void
tcp_timeout(void *arg)
{
	Tcpctl *tcb;
	Ipconv *s;

	s = (Ipconv *)arg;
	tcb = &s->tcpctl;

	DPRINT("Timer %lux state = %d\n", s, tcb->state);

	switch(tcb->state){
	case CLOSED:
		panic("tcptimeout");
	case TIME_WAIT:
		close_self(s, 0);
		break;
	case ESTABLISHED:
		if(tcb->backoff < MAXBACKOFF)
			tcb->backoff++;
		goto retran;
	default:
		tcb->backoff++;
		DPRINT("tcp_timeout: retransmit %d %x\n", tcb->backoff, s);

		if (tcb->backoff >= MAXBACKOFF) {
			DPRINT("tcp_timeout: timeout\n");
			close_self(s, Etimedout);
		}
		else {
	retran:
			qlock(tcb);
			tcb->flags |= RETRAN|FORCE;
			tcb->snd.ptr = tcb->snd.una;

			/* Reduce slowstart threshold to half current window */
			tcb->ssthresh = tcb->cwind / 2;
			tcb->ssthresh = MAX(tcb->ssthresh,tcb->mss);

			/* Shrink congestion window to 1 packet */
			tcb->cwind = tcb->mss;
			tcp_output(s);
			qunlock(tcb);
		}
	}
}

int
backoff(int n)
{
	if(tcptimertype == 1) 
		return n+1;
	else {
		if(n <= 4)
			return 1 << n;
		else
			return n * n;
	}
}

void
tcp_acktimer(Ipconv *s)
{
	Tcpctl *tcb = &s->tcpctl;

	qlock(tcb);
	tcb->flags |= FORCE;
	tcprcvwin(s);
	tcp_output(s);
	qunlock(tcb);
}

void
tcprcvwin(Ipconv *s)
{
	Tcpctl *tcb = &s->tcpctl;

	/* Calculate new window */
	if(s->readq) {
		tcb->rcv.wnd = Streamhi - s->readq->next->len;
		if(tcb->rcv.wnd < 0)
			tcb->rcv.wnd = 0;
	}
	else
		tcb->rcv.wnd = Streamhi;
}

A port/tcptimer.c => port/tcptimer.c +129 -0
@@ 0,0 1,129 @@

#include	"u.h"
#include	"lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"errno.h"
#include 	"arp.h"
#include 	"ipdat.h"

/* Head of running timer chain */
Timer 	*timers;
QLock 	timerlock;
Rendez	Tcpack;
Rendez	tcpflowr;

void
tcpackproc(void *junk)
{
	Timer *t,*tp;
	Timer *expired;

	for(;;) {
		expired = 0;

		/* Run through the list of running timers, decrementing each one.
		 * If one has expired, take it off the running list and put it
		 * on a singly linked list of expired timers
		 */

		qlock(&timerlock);
		for(t = timers;t != 0; t = tp) {
			tp = t->next;
			if(tp == t)
				panic("Timer loop at %lux\n",(long)tp);
	
 			if(t->state == TIMER_RUN && --(t->count) == 0){

				/* Delete from active timer list */
				if(timers == t)
					timers = t->next;
				if(t->next != 0)
					t->next->prev = t->prev;
				if(t->prev != 0)
					t->prev->next = t->next;

				t->state = TIMER_EXPIRE;
				/* Put on head of expired timer list */
				t->next = expired;
				expired = t;
			}
		}
		qunlock(&timerlock);

		while((t = expired) != 0){
			expired = t->next;
			if(t->state == TIMER_EXPIRE && t->func)
				(*t->func)(t->arg);
		}

		tsleep(&Tcpack, return0, 0, MSPTICK);
	}
}

void
start_timer(Timer *t)
{

	if(t == 0 || t->start == 0)
		return;

	qlock(&timerlock);

	t->count = t->start;
	if(t->state != TIMER_RUN){
		t->state = TIMER_RUN;
		/* Put on head of active timer list */
		t->prev = 0;
		t->next = timers;
		if(t->next != 0)
			t->next->prev = t;
		timers = t;
	}
	qunlock(&timerlock);
}

void
stop_timer(Timer *t)
{
	if(t == 0)
		return;

	qlock(&timerlock);

	if(t->state == TIMER_RUN){
		/* Delete from active timer list */
		if(timers == t)
			timers = t->next;
		if(t->next != 0)
			t->next->prev = t->prev;
		if(t->prev != 0)
			t->prev->next = t->next;
	}
	t->state = TIMER_STOP;

	qunlock(&timerlock);
}

void
tcpflow(void *conv)
{
	Ipconv *base, *ifc, *etab;

	base = (Ipconv*)conv;

	etab = &base[conf.ip];
	for(;;) {
		sleep(&tcpflowr, return0, 0);

		for(ifc = base; ifc < etab; ifc++) {
			if(ifc->stproto == &tcpinfo &&
			   ifc->ref != 0 && ifc->readq &&
			   !QFULL(ifc->readq->next)) {
				tcprcvwin(ifc);
				tcp_acktimer(ifc);
			}
		}
	}
}


M power/conf.h => power/conf.h +4 -0
@@ 30,6 30,10 @@ Conftab conftab[] = {
	{"base0", &conf.base0 },
	{"base1", &conf.base1 },
	{"copymode", &conf.copymode },
	{"ipif", &conf.ipif },
	{"ip", &conf.ip },
	{"arp", &conf.arp },
	{"frag", &conf.frag },
	{ 0, 0 },
};


M power/dat.h => power/dat.h +5 -0
@@ 170,6 170,11 @@ struct Conf
	ulong	base0;		/* base of bank 0 */
	ulong	base1;		/* base of bank 1 */
	ulong	copymode;	/* 0 is copy on write, 1 is copy on reference */
	ulong	ipif;		/* Ip protocol interfaces */
	ulong	ip;		/* Ip conversations per interface */
	ulong	arp;		/* Arp table size */
	ulong	frag;		/* Ip fragment assemble queue size */

};

struct Dev

M power/errno.h => power/errno.h +9 -0
@@ 53,6 53,15 @@ enum{
	Enovmem,	/* virtual memory allocation failed */
	Enoasync,	/* out of async stream modules */
	Enopipe,	/* out of pipes */
	Emsgsize,	/* message is too big for protocol */
	Enoport,	/* network port not available */
	Edevbusy,	/* network device is busy or allocated */
	Eaddrnotfound,	/* network address not found */
	Enetunreach,	/* network unreachable */
	Etimedout,	/* connection timed out */
	Econrefused,	/* connection refused */
	Enoproto,	/* network protocol not supported */
	Eprotonosup,	/* operation not supported by network protocol */
	Eisstream,	/* seek on a stream */
	Egreg,		/* ken has implemented datakit */
};

M power/fns.h => power/fns.h +3 -0
@@ 4,6 4,8 @@ Block	*allocb(ulong);
int	anyready(void);
void	append(List**, List*);
void	arginit(void);
int	blen(Block *);
int	bround(Block *, int);
void	cancel(Alarm*);
int	canlock(Lock*);
int	canqlock(QLock*);


@@ 141,6 143,7 @@ void	printinit(void);
void	printslave(void);
void	procinit0(void);
Proc	*proctab(int);
Block	*pullup(Block *, int);
void	purgetlb(int);
Queue	*pushq(Stream*, Qinfo*);
void	putmmu(ulong, ulong);

A power/ipdat.h => power/ipdat.h +440 -0
@@ 0,0 1,440 @@
typedef struct Ipconv	Ipconv;
typedef struct Ipifc	Ipifc;
typedef struct Fragq	Fragq;
typedef struct Ipfrag	Ipfrag;
typedef ulong		Ipaddr;
typedef struct Arpcache	Arpcache;
typedef ushort		Port;
typedef struct Udphdr	Udphdr;
typedef struct Etherhdr	Etherhdr;
typedef struct Reseq	Reseq;
typedef struct Tcp	Tcp;
typedef struct Tcpctl	Tcpctl;
typedef struct Tcphdr	Tcphdr;
typedef struct Timer	Timer;

struct Etherhdr {
#define ETHER_HDR	14
	uchar	d[6];
	uchar	s[6];
	uchar	type[2];

	/* Now we have the ip fields */
#define ETHER_IPHDR	20
	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 */
};

/* Ethernet packet types */
#define ET_IP	0x0800

/* A userlevel data gram */
struct Udphdr {
#define UDP_EHSIZE	22
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */
	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 */

	/* Udp pseudo ip really starts here */
#define UDP_PHDRSIZE	12
#define UDP_HDRSIZE	20
	uchar	Unused;	
	uchar	udpproto;	/* Protocol */
	uchar	udpplen[2];	/* Header plus data length */
	uchar	udpsrc[4];	/* Ip source */
	uchar	udpdst[4];	/* Ip destination */
	uchar	udpsport[2];	/* Source port */
	uchar	udpdport[2];	/* Destination port */
	uchar	udplen[2];	/* data length */
	uchar	udpcksum[2];	/* Checksum */
};

#define TCP_PKT	(TCP_EHSIZE+TCP_IPLEN+TCP_PHDRSIZE)

struct Tcphdr {
#define TCP_EHSIZE	14
	uchar	d[6];		/* Ethernet destination */
	uchar	s[6];		/* Ethernet source */
	uchar	type[2];	/* Ethernet packet type */
#define TCP_IPLEN	8
	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 */

#define TCP_PHDRSIZE	12	
	uchar	Unused;
	uchar	proto;
	uchar	tcplen[2];
	uchar	tcpsrc[4];
	uchar	tcpdst[4];

#define TCP_HDRSIZE	20
	uchar	tcpsport[2];
	uchar	tcpdport[2];
	uchar	tcpseq[4];
	uchar	tcpack[4];
	uchar	tcpflag[2];
	uchar	tcpwin[2];
	uchar	tcpcksum[2];
	uchar	tcpurg[2];

	/* Options segment */
	uchar	tcpopt[2];
	uchar	tcpmss[2];
	};



struct Timer {
	Timer	*next;
	Timer	*prev;
	int	state;
	int	start;
	int	count;
	void	(*func)(void*);
	void	*arg;
	};

struct Tcpctl {
	QLock;
	uchar	state;		/* Connection state */
	uchar	type;		/* Listening or active connection */
	uchar	code;		/* Icmp code */		
	struct {
		int una;	/* Unacked data pointer */
		int nxt;	/* Next sequence expected */
		int ptr;	/* Data pointer */
		ushort wnd;	/* Tcp send window */
		int up;		/* Urgent data pointer */
		int wl1;
		int wl2;
	} snd;
	int	iss;
	ushort	cwind;
	ushort	ssthresh;
	int	resent;
	struct {
		int nxt;
		ushort wnd;
		int up;
	} rcv;
	int	irs;
	ushort	mss;
	int	rerecv;
	ushort	window;
	int	max_snd;
	int	last_ack;
	char	backoff;
	char	flags;
	char	tos;

	Block	*rcvq;
	ushort	rcvcnt;

	Block	*rcvoobq;
	ushort	rcvoobcnt;

	Block	*sndq;			/* List of data going out */
	ushort	sndcnt;			/* Amount of data in send queue */

	Block	*sndoobq;		/* List of blocks going oob */
	ushort	sndoobcnt;		/* Size of out of band queue */
	ushort	oobmark;		/* Out of band sequence mark */
	char	oobflags;		/* Out of band data flags */

	Reseq	*reseq;			/* Resequencing queue */
	Timer	timer;			 
	Timer	acktimer;		/* Acknoledge timer */
	Timer	rtt_timer;		/* Round trip timer */
	int	rttseq;			/* Round trip sequence */
	int	srtt;			/* Shortened round trip */
	int	mdev;			/* Mean deviation of round trip */
};

struct	Tcp {
	Port	source;
	Port	dest;
	int	seq;
	int	ack;
	char	flags;
	ushort	wnd;
	ushort	up;
	ushort	mss;
	};

struct Reseq {
	Reseq 	*next;
	Tcp	seg;
	Block	*bp;
	ushort	length;
	char	tos;
	};

/* An ip interface used for UDP/TCP/ARP/ICMP */
struct Ipconv {
	QLock;				/* Ref count lock */
	int 	ref;
	Qinfo	*stproto;		/* Stream protocol for this device */
	Ipaddr	dst;			/* Destination from connect */

	Port	psrc;			/* Source port */
	Port	pdst;			/* Destination port */

	uchar	ptype;			/* Source port type */
	Ipifc	*ipinterface;		/* Ip protocol interface */
	Queue	*readq;			/* Pointer to upstream read q */

	QLock	listenq;		/* List of people waiting incoming cons */
	Rendez	listenr;		/* Some where to sleep while waiting */
	Ipconv	*listen;
		
	char	err;			/* Async protocol error */
	int	backlog;		/* Maximum number of waiting connections */
	int	curlog;			/* Number of waiting connections */
	int 	contype;
	Tcpctl	tcpctl;			/* Tcp control block */
};

#define	MAX_TIME	100000000	/* Forever */
#define TCP_ACK		200		/* Timed ack sequence every 200ms */

#define URG	0x20
#define ACK	0x10
#define PSH	0x08
#define RST	0x04
#define SYN	0x02
#define FIN	0x01

#define EOL_KIND	0
#define NOOP_KIND	1
#define MSS_KIND	2

#define MSS_LENGTH	4
#define MSL2		10
#define MSPTICK		200
#define DEF_MSS		1024
#define DEF_RTT		1000
#define	TCPOOB_HADDATA	1
#define	TCPOOB_HAVEDATA 2

#define TCP_PASSIVE	0
#define TCP_ACTIVE	1

#define MAXBACKOFF	5
#define FORCE		1
#define	CLONE		2
#define RETRAN		4
#define ACTIVE		8
#define SYNACK		16
#define AGAIN		8
#define DGAIN		4

#define TIMER_STOP	0
#define TIMER_RUN	1
#define TIMER_EXPIRE	2

#define	set_timer(t,x)	(((t)->start) = (x)/MSPTICK)
#define	dur_timer(t)	((t)->start)
#define	read_timer(t)	((t)->count)
#define	run_timer(t)	((t)->state == TIMER_RUN)

enum {
	CLOSED = 0,
	LISTEN,
	SYN_SENT,
	SYN_RECEIVED,
	ESTABLISHED,
	FINWAIT1,
	FINWAIT2,
	CLOSE_WAIT,
	CLOSING,
	LAST_ACK,
	TIME_WAIT
	};

/*
 * Ip interface structure. We have one for each active protocol driver
 */
struct Ipifc {
	QLock;
	int 		ref;
	uchar		protocol;		/* Ip header protocol number */
	char		name[NAMELEN];		/* Protocol name */
	void (*iprcv)	(Ipconv *, Block *);	/* Receive demultiplexor */
	Ipconv		*connections;		/* Connection list */
	int		maxmtu;			/* Maximum transfer unit */
	int		minmtu;			/* Minumum tranfer unit */
	int		hsize;			/* Media header size */	
	Lock;	
};

struct Fragq {
	QLock;
	Block  *blist;
	Fragq  *next;
	Ipaddr src;
	Ipaddr dst;
	ushort id;
	};

struct Ipfrag {
	ushort	foff;
	ushort	flen;
	};

struct Arpcache {
	uchar	status;		/* Entry status */
	uchar	type;		/* Entry type */
	Ipaddr	ip;		/* Host byte order */
	uchar	eip[4];		/* Network byte order */
	uchar	et[6];		/* Ethernet address for this ip */
	int	age;		/* Entry timeout */
	Arpcache *hash;
	Arpcache **hashhd;
	Arpcache *frwd;
	Arpcache *prev;
};
#define ARP_FREE	0
#define ARP_OK		1
#define ARP_ASKED	2
#define ARP_TEMP	0
#define ARP_PERM	1
#define Arphashsize	32
#define ARPHASH(p)	arphash[((p[2]^p[3])%Arphashsize)]
#define ARP_WAITMS	2500		/* Wait for arp replys */

#define IP_VER	0x40			/* Using IP version 4 */
#define IP_HLEN 0x05			/* Header length in characters */
#define IP_DF	0x4000			/* Don't fragment */
#define IP_MF	0x2000			/* More fragments */

#define	ICMP_ECHOREPLY		0	/* Echo Reply */
#define	ICMP_UNREACH		3	/* Destination Unreachable */
#define	ICMP_SOURCEQUENCH	4	/* Source Quench */
#define	ICMP_REDIRECT		5	/* Redirect */
#define	ICMP_ECHO		8	/* Echo Request */
#define	ICMP_TIMXCEED		11	/* Time-to-live Exceeded */
#define	ICMP_PARAMPROB		12	/* Parameter Problem */
#define	ICMP_TSTAMP		13	/* Timestamp */
#define	ICMP_TSTAMPREPLY	14	/* Timestamp Reply */
#define	ICMP_IREQ		15	/* Information Request */
#define	ICMP_IREQREPLY		16	/* Information Reply */

/* Sizes */
#define IP_MAX		8192			/* Maximum Internet packet size */
#define UDP_MAX		(IP_MAX-ETHER_IPHDR)	/* Maximum UDP datagram size */
#define UDP_DATMAX	(UDP_MAX-UDP_HDRSIZE)	/* Maximum amount of udp data */

/* Protocol numbers */
#define IP_UDPPROTO	17
#define IP_TCPPROTO	6

/* Protocol port numbers */
#define PORTALLOC	5000		/* First automatic allocated port */
#define PRIVPORTALLOC	600		/* First priveleged port allocated */
#define PORTMAX		30000		/* Last port to allocte */

/* Stuff to go in funs.h someday */
Ipifc   *newipifc(uchar, void (*)(Ipconv *, Block*), Ipconv *, int, int, int, char*);
void	closeipifc(Ipifc*);
ushort	ip_csum(uchar*);
int	arp_lookup(uchar*, uchar*);
Ipaddr	ipparse(char*);
void	hnputs(uchar*, ushort);
void	hnputl(uchar*, ulong);
ulong	nhgetl(uchar*);
ushort	nhgets(uchar*);
ushort	ptcl_csum(Block*bp, int, int);
void	ppkt(Block*);
void	udprcvmsg(Ipconv *, Block*);
Block	*btrim(Block*, int, int);
Block	*ip_reassemble(int, Block*, Etherhdr*);
Ipconv	*portused(Ipconv *, Port);
Port	nextport(Ipconv *, Port);
void	arp_enter(Arpentry*, int);
void	arp_flush(void);
int	arp_delete(char*);
void	arplinkhead(Arpcache*);
Fragq   *ipfragallo(void);
void	ipfragfree(Fragq*);
void	iproute(uchar*, uchar*);
void	initfrag(int);
Block	*copyb(Block*, int);
int	ntohtcp(Tcp*, Block**);
void	reset(Ipaddr, Ipaddr, char, ushort, Tcp*);
void	proc_syn(Ipconv*, char, Tcp*);
void	send_syn(Tcpctl*);
void	tcp_output(Ipconv*);
int	seq_within(int, int, int);
void	update(Ipconv *, Tcp *);
int	trim(Tcpctl *, Tcp *, Block **, ushort *);
void	add_reseq(Tcpctl *, char, Tcp *, Block *, ushort);
void	close_self(Ipconv *, int);
int	seq_gt(int, int);
void	appendb(Block **, Block *);
Ipconv	*ip_conn(Ipconv *, Port, Port, Ipaddr dest, char proto);
void	ipmkdir(Qinfo *, Dirtab *, Ipconv *);
int	inb_window(Tcpctl *, int);
Block	*htontcp(Tcp *, Block *, Tcphdr *);
void	start_timer(Timer *);
void	stop_timer(Timer *);
int	copyupb(Block **, uchar *, int);
void	init_tcpctl(Ipconv *);
void	close_self(Ipconv *, int);
int	iss(void);
int	seq_within(int, int, int);
int	seq_lt(int, int);
int	seq_le(int, int);
int	seq_gt(int, int);
int	seq_ge(int, int);
void	setstate(Ipconv *, char);
void	tcpackproc(void*);
Block 	*htontcp(Tcp *, Block *, Tcphdr *);
int	ntohtcp(Tcp *, Block **);
void	extract_oob(Block **, Block **, Tcp *);
void	get_reseq(Tcpctl *, char *, Tcp *, Block **, ushort *);
void	state_upcall(Ipconv*, char oldstate, char newstate);
int	backoff(int);
int	dupb(Block **, Block *, int, int);
void	tcp_input(Ipconv *, Block *);
void 	tcprcvwin(Ipconv *);
void	open_tcp(Ipconv *, int, ushort, char);
void	tcpflow(void*);
void 	tcp_timeout(void *);
void	tcp_acktimer(void *);
Ipconv  *ipclonecon(Chan *);
void	iplisten(Chan *, Ipconv *, Ipconv *);

#define	fmtaddr(xx)	(xx>>24)&0xff,(xx>>16)&0xff,(xx>>8)&0xff,xx&0xff
#define	MIN(a, b)	((a) < (b) ? (a) : (b))
#define MAX(a, b)	((a) > (b) ? (a) : (b))
#define BLKIP(xp)	((Etherhdr *)((xp)->rptr))
#define BLKFRAG(xp)	((Ipfrag *)((xp)->rptr))
#define PREC(x)		((x)>>5 & 7)

#define WORKBUF		64

extern Ipaddr Myip;
extern Ipaddr Mymask;
extern Ipaddr classmask[4];
extern Ipconv *ipconv[];
extern char *tcpstate[];
extern Rendez tcpflowr;
extern Qinfo tcpinfo;
extern Qinfo ipinfo;
extern Qinfo udpinfo;

M power/main.c => power/main.c +4 -0
@@ 634,6 634,10 @@ confinit(void)
	conf.nurp = 25;
	conf.nqueue = 5 * conf.nstream;
	conf.nblock = 10 * conf.nstream;
	conf.ipif = 8;
	conf.ip = 64;
	conf.arp = 32;
	conf.frag = 32;

	confread();