~kris/9p

9hist

bd91522ad0a9b938efd0d416755ed5de85d3fd8b — David du Colombier 33 years ago 7e7f85d
Plan 9 from Bell Labs 1993-08-04
20 files changed, 16 insertions(+), 9218 deletions(-)

D port/arp.h
D port/bootp.h
D port/devarp.c
D port/devdk.c
D port/devip.c
D port/deviproute.c
D port/ipdat.h
M port/proc.c
M port/qio.c
D port/stasync.c
D port/stfcall.c
D port/stil.c
D port/stip.c
D port/streboot.c
D port/stsplice.c
D port/sturp.c
D port/tcpif.c
D port/tcpinput.c
D port/tcpoutput.c
D port/tcptimer.c
D port/arp.h => port/arp.h +0 -45
@@ 1,45 0,0 @@
/*
 *  this file used by (at least) the kernel, arpd, snoopy, tboot
 */
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

D port/bootp.h => port/bootp.h +0 -29
@@ 1,29 0,0 @@
/*
 *  this file used by (at least) snoopy, tboot and bootp
 */
enum
{
	Bootrequest = 1,
	Bootreply   = 2,
};

typedef struct Bootp Bootp;
struct Bootp
{
	uchar	op;		/* opcode */
	uchar	htype;		/* hardware type */
	uchar	hlen;		/* hardware address len */
	uchar	hops;		/* hops */
	uchar	xid[4];		/* a random number */
	uchar	secs[2];	/* elapsed snce client started booting */
	uchar	pad[2];
	uchar	ciaddr[4];	/* client IP address (client tells server) */
	uchar	yiaddr[4];	/* client IP address (server tells client) */
	uchar	siaddr[4];	/* server IP address */
	uchar	giaddr[4];	/* gateway IP address */
	uchar	chaddr[16];	/* client hardware address */
	char	sname[64];	/* server host name (optional) */
	char	file[128];	/* boot file name */
	char	vend[128];	/* vendor-specific goo */
};


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

#include	"devtab.h"

#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)]

typedef struct Arpcache Arpcache;
struct Arpcache
{
	uchar	status;
	uchar	type;
	uchar	eip[4];
	uchar	et[6];
	Arpcache *hash;
	Arpcache **hashhd;
	Arpcache *frwd;
	Arpcache *prev;
};

Arpstats	arpstats;
Arpcache 	*arplruhead, *arplrutail;
Arpcache 	*arp, **arphash;
Queue		*Servq;
Lock		larphash;

void	arpiput(Queue *, Block *);
void	arpoput(Queue *, Block *);
void	arpopn(Queue *, Stream *);
void	arpcls(Queue *);
void	arpenter(Arpentry*, int);
void	arpflush(void);
int	arpdelete(char*);
void	arplinkhead(Arpcache*);
int	arplookup(uchar*, uchar*);

Qinfo arpinfo = { arpiput, arpoput, arpopn, arpcls, "arp" };

#define ARP_ENTRYLEN	50
char *padstr = "                                           ";

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

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

/*
 *  create a 2-level directory
 */
int
arpgen(Chan *c, void *vp, int ntab, int i, Dir *dp)
{
	Qid q;

	USED(vp);
	USED(ntab);

	q.vers = 0;

	/* top level directory contains the directory arp */
	if(c->qid.path == CHDIR){
		if(i)
			return -1;
		q.path = CHDIR | arpdir2qid;
		devdir(c, q, "arp", 0, eve, 0555, dp);
		return 1;
	}

	/* next level uses table */
	return devgen(c, arptab, Narptab, i, dp);
}

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

	arp = xalloc(sizeof(Arpcache) * conf.arp);
	arphash = (Arpcache **)xalloc(sizeof(Arpcache *) * Arphashsize);

	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;
	newqinfo(&arpinfo);
}

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, 0, 0, arpgen);
}

void
arpstat(Chan *c, char *db)
{
	devstat(c, db, 0, 0, arpgen);
}

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)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

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

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

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

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

	if(c->qid.path&CHDIR)
		return devdirread(c, a, n, arptab, Narptab, arpgen);

	switch((int)(c->qid.path&~CHDIR)){
	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 readstr(offset, a, n, buf);
	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;

	USED(offset);

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

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

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

			if(arpdelete(field[1]) < 0)
				error(Enetaddr);
		}
		break;

	case arpdataqid:
		if(n != sizeof(Arpentry))
			error(Emsgsize);
		memmove(&entry, a, sizeof(Arpentry));
		arpenter(&entry, ARP_TEMP);
		break;

	default:
		error(Ebadusefd);
	}

	return n;
}

void
arpopn(Queue *q, Stream *s)
{
	USED(q, s);
}

void
arpcls(Queue *q)
{
	if(q == Servq)
		Servq = 0;
}

void
arpiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

void
arpoput(Queue *q, Block *bp)
{
	uchar ip[4];
	Etherhdr *eh;
	Ipaddr addr;
	static int dropped;

	if(bp->type != M_DATA) {
		if(Servq == 0 && streamparse("arpd", bp)) {
			Servq = RD(q);
			freeb(bp);
		}
		else
			PUTNEXT(q, bp);
		return;
	}

	eh = (Etherhdr *)bp->rptr;
	if(nhgets(eh->type) != ET_IP) {
		PUTNEXT(q, bp);	
		return;
	}

	/* if ip broadcast, use ether bcast address */
	addr = nhgetl(eh->dst);
	if(Myip[Myself] == 0 || addr == Myip[Mybcast] || addr == Myip[Mynet]
	|| ((addr & Mymask) == Myip[Mynet+1] && (addr & ~Mynetmask) == ~Mynetmask)){
		memset(eh->d, 0xff, sizeof(eh->d));
		PUTNEXT(q, bp);
		return;
	}

	iproute(eh->dst, ip);

	/* if a known ip addr, send downstream to the ethernet */
	if(arplookup(ip, eh->d)) {
		PUTNEXT(q, bp);
		return;
	}

	/* Push the packet up to the arp server for address resolution */
	if(!Servq) {
		if((dropped++ % 1000) == 0)
			print("arp: No server, packet dropped %d.%d.%d.%d\n",
				eh->dst[0], eh->dst[1], eh->dst[2], eh->dst[3]);
		freeb(bp);
		return;
	}
	memmove(eh->d, ip, sizeof(ip));
	PUTNEXT(Servq, bp);
}

int
arplookup(uchar *ip, uchar *et)
{
	Arpcache *ap;

	lock(&larphash);
	for(ap = ARPHASH(ip); ap; ap = ap->hash) {
		if(ap->status == ARP_OK && memcmp(ap->eip, ip, sizeof(ap->eip)) == 0) {
			memmove(et, ap->et, sizeof(ap->et));
			arplinkhead(ap);
			unlock(&larphash);
			arpstats.hit++;
			return 1;
		}
	}
	arpstats.miss++;
	unlock(&larphash);
	return 0;
}

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

	ep = &arp[conf.arp];
	for(ap = arp; ap < ep; ap++)
		ap->status = ARP_FREE;
}

void
arpenter(Arpentry *ape, int type)
{
	Arpcache *ap, **l, *d;


	/* Update an entry if we have one already */
	l = &ARPHASH(ape->ipaddr);
	lock(&larphash);
	for(ap = *l; ap; ap = ap->hash) {
		if(ap->status == ARP_OK && memcmp(ap->eip, ape->ipaddr, sizeof(ap->eip)) == 0) {
			if(ap->type != ARP_PERM) {
				ap->type = type;
				memmove(ap->et, ape->etaddr, sizeof(ap->et));
				ap->status = ARP_OK;
			}
			unlock(&larphash);
			return;
		}
	}

	/* Find an entry to replace */
	for(ap = arplrutail; ap && ap->type == ARP_PERM; ap = ap->prev)
		;

	if(!ap) {
		print("arp: too many permanent entries\n");
		unlock(&larphash);
		return;
	}

	if(ap->hashhd) {
		for(d = *ap->hashhd; d; d = d->hash) {
			if(d == ap) {
				*(ap->hashhd) = ap->hash;
				break;
			}
			ap->hashhd = &d->hash;
		}
	}

	ap->type = type;
	ap->status = ARP_OK;
	memmove(ap->eip, ape->ipaddr, sizeof(ape->ipaddr));
	memmove(ap->et, ape->etaddr, sizeof(ape->etaddr));
	ap->hashhd = l;
	ap->hash = *l;
	*l = ap;
	arplinkhead(ap);
	unlock(&larphash);
}

int
arpdelete(char *addr)
{
	Arpcache *ap;
	uchar ip[4];
	Ipaddr i;
	int rv;

	rv = -1;
	i = ipparse(addr);
	hnputl(ip, i);	
	lock(&larphash);
	for(ap = arplruhead; ap; ap = ap->frwd) {
		if(memcmp(ap->eip, ip, sizeof(ap->eip)) == 0) {
			ap->status = ARP_FREE;
			rv = 0;
			break;
		}
	}
	unlock(&larphash);
	return rv;
}

void
arplinkhead(Arpcache *ap)
{
	if(ap != arplruhead) {
		if(ap->prev)
			ap->prev->frwd = ap->frwd;
		else
			arplruhead = ap->frwd;
	
		if(ap->frwd)
			ap->frwd->prev = ap->prev;
		else
			arplrutail = ap->prev;
		
		ap->frwd = arplruhead;
		ap->prev = 0;
		arplruhead = ap;
	}
}

D port/devdk.c => port/devdk.c +0 -1636
@@ 1,1636 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#define	DPRINT	if(0) print

#define	NOW	(MACHP(0)->ticks)

typedef struct Dkmsg	Dkmsg;
typedef struct Line	Line;
typedef struct Dk	Dk;

enum {
	Maxdk = 4,
};

/*
 *  types of possible dkcalls
 */
enum {
	Dial,
	Announce,
	Redial
};

/*
 *  format of messages to/from the datakit controller on the common
 *  signalling line
 */
struct Dkmsg {
	uchar	type;
	uchar	srv;
	uchar	param0l;
	uchar	param0h;
	uchar	param1l;
	uchar	param1h;
	uchar	param2l;
	uchar	param2h;
	uchar	param3l;
	uchar	param3h;
	uchar	param4l;
	uchar	param4h;
};

/*
 *  message codes (T_xxx == dialin.type, D_xxx == dialin.srv)
 */
#define	T_SRV	1		/* service request */
#define   D_SERV	1		/* (host to dkmux) announce a service */
#define   D_DIAL	2		/* (host to dkmux) connect to a service */
#define   D_XINIT	7		/* (dkmux to host) line has been spliced */
#define	T_REPLY	2		/* reply to T_SRV/D_SERV or T_SRV/D_DIAL */
#define	  D_OK		1		/* not used */
#define	  D_OPEN	2		/* (dkmux to host) connection established */
#define	  D_FAIL	3		/* (dkmux to host) connection failed */
#define	T_CHG	3		/* change the status of a connection */
#define	  D_CLOSE	1		/* close the connection */
#define	  D_ISCLOSED	2		/* (dkmux to host) confirm a close */
#define	  D_CLOSEALL	3		/* (dkmux to host) close all connections */
#define	  D_REDIAL	6		/* (host to dkmux) redial a call */
#define	T_ALIVE	4		/* (host to dkmux) keep alive message */
#define	  D_CONTINUE	0		/* host has not died since last msg */
#define	  D_RESTART	1		/* host has restarted */
#define   D_MAXCHAN	2		/* request maximum line number */
#define	T_RESTART 8		/* (dkmux to host) datakit restarted */

/*
 *  macros for cracking/forming the window negotiation parameter
 */
#define MIN(x,y)  (x < y ? x : y)
#define W_WINDOW(o,d,t)  ((o<<8) | (d<<4) | t | 0100000)
#define W_VALID(x)  ((x) & 0100000)
#define W_ORIG(x)  (((x)>>8) & 017)
#define W_DEST(x)  (((x)>>4) & 017)
#define W_TRAF(x)  ((x) & 017)
#define W_DESTMAX(x,y)  (W_WINDOW(W_ORIG(x),MIN(W_DEST(x),y),W_TRAF(x)))
#define W_LIMIT(x,y)  (W_WINDOW(MIN(W_ORIG(x),y),MIN(W_DEST(x),y),W_TRAF(x)))
#define	W_VALUE(x)	(1<<((x)+4))

struct Line {
	QLock;
	Netprot;		/* stat info */
	int	lineno;
	Rendez	r;		/* wait here for dial */
	int	state;		/* dial state */
	int	err;		/* dialing error (if non zero) */
	int	window;		/* negotiated window */
	int	timestamp;	/* timestamp of last call received on this line */
	int	calltolive;	/* multiple of 15 seconds for dialing state to last */
	Queue	*rq;
	char	addr[64];
	char	raddr[64];
	char	ruser[32];
	Dk *dp;			/* interface contianing this line */
};

/*
 *  a dkmux dk.  one exists for every stream that a 
 *  dkmux line discipline is pushed onto.
 */
struct Dk
{
	QLock	netlock;
	Network	net;		/* stat info */
	Line	**linep;	/* array of line structures */

	QLock	csclock;
	Chan	*csc;

	char	name[64];	/* dk name */
	Queue	*wq;		/* dk output queue */
	int	ncsc;		/* csc line number */
	int	lines;		/* number of lines */
	int	restart;
	int	urpwindow;
	Rendez	timer;
	int	closeall;	/* set when we receive a closeall message */
	Rendez	closeallr;	/* wait here for a closeall */
};
Lock	dklock;
Dk	*dk[Maxdk];

/*
 *  conversation states (for Line.state)
 */
typedef enum {
	Lclosed=0,
	Lopened,		/* opened but no call out */
	Lconnected,		/* opened and a call set up on htis line */
	Lrclose,		/* remote end has closed down */
	Llclose,		/* local end has closed down */
	Ldialing,		/* dialing a new call */
	Llistening,		/* this line listening for calls */
	Lackwait,		/* incoming call waiting for ack/nak */
	Laccepting,		/* waiting for user to accept or reject the call */
} Lstate;
char *dkstate[] =
{
	[Lclosed]	"Closed",
	[Lopened]	"Opened",
	[Lconnected]	"Established",
	[Lrclose]	"Rclose",
	[Llclose]	"Lclose",
	[Ldialing]	"Dialing",
	[Llistening]	"Listen",
	[Lackwait]	"Ackwait",
	[Laccepting]	"Accepting",
};

/*
 *  map datakit error to errno 
 */
enum {
	DKok,
	DKbusy,
	DKnetotl,
	DKdestotl,
	DKbadnet,
	DKnetbusy,
	DKinuse,
	DKreject,
};
char* dkerr[]={
	[DKok]		"",
	[DKbusy]	"devdk: destination busy",
	[DKnetotl]	"devdk: network not answering",
	[DKdestotl]	"devdk: destination not answering",
	[DKbadnet]	"devdk: unknown address",
	[DKnetbusy]	"devdk: network busy",
	[DKinuse]	"devdk: service in use",
	[DKreject]	"devdk: connection refused", 
};
#define DKERRS sizeof(dkerr)/sizeof(char*)

/*
 *  imported
 */
extern Qinfo urpinfo;

/*
 *  predeclared
 */
Chan*		dkattach(char*);
static void	dkmuxconfig(Queue*, Block*);
static Chan*	dkopenline(Dk*, int);
static Chan*	dkopencsc(Dk*);
static int	dkmesg(Chan*, int, int, int, int);
static void	dkcsckproc(void*);
static void	dkanswer(Chan*, int, int);
static void	dkwindow(Chan*);
static void	dkcall(int, Chan*, char*, char*, char*);
static void	dktimer(void*);
static void	dkchgmesg(Chan*, Dk*, Dkmsg*, int);
static void	dkreplymesg(Dk*, Dkmsg*, int);
Chan*		dkopen(Chan*, int);
static void	dkhangup(Line*);

/*
 *  for standard network interface (net.c)
 */
static int	dkcloneline(Chan*);
static int	dklisten(Chan*);
static void	dkfilladdr(Chan*, char*, int);
static void	dkfillraddr(Chan*, char*, int);
static void	dkfillruser(Chan*, char*, int);
static void	dkfillstatus(Chan*, char*, int);

extern Qinfo dkinfo;

/*
 *  the datakit multiplexor stream module definition
 */
static Streamopen dkmuxopen;
static Streamput dkmuxoput;
static Streamput dkmuxiput;
Qinfo dkmuxinfo =
{
	dkmuxiput,
	dkmuxoput,
	dkmuxopen,
	0,
	"dkmux"
};

/*
 *  allocate a line if it doesn't exist
 */
static Line*
linealloc(Dk *dp, int lineno, int dolock)
{
	Line *lp;

	if(dolock)
		qlock(&dp->netlock);
	if(lineno > dp->lines)
		panic("linealloc");
	lp = dp->linep[lineno];
	if(lp == 0){
		lp = smalloc(sizeof(Line));
		lp->lineno = lineno;
		netadd(&dp->net, lp, lineno);
		dp->linep[lineno] = lp;
	}
	if(dolock)
		qunlock(&dp->netlock);
	return lp;
}

/*
 *  a new dkmux.  hold the stream in place so it can never be closed down.
 */
static void
dkmuxopen(Queue *q, Stream *s)
{
	RD(q)->ptr = 0;
	WR(q)->ptr = 0;

	naildownstream(s);
}

/*
 *  handle configuration
 */
static void
dkmuxoput(Queue *q, Block *bp)
{
	if(bp->type != M_DATA){
		if(streamparse("config", bp))
			dkmuxconfig(q, bp);
		else
			PUTNEXT(q, bp);
		return;
	}
	PUTNEXT(q, bp);
}

/*
 *  gather a message and send it up the appropriate stream
 *
 *  The first two bytes of each message contains the channel
 *  number, low order byte first.
 *
 *  Simplifying assumption:  one put == one message && the channel number
 *	is in the first block.  If this isn't true, demultiplexing will not
 *	work.
 */
static void
dkmuxiput(Queue *q, Block *bp)
{
	Dk *dp;
	Line *lp;
	int line;

	/*
	 *  not configured yet
	 */
	if(q->other->ptr == 0){
		freeb(bp);
		return;
	}

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

	line = bp->rptr[0] | (bp->rptr[1]<<8);
	bp->rptr += 2;
	if(line<0 || line>=dp->lines){
		DPRINT("dkmuxiput bad line %d\n", line);
		freeb(bp);
		return;
	}

	lp = linealloc(dp, line, 1);
	if(lp && canqlock(lp)){
		if(lp->rq)
			PUTNEXT(lp->rq, bp);
		else{
			DPRINT("dkmuxiput unopened line %d\n", line);
			freeb(bp);
		}
		qunlock(lp);
	} else {
		DPRINT("dkmuxiput unopened line %d\n", line);
		freeb(bp);
	}
}

/*
 *  the datakit line stream module definition
 */
static Streamopen dkstopen;
static Streamclose dkstclose;
static Streamput dkoput, dkiput;
Qinfo dkinfo =
{
	dkiput,
	dkoput,
	dkstopen,
	dkstclose,
	"dk"
};

/*
 *  open and save a pointer to the conversation
 */
static void
dkstopen(Queue *q, Stream *s)
{
	Dk *dp;
	Line *lp;

	dp = dk[s->dev];
	q->other->ptr = q->ptr = lp = dp->linep[s->id];
	lp->dp = dp;
	lp->rq = q;
	if(lp->state==Lclosed)
		lp->state = Lopened;
}

/*
 *  close down a datakit conversation
 */
static void
dkstclose(Queue *q)
{
	Dk *dp;
	Line *lp;
	Chan *c;

	lp = (Line *)q->ptr;
	dp = lp->dp;

	/*
	 *  if we never got going, we're done
	 */
	if(lp->rq == 0){
		lp->state = Lclosed; 
		return;
	}

	/*
	 *  these states don't need the datakit
	 */
	switch(lp->state){
	case Lclosed:
	case Llclose:
	case Lopened:
		lp->state = Lclosed;
		goto out;
	}

	c = 0;
	if(waserror()){
		lp->state = Lclosed;
		if(c)
			close(c);
		goto out;
	}	
	c = dkopencsc(dp);

	/*
	 *  shake hands with dk
	 */
	switch(lp->state){
	case Lrclose:
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0);
		lp->state = Lclosed;
		break;

	case Lackwait:
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0);
		lp->state = Llclose;
		break;

	case Llistening:
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0);
		lp->state = Llclose;
		break;

	case Lconnected:
		dkmesg(c, T_CHG, D_CLOSE, lp->lineno, 0);
		lp->state = Llclose;
		break;
	}
	poperror();
	close(c);

out:
	qlock(lp);
	lp->rq = 0;
	qunlock(lp);

	netdisown(lp);
	lp->window = 0;
}

/*
 *  this is only called by hangup
 */
static void
dkiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

/*
 *  we assume that each put is a message.
 *
 *  add a 2 byte channel number to the start of each message,
 *  low order byte first.  Make sure the first block contains
 *  both the 2 channel bytes and the control byte.
 */
static void
dkoput(Queue *q, Block *bp)
{
	Line *lp;
	Dk *dp;
	int line;

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

	lp = (Line *)q->ptr;
	dp = lp->dp;
	line = lp->lineno;

	bp = padb(bp, 2);
	bp->rptr[0] = line;
	bp->rptr[1] = line>>8;

	FLOWCTL(dp->wq, bp);
}

/*
 *  configure a datakit multiplexor.  this takes 5 arguments separated
 *  by spaces:
 *	the line number of the common signalling channel (must be > 0)
 *	the number of lines in the device (optional)
 *	the word `restart' or `norestart' (optional/default==restart)
 *	the name of the dk (default==dk)
 *	the urp window size (default==2048)
 *
 *  we can configure only once
 */
static int
haveca(void *arg)
{
	Dk *dp;

	dp = arg;
	return dp->closeall;
}
static void
dkmuxconfig(Queue *q, Block *bp)
{
	Dk *dp;
	char *fields[5];
	int n;
	char buf[64];
	char name[NAMELEN];
	int lines;
	int ncsc;
	int restart;
	int window;

	if(WR(q)->ptr){
		freeb(bp);
		error(Egreg);
	}

	/*
	 *  defaults
	 */
	ncsc = 1;
	restart = 1;
	lines = 16;
	window = 2048;
	strcpy(name, "dk");

	/*
	 *  parse
	 */
	n = getfields((char *)bp->rptr, fields, 5, ' ');
	switch(n){
	case 5:
		window = strtoul(fields[4], 0, 0);
		if(window < 16)
			window = 1<<(window+4);
	case 4:
		strncpy(name, fields[3], sizeof(name));
		name[sizeof(name)-1] = 0;
	case 3:
		if(strcmp(fields[2], "restart")!=0)
			restart = 0;
	case 2:
		lines = strtoul(fields[1], 0, 0);
	case 1:
		ncsc = strtoul(fields[0], 0, 0);
		break;
	default:
		freeb(bp);
		error(Ebadarg);
	}
	freeb(bp);
	if(ncsc <= 0 || lines <= ncsc)
		error(Ebadarg);

	/*
	 *  find a free dk slot.  it name is already configured
	 *  or no slots are left, error.
	 */
	lock(&dklock);
	if(waserror()){
		unlock(&dklock);
		nexterror();
	}
	for(n = 0; n < Maxdk; n++){
		dp = dk[n];
		if(dp == 0)
			break;
		if(strcmp(name, dp->name) == 0)
			error(Einuse);
	}
	if(n == Maxdk)
		error(Enoifc);

	/*
	 *  allocate both a dk structure and an array of pointers to line
	 *  structures
	 */
	dp = smalloc(sizeof(Dk));
	dp->ncsc = ncsc;
	dp->lines = lines;
	dp->linep = smalloc(sizeof(Line*) * dp->lines);
	strcpy(dp->name, name);
	dp->net.name = dp->name;
	dp->net.nconv = dp->lines;
	dp->net.devp = &dkinfo;
	dp->net.protop = &urpinfo;
	dp->net.listen = dklisten;
	dp->net.clone = dkcloneline;
	dp->net.ninfo = 5;
	dp->net.info[0].name = "local";
	dp->net.info[0].fill = dkfilladdr;
	dp->net.info[1].name = "remote";
	dp->net.info[1].fill = dkfillraddr;
	dp->net.info[2].name = "ruser";
	dp->net.info[2].fill = dkfillruser;
	dp->net.info[3].name = "urpstats";
	dp->net.info[3].fill = urpfillstats;
	dp->net.info[4].name = "status";
	dp->net.info[4].fill = dkfillstatus;
	dp->restart = restart;
	dp->urpwindow = window;
	dp->wq = WR(q);
	q->ptr = q->other->ptr = dp;
	dk[n] = dp;
	unlock(&dklock);
	poperror();

	/*
	 *  open csc here so that boot, dktimer, and dkcsckproc aren't
	 *  all fighting for it at once.
	 */
	dkopencsc(dp);

	/*
	 *  start a process to listen to csc messages
	 */
	sprint(buf, "csc.%s.%d", dp->name, dp->ncsc);
	kproc(buf, dkcsckproc, dp);

	/*
	 *  tell datakit we've rebooted. It should close all channels.
	 *  do this here to get it done before trying to open a channel.
	 */
	if(dp->restart) {
		DPRINT("dktimer: restart %s\n", dp->name);
		dp->closeall = 0;
		dkmesg(dp->csc, T_ALIVE, D_RESTART, 0, 0);
	}
	tsleep(&dp->closeallr, haveca, dp, 15000);

	/*
	 *  start a keepalive process
	 */
	sprint(buf, "timer.%s.%d", dp->name, dp->ncsc);
	kproc(buf, dktimer, dp);
}

