#! /bin/sh
: This is a shar archive.  Extract with sh, not csh.
echo x - README
cat > README << '4266!Funky!Stuff!'
This directory contains the source for the PROM implementation
of BOOTP/TFTP.  Files include:

README		this file
doc		a short descriptive document
boott.c		main program
subs.c		misc subroutines
asm.s		assembler subroutines (just a few)

defs.h		misc defs
inet.h		various internet protocol headers
bootp.h		BOOTP protocol header
tftp.h		TFTP protocol header
ether.h		ether and arp headers

--------------------------------

Device drivers for various multibus ethernet boards
(not all of these are converted to the multi-driver
scheme).

if_ec.c		3COM 3c400 interface
if_il.c		Intel 82586 / Interlan NI3210 interface
if_ex.c		Excelan interface
4266!Funky!Stuff!
echo x - asm.s
cat > asm.s << '4266!Funky!Stuff!'
|	asm.s	1.1	01/08/86

|
| copy bytes, using movb,movw, or movl as appropriate.
|
	.globl	bcopy, bmove
bcopy:
bmove:
	movl	sp@(4),d0
	movl	d0,a0
	movl	d0,d1
	movl	sp@(8),d0
	movl	d0,a1
	orl	d0,d1
	movl	sp@(12),d0
	bles	6$
	orl	d0,d1
	btst	#0,d1
	beqs	2$
	subql	#1,d0
1$:	movb	a0@+,a1@+
	dbra	d0,1$
	rts

2$:	btst	#1,d0
	beqs	4$
	asrl	#1,d0
	subql	#1,d0
3$:	movw	a0@+,a1@+
	dbra	d0,3$
	rts

4$:	asrl	#2,d0
	subql	#1,d0
5$:	movl	a0@+,a1@+
	dbra	d0,5$
6$:	rts


|
| zero bytes
|
	.globl	bzero
bzero:	movl	sp@(4),a0
	movl	sp@(8),d0
	subql	#1,d0
	moveq	#0,d1
1$:	movb	d1,a0@+
	dbra	d0,1$
	rts


|
| compare bytes
|
	.globl	bcmp
bcmp:	movl	sp@(4),a0
	movl	sp@(8),a1
	movl	sp@(12),d0
	subql	#1,d0
1$:	cmpmb	a0@+,a1@+
	dbne	d0,1$
	bnes	2$
	moveq	#0,d0
	rts
2$:	moveq	#1,d0
	rts



|
| Return the 1's complement checksum of the word aligned buffer
| at s, for n bytes.
|
| in_cksum(s,n) 
| u_short *s; int n;
	
	.globl	in_cksum
in_cksum:
	movl	sp@(4),a0
	movl	sp@(8),d1
	asrl	#1,d1
	subql	#1,d1
	clrl	d0
1$:
	addw	a0@+,d0
	bccs	2$
	addqw	#1,d0
2$:
	dbra	d1,1$
	notw	d0
	rts
4266!Funky!Stuff!
echo x - bootp.h
cat > bootp.h << '4266!Funky!Stuff!'
/*
 * Bootstrap Protocol (BOOTP).  RFC 951.
 */

struct bootp {
	u_char	bp_op;		/* packet opcode type */
#define	BOOTREQUEST	1
#define	BOOTREPLY	2
	u_char	bp_htype;	/* hardware addr type */
	u_char	bp_hlen;	/* hardware addr length */
	u_char	bp_hops;	/* gateway hops */
	u_long	bp_xid;		/* transaction ID */
	u_short	bp_secs;	/* seconds since boot began */	
	u_short	bp_unused;
	iaddr_t	bp_ciaddr;	/* client IP address */
	iaddr_t	bp_yiaddr;	/* 'your' IP address */
	iaddr_t	bp_siaddr;	/* server IP address */
	iaddr_t	bp_giaddr;	/* gateway IP address */
	u_char	bp_chaddr[16];	/* client hardware address */
	u_char	bp_sname[64];	/* server host name */
	u_char	bp_file[128];	/* boot file name */
	u_char	bp_vend[64];	/* vendor-specific area */
};

/*
 * UDP port numbers, server and client.
 */
#define	IPPORT_BOOTPS		67
#define	IPPORT_BOOTPC		68

/*
 * "vendor" data permitted for Stanford boot clients.
 */
struct vend {
	u_char	v_magic[4];	/* magic number */
	u_long	v_flags;	/* flags/opcodes, etc. */
	u_char	v_unused[56];	/* currently unused */
};

#define	VM_STANFORD	"STAN"	/* v_magic for Stanford */

/* v_flags values */
#define	VF_PCBOOT	1	/* an IBMPC or Mac wants environment info */
#define	VF_HELP		2	/* help me, I'm not registered */
4266!Funky!Stuff!
echo x - boott.c
cat > boott.c << '4266!Funky!Stuff!'
/*	boott.c	1.1	12/17/85	*/

/*
 * BOOTP/TFTP prom bootstrap.
 *
 * history
 * 12/17/85	croft	created.
 */


#include "../tftp/defs.h"


/*
 * Interface functions and configuration.  This allows the bootstrap
 * to find all connected interfaces.
 */
extern ecinit(), ecread(), ecwrite();
extern ilinit(), ilread(), ilwrite();

struct iffun ecfun = { ecinit, ecread, ecwrite, "3com 3c400" };
struct iffun ilfun = { ilinit, ilread, ilwrite, "Interlan NI3210" };

struct ifconfig ifconfig[] = {
	&ecfun, 0x1f2000, 0,
	&ecfun, 0x1f4000, 0,
	&ecfun, 0x1f6000, 0,
	&ecfun, 0x1f8000, 0,
	&ilfun, 0x1f0500, 0x142000,
	&ilfun, 0x1f0510, 0x144000,
	&ilfun, 0x1f0520, 0x146000,
	&ilfun, 0x1f0530, 0x148000,
	0, 0, 0
};

/*
 */

struct ether_addr ether_broadcast = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };



/*
 * TFTP bootstrap.
 *
 * Called from ROM monitor with the string argument set to the command line.
 * The string can take the forms "filename" or "host:filename" or
 * "host:" or "?" or NULL.  "?" causes the program to prompt for 
 * information.  A NULL string has the same effect as a power-up boot.
 *
 * The command line string can also contain switches preceeding the
 * filename;  these are one or more letters, prefixed by a '-'.
 *
 * Since this code runs out of ROM, no formal globals can be used;  instead
 * a structure at the end of memory 'bglob' is setup for this purpose.  The 
 * macro 'bglobptr' can be used by subroutines to find this area.
 */
tftpboot(s)
	register char *s;
{
	register struct bglob *g;
	register char *cp;
	iaddr_t ia;
	char line[128];
	char promptflag = 0;
	extern int loadat, bcopy();
	int bootp(), tftpread();

	/* setup global area */

	if ((int)bglobptr + sizeof *g > GlobPtr->MemorySize) {
		printf("oops");
		return (0);
	}
	g = bglobptr;
	bzero((caddr_t)g, sizeof *g);
	g->loadat = loadat;	/* user code normally starts at 0x1000 */
	g->bootp = bootp;
	g->tftpread = tftpread;
	g->ifconfigp = ifconfig;

	printf("IP/BOOTP/TFTP Bootstrap.  (3/13/86, Stanford SUMEX)\n");
	while (*s == ' ') s++;
	while (*s == '-') {
		s++;
		switch (*s) {
		case 'p':	/* PUP boot */
			g->pupsw++;
			break;

		case 'h':	/* 'help', I'm not registered */
			g->helpsw++;
			break;

		case 'i':	/* select particular interface */
			s++;
			if (isdigit(*s)) {
				ifp = &ifconfig[*s&7];
				if (ifinit(ifp) == 0)
					return (0);
			} else {
				printif();  /* print available interfaces */
				return (0);
			}
			break;

		case 'd':	/* memory dump (TFTP WRQ) */
			g->dumpsw++;
			break;

		default:
			printf("valid switches: -pUP -hELP -dUMP -iNTERFACE\n");
			return (0);
		}
		while (*s && *s != ' ') s++;
		while (*s == ' ') s++;
	}

	if (ifp == 0) {
#ifdef ENETBOOT		/* 3 megabit ethernet */
		if (g->pupsw || iflocate() == 0) {
			extern enbootload();
			printf("Trying PUP 3mb.\n");
			return(enbootload(s));
		}
#else
		if (iflocate() == 0) {
			printf("No ethernet interfaces.\n");
			goto abort;
		}
#endif ENETBOOT
	}

	/*
	 * Check the command line for arguments or prompt for them.
	 */
	if (*s == '?') {	/* prompt for args */
		promptflag++;
		printf("A 'null reply' uses the default shown in brackets [].\n");
		getiaddr("Server address (or name) [broadcast] >", 
			&g->serveiaddr, g->sname);
		g->gateiaddr = g->serveiaddr;
		getiaddr("Gateway address [server] >", &g->gateiaddr, 0);
		getiaddr("My address [set by BOOTP server] >", &g->myiaddr, 0);
		printf("File name to load [determined by BOOTP server] >");
		gets(line);
		if (line[0])
			strcpy(g->file, line);
		printf("Load address [0x1000] >");
		gets(line);
		if ((ia = inet_addr(line)) >= 0x1000)
			g->loadat = ia;
	} else if (*s) {	/* parse command line */
		for (cp = s ; *cp ; cp++)
			if (*cp == ':')
				break;
		if (*cp) {	/* if hostname present */
			*cp = 0;
			cp++;
			strcpy(g->file, cp);
			if ((ia = inet_addr(s)) != -1)	/* was number */
				g->serveiaddr = ia;
			else
				strcpy(g->sname, s);
		} else {
			strcpy(g->file, s);
		}
	}
retry:
	/* pick a random transaction ID based on etheraddr & ms clock */
	g->xid = *(int *)&g->myeaddr.ether_addr_octet[2] ^ MSCLOCK;
	g->rand = g->xid;	/* also use as random seed */

	/*
	 * If we know the filename to boot, the server IP address,
	 * and my own IP address, then we can skip the BOOTP phase.
	 */
	if (g->file[0] == 0 || g->serveiaddr == 0 || g->myiaddr == 0)
		if (bootp() == 0)
			goto abort;	/* bootp failed */

	/*
	 * Do the TFTP, then call 'setup' to process the b.out header.
	 */
	if (tftpread(bcopy) == 0) {	/* if it fails, try another interface */
		printf("\nTFTP failed, retrying.\n");
		if (iflocate() == 0) {	/* same old interface */
			int oldclock;
			printf("Delaying 60 seconds.\n");
			oldclock = MSCLOCK; 
			while ((MSCLOCK - oldclock) < 60000);
		}
		goto retry;
	}
	printf("\n[Done]\n");
	return (setup(g->loadat, g->loadsize));
abort:
	return (0);
}


/*
 * BOOTP protocol.
 */
