[comp.sources.amiga] v89i082: amigatcp - tcp/ip for the amiga, Part03/06

page@swan.ulowell.edu (Bob Page) (03/18/89)

Submitted-by: rminnich@super.org (Ronald G. Minnich)
Posting-number: Volume 89, Issue 82
Archive-name: comm/amigatcp.3

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	ftpserv.c
#	tcpsubr.c
#	ip.c
#	udp.c
# This archive created: Fri Mar 17 17:57:41 1989
cat << \SHAR_EOF > ftpserv.c
/* FTP Server state machine - see RFC 959 */

#define	LINELEN		128	/* Length of command buffer */

#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "tcp.h"
#include "ftp.h"

/* Command table */
static char *commands[] = {
	"user",
#define	USER_CMD	0
	"acct",
#define	ACCT_CMD	1
	"pass",
#define	PASS_CMD	2
	"type",
#define	TYPE_CMD	3
	"list",
#define	LIST_CMD	4
	"cwd",
#define	CWD_CMD		5
	"dele",
#define	DELE_CMD	6
	"name",
#define	NAME_CMD	7
	"quit",
#define	QUIT_CMD	8
	"retr",
#define	RETR_CMD	9
	"stor",
#define	STOR_CMD	10
	"port",
#define	PORT_CMD	11
	"nlst",
#define	NLST_CMD	12
	"pwd",
#define	PWD_CMD		13
	"xpwd",			/* For compatibility with 4.2BSD */
#define	XPWD_CMD	14
	NULLCHAR
};

/* Response messages */
static char banner[] = "220 %s FTP Ready\r\n";
static char badcmd[] = "500 Unknown command\r\n";
static char nopass[] = "202 Password not needed\r\n";
static char logged[] = "230 Logged in\r\n";
static char typeok[] = "200 Type OK\r\n";
static char cwdok[] = "250 CWD OK\r\n";
static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
static char badtype[] = "501 Unknown type\r\n";
static char badport[] = "501 Bad port syntax\r\n";
static char unimp[] = "502 Command not yet implemented\r\n";
static char bye[] = "221 Goodbye!\r\n";
static char nodir[] = "553 Can't read directory\r\n";
static char cantopen[] = "550 Can't open file\r\n";
static char sending[] = "150 Opening data connection for %s %s\r\n";
static char cantmake[] = "553 Can't create file\r\n";
static char portok[] = "200 Port command okay\r\n";
static char rxok[] = "226 File received OK\r\n";
static char txok[] = "226 File sent OK\r\n";

static struct tcb *ftp_tcb;

/* Start up FTP service */
ftp_start(argc,argv)
int argc;
char *argv[];
{
	struct socket lsocket;
	void r_ftp(),s_ftp();

	lsocket.address = ip_addr;
	if(argc < 2)
		lsocket.port = FTP_PORT;
	else
		lsocket.port = atoi(argv[1]);

	ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,r_ftp,NULLVFP,s_ftp,0,(int *)NULL);
}
ftp_stop()
{
	if(ftp_tcb != NULLTCB)
		close_tcp(ftp_tcb);
}
/* FTP server control channel connection state change upcall handler */
static
void
s_ftp(tcb,old,new)
struct tcb *tcb;
char old,new;
{
	extern char hostname[];
	struct ftp *ftp,*ftp_create();
	void ftp_delete();
	char *inet_ntoa(),*pwd();

	switch(new){
#ifdef	QUICKSTART
	case SYN_RECEIVED:
#else
	case ESTABLISHED:
#endif
		if((ftp = ftp_create(LINELEN)) == NULLFTP){
			/* No space, kill connection */
			close_tcp(tcb);
			return;
		}
		ftp->control = tcb;	/* Downward link */
		tcb->user = (int *)ftp;	/* Upward link */

		/* Set default data port */
		ftp->port.address = tcb->conn.remote.address;
		ftp->port.port = FTPD_PORT;

		/* Note current directory */
#ifndef	AMIGA
		ftp->cd = pwd();
#endif
		log(tcb,"open FTP");
		tprintf(ftp->control,banner,hostname);
		break;		
	case CLOSE_WAIT:
		close_tcp(tcb);
		break;
	case CLOSED:
		log(tcb,"close FTP");
		if((ftp = (struct ftp *)tcb->user) != NULLFTP)
			ftp_delete(ftp);
		/* Check if server is being shut down */
		if(tcb == ftp_tcb)
			ftp_tcb = NULLTCB;
		del_tcp(tcb);
		break;
	}
}

/* FTP control channel receiver upcall handler */
static
void
r_ftp(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	register struct ftp *ftp;
	char *index(),c;
	struct mbuf *bp;
	void docommand();

	if((ftp = (struct ftp *)tcb->user) == NULLFTP){
		/* Unknown connection, just kill it */
		close_tcp(tcb);
		return;
	}
	switch(ftp->state){
	case COMMAND_STATE:
		/* Assemble an input line in the session buffer. Return if incomplete */
		recv_tcp(tcb,&bp,0);
		while(pullup(&bp,&c,1) == 1){
			switch(c){
			case '\r':	/* Strip cr's */
				continue;
			case '\n':	/* Complete line; process it */
				ftp->buf[ftp->cnt] = '\0';
				docommand(ftp);
				ftp->cnt = 0;
				break;
			default:	/* Assemble line */
				if(ftp->cnt != LINELEN-1)
					ftp->buf[ftp->cnt++] = c;
				break;
			}
		}
		/* else no linefeed present yet to terminate command */
		break;
	case SENDING_STATE:
	case RECEIVING_STATE:
		/* Leave commands pending on receive queue until
		 * present command is done
		 */
		break;
	}
}

/* FTP server data channel connection state change upcall handler */
void
s_ftpd(tcb,old,new)
struct tcb *tcb;
char old,new;
{
	struct ftp *ftp;
#ifndef	CPM
#ifndef	AMIGA
	char *cdsave;
#endif
#endif

	if((ftp = (struct ftp *)tcb->user) == NULLFTP){
		/* Unknown connection, kill it */
		close_tcp(tcb);
		return;
	}
	switch(new){
	case FINWAIT2:
	case TIME_WAIT:
		if(ftp != NULLFTP && ftp->state == SENDING_STATE){
			/* We've received an ack of our FIN, so
			 * send a completion message on the control channel
			 */
			ftp->state = COMMAND_STATE;
			tprintf(ftp->control,txok);
			/* Kick command parser if something is waiting */
			if(ftp->control->rcvcnt != 0)
				r_ftp(ftp->control,ftp->control->rcvcnt);
		}
		break;		
	case CLOSE_WAIT:
		close_tcp(tcb);
		if(ftp != NULLFTP && ftp->state == RECEIVING_STATE){
			/* End of file received on incoming file */
#ifdef	CPM
			if(ftp->type == ASCII_TYPE)
				putc(CTLZ,ftp->fp);
#endif
#ifndef	CPM
#ifndef	AMIGA
			cdsave = pwd();		/* Save current directory */
			chdir(ftp->cd);		/* Switch to user's directory*/
#endif
#endif
			fclose(ftp->fp);
#ifndef	CPM
#ifndef	AMIGA
			if(cdsave != NULLCHAR){
				chdir(cdsave);		/* And back */
				free(cdsave);
			}
#endif
#endif
			ftp->fp = NULLFILE;
			ftp->state = COMMAND_STATE;
			tprintf(ftp->control,rxok);
			/* Kick command parser if something is waiting */
			if(ftp->control->rcvcnt != 0)
				r_ftp(ftp->control,ftp->control->rcvcnt);
		}
		break;
	case CLOSED:
		if(ftp != NULLFTP)
			ftp->data = NULLTCB;
		del_tcp(tcb);
		break;
	}
}