void
dkreset(void)
{
	newqinfo(&dkmuxinfo);
}

void
dkinit(void)
{
}

Chan*
dkattach(char *spec)
{
	Chan *c;
	Dk *dp;
	int dev;

	/*
	 *  find a multiplexor with the same name (default dk)
	 */
	if(*spec == 0)
		spec = "dk";
	for(dev = 0; dev < Maxdk; dev++){
		dp = dk[dev];
		if(dp && strcmp(dp->name, spec) == 0)
			break;
	}
	if(dev == Maxdk)
		error(Enoifc);

	/*
	 *  return the new channel
	 */
	c = devattach('k', spec);
	c->dev = dev;
	return c;
}

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

int	 
dkwalk(Chan *c, char *name)
{
	return netwalk(c, name, &dk[c->dev]->net);
}

void	 
dkstat(Chan *c, char *dp)
{
	netstat(c, dp, &dk[c->dev]->net);
}

Chan*
dkopen(Chan *c, int omode)
{
	Dk *dp;

	dp = dk[c->dev];
	linealloc(dp, STREAMID(c->qid.path), 1);
	return netopen(c, omode, &dp->net);
}

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

void	 
dkclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
}

long	 
dkread(Chan *c, void *a, long n, ulong offset)
{
	return netread(c, a, n, offset, &dk[c->dev]->net);
}

long	 
dkwrite(Chan *c, void *a, long n, ulong offset)
{
	int t;
	char buf[256];
	char *field[5];
	int m;

	USED(offset);
	t = STREAMTYPE(c->qid.path);

	/*
	 *  get data dispatched as quickly as possible
	 */
	if(t == Sdataqid)
		return streamwrite(c, a, n, 0);

	/*
	 *  easier to do here than in dkoput
	 */
	if(t == Sctlqid){
		if(n > sizeof buf - 1)
			n = sizeof buf - 1;
		strncpy(buf, a, n);
		buf[n] = '\0';
		m = getfields(buf, field, 5, ' ');
		if(strcmp(field[0], "connect")==0){
			if(m < 2)
				error(Ebadarg);
			dkcall(Dial, c, field[1], 0, 0);
		} else if(strcmp(field[0], "announce")==0){
			if(m < 2)
				error(Ebadarg);
			dkcall(Announce, c, field[1], 0, 0);
		} else if(strcmp(field[0], "redial")==0){
			if(m < 4)
				error(Ebadarg);
			dkcall(Redial, c, field[1], field[2], field[3]);
		} else if(strcmp(field[0], "accept")==0){
			if(m < 2)
				error(Ebadarg);
			dkanswer(c, strtoul(field[1], 0, 0), 0);
		} else if(strcmp(field[0], "reject")==0){
			if(m < 3)
				error(Ebadarg);
			for(m = 0; m < DKERRS-1; m++)
				if(strcmp(field[2], dkerr[m]) == 0)
					break;
			dkanswer(c, strtoul(field[1], 0, 0), m);
		} else
			return streamwrite(c, a, n, 0);
		return n;
	}

	error(Eperm);
	return -1;		/* never reached */
}

void	 
dkremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void	 
dkwstat(Chan *c, char *dp)
{
	netwstat(c, dp, &dk[c->dev]->net);
}

/*
 *  return the number of an unused line (reserve it)
 */
static int
dkcloneline(Chan *c)
{
	Line *lp;
	Dk *dp;
	int line;

	dp = dk[c->dev];
	/*
	 *  get an unused device and open its control file
	 */
	qlock(&dp->netlock);
	for(line = dp->ncsc+1; line < dp->lines; line++){
		lp = dp->linep[line];
		if(lp == 0 || lp->state == Lclosed){
			lp = linealloc(dp, line, 0);
			lp->state = Lopened;

			/* current user becomes owner */
			netown(lp, up->user, 0);

			qunlock(&dp->netlock);
			return lp->lineno;
		}
	}
	qunlock(&dp->netlock);
	error(Enodev);
	return -1;		/* never reached */
}

static Chan*
dkopenline(Dk *dp, int line)
{
	Chan *c;

	c = 0;
	if(waserror()){
		if(c)
			close(c);
		nexterror();
	}
	c = dkattach(dp->name);
	c->qid.path = STREAMQID(line, Sdataqid);
	dkopen(c, ORDWR);
	poperror();

	return c;
}

/*
 *  open the common signalling channel (dp->csc's reference count never goes below 1)
 */
static Chan*
dkopencsc(Dk *dp)
{
	qlock(&dp->csclock);
	if(dp->csc == 0)
		dp->csc = dkopenline(dp, dp->ncsc);
	incref(dp->csc);
	qunlock(&dp->csclock);
	return dp->csc;
}

/*
 *  return the contents of the info files
 */
void
dkfilladdr(Chan *c, char *buf, int len)
{
	if(len < sizeof(dk[0]->linep[0]->addr)+2)
		error(Ebadarg);
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->addr);
}
void
dkfillraddr(Chan *c, char *buf, int len)
{
	if(len < sizeof(dk[0]->linep[0]->raddr)+2)
		error(Ebadarg);
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->raddr);
}
void
dkfillruser(Chan *c, char *buf, int len)
{
	if(len < sizeof(dk[0]->linep[0]->ruser)+2)
		error(Ebadarg);
	sprint(buf, "%s\n", dk[c->dev]->linep[STREAMID(c->qid.path)]->ruser);
}
void
dkfillstatus(Chan *c, char *buf, int len)
{
	Dk *dp;
	Line *lp;
	int line;
	char lbuf[65];

	line = STREAMID(c->qid.path);
	dp = dk[c->dev];
	lp = linealloc(dp, line, 1);
	sprint(lbuf, "%s/%d %d %s window %d\n", dp->name, line,
		lp->state != Lclosed ? 1 : 0, dkstate[lp->state], lp->window);
	strncpy(buf, lbuf, len);
}

/*
 *  send a message to the datakit on the common signaling line
 */
static int
dkmesg(Chan *c, int type, int srv, int p0, int p1)
{
	Dkmsg d;

	if(waserror()){
		print("dkmesg: error\n");
		return -1;
	}
	d.type = type;
	d.srv = srv;
	d.param0l = p0;
	d.param0h = p0>>8;
	d.param1l = p1;
	d.param1h = p1>>8;
	d.param2l = 0;
	d.param2h = 0;
	d.param3l = 0;
	d.param3h = 0;
	d.param4l = 0;
	d.param4h = 0;
	streamwrite(c, (char *)&d, sizeof(Dkmsg), 1);
	poperror();
	return 0;
}

/*
 *  call out on a datakit
 */
static int
calldone(void *a)
{
	Line *lp;

	lp = (Line *)a;
	return lp->state != Ldialing;
}
static void
dkcall(int type, Chan *c, char *addr, char *nuser, char *machine)
{
 	char dialstr[66];
	int line, win;
	char dialtone;
	int t_val, d_val;
	Dk *dp;
	Line *lp;
	Chan *dc;
	Chan *csc;
	char *bang, *dot;
	
	line = STREAMID(c->qid.path);
	dp = dk[c->dev];
	lp = linealloc(dp, line, 1);

	/*
	 *  only dial on virgin lines
	 */
	if(lp->state != Lopened)
		error(Ebadarg);

	DPRINT("dkcall(line=%d, type=%d, dest=%s)\n", line, type, addr);

	/*
	 *  build dial string
	 *	- guard against new lines
	 *	- change ! into . to delimit service
	 */
	if(strchr(addr, '\n'))
		error(Ebadarg);
	if(strlen(addr)+strlen(up->user)+2 >= sizeof(dialstr))
		error(Ebadarg);
	strcpy(dialstr, addr);
	bang = strchr(dialstr, '!');
	if(bang){
		dot = strchr(dialstr, '.');
		if(dot==0 || dot > bang)
			*bang = '.';
	}
	switch(type){
	case Dial:
		t_val = T_SRV;
		d_val = D_DIAL;
		strcat(dialstr, "\n");
		strcat(dialstr, up->user);
		strcat(dialstr, "\n");
		break;
	case Announce:
		t_val = T_SRV;
		d_val = D_SERV;
		break;
	case Redial:
		t_val = T_CHG;
		d_val = D_REDIAL;
		strcat(dialstr, "\n");
		strcat(dialstr, nuser);
		strcat(dialstr, "\n");
		strcat(dialstr, machine);
		strcat(dialstr, "\n");
		break;
	default:
		t_val = 0;
		d_val = 0;
		panic("bad dial type");
	}

	/*
	 *  open the data file
	 */
	dc = dkopenline(dp, line);
	if(waserror()){
		close(dc);
		nexterror();
	}
	lp->calltolive = 4;
	lp->state = Ldialing;

	/*
	 *  tell the controller we want to make a call
	 */
	DPRINT("dialout\n");
	csc = dkopencsc(dp);
	if(waserror()){
		close(csc);
		nexterror();
	}
	for(win = 0; ; win++)
		if(W_VALUE(win) >= dp->urpwindow || win == 15)
			break;
	dkmesg(csc, t_val, d_val, line, W_WINDOW(win, win, 2));
	poperror();
	close(csc);

	/*
	 *  if redial, wait for a dial tone (otherwise we might send
	 *  the dialstr to the previous other end and not the controller)
	 */
	if(type==Redial){
		if(streamread(dc, &dialtone, 1L) != 1L){
			lp->state = Lconnected;
			error(Ebadarg);
		}
	}

	/*
	 *  make the call
	 */
	DPRINT("dialstr %s\n", dialstr);
	streamwrite(dc, dialstr, (long)strlen(dialstr), 1);
	close(dc);
	poperror();

	/*
	 *  redial's never get a reply, assume it worked
	 */
	if(type == Redial) {
		lp->state = Lconnected;
		return;
	}

	/*
	 *  wait for a reply
	 */
	DPRINT("reply wait\n");
	sleep(&lp->r, calldone, lp);

	/*
	 *  if there was an error, translate it to a plan 9
	 *  errno and report it to the user.
	 */
	DPRINT("got reply %d\n", lp->state);
	if(lp->state != Lconnected) {
		if(lp->err >= DKERRS)
			error(dkerr[0]);
		else
			error(dkerr[lp->err]);
	}

	/*
	 *  change state if serving
	 */
	if(type == D_SERV){
		lp->state = Llistening;
	}
	DPRINT("connected!\n");

	/*
	 *  decode the window size
	 */
	if (W_VALID(lp->window)){
		/*
		 *  a 1127 window negotiation
		 */
		lp->window = W_VALUE(W_DEST(lp->window));
	} else if(lp->window>2 && lp->window<31){
		/*
		 *  a generic window negotiation
		 */
		lp->window = 1<<lp->window;
	} else
		lp->window = 0;

	/*
	 *  tag the connection
	 */
	strncpy(lp->addr, addr, sizeof(lp->addr)-1);
	strncpy(lp->raddr, addr, sizeof(lp->raddr)-1);

	/*
	 *  reset the protocol
	 */
	dkwindow(c);
}

/*
 *  listen for a call, reflavor the 
 */
static int
dklisten(Chan *c)
{
	char dialstr[512];
	char *line[12];
	char *field[8];
	Line *lp;
	Dk *dp;
	int n, lineno, ts, window;
	int from;
	Chan *dc;
	static int dts;
	char *cp;

	dp = dk[c->dev];
	from = STREAMID(c->qid.path);

	/*
	 *  open the data file
	 */
	dc = dkopenline(dp, STREAMID(c->qid.path));
	if(waserror()){
		close(dc);
		nexterror();
	}

	/*
	 *  wait for a call in
	 */
	for(;;){
		/*
		 *  read the dialstring and null terminate it
		 */
		n = streamread(dc, dialstr, sizeof(dialstr)-1);
		DPRINT("returns %d\n", n);
		if(n <= 0)
			error(Eio);
		dialstr[n] = 0;
		DPRINT("dialstr = %s\n", dialstr);

		/*
		 *  break the dial string into lines
		 */
		n = getfields(dialstr, line, 12, '\n');
		if (n < 2) {
			DPRINT("bad dialstr from dk (1 line)\n");
			error(Eio);
		}

		/*
		 * line 0 is `line.tstamp.traffic[.urpparms.window]'
		 */
		window = 0;
		switch(getfields(line[0], field, 5, '.')){
		case 5:
			/*
			 *  generic way of passing window
			 */
			window = strtoul(field[4], 0, 0);
			if(window > 0 && window <31)
				window = 1<<window;
			else
				window = 0;
			/*
			 *  intentional fall through
			 */
		case 3:
			/*
			 *  1127 way of passing window
			 */
			if(window == 0){
				window = strtoul(field[2], 0, 0);
				if(W_VALID(window))
					window = W_VALUE(W_ORIG(window));
				else
					window = 0;
			}
			break;
		default:
			print("bad message from dk(bad first line)\n");
			continue;
		}
		lineno = strtoul(field[0], 0, 0);
		if(lineno >= dp->lines){
			print("dklisten: illegal line %d\n", lineno);
			continue;
		}
		lp = linealloc(dp, lineno, 1);
		ts = strtoul(field[1], 0, 0);

		/*
		 *  this could be a duplicate request
		 */
		if(ts == lp->timestamp){
			if((dts++ % 1000) == 0)
				print("dklisten: repeat timestamp %d\n", lineno);
			if(lp->state != Lconnected)
				dkanswer(c, lineno, DKbusy);
			continue;
		}
	
		/*
		 *  take care of glare (datakit picked an inuse channel
		 *  for the call to come in on).
		 */
		if(!canqlock(lp)){
			DPRINT("DKbusy1\n");
			dkanswer(c, lineno, DKbusy);
			continue;
		} else {
			if(lp->state != Lclosed){
				qunlock(lp);
				DPRINT("DKbusy2 %ux\n", lp->state);
				dkanswer(c, lineno, DKbusy);
				continue;
			}
		}
		lp->window = window;

		/*
		 *  Line 1 is `my-dk-name.service[.more-things]'.
		 *  Special characters are escaped by '\'s.  Convert to
		 *  a plan 9 address, i.e. system!service.
		 */
		strncpy(lp->addr, line[1], sizeof(lp->addr)-1);
		if(cp = strchr(lp->addr, '.')){
			*cp = '!';
			if(cp = strchr(cp, '.'))
				*cp = 0;
		}
	
		/*
		 *  the rest is variable length
		 */
		switch(n) {
		case 2:
			/* no more lines */
			lp->ruser[0] = 0;
			lp->raddr[0] = 0;
			break;
		case 3:
			/* line 2 is `source.user.param1.param2' */
			getfields(line[2], field, 3, '.');
			strncpy(lp->raddr, field[0], sizeof(lp->raddr)-1);
			strncpy(lp->ruser, field[1], sizeof(lp->ruser)-1);
			break;
		case 4:
			/* line 2 is `user.param1.param2' */
			getfields(line[2], field, 2, '.');
			strncpy(lp->ruser, field[0], sizeof(lp->ruser)-1);
	
			/* line 3 is `source.node.mod.line' */
			strncpy(lp->raddr, line[3], sizeof(lp->raddr)-1);
			break;
		default:
			print("bad message from dk(>4 line)\n");
			qunlock(lp);
			error(Ebadarg);
		}

		DPRINT("src(%s)user(%s)dest(%s)w(%d)\n", lp->raddr, lp->ruser,
			lp->addr, W_TRAF(lp->window));

		lp->timestamp = ts;
		lp->state = Lconnected;

		/* listener becomes owner */
		netown(lp, dp->linep[from]->owner, 0);

		qunlock(lp);
		close(dc);
		poperror();
		DPRINT("dklisten returns %d\n", lineno);
		return lineno;
	}
	panic("dklisten terminates strangely\n");
	return -1;		/* never reached */
}

/*
 *  answer a call
 */
static void
dkanswer(Chan *c, int line, int code)
{
	char reply[64];
	Chan *dc;
	Line *lp;
	Dk *dp;

	dp = dk[c->dev];
	lp = linealloc(dp, line, 1);

	/*
	 *  open the data file (c is a control file)
	 */
	dc = dkattach(dp->name);
	if(waserror()){
		close(dc);
		nexterror();
	}
	dc->qid.path = STREAMQID(STREAMID(c->qid.path), Sdataqid);
	dkopen(dc, ORDWR);

	/*
	 *  send the reply
	 */
	sprint(reply, "%ud.%ud.%ud", line, lp->timestamp, code);
	DPRINT("dkanswer %s\n", reply);
	streamwrite(dc, reply, strlen(reply), 1);
	close(dc);
	poperror();

	/*
 	 *  set window size
	 */
	if(code == 0){
		if(waserror()){
			close(dc);
			nexterror();
		}
		sprint(reply, "init %d %d", lp->window, Streamhi);
		dc = dkopenline(dp, line);
		dc->qid.path = STREAMQID(line, Sctlqid);
		streamwrite(dc, reply, strlen(reply), 1);
		close(dc);
		poperror();
	}
}

/*
 *  set the window size and reset the protocol
 */
static void
dkwindow(Chan *c)
{
	char buf[64];
	Line *lp;

	lp = linealloc(dk[c->dev], STREAMID(c->qid.path), 1);
	if(lp->window == 0)
		lp->window = 64;
	sprint(buf, "init %d %d", lp->window, Streamhi);
	streamwrite(c, buf, strlen(buf), 1);
}

/*
 *  hangup a datakit connection
 */
static void
dkhangup(Line *lp)
{
	Block *bp;

	qlock(lp);
	if(lp->rq){
		bp = allocb(0);
		bp->type = M_HANGUP;
		PUTNEXT(lp->rq, bp);
	}
	qunlock(lp);
}

/*
 *  A process which listens to all input on a csc line
 */
static void
dkcsckproc(void *a)
{
	long n;
	Dk *dp;
	Dkmsg d;
	int line;

	dp = a;

	if(waserror()){
		close(dp->csc);
		return;
	}
	DPRINT("dkcsckproc: %d\n", dp->ncsc);

	/*
	 *  loop forever listening
	 */
	for(;;){
		n = streamread(dp->csc, (char *)&d, (long)sizeof(d));
		if(n != sizeof(d)){
			if(n == 0)
				error(Ehungup);
			print("strange csc message %d\n", n);
			continue;
		}
		line = (d.param0h<<8) + d.param0l;
		DPRINT("t(%d)s(%d)l(%d)\n", d.type, d.srv, line);
		switch (d.type) {

		case T_CHG:	/* controller wants to close a line */
			dkchgmesg(dp->csc, dp, &d, line);
			break;
		
		case T_REPLY:	/* reply to a dial request */
			dkreplymesg(dp, &d, line);
			break;
		
		case T_SRV:	/* ignore it, it's useless */
/*			print("dksrvmesg(%d)\n", line);		/**/
			break;
		
		case T_RESTART:	/* datakit reboot */
			if(line >=0 && line<dp->lines)
				dp->lines = line+1;
			break;
		
		default:
			print("unrecognized csc message %o.%o(%o)\n",
				d.type, d.srv, line);
			break;
		}
	}
}

/*
 *  datakit requests or confirms closing a line
 */
static void
dkchgmesg(Chan *c, Dk *dp, Dkmsg *dialp, int line)
{
	Line *lp;

	switch (dialp->srv) {

	case D_CLOSE:		/* remote shutdown */
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0) {
			/* tell controller this line is not in use */
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
			return;
		}
		switch (lp->state) {

		case Ldialing:
			/* simulate a failed connection */
			dkreplymesg(dp, (Dkmsg *)0, line);
			lp->state = Lrclose;
			break;

		case Lrclose:
		case Lconnected:
		case Llistening:
		case Lackwait:
			dkhangup(lp);
			lp->state = Lrclose;
			break;

		case Lopened:
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
			break;

		case Llclose:
		case Lclosed:
			dkhangup(lp);
			dkmesg(c, T_CHG, D_CLOSE, line, 0);
			lp->state = Lclosed;
			break;
		}
		break;
	
	case D_ISCLOSED:	/* acknowledging a local shutdown */
		if (line <= 0 || line >= dp->lines || (lp = dp->linep[line]) == 0)
			return;
		switch (lp->state) {
		case Llclose:
		case Lclosed:
			lp->state = Lclosed;
			break;

		case Lrclose:
		case Lconnected:
		case Llistening:
		case Lackwait:
			break;
		}
		break;

	case D_CLOSEALL:
		/*
		 *  datakit wants us to close all lines
		 */
		for(line = dp->ncsc+1; line < dp->lines; line++){
			lp = dp->linep[line];
			if(lp == 0)
				continue;
			switch (lp->state) {
	
			case Ldialing:
				/* simulate a failed connection */
				dkreplymesg(dp, (Dkmsg *)0, line);
				lp->state = Lrclose;
				break;
	
			case Lrclose:
			case Lconnected:
			case Llistening:
			case Lackwait:
				lp->state = Lrclose;
				dkhangup(lp);
				break;
	
			case Lopened:
				break;
	
			case Llclose:
			case Lclosed:
				lp->state = Lclosed;
				break;
			}
		}
		dp->closeall = 1;
		wakeup(&dp->closeallr);
		break;

	default:
		print("unrecognized T_CHG\n");
	}
}

/*
 *  datakit replies to a dialout.  capture reply code and traffic parameters
 */
static void
dkreplymesg(Dk *dp, Dkmsg *dialp, int line)
{
	Line *lp;

	DPRINT("dkreplymesg(%d)\n", line);

	if(line < 0 || line >= dp->lines || (lp = dp->linep[line]) == 0)
		return;

	if(lp->state != Ldialing)
		return;

	if(dialp){
		/*
		 *  a reply from the dk
		 */
		lp->state = (dialp->srv==D_OPEN) ? Lconnected : Lrclose;
		lp->err = (dialp->param1h<<8) + dialp->param1l;
		lp->window = lp->err;
		DPRINT("dkreplymesg: %d\n", lp->state);
	} else {
		/*
		 *  a local abort
		 */
		lp->state = Lrclose;
		lp->err = 0;
	}

	if(lp->state==Lrclose){
		dkhangup(lp);
	}
	wakeup(&lp->r);
}

/*
 *  send a I'm alive message every 7.5 seconds and remind the dk of
 *  any closed channels it hasn't acknowledged.
 */