bootp()
{
	register struct bglob *g = bglobptr;
	register struct bootp *bh = (struct bootp *)g->wbuf;
	register struct bootp *rbh;
	int count, firstclock;
	int trys = 0;
	iaddr_t ia;

	/*
	 * setup the UDP and BOOTP headers.
	 */
	printf("Sending BOOTPs: ");
	g->uh.uh_sport = htons(IPPORT_BOOTPC);
	g->uh.uh_dport = htons(IPPORT_BOOTPS);
	g->uh.uh_ulen = htons(sizeof g->uh + sizeof (struct bootp));
	g->uh.uh_sum = 0;
	bzero(bh, sizeof *bh);
	bh->bp_op = BOOTREQUEST;
	bh->bp_htype = ARPHRD_ETHER;
	bh->bp_hlen = sizeof (struct ether_addr);
	bh->bp_xid = g->xid;
	bh->bp_ciaddr = g->myiaddr;	/* if known */
	bcopy((caddr_t)&g->myeaddr, bh->bp_chaddr, 6);
	strcpy(bh->bp_sname, g->sname);
	strcpy(bh->bp_file, g->file);
	if (g->helpsw) {
		register struct vend *vh = (struct vend *)bh->bp_vend;
		bcopy(VM_STANFORD, vh->v_magic, 4);
		vh->v_flags = htonl(VF_HELP);
	}

	ia = (g->serveiaddr ? g->serveiaddr : INADDR_BROADCAST);
	settimer();
	sendip(ia,ia,sizeof g->ih + sizeof g->uh + sizeof *bh);
	firstclock = MSCLOCK;
	/*
	 * Receive replies until we get a good one.
	 */
	for (;;) {
		if ((count = receive()) == 0) {	/* if timeout */
			if ((++trys & 7) == 0 && iflocate()) {
				/* time to try another interface */
				firstclock = MSCLOCK;
				bcopy((caddr_t)&g->myeaddr, bh->bp_chaddr,
					sizeof g->myeaddr);
			}
			bh->bp_secs = (MSCLOCK - firstclock) >> 10;
			settimer();
			sendip(ia,ia,sizeof g->ih + sizeof g->uh + sizeof *bh);
			continue;
		}
		if (count < (sizeof (struct ether_header) + sizeof (struct ip)
		    + sizeof (struct udphdr) + sizeof (struct bootp)))
			continue;
		if (checkudp(sizeof (struct ip) + sizeof (struct udphdr)
		    + sizeof (struct bootp), IPPORT_BOOTPC) == 0)
			continue;
		rbh = (struct bootp *)(g->rbuf + sizeof (struct ether_header)
		    + sizeof (struct ip) + sizeof (struct udphdr));
		if (rbh->bp_xid != g->xid || rbh->bp_op != BOOTREPLY
		    || bcmp(rbh->bp_chaddr, (caddr_t)&g->myeaddr,
		    sizeof g->myeaddr) != 0)
			continue;
		/* it's for us! */
		break;
	}
	g->myiaddr = rbh->bp_yiaddr;
	g->serveiaddr = rbh->bp_siaddr;
	g->gateiaddr = rbh->bp_giaddr;
	if (g->gateiaddr == 0)
		g->gateiaddr = g->serveiaddr;
	strcpy(g->file, rbh->bp_file);
	return(1);
}


/*
 * TFTP read request (RRQ) file g->file from host g->serveiaddr
 * and copy it into RAM at g->loadat.  'copy' argument is usually
 * 'bcopy', but there is a provision for RAM code to call tftpread in PROM.
 * For example, a just-loaded program (ethertip) may want to TFTP
 * read a port configuration file upon startup.  It does this by 
 * setting copy = the address of its own copy routine which will be called
 * after each buffer is received.
 */
tftpread(copy)
	int (*copy)();
{
	register struct bglob *g = bglobptr;
	register struct tftphdr *th = (struct tftphdr *)g->wbuf;
	int mytid, histid;
	int sendcount, recvcount;
	int trys, block;
	register struct rbufhdr *r;
	register u_char *cp;

	/*
	 * setup the UDP and TFTP headers.
	 */
	mytid = (g->xid & 0x3FFF);
	r = (struct rbufhdr *)&g->rbuf[-2]; /* skip short pad */
restart:
	mytid++;
	g->uh.uh_sport = htons(mytid);
	g->uh.uh_dport = htons(IPPORT_TFTP);
	g->uh.uh_sum = 0;
	th->th_opcode = htons(RRQ);
	strcpy(th->th_stuff, g->file);	/* set filename and mode */
	for (cp = (u_char *)th->th_stuff ; *cp ; cp++);
	cp++;
	strcpy(cp, "octet");
	sendcount = (cp - g->wbuf) + 6;
	g->uh.uh_ulen = htons(sendcount + sizeof g->uh);
	printf("\nTFTP read file %s\nMy address: ", g->file);
	printiaddr(g->myiaddr);
	printf("  Server: ");
	printiaddr(g->serveiaddr);
	printf("  Gateway: ");
	printiaddr(g->gateiaddr);
	putchar('\n');
	trys = block = 0;
	cp = (u_char *)g->loadat;
	g->loadsize = 0;
	g->mask = MASKMIN;	/* short initial timeout */
	/*
	 * send "RRQ" or "ACK" until entire file is read.
	 */
	for (;;) {
		if (++trys > 7) {
			if (block == 0)	/* if never started */
				return (0); /* try something else */
			printf("\nServer timeout(?)  Retrying.\n");
			goto restart;	/* maybe server timed out */
		}
		g->timer = 0;
		while (receive());	/* flush any queued packets */
		settimer();	/* start g->timer */
		sendip(g->serveiaddr, g->gateiaddr,
		    sendcount + sizeof g->uh + sizeof g->ih);
		/*
		 * receive until a good reply comes in.
		 */
		for (;;) {
			recvcount = receive();
			if (recvcount == 0)	/* if receive timeout */
				break;
			if (recvcount < rbufhdrsize
			    || checkudp(rbufhdrsize - sizeof r->eh, mytid) == 0
			    || r->ih.ip_src != g->serveiaddr)
				continue;
			if (block && r->uh.uh_sport != histid)
				continue;
			switch (ntohs(r->th.th_opcode)) {
			default:
				continue;

			case ERROR:
				printf("\nTFTP error.  Code=%d, String=%s\n",
				    ntohs(r->th.th_code), r->th.th_msg);
				return (0);

			case DATA:
				break;
				/* fall thru and break out of receive loop */
			}
			break;
		}
		if (recvcount == 0)
			continue;	/* if timeout, resend */
		if (ntohs(r->th.th_block != (block+1)))
			continue;	/* if not expected block, resend */
		putchar('-');
		if (block == 0) {	/* sending 1st ACK */
			sendcount = sizeof th->th_opcode + sizeof th->th_block;
			th->th_opcode = htons(ACK);
			histid = r->uh.uh_sport;
			g->uh.uh_dport = histid;
			g->uh.uh_ulen = htons(sendcount + sizeof g->uh);
		}
		block++;
		th->th_block = htons(block);
		trys = 0;	/* reset trys and timer counts */
		g->mask = MASKMIN;
		if ((recvcount=ntohs(r->uh.uh_ulen)
		    - sizeof r->uh - sizeof r->th) > 0) {
			(*copy)(&r->th.th_data, cp, recvcount);
			cp += recvcount;
			g->loadsize += recvcount;
		}
		if (recvcount < SEGSIZE) {  /* last block, send final ACK */
			sendip(g->serveiaddr, g->gateiaddr,
			    sendcount + sizeof g->uh + sizeof g->ih);
			break;	/* and return */
		}
	}
	return (1);	/* success */
}
		

/*
 * Send the IP packet in bglob.ih.  Perform ARP if necessary.
 * 'dstaddr' is the address to be used in the IP header,
 * 'gateaddr' is the immediate destination address.
 * 'count' is the length of the IP header plus data.
 */
sendip(dstaddr, gateaddr, count)
	iaddr_t dstaddr,gateaddr;
	int count;
{
	register struct bglob *g = bglobptr;

	g->ih.ip_v = IPVERSION;
	g->ih.ip_hl = sizeof (struct ip) >> 2;
	g->ih.ip_len = htons(count);
	g->ih.ip_id++;
	g->ih.ip_ttl = MAXTTL;
	g->ih.ip_p = IPPROTO_UDP;
	g->ih.ip_src = g->myiaddr;
	g->ih.ip_dst = dstaddr;
	g->ih.ip_sum = 0;
	g->ih.ip_sum = in_cksum((caddr_t)&g->ih, sizeof (struct ip));
	g->eh.ether_shost = g->myeaddr;
	g->eh.ether_type = htons(ETHERTYPE_IPTYPE);
	if (gateaddr == INADDR_BROADCAST)
		g->eh.ether_dhost = ether_broadcast;
	else if (gateaddr == g->arpiaddr && g->arpvalid)
		g->eh.ether_dhost = g->arpeaddr;
	else {
		sendarp(ARPOP_REQUEST, &gateaddr, 0);
		printf("(ARPreq)");
		g->arpiaddr = gateaddr;
		g->arpwait = 1;
		return;
	}
	(*ifp->ifc_fun->iff_write)(ifp, (caddr_t)&g->eh,
		count + sizeof g->eh);
	putchar('.');
}


/*
 * Send an ARP packet, 'tpa' is the target protocol address,
 * 'tha' is the target hardware address, 'req' is the
 * request type.
 */
sendarp(req, tpa, tha)
	iaddr_t	*tpa;
	eaddr_t	*tha;
{
	register struct bglob *g = bglobptr;
	struct ap {	/* arp packet */
		short  vaxpad;		/* for vax struct alignment */
		struct ether_header eh;
		struct ether_arp ah;
	} ap;
	register struct ether_arp *ea = &ap.ah;

	ap.eh.ether_shost = g->myeaddr;
	ap.eh.ether_dhost = (tha ? *tha : ether_broadcast);
	ap.eh.ether_type = htons(ETHERTYPE_ARPTYPE);
	bzero((caddr_t)ea, sizeof *ea);
	ea->arp_hrd = htons(ARPHRD_ETHER);
	ea->arp_pro = htons(ETHERTYPE_IPTYPE);
	ea->arp_hln = sizeof (eaddr_t);
	ea->arp_pln = sizeof (iaddr_t);
	ea->arp_op = htons(req);
	arp_sha(ea) = g->myeaddr;
	if (g->myiaddr == 0)
		return;		/* impossible ... */
	arp_spa(ea) = g->myiaddr;
	arp_tpa(ea) = *tpa;
	if (tha)
		arp_tha(ea) = *tha;
	(*ifp->ifc_fun->iff_write)(ifp, (caddr_t)&ap.eh, sizeof ap - 2);
}


/*
 * Receive next packet into g->rbuf.  Device driver 'read' routine
 * counts down delay in g->timer.  If an ARP reply packet is received,
 * we send any pending IP packet, then continue receiving.
 *
 * We finally return if a packet is received or the driver timer
 * goes off.  Return value is receive count or 0 if timeout.
 * Filter out any non-IP packets;  remove any IP options if present.
 */