/* Parse and execute ftp commands */
static
void
docommand(ftp)
register struct ftp *ftp;
{
	void r_ftpd(),t_ftpd(),s_ftpd();
	char *cmd,*arg,*cp,**cmdp;
	char *index(),*malloc(),*strcpy();
	struct socket dport;
#ifndef	CPM
#ifndef	AMIGA
	FILE *dir();
	char *cdsave;
#endif
#endif

	cmd = ftp->buf;
	if(ftp->cnt == 0){
		/* Can't be a legal FTP command */
		tprintf(ftp->control,badcmd);
		return;
	}	
	cmd = ftp->buf;

	/* Translate entire buffer to lower case */
	for(cp = cmd;*cp != '\0';cp++)
		*cp = tolower(*cp);

	/* Find command in table; if not present, return syntax error */
	for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
		if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
			break;
	if(*cmdp == NULLCHAR){
		tprintf(ftp->control,badcmd);
		return;
	}
	arg = &cmd[strlen(*cmdp)];
	while(*arg == ' ')
		arg++;
	/* Execute specific command */
	switch(cmdp-commands){
	case USER_CMD:
		if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
			close_tcp(ftp->control);
			break;
		}
		strcpy(ftp->username,arg);
		tprintf(ftp->control,logged);
		break;
	case TYPE_CMD:
		switch(*arg){
		case 'a':	/* Ascii */
			ftp->type = ASCII_TYPE;
			tprintf(ftp->control,typeok);
			break;
		case 'b':	/* Binary */
		case 'i':	/* Image */
			ftp->type = IMAGE_TYPE;
			tprintf(ftp->control,typeok);
			break;
		default:	/* Invalid */
			tprintf(ftp->control,badtype);
			break;
		}
		break;
	case QUIT_CMD:
		tprintf(ftp->control,bye);
		close_tcp(ftp->control);
		break;
	case RETR_CMD:
		/* Disk operation; return ACK now */
		tcp_output(ftp->control);
#ifndef	CPM
#ifndef	AMIGA
		cdsave = pwd();		/* Save current directory */
		chdir(ftp->cd);		/* Switch to user's directory*/
#endif
#endif
		ftp->fp = fopen(arg,"r");
#ifndef	CPM
#ifndef	AMIGA
		chdir(cdsave);		/* And back */
		free(cdsave);
#endif
#endif
		if(ftp->fp == NULLFILE){
			tprintf(ftp->control,cantopen);
		} else {
			log(ftp->control,"RETR %s/%s",ftp->cd,arg);
			dport.address = ip_addr;
			dport.port = FTPD_PORT;
			ftp->state = SENDING_STATE;
			tprintf(ftp->control,sending,"RETR",arg);

			/* This hack is just so we can talk to ourselves */
			ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
			 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);

			ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
			 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
		}
		break;
	case STOR_CMD:
		/* Disk operation; return ACK now */
		tcp_output(ftp->control);
#ifndef	CPM
#ifndef	AMIGA
		cdsave = pwd();		/* Save current directory */
		chdir(ftp->cd);		/* Switch to user's directory */
#endif
#endif	
		ftp->fp = fopen(arg,"w");
#ifndef	CPM
#ifndef	AMIGA
		chdir(cdsave);			/* And back */
		free(cdsave);
#endif
#endif		
		if(ftp->fp == NULLFILE){
			tprintf(ftp->control,cantmake);
		} else {
			log(ftp->control,"STOR %s/%s",ftp->cd,arg);
			dport.address = ip_addr;
			dport.port = FTPD_PORT;
			ftp->state = RECEIVING_STATE;
			tprintf(ftp->control,sending,"STOR",arg);

			/* This hack is just so we can talk to ourselves */
			ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
			 0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp);

			ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
			 0,r_ftpd,NULLVFP,s_ftpd,ftp->control->tos,(int *)ftp);
		}
		break;
	case PORT_CMD:
		if(pport(&ftp->port,arg) == -1){
			tprintf(ftp->control,badport);
		} else {
			tprintf(ftp->control,portok);
		}
		break;
/* #ifndef CPM */
#ifndef	AMIGA
	case LIST_CMD:
		/* Disk operation; return ACK now */
		tcp_output(ftp->control);

		cdsave = pwd();		/* Save current directory */
		chdir(ftp->cd);		/* Switch to user's directory */
		ftp->fp = dir(arg,1);
		chdir(cdsave);		/* And back */
		free(cdsave);

		if(ftp->fp == NULLFILE){
			tprintf(ftp->control,nodir);
			break;
		}			
		dport.address = ip_addr;
		dport.port = FTPD_PORT;
		ftp->state = SENDING_STATE;
		tprintf(ftp->control,sending,"LIST",arg);

		/* This hack is just so we can talk to ourselves */
		ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
		 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);

		ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
		 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
		break;
	case NLST_CMD:
		/* Disk operation; return ACK now */
		tcp_output(ftp->control);

		cdsave = pwd();		/* Save current directory */
		chdir(ftp->cd);		/* Switch to user's directory */
		ftp->fp = dir(arg,0);
		chdir(cdsave);		/* And back */
		free(cdsave);

		if(ftp->fp == NULLFILE){
			tprintf(ftp->control,nodir);
			break;
		}			
		dport.address = ip_addr;
		dport.port = FTPD_PORT;
		ftp->state = SENDING_STATE;
		tprintf(ftp->control,sending,"NLST",arg);

		/* This hack is just so we can talk to ourselves */
		ftp->data = open_tcp(&dport,&ftp->port,TCP_PASSIVE,
		 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);

		ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
		 0,NULLVFP,t_ftpd,s_ftpd,ftp->control->tos,(int *)ftp);
		break;
	case CWD_CMD:
		tcp_output(ftp->control);	/* Disk operation; return ACK now */

		cdsave = pwd();		/* Save current directory */
		chdir(ftp->cd);		/* Go to user's context */
		if(chdir(arg) == 0){	/* Attempt switch */
			/* Succeeded, record in control block */
			free(ftp->cd);
			ftp->cd = pwd();
			tprintf(ftp->control,cwdok);
		} else {
			/* Failed, don't change anything */
			tprintf(ftp->control,nodir);
		}
		chdir(cdsave);			/* Go back */
		free(cdsave);
		break;
	case XPWD_CMD:
	case PWD_CMD:
		tprintf(ftp->control,pwdmsg,ftp->cd);
		break;