static void
dktimer(void *a)
{
	int i;
	Dk *dp;
	Line *lp;
	Chan *c;

	dp = (Dk *)a;
	c = dkopencsc(dp);

	while(waserror());

	for(;;){
		/*
		 * send keep alive
		 */
		DPRINT("keep alive\n");
		dkmesg(c, T_ALIVE, D_CONTINUE, 0, 0);

		/*
		 *  remind controller of dead lines and
		 *  timeout calls that take to long
		 */
		for (i=dp->ncsc+1; i<dp->lines; i++){
			lp = dp->linep[i];
			if(lp == 0)
				continue;
			switch(lp->state){
			case Llclose:
				dkmesg(c, T_CHG, D_CLOSE, i, 0);
				break;

			case Ldialing:
				if(lp->calltolive==0 || --lp->calltolive!=0)
					break;
				dkreplymesg(dp, (Dkmsg *)0, i);
				break;
			}
		}
		tsleep(&dp->timer, return0, 0, 7500);
	}
}

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

#include	"devtab.h"

enum
{
	Nrprotocol	= 3,	/* Number of protocols supported by this driver */
	Nipsubdir	= 4,	/* Number of subdirectory entries per connection */
	Nfrag		= 32,	/* Ip reassembly queue entries */
	Nifc		= 4,	/* max interfaces */
};

int 	udpsum = 1;
Queue	*Ipoutput;			/* Control message stream for tcp/il */
Ipifc	*ipifc[Nrprotocol+1];
QLock	ipalloc;			/* Protocol port allocation lock */
Ipconv	**tcpbase;

Streamput	udpstiput, udpstoput, tcpstiput, tcpstoput;
Streamput	iliput, iloput, bsdiput, bsdoput;
Streamopen	udpstopen, tcpstopen, ilopen, bsdopen;
Streamclose	udpstclose, tcpstclose, ilclose, bsdclose;

Qinfo tcpinfo = { tcpstiput, tcpstoput, tcpstopen, tcpstclose, "tcp", 0, 1 };
Qinfo udpinfo = { udpstiput, udpstoput, udpstopen, udpstclose, "udp" };
Qinfo ilinfo  = { iliput,    iloput,    ilopen,    ilclose,    "il"  };
Qinfo bsdinfo = { bsdiput,   bsdoput,	bsdopen,   bsdclose,   "bsd", 0, 1 };

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

void
ipinitifc(Ipifc *ifc, Qinfo *stproto)
{
	ifc->conv = xalloc(Nipconv * sizeof(Ipconv*));
	ifc->protop = stproto;
	ifc->nconv = Nipconv;
	ifc->devp = &ipinfo;
	if(stproto != &udpinfo)
		ifc->listen = iplisten;
	ifc->clone = ipclonecon;
	ifc->ninfo = 3;
	ifc->info[0].name = "remote";
	ifc->info[0].fill = ipremotefill;
	ifc->info[1].name = "local";
	ifc->info[1].fill = iplocalfill;
	ifc->info[2].name = "status";
	ifc->info[2].fill = ipstatusfill;
	ifc->name = stproto->name;
}

void
ipreset(void)
{
	int i;

	for(i = 0; protocols[i]; i++) {
		ipifc[i] = xalloc(sizeof(Ipifc));
		ipinitifc(ipifc[i], protocols[i]);
		newqinfo(protocols[i]);
	}

	initfrag(Nfrag);
}

void
ipinit(void)
{
}

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

	/* fail if ip is not yet configured */
	if(Ipoutput == 0)
		error(Enoproto);

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

			return (c);
		}
	}

	error(Enoproto);
	return 0;		/* not reached */
}

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

int
ipwalk(Chan *c, char *name)
{
	return netwalk(c, name, ipifc[c->dev]);
}

void
ipstat(Chan *c, char *db)
{
	netstat(c, db, ipifc[c->dev]);
}

Chan *
ipopen(Chan *c, int omode)
{
	return netopen(c, omode, ipifc[c->dev]);
}

int
ipclonecon(Chan *c)
{
	Ipconv *new;

	new = ipincoming(ipifc[c->dev], 0);
	if(new == 0)
		error(Enodev);
	return new->id;
}

/*
 *  create a new conversation structure if none exists for this conversation slot
 */
Ipconv*
ipcreateconv(Ipifc *ifc, int id)
{
	Ipconv **p;
	Ipconv *new;

	p = &ifc->conv[id];
	if(*p)
		return *p;
	qlock(ifc);
	p = &ifc->conv[id];
	if(*p){
		qunlock(ifc);
		return *p;
	}
	if(waserror()){
		qunlock(ifc);
		nexterror();
	}
	new = smalloc(sizeof(Ipconv));
	new->ifc = ifc;
	netadd(ifc, new, p - ifc->conv);
	new->ref = 1;
	*p = new;
	qunlock(ifc);
	poperror();
	return new;
}

/*
 *  allocate a conversation structure.
 */
Ipconv*
ipincoming(Ipifc *ifc, Ipconv *from)
{
	Ipconv *new;
	Ipconv **p, **etab;

	/* look for an unused existing conversation */
	etab = &ifc->conv[Nipconv];
	for(p = ifc->conv; p < etab; p++) {
		new = *p;
		if(new == 0)
			break;
		if(new->ref == 0 && canqlock(new)) {
			if(new->ref || ipconbusy(new)) {
				qunlock(new);
				continue;
			}
			if(from)	/* copy ownership from listening channel */
				netown(new, from->owner, 0);
			else		/* current user becomes owner */
				netown(new, up->user, 0);

			new->ref = 1;
			qunlock(new);
			return new;
		}	
	}

	/* create one */
	qlock(ifc);
	etab = &ifc->conv[Nipconv];
	for(p = ifc->conv; ; p++){
		if(p == etab){
			qunlock(ifc);
			return 0;
		}
		if(*p == 0)
			break;
	}
	if(waserror()){
		qunlock(ifc);
		nexterror();
	}
	new = smalloc(sizeof(Ipconv));
	new->ifc = ifc;
	netadd(ifc, new, p - ifc->conv);
	qlock(new);
	*p = new;
	qunlock(ifc);
	poperror();
	if(from)	/* copy ownership from listening channel */
		netown(new, from->owner, 0);
	else		/* current user becomes owner */
		netown(new, up->user, 0);
	new->ref = 1;
	qunlock(new);
	return new;
}

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

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

void
ipwstat(Chan *c, char *dp)
{
	netwstat(c, dp, ipifc[c->dev]);
}

void
ipclose(Chan *c)
{
	if(c->stream)
		streamclose(c);
}

long
ipread(Chan *c, void *a, long n, ulong offset)
{
	return netread(c, a, n, offset, ipifc[c->dev]);
}

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

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

	if (type != Sctlqid)
		error(Eperm);

	cp = ipcreateconv(ipifc[c->dev], STREAMID(c->qid.path));

	m = n;
	if(m > sizeof(buf)-1)
		m = sizeof(buf)-1;
	strncpy(buf, a, m);
	buf[m] = '\0';

	m = getfields(buf, field, 5, ' ');
	if(m < 1)
		error(Ebadarg);

	if(strcmp(field[0], "connect") == 0) {
		if(ipconbusy(cp))
			error(Enetbusy);

		if(m != 2)
			error(Ebadarg);

		switch(getfields(field[1], ctlarg, 5, '!')) {
		default:
			error(Eneedservice);
		case 2:
			priv = 0;
			break;
		case 3:
			if(strcmp(ctlarg[2], "r") != 0)
				error(Eperm);
			priv = 1;
			break;
		}
		cp->dst = ipparse(ctlarg[0]);
		cp->pdst = atoi(ctlarg[1]);

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

		if(cp->ifc->protop == &tcpinfo)
			tcpstart(cp, TCP_ACTIVE, Streamhi, 0);
		else if(cp->ifc->protop == &ilinfo)
			ilstart(cp, IL_ACTIVE, 20);

		/*
		 *  stupid hack for BSD port's 512, 513, & 514
		 *  to make it harder for user to lie about his
		 *  identity. -- presotto
		 */
		switch(cp->pdst){
		case 512:
		case 513:
		case 514:
			pushq(c->stream, &bsdinfo);
			break;
		}
	}
	else if(strcmp(field[0], "disconnect") == 0) {
		if(cp->ifc->protop != &udpinfo)
			error(Eperm);

		cp->dst = 0;
		cp->pdst = 0;
	}
	else if(strcmp(field[0], "announce") == 0) {
		if(ipconbusy(cp))
			error(Enetbusy);

		if(m != 2)
			error(Ebadarg);

		port = atoi(field[1]);

		if(port){
			qlock(&ipalloc);
			if(portused(ipifc[c->dev], port)) {
				qunlock(&ipalloc);	
				error(Einuse);
			}
			cp->psrc = port;
			qunlock(&ipalloc);
		} else if(*field[1] != '*'){
			qlock(&ipalloc);
			cp->psrc = nextport(ipifc[c->dev], 0);
			qunlock(&ipalloc);
		} else
			cp->psrc = 0;

		if(cp->ifc->protop == &tcpinfo)
			tcpstart(cp, TCP_PASSIVE, Streamhi, 0);
		else if(cp->ifc->protop == &ilinfo)
			ilstart(cp, IL_PASSIVE, 10);

		if(cp->backlog == 0)
			cp->backlog = 3;
	}
	else if(strcmp(field[0], "backlog") == 0) {
		if(m != 2)
			error(Ebadarg);
		backlog = atoi(field[1]);
		if(backlog == 0)
			error(Ebadarg);
		if(backlog > 5)
			backlog = 5;
		cp->backlog = backlog;
	}
	else if(strcmp(field[0], "headers") == 0) {
		cp->headers = 1;	/* include addr/port in user packet */
	}
	else
		return streamwrite(c, a, n, 0);

	return n;
}

int
ipconbusy(Ipconv  *cp)
{
	if(cp->ifc->protop == &tcpinfo)
	if(cp->tcpctl.state != Closed)
		return 1;

	if(cp->ifc->protop == &ilinfo)
	if(cp->ilctl.state != Ilclosed)
		return 1;

	return 0;
}

void
udpstiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

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

	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);
	sport = nhgets(uh->udpsport);

	/* Look for a conversation structure for this port */
	etab = &ifc->conv[Nipconv];
	for(p = ifc->conv; p < etab; p++) {
		cp = *p;
		if(cp == 0)
			break;
		if(cp->ref)
		if(cp->psrc == dport)
		if(cp->pdst == 0 || cp->pdst == sport) {
			/* 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;

			if(cp->headers){
				/* pass the src address to the stream head */
				nbp = allocb(Udphdrsize);
				nbp->next = bp;
				bp = nbp;
				hnputl(bp->wptr, addr);
				bp->wptr += 4;
				hnputs(bp->wptr, sport);
				bp->wptr += 2;
			} else {
				/* save the src address in the conversation struct */
			 	cp->dst = addr;
				cp->pdst = sport;
			}
			PUTNEXT(cp->readq, bp);
			return;
		}
	}

	freeb(bp);
}

void
udpstoput(Queue *q, Block *bp)
{
	Ipconv *cp;
	Udphdr *uh;
	int dlen, ptcllen, newlen;
	Ipaddr addr;
	Port port;

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

	cp = (Ipconv *)(q->ptr);
	if(cp->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);
	}

	/*
	 *  if we're in header mode, rip off the first 64 bytes as the
	 *  destination.  The destination is in ascii in the form
	 *	%d.%d.%d.%d!%d
	 */
	if(cp->headers){
		/* get user specified addresses */
		bp = pullup(bp, Udphdrsize);
		if(bp == 0){
			freeb(bp);
			error(Emsgsize);
		}
		addr = nhgetl(bp->rptr);
		bp->rptr += 4;
		port = nhgets(bp->rptr);
		bp->rptr += 2;
	} else
		addr = port = 0;

	/* 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;
	uh->frag[0] = 0;
	uh->frag[1] = 0;
	hnputs(uh->udpplen, ptcllen);
	hnputl(uh->udpsrc, Myip[Myself]);
	hnputs(uh->udpsport, cp->psrc);
	if(cp->headers) {
		hnputl(uh->udpdst, addr);
		hnputs(uh->udpdport, port);
	}
	else {
		hnputl(uh->udpdst, cp->dst);
		hnputs(uh->udpdport, cp->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);

	ipc->headers = 0;
	ipc->psrc = 0;
	ipc->pdst = 0;
	ipc->dst = 0;
}

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

	ipc = ipcreateconv(ipifc[s->dev], s->id);
	initipifc(ipifc[s->dev], IP_UDPPROTO, udprcvmsg, 1500, 512, ETHER_HDR);

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

void
tcpstiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

tcproominq(void *a)
{
	return !((Tcpctl *)a)->sndfull;
}

void
tcpstoput(Queue *q, Block *bp)
{
	Ipconv *s;
	Tcpctl *tcb; 
	Block *f;

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

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

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

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

	switch(tcb->state) {
	case Listen:
		tcb->flags |= ACTIVE;
		tcpsndsyn(tcb);
		tcpsetstate(s, Syn_sent);

		/* No break */
	case Syn_sent:
	case Syn_received:
	case Established:
	case Close_wait:
		/*
		 * Process flow control
	 	 */
		if(tcb->sndfull){
			qlock(&tcb->sndrlock);
			if(waserror()) {
				qunlock(&tcb->sndrlock);
				nexterror();
			}
			sleep(&tcb->sndr, tcproominq, tcb);
			poperror();
			qunlock(&tcb->sndrlock);
		}

		/*
		 * Push data
		 */
		qlock(tcb);
		if(waserror()) {
			qunlock(tcb);
			nexterror();
		}
		tcb->sndcnt += blen(bp);
		if(tcb->sndcnt > Streamhi)
			tcb->sndfull = 1;
		if(tcb->sndq == 0)
			tcb->sndq = bp;
		else {
			for(f = tcb->sndq; f->next; f = f->next)
				;
			f->next = bp;
		}
		tcprcvwin(s);
		tcpoutput(s);
		poperror();
		qunlock(tcb);
		break;

	default:
		freeb(bp);
		error(Ehungup);
	}	
}

void
tcpstopen(Queue *q, Stream *s)
{
	Ipconv *ipc;
	Ipifc *ifc;
	Tcpctl *tcb;
	Block *bp;	
	static int tcpkprocs;

	if(!Ipoutput) {
		Ipoutput = WR(q);
		s->opens++;
		s->inuse++;
	}

	/* Flow control and tcp timer processes */
	if(tcpkprocs == 0) {
		tcpkprocs = 1;
		kproc("tcpack", tcpackproc, 0);
		kproc("tcpflow", tcpflow, ipifc[s->dev]);

	}

	if(tcpbase == 0)
		tcpbase = ipifc[s->dev]->conv;
	ifc = ipifc[s->dev];
	initipifc(ifc, IP_TCPPROTO, tcpinput, 1500, 512, ETHER_HDR);
	ipc = ipcreateconv(ifc, s->id);

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

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

	/* pass any waiting data upstream */
	tcb = &ipc->tcpctl;
	qlock(tcb);
	while(bp = getb(&tcb->rcvq))
		PUTNEXT(ipc->readq, bp);
	qunlock(tcb);
}

void
ipremotefill(Chan *c, char *buf, int len)
{
	Ipconv *cp;

	if(len < 32)
		error(Etoosmall);
	cp = ipcreateconv(ipifc[c->dev], STREAMID(c->qid.path));
	sprint(buf, "%d.%d.%d.%d!%d\n", fmtaddr(cp->dst), cp->pdst);
}

void
iplocalfill(Chan *c, char *buf, int len)
{
	Ipconv *cp;

	if(len < 32)
		error(Etoosmall);
	cp = ipcreateconv(ipifc[c->dev], STREAMID(c->qid.path));
	sprint(buf, "%d.%d.%d.%d!%d\n", fmtaddr(Myip[Myself]), cp->psrc);
}

void
ipstatusfill(Chan *c, char *buf, int len)
{
	Ipconv *cp;
	int connection;

	if(len < 64)
		error(Ebadarg);
	connection = STREAMID(c->qid.path);
	cp = ipcreateconv(ipifc[c->dev], connection);
	if(cp->ifc->protop == &tcpinfo)
		sprint(buf, "tcp/%d %d %s %s\n", connection, cp->ref,
			tcpstate[cp->tcpctl.state],
			cp->tcpctl.flags & CLONE ? "listen" : "connect");
	else if(cp->ifc->protop == &ilinfo)
		sprint(buf, "il/%d %d %s rtt %d ms %d csum\n", connection, cp->ref,
			ilstate[cp->ilctl.state], cp->ilctl.rtt,
			cp->ifc ? cp->ifc->chkerrs : 0);
	else
		sprint(buf, "%s/%d %d Datagram\n",
				cp->ifc->protop->name, connection, cp->ref);
}

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

int
iplisten(Chan *c)
{
	Ipconv *s;
	int connection;
	Ipconv **p, **etab, *new;

	connection = STREAMID(c->qid.path);
	s = ipcreateconv(ipifc[c->dev], connection);

	if(s->ifc->protop == &tcpinfo)
	if(s->tcpctl.state != Listen)
		error(Enolisten);

	if(s->ifc->protop == &ilinfo)
	if(s->ilctl.state != Illistening)
		error(Enolisten);

	for(;;) {
		qlock(&s->listenq);	/* single thread for the sleep */
		if(waserror()) {
			qunlock(&s->listenq);
			nexterror();
		}
		sleep(&s->listenr, iphavecon, s);
		poperror();
		etab = &ipifc[c->dev]->conv[Nipconv];
 		for(p = ipifc[c->dev]->conv; p < etab; p++) {
			new = *p;
			if(new == 0)
				break;
			if(new->newcon == s) {
				qlock(s);
				s->curlog--;
				qunlock(s);
				new->newcon = 0;
				qunlock(&s->listenq);
				return new->id;
			}
		}
		qunlock(&s->listenq);
		print("iplisten: no newcon\n");
	}
	return -1;		/* not reached */
}

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

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

	/* Not interested in data anymore */
	qlock(s);
	s->readq = 0;
	qunlock(s);

	switch(tcb->state){
	case Listen:
		/*
		 *  reset any incoming calls to this listener
		 */
		qlock(s);
		s->backlog = 0;
		s->curlog = 0;
		etab = &tcpbase[Nipconv];
		for(p = tcpbase; p < etab && *p; p++){
			if((*p)->newcon == s) {
				(*p)->newcon = 0;
				tcpflushincoming(*p);
			}
		}
		qunlock(s);

		qlock(tcb);
		localclose(s, 0);
		qunlock(tcb);
		break;

	case Closed:
	case Syn_sent:
		qlock(tcb);
		localclose(s, 0);
		qunlock(tcb);
		break;

	case Syn_received:
	case Established:
		tcb->sndcnt++;
		tcb->snd.nxt++;
		tcpsetstate(s, Finwait1);
		goto output;

	case Close_wait:
		tcb->sndcnt++;
		tcb->snd.nxt++;
		tcpsetstate(s, Last_ack);
	output:
		qlock(tcb);
		if(waserror()) {
			qunlock(tcb);
			nexterror();
		}
		tcpoutput(s);
		poperror();
		qunlock(tcb);
		break;
	}
}


static	short	endian	= 1;
static	char*	aendian	= (char*)&endian;
#define	LITTLE	*aendian

ushort
ptcl_bsum(uchar *addr, int len)
{
	ulong losum, hisum, mdsum, x;
	ulong t1, t2;

	losum = 0;
	hisum = 0;
	mdsum = 0;

	x = 0;
	if((ulong)addr & 1) {
		if(len) {
			hisum += addr[0];
			len--;
			addr++;
		}
		x = 1;
	}
	while(len >= 16) {
		t1 = *(ushort*)(addr+0);
		t2 = *(ushort*)(addr+2);	mdsum += t1;
		t1 = *(ushort*)(addr+4);	mdsum += t2;
		t2 = *(ushort*)(addr+6);	mdsum += t1;
		t1 = *(ushort*)(addr+8);	mdsum += t2;
		t2 = *(ushort*)(addr+10);	mdsum += t1;
		t1 = *(ushort*)(addr+12);	mdsum += t2;
		t2 = *(ushort*)(addr+14);	mdsum += t1;
		mdsum += t2;
		len -= 16;
		addr += 16;
	}
	while(len >= 2) {
		mdsum += *(ushort*)addr;
		len -= 2;
		addr += 2;
	}
	if(x) {
		if(len)
			losum += addr[0];
		if(LITTLE)
			losum += mdsum;
		else
			hisum += mdsum;
	} else {
		if(len)
			hisum += addr[0];
		if(LITTLE)
			hisum += mdsum;
		else
			losum += mdsum;
	}

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

	return losum & 0xffff;
}

ushort
ptcl_csum(Block *bp, int offset, int len)
{
	uchar *addr;
	ulong losum, hisum;
	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;

	if(bp->next == 0)
		return ~ptcl_bsum(addr, MIN(len, blen)) & 0xffff;

	losum = 0;
	hisum = 0;

	odd = 0;
	while(len) {
		x = MIN(len, blen);
		csum = ptcl_bsum(addr, x);
		if(odd)
			hisum += csum;
		else
			losum += csum;
		odd = (odd+x) & 1;
		len -= x;

		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);

	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(Ipifc *ifc, Port port)
{
	Ipconv **p, **etab;
	Ipconv *cp;

	if(port == 0)
		return 0;

	etab = &ifc->conv[Nipconv];
	for(p = ifc->conv; p < etab; p++){
		cp = *p;
		if(cp == 0)
			break;
		if(cp->psrc == port) 
			return cp;
	}

	return 0;
}

static Port lastport[2] = { PORTALLOC-1, PRIVPORTALLOC-1 };

Port
nextport(Ipifc *ifc, int priv)
{
	Port base;
	Port max;
	Port *p;
	Port i;

	if(priv){
		base = PRIVPORTALLOC;
		max = UNPRIVPORTALLOC;
		p = &lastport[1];
	} else {
		base = PORTALLOC;
		max = PORTMAX;
		p = &lastport[0];
	}
	
	for(i = *p + 1; i < max; i++)
		if(!portused(ifc, i))
			return(*p = i);
	for(i = base ; i <= *p; i++)
		if(!portused(ifc, i))
			return(*p = i);

	return(0);
}

/* NEEDS HASHING ! */

Ipconv*
ip_conn(Ipifc *ifc, Port dst, Port src, Ipaddr dest)
{
	Ipconv **p, *s, **etab;

	/* Look for a conversation structure for this port */
	etab = &ifc->conv[Nipconv];
	for(p = ifc->conv; p < etab; p++) {
		s = *p;
		if(s == 0)
			break;
		if(s->psrc == dst)
		if(s->pdst == src)
		if(s->dst == dest || dest == 0)
			return s;
	}

	return 0;
}

/*
 *  Fuck me sideways with a bargepole!!! -- philw
 *
 *  BSD authentication protocol, used on ports 512, 513, & 514.
 *  This makes sure that a user can only write the REAL user id.
 *
 *  q->ptr is number of nulls seen
 */
void
bsdopen(Queue *q, Stream *s)
{
	USED(s);
	RD(q)->ptr = q;
	WR(q)->ptr = q;
}
void
bsdclose(Queue *q)
{
	Block *bp;

	bp = allocb(0);
	bp->type = M_HANGUP;
	PUTNEXT(q->other, bp);
}
void
bsdiput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}
void
bsdoput(Queue *q, Block *bp)
{
	uchar *luser;
	Block *nbp;

	/* just pass it on if we've done authentication */
	if(q->ptr == 0 || bp->type != M_DATA){
		PUTNEXT(q, bp);
		return;
	}

	/* collect into a single block */
	qlock(&q->rlock);
	if(q->first == 0)
		q->first = pullup(bp, blen(bp));
	else{
		nbp = q->first;
		nbp->next = bp;
		q->first = pullup(nbp, blen(nbp));
	}
	bp = q->first;
	if(bp == 0){
		qunlock(&q->rlock);
		bsdclose(q);
		return;
	}

	/* look for 2 nulls to indicate stderr port and local user */
	luser = memchr(bp->rptr, 0, BLEN(bp));
	if(luser == 0){
		qunlock(&q->rlock);
		return;
	}
	luser++;
	if(memchr(luser, 0, bp->wptr - luser) == 0){
		qunlock(&q->rlock);
		return;
	}

	/* if luser is a lie, hangup */
	if(memcmp(luser, up->user, strlen(up->user)+1) != 0)
		bsdclose(q);

	/* mark queue as authenticated and pass data to remote side */
	q->ptr = 0;
	q->first = 0;
	bp->flags |= S_DELIM;
	PUTNEXT(q, bp);
	qunlock(&q->rlock);
}