receive()
{
	register struct bglob *g = bglobptr;
	register struct ether_header *eh;
	register struct ether_arp *ea;
	register struct ip *ip;
	int count;
	int options;

	for (;;) {
		if ((count = (*ifp->ifc_fun->iff_read)(ifp, g->rbuf,
		    sizeof g->rbuf, &g->timer)) == 0)
			return(0);	/* timeout */
		if (count < sizeof *eh)
			continue;
		eh = (struct ether_header *)g->rbuf;
		switch (ntohs(eh->ether_type)) {
		case ETHERTYPE_ARPTYPE:
			if (count < (sizeof *eh + sizeof *ea))
				continue;
			break;	/* break switch and handle ARP */

		case ETHERTYPE_IPTYPE:
			if (count < (sizeof *eh + sizeof *ip))
				continue;
			ip = (struct ip *)&eh[1];
			if (in_cksum((caddr_t)ip, ip->ip_hl<<2) != 0)
				continue;
			if (ip->ip_v != IPVERSION
			    || (ntohs(ip->ip_off) & 0x3FFF))
				continue;
			if ((options = (ip->ip_hl<<2) - sizeof *ip) > 0) {
				/* discard options */
				bcopy((caddr_t)&ip[1] + options,
				 (caddr_t)&ip[1], count-options-sizeof *ip);
				ip->ip_len = htons(ntohs(ip->ip_len)-options);
				count -= options;
			}
			return (count);

		default:
			continue;
		}

		/*
		 * we've received an ARP.
		 */
		ea = (struct ether_arp *)(g->rbuf + sizeof *eh);
		if (ntohs(ea->arp_hrd) != ARPHRD_ETHER
		    || ntohs(ea->arp_pro) != ETHERTYPE_IPTYPE
		    || ea->arp_hln != sizeof (eaddr_t)
		    || ea->arp_pln != sizeof (iaddr_t))
			continue;
		if (ntohs(ea->arp_op) == ARPOP_REPLY) {
			if (g->arpwait == 0
			    || arp_tpa(ea) != g->myiaddr
			    || arp_spa(ea) != g->arpiaddr)
				continue;
			g->arpeaddr = arp_sha(ea);
			g->arpwait = 0;
			g->arpvalid = 1;
			g->eh.ether_dhost = g->arpeaddr;
			(*ifp->ifc_fun->iff_write)(ifp, (caddr_t)&g->eh,
			    ntohs(g->ih.ip_len) + sizeof g->eh);
			putchar('.');
		} else if (ntohs(ea->arp_op) == ARPOP_REQUEST) {
			if (g->myiaddr == 0 || arp_tpa(ea) != g->myiaddr)
				continue;
			sendarp(ARPOP_REPLY, &arp_spa(ea), &arp_sha(ea));
			printf("(ARPrep)");
		}
	}
}


/*
 * Locate an interface to use for booting.  Returns 0 if failure.
 * Picks a new interface in rotating order each time it is called.
 * Returns nonzero (true) if a NEW interface has been found.
 */
iflocate()
{
	register struct bglob *g = bglobptr;
	struct ifconfig *oldifp;
	register int first = 0;
	int i;

	if (ifp == 0) {
		oldifp = ifp = &ifconfig[0];
		first++;
	} else {
		oldifp = ifp;
		ifp++;
		if (ifp->ifc_addr == 0) /* if end of list */
			ifp = &ifconfig[0];
	}
	for (;;) {
		if (ifinit(ifp)) {
			if (first || ifp != oldifp) {
				g->myiaddr = 0;
				g->mask = MASKMIN;
				return(1);
			}
			return(0);
		}
		ifp++;
		if (ifp->ifc_addr == 0) /* if end of list */
			ifp = &ifconfig[0];
		if (ifp == oldifp)
			return(0);	/* tried 'em all */
	}
}


/*
 * Setup the retry timer value (g->timer) as a random number
 * anded by the current backoff mask (g->mask).  Update the mask
 * and random number.
 *
 * Timer value is always >= MASKMIN and <= MASKMAX.
 */
settimer()
{
	register struct bglob *g = bglobptr;
	register int m = g->mask;
	register t;

	/* generate the next random number */
	g->rand = ((g->rand*1103515245 + 12345) & 0x7FFFFFFF);
	if ((t = (g->rand & m)) <= MASKMIN)
		t = MASKMIN;
	if (m < MASKMAX)
		g->mask = ((m<<1) | 1);
	g->timer = t;
}


/*
 * Check an incoming IP/UDP datagram in g->rbuf.
 * Returns 0 if bad.
 */
checkudp(miniplen, dport)
{
	register struct bglob *g = bglobptr;
	register struct rbufhdr *r;

	r = (struct rbufhdr *)&g->rbuf[-2]; /* skip short pad */
	if ( ntohs(r->ih.ip_len) < miniplen
	    || r->ih.ip_p != IPPROTO_UDP)
		return(0);
	if (g->myiaddr && r->ih.ip_dst != g->myiaddr)
		return(0);
	if (r->uh.uh_dport != htons(dport)
	    || ntohs(r->uh.uh_ulen) < (miniplen - sizeof r->ih))
		return(0);
	return(1);
}


/*
 * Print an internet address in dot form.
 */
printiaddr(ia)
	iaddr_t ia;
{
	ia = ntohl(ia);
	printf("%d.%d.%d.%d", (ia>>24)&0xFF, (ia>>16)&0xFF,
		(ia>>8)&0xFF, ia&0xFF);
}


/*
 * Print available interfaces.
 */
printif()
{
	register struct ifconfig *ifc;
	u_char eaddr[8];
	register i;
	register n;

	printf("Available 10MB interfaces:\n");
	i = 0;
	for (ifc = &ifconfig[0] ; ifc->ifc_addr ; ifc++) {
		printf("%d:\t", i++);
		ifinit(ifc);
	}
}


/*
 * Initialize interface at 'ifc'.  Returns true if 
 * initialization completed successfully.
 */
ifinit(ifc)
	register struct ifconfig *ifc;
{
	register struct bglob *g = bglobptr;
	u_char eaddr[8];
	register n;

	printf("%s at (%x,%x)", ifc->ifc_fun->iff_name,
		ifc->ifc_addr, ifc->ifc_addr2);
	if ((*ifc->ifc_fun->iff_init)(ifc, eaddr)) {
		printf(" etheraddr ");
		for (n = 0 ; n < 5 ; n++)
			printf("%x:", eaddr[n]);
		printf("%x\n", eaddr[5]);
		bcopy(eaddr, (caddr_t)&g->myeaddr, 6);
		return (1);
	} else {
		printf(" not found\n");
		return (0);
	}
}

4266!Funky!Stuff!
echo x - defs.h
cat > defs.h << '4266!Funky!Stuff!'
/*	defs.h	1.1	01/08/86	*/


/*
 * typedefs
 */
typedef	long		iaddr_t;	/* internet address type */
typedef	struct ether_addr eaddr_t;	/* ether address type */
typedef	char *		caddr_t;	/* "core" address */
typedef	unsigned char	u_char;		/* unsigned types */
typedef	unsigned short	u_short;
typedef	unsigned int	u_int;
typedef	unsigned long	u_long;
typedef	unsigned short	ushort;		/* sys III compat */
typedef unsigned short	n_short;
typedef unsigned long	n_time;

#if !defined(lowendian)
/*
 * Macros for number representation conversion.
 */
#define	ntohl(x)	(x)
#define	ntohs(x)	(x)
#define	htonl(x)	(x)
#define	htons(x)	(x)
#endif


#define isdigit(c) (c >= '0' && c <= '9')
#define	isspace(c) (c == ' ')
#define	isxdigit(c) (isdigit(c) || (c >= 'a' && c <= 'f'))
#define	islower(c) (c >= 'a' && c <= 'z')


#include "../h/globram.h"
#include "../tftp/inet.h"
#include "../tftp/tftp.h"
#include "../tftp/bootp.h"
#include "../tftp/ether.h"


/*
 * Interface functions, one set per hardware type.
 */
struct iffun {
	int	(*iff_init)();	/* initialize interface */
	int	(*iff_read)();	/* read from interface */
	int	(*iff_write)();
	char	iff_name[32];	/* printable name of interface */
};


/*
 * Interface configuration, one per possible board.
 */
struct ifconfig {
	struct iffun *ifc_fun;	/* associated functions */
	int	ifc_addr;	/* hardware address */
	int	ifc_addr2;	/* another hardware address, if needed */
};


/*
 * Bootstrap globals live at the end of memory, and can be referenced
 * through system global 'bglobptr' (defined below), which
 * points to this structure.
 */
struct bglob {
	/*
	 * the first few fields would be good candidates 
	 * to copy in from EAPROM.
	 */
	iaddr_t	myiaddr;	/* my internet address */
	iaddr_t	gateiaddr;	/* gateway ip addr */
	iaddr_t	serveiaddr;	/* server ip addr */
	eaddr_t	myeaddr;	/* my ethernet address */
	int	loadat;		/* load address */
	struct	ifconfig *ifptr;/* current interface */
	u_char	file[64];	/* file name to boot */
	u_char	sname[64];	/* server name */
				/* end EAPROM area */
	/*
	 * next fields allow a program just loaded to re-call PROM
	 * bootp or tftp, e.g. to load an ethertip configuration file.
	 */
	struct ifconfig *ifconfigp; /* points to PROM ifconfig */
	int	(*bootp)();	/* bootp entry point */
	int	(*tftpread)();	/* tftp read entry point */
	int	(*tftpwrite)();	/* tftp write (dump) entry */

	iaddr_t	arpiaddr;	/* arp cache: ip addr */
	eaddr_t	arpeaddr;	/* arp cache: ether addr */
	u_char	arpvalid;	/* arp cache: valid flag */
	u_char	arpwait;	/* arp cache: ip packet waiting to go out */
	u_char	pupsw;		/* -[p]up switch */
	u_char	dumpsw;		/* -[d]ump switch */
	u_char	helpsw;		/* -[h]elp switch */
	u_char	xxxxsw;
	int	loadsize;	/* size of load image */
	int	xid;		/* transaction id */
	int	rand;		/* random number (for backoff) */
	int	mask;		/* backoff mask */
	int	timer;		/* receive timer (decremented by driver) */
	short	vaxpad;		/* (for vax structure alignment) */
	struct ether_header eh;	/* ether header (contiguous with below) */
	struct ip ih;		/* ip header */
	struct udphdr uh;	/* udp header */
	u_char	wbuf[256];	/* udp data (write buffer) */
	u_char	rbuf[SEGSIZE+128]; /* read buffer */
};

struct rbufhdr {		/* can overlay rbuf */
	short  vaxpad;		/* for vax structure alignment */
	struct ether_header eh;
	struct ip ih;
	struct udphdr uh;
	struct tftphdr th;
};

#define	bglobptr	( (struct bglob *) (GlobPtr->MemorySize - 2048) )
#define	rbufhdrsize	( sizeof (struct rbufhdr) - 2 )
#define	ifp	g->ifptr	/* interface pointer */
#define	MASK1SEC	0x3FF	/* backoff mask (1023) of about
				   one second worth of ms. clock ticks */