#else
	case LIST_CMD:
	case NLST_CMD:
	case CWD_CMD:
	case XPWD_CMD:
	case PWD_CMD:
#endif
	case ACCT_CMD:		
	case DELE_CMD:
		tprintf(ftp->control,unimp);
		break;
	case PASS_CMD:
		tprintf(ftp->control,nopass);
		break;
	}
}
static
int
pport(sock,arg)
struct socket *sock;
char *arg;
{
	int32 n;
	int atoi(),i;

	n = 0;
	for(i=0;i<4;i++){
		n = atoi(arg) + (n << 8);
		if((arg = index(arg,',')) == NULLCHAR)
			return -1;
		arg++;
	}
	sock->address = n;
	n = atoi(arg);
	if((arg = index(arg,',')) == NULLCHAR)
		return -1;
	arg++;
	n = atoi(arg) + (n << 8);
	sock->port = n;
	return 0;
}
SHAR_EOF
cat << \SHAR_EOF > tcpsubr.c
#ifdef	TRACE
#include <stdio.h>
#endif

#include "machdep.h"
#include "timer.h"
#include "mbuf.h"
#include "netuser.h"
#include "internet.h"
#include "tcp.h"

struct tcb *tcbs[NTCB];

/* Lookup connection, return TCB pointer or NULLTCB if nonexistant */
struct tcb *
lookup_tcb(conn)
struct connection *conn;
{
	register struct tcb *tcb;
	int16 hash_tcb();	

	tcb = tcbs[hash_tcb(conn)];
	while(tcb != NULLTCB){
		/* Yet another structure compatibility hack */
		if(conn->local.address == tcb->conn.local.address
		 && conn->remote.address == tcb->conn.remote.address
		 && conn->local.port == tcb->conn.local.port
		 && conn->remote.port == tcb->conn.remote.port)
			break;
		tcb = tcb->next;
	}
	return tcb;
}

/* Create a TCB, return pointer. Return pointer if TCB already exists. */
struct tcb *
create_tcb(conn)
struct connection *conn;
{
	char *calloc();
	register struct tcb *tcb;
	void tcp_timeout(),tcp_msl();
	void link_tcb();

	if((tcb = lookup_tcb(conn)) != NULLTCB)
		return tcb;
	if((tcb = (struct tcb *)calloc(1,sizeof (struct tcb))) == NULLTCB)
		return NULLTCB;
	bcopy((char *)conn,(char *)&tcb->conn,sizeof(struct connection));

	tcb->mss = DEF_MSS;
	tcb->srtt = DEF_RTT * MSPTICK;
	/* Initialize retransmission timeout */
	tcb->timer.start = (BETA * tcb->srtt)/MSPTICK;

	tcb->timer.func = tcp_timeout;
	tcb->timer.arg = (int *)tcb;

	link_tcb(tcb);
	return tcb;
}

/* Close our TCB */
void
close_self(tcb,reason)
register struct tcb *tcb;
char reason;
{
	struct reseq *rp,*rp1;

	stop_timer(&tcb->timer);
	tcb->reason = reason;

	/* Flush reassembly queue; nothing more can arrive */
	for(rp = tcb->reseq;rp != NULLRESEQ;rp = rp1){
		rp1 = rp->next;
		free_p(rp->bp);
		free((char *)rp);
	}
	tcb->reseq = NULLRESEQ;
	setstate(tcb,CLOSED);
}

/* Determine initial sequence number */

#ifdef	AMIGA
/*
 *  routine called at startup time with inital value of iss for system.  This
 *  is probably based on the time or something
 */
static int32 seq;

void
setiss(initval)
	int32 initval;
{
	seq = initval;
}
#endif

int32
iss()
{
#ifndef	AMIGA
	static int32 seq;
#endif
	seq += 250000;
	return seq;
}

/* Sequence number comparisons
 * Return true if x is between low and high inclusive,
 * false otherwise
 */
int
seq_within(x,low,high)
register int32 x,low,high;
{
	if(low <= high){
		if(low <= x && x <= high)
			return 1;
	} else {
		if(low >= x && x >= high)
			return 1;
	}
	return 0;
}
int
seq_lt(x,y)
register int32 x,y;
{
	return (long)(x-y) < 0;
}
int
seq_le(x,y)
register int32 x,y;
{
	return (long)(x-y) <= 0;
}
int
seq_gt(x,y)
register int32 x,y;
{
	return (long)(x-y) > 0;
}
int
seq_ge(x,y)
register int32 x,y;
{
	return (long)(x-y) >= 0;
}

/* Hash a connect structure into the hash chain header array */
static int16
hash_tcb(conn)
struct connection *conn;
{
	register int16 hval;

	/* Compute hash function on connection structure */
	hval = hiword(conn->remote.address);
	hval ^= loword(conn->remote.address);
	hval ^= hiword(conn->local.address);
	hval ^= loword(conn->local.address);
	hval ^= conn->remote.port;
	hval ^= conn->local.port;
	hval %= NTCB;
	return hval;
}
/* Insert TCB at head of proper hash chain */
void
link_tcb(tcb)
register struct tcb *tcb;
{
	register struct tcb **tcbhead;
	int16 hash_tcb();
	char i_state;

	tcb->prev = NULLTCB;
	i_state = disable();
	tcbhead = &tcbs[hash_tcb(&tcb->conn)];
	tcb->next = *tcbhead;
	if(tcb->next != NULLTCB){
		tcb->next->prev = tcb;
	}
	*tcbhead = tcb;
	restore(i_state);
}
/* Remove TCB from whatever hash chain it may be on */
void
unlink_tcb(tcb)
register struct tcb *tcb;
{
	register struct tcb **tcbhead;
	int16 hash_tcb();
	char i_state;

	i_state = disable();
	tcbhead = &tcbs[hash_tcb(&tcb->conn)];
	if(*tcbhead == tcb)
		*tcbhead = tcb->next;	/* We're the first one on the chain */
	if(tcb->prev != NULLTCB)
		tcb->prev->next = tcb->next;
	if(tcb->next != NULLTCB)
		tcb->next->prev = tcb->prev;
	restore(i_state);
}
void
setstate(tcb,newstate)
register struct tcb *tcb;
register char newstate;
{
	register char oldstate;

	oldstate = tcb->state;
	tcb->state = newstate;
	if(tcb->s_upcall){
		(*tcb->s_upcall)(tcb,oldstate,newstate);
	}
	/* Notify the user that he can begin sending data */
	if(tcb->t_upcall && newstate == ESTABLISHED){
		(*tcb->t_upcall)(tcb,tcb->window - tcb->sndcnt);
	}
}
#ifdef	TRACE
/* TCP connection states */
char *tcpstates[] = {
	"Closed",
	"Listen",
	"SYN sent",
	"SYN received",
	"Established",
	"FIN wait 1",
	"FIN wait 2",
	"Close wait",
	"Closing",
	"Last ACK",
	"Time wait"
};
/* TCP segment header flags */
char *tcpflags[] = {
	"FIN",	/* 0x01 */
	"SYN",	/* 0x02 */
	"RST",	/* 0x04 */
	"PSH",	/* 0x08 */
	"ACK",	/* 0x10 */
	"URG"	/* 0x20 */
};