D port/deviproute.c => port/deviproute.c +0 -377
@@ 1,377 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"arp.h"
#include	"ipdat.h"

#include	"devtab.h"

/*
 *  All ip numbers and masks are stored as ulongs.
 *  All interfaces to this code uses the standard byte
 *  string representation.
 */

typedef	struct Iproute	Iproute;
typedef	struct Iprtab	Iprtab;

enum
{
	Nroutes=	256,
};

/*
 *  routes
 */
struct Iproute {
	ulong	dst;
	ulong	gate;
	ulong	mask;
	Iproute	*next;
	int	inuse;
};
struct Iprtab {
	Lock;
	int	n;		/* number of valid routes */
	Iproute *first;		/* list of valid routes */
	Iproute	r[Nroutes];	/* all routes */
};
Iprtab	iprtab;

/*
 *  The chosen route is the one obeys the constraint
 *		r->mask & dst == r->dst
 *
 *  If there are several matches, the one whose mask has the most
 *  leading ones (and hence is the most specific) wins.  This is
 *  forced by storing the routes in decreasing number of ones order
 *  and returning the first match.  The default gateway has no ones
 *  in the mask and is thus the last matched.
 */
void
iproute(uchar *dst, uchar *gate)
{
	Iproute *r;
	ulong udst;

	udst = nhgetl(dst);
	if((udst&Mynetmask) == (Myip[Myself]&Mynetmask)){
		memmove(gate, dst, 4);
		return;
	}

	/*
	 *  first check routes
	 */
	for(r = iprtab.first; r; r = r->next){
		if((r->mask&udst) == r->dst){
			hnputl(gate, r->gate);
			return;
		}
	}

	/*
	 *  else just return the same address
	 */	
	memmove(gate, dst, 4);
}

/*
 *  Add a route, create a mask if the first mask is 0.
 *
 *  All routes are stored sorted by the length of leading
 *  ones in the mask.
 *
 *  NOTE: A default route has an all zeroes mask and dst.
 */
void
ipaddroute(ulong dst, ulong mask, ulong gate)
{
	Iproute *r, *e, *free;

	/*
	 *  filter out impossible requests
	 */
	if((dst&mask) != dst)
		error(Enetaddr);

	/*
	 *  see if we already have a route for
	 *  the destination
	 */
	lock(&iprtab);
	free = 0;
	for(r = iprtab.r; r < &iprtab.r[Nroutes]; r++){
		if(r->inuse == 0){
			free = r;
			continue;
		}
		if(dst==r->dst && mask==r->mask){
			r->gate = gate;
			unlock(&iprtab);
			return;
		}
	}
	if(free == 0){
		unlock(&iprtab);
		exhausted("routes");
	}

	/*
	 *  add the new route in sorted order
	 */
	free->dst = dst;
	free->mask = mask;
	free->gate = gate;
	free->inuse = 1;
	for(r = e = iprtab.first; r; r = r->next){
		if(mask > r->mask)
			break;
		e = r;
	}
	free->next = r;
	if(r == iprtab.first)
		iprtab.first = free;
	else
		e->next = free;
	iprtab.n++;
	unlock(&iprtab);
}

/*
 *  remove a route
 */
void
ipremroute(ulong dst, ulong mask)
{
	Iproute *r, *e;

	lock(&iprtab);
	for(r = e = iprtab.first; r; r = r->next){
		if(dst==r->dst && mask==r->mask){
			if(r == iprtab.first)
				iprtab.first = r->next;
			else
				e->next = r->next;
			r->inuse = 0;
			iprtab.n--;
			break;
		}
		e = r;
	}
	unlock(&iprtab);
}

/*
 *  remove all routes
 */
void
ipflushroute(void)
{
	Iproute *r;

	lock(&iprtab);
	for(r = iprtab.first; r; r = r->next)
		r->inuse = 0;
	iprtab.first = 0;
	iprtab.n = 0;
	unlock(&iprtab);
}

/*
 *  device interface
 */
enum{
	Qdir,
	Qdata,
};
Dirtab iproutetab[]={
	"iproute",		{Qdata},		0,	0666,
};
#define Niproutetab (sizeof(iproutetab)/sizeof(Dirtab))

void
iproutereset(void)
{
}

void
iprouteinit(void)
{
}

Chan *
iprouteattach(char *spec)
{
	return devattach('P', spec);
}

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

int
iproutewalk(Chan *c, char *name)
{
	return devwalk(c, name, iproutetab, (long)Niproutetab, devgen);
}

void
iproutestat(Chan *c, char *db)
{
	devstat(c, db, iproutetab, (long)Niproutetab, devgen);
}

Chan *
iprouteopen(Chan *c, int omode)
{
	if(c->qid.path == CHDIR){
		if(omode != OREAD)
			error(Eperm);
	}
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

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

void
iprouteremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

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

void
iprouteclose(Chan *c)
{
	USED(c);
}

#define IPR_ENTRYLEN 54
#define PAD "                                                                  "

long
iprouteread(Chan *c, void *a, long n, ulong offset)
{
	char	buf[IPR_ENTRYLEN*3];
	Iproute	*r;
	int	part, bytes, size;
	uchar	dst[4], mask[4], gate[4];

	switch((int)(c->qid.path&~CHDIR)){
	case Qdir:
		return devdirread(c, a, n, iproutetab, Niproutetab, devgen);
	case Qdata:
		lock(&iprtab);
		part = offset/IPR_ENTRYLEN;
		for(r = iprtab.first; part && r; r = r->next)
			part--;
		bytes = offset;
		while(r && bytes < iprtab.n*IPR_ENTRYLEN && n){
			part = bytes%IPR_ENTRYLEN;

			hnputl(dst, r->dst);
			hnputl(mask, r->mask);
			hnputl(gate, r->gate);
			sprint(buf,"%d.%d.%d.%d & %d.%d.%d.%d -> %d.%d.%d.%d%s",
				dst[0], dst[1], dst[2], dst[3],
				mask[0], mask[1], mask[2], mask[3],
				gate[0], gate[1], gate[2], gate[3],
				PAD); 
			
			buf[IPR_ENTRYLEN-1] = '\n';

			size = IPR_ENTRYLEN - part;
			if(size > n)
				size = n;
			memmove(a, buf+part, size);

			a = (void *)((int)a + size);
			n -= size;
			bytes += size;
			r = r->next;
		}
		unlock(&iprtab);
		return bytes - offset;
		break;
	default:
		n=0;
		break;
	}
	return n;
}

long
iproutewrite(Chan *c, char *a, long n, ulong offset)
{
	char buf[IPR_ENTRYLEN];
	char *field[4];
	Ipaddr mask, dst, gate;
	int m;

	USED(offset);

	switch((int)(c->qid.path&~CHDIR)){
	case Qdata:
		strncpy(buf, a, sizeof buf);
		m = getfields(buf, field, 4, ' ');

		if(strncmp(field[0], "flush", 5) == 0)
			ipflushroute();
		else if(strcmp(field[0], "add") == 0){
			switch(m){
			case 4:
				dst = ipparse(field[1]);
				mask = ipparse(field[2]);
				gate = ipparse(field[3]);
				ipaddroute(dst, mask, gate);
				break;
			case 3:
				dst = ipparse(field[1]);
				gate = ipparse(field[2]);
				ipaddroute(dst, classmask[dst>>30], gate);
				break;
			default:
				error(Ebadarg);
			}
		} else if(strcmp(field[0], "delete") == 0){
			switch(m){
			case 3:
				dst = ipparse(field[1]);
				mask = ipparse(field[2]);
				ipremroute(dst, mask);
				break;
			case 2:
				dst = ipparse(field[1]);
				ipremroute(dst, classmask[dst>>30]);
				break;
			default:
				error(Ebadarg);
			}
		}
		break;
	default:
		error(Ebadusefd);
	}
	return n;
}

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

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

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 */
};

struct Ilhdr
{
#define IL_EHSIZE	34
	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 */
	uchar	ttl;		/* Time to live */
	uchar	proto;		/* Protocol */
	uchar	cksum[2];	/* Header checksum */
	uchar	src[4];		/* Ip source */
	uchar	dst[4];		/* Ip destination */
#define IL_HDRSIZE	18	
	uchar	ilsum[2];	/* Checksum including header */
	uchar	illen[2];	/* Packet length */
	uchar	iltype;		/* Packet type */
	uchar	ilspec;		/* Special */
	uchar	ilsrc[2];	/* Src port */
	uchar	ildst[2];	/* Dst port */
	uchar	ilid[4];	/* Sequence id */
	uchar	ilack[4];	/* Acked sequence */
};

struct Ilcb			/* Control block */
{
	int	state;		/* Connection state */

	Rendez	syncer;		/* where syncer waits for a connect */

	QLock	ackq;		/* Unacknowledged queue */
	Block	*unacked;
	Block	*unackedtail;

	QLock	outo;		/* Out of order packet queue */
	Block	*outoforder;

	Lock	nxl;
	ulong	next;		/* Id of next to send */
	ulong	recvd;		/* Last packet received */
	ulong	start;		/* Local start id */
	ulong	rstart;		/* Remote start id */

	int	timeout;	/* Time out counter */
	int	slowtime;	/* Slow time counter */
	int	fasttime;	/* Retransmission timer */
	int	acktime;	/* Acknowledge timer */
	int	querytime;	/* Query timer */
	int	deathtime;	/* Time to kill connection */

	int	rtt;		/* Average round trip time */
	ulong	rttack;		/* The ack we are waiting for */
	ulong	ackms;		/* Time we issued */

	int	window;		/* Maximum receive window */
};

enum				/* Packet types */
{
	Ilsync,
	Ildata,
	Ildataquery,
	Ilack,
	Ilquerey,
	Ilstate,
	Ilclose,
};

enum				/* Connection state */
{
	Ilclosed,
	Ilsyncer,
	Ilsyncee,
	Ilestablished,
	Illistening,
	Ilclosing,
};

#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];
};

enum
{
	TimerOFF	= 0,
	TimerON		= 1,
	TimerDONE	= 2,
};

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

struct Tctl
{
	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;
	struct {
		int	nxt;		/* Receive pointer to next byte slot */
		ushort	wnd;		/* Receive window incoming */
		int	up;		/* Urgent pointer */
	} rcv;
	int	iss;			/* Initial sequence number */
	ushort	cwind;			/* Congestion window */
	ushort	ssthresh;		/* Slow start threshold */
	int	resent;			/* Bytes just resent */
	int	irs;			/* Initial received squence */
	ushort	mss;			/* Mean segment size */
	int	rerecv;			/* Overlap of data rerecevived */
	ushort	window;			/* Recevive window */
	int	max_snd;		/* Max send */
	int	last_ack;		/* Last acknowledege received */
	char	backoff;		/* Exponential backoff counter */
	char	flags;			/* State flags */
	char	tos;			/* Type of service */

	Blist	rcvq;			/* Received data */
	ulong	rcvcnt;			/* Bytes queued for upstream */

	Block	*sndq;			/* List of data going out */
	ulong	sndcnt;			/* Amount of data in send queue */
	Rendez	sndr;			/* process flow control */
	QLock	sndrlock;
	int	sndfull;

	Reseq	*reseq;			/* Resequencing queue */
	Timer	timer;			/* Activity 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 Tcpctl
{
	QLock;
	Rendez syner;
	struct Tctl;
};

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/IL */
struct Ipconv
{
	QLock;				/* Ref count lock */
	Netprot;			/* stat info */
	int 	ref;
	Ipaddr	dst;			/* Destination from connect */
	Port	psrc;			/* Source port */
	Port	pdst;			/* Destination port */

	Ipifc	*ifc;			/* 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 */
		
	char	*err;			/* Async protocol error */
	int	backlog;		/* Maximum number of waiting connections */
	int	headers;		/* include header in packet */
	int	curlog;			/* Number of waiting connections */
	Ipconv 	*newcon;		/* This is the start of a connection */

	union {
		Tcpctl	tcpctl;		/* Tcp control block */
		Ilcb	ilctl;		/* Il control block */
	};
};

enum
{
	MAX_TIME 	= (1<<20),	/* Forever */
	TCP_ACK		= 200,		/* Timed ack sequence every 200ms */

	URG		= 0x20,		/* Data marked urgent */
	ACK		= 0x10,		/* Aknowledge is valid */
	PSH		= 0x08,		/* Whole data pipe is pushed */
	RST		= 0x04,		/* Reset connection */
	SYN		= 0x02,		/* Pkt. is synchronise */
	FIN		= 0x01,		/* Start close down */

	EOL_KIND	= 0,
	NOOP_KIND	= 1,
	MSS_KIND	= 2,

	MSS_LENGTH	= 4,		/* Mean segment size */
	MSL2		= 10,
	MSPTICK		= 100,		/* Milliseconds per timer tick */
	DEF_MSS		= 1024,		/* Default mean segment */
	DEF_RTT		= 1000,		/* Default round trip */

	TCP_PASSIVE	= 0,		/* Listen connection */
	TCP_ACTIVE	= 1,		/* Outgoing connection */
	IL_PASSIVE	= 0,
	IL_ACTIVE	= 1,

	MAXBACKOFF	= 5,
	FORCE		= 1,
	CLONE		= 2,
	RETRAN		= 4,
	ACTIVE		= 8,
	SYNACK		= 16,
	AGAIN		= 8,
	DGAIN		= 4,
};

#define	set_timer(t,x)	(((t)->start) = (x)/MSPTICK)
#define	run_timer(t)	((t)->state == TimerON)

enum					/* Tcp connection states */
{
	Closed		= 0,
	Listen,
	Syn_sent,
	Syn_received,
	Established,
	Finwait1,
	Finwait2,
	Close_wait,
	Closing,
	Last_ack,
	Time_wait
};

enum
{
	Nipconv=	512,		/* max conversations per interface */
	Udphdrsize=	6,		/* size if a to/from user Udp header */
};

/*
 * Ip interface structure. We have one for each active protocol driver
 */
struct Ipifc 
{
	QLock;
	Network;				/* user level network interface */
	Ipifc		*next;
	int 		inited;
	uchar		protocol;		/* Ip header protocol number */
	void (*iprcv)	(Ipifc*, Block*);	/* Receive demultiplexor */
	int		maxmtu;			/* Maximum transfer unit */
	int		minmtu;			/* Minumum tranfer unit */
	int		hsize;			/* Media header size */	
	ulong		chkerrs;		/* checksum errors */
	Ipconv		**conv;			/* conversations */
};


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

struct Ipfrag
{
	ushort	foff;
	ushort	flen;
};

enum {
	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 */

	/* Sizes */
	IP_MAX		= (32*1024),		/* Maximum Internet packet size */
	UDP_MAX		= (IP_MAX-ETHER_IPHDR),	/* Maximum UDP datagram size */
	UDP_DATMAX	= (UDP_MAX-UDP_HDRSIZE),/* Maximum amount of udp data */
	IL_DATMAX	= (IP_MAX-IL_HDRSIZE),	/* Maximum IL data in one ip packet */

	/* Protocol numbers */
	IP_UDPPROTO	= 17,
	IP_TCPPROTO	= 6,
	IP_ILPROTO	= 40,