#define	MASKMAX		(((MASK1SEC+1)*16)-1)	/* max mask (16 seconds)*/
#define	MASKMIN		(((MASK1SEC+1)*2)-1)	/* min mask (2 seconds)*/
#define	MSCLOCK		(GlobPtr->RefrCnt)	/* millisecond clock */
4266!Funky!Stuff!
echo x - ether.h
cat > ether.h << '4266!Funky!Stuff!'
/*	ether.h	6.3	84/03/20	*/

/*
 * Ethernet address - 6 octets
 */
struct ether_addr {
	u_char	ether_addr_octet[6];
};

/*
 * Structure of a 10Mb/s Ethernet header.
 */
struct	ether_header {
	struct	ether_addr ether_dhost;
	struct	ether_addr ether_shost;
	u_short	ether_type;
};

#define	ETHERTYPE_PUPTYPE	0x0200		/* PUP protocol */
#define	ETHERTYPE_IPTYPE	0x0800		/* IP protocol */
#define ETHERTYPE_ARPTYPE	0x0806		/* Addr. resolution protocol*/

#define	ETHERMTU	1500
#define	ETHERMIN	(60-14)

/*
 * Ethernet Address Resolution Protocol.
 *
 * See RFC 826 for protocol description.  Structure below is adapted
 * to resolving internet addresses.  Field names used correspond to 
 * RFC 826.
 */
struct	ether_arp {
	u_short	arp_hrd;	/* format of hardware address */
#define ARPHRD_ETHER 	1	/* ethernet hardware address */
	u_short	arp_pro;	/* format of proto. address (ETHERPUP_IPTYPE) */
	u_char	arp_hln;	/* length of hardware address (6) */
	u_char	arp_pln;	/* length of protocol address (4) */
	u_short	arp_op;
#define	ARPOP_REQUEST	1	/* request to resolve address */
#define	ARPOP_REPLY	2	/* response to previous request */
#define RARPOP_REQUEST	3	/* Reverse ARP request (RARP packets only!) */
#define RARPOP_REPLY	4	/* Reverse ARP reply (RARP packets only!) */
	u_char	arp_xsha[6];	/* sender hardware address */
	u_char	arp_xspa[4];	/* sender protocol address */
	u_char	arp_xtha[6];	/* target hardware address */
	u_char	arp_xtpa[4];	/* target protocol address */
};
#define	arp_sha(ea)	(*(eaddr_t *)(ea)->arp_xsha)
#define	arp_spa(ea)	(*(iaddr_t *)(ea)->arp_xspa)
#define	arp_tha(ea)	(*(eaddr_t *)(ea)->arp_xtha)
#define	arp_tpa(ea)	(*(iaddr_t *)(ea)->arp_xtpa)
4266!Funky!Stuff!
echo x - if_ec.c
cat > if_ec.c << '4266!Funky!Stuff!'
/*	if_ec.c	1.1	2/6/86	*/

/*
 * 3COM 3C400 Ethernet driver.
 *
 * history
 * 01/22/86	croft	created.
 */


#include "../tftp/defs.h"
#undef ifp


/*
 * 3Com Ethernet controller registers.
 */
struct ecdevice {
	u_short	ec_csr;		/* control and status */
	u_short	ec_back;	/* backoff value */
	u_char	ec_pad1[0x400-2*2];
	u_char	ec_arom[6];	/* address ROM */
	u_char	ec_pad2[0x200-6];
	u_char	ec_aram[6];	/* address RAM */
	u_char	ec_pad3[0x200-6];
	u_char	ec_tbuf[2048];	/* transmit buffer */
	u_char	ec_abuf[2048];	/* receive buffer A */
	u_char	ec_bbuf[2048];	/* receive buffer B */
};

/*
 * Control and status bits
 */
#define	EC_BBSW		0x8000		/* buffer B belongs to ether */
#define	EC_ABSW		0x4000		/* buffer A belongs to ether */
#define	EC_TBSW		0x2000		/* transmit buffer belongs to ether */
#define	EC_JAM		0x1000		/* Ethernet jammed (collision) */
#define	EC_AMSW		0x0800		/* address RAM belongs to ether */
#define	EC_RBBA		0x0400		/* buffer B older than A */
#define	EC_RESET	0x0100		/* reset controller */
#define	EC_BINT		0x0080		/* buffer B interrupt enable */
#define	EC_AINT		0x0040		/* buffer A interrupt enable */
#define	EC_TINT		0x0020		/* transmitter interrupt enable */
#define	EC_JINT		0x0010		/* jam interrupt enable */
#define	EC_INTPA	0x00ff		/* mask for interrupt and PA fields */
#define	EC_PAMASK	0x000f		/* PA field */

#define	EC_PA		0x0007		/* receive mine+broadcast-errors */

/*
 * Receive status bits
 */
#define	EC_FCSERR	0x8000		/* FCS error */
#define	EC_BROADCAST	0x4000		/* packet was broadcast packet */
#define	EC_RGERR	0x2000		/* range error */
#define	EC_ADDRMATCH	0x1000		/* address match */
#define	EC_FRERR	0x0800		/* framing error */
#define	EC_DOFF		0x07ff		/* first free byte */

#define	ECRDOFF		2		/* packet offset in read buffer */
#define	ECMAXTDOFF	(2048-60)	/* max packet offset (min size) */

/* Macros to set and clear CSR bits */

#define CSRSET(v) ec->ec_csr = (ec->ec_csr & EC_INTPA) | (v)
#define CSRCLR(v) ec->ec_csr = ec->ec_csr & (EC_INTPA & ~(v))


/*
 * Initialize interface.  Returns 0 if failure.
 * Stores our ether address in the 2nd argument.
 */
ecinit(ifp, eaddr)
	struct ifconfig *ifp;
	char *eaddr;
{
	register struct ecdevice *ec = (struct ecdevice *)ifp->ifc_addr;

	if (!ProbeAddress((caddr_t)ec))
		return(0);
	ecreset(ec);
	bcopy(ec->ec_arom, eaddr, 6);
	return (1);
}


/*
 * Reset the interface and load the address RAM.
 * Enable the receive buffers.
 */
ecreset(ec)
	register struct ecdevice *ec;
{
	register i;

	ec->ec_csr = EC_RESET;
	for (i=0 ; i<10 ; i++);
	bcopy(ec->ec_arom, ec->ec_aram, 6);
	CSRSET(EC_AMSW);
	CSRSET(EC_PA | EC_ABSW | EC_BBSW);
}


/*
 * Write (xmit) packet to ether.
 */
ecwrite(ifp, buf, count)
	struct ifconfig *ifp;
	u_char *buf;
{
	register u_char *cp;
	register struct ecdevice *ec = (struct ecdevice *)ifp->ifc_addr;
	short mask = -1, back;
	int time = MSCLOCK + 500;	/* .5 seconds */

	if (count < 60)
		count = 60;
	cp = &ec->ec_abuf[-count];
	bcopy(buf, cp, count);
	*(short *)(ec->ec_tbuf) = cp - (u_char *)ec->ec_tbuf;
	CSRSET(EC_TBSW);
	for (;;) {
		if (MSCLOCK - time > 0) {
			ecreset(ec);
			return;
		}
		if (ec->ec_csr & EC_JAM) {
			mask <<= 1;
			if (mask == 0) {
				ecreset(ec);
				return;
			}
			back = -(MSCLOCK & ~mask);
			if (back == 0)
				back = -(0x5555 & ~mask);
			ec->ec_back = back;
			CSRSET(EC_JAM);
		}
		if ((ec->ec_csr & EC_TBSW) == 0)
			return;
	}
}


/*
 * Read packet from ether.  Count down the ms. delay in *timer
 * and return if timeout (< 0).
 */
ecread(ifp, buf, count, timer)
	struct ifconfig *ifp;
	u_char *buf;
	register int *timer;
{
	register struct ecdevice *ec = (struct ecdevice *)ifp->ifc_addr;
	int time;
	short *sp;
	int xbsw = 0;
	int rcount;

	time = MSCLOCK;
wait:
	CSRSET(xbsw);
	for (;;) {
		if ((ec->ec_csr & EC_ABSW) == 0) {
			sp = (short *)ec->ec_abuf;
			xbsw = EC_ABSW;
			break;
		}
		if ((ec->ec_csr & EC_BBSW) == 0) {
			sp = (short *)ec->ec_bbuf;
			xbsw = EC_BBSW;
			break;
		}
		if (MSCLOCK - time >= *timer) {
			*timer -= (MSCLOCK - time);
			return (0);
		}
	}
	rcount = (*sp & EC_DOFF);
	if (rcount > count)
		rcount = count;
	bcopy((caddr_t)&sp[1], buf, rcount);
	CSRSET(xbsw);
	*timer -= (MSCLOCK - time);
	return (rcount);
}
4266!Funky!Stuff!
echo x - if_il.c
cat > if_il.c << '4266!Funky!Stuff!'
/*	il.h	1.0	84/09/06	*/

/*
 * Interlan NI3210 (Intel 82586) driver.
 *
 * history
 * 09/06/84	croft	created.
 * 02/05/85	croft	recover after bogus 'all ones' packets
 * 03/01/86	croft	standalone boot version.
 */


#include "../tftp/defs.h"
#undef ifp

#define	INTIL		0x0	/* interrupt level for il */
#define	splil()		0	/* asm("movw #02700, sr"); */
#define	splx()
#define	WAITCMD()	while (scb->sc_cmd)

#define	NRFD		12	/* number of receive frame descriptors */
#define	RBSZ		320	/* receive buffer size */


struct csregs {			/* command and status registers */
	u_char	cs_2;		/* command/status 2 */
	u_char	cs_1;	
	u_char	cs_xxx[6];	/* unused */
	u_short	cs_bar;		/* DMA buffer address register */
	u_short	cs_mar[2];	/* ..  memory   ..      ..     */
	u_short	cs_count;	/* DMA count */
};

/* cs_1 bits */
#define	CS_DO		0x80	/* start DMA */
#define	CS_DONE		0x40	/* DMA done */
#define	CS_TWORDS	0x20	/* DMA command, transfer words */
#define	CS_TSWAP	0x10	/*  ..  ..	swap bytes */
#define	CS_TWRITE	0x08	/*  ..  ..	write */
#define	CS_TREAD	0	/*  ..  ..	read */
#define	CS_INTA		0x07	/* DMA int level */

/* cs_2 bits */
#define	CS_CA		0x80	/* channel attention (W), interrupt (R) */
#define	CS_ONLINE	0x40	/* online */
#define	CS_SWAP		0x20	/* swap bytes */
#define	CS_IEDMA	0x10	/* enable DMA ints */
#define	CS_NRESET	0x08	/* not reset */
#define	CS_INTB		0x07	/* interrupt level */

#define	CS(bits)	CSOFF(bits|CS_ONLINE) /* bits to set in cs_2 */
#define	CSOFF(bits)	cs->cs_2 = (bits|CS_NRESET|INTIL)


/*
 * Transmit/receive buffer descriptor.
 */
struct bd {
	u_short	bd_count;	/* data count */
	u_short	bd_next;	/* link to next */
	u_short	bd_buf;		/* buffer pointer */
	u_short	bd_bufhi;
	u_short	bd_size;	/* buffer size (rbd only) */
};