/* TCP closing reasons */
char *reasons[] = {
	"Normal",
	"Reset",
	"Timeout",
	"ICMP"
};
/* Return 1 if arg is a valid TCB, 0 otherwise */
int
tcpval(tcb)
struct tcb *tcb;
{
	register int i;
	register struct tcb *tcb1;

	for(i=0;i<NTCB;i++){
		for(tcb1=tcbs[i];tcb1 != NULLTCB;tcb1 = tcb1->next){
			if(tcb1 == tcb)
				return 1;
		}
	}
	return 0;
}

/* Dump TCP stats and summary of all TCBs
/* &TCB Rcv-Q Snd-Q  Local socket           Remote socket          State
 * 1234     0     0  xxx.xxx.xxx.xxx:xxxxx  xxx.xxx.xxx.xxx:xxxxx  Established
 */
int
tcpstat()
{
	register int i;
	register struct tcb *tcb;
	char *psocket();

	printf("conout %u conin %u reset out %u runt %u chksum err %u bdcsts %u\r\n",
		tcp_stat.conout,tcp_stat.conin,tcp_stat.resets,tcp_stat.runt,
		tcp_stat.checksum,tcp_stat.bdcsts);
#ifdef	AMIGA
	printf("&TCB   Rcv-Q Snd-Q  Local socket           Remote socket          State\r\n");
#else
	printf("&TCB Rcv-Q Snd-Q  Local socket           Remote socket          State\r\n");
#endif
	for(i=0;i<NTCB;i++){
		for(tcb=tcbs[i];tcb != NULLTCB;tcb = tcb->next){
#ifdef	AMIGA
			printf("%6lx%6u%6u  ",(unsigned long)tcb,
						tcb->rcvcnt,tcb->sndcnt);
#else
			printf("%4x%6u%6u  ",(int)tcb,tcb->rcvcnt,tcb->sndcnt);
#endif
			printf("%-23s",psocket(&tcb->conn.local));
			printf("%-23s",psocket(&tcb->conn.remote));
			printf("%-s\r\n",tcpstates[tcb->state]);
		}
	}
	fflush(stdout);
	return 0;
}
/* Dump a TCP control block */
void
state_tcp(tcb)
struct tcb *tcb;
{
	int32 sent,recvd;

	if(tcb == NULLTCB)
		return;
	/* Compute total data sent and received; take out SYN and FIN */
	sent = tcb->snd.una - tcb->iss;	/* Acknowledged data only */
	recvd = tcb->rcv.nxt - tcb->irs;
	switch(tcb->state){
	case LISTEN:
	case SYN_SENT:		/* Nothing received or acked yet */
		sent = recvd = 0;	
		break;
	case SYN_RECEIVED:
		recvd--;	/* Got SYN, no data acked yet */
		sent = 0;
		break;
	case ESTABLISHED:	/* Got and sent SYN */
	case FINWAIT1:		/* FIN not acked yet */
		sent--;
		recvd--;
		break;
	case FINWAIT2:		/* Our SYN and FIN both acked */
		sent -= 2;
		recvd--;
		break;
	case CLOSE_WAIT:	/* Got SYN and FIN, our FIN not yet acked */
	case CLOSING:
	case LAST_ACK:
		sent--;
		recvd -= 2;
		break;
	case TIME_WAIT:		/* Sent and received SYN/FIN, all acked */
		sent -= 2;
		recvd -= 2;
		break;
	}
	printf("Local: %s",psocket(&tcb->conn.local));
	printf(" Remote: %s",psocket(&tcb->conn.remote));
	printf(" State: %s\r\n",tcpstates[tcb->state]);
	printf("      Init seq    Unack     Next      WL1      WL2  Wind   MSS Queue      Total\r\n");
	printf("Send:");
	printf("%9lx",tcb->iss);
	printf("%9lx",tcb->snd.una);
	printf("%9lx",tcb->snd.nxt);
	printf("%9lx",tcb->snd.wl1);
	printf("%9lx",tcb->snd.wl2);
	printf("%6u",tcb->snd.wnd);
	printf("%6u",tcb->mss);
	printf("%6u",tcb->sndcnt);
	printf("%11lu\r\n",sent);

	printf("Recv:");
	printf("%9lx",tcb->irs);
	printf("         ");
	printf("%9lx",tcb->rcv.nxt);
	printf("         ");
	printf("         ");
	printf("%6u",tcb->rcv.wnd);
	printf("      ");
	printf("%6u",tcb->rcvcnt);
	printf("%11lu\r\n",recvd);

	if(tcb->reseq != (struct reseq *)NULL){
		register struct reseq *rp;

		printf("Reassembly queue:\r\n");
		for(rp = tcb->reseq;rp != (struct reseq *)NULL; rp = rp->next){
			printf("  seq x%lx %u bytes\r\n",rp->seg.seq,rp->length);
		}
	}
	printf("Retry %u",tcb->retry);
	switch(tcb->timer.state){
	case TIMER_STOP:
		printf(" Timer stopped");
		break;
	case TIMER_RUN:
		printf(" Timer running (%ld/%ld mS)",
		 (long)MSPTICK * (tcb->timer.start - tcb->timer.count),
		 (long)MSPTICK * tcb->timer.start);
		break;
	case TIMER_EXPIRE:
		printf(" Timer expired");
	}
	printf(" Smoothed round trip time %ld mS\r\n",tcb->srtt);
	fflush(stdout);
}