	/* Protocol port numbers */
	PORTALLOC	= 5000,			/* First automatic allocated port */
	PRIVPORTALLOC	= 600,			/* First priveleged port allocated */
	UNPRIVPORTALLOC	= 1024,			/* First unpriveleged port allocated */
	PORTMAX		= 30000,		/* Last port to allocte */
};

void	add_reseq(Tcpctl *, char, Tcp *, Block *, ushort);
int	arp_lookup(uchar*, uchar*);
int	backoff(int);
Block*	btrim(Block*, int, int);
void	localclose(Ipconv *, char []);
int	dupb(Block **, Block *, int, int);
void	extract_oob(Block **, Block **, Tcp *);
void	get_reseq(Tcpctl *, char *, Tcp *, Block **, ushort *);
void	hnputl(uchar*, ulong);
void	hnputs(uchar*, ushort);
Block*	htontcp(Tcp *, Block *, Tcphdr *);
Block*	htontcp(Tcp *, Block *, Tcphdr *);
void	iloutoforder(Ipconv*, Ilhdr*, Block*);
void	ilstart(Ipconv *, int, int);
int	inb_window(Tcpctl *, int);
void	init_tcpctl(Ipconv *);
void	initfrag(int);
void	initipifc(Ipifc*, uchar, void (*)(Ipifc*, Block*), int, int, int);
Ipconv*	ip_conn(Ipifc*, Port, Port, Ipaddr dest);
ushort	ip_csum(uchar*);
Block*	ip_reassemble(int, Block*, Etherhdr*);
int	ipclonecon(Chan *);
int	ipconbusy(Ipconv*);
Ipconv*	ipcreateconv(Ipifc*, int);
int	ipforme(uchar*);
Fragq*	ipfragallo(void);
void	ipfragfree(Fragq*, int);
Ipconv*	ipincoming(Ipifc*, Ipconv*);
int	iplisten(Chan *);
void	iplocalfill(Chan*, char*, int);
void	ipmkdir(Qinfo *, Dirtab *, Ipconv *);
Ipaddr	ipparse(char*);
void	ipremotefill(Chan*, char*, int);
void	iproute(uchar*, uchar*);
void	ipsetaddrs(void);
void	ipstatusfill(Chan*, char*, int);
Port	nextport(Ipifc*, int);
ushort	nhgets(uchar*);
ulong	nhgetl(uchar*);
int	ntohtcp(Tcp *, Block **);
int	ntohtcp(Tcp*, Block**);
Ipconv*	portused(Ipifc*, Port);
void	ppkt(Block*);
void	proc_syn(Ipconv*, char, Tcp*);
ushort	ptcl_csum(Block*bp, int, int);
int	pullb(Block **, int);
void	reset(Ipaddr, Ipaddr, char, ushort, Tcp*);
void	tcpsndsyn(Tcpctl*);
int	seq_ge(int, int);
int	seq_gt(int, int);
int	seq_gt(int, int);
int	seq_le(int, int);
int	seq_lt(int, int);
int	seq_within(int, int, int);
int	seq_within(int, int, int);
void	tcpsetstate(Ipconv *, char);
void	tcpgo(Timer *);
void	tcphalt(Timer *);
void	tcpxstate(Ipconv*, char oldstate, char newstate);
void	tcpacktimer(void *);
void	tcpinput(Ipifc*, Block *);
void	tcpoutput(Ipconv*);
void	tcptimeout(void *);
void	tcpackproc(void*);
void	tcpflow(void*);
void	tcpflushincoming(Ipconv*);
void	tcprcvwin(Ipconv *);
void	tcpstart(Ipconv *, int, ushort, char);
int	trim(Tcpctl *, Tcp *, Block **, ushort *);
void	udprcvmsg(Ipifc*, Block*);
void	update(Ipconv *, Tcp *);

#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)->base))
#define PREC(x)		((x)>>5 & 7)

extern Ipaddr Myip[7];
extern Ipaddr Mymask;
extern Ipaddr Mynetmask;
extern Ipaddr classmask[4];
extern Ipifc *ipifc[];
extern char *tcpstate[];
extern char *ilstate[];
extern Rendez tcpflowr;
extern Qinfo tcpinfo;
extern Qinfo ipinfo;
extern Qinfo udpinfo;
extern Qinfo ilinfo;
extern Qinfo arpinfo;
extern Queue *Ipoutput;

/* offsets into Myip */
enum
{
	Myself		= 0,
	Mybcast		= 1,
	Mynet		= 3,
	Mysubnet	= 5,
};

M port/proc.c => port/proc.c +0 -1
@@ 84,7 84,6 @@ schedinit(void)		/* never returns */
void
sched(void)
{
	kmapinval();
	if(up) {
		splhi();
		m->cs++;

M port/qio.c => port/qio.c +16 -9
@@ 115,9 115,10 @@ iallockproc(void *arg)
			unlock(cl);
			splx(x);
	
			for(; first; first = p){
			while(first != 0) {
				p = first->next;
				free(first);
				first = p;
			}
		}



@@ 146,10 147,12 @@ iallockproc(void *arg)

			first = 0;
			l = &first;
			for(i = x = cl->goal - cl->have; x > 0; x--){
			i = cl->goal - cl->have;
			for(x = i; x > 0; x--){
				p = malloc(1<<pow);
				if(p == 0)
					break;

				*l = p;
				l = &p->next;
			}


@@ 190,7 193,7 @@ iallocb(int size)
	Block *b;

	size += sizeof(Block);
	for(pow = Minpow; pow <= Maxpow; pow++)
	for(pow = Minpow; pow <= Maxpow; pow++){
		if(size <= (1<<pow)){
			cl = &arena.alloc[pow];
			lock(cl);


@@ 205,10 208,13 @@ iallocb(int size)
			b = (Block *)p;
			memset(b, 0, sizeof(Block));
			b->base = (uchar*)(b+1);
			b->wp = b->rp = b->base;
			b->wp = b->base;
			b->rp = b->base;
			b->lim = b->base + (1<<pow) - sizeof(Block);
			return b;
		}
	}

	panic("iallocb %d\n", size);
	return 0;			/* not reached */
}


@@ 239,9 245,9 @@ allocb(int size)
	if(b == 0)
		exhausted("Blocks");

	memset(b, 0, sizeof(Block));
	b->base = (uchar*)(b+1);
	b->rp = b->wp = b->base;
	b->rp = b->base;
	b->wp = b->base;
	b->lim = b->base + size;
	b->flag = 0;



@@ 549,9 555,10 @@ qclose(Queue *q)
	splx(x);

	/* free queued blocks */
	while(b = bfirst){
		bfirst = b->next;
		free(b);
	while(bfirst){
		b = bfirst->next;
		free(bfirst);
		bfirst = b;
	}

	/* wake up readers/writers */

D port/stasync.c => port/stasync.c +0 -477
@@ 1,477 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

#define DPRINT 	if(asyncdebug)kprint

/*
 *  configuration
 */
enum {
	MAXFRAME=	256,	/* also known to tsm8 code */
};

/* input states */
enum
{
	Hunt,
	Framing,
	Framed,
	Data,
	Escape
};

typedef struct Async Async;
struct Async
{
	QLock;
	Async	*list;
	int	id;

	int	inuse;
	Queue	*wq;

	/* output state */
	QLock	xmit;		/* transmit lock */
	int	chan;		/* current urp channel */
	Block	*bp;		/* current output buffer */
	int	count;
	ushort	crc;

	/* input state */
	int	state;		/* input state */
	uchar	buf[MAXFRAME];	/* current input buffer */
	int	icount;
	ushort	icrc;

	/* statistics */
	ulong	chan0;
	ulong	toolong;
	ulong	tooshort;
	ulong	badcrc;
	ulong	badescape;
	ulong	in;		/* bytes in */
	ulong	out;		/* bytes out */
};

int nasync;

/* list of allocated async structures (never freed) */
struct
{
	Lock;
	Async *async;
} asyncalloc;

/*
 *  async stream module definition
 */
static void asynciput(Queue*, Block*);
static void asyncoput(Queue*, Block*);
static void asyncopen(Queue*, Stream*);
static void asyncclose(Queue*);
static void asyncreset(void);
Qinfo asyncinfo =
{
	asynciput,
	asyncoput,
	asyncopen,
	asyncclose,
	"async",
	asyncreset
};

static int	debugcount = 6;
int asyncdebug;
int asyncerror;

static ushort crc_table[256] = {
#include "../port/crc_16.h"
};

#define	BOT	0050		/* begin trailer */
#define	BOTM	0051		/* begin trailer, more data follows */
#define	BOTS	0052		/* seq update alg. on this trailer */

#define	FRAME		0x7e
#define	STUF		0x9d

#define	CRCSTART	(crc_table[0xff])
#define	CRCFUNC(crc,x)	(crc_table[((crc)^(x))&0xff]^((crc)>>8))

/*
 *  create the async structures
 */
static void
asyncreset(void)
{
}

/*
 *  allocate an async structure
 */
static void
asyncopen(Queue *q, Stream *s)
{
	Async *ap;

	DPRINT("asyncopen %d\n", s->dev);

	for(ap = asyncalloc.async; ap; ap = ap->list){
		qlock(ap);
		if(ap->inuse == 0)
			break;
		qunlock(ap);
	}
	if(ap == 0){
		ap = smalloc(sizeof(Async));
		qlock(ap);
		lock(&asyncalloc);
		ap->list = asyncalloc.async;
		asyncalloc.async = ap;
		ap->id = nasync++;
		unlock(&asyncalloc);
	}
	q->ptr = q->other->ptr = ap;

	ap->inuse = 1;
	ap->bp = 0;
	ap->chan = -1;
	ap->count = 0;
	ap->toolong = 0;
	ap->tooshort = 0;
	ap->badcrc = 0;
	ap->badescape = 0;
	ap->chan0 = 0;
	ap->in = 0;
	ap->out = 0;
	ap->wq = WR(q);
	ap->state = Hunt;
	qunlock(ap);
}

static void
asyncclose(Queue * q)
{
	Async *ap = (Async *)q->ptr;

	DPRINT("asyncstclose %d\n", ap->id);
	qlock(ap);
	ap->inuse = 0;
	qunlock(ap);
}

/*
 *  free all blocks of a message in `q', `bp' is the first block
 *  of the message
 */
static void
freemsg(Queue *q, Block *bp)
{
	for(; bp; bp = getq(q)){
		if(bp->flags & S_DELIM){
			freeb(bp);
			return;
		}
		freeb(bp);
	}
}

static void
showframe(char *t, Async *ap, uchar *buf, int n)
{
	kprint("a%d %s [", ap->id, t);
	while (--n >= 0)
		kprint(" %2.2ux", *buf++);
	kprint(" ]\n");
}

void
aswrite(Async *ap)
{
	if(ap->bp->rptr == ap->bp->wptr)
		return;
	FLOWCTL(ap->wq, ap->bp);
	ap->bp = 0;
}

void
asputf(Async *ap)
{
	uchar *p;
	int c;

	p = ap->bp->wptr;
	if(ap->count > 0) {
		if(asyncerror)
			ap->crc^=1, asyncerror=0;
		*p++ = c = ap->crc&0xff;
		if(c == FRAME)
			*p++ = 0x00;
		*p++ = c = (ap->crc>>8)&0xff;
		if(c == FRAME)
			*p++ = 0x00;
		ap->count = 0;
	}
	*p++ = FRAME;
	*p++ = FRAME;
	ap->bp->wptr = p;
	if(asyncdebug > 2)
		showframe("out", ap, ap->bp->rptr, BLEN(ap->bp));
	aswrite(ap);
}

void
asputc(Async *ap, int c)
{
	int d;
	uchar *p;

	if(ap->bp == 0)
		ap->bp = allocb(MAXFRAME+4);
	p = ap->bp->wptr;
	if(ap->count <= 0) {
		*p++ = d = 0x80|((ap->chan>>5)&0x7e);
		ap->crc = CRCFUNC(CRCSTART, d);
		*p++ = d = 0x80|((ap->chan<<1)&0x7e);
		ap->crc = CRCFUNC(ap->crc, d);
	}
	*p++ = c;
	if(c == FRAME)
		*p++ = 0x00;
	ap->crc = CRCFUNC(ap->crc, c);
	ap->bp->wptr = p;
	if(++ap->count >= MAXFRAME-4)
		asputf(ap);
	else if(ap->bp->lim - p < 8)
		aswrite(ap);
}

/*
 *  output a block
 *
 *  the first 2 bytes of every message are the channel number,
 *  low order byte first.  the third is a possible trailing control
 *  character.
 */
void
asyncoput(Queue *q, Block *bp)
{
	Async *ap = (Async *)q->ptr;
	int c, chan, ctl;
	Block *msg;

	if(bp->type != M_DATA){
		if(streamparse("debug", bp)){
			asyncdebug = 3;
			freeb(bp);
		} else {
			PUTNEXT(q, bp);
		}
		return;
	}

	/*
	 *  each datakit message has a 2 byte channel number followed by
	 *  one control byte
	 */
	msg = pullup(bp, 3);
	if(msg == 0){
		print("asyncoput msglen < 3\n");
		return;
	}
	chan = msg->rptr[0] | (msg->rptr[1]<<8);
	ctl = msg->rptr[2];
	msg->rptr += 3;

	qlock(&ap->xmit);
	if(waserror()){
		qunlock(&ap->xmit);
		freeb(msg);
		nexterror();
	}

	/*
	 *  new frame if the channel number has changed
	 */
	if(chan != ap->chan && ap->count > 0)
		asputf(ap);
	ap->chan = chan;

	if(asyncdebug > 1)
		kprint("a%d->(%d)%3.3uo %d\n",
			ap->id, chan, ctl, bp->wptr-bp->rptr);

	/*
	 *  send the 8 bit data
	 */
	for(bp = msg; bp; bp = bp->next){
		while (bp->rptr < bp->wptr) {
			asputc(ap, c = *bp->rptr++);
			if(c == STUF)
				asputc(ap, 0);
		}
	}

	/*
	 *  send the control byte if there is one
	 */
	if(ctl){
		asputc(ap, STUF);
		asputc(ap, ctl);
		switch (ctl) {
		case BOT:
		case BOTM:
		case BOTS:
			break;
		default:
			asputf(ap);
		}
	}
	if(debugcount > 0 && --debugcount == 0)
		asyncdebug = 1;

	freeb(msg);
	qunlock(&ap->xmit);
	poperror();
	return;
}

/*
 *  Read bytes from the raw input.
 */

void
asdeliver(Queue *q, Async *ap)
{
	int chan, c;
	Block *bp = 0;
	uchar *p = ap->buf;
	int n = ap->icount;

	chan = *p++ & 0x7e;
	chan = (chan<<5)|((*p++ & 0x7e)>>1);
	if(chan==0) {
		DPRINT("a%d deliver chan 0\n", ap->id);
		ap->chan0++;
		return;
	}
	for (n-=4; n>0; n--) {
		if(!bp) {
			bp = allocb(n+2);
			bp->flags |= S_DELIM;
			bp->wptr[0] = chan;
			bp->wptr[1] = chan>>8;
			bp->wptr[2] = 0;
			bp->wptr += 3;
		}
		if((c = *p++) == STUF) {
			--n;
			if((c = *p++) != 0) {
				bp->rptr[2] = c;
				if(asyncdebug > 1)
					kprint("a%d<-(%d)%3.3uo %d\n",
						ap->id, chan, bp->rptr[2],
						bp->wptr - bp->rptr - 3);
				PUTNEXT(q, bp);
				bp = 0;
				continue;
			} else
				c = STUF;
		}
		*bp->wptr++ = c;
	}
	if(bp) {
		if(asyncdebug > 1)
			kprint("a%d<-(%d)%3.3uo %d\n",
				ap->id, chan, bp->rptr[2],
				bp->wptr - bp->rptr - 3);
		PUTNEXT(q, bp);
	}
}

static void
asynciput(Queue *q, Block *bp)
{
	int c;
	Async *ap = q->ptr;
	int state = ap->state;

	while(bp->wptr > bp->rptr){
		c = *bp->rptr++;
		switch(state) {
		case Hunt:	/* wait for framing byte */
			if(c == FRAME)
				state = Framing;
			break;
	
		case Framing:	/* saw 1 framing byte after Hunt */
			if(c == FRAME)
				state = Framed;
			else
				state = Hunt;
			break;
	
		case Framed:	/* saw 2 or more framing bytes */
			if(c == FRAME)
				break;
			state = Data;
			ap->icrc = CRCSTART;
			ap->icount = 0;
			goto Datachar;
	
		case Data:	/* mid-frame */
			if(c == FRAME) {
				state = Escape;
				break;
			}
		Datachar:
			if(ap->icount >= MAXFRAME) {
				DPRINT("a%d pkt too long\n", ap->id);
				ap->toolong++;
				state = Hunt;
				break;
			}
			ap->icrc = CRCFUNC(ap->icrc, c);
			ap->buf[ap->icount++] = c;
			break;
	
		case Escape:	/* saw framing byte in Data */
			switch (c) {
			case FRAME:
				if(asyncdebug > 2)
					showframe("in", ap, ap->buf, ap->icount);
				if(ap->icount < 5) {
					DPRINT("a%d pkt too short\n", ap->id);
					if(asyncdebug && asyncdebug<=2)
						showframe("shortin", ap, ap->buf, ap->icount);
					ap->tooshort++;
				} else if(ap->icrc != 0) {
					DPRINT("a%d bad crc\n", ap->id);
					if(asyncdebug && asyncdebug<=2)
						showframe("badin", ap, ap->buf, ap->icount);
					ap->badcrc++;
				} else {
					asdeliver(q, ap);
				}
				state = Framed;
				break;
			case 0:
				c = FRAME;
				state = Data;
				goto Datachar;
			default:
				DPRINT("a%d bad escape\n", ap->id);
				ap->badescape++;
				state = Hunt;
				break;
			}
			break;
		}
	}
	ap->state = state;
	freeb(bp);
}

D port/stfcall.c => port/stfcall.c +0 -172
@@ 1,172 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

enum
{
	Twritehdr	= 16,	/* Min bytes for Twrite */
	Rreadhdr	= 8,	/* Min bytes for Rread */
	Twritecnt	= 13,	/* Offset in byte stream of write count */
	Rreadcnt	= 5,	/* Offset for Readcnt */
};

static void fcalliput(Queue*, Block*);
static void fcalloput(Queue*, Block*);
static void fcallopen(Queue*, Stream*);
static void fcallclose(Queue*);
static void fcallreset(void);
Qinfo fcallinfo = { fcalliput, fcalloput, fcallopen, fcallclose, "fcall", fcallreset };

static uchar msglen[256] =
{
	[Tnop]		3,
	[Rnop]		3,
	[Tsession]	3,
	[Rsession]	3,
	[Terror]	0,
	[Rerror]	67,
	[Tflush]	5,
	[Rflush]	3,
	[Tattach]	89,
	[Rattach]	13,
	[Tclone]	7,
	[Rclone]	5,
	[Twalk]		33,
	[Rwalk]		13,
	[Topen]		6,
	[Ropen]		13,
	[Tcreate]	38,
	[Rcreate]	13,
	[Tread]		15,
	[Rread]		8,
	[Twrite]	16,
	[Rwrite]	7,
	[Tclunk]	5,
	[Rclunk]	5,
	[Tremove]	5,
	[Rremove]	5,
	[Tstat]		5,
	[Rstat]		121,
	[Twstat]	121,
	[Rwstat]	5,
	[Tclwalk]	35,
	[Rclwalk]	13,
	[Tauth]		69,
	[Rauth]		35,
};

static void
fcallreset(void)
{
}

static void
fcallopen(Queue *q, Stream *s)
{
	USED(q, s);
}

static void
fcallclose(Queue * q)
{
	USED(q);
}

void
fcalloput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

void
upstream(Queue *q, ulong len)
{
	Block *bl, **tail, *bp;
	ulong l;

	tail = &bl;
	while(len) {
		l = BLEN(q->first);
		if(l > len)
			break;
		bp = getq(q);			/* Consume all of block */
		*tail = bp;
		tail = &bp->next;
		len -= l;
	}
	if(len) {				/* Consume partial block */
		lock(q);
		*tail = copyb(q->first, len);
		q->first->rptr += len;
		q->len -= len;
		unlock(q);
	}
	for(bp = bl; bp->next; bp = bp->next)
		;
	bp->flags |= S_DELIM;
	PUTNEXT(q, bl);
}

static void
fcalliput(Queue *q, Block *bp)
{
	ulong len, need, off;

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

	/* Stash the data */
	bp->flags &= ~S_DELIM;
	putq(q, bp);

	for(;;) {
		bp = q->first;
		if(bp == 0)
			return;
		switch(bp->rptr[0]) {		/* This is the type */
		default:
			len = msglen[bp->rptr[0]];
			if(len == 0){
				bp = allocb(0);
				bp->type = M_HANGUP;
				PUTNEXT(q, bp);
				return;
			}
			if(q->len < len)
				return;
	
			upstream(q, len);
			continue;

		case Twrite:			/* Fmt: TGGFFOOOOOOOOCC */
			len = Twritehdr;	/* T = type, G = tag, F = fid */
			off = Twritecnt;	/* O = offset, C = count */
			break;

		case Rread:			/* Fmt: TGGFFCC */
			len = Rreadhdr;
			off = Rreadcnt;
			break;
		}
	
		if(q->len < len)
			return;
	
		pullup(q->first, len);
		bp = q->first;
		need = len+bp->rptr[off]+(bp->rptr[off+1]<<8);
		if(q->len < need)
			return;
	
		upstream(q, need);
	}
}

D port/stil.c => port/stil.c +0 -877
@@ 1,877 0,0 @@
/*
 * stil - Internet link protocol
 */
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"
#include	"arp.h"
#include 	"ipdat.h"

#define	 DBG	if(0)print
int 		ilcksum = 1;
static 	int 	initseq = 26000;
static	Rendez	ilackr;

char	*ilstate[] = 
{ 
	"Closed",
	"Syncer",
	"Syncee",
	"Established",
	"Listening",
	"Closing" 
};

char	*iltype[] = 
{	
	"sync",
	"data",
	"dataquerey",
	"ack",
	"querey",
	"state",
	"close" 
};
static char *etime = "connection timed out";

/* Always Acktime < Fasttime < Slowtime << Ackkeepalive */
enum
{
	Iltickms 	= 100,
	Slowtime 	= 200*Iltickms,
	Fasttime 	= 4*Iltickms,
	Acktime		= 2*Iltickms,
	Ackkeepalive	= 6000*Iltickms,
	Querytime	= 60*Iltickms,		/* time between queries */
	Keepalivetime	= 10*Querytime,		/* keep alive time */
	Defaultwin	= 20,
	ILgain		= 8,
};

#define Starttimer(s)	{(s)->timeout = 0; \
			 (s)->fasttime = (Fasttime*(s)->rtt)/Iltickms; \
			 (s)->slowtime = (Slowtime*(s)->rtt)/Iltickms; }

void	ilrcvmsg(Ipifc*, Block*);
void	ilackproc(void*);
void	ilsendctl(Ipconv*, Ilhdr*, int, ulong, ulong);
void	ilackq(Ilcb*, Block*);
void	ilprocess(Ipconv*, Ilhdr*, Block*);
void	ilpullup(Ipconv*);
void	ilhangup(Ipconv*, char *);
void	ilfreeq(Ilcb*);
void	ilrexmit(Ilcb*);
void	ilbackoff(Ilcb*);

void
ilopen(Queue *q, Stream *s)
{
	Ipconv *ipc;
	static int ilkproc;

	if(!Ipoutput) {
		Ipoutput = WR(q);
		s->opens++;
		s->inuse++;
	}

	if(ilkproc == 0) {
		ilkproc = 1;
		kproc("ilack", ilackproc, ipifc[s->dev]);
	}

	ipc = ipcreateconv(ipifc[s->dev], s->id);
	initipifc(ipc->ifc, IP_ILPROTO, ilrcvmsg, 1500, 60, ETHER_HDR);

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

void
ilclose(Queue *q)
{
	Ipconv *s;
	Ilcb *ic;

	s = (Ipconv *)(q->ptr);
	ic = &s->ilctl;
	qlock(s);
	s->readq = 0;
	qunlock(s);

	switch(ic->state) {
	case Ilclosing:
	case Ilclosed:
		break;
	case Ilsyncer:
	case Ilsyncee:
	case Ilestablished:
		ilfreeq(ic);
		ic->state = Ilclosing;
		ilsendctl(s, 0, Ilclose, ic->next, ic->recvd);
		break;
	case Illistening:
		ic->state = Ilclosed;
		s->psrc = 0;
		s->pdst = 0;
		s->dst = 0;
		break;
	}
}

void
iloput(Queue *q, Block *bp)
{
	Ipconv *ipc;
	Ilhdr *ih;
	Ilcb *ic;
	int dlen;
	Block *f;
	ulong id;

	ipc = (Ipconv *)(q->ptr);
	if(ipc->psrc == 0)
		error(Enoport);

	ic = &ipc->ilctl;
	switch(ic->state) {
	case Ilclosed:
	case Illistening:
	case Ilclosing:
		error(Ehungup);
	}

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

	/* Only allow atomic Il writes to form datagrams */
	for(f = bp; f->next; f = f->next)
		;
	if((f->flags & S_DELIM) == 0) {
		freeb(bp);
		error(Emsgsize);
	}

	dlen = blen(bp);
	if(dlen > IL_DATMAX) {
		freeb(bp);
		error(Emsgsize);
	}

	/* Make space to fit il & ip & ethernet header */
	bp = padb(bp, IL_EHSIZE+IL_HDRSIZE);
	ih = (Ilhdr *)(bp->rptr);

	/* Ip fields */
	ih->frag[0] = 0;
	ih->frag[1] = 0;
	hnputl(ih->src, Myip[Myself]);
	hnputl(ih->dst, ipc->dst);
	ih->proto = IP_ILPROTO;
	/* Il fields */
	hnputs(ih->illen, dlen+IL_HDRSIZE);
	hnputs(ih->ilsrc, ipc->psrc);
	hnputs(ih->ildst, ipc->pdst);

	lock(&ic->nxl);
	id = ic->next++;
	unlock(&ic->nxl);
	hnputl(ih->ilid, id);

	hnputl(ih->ilack, ic->recvd);
	ih->iltype = Ildata;
	ih->ilspec = 0;
	ih->ilsum[0] = 0;
	ih->ilsum[1] = 0;

	/* Checksum of ilheader plus data (not ip & no pseudo header) */
	if(ilcksum)
		hnputs(ih->ilsum, ptcl_csum(bp, IL_EHSIZE, dlen+IL_HDRSIZE));
	ilackq(ic, bp);

	/* Start the round trip timer for this packet if the timer is free */
	if(ic->rttack == 0) {
		ic->rttack = id;
		ic->ackms = MACHP(0)->ticks;
	}
	ic->acktime = Ackkeepalive;

	PUTNEXT(q, bp);
}

void
ilackq(Ilcb *ic, Block *bp)
{
	Block *np;

	/* Enqueue a copy on the unacked queue in case this one gets lost */
	np = copyb(bp, blen(bp));
	qlock(&ic->ackq);
	if(ic->unacked)
		ic->unackedtail->list = np;
	else {
		/* Start timer since we may have been idle for some time */
		Starttimer(ic);
		ic->unacked = np;
	}
	ic->unackedtail = np;
	np->list = 0;
	qunlock(&ic->ackq);
}

void
ilackto(Ilcb *ic, ulong ackto)
{
	Ilhdr *h;
	Block *bp;
	ulong id, t;

	if(ic->rttack == ackto) {
		t = TK2MS(MACHP(0)->ticks - ic->ackms);
		/* Guard against the ulong zero wrap if MACP->ticks */
		if(t < 100*ic->rtt)
			ic->rtt = (ic->rtt*(ILgain-1)+t)/ILgain;
		if(ic->rtt < Iltickms)
			ic->rtt = Iltickms;
	}

	/* Cancel if we lost the packet we were interested in */
	if(ic->rttack <= ackto)
		ic->rttack = 0;

	qlock(&ic->ackq);
	while(ic->unacked) {
		h = (Ilhdr *)ic->unacked->rptr;
		id = nhgetl(h->ilid);
		if(ackto < id)
			break;

		bp = ic->unacked;
		ic->unacked = bp->list;
		bp->list = 0;
		freeb(bp);
	}
	qunlock(&ic->ackq);
}

void
iliput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

void
ilrcvmsg(Ipifc *ifc, Block *bp)
{
	Ilhdr *ih;
	Ilcb *ic;
	int plen, illen;
	Ipconv *s, **p, **etab, *new, *spec, *gen;
	short sp, dp;
	Ipaddr dst;

	ih = (Ilhdr *)bp->rptr;
	plen = blen(bp);
	if(plen < IL_EHSIZE+IL_HDRSIZE)
		goto drop;

	illen = nhgets(ih->illen);
	if(illen+IL_EHSIZE > plen)
		goto drop;

	sp = nhgets(ih->ildst);
	dp = nhgets(ih->ilsrc);
	dst = nhgetl(ih->src);

	if(ilcksum && ptcl_csum(bp, IL_EHSIZE, illen) != 0) {
		ifc->chkerrs++;
/*		st = (ih->iltype < 0 || ih->iltype > Ilclose) ? "?" : iltype[ih->iltype];
		print("il: cksum error, pkt(%s id %lud ack %lud %d.%d.%d.%d/%d->%d)\n",
			st, nhgetl(ih->ilid), nhgetl(ih->ilack), fmtaddr(dst), sp, dp); /**/
		goto drop;
	}

	etab = &ifc->conv[Nipconv];
	for(p = ifc->conv; p < etab; p++) {
		s = *p;
		if(s == 0)
			break;
		if(s->psrc == sp)
		if(s->pdst == dp)
		if(s->dst == dst) {
			ilprocess(s, ih, bp);
			return;
		}
	}

	if(ih->iltype != Ilsync)
		goto drop;

	gen = 0;
	spec = 0;
	etab = &ifc->conv[Nipconv];
	for(p = ifc->conv; p < etab && *p; p++) {
		s = *p;
		if(s->ilctl.state == Illistening)
		if(s->pdst == 0)
		if(s->dst == 0) {
			if(s->psrc == sp){
				spec = s;
				break;
			}
			if(s->psrc == 0)
				gen = s;
		}
	}

	if(spec)
		s = spec;
	else if(gen)
		s = gen;
	else
		goto drop;

	if(s->curlog > s->backlog)
		goto reset;

	new = ipincoming(ifc, s);
	if(new == 0)
		goto reset;

	new->newcon = s;
	new->ifc = s->ifc;
	new->psrc = sp;
	new->pdst = dp;
	new->dst = nhgetl(ih->src);

	ic = &new->ilctl;
	ic->state = Ilsyncee;
	initseq += TK2MS(MACHP(0)->ticks);
	ic->start = initseq & 0xffffff;
	ic->next = ic->start+1;
	ic->recvd = 0;
	ic->rstart = nhgetl(ih->ilid);
	ic->slowtime = Slowtime;
	ic->rtt = Iltickms;
	ic->querytime = Keepalivetime;
	ic->deathtime = Keepalivetime;
	ic->window = Defaultwin;
	ilprocess(new, ih, bp);

	s->curlog++;
	wakeup(&s->listenr);
	return;

drop:
	freeb(bp);
	return;
reset:
	ilsendctl(0, ih, Ilclose, 0, 0);
	freeb(bp);
}

void
_ilprocess(Ipconv *s, Ilhdr *h, Block *bp)
{
	Ilcb *ic;
	ulong id, ack;

	id = nhgetl(h->ilid);
	ack = nhgetl(h->ilack);
	ic = &s->ilctl;

	ic->querytime = Keepalivetime;
	ic->deathtime = Keepalivetime;
	switch(ic->state) {
	default:
		panic("il unknown state");
	case Ilclosed:
		freeb(bp);
		break;
	case Ilsyncer:
		switch(h->iltype) {
		default:
			break;
		case Ilsync:
			if(ack != ic->start)
				ilhangup(s, "connection rejected");
			else {
				ic->recvd = id;
				ic->rstart = id;
				ilsendctl(s, 0, Ilack, ic->next, ic->recvd);
				ic->state = Ilestablished;
				wakeup(&ic->syncer);
				ilpullup(s);
				Starttimer(ic);
			}
			break;
		case Ilclose:
			if(ack == ic->start)
				ilhangup(s, "remote close");
			break;
		}
		freeb(bp);
		break;
	case Ilsyncee:
		switch(h->iltype) {
		default:
			break;
		case Ilsync:
			if(id != ic->rstart || ack != 0)
				ic->state = Ilclosed;
			else {
				ic->recvd = id;
				ilsendctl(s, 0, Ilsync, ic->start, ic->recvd);
				Starttimer(ic);
			}
			break;
		case Ilack:
			if(ack == ic->start) {
				ic->state = Ilestablished;
				ilpullup(s);
				Starttimer(ic);
			}
			break;
		case Ilclose:
			if(id == ic->next)
				ilhangup(s, "remote close");
			break;
		}
		freeb(bp);
		break;
	case Ilestablished:
		switch(h->iltype) {
		case Ilsync:
			if(id != ic->rstart)
				ilhangup(s, "remote close");
			else {
				ilsendctl(s, 0, Ilack, ic->next, ic->rstart);
				Starttimer(ic);
			}
			freeb(bp);	
			break;
		case Ildata:
			Starttimer(ic);
			ilackto(ic, ack);
			ic->acktime = Acktime;
			iloutoforder(s, h, bp);
			ilpullup(s);
			break;
		case Ildataquery:
			Starttimer(ic);
			ilackto(ic, ack);
			ic->acktime = Acktime;
			iloutoforder(s, h, bp);
			ilpullup(s);
			ilsendctl(s, 0, Ilstate, ic->next, ic->recvd);
			break;
		case Ilack:
			ilackto(ic, ack);
			Starttimer(ic);
			freeb(bp);
			break;
		case Ilquerey:
			ilackto(ic, ack);
			ilsendctl(s, 0, Ilstate, ic->next, ic->recvd);
			Starttimer(ic);
			freeb(bp);
			break;
		case Ilstate:
			ilackto(ic, ack);
			ilrexmit(ic);
			Starttimer(ic);
			freeb(bp);
			break;
		case Ilclose:
			freeb(bp);
			if(ack < ic->start || ack > ic->next) 
				break;
			ilsendctl(s, 0, Ilclose, ic->next, ic->recvd);
			ic->state = Ilclosing;
			ilfreeq(ic);
			Starttimer(ic);
			break;
		}
		break;
	case Illistening:
		freeb(bp);
		break;
	case Ilclosing:
		switch(h->iltype) {
		case Ilclose:
			ic->recvd = id;
			ilsendctl(s, 0, Ilclose, ic->next, ic->recvd);
			if(ack == ic->next)
				ilhangup(s, 0);
			Starttimer(ic);
			break;
		default:
			break;
		}
		freeb(bp);
		break;
	}
}

void
ilrexmit(Ilcb *ic)
{
	Block *nb;
	Ilhdr *h;

	nb = 0;
	qlock(&ic->ackq);
	if(ic->unacked)
		nb = copyb(ic->unacked, blen(ic->unacked));
	qunlock(&ic->ackq);

	if(nb == 0)
		return;

	h = (Ilhdr*)nb->rptr;
	DBG("rxmit %d.", nhgetl(h->ilid));

	h->iltype = Ildataquery;
	hnputl(h->ilack, ic->recvd);
	h->ilsum[0] = 0;
	h->ilsum[1] = 0;
	if(ilcksum)
		hnputs(h->ilsum, ptcl_csum(nb, IL_EHSIZE, nhgets(h->illen)));

	PUTNEXT(Ipoutput, nb);
}

/* DEBUG */
void
ilprocess(Ipconv *s, Ilhdr *h, Block *bp)
{
	Ilcb *ic = &s->ilctl;

	USED(ic);
	DBG("%11s rcv %d/%d snt %d/%d pkt(%s id %d ack %d %d->%d) ",
		ilstate[ic->state],  ic->rstart, ic->recvd, ic->start, ic->next,
		iltype[h->iltype], nhgetl(h->ilid), nhgetl(h->ilack), 
		nhgets(h->ilsrc), nhgets(h->ildst));

	_ilprocess(s, h, bp);

	DBG("%11s rcv %d snt %d\n", ilstate[ic->state], ic->recvd, ic->next);
}

void
ilhangup(Ipconv *s, char *msg)
{
	Block *nb;
	int l;
	Ilcb *ic;
	int callout;

	DBG("hangup! %s %d/%d\n", msg ? msg : "??", s->psrc, s->pdst);

	ic = &s->ilctl;
	callout = ic->state == Ilsyncer;
	ic->state = Ilclosed;
	qlock(s);
	if(s->readq) {
		if(msg) {
			l = strlen(msg);
			nb = allocb(l);
			strcpy((char*)nb->wptr, msg);
			nb->wptr += l;
		}
		else
			nb = allocb(0);
		nb->type = M_HANGUP;
		nb->flags |= S_DELIM;
		PUTNEXT(s->readq, nb);
	}
	qunlock(s);
	if(callout)
		wakeup(&ic->syncer);

	s->psrc = 0;
}

void
ilpullup(Ipconv *s)
{
	Ilcb *ic;
	Ilhdr *oh;
	Block *bp;
	ulong oid, dlen;

	if(s->readq == 0)
		return;

	ic = &s->ilctl;
	if(ic->state != Ilestablished)
		return;

	qlock(&ic->outo);
	while(ic->outoforder) {
		bp = ic->outoforder;
		oh = (Ilhdr*)bp->rptr;
		oid = nhgetl(oh->ilid);
		if(oid <= ic->recvd) {
			ic->outoforder = bp->list;
			freeb(bp);
			continue;
		}
		if(oid != ic->recvd+1)
			break;

		ic->recvd = oid;
		ic->outoforder = bp->list;

		qunlock(&ic->outo);
		bp->list = 0;
		dlen = nhgets(oh->illen)-IL_HDRSIZE;
		bp = btrim(bp, IL_EHSIZE+IL_HDRSIZE, dlen);
		PUTNEXT(s->readq, bp);
		qlock(&ic->outo);
	}
	qunlock(&ic->outo);
}

void
iloutoforder(Ipconv *s, Ilhdr *h, Block *bp)
{
	Block *f, **l;
	Ilcb *ic;
	ulong id;
	uchar *lid;

	ic = &s->ilctl;
	bp->list = 0;


	id = nhgetl(h->ilid);
	/* Window checks */
	if(id <= ic->recvd || id > ic->recvd+ic->window) {
		freeb(bp);
		return;
	}

	/* Packet is acceptable so sort onto receive queue for pullup */
	qlock(&ic->outo);
	if(ic->outoforder == 0)
		ic->outoforder = bp;
	else {
		l = &ic->outoforder;
		for(f = *l; f; f = f->list) {
			lid = ((Ilhdr*)(f->rptr))->ilid;
			if(id < nhgetl(lid)) {
				bp->list = f;
				*l = bp;
				qunlock(&ic->outo);
				return;
			}
			l = &f->list;
		}
		*l = bp;
	}
	qunlock(&ic->outo);
}

void
ilsendctl(Ipconv *ipc, Ilhdr *inih, int type, ulong id, ulong ack)
{
	Ilhdr *ih;
	Ilcb *ic;
	Block *bp;

	bp = allocb(IL_EHSIZE+IL_HDRSIZE);
	bp->wptr += IL_EHSIZE+IL_HDRSIZE;
	bp->flags |= S_DELIM;

	ih = (Ilhdr *)(bp->rptr);
	ic = &ipc->ilctl;

	/* Ip fields */
	ih->proto = IP_ILPROTO;
	hnputl(ih->src, Myip[Myself]);
	hnputs(ih->illen, IL_HDRSIZE);
	ih->frag[0] = 0;
	ih->frag[1] = 0;
	if(inih) {
		hnputl(ih->dst, nhgetl(inih->src));
		hnputs(ih->ilsrc, nhgets(inih->ildst));
		hnputs(ih->ildst, nhgets(inih->ilsrc));
		hnputl(ih->ilid, nhgetl(inih->ilack));
		hnputl(ih->ilack, nhgetl(inih->ilid));
	}
	else {
		hnputl(ih->dst, ipc->dst);
		hnputs(ih->ilsrc, ipc->psrc);
		hnputs(ih->ildst, ipc->pdst);
		hnputl(ih->ilid, id);
		hnputl(ih->ilack, ack);
		ic->acktime = Ackkeepalive;
	}
	ih->iltype = type;
	ih->ilspec = 0;
	ih->ilsum[0] = 0;
	ih->ilsum[1] = 0;

	if(ilcksum)
		hnputs(ih->ilsum, ptcl_csum(bp, IL_EHSIZE, IL_HDRSIZE));

/*	DBG("\nctl(%s id %d ack %d %d->%d)\n",
		iltype[ih->iltype], nhgetl(ih->ilid), nhgetl(ih->ilack), 
		nhgets(ih->ilsrc), nhgets(ih->ildst));
*/
	PUTNEXT(Ipoutput, bp);
}

void
ilackproc(void *a)
{
	Ipifc *ifc;
	Ipconv **base, **p, **end, *s;
	Ilcb *ic;

	ifc = (Ipifc*)a;
	base = ifc->conv;
	end = &base[Nipconv];

	for(;;) {
		tsleep(&ilackr, return0, 0, Iltickms);
		for(p = base; p < end && *p; p++) {
			s = *p;
			ic = &s->ilctl;
			ic->timeout += Iltickms;
			switch(ic->state) {
			case Ilclosed:
			case Illistening:
				break;
			case Ilclosing:
				if(ic->timeout >= ic->fasttime) {
					ilsendctl(s, 0, Ilclose, ic->next, ic->recvd);
					ilbackoff(ic);
				}
				if(ic->timeout >= ic->slowtime)
					ilhangup(s, 0);
				break;
			case Ilsyncee:
			case Ilsyncer:
				if(ic->timeout >= ic->fasttime) {
					ilsendctl(s, 0, Ilsync, ic->start, ic->recvd);
					ilbackoff(ic);
				}
				if(ic->timeout >= ic->slowtime)
					ilhangup(s, etime);
				break;
			case Ilestablished:
				ic->acktime -= Iltickms;
				if(ic->acktime <= 0)
					ilsendctl(s, 0, Ilack, ic->next, ic->recvd);

				ic->querytime -= Iltickms;
				if(ic->querytime <= 0){
					ic->deathtime -= Querytime;
					if(ic->deathtime < 0){
						ilhangup(s, etime);
						break;
					}
					ilsendctl(s, 0, Ilquerey, ic->next, ic->recvd);
					ic->querytime = Querytime;
				}
				if(ic->unacked == 0) {
					ic->timeout = 0;
					break;
				}
				if(ic->timeout >= ic->fasttime) {
					ilrexmit(ic);
					ilbackoff(ic);
				}
				if(ic->timeout >= ic->slowtime) {
					ilhangup(s, etime);
					break;
				}
				break;
			}
		}
	}
}

void
ilbackoff(Ilcb *ic)
{
	if(ic->fasttime < Slowtime/2)
		ic->fasttime += Fasttime;
	else
		ic->fasttime = (ic->fasttime)*3/2;
}

static int
notsyncer(void *ic)
{
	Ilcb *i;

	i = ic;
	return i->state != Ilsyncer;
}

void
ilstart(Ipconv *ipc, int type, int window)
{
	Ilcb *ic = &ipc->ilctl;

	if(ic->state != Ilclosed)
		return;

	ic->unacked = 0;
	ic->outoforder = 0;
	ic->slowtime = Slowtime;
	ic->rtt = Iltickms;
	Starttimer(ic);

	initseq += TK2MS(MACHP(0)->ticks);
	ic->start = initseq & 0xffffff;
	ic->next = ic->start+1;
	ic->recvd = 0;
	ic->window = window;

	switch(type) {
	case IL_PASSIVE:
		ic->state = Illistening;
		break;
	case IL_ACTIVE:
		ic->state = Ilsyncer;
		ilsendctl(ipc, 0, Ilsync, ic->start, ic->recvd);
		sleep(&ic->syncer, notsyncer, ic);
		if(ic->state == Ilclosed)
			error(Etimedout);
		break;
	}
}

void
ilfreeq(Ilcb *ic)
{
	Block *bp, *next;

	qlock(&ic->ackq);
	for(bp = ic->unacked; bp; bp = next) {
		next = bp->list;
		freeb(bp);
	}
	ic->unacked = 0;
	qunlock(&ic->ackq);

	qlock(&ic->outo);
	for(bp = ic->outoforder; bp; bp = next) {
		next = bp->list;
		freeb(bp);
	}
	ic->outoforder = 0;
	qunlock(&ic->outo);
}

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

#define DPRINT if(pip)print
int pip = 0;
int ipcksum = 1;
int Id = 1;

Fragq		*flisthead;
Fragq		*fragfree;
QLock		fraglock;

Queue 		*Etherq;

Ipaddr		Myip[7];
Ipaddr		Mymask;
Ipaddr		Mynetmask;
uchar		Netmyip[4];	/* In Network byte order */
uchar		bcast[4] = { 0xff, 0xff, 0xff, 0xff };

/* 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*)xalloc(sizeof(Fragq) * size);

	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)
{
	Ipconv *ipc;

	/* First open is by ipconfig and sets up channel
	 * to ethernet
	 */
	if(!Etherq) {
		Etherq = WR(q);
		s->opens++;		/* Hold this queue in place */
		s->inuse++;
		ipsetaddrs();
	} else {
		ipc = ipcreateconv(ipifc[s->dev], s->id);
		RD(q)->ptr = (void *)ipc;
		WR(q)->ptr = (void *)ipc;
		ipc->ref = 1;
	}

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

/*
 *  initipifc - set parameters of an ip protocol interface
 */
void
initipifc(Ipifc *ifc, uchar ptcl, void (*recvfun)(Ipifc*, Block *bp), int max,
	int min, int hdrsize)
{
	qlock(ifc);
	ifc->iprcv = recvfun;

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

	ifc->protocol = ptcl;
	ifc->inited = 1;

	qunlock(ifc);
}

static void
ipetherclose(Queue *q)
{
	Ipconv *ipc;

	ipc = (Ipconv *)(q->ptr);
	if(ipc){
		netdisown(ipc);
		ipc->ref = 0;
	}
}

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(streamparse("setip", bp)) {
			if(strncmp(eve, up->user, sizeof(eve)) != 0)
				error(Eperm);
			ptr = bp->rptr;
			Myip[Myself] = ipparse((char *)ptr);
			Netmyip[0] = (Myip[Myself]>>24)&0xff;
			Netmyip[1] = (Myip[Myself]>>16)&0xff;
			Netmyip[2] = (Myip[Myself]>>8)&0xff;
			Netmyip[3] = Myip[Myself]&0xff;
			Mymask = classmask[Myip[Myself]>>30];
			while(*ptr != ' ' && *ptr)
				ptr++;
			if(*ptr)
				Mynetmask = ipparse((char *)ptr);
			else
				Mynetmask = Mymask;
			freeb(bp);
			ipsetaddrs();
		}
		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);
		hnputs(eh->id, Id++);
		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 type and pass down to the arp layer */
		hnputs(eh->type, ET_IP);
		PUTNEXT(Etherq, bp);
		return;
	}

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

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

	/* Make prototype output header */
	hnputs(eh->type, ET_IP);
	
	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 	 *ifc, **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 == 0)
			return;
	}

	/* Look to see if its for me before we waste time checksuming it */
	if(ipforme(h->dst) == 0)
		goto drop;


	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
	 */
	for(ifp = ipifc; ; ifp++){
		ifc = *ifp;
		if(ifc == 0)
			break;
		if(ifc->protocol == h->proto) {
			(*ifc->iprcv)(ifc, bp);
			return;
		}
	}