/* bd flags */
/* in bd_count */
#define	BD_EOF		0x8000	/* end of frame */
#define	BD_F		0x4000	/* filled by 82586 */
#define	BD_COUNT	0x3FFF	/* count field */
/* in bd_size */
#define	BD_EL		0x8000	/* end of list */


/*
 * Command block / receive frame descriptor.
 */
struct cb {
	u_short	cb_status;	/* status */
	u_short	cb_cmd;		/* command */
	u_short	cb_link;	/* link to next */
	u_short	cb_param;	/* parameters here and following */
};

/* cb bits */
/* status */
#define	CB_C		0x8000	/* complete */
#define	CB_B		0x4000	/* busy */
#define	CB_OK		0x2000	/* ok */
#define	CB_ABORT	0x1000	/* aborted */
/* command */
#define	CB_EL		0x8000	/* end of list */
#define	CB_S		0x4000	/* suspend */
#define	CB_I		0x2000	/* interrupt (CX) when done */
/* action commands */
#define	CBC_IASETUP	1	/* individual address setup */
#define	CBC_CONFIG	2	/* configure */
#define	CBC_TRANS	4	/* transmit */


/*
 * System control block, plus some driver static structures.
 */
struct scb {
	u_short	sc_status;	/* status */
	u_short	sc_cmd;		/* command */
	u_short	sc_clist;	/* command list */
	u_short	sc_rlist;	/* receive frame list */
	u_short	sc_crcerrs;	/* crc errors */
	u_short	sc_alnerrs;	/* alignment errors */
	u_short	sc_rscerrs;	/* resource errors (lack of rfd/rbd's) */
	u_short	sc_ovrnerrs;	/* overrun errors (mem bus not avail) */
				/* end of 82586 registers */

	caddr_t	ss_base;	/* used by STOA macro */
	struct cb *ss_rfdhead;	/* receive frame descriptors, head */
	struct cb *ss_rfdtail;	/* receive frame descriptors, tail */
	struct bd *ss_rbdhead;	/* receive buffer descriptors, head */
	struct bd *ss_rbdtail;	/* receive buffer descriptors, tail */

	struct cb sc_cb;	/* general command block */
	u_char	sc_data[1024];	/* transmit buffer/general data */
	struct bd sc_tbd;	/* transmit buffer descriptor */
	struct cb sc_rfd[NRFD];	/* receive frame descriptors */
	struct bd sc_rbd[1];	/* first receive buffer descriptor */
};

/* status bits */
#define	SC_CX		0x8000	/* command executed */
#define	SC_FR		0x4000	/* frame received */
#define	SC_CNR		0x2000	/* cmd unit not ready */
#define	SC_RNR		0x1000	/* rec  .. */
#define	SC_CUS		0x700	/* command unit status field */
#define	SC_RUS		0x70	/* receive unit status field */
#define	SC_RESET	0x80	/* software reset */

/* command unit status */
#define	CUS_IDLE	0x0	/* idle */
#define	CUS_SUSP	0x100	/* suspended */
#define	CUS_READY	0x200	/* ready */

/* receive unit status */
#define	RUS_IDLE	0x0	/* idle */
#define	RUS_SUSP	0x10	/* suspended */
#define	RUS_NORES	0x20	/* no resources */
#define	RUS_READY	0x40	/* ready */

/* command unit commands */
#define	CUC_START	0x100	/* start */
#define	CUC_ABORT	0x400	/* abort */

/* receive unit commands */
#define	RUC_START	0x10	/* start */
#define	RUC_ABORT	0x40	/* abort */

#define	STOA(type,off)	((type)(scb->ss_base+(off)))/* scb offset to addr */
#define	ATOS(addr)	((int)(addr))	/* addr to scb offset */

#define	SCBLEN		0x2000	/* length of scb and structures */
#define	ISCPOFF		SCBLEN-18 /* offset of ISCP/SCP */


/*
 * System configuration pointer (ISCP and SCP) at end of memory.
 */
short	iliscp[] = {
	1,			/* busy */
	0,			/* scb at offset 0, base 0 */
	0,
	0,
	0,			/* 16 bit bus */
	0,
	0,
	ISCPOFF,		/* iscp address */
	0
};


/*
 * Configuration command data (from page 4-5 of NI3210 manual).
 */
short	ilconfig[] = {
	0,	CB_EL|CBC_CONFIG,
	0,	0x080c,
	0x2e00,	0x6000,
	0xf200,	0x0000,
	0x0040
};




/*
 * Initialize interface.
 */
ilinit(ifp, eaddr)
	struct ifconfig *ifp;
	char *eaddr;
{
	register struct	csregs *cs = (struct csregs *)ifp->ifc_addr;
	register struct	scb *scb = (struct scb *)ifp->ifc_addr2;
	int s;
	register struct cb *rfd;
	register struct	bd *rbd;
	short *iscp = (short *)((caddr_t)scb + ISCPOFF);

	s = splil();
	if (!ProbeAddress((caddr_t)cs) || !ProbeAddress((caddr_t)scb))
		return (0);
	/*
	 * setup initial SCB
	 */
	cs->cs_1 = INTIL;
	cs->cs_2 = INTIL;
	bzero((caddr_t)scb, SCBLEN);
	scb->ss_base = (caddr_t)((int)scb & 0xFF0000);
	bcopy((caddr_t)iliscp, iscp, sizeof iliscp);
	CSOFF(0);
	CSOFF(CS_CA);
	while ((cs->cs_2 & CS_CA) == 0);	/* until interrupt asserted */
	if (*iscp != 0 || scb->sc_status != (SC_CX|SC_CNR))
		return (0);	/* sanity check */
	scb->sc_cmd = (SC_CX|SC_CNR);	/* acknowledge */
	CSOFF(CS_CA);
	while ((cs->cs_2 & CS_CA) != 0);	/* until interrupt drops */
	bzero(iscp, sizeof iliscp);
	/*
	 * configure and iasetup
	 */
	bcopy((caddr_t)ilconfig, (caddr_t)&scb->sc_cb, sizeof ilconfig);
	scb->sc_clist = ATOS(&scb->sc_cb);
	ilcmd(ifp);	/* configure */
	scb->sc_cb.cb_cmd = (CB_EL|CBC_IASETUP);
	/* odd bcopy count forces byte xfer;  src/dst already swapped. */
	bcopy((caddr_t)&cs->cs_bar, (caddr_t)&scb->sc_cb.cb_param, 7);
	CSOFF(CS_SWAP);
	bcopy((caddr_t)&scb->sc_cb.cb_param, eaddr, 6);
	CSOFF(0);
	ilcmd(ifp);	/* iasetup */
	scb->sc_cb.cb_cmd = (CB_EL|CB_I|CBC_TRANS);  /* leave setup for xmit */
	scb->sc_cb.cb_param = ATOS(&scb->sc_tbd);
	scb->sc_tbd.bd_buf = ATOS(scb->sc_data);
	/*
	 * setup receive unit
	 */
	for (rfd = &scb->sc_rfd[0] ; rfd < &scb->sc_rfd[NRFD] ; rfd++ ) {
		rfd->cb_link = ATOS(rfd+1);
		rfd->cb_param = 0xFFFF;
	}
	rfd--;
	scb->ss_rfdtail = rfd;
	rfd->cb_cmd = CB_EL;
	rfd->cb_link = ATOS(&scb->sc_rfd[0]);
	scb->ss_rfdhead = rfd = &scb->sc_rfd[0];
	rfd->cb_param = ATOS(&scb->sc_rbd[0]);
	for (rbd = &scb->sc_rbd[0] ;
	    (caddr_t)(rbd+1) + RBSZ < (caddr_t)(scb) + SCBLEN ;
	    rbd = (struct bd *)((caddr_t)rbd + sizeof *rbd + RBSZ) ) {
		rbd->bd_next = ATOS((caddr_t)(rbd+1)+RBSZ);
		rbd->bd_size = RBSZ;
		rbd->bd_buf = ATOS(rbd+1);
	}
	rbd = (struct bd *)((caddr_t)rbd - sizeof *rbd - RBSZ);
	scb->ss_rbdtail = rbd;
	scb->ss_rbdhead = &scb->sc_rbd[0];
	rbd->bd_next = ATOS(&scb->sc_rbd[0]);
	rbd->bd_size |= BD_EL;
	scb->sc_rlist = ATOS(&scb->sc_rfd[0]);
	WAITCMD();
	scb->sc_cmd = (SC_CX|SC_CNR|SC_FR|SC_RNR|RUC_START);
	CS(CS_CA);
	splx(s);
}


/*
 * Read packet. Count down the ms. delay in *timer and
 * return if timeout (*timer < 0).
 */
ilread(ifp, buf, bcount, timer)
	struct ifconfig *ifp;
	u_char *buf;
	int *timer;
{
	register struct	csregs *cs = (struct csregs *)ifp->ifc_addr;
	register struct	scb *scb = (struct scb *)ifp->ifc_addr2;
	int s,scbstatus;
	register struct cb *rfd;
	register struct bd *rbd;
	int time;
	u_char *cp;
	int free, count;

	/* spl used to guard against NI3210 edge vs. level trig. intr. */
	s = splil();
	time = MSCLOCK;
wait:
	while ((scbstatus = (scb->sc_status & (SC_FR|SC_RNR))) == 0) {
		if (MSCLOCK - time >= *timer) {
			*timer -= (MSCLOCK - time);
			return (0);
		}
	}
	WAITCMD();
	scb->sc_cmd = scbstatus;
	CS(CS_CA);	/* ack current interrupts */
	WAITCMD();
	if ((scbstatus & SC_RNR) && 
	    ((rfd = scb->ss_rfdhead)->cb_status & CB_B)) {
		/*
		 * Receive unit not ready, yet still busy on 1st frame!
		 * This is a bogus packet of 'infinite' length and all
		 * ones.  Restart the RU.
		 */
		for (rbd = STOA(struct bd *, rfd->cb_param) ;
		    rbd->bd_count & BD_F ;
		    rbd = STOA(struct bd *, rbd->bd_next) ) {
			rbd->bd_count = 0;
		}
		ilrstart(ifp);
	}
	if ((scbstatus & SC_FR) == 0)
		goto wait;	/* if frame not received */
	/*
	 * receive frame.
	 */
	rfd = scb->ss_rfdhead; 
	if ((rfd->cb_status&CB_C) == 0)
		goto wait;	/* impossible? */
	free = bcount;
	cp = buf;
	for (rbd = STOA(struct bd *, rfd->cb_param) ;
	    rbd->bd_count & BD_F ;
	    rbd = STOA(struct bd *, rbd->bd_next) ) {
		count = (rbd->bd_count & BD_COUNT);
		if (count <= free) {
			CS(CS_SWAP);
			bcopy((caddr_t)(rbd+1), cp, count);
			CS(0);		/* no swap */
			cp += count;
			free -= count;
		} 
		if (rbd->bd_count & BD_EOF)
			break;
		rbd->bd_count = 0;
	}
	rbd->bd_count = 0;
	rbd->bd_size |= BD_EL;
	scb->ss_rbdtail->bd_size &= BD_COUNT; /* clear previous EL */
	scb->ss_rbdtail = rbd;
	scb->ss_rbdhead = STOA(struct bd *, rbd->bd_next);
	count = bcount - free;

	rfd->cb_status = 0;
	rfd->cb_cmd = CB_EL;
	rfd->cb_param = 0xFFFF;
	scb->ss_rfdtail->cb_cmd = 0;	/* clear previous CB_EL */
	scb->ss_rfdtail = rfd;

	rfd = scb->ss_rfdhead = STOA(struct cb *, rfd->cb_link);
	ilrstart(ifp);	/* kick the receive unit */
	*timer -= (MSCLOCK - time);
	splx(s);
	return (count);
}