/* Dump a TCP segment header. Assumed to be in network byte order */
void
tcp_dump(bp,source,dest,check)
struct mbuf *bp;
int32 source,dest;	/* IP source and dest addresses */
int check;		/* 0 if checksum test is to be bypassed */
{
	int hdr_len,i;
	register struct tcp_header *tcph;
	struct pseudo_header ph;
	char tmpbuf;

	if(bp == NULLBUF)
		return;
	/* If packet isn't in a single buffer, make a temporary copy and
	 * note the fact so we free it later
	 */
	if(bp->next != NULLBUF){
		bp = copy_p(bp,len_mbuf(bp));
		tmpbuf = 1;
	} else
		tmpbuf = 0;
		
	tcph = (struct tcp_header *)bp->data;
	hdr_len = hinibble(tcph->offset) * sizeof(int32);
	printf("TCP: %u->%u Seq x%lx",
		ntohs(tcph->source),ntohs(tcph->dest),
		ntohl(tcph->seq),ntohl(tcph->ack));
	
	if(tcph->flags & ACK)
		printf(" Ack x%lx",ntohl(tcph->ack));
	for(i=0;i<6;i++){
		if(tcph->flags & 1 << i){
			printf(" %s",tcpflags[i]);
		}
	}
	printf(" Wnd %u",ntohs(tcph->wnd));
	if(tcph->flags & URG)
		printf(" UP x%x",ntohs(tcph->up));

	if(hdr_len > sizeof(struct tcp_header)){
		struct mss *mssp;
	
		mssp = (struct mss *)(tcph + 1);
		if(mssp->kind == MSS_KIND && mssp->length == MSS_LENGTH){
			printf(" MSS %u",ntohs(mssp->mss));
		}
	}
	/* Verify checksum */
	if(check){
		ph.source = source;
		ph.dest = dest;
		ph.protocol = TCP_PTCL;
		ph.length = len_mbuf(bp);
		ph.zero = 0;
		if((i = cksum(&ph,bp,ph.length)) != 0)
			printf(" CHECKSUM ERROR (%u)",i);
	}
	printf("\r\n");
	if(tmpbuf)
		free_p(bp);
}
#endif
SHAR_EOF
cat << \SHAR_EOF > ip.c
/* Upper half of IP, consisting of send/receive primitives, including
 * fragment reassembly, for higher level protocols.
 * Not needed when running as a standalone gateway.
 */
#define	TLB		30	/* Reassembly limit time */
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "timer.h"
#include "internet.h"
#include "ip.h"
#include "icmp.h"
#include "iface.h"

int ip_recv();	/* Should be void, but C complains */
char *calloc(),*malloc();

char ip_ttl = MAXTTL;	/* Default time-to-live for IP datagrams */

struct reasm *reasmq;

#define	INSERT	0
#define	APPEND	1
#define	PREPEND	2

/* Send an IP datagram. Modeled after the example interface on p 32 of
 * RFC 791
 */
void
ip_send(source,dest,protocol,tos,ttl,bp,length,id,df)
int32 source;		/* source address */
int32 dest;		/* Destination address */
char protocol;		/* Protocol */
char tos;		/* Type of service */
char ttl;		/* Time-to-live */
struct mbuf *bp;	/* Data portion of datagram */
int16 length;		/* Optional length of data portion */
int16 id;		/* Optional identification */
char df;		/* Don't-fragment flag */
{
	struct mbuf *hbp;	/* mbuf containing IP header */
	struct ip_header *iph;	/* Pointer to IP header */
	static int16 id_cntr;	/* Datagram serial number */
	int16 hdr_len;		/* IP header length, bytes */
	void ip_route();	/* Datagram router */

	if(length == 0 && bp != NULLBUF)
		length = len_mbuf(bp);
	if(id == 0)
		id = id_cntr++;		
	if(ttl == 0)
		ttl = ip_ttl;

	/* Allocate an mbuf for the IP header */
	hdr_len = sizeof(struct ip_header);
	if((hbp = alloc_mbuf(hdr_len)) == NULLBUF){
		/* We really ought to source-quench the sender, but that would
		 * probably fail too.
		 */
		free_p(bp);
		return;
	}
	hbp->cnt = hdr_len;

	/* and fill it in */
	iph = (struct ip_header *)hbp->data;

	iph->v_ihl = (IPVERSION << 4) | (hdr_len/sizeof(int32));
	iph->tos = tos;
	iph->length = htons(hdr_len + length);
	iph->id = htons(id);
	if(df)
		iph->fl_offs = htons(DF);
	else
		iph->fl_offs = 0;
	iph->ttl = ttl;
	iph->protocol = protocol;
	iph->checksum = 0;
	iph->source = htonl(source);
	iph->dest = htonl(dest);
	iph->checksum = cksum(NULLHEADER,hbp,hdr_len);
	hbp->next = bp;
	ip_route(hbp,0);		/* Toss it to the router */
}

/* Reassemble incoming IP fragments and dispatch completed datagrams
 * to the proper transport module
 */
int	/* Should really be void */
ip_recv(bp,rxbroadcast)
struct mbuf *bp;
char rxbroadcast;
{
	register struct ip_header *ipp;	/* Pointer to original IP header */
	struct ip_header ip;	/* Extracted copy of header */
	int16 ip_len;		/* Length of IP header */
	struct mbuf *fraghandle();
	void (*recv)();	/* Function to call with completed datagram */
	void tcp_input(),udp_input(),icmp_input();

	ipp = (struct ip_header *)bp->data;

	/* Initial check for protocols we can't handle */
	switch(ipp->protocol & 0xff){
	case TCP_PTCL:
		recv = tcp_input;
		break;
	case UDP_PTCL:
		recv = udp_input;
		break;
	case ICMP_PTCL:
		recv = icmp_input;
		break;
	default:
		/* Send an ICMP Protocol Unknown response... */
		ip_stats.badproto++;
		/* ...unless it's a broadcast */
		if(!rxbroadcast)
			icmp_output(bp,DEST_UNREACH,PROT_UNREACH,(union icmp_args *)NULL);
		free_p(bp);
		return;
	}
	ip_len = lonibble(ipp->v_ihl) * sizeof(int32);
	/* Extract IP header */
	pullup(&bp,(char *)&ip,ip_len);

	/* Convert to host byte order */
	ip.length = ntohs(ip.length) - ip_len;	/* Length of data portion */
	ip.id = ntohs(ip.id);
	ip.fl_offs = ntohs(ip.fl_offs);
	ip.source = ntohl(ip.source);
	ip.dest = ntohl(ip.dest);

	/* If we have a complete packet, call the next layer
	 * to handle the result
	 */
	if((bp = fraghandle(&ip,bp)) != NULLBUF)
		(*recv)(bp,ip.protocol,ip.source,ip.dest,ip.tos,ip.length,rxbroadcast);
}
/* Process IP datagram fragments
 * If datagram is complete, return it with ip->length containing its
 * entire length; otherwise return NULLBUF
 */