drop:
	freeb(bp);
}

void
ipsetaddrs(void)
{
	Myip[Mybcast] = ~0;				/* local broadcast */
	Myip[2] = 0;					/* local broadcast - old */
	Myip[Mysubnet] = Myip[Myself] | ~Mynetmask;	/* subnet broadcast */
	Myip[Mysubnet+1] = Myip[Myself] & Mynetmask;	/* subnet broadcast - old */
	Myip[Mynet] = Myip[Myself] | ~Mymask;		/* net broadcast */
	Myip[Mynet+1] = Myip[Myself] & Mymask;		/* net broadcast - old */
}

int
ipforme(uchar *addr)
{
	Ipaddr haddr;
	Ipaddr *p;

	haddr = nhgetl(addr);

	/* try my address plus all the forms of ip broadcast */
	for(p = Myip; p < &Myip[7]; p++)
		if(haddr == *p);
			return 1;

	/* if we haven't set an address yet, accept anything we get */
	if(Myip[Myself] == 0)
		return 1;

	return 0;
}

typedef struct Fragstat	Fragstat;
struct Fragstat
{
	ulong	prev;
	ulong	prevall;
	ulong	succ;
	ulong	succall;
};
Fragstat fragstat;

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, pktposn;
	int end;

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

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

	/*
	 *  find a reassembly queue for this fragment
	 */
	qlock(&fraglock);
	for(f = flisthead; f; f = f->next)
		if(f->src == src && f->dst == dst && f->id == id)
			break;
	qunlock(&fraglock);

	/*
	 *  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) {
		if(f != 0) {
			qlock(f);
			ipfragfree(f, 1);
		}
		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();
		qlock(f);
		f->id = id;
		f->src = src;
		f->dst = dst;

		f->blist = bp;

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

	/*
	 *  find the new fragment's position in the queue
	 */
	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) {
			fragstat.prev++;
			if(ovlap >= BLKFRAG(bp)->flen) {
				fragstat.prevall++;
				freeb(bp);
				qunlock(f);
				return 0;
			}
			BLKFRAG(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;
		end = BLKFRAG(bp)->foff + BLKFRAG(bp)->flen;
		/* Take completely covered segements out */
		while(*l){
			ovlap = end - BLKFRAG(*l)->foff;
			if(ovlap <= 0)
				break;
			fragstat.succ++;
			if(ovlap < BLKFRAG(*l)->flen) {
				fragstat.succall++;
				BLKFRAG(*l)->flen -= ovlap;
				BLKFRAG(*l)->foff += ovlap;
				/* move up ether+ip hdrs */
				memmove((*l)->rptr + ovlap, (*l)->rptr,
					 ETHER_HDR+ETHER_IPHDR);
				(*l)->rptr += ovlap;
				break;
			}
			last = (*l)->next;
			(*l)->next = 0;
			freeb(*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(BLKFRAG(bl)->foff != pktposn)
			break;
		if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0)
			goto complete;

		pktposn += BLKFRAG(bl)->flen;
	}
	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, 1);

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

	return(bl);		
}

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

void
ipfragfree(Fragq *frag, int lockq)
{
	Fragq *fl, **l;

	if(frag->blist)
		freeb(frag->blist);

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

	if(lockq)
		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;

	if(lockq)
		qunlock(&fraglock);
}

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

	qlock(&fraglock);
	while(fragfree == 0) {
		for(f = flisthead; f; f = f->next)
			if(canqlock(f)) {
				ipfragfree(f, 0);
				break;
			}
	}
	f = fragfree;
	fragfree = f->next;
	f->next = flisthead;
	flisthead = f;
	f->age = TK2MS(MACHP(0)->ticks) + 30000;

	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);
}

D port/streboot.c => port/streboot.c +0 -48
@@ 1,48 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"

/*
 *  reboot stream module definition
 */
static void rebootopen(Queue*, Stream*);
static void rebootiput(Queue*, Block*);
static void rebootoput(Queue*, Block*);
static void rebootreset(void);
Qinfo rebootinfo =
{
	rebootiput,
	rebootoput,
	rebootopen,
	0,
	"reboot",
	0
};

static void
rebootopen(Queue *q, Stream *s)
{
	USED(q);
	USED(s);
	if(strcmp(up->user, eve) != 0)
		error(Eperm);
}

void
rebootoput(Queue *q, Block *bp)
{
	PUTNEXT(q, bp);
}

static void
rebootiput(Queue *q, Block *bp)
{
	if(bp->type == M_HANGUP){
		print("lost connection to fs, rebooting");
		exit(0);
	}
	PUTNEXT(q, bp);
}

D port/stsplice.c => port/stsplice.c +0 -80
@@ 1,80 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#define	splicedebug	1

#define	DPRINT	if(splicedebug)kprint

/*
 * Splice line discipline.
 */

static void spliceopen(Queue*, Stream*);
static void spliceclose(Queue*);
static void spliceoput(Queue*, Block*);
static void spliceiput(Queue*, Block*);
Qinfo spliceinfo =
{
	spliceiput,
	spliceoput,
	spliceopen,
	spliceclose,
	"splice"
};

static void
spliceopen(Queue *q, Stream *s)
{
	DPRINT("spliceopen q=0x%ux s=0x%ux\n", q, s);

	WR(q)->ptr = s;		/* pointer to ourselves */
	RD(q)->ptr = 0;		/* pointer to other side */
}

static void
spliceclose(Queue *q)
{
	DPRINT("spliceclose q=0x%ux us=0x%ux them=0x%ux\n",
		q, WR(q)->ptr, RD(q)->ptr);

	RD(q)->ptr = 0;
	WR(q)->ptr = 0;
}

static void
spliceoput(Queue *q, Block *bp)
{
	int fd;
	Chan *c;
	Stream *s;

	if(bp->type != M_CTL){
		PUTNEXT(q, bp);
		return;
	}
	fd = strtol((char *)bp->rptr, 0, 0);
	freeb(bp);
	if(RD(q)->ptr)
		error("stream already spliced");
	c = fdtochan(fd, ORDWR, 0, 0);
	s = c->stream;
	if(s == 0)
		error("splice attempt on non-stream");
	pushq(s, &spliceinfo);
	RD(q)->ptr = s;
	RD(s->procq->next)->ptr = WR(q)->ptr;
}

static void
spliceiput(Queue *q, Block *bp)
{
	if(bp->type == M_HANGUP)
		PUTNEXT(q, bp);
	else
		FLOWCTL(((Stream *)q->ptr)->procq, bp);
}

D port/sturp.c => port/sturp.c +0 -1072
@@ 1,1072 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

enum {
	MSrexmit=	1000,
	Nmask=		0x7,
};

#define DPRINT if(q->flag&QDEBUG)kprint

typedef struct Urp	Urp;

#define NOW (MACHP(0)->ticks*MS2HZ)

/*
 * URP status
 */
struct urpstat {
	ulong	input;		/* bytes read from urp */
	ulong	output;		/* bytes output to urp */
	ulong	rexmit;		/* retransmit rejected urp msg */
	ulong	rjtrs;		/* reject, trailer size */
	ulong	rjpks;		/* reject, packet size */
	ulong	rjseq;		/* reject, sequence number */
	ulong	levelb;		/* unknown level b */
	ulong	enqsx;		/* enqs sent */
	ulong	enqsr;		/* enqs rcved */
} urpstat;

struct Urp {
	QLock;
	Urp	*list;		/* list of all urp structures */
	short	state;		/* flags */
	Rendez	r;		/* process waiting for output to finish */

	/* input */
	QLock	ack;		/* ack lock */
	Queue	*rq;		/* input queue */
	uchar	iseq;		/* last good input sequence number */
	uchar	lastecho;	/* last echo/rej sent */
	uchar	trbuf[3];	/* trailer being collected */
	short	trx;		/* # bytes in trailer being collected */
	int	blocks;

	/* output */
	QLock	xmit;		/* output lock, only one process at a time */
	Queue	*wq;		/* output queue */
	int	maxout;		/* maximum outstanding unacked blocks */
	int	maxblock;	/* max block size */
	int	next;		/* next block to send */
	int	unechoed;	/* first unechoed block */
	int	unacked;	/* first unacked block */
	int	nxb;		/* next xb to use */
	Block	*xb[8];		/* the xmit window buffer */
	QLock	xl[8];
	ulong	timer;		/* timeout for xmit */
	int	rexmit;
};

/* list of allocated urp structures (never freed) */
struct
{
	Lock;
	Urp	*urp;
} urpalloc;

Rendez	urpkr;
QLock	urpkl;
int	urpkstarted;

#define WINDOW(u) ((u)->unechoed>(u)->next ? (u)->unechoed+(u)->maxout-(u)->next-8 :\
			(u)->unechoed+(u)->maxout-(u)->next)