/*
 * Write (transmit) packet.
 */
ilwrite(ifp, buf, count)
	struct ifconfig *ifp;
	u_char *buf;
{
	register struct	csregs *cs = (struct csregs *)ifp->ifc_addr;
	register struct	scb *scb = (struct scb *)ifp->ifc_addr2;
	int s;
	int time = MSCLOCK + 500;	/* .5 second delay */

	s = splil();
	CS(CS_SWAP);
	bcopy(buf, (caddr_t)&scb->sc_data[0], count);
	CS(0);
	if (count < 64)
		count = 64;
	scb->sc_tbd.bd_count = (count | BD_EOF);
	WAITCMD();
	scb->sc_cmd = (SC_CX|SC_CNR|CUC_START);
	CS(CS_CA);
	WAITCMD();
	while ((scb->sc_status & SC_CX) == 0) {
		if (MSCLOCK - time > 0) {
			char eaddr[6];
			printf("(iljammed)");
			ilinit(ifp, eaddr);
			goto out;
		}
	}
	if ((scb->sc_cb.cb_status & CB_C) == 0
	    || (scb->sc_cb.cb_status & CB_OK) == 0)
		printf("(ilerror)");
out:
	splx(s);
}

	
/*
 * Execute a single command, in scb->sc_cb.
 */
ilcmd(ifp)
	struct ifconfig *ifp;
{
	register struct	csregs *cs = (struct csregs *)ifp->ifc_addr;
	register struct	scb *scb = (struct scb *)ifp->ifc_addr2;

	WAITCMD();
	scb->sc_cmd = (SC_CX|SC_CNR|CUC_START);
	CSOFF(CS_CA);
	WAITCMD();
	while ((scb->sc_status & SC_CNR) == 0);
	scb->sc_cmd = (SC_CX|SC_CNR);	/* ack, clear interr */
	CSOFF(CS_CA);
}


/*
 * Start receive unit, if needed.
 */
ilrstart(ifp)
	struct ifconfig *ifp;
{
	register struct	csregs *cs = (struct csregs *)ifp->ifc_addr;
	register struct	scb *scb = (struct scb *)ifp->ifc_addr2;

	/* ignore if RU already running or less than 2 elements on lists */
	if ((scb->sc_status & SC_RUS) == RUS_READY)
		return;
	if (scb->ss_rfdhead->cb_cmd & CB_EL)
		return;
	if (scb->ss_rbdhead->bd_size & BD_EL)
		return;
	WAITCMD();
	scb->ss_rfdhead->cb_param = ATOS(scb->ss_rbdhead);
	scb->sc_rlist = ATOS(scb->ss_rfdhead);
	scb->sc_cmd = RUC_START;
	CS(CS_CA);
}
4266!Funky!Stuff!
echo x - inet.h
cat > inet.h << '4266!Funky!Stuff!'
/*	inet.h	1.0	84/12/11	*/

/*
 * Constants and structures defined by the internet system,
 * Per RFC 790, September 1981.
 */

/*
 * Protocols
 */
#define	IPPROTO_ICMP		1		/* control message protocol */
#define	IPPROTO_GGP		2		/* gateway^2 (deprecated) */
#define	IPPROTO_TCP		6		/* tcp */
#define	IPPROTO_PUP		12		/* pup */
#define	IPPROTO_UDP		17		/* user datagram protocol */
#define	IPPROTO_ND		77		/* UNOFFICIAL net disk proto */

#define	IPPROTO_RAW		255		/* raw IP packet */
#define	IPPROTO_MAX		256

/*
 * Port/socket numbers: network standard functions
 */
#define	IPPORT_ECHO		7
#define	IPPORT_DISCARD		9
#define	IPPORT_SYSTAT		11
#define	IPPORT_DAYTIME		13
#define	IPPORT_NETSTAT		15
#define	IPPORT_FTP		21
#define	IPPORT_TELNET		23
#define	IPPORT_SMTP		25
#define	IPPORT_TIMESERVER	37
#define	IPPORT_NAMESERVER	42
#define	IPPORT_WHOIS		43
#define	IPPORT_MTP		57

/*
 * Port/socket numbers: host specific functions
 */
#define	IPPORT_TFTP		69
#define	IPPORT_RJE		77
#define	IPPORT_FINGER		79
#define	IPPORT_TTYLINK		87
#define	IPPORT_SUPDUP		95

/*
 * UNIX TCP sockets
 */
#define	IPPORT_EXECSERVER	512
#define	IPPORT_LOGINSERVER	513
#define	IPPORT_CMDSERVER	514
#define	IPPORT_EFSSERVER	520

/*
 * UNIX UDP sockets
 */
#define	IPPORT_BIFFUDP		512
#define	IPPORT_WHOSERVER	513
#define	IPPORT_ROUTESERVER	520	/* 520+1 also used */

/*
 * Ports < IPPORT_RESERVED are reserved for
 * privileged processes (e.g. root).
 */
#define	IPPORT_RESERVED		1024

/*
 * Link numbers
 */
#define	IMPLINK_IP		155
#define	IMPLINK_LOWEXPER	156
#define	IMPLINK_HIGHEXPER	158


/*
 * Definitions of bits in internet address integers.
 */
#define	IN_CLASSA(i)		((((long)(i))&0x80000000)==0)
#define	IN_CLASSA_NET		0xff000000
#define	IN_CLASSA_NSHIFT	24
#define	IN_CLASSA_HOST		0x00ffffff

#define	IN_CLASSB(i)		((((long)(i))&0xc0000000)==0x80000000)
#define	IN_CLASSB_NET		0xffff0000
#define	IN_CLASSB_NSHIFT	16
#define	IN_CLASSB_HOST		0x0000ffff

#define	IN_CLASSC(i)		((((long)(i))&0xc0000000)==0xc0000000)
#define	IN_CLASSC_NET		0xffffff00
#define	IN_CLASSC_NSHIFT	8
#define	IN_CLASSC_HOST		0x000000ff

#define	INADDR_ANY	0x00000000
#define	INADDR_BROADCAST	0xFFFFFFFF


/*
 * Definitions for internet protocol version 4.
 * Per RFC 791, September 1981.
 */
#define	IPVERSION	4

/*
 * Structure of an internet header, naked of options.
 *
 * We declare ip_len and ip_off to be short, rather than u_short
 * pragmatically since otherwise unsigned comparisons can result
 * against negative integers quite easily, and fail in subtle ways.
 */
struct ip {
#if defined(lowendian)
	u_char	ip_hl:4,		/* header length */
		ip_v:4;			/* version */
#else
	u_char	ip_v:4,			/* version */
		ip_hl:4;		/* header length */
#endif
	u_char	ip_tos;			/* type of service */
	short	ip_len;			/* total length */
	u_short	ip_id;			/* identification */
	short	ip_off;			/* fragment offset field */
#define	IP_DF 0x4000			/* dont fragment flag */
#define	IP_MF 0x2000			/* more fragments flag */
	u_char	ip_ttl;			/* time to live */
	u_char	ip_p;			/* protocol */
	u_short	ip_sum;			/* checksum */
	iaddr_t	ip_src,ip_dst;		/* source and dest address */
};

/*
 * Definitions for options.
 */
#define	IPOPT_COPIED(o)		((o)&0x80)
#define	IPOPT_CLASS(o)		((o)&0x60)
#define	IPOPT_NUMBER(o)		((o)&0x1f)

#define	IPOPT_CONTROL		0x00
#define	IPOPT_RESERVED1		0x20
#define	IPOPT_DEBMEAS		0x40
#define	IPOPT_RESERVED2		0x60

#define	IPOPT_EOL		0		/* end of option list */
#define	IPOPT_NOP		1		/* no operation */

#define	IPOPT_RR		7		/* record packet route */
#define	IPOPT_TS		68		/* timestamp */
#define	IPOPT_SECURITY		130		/* provide s,c,h,tcc */
#define	IPOPT_LSRR		131		/* loose source route */
#define	IPOPT_SATID		136		/* satnet id */
#define	IPOPT_SSRR		137		/* strict source route */


/*
 * Internet implementation parameters.
 */
#define	MAXTTL		255		/* maximum time to live (seconds) */
#define	IPFRAGTTL	15		/* time to live for frag chains */
#define	IPTTLDEC	1		/* subtracted when forwarding */


/*
 * Interface Control Message Protocol Definitions.
 * Per RFC 792, September 1981.
 */

/*
 * Structure of an icmp header.
 */
struct icmp {
	u_char	icmp_type;		/* type of message, see below */
	u_char	icmp_code;		/* type sub code */
	u_short	icmp_cksum;		/* ones complement cksum of struct */
	union {
		u_char ih_pptr;			/* ICMP_PARAMPROB */
		iaddr_t ih_gwaddr;		/* ICMP_REDIRECT */
		struct ih_idseq {
			n_short	icd_id;
			n_short	icd_seq;
		} ih_idseq;
		int ih_void;
	} icmp_hun;
#define	icmp_pptr	icmp_hun.ih_pptr
#define	icmp_gwaddr	icmp_hun.ih_gwaddr
#define	icmp_id		icmp_hun.ih_idseq.icd_id
#define	icmp_seq	icmp_hun.ih_idseq.icd_seq
#define	icmp_void	icmp_hun.ih_void
	union {
		struct id_ts {
			n_time its_otime;
			n_time its_rtime;
			n_time its_ttime;
		} id_ts;
		struct id_ip  {
			struct ip idi_ip;
			/* options and then 64 bits of data */
		} id_ip;
	} icmp_dun;
#define	icmp_otime	icmp_dun.id_ts.its_otime
#define	icmp_rtime	icmp_dun.id_ts.its_rtime
#define	icmp_ttime	icmp_dun.id_ts.its_ttime
#define	icmp_ip		icmp_dun.id_ip.idi_ip
};

/*
 * Lower bounds on packet lengths for various types.
 * For the error advice packets must first insure that the
 * packet is large enought to contain the returned ip header.
 * Only then can we do the check to see if 64 bits of packet
 * data have been returned, since we need to check the returned
 * ip header length.
 */
#define	ICMP_MINLEN	8				/* abs minimum */
#define	ICMP_TSLEN	(8 + 3 * sizeof (n_time))	/* timestamp */
#define	ICMP_ADVLENMIN	(8 + sizeof (struct ip) + 8)	/* min */
#define	ICMP_ADVLEN(p)	(8 + ((p)->icmp_ip.ip_hl << 2) + 8)
	/* N.B.: must separately check that ip_hl >= 5 */