static
struct mbuf *
fraghandle(ip,bp)
struct ip_header *ip;	/* IP header, host byte order */
struct mbuf *bp;	/* The fragment itself */
{
	void ip_timeout(),freefrag(),free_reasm();
	struct reasm *lookup_reasm(),*creat_reasm();
	register struct reasm *rp; /* Pointer to reassembly descriptor */
	struct frag *lastfrag,*nextfrag,*tfp,*newfrag();
	struct mbuf *tbp;
	int16 i;
	int16 offset;		/* Index of first byte in fragment */
	int16 last;		/* Index of first byte beyond fragment */
	char mf;		/* 1 if not last fragment, 0 otherwise */

	offset = (ip->fl_offs & F_OFFSET) << 3;	/* Convert to bytes */
	last = offset + ip->length;
	mf = (ip->fl_offs & MF) ? 1 : 0;

	rp = lookup_reasm(ip);
	if(offset == 0 && !mf){
		/* Complete datagram received. Discard any earlier fragments */
		if(rp != NULLREASM)
			free_reasm(rp);

		return bp;
	}
	if(rp == NULLREASM){
		/* First fragment; create new reassembly descriptor */
		if((rp = creat_reasm(ip)) == NULLREASM){
			/* No space for descriptor, drop fragment */
			free_p(bp);
			return NULLBUF;
		}
	}
	/* Keep restarting timer as long as we keep getting fragments */
	stop_timer(&rp->timer);
	start_timer(&rp->timer);

	/* If this is the last fragment, we now know how long the
	 * entire datagram is; record it
	 */
	if(!mf)
		rp->length = last;

	/* Set nextfrag to the first fragment which begins after us,
	 * and lastfrag to the last fragment which begins before us
	 */
	lastfrag = NULLFRAG;
	for(nextfrag = rp->fraglist;nextfrag != NULLFRAG;nextfrag = nextfrag->next){
		if(nextfrag->offset > offset)
			break;
		lastfrag = nextfrag;
	}
	/* Check for overlap with preceeding fragment */
	if(lastfrag != NULLFRAG  && offset < lastfrag->last){
		/* Strip overlap from new fragment */
		i = lastfrag->last - offset;
		pullup(&bp,NULLCHAR,i);
		if(bp == NULLBUF)
			return NULLBUF;	/* Nothing left */
		offset += i;
	}
	/* Look for overlap with succeeding segments */
	for(; nextfrag != NULLFRAG; nextfrag = tfp){
		tfp = nextfrag->next;	/* save in case we delete fp */

		if(nextfrag->offset >= last)
			break;	/* Past our end */
		/* Trim the front of this entry; if nothing is
		 * left, remove it.
		 */
		i = last - nextfrag->offset;
		pullup(&nextfrag->buf,NULLCHAR,i);
		if(nextfrag->buf == NULLBUF){
			/* superseded; delete from list */
			if(nextfrag->prev != NULLFRAG)
				nextfrag->prev->next = nextfrag->next;
			else
				rp->fraglist = nextfrag->next;
			if(tfp->next != NULLFRAG)
				nextfrag->next->prev = nextfrag->prev;
			freefrag(nextfrag);
		} else
			nextfrag->offset = last;
	}
	/* Lastfrag now points, as before, to the fragment before us;
	 * nextfrag points at the next fragment. Check to see if we can
	 * join to either or both fragments.
	 */
	i = INSERT;
	if(lastfrag != NULLFRAG && lastfrag->last == offset)
		i |= APPEND;
	if(nextfrag != NULLFRAG && nextfrag->offset == last)
		i |= PREPEND;
	switch(i){
	case INSERT:	/* Insert new desc between lastfrag and nextfrag */
		tfp = newfrag(offset,last,bp);
		tfp->prev = lastfrag;
		tfp->next = nextfrag;
		if(lastfrag != NULLFRAG)
			lastfrag->next = tfp;	/* Middle of list */
		else
			rp->fraglist = tfp;	/* First on list */
		if(nextfrag != NULLFRAG)
			nextfrag->prev = tfp;
		break;
	case APPEND:	/* Append to lastfrag */
		append(&lastfrag->buf,bp);
		lastfrag->last = last;	/* Extend forward */
		break;
	case PREPEND:	/* Prepend to nextfrag */
		tbp = nextfrag->buf;
		nextfrag->buf = bp;
		append(&nextfrag->buf,tbp);
		nextfrag->offset = offset;	/* Extend backward */
		break;
	case (APPEND|PREPEND):
		/* Consolidate by appending this fragment and nextfrag
		 * to lastfrag and removing the nextfrag descriptor
		 */
		append(&lastfrag->buf,bp);
		append(&lastfrag->buf,nextfrag->buf);
		nextfrag->buf = NULLBUF;
		lastfrag->last = nextfrag->last;

		/* Finally unlink and delete the now unneeded nextfrag */
		lastfrag->next = nextfrag->next;
		if(nextfrag->next != NULLFRAG)
			nextfrag->next->prev = lastfrag;
		freefrag(nextfrag);
		break;
	}
	if(rp->fraglist->offset == 0 && rp->fraglist->next == NULLFRAG 
		&& rp->length != 0){
		/* We've gotten a complete datagram, so extract it from the
		 * reassembly buffer and pass it on.
		 */
		bp = rp->fraglist->buf;
		rp->fraglist->buf = NULLBUF;
		ip->length = rp->length;	/* Tell IP the entire length */
		free_reasm(rp);
		return bp;
	} else
		return NULLBUF;
}
static struct reasm *
lookup_reasm(ip)
struct ip_header *ip;
{
	register struct reasm *rp;

	for(rp = reasmq;rp != NULLREASM;rp = rp->next){
		if(ip->source == rp->source && ip->dest == rp->dest
		 && ip->protocol == rp->protocol && ip->id == rp->id)
			return rp;
	}
	return NULLREASM;
}
#ifdef	FOO
static
int16
hash_reasm(source,dest,protocol,id)
int32 source;
int32 dest,
char protocol;
int16 id;
{
	register int16 hval;

	hval = loword(source);
	hval ^= hiword(source);
	hval ^= loword(dest);
	hval ^= hiword(dest);
	hval ^= protocol & 0xff;
	hval ^= id;
	hval %= RHASH;
	return hval;
}
#endif
/* Create a reassembly descriptor,
 * put at head of reassembly list
 */
static struct reasm *
creat_reasm(ip)
register struct ip_header *ip;
{
	register struct reasm *rp;

	if((rp = (struct reasm *)calloc(1,sizeof(struct reasm))) == NULLREASM)
		return rp;	/* No space for descriptor */
	rp->source = ip->source;
	rp->dest = ip->dest;
	rp->id = ip->id;
	rp->protocol = ip->protocol;
	rp->timer.start = TLB;
	rp->timer.func = ip_timeout;
	rp->timer.arg = (int *)rp;

	rp->next = reasmq;
	if(rp->next != NULLREASM)
		rp->next->prev = rp;
	reasmq = rp;
	return rp;
}