#define IN(x, f, n) (f<=n ? (x>=f && x<n) : (x<n || x>=f))
#define NEXT(x) (((x)+1)&Nmask)

/*
 *  Protocol control bytes
 */
#define	SEQ	0010		/* sequence number, ends trailers */
#undef	ECHO
#define	ECHO	0020		/* echos, data given to next queue */
#define	REJ	0030		/* rejections, transmission error */
#define	ACK	0040		/* acknowledgments */
#define	BOT	0050		/* beginning of trailer */
#define	BOTM	0051		/* beginning of trailer, more data follows */
#define	BOTS	0052		/* seq update algorithm on this trailer */
#define	SOU	0053		/* start of unsequenced trailer */
#define	EOU	0054		/* end of unsequenced trailer */
#define	ENQ	0055		/* xmitter requests flow/error status */
#define	CHECK	0056		/* xmitter requests error status */
#define	INITREQ 0057		/* request initialization */
#define	INIT0	0060		/* disable trailer processing */
#define	INIT1	0061		/* enable trailer procesing */
#define	AINIT	0062		/* response to INIT0/INIT1 */
#undef	DELAY
#define	DELAY	0100		/* real-time printing delay */
#define	BREAK	0110		/* Send/receive break (new style) */

#define	REJECTING	0x1
#define	INITING 	0x2
#define HUNGUP		0x4
#define	OPEN		0x8
#define CLOSING		0x10

/*
 *  predeclared
 */
static void	urpreset(void);
static void	urpciput(Queue*, Block*);
static void	urpiput(Queue*, Block*);
static void	urpoput(Queue*, Block*);
static void	urpopen(Queue*, Stream*);
static void	urpclose(Queue *);
static void	output(Urp*);
static void	sendblock(Urp*, int);
static void	rcvack(Urp*, int);
static void	flushinput(Urp*);
static void	sendctl(Urp*, int);
static void	sendack(Urp*);
static void	sendrej(Urp*);
static void	initoutput(Urp*, int);
static void	initinput(Urp*);
static void	urpkproc(void *arg);
static void	urpvomit(char*, Urp*);
static void	tryoutput(Urp*);

Qinfo urpinfo =
{
	urpciput,
	urpoput,
	urpopen,
	urpclose,
	"urp",
	urpreset
};

static void
urpreset(void)
{
}

static void
urpopen(Queue *q, Stream *s)
{
	Urp *urp;

	USED(s);
	if(!urpkstarted){
		qlock(&urpkl);
		if(!urpkstarted){
			urpkstarted = 1;
			kproc("urpkproc", urpkproc, 0);
		}
		qunlock(&urpkl);
	}

	/*
	 *  find an unused urp structure
	 */
	for(urp = urpalloc.urp; urp; urp = urp->list){
		if(urp->state == 0){
			qlock(urp);
			if(urp->state == 0)
				break;
			qunlock(urp);
		}
	}
	if(urp == 0){
		/*
		 *  none available, create a new one, they are never freed
		 */
		urp = smalloc(sizeof(Urp));
		qlock(urp);
		lock(&urpalloc);
		urp->list = urpalloc.urp;
		urpalloc.urp = urp;
		unlock(&urpalloc);
	}
	q->ptr = q->other->ptr = urp;
	q->rp = &urpkr;
	urp->rq = q;
	urp->wq = q->other;
	urp->state = OPEN;
	qunlock(urp);
	initinput(urp);
	initoutput(urp, 0);
}

/*
 *  Shut down the connection and kill off the kernel process
 */
static int
isflushed(void *a)
{
	Urp *urp;

	urp = (Urp *)a;
	return (urp->state&HUNGUP) || (urp->unechoed==urp->nxb && urp->wq->len==0);
}
static void
urpclose(Queue *q)
{
	Urp *urp;
	int i;

	urp = (Urp *)q->ptr;
	if(urp == 0)
		return;

	/*
	 *  wait for all outstanding messages to drain, tell kernel
	 *  process we're closing.
	 *
	 *  if 2 minutes elapse, give it up
	 */
	urp->state |= CLOSING;
	if(!waserror()){
		tsleep(&urp->r, isflushed, urp, 2*60*1000);
		poperror();
	}
	urp->state |= HUNGUP;

	qlock(&urp->xmit);
	/*
	 *  ack all outstanding messages
	 */
	i = urp->next - 1;
	if(i < 0)
		i = 7;
	rcvack(urp, ECHO+i);

	/*
	 *  free all staged but unsent messages
	 */
	for(i = 0; i < 7; i++)
		if(urp->xb[i]){
			freeb(urp->xb[i]);
			urp->xb[i] = 0;
		}
	qunlock(&urp->xmit);

	qlock(urp);
	urp->state = 0;
	qunlock(urp);
}

/*
 *  upstream control messages
 */
static void
urpctliput(Urp *urp, Queue *q, Block *bp)
{
	switch(bp->type){
	case M_HANGUP:
		urp->state |= HUNGUP;
		wakeup(&urp->r);
		break;
	}
	PUTNEXT(q, bp);
}

/*
 *  character mode input.
 *
 *  the first byte in every message is a ctl byte (which belongs at the end).
 */
void
urpciput(Queue *q, Block *bp)
{
	Urp *urp;
	int i;
	int ctl;

	urp = (Urp *)q->ptr;
	if(urp == 0)
		return;
	if(bp->type != M_DATA){
		urpctliput(urp, q, bp);
		return;
	}

	/*
	 *  get the control character
	 */
	ctl = *bp->rptr++;
	if(ctl < 0)
		return;

	/*
	 *  take care of any data
	 */
	if(BLEN(bp)>0  && q->next->len<2*Streamhi && q->next->nb<2*Streambhi){
		bp->flags |= S_DELIM;
		urpstat.input += BLEN(bp);
		PUTNEXT(q, bp);
	} else
		freeb(bp);

	/*
	 *  handle the control character
	 */
	switch(ctl){
	case 0:
		break;
	case ENQ:
		DPRINT("rENQ(c)\n");
		urpstat.enqsr++;
		sendctl(urp, urp->lastecho);
		sendctl(urp, ACK+urp->iseq);
		break;

	case CHECK:
		DPRINT("rCHECK(c)\n");
		sendctl(urp, ACK+urp->iseq);
		break;

	case AINIT:
		DPRINT("rAINIT(c)\n");
		urp->state &= ~INITING;
		flushinput(urp);
		tryoutput(urp);
		break;

	case INIT0:
	case INIT1:
		DPRINT("rINIT%d(c)\n", ctl-INIT0);
		sendctl(urp, AINIT);
		if(ctl == INIT1)
			q->put = urpiput;
		initinput(urp);
		break;

	case INITREQ:
		DPRINT("rINITREQ(c)\n");
		initoutput(urp, 0);
		break;

	case BREAK:
		break;

	case REJ+0: case REJ+1: case REJ+2: case REJ+3:
	case REJ+4: case REJ+5: case REJ+6: case REJ+7:
		DPRINT("rREJ%d(c)\n", ctl-REJ);
		rcvack(urp, ctl);
		break;
	
	case ACK+0: case ACK+1: case ACK+2: case ACK+3:
	case ACK+4: case ACK+5: case ACK+6: case ACK+7:
	case ECHO+0: case ECHO+1: case ECHO+2: case ECHO+3:
	case ECHO+4: case ECHO+5: case ECHO+6: case ECHO+7:
		DPRINT("%s%d(c)\n", (ctl&ECHO)?"rECHO":"rACK", ctl&7);
		rcvack(urp, ctl);
		break;

	case SEQ+0: case SEQ+1: case SEQ+2: case SEQ+3:
	case SEQ+4: case SEQ+5: case SEQ+6: case SEQ+7:
		DPRINT("rSEQ%d(c)\n", ctl-SEQ);
		qlock(&urp->ack);
		i = ctl & Nmask;
		if(!QFULL(q->next))
			sendctl(urp, urp->lastecho = ECHO+i);
		urp->iseq = i;
		qunlock(&urp->ack);
		break;
	}
}

/*
 *  block mode input.
 *
 *  the first byte in every message is a ctl byte (which belongs at the end).
 *
 *  Simplifying assumption:  one put == one message && the control byte
 *	is in the first block.  If this isn't true, strange bytes will be
 *	used as control bytes.
 *
 *	There's no input lock.  The channel could be closed while we're
 *	processing a message.
 */
void
urpiput(Queue *q, Block *bp)
{
	Urp *urp;
	int i, len;
	int ctl;

	urp = (Urp *)q->ptr;
	if(urp == 0)
		return;
	if(bp->type != M_DATA){
		urpctliput(urp, q, bp);
		return;
	}

	/*
	 *  get the control character
	 */
	ctl = *bp->rptr++;

	/*
	 *  take care of any block count(trx)
	 */
	while(urp->trx){
		if(BLEN(bp)<=0)
			break;
		switch (urp->trx) {
		case 1:
		case 2:
			urp->trbuf[urp->trx++] = *bp->rptr++;
			continue;
		default:
			urp->trx = 0;
			break;
		}
	}

	/*
	 *  queue the block(s)
	 */
	if(BLEN(bp) > 0){
		bp->flags &= ~S_DELIM;
		putq(q, bp);
		if(q->len > 4*1024){
			flushinput(urp);
			return;
		}
	} else
		freeb(bp);

	/*
	 *  handle the control character
	 */
	switch(ctl){
	case 0:
		break;
	case ENQ:
		DPRINT("rENQ %d %uo %uo\n", urp->blocks, urp->lastecho, ACK+urp->iseq);
		urp->blocks = 0;
		urpstat.enqsr++;
		sendctl(urp, urp->lastecho);
		sendctl(urp, ACK+urp->iseq);
		flushinput(urp);
		break;

	case CHECK:
		DPRINT("rCHECK\n");
		sendctl(urp, ACK+urp->iseq);
		break;

	case AINIT:
		DPRINT("rAINIT\n");
		urp->state &= ~INITING;
		flushinput(urp);
		tryoutput(urp);
		break;

	case INIT0:
	case INIT1:
		DPRINT("rINIT%d\n", ctl-INIT0);
		sendctl(urp, AINIT);
		if(ctl == INIT0)
			q->put = urpciput;
		initinput(urp);
		break;

	case INITREQ:
		DPRINT("rINITREQ\n");
		initoutput(urp, 0);
		break;

	case BREAK:
		break;

	case BOT:
	case BOTM:
	case BOTS:
		DPRINT("rBOT%c...", " MS"[ctl-BOT]);
		urp->trx = 1;
		urp->trbuf[0] = ctl;
		break;

	case REJ+0: case REJ+1: case REJ+2: case REJ+3:
	case REJ+4: case REJ+5: case REJ+6: case REJ+7:
		DPRINT("rREJ%d\n", ctl-REJ);
		rcvack(urp, ctl);
		break;
	
	case ACK+0: case ACK+1: case ACK+2: case ACK+3:
	case ACK+4: case ACK+5: case ACK+6: case ACK+7:
	case ECHO+0: case ECHO+1: case ECHO+2: case ECHO+3:
	case ECHO+4: case ECHO+5: case ECHO+6: case ECHO+7:
		DPRINT("%s%d\n", (ctl&ECHO)?"rECHO":"rACK", ctl&7);
		rcvack(urp, ctl);
		break;

	/*
	 *  if the sequence number is the next expected
	 *	and the trailer length == 3
	 *	and the block count matches the bytes received
	 *  then send the bytes upstream.
	 */
	case SEQ+0: case SEQ+1: case SEQ+2: case SEQ+3:
	case SEQ+4: case SEQ+5: case SEQ+6: case SEQ+7:
		len = urp->trbuf[1] + (urp->trbuf[2]<<8);
		DPRINT("rSEQ%d(%d,%d,%d)...", ctl-SEQ, urp->trx, len, q->len);
		i = ctl & Nmask;
		if(urp->trx != 3){
			urpstat.rjtrs++;
			sendrej(urp);
			break;
		}else if(q->len != len){
			urpstat.rjpks++;
			sendrej(urp);
			break;
		}else if(i != ((urp->iseq+1)&Nmask)){
			urpstat.rjseq++;
			sendrej(urp);
			break;
		}else if(q->next->len > (3*Streamhi)/2
			|| q->next->nb > (3*Streambhi)/2){
			DPRINT("next->len=%d, next->nb=%d\n",
				q->next->len, q->next->nb);
			flushinput(urp);
			break;
		}
		DPRINT("accept %d\n", q->len);

		/*
		 *  send data upstream
		 */
		if(q->first) {
			if(urp->trbuf[0] != BOTM)
				q->last->flags |= S_DELIM;
			while(bp = getq(q)){
				urpstat.input += BLEN(bp);
				PUTNEXT(q, bp);
			}
		} else {
			bp = allocb(0);
			if(urp->trbuf[0] != BOTM)
				bp->flags |= S_DELIM;
			PUTNEXT(q, bp);
		}
		urp->trx = 0;

		/*
		 *  acknowledge receipt
		 */
		qlock(&urp->ack);
		urp->iseq = i;
		if(!QFULL(q->next))
			sendctl(urp, urp->lastecho = ECHO|i);
		qunlock(&urp->ack);
		break;
	}
}

/*
 *  downstream control
 */
static void
urpctloput(Urp *urp, Queue *q, Block *bp)
{
	char *fields[2];
	int outwin;

	switch(bp->type){
	case M_CTL:
		if(streamparse("break", bp)){
			/*
			 *  send a break as part of the data stream
			 */
			urpstat.output++;
			bp->wptr = bp->lim;
			bp->rptr = bp->wptr - 1;
			*bp->rptr = BREAK;
			putq(q, bp);
			output(urp);
			return;
		}
		if(streamparse("init", bp)){
			outwin = strtoul((char*)bp->rptr, 0, 0);
			initoutput(urp, outwin);
			freeb(bp);
			return;
		}
		if(streamparse("debug", bp)){
			switch(getfields((char *)bp->rptr, fields, 2, ' ')){
			case 1:
				if (strcmp(fields[0], "on") == 0) {
					q->flag |= QDEBUG;
					q->other->flag |= QDEBUG;
				}
				if (strcmp(fields[0], "off") == 0) {
					q->flag &= ~QDEBUG;
					q->other->flag &= ~QDEBUG;
				}
			}
			freeb(bp);
			return;
		}
	}
	PUTNEXT(q, bp);
}

/*
 *  accept data from a writer
 */
static void
urpoput(Queue *q, Block *bp)
{
	Urp *urp;

	urp = (Urp *)q->ptr;

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

	urpstat.output += BLEN(bp);
	putq(q, bp);
	output(urp);
}

/*
 *  start output
 */
static void
output(Urp *urp)
{
	Block *bp, *nbp;
	ulong now;
	Queue *q;
	int i;

	if(!canqlock(&urp->xmit))
		return;

	if(waserror()){
		print("urp output error\n");
		qunlock(&urp->xmit);
		nexterror();
	}

	/*
	 *  if still initing and it's time to rexmit, send an INIT1
	 */
	now = NOW;
	if(urp->state & INITING){
		if(now > urp->timer){
			q = urp->wq;
			DPRINT("INITING timer (%d, %d): ", now, urp->timer);
			sendctl(urp, INIT1);
			urp->timer = now + MSrexmit;
		}
		goto out;
	}

	/*
	 *  fill the transmit buffers, `nxb' can never overtake `unechoed'
	 */
	q = urp->wq;
	i = NEXT(urp->nxb);
	if(i != urp->unechoed) {
		for(bp = getq(q); bp && i!=urp->unechoed; i = NEXT(i)){
			if(urp->xb[urp->nxb] != 0)
				urpvomit("output", urp);
			if(BLEN(bp) > urp->maxblock){
				nbp = urp->xb[urp->nxb] = allocb(0);
				nbp->rptr = bp->rptr;
				nbp->wptr = bp->rptr = bp->rptr + urp->maxblock;
			} else {
				urp->xb[urp->nxb] = bp;
				bp = getq(q);
			}
			urp->nxb = i;
		}
		if(bp)
			putbq(q, bp);
	}

	/*
	 *  retransmit cruft
	 */
	if(urp->rexmit){
		/*
		 *  if a retransmit is requested, move next back to
		 *  the unacked blocks
		 */
		urpstat.rexmit++;
		urp->rexmit = 0;
		urp->next = urp->unacked;
	} else if(urp->unechoed!=urp->next && NOW>urp->timer){
		/*
		 *  if a retransmit time has elapsed since a transmit,
		 *  send an ENQ
		 */
		DPRINT("OUTPUT timer (%d, %d): ", NOW, urp->timer);
		urp->timer = NOW + MSrexmit;
		urp->state &= ~REJECTING;
		urpstat.enqsx++;
		sendctl(urp, ENQ);
		goto out;
	}

	/*
	 *  if there's a window open, push some blocks out
	 *
	 *  the lock is to synchronize with acknowledges that free
	 *  blocks.
	 */
	while(WINDOW(urp)>0 && urp->next!=urp->nxb){
		i = urp->next;
		qlock(&urp->xl[i]);
		if(waserror()){
			qunlock(&urp->xl[i]);
			nexterror();
		}
		sendblock(urp, i);
		qunlock(&urp->xl[i]);
		urp->next = NEXT(urp->next);
		poperror();
	}
out:
	qunlock(&urp->xmit);
	poperror();
}

/*
 *  try output, this is called by an input process
 */
void
tryoutput(Urp *urp)
{
	if(!waserror()){
		output(urp);
		poperror();
	}
}

/*
 *  send a control byte, put the byte at the end of the allocated
 *  space in case a lower layer needs header room.
 */
static void
sendctl(Urp *urp, int ctl)
{
	Block *bp;
	Queue *q;

	q = urp->wq;
	if(QFULL(q->next))
		return;
	bp = allocb(1);
	bp->wptr = bp->lim;
	bp->rptr = bp->lim-1;
	*bp->rptr = ctl;
	bp->flags |= S_DELIM;
	DPRINT("sCTL %ulx\n", ctl);
	PUTNEXT(q, bp);
}

/*
 *  send a reject
 */
static void
sendrej(Urp *urp)
{
	Queue *q = urp->wq;
	flushinput(urp);
	qlock(&urp->ack);
	if((urp->lastecho&~Nmask) == ECHO){
		DPRINT("REJ %d\n", urp->iseq);
		sendctl(urp, urp->lastecho = REJ|urp->iseq);
	}
	qunlock(&urp->ack);
}

/*
 *  send an acknowledge
 */
static void
sendack(Urp *urp)
{
	/*
	 *  check the precondition for acking
	 */
	if(QFULL(urp->rq->next) || (urp->lastecho&Nmask)==urp->iseq)
		return;

	if(!canqlock(&urp->ack))
		return;

	/*
	 *  check again now that we've locked
	 */
	if(QFULL(urp->rq->next) || (urp->lastecho&Nmask)==urp->iseq){
		qunlock(&urp->ack);
		return;
	}

	/*
	 *  send the ack
	 */
	{ Queue *q = urp->wq; DPRINT("sendack: "); }
	sendctl(urp, urp->lastecho = ECHO|urp->iseq);
	qunlock(&urp->ack);
}

/*
 *  send a block.
 */
static void
sendblock(Urp *urp, int bn)
{
	int n;
	Queue *q;
	Block *bp, *m, *nbp;

	q = urp->wq;
	urp->timer = NOW + MSrexmit;
	if(QFULL(q->next))
		return;

	/*
	 *  message 1, the BOT and the data
	 */
	bp = urp->xb[bn];
	if(bp == 0)
		return;
	m = allocb(1);
	m->rptr = m->lim - 1;
	m->wptr = m->lim;
	*m->rptr = (bp->flags & S_DELIM) ? BOT : BOTM;
	nbp = allocb(0);
	nbp->rptr = bp->rptr;
	nbp->wptr = bp->wptr;
	nbp->base = bp->base;
	nbp->lim = bp->lim;
	nbp->flags |= S_DELIM;
	if(bp->type == M_CTL){
		PUTNEXT(q, nbp);
		m->flags |= S_DELIM;
		PUTNEXT(q, m);
	} else {
		m->next = nbp;
		PUTNEXT(q, m);
	}

	/*
	 *  message 2, the block length and the SEQ
	 */
	m = allocb(3);
	m->rptr = m->lim - 3;
	m->wptr = m->lim;
	n = BLEN(bp);
	m->rptr[0] = SEQ | bn;
	m->rptr[1] = n;
	m->rptr[2] = n<<8;
	m->flags |= S_DELIM;
	PUTNEXT(q, m);
	DPRINT("sb %d (%d)\n", bn, urp->timer);
}

/*
 *  receive an acknowledgement
 */
static void
rcvack(Urp *urp, int msg)
{
	int seqno;
	int next;
	int i;

	seqno = msg&Nmask;
	next = NEXT(seqno);

	/*
	 *  release any acknowledged blocks
	 */
	if(IN(seqno, urp->unacked, urp->next)){
		for(; urp->unacked != next; urp->unacked = NEXT(urp->unacked)){
			i = urp->unacked;
			qlock(&urp->xl[i]);
			if(urp->xb[i])
				freeb(urp->xb[i]);
			else
				urpvomit("rcvack", urp);
			urp->xb[i] = 0;
			qunlock(&urp->xl[i]);
		}
	}

	switch(msg & 0370){
	case ECHO:
		if(IN(seqno, urp->unechoed, urp->next)) {
			urp->unechoed = next;
		}
		/*
		 *  the next reject at the start of a window starts a 
		 *  retransmission.
		 */
		urp->state &= ~REJECTING;
		break;
	case REJ:
		if(IN(seqno, urp->unechoed, urp->next))
			urp->unechoed = next;
		/*
		 *  ... FALL THROUGH ...
		 */
	case ACK:
		/*
		 *  start a retransmission if we aren't retransmitting
		 *  and this is the start of a window.
		 */
		if(urp->unechoed==next && !(urp->state & REJECTING)){
			urp->state |= REJECTING;
			urp->rexmit = 1;
		}
		break;
	}

	tryoutput(urp);
	if(urp->state & CLOSING)
		wakeup(&urp->r);
}

/*
 * throw away any partially collected input
 */
static void
flushinput(Urp *urp)
{
	Block *bp;

	while (bp = getq(urp->rq))
		freeb(bp);
	urp->trx = 0;
}

/*
 *  initialize output
 */
static void
initoutput(Urp *urp, int window)
{
	int i;

	/*
	 *  set output window
	 */
	urp->maxblock = window/4;
	if(urp->maxblock < 64)
		urp->maxblock = 64;
	urp->maxblock -= 4;
	urp->maxout = 4;

	/*
	 *  set sequence varialbles
	 */
	urp->unechoed = 1;
	urp->unacked = 1;
	urp->next = 1;
	urp->nxb = 1;
	urp->rexmit = 0;

	/*
	 *  free any outstanding blocks
	 */
	for(i = 0; i < 8; i++){
		qlock(&urp->xl[i]);
		if(urp->xb[i])
			freeb(urp->xb[i]);
		urp->xb[i] = 0;
		qunlock(&urp->xl[i]);
	}

	/*
	 *  tell the other side we've inited
	 */
	urp->state |= INITING;
	urp->timer = NOW + MSrexmit;
	{ Queue *q = urp->wq; DPRINT("initoutput (%d): ", urp->timer); }
	sendctl(urp, INIT1);
}

/*
 *  initialize input
 */
static void
initinput(Urp *urp)
{
	/*
	 *  restart all sequence parameters
	 */
	urp->blocks = 0;
	urp->trx = 0;
	urp->iseq = 0;
	urp->lastecho = ECHO+0;
	flushinput(urp);
}

static void
urpkproc(void *arg)
{
	Urp *urp;

	USED(arg);

	if(waserror())
		;

	for(;;){
		for(urp = urpalloc.urp; urp; urp = urp->list){
			if(urp->state==0 || (urp->state&HUNGUP))
				continue;
			if(!canqlock(urp))
				continue;
			if(waserror()){
				qunlock(urp);
				continue;
			}
			if(urp->state==0 || (urp->state&HUNGUP)){
				qunlock(urp);
				poperror();
				continue;
			}
			if(urp->iseq!=(urp->lastecho&7) && !QFULL(urp->rq->next))
				sendack(urp);
			output(urp);
			qunlock(urp);
			poperror();
		}
		tsleep(&urpkr, return0, 0, 500);
	}
}