/*
 * Definition of type and code field values.
 */
#define	ICMP_ECHOREPLY		0		/* echo reply */
#define	ICMP_UNREACH		3		/* dest unreachable, codes: */
#define		ICMP_UNREACH_NET	0		/* bad net */
#define		ICMP_UNREACH_HOST	1		/* bad host */
#define		ICMP_UNREACH_PROTOCOL	2		/* bad protocol */
#define		ICMP_UNREACH_PORT	3		/* bad port */
#define		ICMP_UNREACH_NEEDFRAG	4		/* IP_DF caused drop */
#define		ICMP_UNREACH_SRCFAIL	5		/* src route failed */
#define	ICMP_SOURCEQUENCH	4		/* packet lost, slow down */
#define	ICMP_REDIRECT		5		/* shorter route, codes: */
#define		ICMP_REDIRECT_NET	0		/* for network */
#define		ICMP_REDIRECT_HOST	1		/* for host */
#define		ICMP_REDIRECT_TOSNET	2		/* for tos and net */
#define		ICMP_REDIRECT_TOSHOST	3		/* for tos and host */
#define	ICMP_ECHO		8		/* echo service */
#define	ICMP_TIMXCEED		11		/* time exceeded, code: */
#define		ICMP_TIMXCEED_INTRANS	0		/* ttl==0 in transit */
#define		ICMP_TIMXCEED_REASS	1		/* ttl==0 in reass */
#define	ICMP_PARAMPROB		12		/* ip header bad */
#define	ICMP_TSTAMP		13		/* timestamp request */
#define	ICMP_TSTAMPREPLY	14		/* timestamp reply */
#define	ICMP_IREQ		15		/* information request */
#define	ICMP_IREQREPLY		16		/* information reply */
#define	ICMP_AFREQ		17		/* address format request */
#define	ICMP_AFREPLY		18		/* address format reply */
	/* highest known ICMP type code */
#define	ICMP_MAXTYPE		18


/*
 * Udp protocol header.
 * Per RFC 768, September, 1981.
 */
struct udphdr {
	u_short	uh_sport;		/* source port */
	u_short	uh_dport;		/* destination port */
	short	uh_ulen;		/* udp length */
	u_short	uh_sum;			/* udp checksum */
};
4266!Funky!Stuff!
echo x - subs.c
cat > subs.c << '4266!Funky!Stuff!'
/*	sub.c	1.1	01/08/86	*/


#include "../tftp/defs.h"


/*	inet_addr.c	4.5	82/11/14	*/

/*
 * Internet address interpretation routine.
 * All the network library routines call this
 * routine to interpret entries in the data bases
 * which are expected to be an address.
 * The value returned is in network order.
 */
u_long
inet_addr(cp)
	register char *cp;
{
	register u_long val, base, n;
	register char c;
	u_long parts[4], *pp = parts;

	while (*cp == ' ')
		cp++;
	if (*cp == 0)
		return (-1);
again:
	/*
	 * Collect number up to ``.''.
	 * Values are specified as for C:
	 * 0x=hex, 0=octal, other=decimal.
	 */
	val = 0; base = 10;
	if (*cp == '0')
		base = 8, cp++;
	if (*cp == 'x' || *cp == 'X')
		base = 16, cp++;
	while (c = *cp) {
		if (isdigit(c)) {
			val = (val * base) + (c - '0');
			cp++;
			continue;
		}
		if (base == 16 && isxdigit(c)) {
			val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A'));
			cp++;
			continue;
		}
		break;
	}
	if (*cp == '.') {
		/*
		 * Internet format:
		 *	a.b.c.d
		 *	a.b.c	(with c treated as 16-bits)
		 *	a.b	(with b treated as 24 bits)
		 */
		if (pp >= parts + 4)
			return (-1);
		*pp++ = val, cp++;
		goto again;
	}
	/*
	 * Check for trailing characters.
	 */
	if (*cp && !isspace(*cp))
		return (-1);
	*pp++ = val;
	/*
	 * Concoct the address according to
	 * the number of parts specified.
	 */
	n = pp - parts;
	switch (n) {

	case 1:				/* a -- 32 bits */
		val = parts[0];
		break;

	case 2:				/* a.b -- 8.24 bits */
		val = (parts[0] << 24) | (parts[1] & 0xffffff);
		break;

	case 3:				/* a.b.c -- 8.8.16 bits */
		val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
			(parts[2] & 0xffff);
		break;

	case 4:				/* a.b.c.d -- 8.8.8.8 bits */
		val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
		      ((parts[2] & 0xff) << 8) | (parts[3] & 0xff);
		break;

	default:
		return (-1);
	}
	val = htonl(val);
	return (val);
}


/* @(#)strcpy.c	4.1 (Berkeley) 10/5/82 */
/*
 * Copy string s2 to s1.  s1 must be large enough.
 * return s1
 */
char *
strcpy(s1, s2)
register char *s1, *s2;
{
	register char *os1;

	os1 = s1;
	while (*s1++ = *s2++)
		;
	return(os1);
}


/* @(#)strncpy.c	4.1 (Berkeley) 12/21/80 */
/*
 * Copy s2 to s1, truncating or null-padding to always copy n bytes
 * return s1
 */
char *
strncpy(s1, s2, n)
register char *s1, *s2;
{
	register i;
	register char *os1;

	os1 = s1;
	for (i = 0; i < n; i++)
		if ((*s1++ = *s2++) == '\0') {
			while (++i < n)
				*s1++ = '\0';
			return(os1);
		}
	return(os1);
}


/* @(#)strcat.c	4.1 (Berkeley) 12/21/80 */
/*
 * Concatenate s2 on the end of s1.  S1's space must be large enough.
 * Return s1.
 */
char *
strcat(s1, s2)
register char *s1, *s2;
{
	register char *os1;

	os1 = s1;
	while (*s1++)
		;
	--s1;
	while (*s1++ = *s2++)
		;
	return(os1);
}


/*
 * Scaled down version of C Library printf.
 * Used to print diagnostic information directly on console tty.
 * Since it is not interrupt driven, all system activities are
 * suspended.  Printf should not be used for chit-chat.
 *
 * One additional format: %b is supported to decode error registers.
 * Usage is:
 *	printf("reg=%b\n", regval, "<base><arg>*");
 * Where <base> is the output base expressed as a control character,
 * e.g. \10 gives octal; \20 gives hex.  Each arg is a sequence of
 * characters, the first of which gives the bit number to be inspected
 * (origin 1), and the next characters (up to a control character, i.e.
 * a character <= 32), give the name of the register.  Thus
 *	printf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n");
 * would produce output:
 *	reg=2<BITTWO,BITONE>
 */
/*VARARGS1*/
printf(fmt, x1)
	char *fmt;
	unsigned x1;
{

	prf(fmt, &x1, 0);
}

prf(fmt, adx)
	register char *fmt;
	register unsigned int *adx;
{
	register int b, c, i;
	char *s;
	int any;

look:
	while ((c = *fmt++) != '%') {
		if(c == '\0')
			return;
		putchar(c);
	}
again:
	c = *fmt++;
	/* THIS CODE IS VAX DEPENDENT IN HANDLING %l? AND %c */
	switch (c) {

	case 'l':
		goto again;
	case 'x': case 'X':
		b = 16;
		goto number;
	case 'd': case 'D':
	case 'u':		/* what a joke */
		b = 10;
		goto number;
	case 'o': case 'O':
		b = 8;
number:
		printn((unsigned long)*adx, b);
		break;
	case 'c':
		b = *adx;
		for (i = 24; i >= 0; i -= 8)
			if (c = (b >> i) & 0x7f)
				putchar(c);
		break;
#ifdef 0
	case 'b':
		b = *adx++;
		s = (char *)*adx;
		printn((unsigned long)b, *s++);
		any = 0;
		if (b) {
			putchar('<');
			while (i = *s++) {
				if (b & (1 << (i-1))) {
					if (any)
						putchar(',');
					any = 1;
					for (; (c = *s) > 32; s++)
						putchar(c);
				} else
					for (; *s > 32; s++)
						;
			}
			if (any)
				putchar('>');
		}
		break;
#endif
	case 's':
		s = (char *)*adx;
		while (c = *s++)
			putchar(c);
		break;

	case '%':
		putchar('%');
		break;
	}
	adx++;
	goto look;
}


/*
 * Printn prints a number n in base b.
 * We don't use recursion to avoid deep kernel stacks.
 */
printn(n, b)
	unsigned long n;
{
	char prbuf[11];
	register char *cp;

	if (b == 10 && (int)n < 0) {
		putchar('-');
		n = (unsigned)(-(int)n);
	}
	cp = prbuf;
	do {
		*cp++ = "0123456789abcdef"[n%b];
		n /= b;
	} while (n);
	do
		putchar(*--cp);
	while (cp > prbuf);
}


/*
 * Get string from console keyboard into buffer s.
 */
gets(s)
	char *s;
{
	register char *cp;

	getline();
	for (cp = GlobPtr->lineptr ; *cp != '\r' ; )
		*s++ = *cp++;
	*s = 0;
}


/*
 * Get an internet address / name.
 * Returns 0 if nothing typed.
 */
getiaddr(s, addr, name)
	char *s;
	iaddr_t *addr;
	char *name;
{
	iaddr_t ia;
	char line[64];

tryagain:
	printf(s);
	gets(line);
	if (line[0] == 0)
		return(0);
	if (isdigit(line[0])) {
		if ((ia = inet_addr(line)) == -1) {
			printf("Bad address.\n");
			goto tryagain;
		}
		*addr = ia;
	} else {
		if (name == 0)
			goto tryagain;
		strcpy(name, line);
	}
	return(1);
}
4266!Funky!Stuff!
echo x - tftp.h
cat > tftp.h << '4266!Funky!Stuff!'
/*
 * Trivial File Transfer Protocol (IEN-133)
 */
#define	SEGSIZE		512		/* data segment size */

/*
 * Packet types.
 */
#define	RRQ	01			/* read request */
#define	WRQ	02			/* write request */
#define	DATA	03			/* data packet */
#define	ACK	04			/* acknowledgement */
#define	ERROR	05			/* error code */

struct	tftphdr {
	short	th_opcode;		/* packet type */
	union {
		short	tu_block;	/* block # */
		short	tu_code;	/* error code */
		char	tu_stuff[1];	/* request packet stuff */
	} th_u;
};

#define	th_block	th_u.tu_block
#define	th_code		th_u.tu_code
#define	th_stuff	th_u.tu_stuff
#define	th_data		th_stuff[2]
#define	th_msg		th_data

/*
 * Error codes.
 */
#define	EUNDEF		0		/* not defined */
#define	ENOTFOUND	1		/* file not found */
#define	EACCESS		2		/* access violation */
#define	ENOSPACE	3		/* disk full or allocation exceeded */
#define	EBADOP		4		/* illegal TFTP operation */
#define	EBADID		5		/* unknown transfer ID */
#define	EEXISTS		6		/* file already exists */
#define	ENOUSER		7		/* no such user */
4266!Funky!Stuff!
echo x - doc
cat > doc << '4266!Funky!Stuff!'
USING THE BOOTP PROMS AND SERVER