/* Free all resources associated with a reassembly descriptor */
static void
free_reasm(rp)
register struct reasm *rp;
{
	register struct frag *fp;

	stop_timer(&rp->timer);
	/* Remove from list of reassembly descriptors */
	if(rp->prev != NULLREASM)
		rp->prev->next = rp->next;
	else
		reasmq = rp->next;
	if(rp->next != NULLREASM)
		rp->next->prev = rp->prev;
	/* Free any fragments on list, starting at beginning */
	while((fp = rp->fraglist) != NULLFRAG){
		rp->fraglist = fp->next;
		free_p(fp->buf);
		free((char *)fp);
	}
	free((char *)rp);
}

/* Handle reassembly timeouts by deleting all reassembly resources */
static void
ip_timeout(arg)
int *arg;
{
	register struct reasm *rp;

	rp = (struct reasm *)arg;
	free_reasm(rp);
}
/* Create a fragment */
static
struct frag *
newfrag(offset,last,bp)
int16 offset,last;
struct mbuf *bp;
{
	struct frag *fp;

	if((fp = (struct frag *)calloc(1,sizeof(struct frag))) == NULLFRAG){
		/* Drop fragment */
		free_p(bp);
		return NULLFRAG;
	}
	fp->buf = bp;
	fp->offset = offset;
	fp->last = last;
	return fp;
}
/* Delete a fragment, return next one on queue */
static
void
freefrag(fp)
struct frag *fp;
{
	free_p(fp->buf);
	free((char *)fp);
}
#ifdef TRACE
int
doipstat(argc,argv)
int argc;
char *argv[];
{
	extern struct ip_stats ip_stats;
	extern struct reasm *reasmq;
	register struct reasm *rp;
	register struct frag *fp;
	char *inet_ntoa();

	printf("total %ld runt %u len err %u vers err %u",
		ip_stats.total,ip_stats.runt,ip_stats.length,ip_stats.version);
	printf(" chksum err %u badproto %u\r\n",
		ip_stats.checksum,ip_stats.badproto);

	if(reasmq != NULLREASM)
		printf("Reassembly fragments:\r\n");
	for(rp = reasmq;rp != NULLREASM;rp = rp->next){
		printf("src %s",inet_ntoa(rp->source));
		printf(" dest %s",inet_ntoa(rp->dest));
		printf(" id %u pctl %u time %u len %u\r\n",
			rp->id,rp->protocol,rp->timer.count,rp->length);
		for(fp = rp->fraglist;fp != NULLFRAG;fp = fp->next){
			printf(" offset %u last %u\r\n",fp->offset,fp->last);
		}
	}
	return 0;
}
#endif
SHAR_EOF
cat << \SHAR_EOF > udp.c
/* Send and receive User Datagram Protocol packets */
#include "machdep.h"
#include "mbuf.h"
#include "netuser.h"
#include "udp.h"
#include "internet.h"

struct udp_cb *udps[NUDP];	/* Hash table for UDP structures */
struct udp_stat udp_stat;	/* Statistics */

/* Create a UDP control block for lsocket, so that we can queue
 * incoming datagrams.
 */
int
open_udp(lsocket,r_upcall)
struct socket *lsocket;
void (*r_upcall)();
{
	char *malloc();
	register struct udp_cb *up;
	struct udp_cb *lookup_udp();
	int16 hval,hash_udp();

	if((up = lookup_udp(lsocket)) != NULLUDP)
		return 0;	/* Already exists */
	if((up = (struct udp_cb *)malloc(sizeof (struct udp_cb))) == NULLUDP){
		net_error = NO_SPACE;
		return -1;
	}
	up->rcvq = NULLBUF;
	up->rcvcnt = 0;
	up->socket.address = lsocket->address;
	up->socket.port = lsocket->port;
	up->r_upcall = r_upcall;

	hval = hash_udp(lsocket);
	up->next = udps[hval];
	up->prev = NULLUDP;
	up->next->prev = up;
	udps[hval] = up;
	return 0;
}

/* Send a UDP datagram */
int
send_udp(lsocket,fsocket,tos,ttl,bp,length,id,df)
struct socket *lsocket;		/* Source socket */
struct socket *fsocket;		/* Destination socket */
char tos;					/* Type-of-service for IP */
char ttl;					/* Time-to-live for IP */
struct mbuf *bp;			/* Data field, if any */
int16 length;				/* Length of data field */
int16 id;					/* Optional ID field for IP */
char df;					/* Don't Fragment flag for IP */
{
	struct mbuf *hbp;
	int16 hdr_len;
	struct pseudo_header ph;
	struct udp_header *uhdr;

	if(length == 0 && bp != NULLBUF)
		length = len_mbuf(bp);
	hdr_len = sizeof(struct udp_header);
	length += hdr_len;

	/* Allocate UDP protocol header and fill it in */
	if((hbp = alloc_mbuf(hdr_len)) == NULLBUF){
		net_error = NO_SPACE;
		return -1;
	}
	hbp->cnt = hdr_len;

	uhdr = (struct udp_header *)hbp->data;
	uhdr->source = htons(lsocket->port);
	uhdr->dest = htons(fsocket->port);
	uhdr->length = htons(length);
	uhdr->checksum = 0;

	/* Link in the user data */
	hbp->next = bp;

	/* Create IP pseudo-header, compute checksum and send it */
	ph.length = length;
	ph.source = lsocket->address;
	ph.dest = fsocket->address;
	ph.protocol = UDP_PTCL;
	ph.zero = 0;
	/* All zeros and all ones is equivalent in one's complement arithmetic;
	 * the spec requires us to change zeros into ones to distinguish an
 	 * all-zero checksum from no checksum at all
	 */
	if((uhdr->checksum = cksum(&ph,hbp,length)) == 0)
		uhdr->checksum = 0xffffffff;

	udp_stat.sent++;
	ip_send(lsocket->address,fsocket->address,UDP_PTCL,tos,ttl,hbp,length,id,df);
	return length;
}

/* Accept a waiting datagram, if available. Returns length of datagram */
int
recv_udp(lsocket,fsocket,bp)
struct socket *lsocket;		/* Local socket to receive on */
struct socket *fsocket;		/* Place to stash incoming socket */
struct mbuf **bp;			/* Place to stash data packet */
{
	struct udp_cb *lookup_udp();
	register struct udp_cb *up;
	struct socket *sp;
	struct mbuf *buf;
	int16 length;

	up = lookup_udp(lsocket);
	if(up == NULLUDP){
		net_error = NO_CONN;
		return -1;
	}
	if(up->rcvcnt == 0){
		net_error = WOULDBLK;
		return -1;
	}
	buf = dequeue(&up->rcvq);
	up->rcvcnt--;