/*
 *  urp got very confused, complain
 */
static void
urpvomit(char *msg, Urp* urp)
{
	print("urpvomit: %s %ux next %d unechoed %d unacked %d nxb %d\n",
		msg, urp, urp->next, urp->unechoed, urp->unacked, urp->nxb);
	print("\txb: %ux %ux %ux %ux %ux %ux %ux %ux\n",
		urp->xb[0], urp->xb[1], urp->xb[2], urp->xb[3], urp->xb[4], 
		urp->xb[5], urp->xb[6], urp->xb[7]);
	print("\tiseq: %uo lastecho: %uo trx: %d trbuf: %uo %uo %uo\n",
		urp->iseq, urp->lastecho, urp->trx, urp->trbuf[0], urp->trbuf[1],
		urp->trbuf[2]);
	print("\tupq: %ux %d %d\n", &urp->rq->next->r,  urp->rq->next->nb,
		urp->rq->next->len);
}

void
urpfillstats(Chan *c, char *buf, int len)
{
	char b[256];

	USED(c);
	sprint(b, "in: %d\nout: %d\nrexmit: %d\nrjtrs: %d\nrjpks: %d\nrjseq: %d\nenqsx: %d\nenqsr: %d\n",
		urpstat.input, urpstat.output, urpstat.rexmit, urpstat.rjtrs,
		urpstat.rjpks, urpstat.rjseq, urpstat.enqsx, urpstat.enqsr);
	strncpy(buf, b, len);
}

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

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

void
tcpxstate(Ipconv *s, char oldstate, char newstate)
{
	int len;
	Block *bp;
	Tcpctl *tcb;

	if(oldstate == newstate)
		return;

	tcb = &s->tcpctl;
	switch(newstate) {
	case Closed:
		s->psrc = 0;		/* This connection is toast */
		s->pdst = 0;
		s->dst = 0;

	case Close_wait:		/* Remote closes */
		if(s->err) {
			len = strlen(s->err);
			bp = allocb(len);
			strcpy((char *)bp->wptr, s->err);
			bp->wptr += len;
		}
		else
			bp = allocb(0);

		bp->flags |= S_DELIM;
		bp->type = M_HANGUP;
		qlock(s);
		if(waserror()) {
			qunlock(s);
			nexterror();
		}
		if(s->readq == 0) {
			if(newstate == Close_wait)
				putb(&tcb->rcvq, bp);
			else
				freeb(bp);
		} else
			PUTNEXT(s->readq, bp);
		poperror();
		qunlock(s);
		break;
	}

	if(oldstate == Syn_sent)
		wakeup(&tcb->syner);
}

static int
notsyner(void *ic)
{
	return ((Tcpctl*)ic)->state != Syn_sent;
}

void
tcpstart(Ipconv *s, int mode, ushort window, char tos)
{
	Tcpctl *tcb;

	tcb = &s->tcpctl;
	if(tcb->state != Closed)
		return;

	init_tcpctl(s);

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

	switch(mode){
	case TCP_PASSIVE:
		tcb->flags |= CLONE;
		tcpsetstate(s, Listen);
		break;

	case TCP_ACTIVE:
		/* Send SYN, go into SYN_SENT state */
		tcb->flags |= ACTIVE;
		qlock(tcb);
		if(waserror()) {
			qunlock(tcb);
			nexterror();
		}
		tcpsndsyn(tcb);
		tcpsetstate(s, Syn_sent);
		tcpoutput(s);
		poperror();
		qunlock(tcb);
		tsleep(&tcb->syner, notsyner, tcb, 120*1000);
		if(tcb->state != Established && tcb->state != Syn_received)
			error(Etimedout);
		break;
	}
}

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

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

#define DPRINT	if(tcpdbg) print
#define LPRINT  if(tcpdbg) print

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

void
sndrst(Ipaddr source, Ipaddr dest, 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;

	/* convince the other end that this reset is in band */
	if(seg->flags & ACK) {
		seg->seq = seg->ack;
		seg->ack = 0;
	}
	else {
		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;

	PUTNEXT(Ipoutput, hbp);
}

/*
 *  flush an incoming call; send a reset to the remote side and close the
 *  conversation
 */
void
tcpflushincoming(Ipconv *s)
{
	Tcp seg;
	Tcpctl *tcb;		

	tcb = &s->tcpctl;
	seg.source = s->pdst;
	seg.dest = s->psrc;
	seg.flags = ACK;	
	seg.seq = tcb->snd.ptr;
	seg.ack = tcb->last_ack = tcb->rcv.nxt;

	sndrst(s->dst, Myip[Myself], 0, &seg);
	localclose(s, 0);
}

static void
tcpmove(struct Tctl *to, struct Tctl *from)
{
	memmove(to, from, sizeof(struct Tctl));
}

Ipconv*
tcpincoming(Ipifc *ifc, Ipconv *s, Tcp *segp, Ipaddr source)
{
	Ipconv *new;

	qlock(s);
	if(s->curlog >= s->backlog){
		qunlock(s);
		return 0;
	}

	new = ipincoming(ifc, s);
	if(new == 0){
		qunlock(s);
		return 0;
	}

	s->curlog++;
	qunlock(s);
	new->psrc = segp->dest;
	new->pdst = segp->source;
	new->dst = source;
	tcpmove(&new->tcpctl, &s->tcpctl);
	new->tcpctl.flags &= ~CLONE;
	new->tcpctl.timer.arg = new;
	new->tcpctl.timer.state = TimerOFF;
	new->tcpctl.acktimer.arg = new;
	new->tcpctl.acktimer.state = TimerOFF;
	new->newcon = s;

	wakeup(&s->listenr);
	return new;
}

void
tcpinput(Ipifc *ifc, Block *bp)
{
	Tcp seg;
	char tos;
	Tcphdr *h;
	int hdrlen;	
	Tcpctl *tcb;		
	ushort length;
	Ipconv *spec, *gen;
	Ipaddr source, dest;
	Ipconv *s, **p, **etab;

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

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

	h->Unused = 0;
	hnputs(h->tcplen, length - (TCP_IPLEN+TCP_PHDRSIZE));
	if(ptcl_csum(bp, TCP_EHSIZE+TCP_IPLEN, length - TCP_IPLEN)) {
		freeb(bp);
		return;
	}

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

	/* trim the packet to the size claimed by the datagram */
	length -= (hdrlen+TCP_IPLEN+TCP_PHDRSIZE);
	bp = btrim(bp, hdrlen+TCP_PKT, length);
	if(bp == 0)
		return;
	
	/* Look for a connection. failing that look for a listener. */
	s = ip_conn(ifc, seg.dest, seg.source, source);
	if (s == 0) {
		if(seg.flags & SYN){
			/*
			 *  find a listener specific to this port (spec) or,
			 *  failing that, a general one (gen)
			 */
			spec = 0;
			gen = 0;
			etab = &ifc->conv[Nipconv];
			for(p = ifc->conv; p < etab && *p; p++) {
				s = *p;
				if(s->tcpctl.state == Listen)
				if(s->pdst == 0)
				if(s->dst == 0) {
					if(s->psrc == seg.dest){
						spec = s;
						break;
					}
					if(s->psrc == 0)
						gen = s;
				}
			}
			if(spec)
				s = tcpincoming(ifc, spec, &seg, source);
			else if(gen)
				s = tcpincoming(ifc, gen, &seg, source);
			else
				s = 0;
		}
		if(s == 0){
			freeb(bp);   
			sndrst(source, dest, length, &seg);
			return;
		}
	}

	/* The rest of the input state machine is run with the control block
	 * locked and implements the state machine directly out of the RFC
	 * Out-of-band data is ignored - it was always a bad idea.
	 */
	tcb = &s->tcpctl;
	qlock(tcb);

	switch(tcb->state) {
	case Closed:
		freeb(bp);
		sndrst(source, dest, length, &seg);
		goto done;
	case Listen:
		if(seg.flags & RST) {
			freeb(bp);
			goto done;
		} 
		if(seg.flags & ACK) {
			freeb(bp);
			sndrst(source, dest, length, &seg);
			goto done;
		}
		if(seg.flags & SYN) {
			proc_syn(s, tos, &seg);
			tcpsndsyn(tcb);
			tcpsetstate(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);
				sndrst(source, dest, length, &seg);
				goto done;
			}
		}
		if(seg.flags & RST) {
			if(seg.flags & ACK)
				localclose(s, Econrefused);
			freeb(bp);
			goto done;
		}

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

		if(seg.flags & SYN) {
			proc_syn(s, tos, &seg);
			if(seg.flags & ACK){
				update(s, &seg);
				tcpsetstate(s, Established);
			}
			else 
				tcpsetstate(s, Syn_received);

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

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

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

	/* Cannot accept so answer with a rst */
	if(length)
	if(s->readq == 0)
	if(tcb->state == Closed) {
		freeb(bp);
		sndrst(source, dest, length, &seg);
		goto done;
	}

	/* The segment is beyond the current receive pointer so
	 * queue the data in the resequence queue
	 */
	if(seg.seq != tcb->rcv.nxt)
	if(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))) 
				tcpsetstate(s, Listen);
			else
				localclose(s, Econrefused);

			freeb(bp);
			goto done;
		}

		/* This tos stuff should be removed */
		if(PREC(tos) != PREC(tcb->tos) || (seg.flags & SYN)){
			freeb(bp);
			sndrst(source, dest, 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)){
				freeb(bp);
				sndrst(source, dest, length, &seg);
				goto done;
			}
			update(s, &seg);
			tcpsetstate(s, Established);
		case Established:
		case Close_wait:
			update(s, &seg);
			break;
		case Finwait1:
			update(s, &seg);
			if(tcb->sndcnt == 0)
				tcpsetstate(s, Finwait2);
			break;
		case Finwait2:
			update(s, &seg);
			break;
		case Closing:
			update(s, &seg);
			if(tcb->sndcnt == 0) {
				tcpsetstate(s, Time_wait);
				tcb->timer.start = MSL2 * (1000 / MSPTICK);
				tcpgo(&tcb->timer);
			}
			break;
		case Last_ack:
			update(s, &seg);
			if(tcb->sndcnt == 0) {
				freeb(bp);
				localclose(s, Enoerror);
				goto done;
			}			
		case Time_wait:
			tcb->flags |= FORCE;
			tcpgo(&tcb->timer);
		}

		if((seg.flags&URG) && seg.up) {
			if(seq_gt(seg.up + seg.seq, tcb->rcv.up)) {
				tcb->rcv.up = seg.up + seg.seq;
				pullb(&bp, seg.up);
			}
		} 
		else if(seq_gt(tcb->rcv.nxt, tcb->rcv.up))
			tcb->rcv.up = tcb->rcv.nxt;

		if(length == 0){
			if(bp)
				freeb(bp);
		}
		else {
			switch(tcb->state){
			default:
				/* Ignore segment text */
				if(bp)
					freeb(bp);
				break;

			case Syn_received:
			case Established:
			case Finwait1:
				/* If we still have some data place on receive queue */
				tcb->rcvcnt += blen(bp);
				if(bp){
					if(s->readq)
						PUTNEXT(s->readq, bp);
					else
						putb(&tcb->rcvq, bp);
					bp = 0;
				}
				tcb->rcv.nxt += length;

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

				if(tcb->max_snd <= tcb->rcv.nxt-tcb->last_ack)
					tcb->flags |= FORCE;
				break;
			case Finwait2:
				/* no process to read the data, send a reset */
				if(bp)
					freeb(bp);
				sndrst(source, dest, length, &seg);
				goto done;
			}
		}

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

			switch(tcb->state) {
			case Syn_received:
			case Established:
				tcb->rcv.nxt++;
				tcpsetstate(s, Close_wait);
				break;
			case Finwait1:
				tcb->rcv.nxt++;
				if(tcb->sndcnt == 0) {
					tcpsetstate(s, Time_wait);
					tcb->timer.start = MSL2 * (1000/MSPTICK);
					tcpgo(&tcb->timer);
				}
				else 
					tcpsetstate(s, Closing);
				break;
			case Finwait2:
				tcb->rcv.nxt++;
				tcpsetstate(s, Time_wait);
				tcb->timer.start = MSL2 * (1000/MSPTICK);
				tcpgo(&tcb->timer);
				break;
			case Close_wait:
			case Closing:
			case Last_ack:
				break;
			case Time_wait:
				tcpgo(&tcb->timer);
				break;
			}
		}

		while(tcb->reseq) {
			if(seq_ge(tcb->rcv.nxt, tcb->reseq->seg.seq) == 0)
				break;

			get_reseq(tcb, &tos, &seg, &bp, &length);

			if(trim(tcb, &seg, &bp, &length) == 0)
				break;
		}
		break;
	}
output:
	tcpoutput(s);
done:
	qunlock(tcb);
}

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

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

	if(seq_ge(seg->ack,tcb->snd.wl2))
	if(seq_gt(seg->seq,tcb->snd.wl1) || (seg->seq == tcb->snd.wl1)) {
		if(seg->wnd != 0)
		if(tcb->snd.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;	

	/* Compute the new send window size */
	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;
	}

	/* Adjust the timers acorrding to the round trip time */
	if(run_timer(&tcb->rtt_timer))
	if(seq_ge(seg->ack, tcb->rttseq)) {
		tcphalt(&tcb->rtt_timer);
		if((tcb->flags&RETRAN) == 0) {
			tcb->backoff = 0;
			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 {
				tcb->srtt = ((AGAIN-1)*tcb->srtt + rtt) / AGAIN;
				rtt = abs(rtt - tcb->srtt);
				tcb->mdev = ((DGAIN-1)*tcb->mdev + rtt) / DGAIN;
			}
		}
	}

	if((tcb->flags & SYNACK) == 0){
		tcb->flags |= SYNACK;
		acked--;
		tcb->sndcnt--;
	}

	pullb(&tcb->sndq, acked);

	tcb->sndcnt -= acked;
	tcb->snd.una = seg->ack;
	if(seq_gt(seg->ack, tcb->snd.up))
		tcb->snd.up = seg->ack;

	tcphalt(&tcb->timer);
	if(tcb->snd.una != tcb->snd.nxt)
		tcpgo(&tcb->timer);

	if(seq_lt(tcb->snd.ptr, tcb->snd.una))
		tcb->snd.ptr = tcb->snd.una;

	tcb->flags &= ~RETRAN;
	tcb->backoff = 0;

	if(tcb->sndfull && tcb->sndcnt < Streamhi/2){
		wakeup(&tcb->sndr);
		tcb->sndfull = 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;
	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->ifc->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
tcpsndsyn(Tcpctl *tcb)
{
	static int start;

	start += 250000;
	tcb->iss = start;
	tcb->rttseq = tcb->iss;
	tcb->snd.wl2 = tcb->iss;
	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;

	rp = malloc(sizeof(Reseq));
	if(rp == 0){
		freeb(bp);	/* bp always consumed by add_reseq */
		return;
	}

	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;
		return;
	}

	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;

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

	tcb->reseq = rp->next;

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

	free(rp);
}

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

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

	if(tcb->rcv.wnd == 0) {
		if(len == 0)
		if(seg->seq == tcb->rcv.nxt)
			return 0;
	}
	else {
		/* Some part of the segment should 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){
			pullb(bp, (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;
}

int
pullb(Block **bph, 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));
		bytes += n;
		count -= n;
		bp->rptr += n;
		if(BLEN(bp) == 0) {
			*bph = bp->next;
			bp->next = 0;
			freeb(bp);
		}
	}
	return bytes;
}

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;
}

static void
cleartcp(struct Tctl *a)
{
	memset(a, 0, sizeof(struct Tctl));
}

void
init_tcpctl(Ipconv *s)
{

	Tcpctl *tcb = &s->tcpctl;

	cleartcp(tcb);

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

	tcb->timer.start = tcb->srtt / MSPTICK;
	tcb->timer.func = tcptimeout;
	tcb->timer.arg = s;
	tcb->rtt_timer.start = MAX_TIME; 
	tcb->acktimer.start = TCP_ACK / MSPTICK;
	tcb->acktimer.func = tcpacktimer;
	tcb->acktimer.arg = s;
}

/*
 *  called with tcb locked
 */
void
localclose(Ipconv *s, char reason[])
{
	Reseq *rp,*rp1;
	Tcpctl *tcb = &s->tcpctl;
	Block *bp;

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

	/* flush receive queue */
	while(bp = getb(&tcb->rcvq))
		freeb(bp);

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

	tcb->reseq = 0;
	s->err = reason;
	tcpsetstate(s, Closed);
}

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
tcpsetstate(Ipconv *s, char newstate)
{
	Tcpctl *tcb;
	char oldstate;

	tcb = &s->tcpctl;

	oldstate = tcb->state;
	tcb->state = newstate;
	tcpxstate(s, oldstate, newstate);
}

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

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

	if(data) {
		dlen = blen(data);	
		data = padb(data, hdrlen + TCP_PKT);
		if(data == 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;
	h->frag[0] = 0;
	h->frag[1] = 0;
	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;
	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;

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

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

#define DPRINT if(tcpdbg) print

extern	int tcpdbg;
extern	ushort tcp_mss;
	int tcptimertype;

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

	tcb = &s->tcpctl;

	switch(tcb->state) {
	case Listen:
	case Closed:
		return;
	}

	for(;;) {
		qlen = tcb->sndcnt;
		sent = tcb->snd.ptr - tcb->snd.una;
		sndq = tcb->sndq;

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

		/* Compute usable segment based on offered window and limit
		 * window probes to one
		 */
		if(tcb->snd.wnd == 0){
			if(sent != 0) {
				if (tcb->flags&FORCE)
						tcb->snd.ptr = tcb->snd.una;
				else
					break;
			}
			usable = 1;
		}
		else {
			usable = MIN(tcb->snd.wnd,tcb->cwind) - sent;
			if(sent != 0)
			if(qlen - sent < tcb->mss) 
				usable = 0;
		}

		ssize = MIN(qlen - sent, usable);
		ssize = MIN(ssize, tcb->mss);
		dsize = ssize;
		seg.up = 0;

		if(ssize == 0)
		if((tcb->flags&FORCE) == 0)
			break;

		tcphalt(&tcb->acktimer);

		tcb->flags &= ~FORCE;
		tcprcvwin(s);

		/* By default we will generate an ack */
		seg.source = s->psrc;
		seg.dest = s->pdst;
		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--;
				seg.mss = tcp_mss;
			}
			break;
		}
		seg.seq = tcb->snd.ptr;
		seg.ack = tcb->last_ack = tcb->rcv.nxt;
		seg.wnd = tcb->rcv.wnd;

		/* Pull out data to send */
		dbp = 0;
		if(dsize != 0){
			if(dupb(&dbp, sndq, sent, dsize) != dsize) {
				seg.flags |= FIN;
				dsize--;
			}
			DPRINT("dupb: %d\n", dbp->rptr[0]);
		}

		if(sent+dsize == qlen)
			seg.flags |= PSH;

		/*
		 * keep track of balance of resent data */
		if(tcb->snd.ptr < tcb->snd.nxt)
			tcb->resent += MIN((int)tcb->snd.nxt - (int)tcb->snd.ptr,(int)ssize);

		tcb->snd.ptr += ssize;

		/* Pull up the send pointer so we can accept acks for this window */
		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[Myself]);
		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;
		}

		/* Start the transmission timers if there is new data and we
		 * expect acknowledges
		 */
		if(ssize != 0){
			tcb->timer.start = backoff(tcb->backoff) *
			 (2 * tcb->mdev + tcb->srtt + MSPTICK) / MSPTICK;
			if(!run_timer(&tcb->timer))
				tcpgo(&tcb->timer);

			/* If round trip timer isn't running, start it */
			if(!run_timer(&tcb->rtt_timer)){
				tcpgo(&tcb->rtt_timer);
				tcb->rttseq = tcb->snd.ptr;
			}
		}
		PUTNEXT(Ipoutput, hbp);
	}
}

void
tcprxmit(Ipconv *s)
{
	Tcpctl *tcb;

	tcb = &s->tcpctl;
	qlock(tcb);
	tcb->flags |= RETRAN|FORCE;
	tcb->snd.ptr = tcb->snd.una;

	/* Pull window down to a single packet and halve the slow
	 * start threshold
	 */
	tcb->ssthresh = tcb->cwind / 2;
	tcb->ssthresh = MAX(tcb->ssthresh, tcb->mss);

	tcb->cwind = tcb->mss;
	tcpoutput(s);
	qunlock(tcb);
}

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

	s = (Ipconv *)arg;
	tcb = &s->tcpctl;
	switch(tcb->state){
	default:
		tcb->backoff++;
		if (tcb->backoff >= MAXBACKOFF && tcb->snd.wnd > 0) {
			localclose(s, Etimedout);
			break;
		}
		tcprxmit(s);
		break;

	case Time_wait:
		localclose(s, 0);
		break;
	}
}

int
backoff(int n)
{
	if(tcptimertype == 1) 
		return n+1;

	if(n <= 4)
		return 1 << n;

	return n*n;
}

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

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

void
tcprcvwin(Ipconv *s)				/* Call with tcb locked */
{
	int w;
	Tcpctl *tcb;

	tcb = &s->tcpctl;
	qlock(s);
	if(s->readq) {
		w = Streamhi - s->readq->next->len;
		if(w < 0)
			tcb->rcv.wnd = 0;
		else
			tcb->rcv.wnd = w;
	}
	else
		tcb->rcv.wnd = Streamhi;
	qunlock(s);
}

/*
 * 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]);
}

D port/tcptimer.c => port/tcptimer.c +0 -125
@@ 1,125 0,0 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include 	"arp.h"
#include 	"ipdat.h"

static	Timer 	*timers;	/* List of active timers */
static	QLock 	tl;		/* Protect timer list */
static	Rendez	Tcpack;
Rendez	tcpflowr;

static void
deltimer(Timer *t)
{
	if(timers == t)
		timers = t->next;

	if(t->next)
		t->next->prev = t->prev;

	if(t->prev)
		t->prev->next = t->next;
}

/*
 * Poke each tcp connection to recompute window size and
 * acknowledgement timer
 */

void
tcpflow(void *x)
{
	Ipifc *ifc;
	Ipconv *cp, **p, **etab;

	ifc = x;
	etab = &ifc->conv[Nipconv];

	for(;;) {
		sleep(&tcpflowr, return0, 0);

		for(p = ifc->conv; p < etab; p++) {
			cp = *p;
			if(cp == 0)
				break;
			if(cp->readq && cp->ref != 0 && !QFULL(cp->readq->next)) {
				tcprcvwin(cp);
				tcpacktimer(cp);
			}
		}
	}
}

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

	USED(junk);
	for(;;) {
		timeo = 0;

		qlock(&tl);
		for(t = timers;t != 0; t = tp) {
			tp = t->next;
 			if(t->state == TimerON) {
				t->count--;
				if(t->count == 0) {
					deltimer(t);
					t->state = TimerDONE;
					t->next = timeo;
					timeo = t;
				}
			}
		}
		qunlock(&tl);

		for(;;) {
			t = timeo;
			if(t == 0)
				break;

			timeo = t->next;
			if(t->state == TimerDONE)
			if(t->func)
				(*t->func)(t->arg);
		}
		tsleep(&Tcpack, return0, 0, MSPTICK);
	}
}

void
tcpgo(Timer *t)
{
	if(t == 0 || t->start == 0)
		return;

	qlock(&tl);
	t->count = t->start;
	if(t->state != TimerON) {
		t->state = TimerON;
		t->prev = 0;
		t->next = timers;
		if(t->next)
			t->next->prev = t;
		timers = t;
	}
	qunlock(&tl);
}

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

	qlock(&tl);
	if(t->state == TimerON)
		deltimer(t);
	t->state = TimerOFF;
	qunlock(&tl);
}