This short document gives an overview of the operation and usage of the new
bootp proms.  If you are interested in how the protocol itself works, please
see RFC951, Bootstrap Protocol, available via FTP from 
[sri-nic]<rfc>rfc951.txt.



PROM COMMANDS RELATED TO BOOTING

The prom monitor has these commands at the 'top level' ('>' prompt)
related to boot loading.  (This is unchanged from the old proms):

	n	Net load (boot) and go
	f	net load (boot) File and stop before executing
	w	show Which devices/files are available for booting

The usual syntax was just 'n filename', which would load the filename and
start execution.



SWITCHES AND SYNTAX WITHIN THE 'N' COMMAND

For the new bootstrap, some extensions have been made within the existing
command structure to allow additional features.  The 'filename' argument
can have the following form:

	filename		normal filename
	<null>			boot the file chosen by the server host
	host:filename		boot filename from a specific server host
	host:			boot the default filename from server host
	?			prompt for parameters

While the 'host:' forms are currently accepted and placed as specified in the
bootp packet, at present the bootp server daemon does not process the host
name field.  (It could do a hostname lookup and forward the request there).

The '?' argument will cause the prom code to prompt for various arguments
that are normally determined automatically.  This can be useful when booting
from a particular internet address/gateway combination.  Arguments prompted
for include:

	Server address/name [broadcast]
	Gateway address [server]
	My own address [set by BOOTP server]
	File name [set by BOOTP server]
	RAM memory address at which to load [0x1000]

The arguments also have default values (shown in []'s) which are assumed
if not specified.  If the user specifies enough information (server address,
my address, and file name), then the prom bypasses the BOOTP phase entirely
and directly enters the TFTP phase.  This can be useful for manually booting
from arbitrary internet hosts not running BOOTP servers.

Normally however, the '?' feature is not used and instead the bootp protocol
itself determines the parameters.

When invoked by the 'n filename' or 'f filename' commands, the filename can
also be prefixed by the following (UNIX-style) switches:

	-i	show available 10MB interfaces
	-iX	select a particular interface (e.g. -i2)
	-p	use PUP booting on 3MB interface (not BOOTP)
	-h	"help me I'm not registered"

The -i switch is used to print out the six byte ethernet addresses of
any attached interfaces.  The prom code currently contains drivers for
the 3COM 3C400 interface (at 4 possible multibus board addresses) and the
Interlan NI3210 (also at 4 addresses).  The -i printout lists both the
multibus address and the ethernet address (if plugged in).  Using this
information you can configure your boards and the bootp database file
discussed below.

Normally the interfaces are scanned in a circular order, starting with
the first.  If a boot can't be obtained after a certain timeout (about
one minute), the next interface is tried in succession.  The -iX switch
can allow you to override the starting point of the scan to use any of
the available interfaces.

At Stanford we still have some PUP 3 megabit gear and so to maintain 
backward compatibility, the -p switch will force the boot to occur from 
that interface, if present.  Normally only the 10MB interfaces are scanned.

The -h switch is currently implemented in the prom, but not in the bootp
server.  It would allow users who arent registered in any bootp database
to still boot by obtaining a 'temporary' IP address, just for the duration
of the boot phase.  As I said this hasnt been implemented in the server yet
but would probably be done by having a small group of IP addresses per given
cable, say xx.yy.zz.250 through xx.yy.zz.254, assignable for this purpose
on a temporary basis.



BOOTP FILES

The bootp server lives in /etc/bootp.  It is normally started by 
/etc/rc.local.  /etc/bootp reads /etc/bootptab when it starts up, this
contains the configuration table, here is a sample:

	#
	# /etc/bootptab:  database for bootp server (/etc/bootp)
	#
	# last update 2/2/86 by croft
	#
	# Blank lines and lines beginning with '#' are ignored.
	#
	# home directory
	
	/usr/sun/bootfile
	
	# default bootfile
	
	sunmonhelp
	
	# end of first section
	
	%%
	
	#
	# The remainder of this file contains one line per client interface
	# with the information shown by the table headings below.
	# The 'host' name is also tried as a suffix for the 'bootfile'
	# when searching the home directory.  (e.g., bootfile.host)
	#
	# host		htype haddr		iaddr		bootfile
	#
	
	tc101g		1 02:60:8c:06:35:05	36.44.0.65	ecw
	tc101gi		1 02:07:01:00:30:02	36.44.0.65	seagate
	sun-tb113a	1 02:60:8c:00:77:78     36.44.0.03      sumextip
	nick		1 02:60:8c:00:99:47	36.44.0.01	ecw

You should start with a file similar to this and edit the host entries
to correspond to your local systems.  The host field does not have to
be a formal host name, it is used for identification in the log file
and also as a possible extension to the bootfile name.

The 'htype' is always '1' and corresponds to the hardware type assigned
ethernet by the Assigned Numbers RFC.  The 'haddr' field can use periods
or colons as separators.  The 'bootfile' entry is the file used if the
client does a default boot ('n') with no filename specified.  This is
frequently the case when a powerup occurs (see CONFIGURATION JUMPERS
section below).

/etc/bootptab is read once at startup and then re-read whenever the write
date/time changes on the file.  This allows the database to be updated
without sending any special signals to the daemon.  Be careful however that
your text editor re-writes the file in place, rather than linking it to
a backup name upon exit.  In the latter case the daemon still has the
original file open and the date hasnt changed, but the file names have
changed out from under the daemon.

The daemon also keeps a log file, currently in /usr/adm/bootplog.  This
may be changed to use the system logging mechanism instead;  in that case
the messages would go to /usr/spool/mqueue/syslog intermixed with all the
other syslog messages from other processes.  Here is an example log:

	#
	#    BOOTP server starting at Wed Mar  5 10:50:08 1986
	#
	(re)reading /etc/bootptab
	request from tc101g for 'ecw'
	replyfile /usr/sun/bootfile/ecw
	(re)reading /etc/bootptab
	request from nick for 'edp'
	replyfile /usr/sun/bootfile/edp
	request from tc101g for 'welchtipa'
	replyfile /usr/sun/bootfile/welchtipa
	request from tc101g for 'zt'
	replyfile /usr/sun/bootfile/zt
	request from tc101g for 'edp'
	replyfile /usr/sun/bootfile/edp



PROM INSTALLATION

The BOOTP software and device drivers are contained in proms U102 and U104,
which are 2764 EPROMs, a total of 16K bytes.  This also includes the
PUP booting code and 3MB device driver.  The old Sun-1 terminal emulator
(for the Sun-1 display buffer and monitor) was omitted from this prom
version to make enough room for the 2 protocols and 3 device drivers
included in this configuration.  When the PUP code is eventually deleted
that will make enough room for a couple more 10 MB drivers.

The U101 and U103 proms, which contain the keyboard command executive and
powerup code are unaffected by the new booting software.  This is good
because 101/103 also contain CPU specific constants like the CPU and UART
clock rates.  When these are changed you have to have a custom set based
on your CPU type (Sun, Forward, Cadlink, etc.) and clock (8 MHz, 10 MHz,...)



CONFIGURATION JUMPERS

On the SUN processor board, edge connector J2 pins 4 through 7 are used
to select one of 14 different programs to boot.  The number to name table
is stored in the first prom set (U101/103) and so it remains constant, 
even though we would have liked to revise it with the new BOOTP software in
U102 and 104.  Here is the table built into most existing prom sets:

	/* 1  */	"tip",		1,
	/* 2  */	"bridge",	1,
	/* 3  */	"gateway",	1,
	/* 4  */	"suntty",	1,
	/* 5  */	"Vload", 	1,
	/* 6  */	"dk(0,2)unix",	1,
	/* 7  */	"db(0,2)unix",	1,
	/* 8  */	"testtip",	1,
	/* 9  */	"sumextip", 	1,
	/* 10 */	"sunboot10",	1,
	/* 11 */	"sunboot11",	1,
	/* 12 */	"sunboot12",	1,
	/* 13 */	"sunboot13",	1,
	/* 14 */	"sunboot14",	1

The actual table can be printed by the monitor "w ff" command.  When no
jumpers are installed the value is 15, which means "self test and enter
keyboard monitor".  If all four jumpers are installed the meaning is the
same, but the self test is skipped.

In the old days, these bootfile names were important since they selected
the file to be autobooted on powerup.  With the new BOOTP proms, the
bootfile name can instead be determined by the configuration file
(/etc/bootptab).  Thus the jumpers can still be used, but are less
important.

To select a load file based on the configuration database, one would like
to use one of the table entries with a null string "", since this is
the proper 'n' argument to indicate this.  However you can see from the
U101/103 table above, there is no null jumper option available.  INSTEAD,
you can use jumper setting #14 (one jumper at pin 4), corresponding to file
"sunboot14".  When the BOOTP server sees this special case, it treats it
the same as if the null filename "" was specified.  Thus the server
searches its database to find the proper boot file for this particular
client machine.



PROM CALLABLE TFTP

The BOOTP/TFTP bootstrap uses a global structure (bglob) located at the
end of memory during its operation.  This structure is left intact after
the booted program gets control.  In some cases a program (such as an
ethertip) may want to fetch a configuration file listing its addresses and
options before starting up.  With the mechanism provided by this bglob
structure, that program can call the prom resident TFTP code to fetch the
desired config file.  Bglob provides variables and strings to hold the
appropriate TFTP data.  Most of these values, except for the actual file
name, would still be valid for such an intended purpose.  Here is the
beginning of bglob so you can get the idea.

	/*
	 * Bootstrap globals live at the end of memory, and can be referenced
	 * through system global 'bglobptr' (defined below), which
	 * points to this structure.
	 */
	struct bglob {
		/*
		 * the first few fields would be good candidates 
		 * to copy in from EAPROM.
		 */
		iaddr_t	myiaddr;	/* my internet address */
		iaddr_t	gateiaddr;	/* gateway ip addr */
		iaddr_t	serveiaddr;	/* server ip addr */
		eaddr_t	myeaddr;	/* my ethernet address */
		int	loadat;		/* load address */
		struct	ifconfig *ifptr;/* current interface */
		u_char	file[64];	/* file name to boot */
		u_char	sname[64];	/* server name */
					/* end EAPROM area */
		/*
		 * next fields allow a program just loaded to re-call PROM
		 * bootp or tftp, e.g. to load an ethertip configuration file.
		 */
		struct ifconfig *ifconfigp; /* points to PROM ifconfig */
		int	(*bootp)();	/* bootp entry point */
		int	(*tftpread)();	/* tftp read entry point */
		int	(*tftpwrite)();	/* tftp write (dump) entry */

		...

	#define	bglobptr	((struct bglob *) (GlobPtr->MemorySize - 2048))



GATEWAY MODIFICATIONS

Another documentation file in this directory, "gmods", explains with a 
pseudo code example, how to make your campus gateways forward BOOTP
requests properly so that the servers can respond to cross-gateway boot
requests.  These modifications have already been installed and are operating
in the Yeager and Lougheed gateways here at Stanford.

4266!Funky!Stuff!