	sp = (struct socket *)buf->data;
	/* Fill in the user's foreign socket structure, if given */
	if(fsocket != NULLSOCK){
		fsocket->address = sp->address;
		fsocket->port = sp->port;
	}
	/* Strip socket header and hand data to user */
	pullup(&buf,NULLCHAR,sizeof(struct socket));
	length = len_mbuf(buf);
	if(bp != (struct mbuf **)NULL)
		*bp = buf;
	else
		free_p(buf);
	return length;
}
/* Delete a UDP control block */
int
del_udp(lsocket)
struct socket *lsocket;
{
	register struct udp_cb *up;
	struct udp_cb *lookup_udp();
	struct mbuf *bp;
	int16 hval;

	if((up = lookup_udp(lsocket)) == NULLUDP){
		net_error = INVALID;
		return -1;
	}		
	/* Get rid of any pending packets */
	while(up->rcvcnt != 0){
		bp = up->rcvq;
		up->rcvq = up->rcvq->anext;
		free_p(bp);
		up->rcvcnt--;
	}
	hval = hash_udp(&up->socket);
	if(udps[hval] == up){
		/* First on list */
		udps[hval] = up->next;
		up->next->prev = NULLUDP;
	} else {
		up->prev->next = up->next;
		up->next->prev = up->prev;
	}
	free((char *)up);
	return 0;
}
/* Process an incoming UDP datagram */
void
udp_input(bp,protocol,source,dest,tos,length,rxbroadcast)
struct mbuf *bp;
char protocol;
int32 source;		/* Source IP address */
int32 dest;		/* Dest IP address */
char tos;
int16 length;
char rxbroadcast;	/* The only protocol that accepts 'em */
{
	struct pseudo_header ph;
	struct udp_header udp;
	struct udp_cb *up,*lookup_udp();
	struct socket lsocket;
	struct socket *fsocket;
	struct mbuf *sp;
	int ckfail = 0;

	if(bp == NULLBUF)
		return;

	udp_stat.rcvd++;

	/* Create pseudo-header and verify checksum */
	ph.source = source;
	ph.dest = dest;
	ph.protocol = protocol;
	ph.length = length;
	ph.zero = 0;
	if(cksum(&ph,bp,length) != 0)
		/* Checksum apparently failed, note for later */
		ckfail++;

	/* Extract UDP header in host order */
	pullup(&bp,(char *)&udp,sizeof(struct udp_header));

	/* If the checksum field is zero, then ignore a checksum error.
	 * I think this is dangerously wrong, but it is in the spec.
	 */
	if(ckfail && udp.checksum != 0){
		udp_stat.cksum++;
		free_p(bp);
		return;
	}
	udp.dest = ntohs(udp.dest);
	udp.source = ntohs(udp.source);

	/* If this was a broadcast packet, pretend it was sent to us */
	if(rxbroadcast){
		lsocket.address = ip_addr;
		udp_stat.bdcsts++;
	} else
		lsocket.address = dest;

	lsocket.port = udp.dest;
	/* See if there's somebody around to read it */
	if((up = lookup_udp(&lsocket)) == NULLUDP){
		/* Nope, toss it on the floor */
		udp_stat.unknown++;
		free_p(bp);
		return;
	}
	/* Create a buffer which will contain the foreign socket info */
	if((sp = alloc_mbuf(sizeof(struct socket))) == NULLBUF){
		/* No space, drop whole packet */
		free_p(bp);
		return;
	}
	sp->cnt = sizeof(struct socket);

	fsocket = (struct socket *)sp->data;
	fsocket->address = source;
	fsocket->port = udp.source;

	/* Yes, remove the now redundant UDP header, chain the foreign socket
	 * info in front of it and queue it
	 */

	sp->next = bp;
	enqueue(&up->rcvq,sp);
	up->rcvcnt++;
	if(up->r_upcall)
		(*up->r_upcall)(&lsocket,up->rcvcnt);
}
/* Look up UDP socket, return control block pointer or NULLUDP if nonexistant */
static
struct udp_cb *
lookup_udp(socket)
struct socket *socket;
{
	register struct udp_cb *up;
	int16 hash_udp();

	up = udps[hash_udp(socket)];
	while(up != NULLUDP){
		if(bcmp((char *)socket,(char *)&up->socket,sizeof(struct socket)) == 0)
			break;
		up = up->next;
	}
	return up;
}

/* Hash a UDP socket (address and port) structure */
static
int16
hash_udp(socket)
struct socket *socket;
{
	int16 hval;

	/* Compute hash function on socket structure */
	hval = hiword(socket->address);
	hval ^= loword(socket->address);
	hval ^= socket->port;
	hval %= NUDP;
	return hval;
}
#ifdef	TRACE
/* Dump UDP statistics and control blocks */
doudpstat()
{
	extern struct udp_stat udp_stat;
	char *psocket();
	register struct udp_cb *udp;
	register int i;

	printf("sent %u rcvd %u bdcsts %u cksum err %u unknown socket %u\r\n",
	udp_stat.sent,udp_stat.rcvd,udp_stat.bdcsts,udp_stat.cksum,udp_stat.unknown);
#ifdef	AMIGA
	printf("&UCB   Rcv-Q  Local socket\r\n");
#else
	printf("&UCB Rcv-Q  Local socket\r\n");
#endif
	for(i=0;i<NUDP;i++){
		for(udp = udps[i];udp != NULLUDP; udp = udp->next){
#ifdef	AMIGA
			printf("%6lx%6u  %s\r\n",(unsigned long)udp,udp->rcvcnt,
#else
			printf("%x%6u  %s\r\n",(int)udp,udp->rcvcnt,
#endif
			 psocket(&udp->socket));
		}
	}
}
/* Dump a UDP header */
void
udp_dump(bp,source,dest,check)
struct mbuf *bp;
int32 source,dest;
int check;		/* If 0, bypass checksum verify */
{
	register struct udp_header *udph;
	struct pseudo_header ph;
	int i;
	char tmpbuf;

	if(bp == NULLBUF)
		return;
	/* If packet isn't in a single buffer, make a temporary copy and
	 * note the fact so we free it later
	 */
	if(bp->next != NULLBUF){
		bp = copy_p(bp,len_mbuf(bp));
		tmpbuf = 1;
	} else
		tmpbuf = 0;

	udph = (struct udp_header *)bp->data;
	printf("UDP:");
	printf(" %u->%u",ntohs(udph->source),
		ntohs(udph->dest));
	printf(" len %u",ntohs(udph->length));
	
	if(check){
		/* Verify checksum */
		ph.source = source;
		ph.dest = dest;
		ph.zero = 0;
		ph.protocol = UDP_PTCL;
		ph.length = len_mbuf(bp);
		if(udph->checksum != 0 && (i = cksum(&ph,bp,ph.length)) != 0)
			printf(" CHECKSUM ERROR (%u)",i);
	}
	printf("\r\n");
	if(tmpbuf)
		free_p(bp);
}

#endif
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.